@sip-protocol/cli 0.2.0 → 0.2.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/index.js +904 -40
- package/dist/index.mjs +924 -43
- package/package.json +10 -8
package/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
|
-
var
|
|
27
|
+
var import_commander14 = require("commander");
|
|
28
28
|
|
|
29
29
|
// src/commands/init.ts
|
|
30
30
|
var import_commander = require("commander");
|
|
@@ -86,6 +86,13 @@ ${message}
|
|
|
86
86
|
function keyValue(key, value) {
|
|
87
87
|
console.log(import_chalk.default.gray(` ${key}:`), import_chalk.default.white(String(value)));
|
|
88
88
|
}
|
|
89
|
+
function json(data) {
|
|
90
|
+
console.log(JSON.stringify(
|
|
91
|
+
data,
|
|
92
|
+
(_, value) => typeof value === "bigint" ? value.toString() : value,
|
|
93
|
+
2
|
|
94
|
+
));
|
|
95
|
+
}
|
|
89
96
|
function spinner(text) {
|
|
90
97
|
return (0, import_ora.default)(text).start();
|
|
91
98
|
}
|
|
@@ -147,35 +154,85 @@ function createInitCommand() {
|
|
|
147
154
|
// src/commands/keygen.ts
|
|
148
155
|
var import_commander2 = require("commander");
|
|
149
156
|
var import_sdk = require("@sip-protocol/sdk");
|
|
157
|
+
var fs = __toESM(require("fs"));
|
|
158
|
+
var path = __toESM(require("path"));
|
|
159
|
+
function formatKeysAsText(data) {
|
|
160
|
+
return [
|
|
161
|
+
`# SIP Stealth Meta-Address Keys`,
|
|
162
|
+
`# Generated: ${data.generatedAt}`,
|
|
163
|
+
`# Chain: ${data.chain}`,
|
|
164
|
+
``,
|
|
165
|
+
`## Public Keys (safe to share)`,
|
|
166
|
+
`SPENDING_PUBLIC_KEY=${data.spendingPublicKey}`,
|
|
167
|
+
`VIEWING_PUBLIC_KEY=${data.viewingPublicKey}`,
|
|
168
|
+
`META_ADDRESS=${data.metaAddress}`,
|
|
169
|
+
``,
|
|
170
|
+
`## Private Keys (KEEP SECRET!)`,
|
|
171
|
+
`SPENDING_PRIVATE_KEY=${data.spendingPrivateKey}`,
|
|
172
|
+
`VIEWING_PRIVATE_KEY=${data.viewingPrivateKey}`,
|
|
173
|
+
``,
|
|
174
|
+
`# WARNING: Delete this file after securely storing the keys!`
|
|
175
|
+
].join("\n");
|
|
176
|
+
}
|
|
150
177
|
function createKeygenCommand() {
|
|
151
|
-
return new import_commander2.Command("keygen").description("Generate stealth meta-address").option("-c, --chain <chain>", "Target chain (ethereum, solana, near)", "ethereum").option("--spending-key <key>", "Spending private key (hex)").option("--viewing-key <key>", "Viewing private key (hex)").action(async (options) => {
|
|
178
|
+
return new import_commander2.Command("keygen").description("Generate stealth meta-address").option("-c, --chain <chain>", "Target chain (ethereum, solana, near)", "ethereum").option("--spending-key <key>", "Spending private key (hex)").option("--viewing-key <key>", "Viewing private key (hex)").option("-o, --output-file <path>", "Output file for keys (enables secure export)").option("-f, --format <format>", "Output format: json or text", "json").action(async (options) => {
|
|
152
179
|
try {
|
|
153
180
|
heading("Generate Stealth Meta-Address");
|
|
154
181
|
const chain = options.chain;
|
|
155
182
|
const useEd25519 = (0, import_sdk.isEd25519Chain)(chain);
|
|
156
|
-
|
|
183
|
+
const format = options.format || "json";
|
|
184
|
+
let result;
|
|
157
185
|
if (useEd25519) {
|
|
158
186
|
if (options.spendingKey || options.viewingKey) {
|
|
159
187
|
warning("Ed25519 chains do not support custom keys in CLI yet");
|
|
160
188
|
}
|
|
161
|
-
|
|
189
|
+
result = (0, import_sdk.generateEd25519StealthMetaAddress)(chain);
|
|
162
190
|
} else {
|
|
163
|
-
|
|
191
|
+
result = (0, import_sdk.generateStealthMetaAddress)(chain);
|
|
164
192
|
}
|
|
165
193
|
success("Stealth meta-address generated");
|
|
166
194
|
keyValue("Chain", chain);
|
|
167
|
-
const spendingPubKey =
|
|
168
|
-
const viewingPubKey =
|
|
169
|
-
const spendingPrivKey =
|
|
170
|
-
const viewingPrivKey =
|
|
195
|
+
const spendingPubKey = result.metaAddress.spendingKey;
|
|
196
|
+
const viewingPubKey = result.metaAddress.viewingKey;
|
|
197
|
+
const spendingPrivKey = result.spendingPrivateKey;
|
|
198
|
+
const viewingPrivKey = result.viewingPrivateKey;
|
|
199
|
+
const encoded = (0, import_sdk.encodeStealthMetaAddress)(result.metaAddress);
|
|
171
200
|
keyValue("Spending Public Key", spendingPubKey);
|
|
172
201
|
keyValue("Viewing Public Key", viewingPubKey);
|
|
173
|
-
const encoded = (0, import_sdk.encodeStealthMetaAddress)(metaAddress.metaAddress);
|
|
174
202
|
keyValue("Encoded Address", encoded);
|
|
175
203
|
console.log();
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
204
|
+
if (options.outputFile) {
|
|
205
|
+
const outputPath = path.resolve(options.outputFile);
|
|
206
|
+
const dir = path.dirname(outputPath);
|
|
207
|
+
if (dir !== "." && dir !== "/") {
|
|
208
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
209
|
+
}
|
|
210
|
+
const exportData = {
|
|
211
|
+
chain,
|
|
212
|
+
spendingPublicKey: spendingPubKey,
|
|
213
|
+
viewingPublicKey: viewingPubKey,
|
|
214
|
+
spendingPrivateKey: spendingPrivKey,
|
|
215
|
+
viewingPrivateKey: viewingPrivKey,
|
|
216
|
+
metaAddress: encoded,
|
|
217
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
218
|
+
};
|
|
219
|
+
const content = format === "json" ? JSON.stringify(exportData, null, 2) : formatKeysAsText(exportData);
|
|
220
|
+
fs.writeFileSync(outputPath, content, {
|
|
221
|
+
mode: 384,
|
|
222
|
+
encoding: "utf-8"
|
|
223
|
+
});
|
|
224
|
+
success(`Keys exported to: ${outputPath}`);
|
|
225
|
+
warning("SECURITY: File permissions set to 600 (owner only)");
|
|
226
|
+
warning("SECURITY: Delete this file after securely storing the keys!");
|
|
227
|
+
info("Private keys NOT displayed in terminal for security");
|
|
228
|
+
} else {
|
|
229
|
+
warning("PRIVATE KEYS - Keep these secure!");
|
|
230
|
+
keyValue("Spending Private Key", spendingPrivKey);
|
|
231
|
+
keyValue("Viewing Private Key", viewingPrivKey);
|
|
232
|
+
console.log();
|
|
233
|
+
info("TIP: Use --output-file for secure key export:");
|
|
234
|
+
info(" sip keygen --chain solana --output-file ./keys.json");
|
|
235
|
+
}
|
|
179
236
|
} catch (err) {
|
|
180
237
|
console.error("Failed to generate keys:", err);
|
|
181
238
|
process.exit(1);
|
|
@@ -463,8 +520,31 @@ Solver not found: ${options.solver}`);
|
|
|
463
520
|
// src/commands/scan.ts
|
|
464
521
|
var import_commander8 = require("commander");
|
|
465
522
|
var import_sdk7 = require("@sip-protocol/sdk");
|
|
523
|
+
var import_ed25519 = require("@noble/curves/ed25519");
|
|
524
|
+
var import_secp256k1 = require("@noble/curves/secp256k1");
|
|
525
|
+
var import_utils = require("@noble/hashes/utils");
|
|
526
|
+
var fs2 = __toESM(require("fs"));
|
|
527
|
+
var path2 = __toESM(require("path"));
|
|
528
|
+
function formatScanResultsAsText(results, exportedAt) {
|
|
529
|
+
const lines = [
|
|
530
|
+
`# SIP Scan Results`,
|
|
531
|
+
`# Exported: ${exportedAt}`,
|
|
532
|
+
`# Found: ${results.length} stealth payment(s)`,
|
|
533
|
+
``
|
|
534
|
+
];
|
|
535
|
+
for (const r of results) {
|
|
536
|
+
lines.push(`## Address: ${r.address}`);
|
|
537
|
+
lines.push(`CHAIN=${r.chain}`);
|
|
538
|
+
if (r.privateKey) {
|
|
539
|
+
lines.push(`PRIVATE_KEY=${r.privateKey}`);
|
|
540
|
+
}
|
|
541
|
+
lines.push(``);
|
|
542
|
+
}
|
|
543
|
+
lines.push(`# WARNING: Delete this file after importing keys to your wallet!`);
|
|
544
|
+
return lines.join("\n");
|
|
545
|
+
}
|
|
466
546
|
function createScanCommand() {
|
|
467
|
-
return new import_commander8.Command("scan").description("Scan for stealth payments").requiredOption("-c, --chain <chain>", "Chain to scan (ethereum, solana, near)").requiredOption("-s, --spending-key <key>", "Your spending private key (hex)").requiredOption("-v, --viewing-key <key>", "Your viewing private key (hex)").option("-a, --addresses <addresses...>", "Specific addresses to check").action(async (options) => {
|
|
547
|
+
return new import_commander8.Command("scan").description("Scan for stealth payments").requiredOption("-c, --chain <chain>", "Chain to scan (ethereum, solana, near)").requiredOption("-s, --spending-key <key>", "Your spending private key (hex)").requiredOption("-v, --viewing-key <key>", "Your viewing private key (hex)").option("-a, --addresses <addresses...>", "Specific addresses to check").option("-o, --output-file <path>", "Output file for private keys (required to export keys)").option("-f, --format <format>", "Output format: json or text", "json").action(async (options) => {
|
|
468
548
|
try {
|
|
469
549
|
heading("Scan for Stealth Payments");
|
|
470
550
|
const chain = options.chain;
|
|
@@ -479,24 +559,28 @@ function createScanCommand() {
|
|
|
479
559
|
const results = [];
|
|
480
560
|
for (const address of options.addresses) {
|
|
481
561
|
try {
|
|
562
|
+
const stealthAddr = (0, import_sdk7.parseStealthAddress)(address);
|
|
482
563
|
let result;
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
)
|
|
564
|
+
const spendingPubKey = useEd25519 ? `0x${(0, import_utils.bytesToHex)(import_ed25519.ed25519.getPublicKey((0, import_utils.hexToBytes)(options.spendingKey.slice(2))))}` : `0x${(0, import_utils.bytesToHex)(import_secp256k1.secp256k1.getPublicKey((0, import_utils.hexToBytes)(options.spendingKey.slice(2)), true))}`;
|
|
565
|
+
let isMine = (0, import_sdk7.checkStealthAddress)(stealthAddr, options.viewingKey, spendingPubKey);
|
|
566
|
+
let isLegacy = false;
|
|
567
|
+
if (!isMine) {
|
|
568
|
+
const legacyMatch = useEd25519 ? (0, import_sdk7.checkEd25519StealthAddressV1)(stealthAddr, options.spendingKey, options.viewingKey) : (0, import_sdk7.checkSecp256k1StealthAddressV1)(stealthAddr, options.spendingKey, options.viewingKey);
|
|
569
|
+
if (legacyMatch) {
|
|
570
|
+
isMine = true;
|
|
571
|
+
isLegacy = true;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (isMine) {
|
|
575
|
+
const derivedKey = isLegacy ? (0, import_sdk7.deriveStealthPrivateKeyV1)(stealthAddr, options.spendingKey, options.viewingKey) : (0, import_sdk7.deriveStealthPrivateKey)(stealthAddr, options.spendingKey, options.viewingKey);
|
|
576
|
+
result = { isMine: true, stealthPrivateKey: derivedKey.privateKey };
|
|
489
577
|
} else {
|
|
490
|
-
result =
|
|
491
|
-
address,
|
|
492
|
-
options.spendingKey,
|
|
493
|
-
options.viewingKey
|
|
494
|
-
);
|
|
578
|
+
result = { isMine: false };
|
|
495
579
|
}
|
|
496
580
|
results.push({
|
|
497
581
|
address,
|
|
498
582
|
isMine: result.isMine,
|
|
499
|
-
privateKey: result.
|
|
583
|
+
privateKey: result.stealthPrivateKey
|
|
500
584
|
});
|
|
501
585
|
} catch (err) {
|
|
502
586
|
results.push({
|
|
@@ -510,15 +594,34 @@ function createScanCommand() {
|
|
|
510
594
|
success(`Scanned ${results.length} address(es), found ${foundCount} stealth payment(s)`);
|
|
511
595
|
if (foundCount > 0) {
|
|
512
596
|
console.log();
|
|
513
|
-
const headers = ["Address", "Match"
|
|
597
|
+
const headers = ["Address", "Match"];
|
|
514
598
|
const rows = results.filter((r) => r.isMine).map((r) => [
|
|
515
|
-
r.address.slice(0,
|
|
516
|
-
"Yes"
|
|
517
|
-
r.privateKey ? r.privateKey.slice(0, 10) + "..." : "N/A"
|
|
599
|
+
r.address.slice(0, 16) + "..." + r.address.slice(-8),
|
|
600
|
+
"Yes"
|
|
518
601
|
]);
|
|
519
602
|
table(headers, rows);
|
|
520
603
|
console.log();
|
|
521
|
-
|
|
604
|
+
if (options.outputFile) {
|
|
605
|
+
const outputPath = path2.resolve(options.outputFile);
|
|
606
|
+
const format = options.format || "json";
|
|
607
|
+
const exportedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
608
|
+
const exportData = results.filter((r) => r.isMine).map((r) => ({
|
|
609
|
+
address: r.address,
|
|
610
|
+
privateKey: r.privateKey,
|
|
611
|
+
chain: options.chain
|
|
612
|
+
}));
|
|
613
|
+
const content = format === "json" ? JSON.stringify(exportData.map((d) => ({ ...d, exportedAt })), null, 2) : formatScanResultsAsText(exportData, exportedAt);
|
|
614
|
+
fs2.writeFileSync(outputPath, content, {
|
|
615
|
+
mode: 384,
|
|
616
|
+
encoding: "utf-8"
|
|
617
|
+
});
|
|
618
|
+
success(`Private keys exported to: ${outputPath}`);
|
|
619
|
+
warning("SECURITY: Delete this file after importing keys to your wallet!");
|
|
620
|
+
warning("SECURITY: File permissions set to 600 (owner only)");
|
|
621
|
+
} else {
|
|
622
|
+
info("Private keys not exported (use --output-file to export securely)");
|
|
623
|
+
warning("Keys are NOT displayed in terminal for security reasons");
|
|
624
|
+
}
|
|
522
625
|
} else {
|
|
523
626
|
info("No stealth payments found");
|
|
524
627
|
}
|
|
@@ -872,8 +975,8 @@ function createViewingKeyCommand() {
|
|
|
872
975
|
const cmd = new import_commander11.Command("viewing-key").alias("vk").description("Manage viewing keys for selective disclosure");
|
|
873
976
|
cmd.command("generate").alias("gen").description("Generate a new viewing key").option("-p, --path <path>", 'Key derivation path (e.g., "payments/2024")').option("-i, --interactive", "Interactive mode").action(async (options) => {
|
|
874
977
|
heading("Generate Viewing Key");
|
|
875
|
-
let
|
|
876
|
-
if (options.interactive || !
|
|
978
|
+
let path3 = options.path;
|
|
979
|
+
if (options.interactive || !path3) {
|
|
877
980
|
const response = await (0, import_prompts3.default)([
|
|
878
981
|
{
|
|
879
982
|
type: "text",
|
|
@@ -892,11 +995,11 @@ function createViewingKeyCommand() {
|
|
|
892
995
|
console.log(import_chalk4.default.yellow("Cancelled."));
|
|
893
996
|
return;
|
|
894
997
|
}
|
|
895
|
-
|
|
998
|
+
path3 = response.path;
|
|
896
999
|
}
|
|
897
1000
|
const spinner2 = (0, import_ora4.default)("Generating viewing key...").start();
|
|
898
1001
|
try {
|
|
899
|
-
const viewingKey = (0, import_sdk10.generateViewingKey)(
|
|
1002
|
+
const viewingKey = (0, import_sdk10.generateViewingKey)(path3);
|
|
900
1003
|
spinner2.succeed("Viewing key generated");
|
|
901
1004
|
console.log();
|
|
902
1005
|
keyValue("Path", viewingKey.path);
|
|
@@ -921,7 +1024,7 @@ function createViewingKeyCommand() {
|
|
|
921
1024
|
]);
|
|
922
1025
|
if (saveResponse.save) {
|
|
923
1026
|
const existingKeys = getConfig("viewingKeys") || {};
|
|
924
|
-
existingKeys[
|
|
1027
|
+
existingKeys[path3] = viewingKey.key;
|
|
925
1028
|
setConfig("viewingKeys", existingKeys);
|
|
926
1029
|
success("Viewing key saved to config");
|
|
927
1030
|
}
|
|
@@ -942,8 +1045,8 @@ function createViewingKeyCommand() {
|
|
|
942
1045
|
return;
|
|
943
1046
|
}
|
|
944
1047
|
console.log();
|
|
945
|
-
entries.forEach(([
|
|
946
|
-
console.log(import_chalk4.default.cyan(` ${
|
|
1048
|
+
entries.forEach(([path3, key]) => {
|
|
1049
|
+
console.log(import_chalk4.default.cyan(` ${path3}`));
|
|
947
1050
|
console.log(import_chalk4.default.gray(` ${key.slice(0, 20)}...${key.slice(-10)}`));
|
|
948
1051
|
console.log();
|
|
949
1052
|
});
|
|
@@ -963,7 +1066,7 @@ function createViewingKeyCommand() {
|
|
|
963
1066
|
type: "select",
|
|
964
1067
|
name: "path",
|
|
965
1068
|
message: "Select viewing key to share:",
|
|
966
|
-
choices: entries.map(([
|
|
1069
|
+
choices: entries.map(([path3]) => ({ title: path3, value: path3 }))
|
|
967
1070
|
},
|
|
968
1071
|
{
|
|
969
1072
|
type: "select",
|
|
@@ -1006,8 +1109,767 @@ function createViewingKeyCommand() {
|
|
|
1006
1109
|
return cmd;
|
|
1007
1110
|
}
|
|
1008
1111
|
|
|
1112
|
+
// src/commands/backends.ts
|
|
1113
|
+
var import_commander12 = require("commander");
|
|
1114
|
+
var import_sdk11 = require("@sip-protocol/sdk");
|
|
1115
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
1116
|
+
function createDefaultRegistry() {
|
|
1117
|
+
const registry = new import_sdk11.PrivacyBackendRegistry({ enableHealthTracking: true });
|
|
1118
|
+
registry.register(new import_sdk11.SIPNativeBackend());
|
|
1119
|
+
return registry;
|
|
1120
|
+
}
|
|
1121
|
+
function collectBackendInfo(registry) {
|
|
1122
|
+
const entries = registry.getAllEntries();
|
|
1123
|
+
const healthTracker = registry.getHealthTracker();
|
|
1124
|
+
const results = [];
|
|
1125
|
+
for (const entry of entries) {
|
|
1126
|
+
const backend = entry.backend;
|
|
1127
|
+
const caps = backend.getCapabilities();
|
|
1128
|
+
let healthy = true;
|
|
1129
|
+
let failures = 0;
|
|
1130
|
+
if (healthTracker) {
|
|
1131
|
+
const health = healthTracker.getHealth(backend.name);
|
|
1132
|
+
if (health) {
|
|
1133
|
+
healthy = health.isHealthy;
|
|
1134
|
+
failures = health.consecutiveFailures;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
results.push({
|
|
1138
|
+
name: backend.name,
|
|
1139
|
+
type: backend.type,
|
|
1140
|
+
chains: [...backend.chains],
|
|
1141
|
+
healthy,
|
|
1142
|
+
failures,
|
|
1143
|
+
enabled: entry.enabled,
|
|
1144
|
+
compliance: caps.complianceSupport
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
return results;
|
|
1148
|
+
}
|
|
1149
|
+
function createBackendsCommand() {
|
|
1150
|
+
const cmd = new import_commander12.Command("backends").description("Manage and list privacy backends");
|
|
1151
|
+
cmd.command("list").description("List all registered privacy backends").option("--health", "Show health status").option("--metrics", "Show detailed metrics").option("--json", "Output as JSON").option("--type <type>", "Filter by type (transaction, compute, both)").option("--chain <chain>", "Filter by chain support").action(async (options) => {
|
|
1152
|
+
try {
|
|
1153
|
+
const registry = createDefaultRegistry();
|
|
1154
|
+
let backends = collectBackendInfo(registry);
|
|
1155
|
+
if (options.type) {
|
|
1156
|
+
backends = backends.filter((b) => b.type === options.type || b.type === "both");
|
|
1157
|
+
}
|
|
1158
|
+
if (options.chain) {
|
|
1159
|
+
backends = backends.filter((b) => b.chains.includes(options.chain));
|
|
1160
|
+
}
|
|
1161
|
+
if (options.json) {
|
|
1162
|
+
json({
|
|
1163
|
+
backends: backends.map((b) => ({
|
|
1164
|
+
...b,
|
|
1165
|
+
chains: b.chains
|
|
1166
|
+
})),
|
|
1167
|
+
total: backends.length,
|
|
1168
|
+
healthy: backends.filter((b) => b.healthy).length
|
|
1169
|
+
});
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
heading("Privacy Backends");
|
|
1173
|
+
if (backends.length === 0) {
|
|
1174
|
+
warning("No backends match the specified filters");
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
const headers = ["NAME", "TYPE", "CHAINS", "COMPLIANCE"];
|
|
1178
|
+
if (options.health || options.metrics) {
|
|
1179
|
+
headers.push("HEALTHY", "FAILURES");
|
|
1180
|
+
}
|
|
1181
|
+
const rows = backends.map((b) => {
|
|
1182
|
+
const row = [
|
|
1183
|
+
b.enabled ? b.name : import_chalk5.default.gray(b.name + " (disabled)"),
|
|
1184
|
+
b.type,
|
|
1185
|
+
b.chains.join(", "),
|
|
1186
|
+
b.compliance ? import_chalk5.default.green("\u2713") : import_chalk5.default.gray("\u2717")
|
|
1187
|
+
];
|
|
1188
|
+
if (options.health || options.metrics) {
|
|
1189
|
+
row.push(
|
|
1190
|
+
b.healthy ? import_chalk5.default.green("\u2713") : import_chalk5.default.red("\u2717"),
|
|
1191
|
+
b.failures
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
return row;
|
|
1195
|
+
});
|
|
1196
|
+
table(headers, rows);
|
|
1197
|
+
console.log();
|
|
1198
|
+
const healthyCount = backends.filter((b) => b.healthy).length;
|
|
1199
|
+
const complianceCount = backends.filter((b) => b.compliance).length;
|
|
1200
|
+
success(`${backends.length} backend(s) registered`);
|
|
1201
|
+
if (options.health || options.metrics) {
|
|
1202
|
+
if (healthyCount === backends.length) {
|
|
1203
|
+
info(`All backends healthy`);
|
|
1204
|
+
} else {
|
|
1205
|
+
warning(`${healthyCount}/${backends.length} backends healthy`);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
info(`${complianceCount} backend(s) support compliance (viewing keys)`);
|
|
1209
|
+
} catch (err) {
|
|
1210
|
+
console.error("Failed to list backends:", err);
|
|
1211
|
+
process.exit(1);
|
|
1212
|
+
}
|
|
1213
|
+
});
|
|
1214
|
+
cmd.command("info <name>").description("Show detailed information about a specific backend").option("--json", "Output as JSON").action(async (name, options) => {
|
|
1215
|
+
try {
|
|
1216
|
+
const registry = createDefaultRegistry();
|
|
1217
|
+
const backend = registry.get(name);
|
|
1218
|
+
if (!backend) {
|
|
1219
|
+
console.error(`Backend '${name}' not found`);
|
|
1220
|
+
console.error("Available backends:", registry.getNames().join(", "));
|
|
1221
|
+
process.exit(1);
|
|
1222
|
+
}
|
|
1223
|
+
const caps = backend.getCapabilities();
|
|
1224
|
+
const healthTracker = registry.getHealthTracker();
|
|
1225
|
+
const health = healthTracker?.getHealth(name);
|
|
1226
|
+
const metrics = healthTracker?.getMetrics(name);
|
|
1227
|
+
const backendInfo = {
|
|
1228
|
+
name: backend.name,
|
|
1229
|
+
type: backend.type,
|
|
1230
|
+
chains: [...backend.chains],
|
|
1231
|
+
capabilities: {
|
|
1232
|
+
complianceSupport: caps.complianceSupport,
|
|
1233
|
+
anonymitySet: caps.anonymitySet,
|
|
1234
|
+
latency: caps.latencyEstimate,
|
|
1235
|
+
setupRequired: caps.setupRequired
|
|
1236
|
+
},
|
|
1237
|
+
health: health ? {
|
|
1238
|
+
state: health.circuitState,
|
|
1239
|
+
isHealthy: health.isHealthy,
|
|
1240
|
+
consecutiveFailures: health.consecutiveFailures,
|
|
1241
|
+
consecutiveSuccesses: health.consecutiveSuccesses,
|
|
1242
|
+
lastFailureTime: health.lastFailureTime ? new Date(health.lastFailureTime).toISOString() : null,
|
|
1243
|
+
lastFailureReason: health.lastFailureReason ?? null
|
|
1244
|
+
} : null,
|
|
1245
|
+
metrics: metrics ? {
|
|
1246
|
+
totalRequests: metrics.totalRequests,
|
|
1247
|
+
successfulRequests: metrics.successfulRequests,
|
|
1248
|
+
failedRequests: metrics.failedRequests,
|
|
1249
|
+
averageLatencyMs: Math.round(metrics.averageLatencyMs),
|
|
1250
|
+
successRate: metrics.totalRequests > 0 ? Math.round(metrics.successfulRequests / metrics.totalRequests * 100) : 0
|
|
1251
|
+
} : null
|
|
1252
|
+
};
|
|
1253
|
+
if (options.json) {
|
|
1254
|
+
json(backendInfo);
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
heading(`Backend: ${backend.name}`);
|
|
1258
|
+
console.log(import_chalk5.default.bold("General"));
|
|
1259
|
+
console.log(` Type: ${backend.type}`);
|
|
1260
|
+
console.log(` Chains: ${backend.chains.join(", ")}`);
|
|
1261
|
+
console.log();
|
|
1262
|
+
console.log(import_chalk5.default.bold("Capabilities"));
|
|
1263
|
+
console.log(` Compliance: ${caps.complianceSupport ? import_chalk5.default.green("Yes") : import_chalk5.default.gray("No")}`);
|
|
1264
|
+
console.log(` Anonymity Set: ${caps.anonymitySet ?? "N/A"}`);
|
|
1265
|
+
console.log(` Est. Latency: ${caps.latencyEstimate}`);
|
|
1266
|
+
console.log(` Setup Required: ${caps.setupRequired ? "Yes" : "No"}`);
|
|
1267
|
+
if (health) {
|
|
1268
|
+
console.log();
|
|
1269
|
+
console.log(import_chalk5.default.bold("Health"));
|
|
1270
|
+
console.log(` State: ${health.circuitState === "closed" ? import_chalk5.default.green("Healthy") : import_chalk5.default.red(health.circuitState)}`);
|
|
1271
|
+
console.log(` Healthy: ${health.isHealthy ? import_chalk5.default.green("Yes") : import_chalk5.default.red("No")}`);
|
|
1272
|
+
console.log(` Failures: ${health.consecutiveFailures}`);
|
|
1273
|
+
console.log(` Successes: ${health.consecutiveSuccesses}`);
|
|
1274
|
+
if (health.lastFailureReason) {
|
|
1275
|
+
console.log(` Last Error: ${health.lastFailureReason}`);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
if (metrics) {
|
|
1279
|
+
console.log();
|
|
1280
|
+
console.log(import_chalk5.default.bold("Metrics"));
|
|
1281
|
+
console.log(` Total Requests: ${metrics.totalRequests}`);
|
|
1282
|
+
console.log(` Success Rate: ${backendInfo.metrics?.successRate}%`);
|
|
1283
|
+
console.log(` Avg Latency: ${backendInfo.metrics?.averageLatencyMs}ms`);
|
|
1284
|
+
}
|
|
1285
|
+
} catch (err) {
|
|
1286
|
+
console.error("Failed to get backend info:", err);
|
|
1287
|
+
process.exit(1);
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
return cmd;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
// src/commands/proof.ts
|
|
1294
|
+
var import_commander13 = require("commander");
|
|
1295
|
+
var import_fs = require("fs");
|
|
1296
|
+
var import_sdk12 = require("@sip-protocol/sdk");
|
|
1297
|
+
function readProofFile(path3) {
|
|
1298
|
+
if (!(0, import_fs.existsSync)(path3)) {
|
|
1299
|
+
throw new Error(`File not found: ${path3}`);
|
|
1300
|
+
}
|
|
1301
|
+
const content = (0, import_fs.readFileSync)(path3, "utf-8");
|
|
1302
|
+
return JSON.parse(content);
|
|
1303
|
+
}
|
|
1304
|
+
function writeProofFile(path3, data) {
|
|
1305
|
+
(0, import_fs.writeFileSync)(path3, JSON.stringify(data, null, 2));
|
|
1306
|
+
}
|
|
1307
|
+
function readFromStdin() {
|
|
1308
|
+
return new Promise((resolve3, reject) => {
|
|
1309
|
+
let data = "";
|
|
1310
|
+
process.stdin.setEncoding("utf8");
|
|
1311
|
+
process.stdin.on("data", (chunk) => {
|
|
1312
|
+
data += chunk;
|
|
1313
|
+
});
|
|
1314
|
+
process.stdin.on("end", () => resolve3(data));
|
|
1315
|
+
process.stdin.on("error", reject);
|
|
1316
|
+
setTimeout(() => {
|
|
1317
|
+
if (data === "") {
|
|
1318
|
+
reject(new Error("No input received from stdin"));
|
|
1319
|
+
}
|
|
1320
|
+
}, 1e3);
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
var PROOF_SYSTEM_NAMES = {
|
|
1324
|
+
noir: "Noir (Aztec)",
|
|
1325
|
+
halo2: "Halo2 (Zcash)",
|
|
1326
|
+
kimchi: "Kimchi (Mina)",
|
|
1327
|
+
groth16: "Groth16",
|
|
1328
|
+
plonk: "PLONK",
|
|
1329
|
+
stark: "STARK"
|
|
1330
|
+
};
|
|
1331
|
+
function formatProofSystem(system) {
|
|
1332
|
+
return PROOF_SYSTEM_NAMES[system] || system;
|
|
1333
|
+
}
|
|
1334
|
+
function getSystemFromProof(proof) {
|
|
1335
|
+
return proof.metadata.system;
|
|
1336
|
+
}
|
|
1337
|
+
function createProofCommand() {
|
|
1338
|
+
const proof = new import_commander13.Command("proof").description("Proof composition operations (M20)");
|
|
1339
|
+
proof.command("generate").description("Generate a ZK proof").requiredOption("-s, --system <system>", "Proof system (noir|halo2|kimchi|mock)").requiredOption("-c, --circuit <id>", "Circuit identifier").option("-i, --inputs <json>", "Public inputs as JSON string").option("-f, --inputs-file <path>", "Public inputs from JSON file").option("-w, --witness <json>", "Private witness as JSON string").option("--witness-file <path>", "Private witness from JSON file").option("-o, --output <path>", "Output file path (default: stdout)").option("--json", "Output as JSON", false).action(async (options) => {
|
|
1340
|
+
const format = options.json ? "json" : "human";
|
|
1341
|
+
try {
|
|
1342
|
+
if (format === "human") {
|
|
1343
|
+
heading("Generate ZK Proof");
|
|
1344
|
+
}
|
|
1345
|
+
let publicInputs = {};
|
|
1346
|
+
if (options.inputs) {
|
|
1347
|
+
publicInputs = JSON.parse(options.inputs);
|
|
1348
|
+
} else if (options.inputsFile) {
|
|
1349
|
+
publicInputs = JSON.parse((0, import_fs.readFileSync)(options.inputsFile, "utf-8"));
|
|
1350
|
+
}
|
|
1351
|
+
const spin = format === "human" ? spinner(`Generating ${options.system} proof...`) : null;
|
|
1352
|
+
const systemLower = options.system.toLowerCase();
|
|
1353
|
+
const provider = new import_sdk12.MockProofProvider({ silent: true });
|
|
1354
|
+
await provider.initialize();
|
|
1355
|
+
const result = await provider.generateFundingProof({
|
|
1356
|
+
balance: BigInt(publicInputs.balance || "1000000"),
|
|
1357
|
+
minimumRequired: BigInt(publicInputs.minimum || "100"),
|
|
1358
|
+
blindingFactor: new Uint8Array(32),
|
|
1359
|
+
assetId: publicInputs.asset || "ETH",
|
|
1360
|
+
userAddress: publicInputs.user || "0x0000000000000000000000000000000000000000",
|
|
1361
|
+
ownershipSignature: new Uint8Array(64)
|
|
1362
|
+
});
|
|
1363
|
+
spin?.succeed("Proof generated");
|
|
1364
|
+
const proofMetadata = {
|
|
1365
|
+
system: systemLower,
|
|
1366
|
+
systemVersion: "1.0.0",
|
|
1367
|
+
circuitId: options.circuit,
|
|
1368
|
+
circuitVersion: "1.0.0",
|
|
1369
|
+
generatedAt: Date.now(),
|
|
1370
|
+
proofSizeBytes: result.proof.proof.length / 2
|
|
1371
|
+
// hex string to bytes
|
|
1372
|
+
};
|
|
1373
|
+
const proofData = {
|
|
1374
|
+
proof: {
|
|
1375
|
+
id: `proof_${Date.now()}`,
|
|
1376
|
+
proof: result.proof.proof,
|
|
1377
|
+
publicInputs: result.publicInputs.map(String),
|
|
1378
|
+
metadata: proofMetadata
|
|
1379
|
+
},
|
|
1380
|
+
metadata: {
|
|
1381
|
+
system: options.system,
|
|
1382
|
+
circuit: options.circuit,
|
|
1383
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1384
|
+
}
|
|
1385
|
+
};
|
|
1386
|
+
if (options.output) {
|
|
1387
|
+
writeProofFile(options.output, proofData);
|
|
1388
|
+
if (format === "human") {
|
|
1389
|
+
success(`Proof written to ${options.output}`);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
if (format === "json") {
|
|
1393
|
+
json(proofData);
|
|
1394
|
+
} else if (!options.output) {
|
|
1395
|
+
keyValue("System", formatProofSystem(systemLower));
|
|
1396
|
+
keyValue("Circuit", options.circuit);
|
|
1397
|
+
keyValue("Proof", formatHash(result.proof.proof, 16));
|
|
1398
|
+
keyValue("Public Inputs", JSON.stringify(result.publicInputs));
|
|
1399
|
+
}
|
|
1400
|
+
} catch (err) {
|
|
1401
|
+
if (format === "json") {
|
|
1402
|
+
json({ error: String(err) });
|
|
1403
|
+
} else {
|
|
1404
|
+
error(`Failed to generate proof: ${err}`);
|
|
1405
|
+
}
|
|
1406
|
+
process.exit(1);
|
|
1407
|
+
}
|
|
1408
|
+
});
|
|
1409
|
+
proof.command("compose").description("Compose multiple proofs together").requiredOption("-p, --proofs <paths...>", "Proof file paths to compose").option("-t, --template <name>", "Composition template (sequential|parallel|recursive)", "sequential").option("-o, --output <path>", "Output file path (default: stdout)").option("--json", "Output as JSON", false).option("--validate", "Validate compatibility before composing", true).action(async (options) => {
|
|
1410
|
+
const format = options.json ? "json" : "human";
|
|
1411
|
+
try {
|
|
1412
|
+
if (format === "human") {
|
|
1413
|
+
heading("Compose Proofs");
|
|
1414
|
+
}
|
|
1415
|
+
const proofs = [];
|
|
1416
|
+
for (const path3 of options.proofs) {
|
|
1417
|
+
const proofFile = readProofFile(path3);
|
|
1418
|
+
proofs.push(proofFile.proof);
|
|
1419
|
+
}
|
|
1420
|
+
if (format === "human") {
|
|
1421
|
+
info(`Loaded ${proofs.length} proofs`);
|
|
1422
|
+
proofs.forEach((p, i) => {
|
|
1423
|
+
keyValue(` Proof ${i + 1}`, `${formatProofSystem(getSystemFromProof(p))} - ${formatHash(p.id)}`);
|
|
1424
|
+
});
|
|
1425
|
+
divider();
|
|
1426
|
+
}
|
|
1427
|
+
if (options.validate) {
|
|
1428
|
+
const validator = (0, import_sdk12.createCrossSystemValidator)();
|
|
1429
|
+
const systems = [...new Set(proofs.map((p) => getSystemFromProof(p)))];
|
|
1430
|
+
if (systems.length > 1) {
|
|
1431
|
+
const spin2 = format === "human" ? spinner("Validating cross-system compatibility...") : null;
|
|
1432
|
+
const report = validator.validate(proofs, {
|
|
1433
|
+
skipFieldCheck: false,
|
|
1434
|
+
skipCurveCheck: false
|
|
1435
|
+
});
|
|
1436
|
+
const errors = report.checks.filter((c) => !c.passed && c.severity === "error");
|
|
1437
|
+
if (errors.length > 0) {
|
|
1438
|
+
spin2?.fail("Compatibility check failed");
|
|
1439
|
+
if (format === "json") {
|
|
1440
|
+
json({ error: "Incompatible proof systems", report });
|
|
1441
|
+
} else {
|
|
1442
|
+
error("Proof systems are not compatible for composition");
|
|
1443
|
+
errors.forEach((e) => {
|
|
1444
|
+
error(` - ${e.name}: ${e.message}`);
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
process.exit(1);
|
|
1448
|
+
}
|
|
1449
|
+
spin2?.succeed("Compatibility validated");
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
const spin = format === "human" ? spinner(`Composing proofs (${options.template})...`) : null;
|
|
1453
|
+
const startTime = Date.now();
|
|
1454
|
+
const aggregator = (0, import_sdk12.createProofAggregator)();
|
|
1455
|
+
const mockProvider = new import_sdk12.MockProofProvider({ silent: true });
|
|
1456
|
+
await mockProvider.initialize();
|
|
1457
|
+
const getProvider = (_system) => {
|
|
1458
|
+
return mockProvider;
|
|
1459
|
+
};
|
|
1460
|
+
let result;
|
|
1461
|
+
if (options.template === "parallel") {
|
|
1462
|
+
result = await aggregator.aggregateParallel({
|
|
1463
|
+
proofs,
|
|
1464
|
+
getProvider,
|
|
1465
|
+
verifyBefore: options.validate,
|
|
1466
|
+
maxConcurrent: 4,
|
|
1467
|
+
onProgress: (event) => {
|
|
1468
|
+
if (format === "human" && spin) {
|
|
1469
|
+
const progress = Math.round(event.step / event.totalSteps * 100);
|
|
1470
|
+
spin.text = `${event.operation} (${progress}%)`;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
});
|
|
1474
|
+
} else {
|
|
1475
|
+
result = await aggregator.aggregateSequential({
|
|
1476
|
+
proofs,
|
|
1477
|
+
getProvider,
|
|
1478
|
+
verifyBefore: options.validate,
|
|
1479
|
+
onProgress: (event) => {
|
|
1480
|
+
if (format === "human" && spin) {
|
|
1481
|
+
const progress = Math.round(event.step / event.totalSteps * 100);
|
|
1482
|
+
spin.text = `${event.operation} (${progress}%)`;
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1487
|
+
const timeMs = Date.now() - startTime;
|
|
1488
|
+
if (!result.success || !result.composedProof) {
|
|
1489
|
+
spin?.fail("Composition failed");
|
|
1490
|
+
if (format === "json") {
|
|
1491
|
+
json({ error: result.error || "Unknown error" });
|
|
1492
|
+
} else {
|
|
1493
|
+
error(`Composition failed: ${result.error || "Unknown error"}`);
|
|
1494
|
+
}
|
|
1495
|
+
process.exit(1);
|
|
1496
|
+
}
|
|
1497
|
+
spin?.succeed("Composition complete");
|
|
1498
|
+
const outputData = {
|
|
1499
|
+
proof: result.composedProof,
|
|
1500
|
+
metadata: {
|
|
1501
|
+
template: options.template,
|
|
1502
|
+
inputProofs: proofs.map((p) => p.id),
|
|
1503
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1504
|
+
timeMs
|
|
1505
|
+
}
|
|
1506
|
+
};
|
|
1507
|
+
if (options.output) {
|
|
1508
|
+
writeProofFile(options.output, outputData);
|
|
1509
|
+
if (format === "human") {
|
|
1510
|
+
success(`Composed proof written to ${options.output}`);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
if (format === "json") {
|
|
1514
|
+
json(outputData);
|
|
1515
|
+
} else if (!options.output) {
|
|
1516
|
+
keyValue("Composed Proof ID", result.composedProof.id);
|
|
1517
|
+
keyValue("Component Proofs", result.composedProof.proofs.length.toString());
|
|
1518
|
+
keyValue("Strategy", result.composedProof.strategy);
|
|
1519
|
+
keyValue("Time", `${timeMs}ms`);
|
|
1520
|
+
}
|
|
1521
|
+
} catch (err) {
|
|
1522
|
+
if (format === "json") {
|
|
1523
|
+
json({ error: String(err) });
|
|
1524
|
+
} else {
|
|
1525
|
+
error(`Failed to compose proofs: ${err}`);
|
|
1526
|
+
}
|
|
1527
|
+
process.exit(1);
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
proof.command("verify").description("Verify a proof or composed proof").argument("<path>", "Proof file path (or - for stdin)").option("--strict", "Enable strict verification mode", false).option("--json", "Output as JSON", false).action(async (path3, options) => {
|
|
1531
|
+
const format = options.json ? "json" : "human";
|
|
1532
|
+
try {
|
|
1533
|
+
if (format === "human") {
|
|
1534
|
+
heading("Verify Proof");
|
|
1535
|
+
}
|
|
1536
|
+
let proofData;
|
|
1537
|
+
if (path3 === "-") {
|
|
1538
|
+
const stdinData = await readFromStdin();
|
|
1539
|
+
proofData = JSON.parse(stdinData);
|
|
1540
|
+
} else {
|
|
1541
|
+
proofData = readProofFile(path3);
|
|
1542
|
+
}
|
|
1543
|
+
const spin = format === "human" ? spinner("Verifying proof...") : null;
|
|
1544
|
+
const pipeline = (0, import_sdk12.createVerificationPipeline)();
|
|
1545
|
+
const isComposed = "proofs" in proofData.proof && Array.isArray(proofData.proof.proofs);
|
|
1546
|
+
const mockProvider = new import_sdk12.MockProofProvider({ silent: true });
|
|
1547
|
+
await mockProvider.initialize();
|
|
1548
|
+
const getProvider = (_system) => {
|
|
1549
|
+
return mockProvider;
|
|
1550
|
+
};
|
|
1551
|
+
let result;
|
|
1552
|
+
if (isComposed) {
|
|
1553
|
+
result = await pipeline.verify(proofData.proof, {
|
|
1554
|
+
getProvider,
|
|
1555
|
+
config: {}
|
|
1556
|
+
});
|
|
1557
|
+
} else {
|
|
1558
|
+
result = await pipeline.verifySingle(proofData.proof, getProvider);
|
|
1559
|
+
}
|
|
1560
|
+
if (result.valid) {
|
|
1561
|
+
spin?.succeed("Proof is valid");
|
|
1562
|
+
} else {
|
|
1563
|
+
spin?.fail("Proof verification failed");
|
|
1564
|
+
}
|
|
1565
|
+
if (format === "json") {
|
|
1566
|
+
json({
|
|
1567
|
+
valid: result.valid,
|
|
1568
|
+
isComposed,
|
|
1569
|
+
details: result
|
|
1570
|
+
});
|
|
1571
|
+
} else {
|
|
1572
|
+
keyValue("Valid", result.valid ? "Yes" : "No");
|
|
1573
|
+
keyValue("Type", isComposed ? "Composed" : "Single");
|
|
1574
|
+
if (isComposed) {
|
|
1575
|
+
const composed = proofData.proof;
|
|
1576
|
+
keyValue("Component Proofs", composed.proofs.length.toString());
|
|
1577
|
+
}
|
|
1578
|
+
if (!result.valid && "error" in result) {
|
|
1579
|
+
error(`Reason: ${result.error}`);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
if (!result.valid) {
|
|
1583
|
+
process.exit(1);
|
|
1584
|
+
}
|
|
1585
|
+
} catch (err) {
|
|
1586
|
+
if (format === "json") {
|
|
1587
|
+
json({ valid: false, error: String(err) });
|
|
1588
|
+
} else {
|
|
1589
|
+
error(`Failed to verify proof: ${err}`);
|
|
1590
|
+
}
|
|
1591
|
+
process.exit(1);
|
|
1592
|
+
}
|
|
1593
|
+
});
|
|
1594
|
+
proof.command("inspect").description("Analyze and display proof details").argument("<path>", "Proof file path (or - for stdin)").option("--json", "Output as JSON", false).option("--full", "Show full proof data (not truncated)", false).action(async (path3, options) => {
|
|
1595
|
+
const format = options.json ? "json" : "human";
|
|
1596
|
+
try {
|
|
1597
|
+
if (format === "human") {
|
|
1598
|
+
heading("Proof Inspection");
|
|
1599
|
+
}
|
|
1600
|
+
let proofData;
|
|
1601
|
+
if (path3 === "-") {
|
|
1602
|
+
const stdinData = await readFromStdin();
|
|
1603
|
+
proofData = JSON.parse(stdinData);
|
|
1604
|
+
} else {
|
|
1605
|
+
proofData = readProofFile(path3);
|
|
1606
|
+
}
|
|
1607
|
+
const proof2 = proofData.proof;
|
|
1608
|
+
const isComposed = "proofs" in proof2 && Array.isArray(proof2.proofs);
|
|
1609
|
+
if (format === "json") {
|
|
1610
|
+
json({
|
|
1611
|
+
type: isComposed ? "composed" : "single",
|
|
1612
|
+
...proofData
|
|
1613
|
+
});
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
keyValue("Type", isComposed ? "Composed Proof" : "Single Proof");
|
|
1617
|
+
keyValue("ID", proof2.id);
|
|
1618
|
+
divider();
|
|
1619
|
+
if (isComposed) {
|
|
1620
|
+
const composed = proof2;
|
|
1621
|
+
keyValue("Strategy", composed.strategy);
|
|
1622
|
+
keyValue("Status", composed.status);
|
|
1623
|
+
keyValue("Component Proofs", composed.proofs.length.toString());
|
|
1624
|
+
info("\nComponent Proofs:");
|
|
1625
|
+
table(
|
|
1626
|
+
["#", "System", "ID", "Public Inputs"],
|
|
1627
|
+
composed.proofs.map((p, i) => [
|
|
1628
|
+
(i + 1).toString(),
|
|
1629
|
+
formatProofSystem(getSystemFromProof(p)),
|
|
1630
|
+
formatHash(p.id),
|
|
1631
|
+
p.publicInputs.length.toString()
|
|
1632
|
+
])
|
|
1633
|
+
);
|
|
1634
|
+
if (composed.compositionMetadata) {
|
|
1635
|
+
divider();
|
|
1636
|
+
info("Composition Metadata:");
|
|
1637
|
+
keyValue(" Proof Count", composed.compositionMetadata.proofCount.toString());
|
|
1638
|
+
keyValue(" Systems", composed.compositionMetadata.systems.join(", "));
|
|
1639
|
+
keyValue(" Composition Time", `${composed.compositionMetadata.compositionTimeMs}ms`);
|
|
1640
|
+
}
|
|
1641
|
+
} else {
|
|
1642
|
+
const single = proof2;
|
|
1643
|
+
keyValue("System", formatProofSystem(getSystemFromProof(single)));
|
|
1644
|
+
if (single.metadata) {
|
|
1645
|
+
keyValue("Circuit ID", single.metadata.circuitId || "N/A");
|
|
1646
|
+
keyValue("Circuit Version", single.metadata.circuitVersion || "N/A");
|
|
1647
|
+
keyValue("System Version", single.metadata.systemVersion || "N/A");
|
|
1648
|
+
keyValue("Proof Size", `${single.metadata.proofSizeBytes} bytes`);
|
|
1649
|
+
if (single.metadata.generatedAt) {
|
|
1650
|
+
keyValue("Generated At", new Date(single.metadata.generatedAt).toISOString());
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
divider();
|
|
1654
|
+
info("Proof Data:");
|
|
1655
|
+
if (options.full) {
|
|
1656
|
+
keyValue("Proof", single.proof);
|
|
1657
|
+
} else {
|
|
1658
|
+
keyValue("Proof", formatHash(single.proof, 32));
|
|
1659
|
+
}
|
|
1660
|
+
info("\nPublic Inputs:");
|
|
1661
|
+
if (single.publicInputs && single.publicInputs.length > 0) {
|
|
1662
|
+
single.publicInputs.forEach((input, i) => {
|
|
1663
|
+
keyValue(` [${i}]`, formatHash(input, 16));
|
|
1664
|
+
});
|
|
1665
|
+
} else {
|
|
1666
|
+
info(" (none)");
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
if (proofData.metadata) {
|
|
1670
|
+
divider();
|
|
1671
|
+
info("File Metadata:");
|
|
1672
|
+
Object.entries(proofData.metadata).forEach(([key, value]) => {
|
|
1673
|
+
keyValue(` ${key}`, typeof value === "object" ? JSON.stringify(value) : String(value));
|
|
1674
|
+
});
|
|
1675
|
+
}
|
|
1676
|
+
} catch (err) {
|
|
1677
|
+
if (format === "json") {
|
|
1678
|
+
json({ error: String(err) });
|
|
1679
|
+
} else {
|
|
1680
|
+
error(`Failed to inspect proof: ${err}`);
|
|
1681
|
+
}
|
|
1682
|
+
process.exit(1);
|
|
1683
|
+
}
|
|
1684
|
+
});
|
|
1685
|
+
proof.command("convert").description("Convert proof between formats").argument("<path>", "Source proof file path (or - for stdin)").requiredOption("-t, --to <format>", "Target format (sip|noir|halo2|kimchi|json)").option("-o, --output <path>", "Output file path (default: stdout)").option("--json", "Output as JSON", false).action(async (path3, options) => {
|
|
1686
|
+
const format = options.json ? "json" : "human";
|
|
1687
|
+
try {
|
|
1688
|
+
if (format === "human") {
|
|
1689
|
+
heading("Convert Proof Format");
|
|
1690
|
+
}
|
|
1691
|
+
let proofData;
|
|
1692
|
+
if (path3 === "-") {
|
|
1693
|
+
const stdinData = await readFromStdin();
|
|
1694
|
+
proofData = JSON.parse(stdinData);
|
|
1695
|
+
} else {
|
|
1696
|
+
proofData = readProofFile(path3);
|
|
1697
|
+
}
|
|
1698
|
+
const spin = format === "human" ? spinner(`Converting to ${options.to} format...`) : null;
|
|
1699
|
+
const converter = new import_sdk12.UnifiedProofConverter();
|
|
1700
|
+
const targetFormat = options.to.toLowerCase();
|
|
1701
|
+
const single = proofData.proof;
|
|
1702
|
+
let result;
|
|
1703
|
+
if (targetFormat === "json") {
|
|
1704
|
+
result = proofData;
|
|
1705
|
+
} else if (targetFormat === "sip") {
|
|
1706
|
+
result = proofData;
|
|
1707
|
+
} else {
|
|
1708
|
+
const converted = converter.fromSIP(single);
|
|
1709
|
+
if (!converted.success || !converted.result) {
|
|
1710
|
+
throw new Error(converted.error || "Conversion failed");
|
|
1711
|
+
}
|
|
1712
|
+
result = converted.result;
|
|
1713
|
+
}
|
|
1714
|
+
spin?.succeed("Conversion complete");
|
|
1715
|
+
const outputData = {
|
|
1716
|
+
originalFormat: getSystemFromProof(single),
|
|
1717
|
+
targetFormat,
|
|
1718
|
+
proof: result,
|
|
1719
|
+
convertedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1720
|
+
};
|
|
1721
|
+
if (options.output) {
|
|
1722
|
+
(0, import_fs.writeFileSync)(options.output, JSON.stringify(outputData, null, 2));
|
|
1723
|
+
if (format === "human") {
|
|
1724
|
+
success(`Converted proof written to ${options.output}`);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
if (format === "json" || !options.output) {
|
|
1728
|
+
json(outputData);
|
|
1729
|
+
}
|
|
1730
|
+
} catch (err) {
|
|
1731
|
+
if (format === "json") {
|
|
1732
|
+
json({ error: String(err) });
|
|
1733
|
+
} else {
|
|
1734
|
+
error(`Failed to convert proof: ${err}`);
|
|
1735
|
+
}
|
|
1736
|
+
process.exit(1);
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
proof.command("systems").description("List supported proof systems and their capabilities").option("--json", "Output as JSON", false).action(async (options) => {
|
|
1740
|
+
const format = options.json ? "json" : "human";
|
|
1741
|
+
const systems = [
|
|
1742
|
+
{
|
|
1743
|
+
id: "noir",
|
|
1744
|
+
name: "Noir (Aztec)",
|
|
1745
|
+
curve: "BN254",
|
|
1746
|
+
features: ["SNARK", "Universal Setup", "Browser Support"],
|
|
1747
|
+
status: "Production"
|
|
1748
|
+
},
|
|
1749
|
+
{
|
|
1750
|
+
id: "halo2",
|
|
1751
|
+
name: "Halo2 (Zcash)",
|
|
1752
|
+
curve: "Pallas/Vesta",
|
|
1753
|
+
features: ["SNARK", "No Trusted Setup", "Recursive"],
|
|
1754
|
+
status: "Production"
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
id: "kimchi",
|
|
1758
|
+
name: "Kimchi (Mina)",
|
|
1759
|
+
curve: "Pasta",
|
|
1760
|
+
features: ["SNARK", "Recursive", "Succinct"],
|
|
1761
|
+
status: "Production"
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
id: "groth16",
|
|
1765
|
+
name: "Groth16",
|
|
1766
|
+
curve: "BN254",
|
|
1767
|
+
features: ["SNARK", "Smallest Proofs", "Fastest Verification"],
|
|
1768
|
+
status: "Supported"
|
|
1769
|
+
},
|
|
1770
|
+
{
|
|
1771
|
+
id: "plonk",
|
|
1772
|
+
name: "PLONK",
|
|
1773
|
+
curve: "BN254",
|
|
1774
|
+
features: ["SNARK", "Universal Setup", "Flexible"],
|
|
1775
|
+
status: "Supported"
|
|
1776
|
+
},
|
|
1777
|
+
{
|
|
1778
|
+
id: "stark",
|
|
1779
|
+
name: "STARK",
|
|
1780
|
+
curve: "None (Hash-based)",
|
|
1781
|
+
features: ["No Trusted Setup", "Post-Quantum", "Large Proofs"],
|
|
1782
|
+
status: "Experimental"
|
|
1783
|
+
}
|
|
1784
|
+
];
|
|
1785
|
+
if (format === "json") {
|
|
1786
|
+
json({ systems });
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
heading("Supported Proof Systems");
|
|
1790
|
+
table(
|
|
1791
|
+
["System", "Curve", "Status"],
|
|
1792
|
+
systems.map((s) => [s.name, s.curve, s.status])
|
|
1793
|
+
);
|
|
1794
|
+
divider();
|
|
1795
|
+
info("Features by System:");
|
|
1796
|
+
systems.forEach((s) => {
|
|
1797
|
+
keyValue(` ${s.name}`, s.features.join(", "));
|
|
1798
|
+
});
|
|
1799
|
+
});
|
|
1800
|
+
proof.command("compat").description("Check compatibility between proof systems").argument("<systems...>", "Proof systems to check (e.g., noir halo2)").option("--json", "Output as JSON", false).action(async (systems, options) => {
|
|
1801
|
+
const format = options.json ? "json" : "human";
|
|
1802
|
+
try {
|
|
1803
|
+
if (format === "human") {
|
|
1804
|
+
heading("Proof System Compatibility");
|
|
1805
|
+
}
|
|
1806
|
+
const mockProofs = systems.map((sys, i) => ({
|
|
1807
|
+
id: `mock_${sys}_${i}`,
|
|
1808
|
+
proof: "0x00",
|
|
1809
|
+
publicInputs: ["0x01"],
|
|
1810
|
+
metadata: {
|
|
1811
|
+
system: sys,
|
|
1812
|
+
systemVersion: "1.0.0",
|
|
1813
|
+
circuitId: "test",
|
|
1814
|
+
circuitVersion: "1.0.0",
|
|
1815
|
+
generatedAt: Date.now(),
|
|
1816
|
+
proofSizeBytes: 1
|
|
1817
|
+
}
|
|
1818
|
+
}));
|
|
1819
|
+
const validator = (0, import_sdk12.createCrossSystemValidator)();
|
|
1820
|
+
const report = validator.validate(mockProofs, {
|
|
1821
|
+
skipFieldCheck: false,
|
|
1822
|
+
skipCurveCheck: false
|
|
1823
|
+
});
|
|
1824
|
+
const errors = report.checks.filter((c) => !c.passed && c.severity === "error");
|
|
1825
|
+
const warnings = report.checks.filter((c) => !c.passed && c.severity === "warning");
|
|
1826
|
+
const compatible = errors.length === 0;
|
|
1827
|
+
if (format === "json") {
|
|
1828
|
+
json({
|
|
1829
|
+
systems,
|
|
1830
|
+
compatible,
|
|
1831
|
+
errors: errors.map((e) => ({ check: e.name, message: e.message })),
|
|
1832
|
+
warnings: warnings.map((w) => ({ check: w.name, message: w.message })),
|
|
1833
|
+
report
|
|
1834
|
+
});
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1837
|
+
keyValue("Systems", systems.join(", "));
|
|
1838
|
+
keyValue("Compatible", compatible ? "Yes" : "No");
|
|
1839
|
+
if (compatible) {
|
|
1840
|
+
success("These proof systems can be composed together");
|
|
1841
|
+
} else {
|
|
1842
|
+
error("These proof systems are not directly compatible");
|
|
1843
|
+
divider();
|
|
1844
|
+
info("Issues:");
|
|
1845
|
+
errors.forEach((e) => {
|
|
1846
|
+
error(` - ${e.name}: ${e.message}`);
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
if (warnings.length > 0) {
|
|
1850
|
+
divider();
|
|
1851
|
+
info("Warnings:");
|
|
1852
|
+
warnings.forEach((w) => {
|
|
1853
|
+
info(` \u26A0 ${w.name}: ${w.message}`);
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1856
|
+
if (!compatible) {
|
|
1857
|
+
process.exit(1);
|
|
1858
|
+
}
|
|
1859
|
+
} catch (err) {
|
|
1860
|
+
if (format === "json") {
|
|
1861
|
+
json({ error: String(err) });
|
|
1862
|
+
} else {
|
|
1863
|
+
error(`Failed to check compatibility: ${err}`);
|
|
1864
|
+
}
|
|
1865
|
+
process.exit(1);
|
|
1866
|
+
}
|
|
1867
|
+
});
|
|
1868
|
+
return proof;
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1009
1871
|
// src/index.ts
|
|
1010
|
-
var program = new
|
|
1872
|
+
var program = new import_commander14.Command();
|
|
1011
1873
|
program.name("sip").description("Shielded Intents Protocol (SIP) - Privacy layer for cross-chain transactions").version("0.2.0");
|
|
1012
1874
|
program.addCommand(createSetupCommand());
|
|
1013
1875
|
program.addCommand(createInitCommand());
|
|
@@ -1020,4 +1882,6 @@ program.addCommand(createVerifyCommand());
|
|
|
1020
1882
|
program.addCommand(createQuoteCommand());
|
|
1021
1883
|
program.addCommand(createSwapCommand());
|
|
1022
1884
|
program.addCommand(createScanCommand());
|
|
1885
|
+
program.addCommand(createBackendsCommand());
|
|
1886
|
+
program.addCommand(createProofCommand());
|
|
1023
1887
|
program.parse();
|