chainlesschain 0.160.1 → 0.161.2
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/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-BHiKMxFI.js → AIOps-CoZ9bIqF.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-5QD5uzWP.js → ActionButton-BvMi4awy.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D1JxsGBN.js → Analytics-hRk2ziup.js} +1 -1
- package/src/assets/web-panel/assets/{AppLayout-DykU9tOE.js → AppLayout-_JR3Gko8.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-TGBqld9c.js → Audit-D8WmaHdX.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DjpzIwA6.js → Backup-CogYVeiE.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-joQlVauq.js → BaseInput-NAp5_OPY.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-9TYfosy-.js → Chat-DkQnhjfk.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-OEOFA9GM.js → Checkbox-C9dkWb-7.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-5H5UgHJu.js → Codegen-BHyJ3j-p.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BnLUipDp.js → Col-JyQOivHb.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BF3R5GAl.js → Community-UMq5QuBA.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-C1EkTFek.js → Compact-DGlwooBJ.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CpP-ODRU.js → Compliance-2rWGO55k.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CCgGSKVR.js → Cowork-V-tDxtrt.js} +1 -1
- package/src/assets/web-panel/assets/{Cron-CZ5pjRxn.js → Cron-YgEeQvdV.js} +1 -1
- package/src/assets/web-panel/assets/{Crosschain-C0P-5sm3.js → Crosschain-Cgd5cRKn.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DPZKMApP.js → DID-swdBCdMZ.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-G-BDDAov.js → Dashboard-ClnWtxsT.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CeywCcVQ.js → Dropdown-mlITwb7d.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DuFRY867.js → Federation-DtUN3wQa.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CFSiPqbu.js → FormItemContext-BYmWDwAT.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CjVRJDLg.js → Git-Du1k1iHz.js} +1 -1
- package/src/assets/web-panel/assets/{Governance-C0lyocJc.js → Governance-B2TFaWsf.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BVSAexgk.js → Inference-Cm_hmXla.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-SE4jCwIn.js → KnowledgeGraph-DLaLMo4r.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BD5C-wTx.js → Logs-CElvIBBJ.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CL93dFBs.js → Marketplace-BlR3RCDV.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-x-Tibae-.js → McpTools-BpHkrlka.js} +1 -1
- package/src/assets/web-panel/assets/{Memory-CR8LXq37.js → Memory-DeU9ys_m.js} +1 -1
- package/src/assets/web-panel/assets/Mtc-CCJZpnJo.js +6 -0
- package/src/assets/web-panel/assets/Mtc-Cc8OJxe_.css +1 -0
- package/src/assets/web-panel/assets/{NLProgramming-B09F6gt2.js → NLProgramming-B_Tie6j1.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BYIn2GOe.js → Notes-BcpuirPj.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-CybJTFN9.js → Organization-B_SHESSc.js} +1 -1
- package/src/assets/web-panel/assets/{Overflow-W4YLQ7yY.js → Overflow-R2SOGT0l.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-kVj43R4j.js → P2P-IYYy3cEd.js} +1 -1
- package/src/assets/web-panel/assets/{Permissions-CfYE4XFJ.js → Permissions-CR1N42yW.js} +1 -1
- package/src/assets/web-panel/assets/{Pipeline-BVLo32Ak.js → Pipeline-nwFpKsU_.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Efyb3xpJ.js → Privacy-BGpz72PX.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectSettings-cBqrIhNN.js → ProjectSettings-CEDhpgbs.js} +1 -1
- package/src/assets/web-panel/assets/{Projects-BYY38oZd.js → Projects-DABi6ylb.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BsS27cWs.js → Providers-HzrcE8ma.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BvMXwWFN.js → Recommend-BPhQwye7.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-DmwTtBfl.js → Reputation-BL6hTN1s.js} +1 -1
- package/src/assets/web-panel/assets/{Row-N-X7EJ3w.js → Row-2akLU3YS.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-D9TjnwgF.js → RssFeed-D6qNq6Ht.js} +1 -1
- package/src/assets/web-panel/assets/{Search-Hapv-QkV.js → Search-R_b-u9oL.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DWbFJK10.js → Security-DdW4hu_4.js} +1 -1
- package/src/assets/web-panel/assets/{Services-BPUmhVoH.js → Services-CnzEzGFN.js} +1 -1
- package/src/assets/web-panel/assets/{Skeleton-Bo5qPHbE.js → Skeleton-D6RevdW2.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-JJ8uInMW.js → Skills-DD5ReHH7.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CEDF9zdV.js → Sla-CaQOOsjD.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-oIoX_vCx.js → SpeechSettings-D-pGIn9Z.js} +1 -1
- package/src/assets/web-panel/assets/{Tasks-Cx5wgv5Z.js → Tasks-BbdO_i4Q.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BomcBlkN.js → Templates-CupAugDn.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BxSQZUNh.js → Tenant-rseAzHcY.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BlPPoB3C.js → Tokens-DXMokNbR.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-Bhjmjsc5.js → Trigger-DkSZjOlY.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-Dsjv7rkb.js → Trust-CKb7QDH1.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BsVR1PN8.js → VideoEditing-CSOjdBZg.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-dcRAYsdL.js → Wallet-DHbi5dHt.js} +1 -1
- package/src/assets/web-panel/assets/{WebAuthn-oqIS5PCi.js → WebAuthn-BkGDI33-.js} +1 -1
- package/src/assets/web-panel/assets/{WorkflowEditor-C_fYMBvB.js → WorkflowEditor-BFZ3RYva.js} +1 -1
- package/src/assets/web-panel/assets/{colors-D2P6CqS5.js → colors-D2tTvuDI.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CG7qutT_.js → compact-item-CqCEUZiy.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-y4UPKgbA.js → createContext-C6HFlAQP.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-Butbu9jZ.js → hasIn-yp2CbhYc.js} +1 -1
- package/src/assets/web-panel/assets/index-4cn1LmJ9.js +1 -0
- package/src/assets/web-panel/assets/{index-BtuwtDUE.js → index-B0jkl2Zb.js} +1 -1
- package/src/assets/web-panel/assets/{index-YmGOWX7h.js → index-B2qFUwGb.js} +1 -1
- package/src/assets/web-panel/assets/{index-BEfvpbz-.js → index-B74gWYqD.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJN_3RTO.js → index-B8Qxu0q2.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2K61jP8.js → index-B9b_mz4I.js} +1 -1
- package/src/assets/web-panel/assets/index-BAA1SFp1.js +1 -0
- package/src/assets/web-panel/assets/{index-rIbVsjde.js → index-BJeE7n_I.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ceaxjpqh.js → index-BJoK7MkB.js} +1 -1
- package/src/assets/web-panel/assets/{index-CYlDKn3O.js → index-BTL2yIvT.js} +1 -1
- package/src/assets/web-panel/assets/{index-DrVnyYpX.js → index-BlVnFOFL.js} +1 -1
- package/src/assets/web-panel/assets/{index-89HJLKZ-.js → index-Blq49aTW.js} +1 -1
- package/src/assets/web-panel/assets/{index-vC5cTycG.js → index-BxSsO6Sm.js} +1 -1
- package/src/assets/web-panel/assets/{index-38mVlGHc.js → index-CA3g3EpL.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLMJy9pE.js → index-CFoFkVUt.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtNHlrxp.js → index-CWhXxdyo.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bs9aHxDD.js → index-CXf0zL5i.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQr8Y0o5.js → index-CcwZodUl.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdgjeX4z.js → index-Cd6m6ynF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQfow_sh.js → index-Cfy9l115.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1mK1Ga3.js → index-CijiVpfO.js} +1 -1
- package/src/assets/web-panel/assets/{index-hSilB_Q-.js → index-Cpfx7-LN.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYZPJS7A.js → index-CrEEL63u.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_8hWf5_.js → index-CucxAdwN.js} +1 -1
- package/src/assets/web-panel/assets/{index-qXvwlbkq.js → index-CvuBD5TK.js} +1 -1
- package/src/assets/web-panel/assets/{index-CyqU4Tck.js → index-Cw4v7ezB.js} +3 -3
- package/src/assets/web-panel/assets/{index-B6U6cYUa.js → index-D34iabcS.js} +1 -1
- package/src/assets/web-panel/assets/{index-BvJgRWBq.js → index-DEKuiAPQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cc77JZKd.js → index-Dj4P0iWm.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAeKBs9n.js → index-DjeNNVwu.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4Jfv4EB.js → index-DlgMVieO.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWh3IxEh.js → index-DnehXcB-.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ci6jXp3l.js → index-JszcDpsT.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCQ0WlB2.js → index-KdFFI-p3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnI4Aq0q.js → index-NNymVAza.js} +1 -1
- package/src/assets/web-panel/assets/{index-gWmZm8_Q.js → index-aw2DwKj-.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dx_ZTZo_.js → index-nzDx0JAR.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1ucrJLg.js → index-w1xShUDf.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CZRZ-1bk.js → initDefaultProps-DfdVxwz6.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CvU8SiWF.js → motion-CL0bdvJg.js} +1 -1
- package/src/assets/web-panel/assets/{move-ipAfWhya.js → move-Bo9Fgzv7.js} +1 -1
- package/src/assets/web-panel/assets/{omit-D6bJEjz9.js → omit-C-cc6wHr.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Dpvzf7sL.js → pickAttrs-CjLp5RN-.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-D_tEolP1.js → placementArrow-CJa8gsqa.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BEFI7neO.js → responsiveObserve-CcDj3P-p.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Bte_KOqM.js → slide-CpvbHO26.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-K4xaDRuO.js → statusUtils-BK69kP1U.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Cl9YgOVY.js → styleChecker-CDvBRzsG.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DNstl1wK.js → useFlexGapSupport-CRN_hzJt.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-ChB-8cXr.js → vnode-Bzp-FsbB.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-meTNBulL.js → zoom-BHqpWXJV.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/crosschain.js +564 -8
- package/src/commands/mtc.js +1334 -0
- package/src/lib/cross-chain-mtc.js +904 -0
- package/src/assets/web-panel/assets/Mtc-C-PfF5B3.css +0 -1
- package/src/assets/web-panel/assets/Mtc-CEtRtMcc.js +0 -1
- package/src/assets/web-panel/assets/index-B5FRjJMb.js +0 -1
- package/src/assets/web-panel/assets/index-B7FV5EnN.js +0 -1
|
@@ -2,8 +2,30 @@
|
|
|
2
2
|
* `cc crosschain` — CLI surface for Phase 89 Cross-Chain Interoperability.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
|
|
5
7
|
import { Command } from "commander";
|
|
6
8
|
|
|
9
|
+
import { bootstrap } from "../runtime/bootstrap.js";
|
|
10
|
+
import { getHomeDir } from "../lib/paths.js";
|
|
11
|
+
import {
|
|
12
|
+
getCrossChainMtcDir,
|
|
13
|
+
getBridgeMtcStatus,
|
|
14
|
+
loadCrossChainMtcConfig,
|
|
15
|
+
addTrustAnchor,
|
|
16
|
+
listTrustAnchors,
|
|
17
|
+
removeTrustAnchor,
|
|
18
|
+
verifyBridgeEnvelope,
|
|
19
|
+
assembleBridgeBatch,
|
|
20
|
+
stageBridgeOp,
|
|
21
|
+
closeBatch,
|
|
22
|
+
buildMultiHopBridgeEnvelope,
|
|
23
|
+
verifyMultiHopBridgeEnvelope,
|
|
24
|
+
shouldCloseBatchGasAware,
|
|
25
|
+
getBridgeMtcSlaMetrics,
|
|
26
|
+
} from "../lib/cross-chain-mtc.js";
|
|
27
|
+
import mtcLib from "@chainlesschain/core-mtc";
|
|
28
|
+
|
|
7
29
|
import {
|
|
8
30
|
SUPPORTED_CHAINS,
|
|
9
31
|
BRIDGE_STATUS,
|
|
@@ -48,16 +70,64 @@ import {
|
|
|
48
70
|
} from "../lib/cross-chain.js";
|
|
49
71
|
|
|
50
72
|
function _dbFromCtx(cmd) {
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
// cmd is typically the action subcommand (e.g. `bridge`).
|
|
74
|
+
// _db may be wired onto cmd itself, the parent (`cc crosschain` group),
|
|
75
|
+
// or the root program — return the first one we find.
|
|
76
|
+
return cmd?._db ?? cmd?.parent?._db ?? cmd?.parent?.parent?._db ?? null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Bootstrap a real DB when the crosschain command is invoked headless
|
|
81
|
+
* (e.g. via spawnSync in integration tests). Falls back to whatever the
|
|
82
|
+
* REPL/runtime already wired into _dbFromCtx if present.
|
|
83
|
+
*/
|
|
84
|
+
async function _ensureDb(thisCmd) {
|
|
85
|
+
const existing = _dbFromCtx(thisCmd);
|
|
86
|
+
if (existing) {
|
|
87
|
+
ensureCrossChainTables(existing);
|
|
88
|
+
return existing;
|
|
89
|
+
}
|
|
90
|
+
const ctx = await bootstrap({ verbose: false });
|
|
91
|
+
if (!ctx.db) return null;
|
|
92
|
+
const db = ctx.db.getDatabase();
|
|
93
|
+
ensureCrossChainTables(db);
|
|
94
|
+
thisCmd._db = db;
|
|
95
|
+
return db;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* --mtc opt-in hook: when caller passes --mtc and the action succeeded,
|
|
100
|
+
* stage one bridge op for the next batch close. Silent best-effort —
|
|
101
|
+
* an MTC failure must not break the existing crosschain flow.
|
|
102
|
+
*/
|
|
103
|
+
function _maybeStageMtc(opts, opBuilder) {
|
|
104
|
+
if (!opts || !opts.mtc) return null;
|
|
105
|
+
try {
|
|
106
|
+
const home = opts.mtcConfigDir || getHomeDir();
|
|
107
|
+
const dir = getCrossChainMtcDir(home);
|
|
108
|
+
return stageBridgeOp(dir, opBuilder(), { requireEnabled: false });
|
|
109
|
+
} catch (err) {
|
|
110
|
+
return { staged: false, path: null, reason: `STAGE_ERROR: ${err.message}` };
|
|
111
|
+
}
|
|
53
112
|
}
|
|
54
113
|
|
|
55
114
|
export function registerCrossChainCommand(program) {
|
|
56
115
|
const cc = new Command("crosschain")
|
|
57
116
|
.description("Cross-chain interoperability (Phase 89)")
|
|
58
|
-
.hook("preAction", (thisCmd) => {
|
|
59
|
-
|
|
60
|
-
|
|
117
|
+
.hook("preAction", async (thisCmd, actionCommand) => {
|
|
118
|
+
// Skip db bootstrap for MTC-only subcommands that don't touch the
|
|
119
|
+
// crosschain DB — keeps `cc crosschain mtc-status` etc. usable on
|
|
120
|
+
// a fresh box where the SQLite store hasn't been initialized.
|
|
121
|
+
const name = actionCommand?.name?.() || "";
|
|
122
|
+
if (
|
|
123
|
+
name.startsWith("mtc-") ||
|
|
124
|
+
name === "chains" ||
|
|
125
|
+
name === "bridge-statuses" ||
|
|
126
|
+
name === "swap-statuses"
|
|
127
|
+
) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
await _ensureDb(thisCmd);
|
|
61
131
|
});
|
|
62
132
|
|
|
63
133
|
/* ── Catalogs ────────────────────────────────────── */
|
|
@@ -99,6 +169,11 @@ export function registerCrossChainCommand(program) {
|
|
|
99
169
|
.option("-a, --asset <asset>", "Asset name", "native")
|
|
100
170
|
.option("-s, --sender <address>", "Sender address")
|
|
101
171
|
.option("-r, --recipient <address>", "Recipient address")
|
|
172
|
+
.option(
|
|
173
|
+
"--mtc",
|
|
174
|
+
"On success, stage an MTC bridge envelope (requires cc crosschain mtc-batch to close)",
|
|
175
|
+
)
|
|
176
|
+
.option("--mtc-config-dir <dir>", "Override MTC config root")
|
|
102
177
|
.option("--json", "JSON output")
|
|
103
178
|
.action((fromChain, toChain, amount, opts) => {
|
|
104
179
|
const db = _dbFromCtx(cc);
|
|
@@ -110,10 +185,27 @@ export function registerCrossChainCommand(program) {
|
|
|
110
185
|
senderAddress: opts.sender,
|
|
111
186
|
recipientAddress: opts.recipient,
|
|
112
187
|
});
|
|
113
|
-
|
|
188
|
+
const mtc =
|
|
189
|
+
result.bridgeId &&
|
|
190
|
+
_maybeStageMtc(opts, () => ({
|
|
191
|
+
bridge_op: "lock",
|
|
192
|
+
src_chain: fromChain,
|
|
193
|
+
dst_chain: toChain,
|
|
194
|
+
src_tx_hash: result.bridgeId,
|
|
195
|
+
amount: String(amount),
|
|
196
|
+
asset: opts.asset || "native",
|
|
197
|
+
src_did: opts.sender || null,
|
|
198
|
+
dst_did: opts.recipient || null,
|
|
199
|
+
issued_at: new Date().toISOString(),
|
|
200
|
+
}));
|
|
201
|
+
if (opts.json)
|
|
202
|
+
return console.log(JSON.stringify({ ...result, mtc }, null, 2));
|
|
114
203
|
if (result.bridgeId)
|
|
115
204
|
console.log(`Bridge created: ${result.bridgeId} (fee: ${result.fee})`);
|
|
116
205
|
else console.log(`Failed: ${result.reason}`);
|
|
206
|
+
if (mtc?.staged) console.log(`MTC envelope staged: ${mtc.path}`);
|
|
207
|
+
else if (mtc && !mtc.staged)
|
|
208
|
+
console.log(`MTC stage skipped: ${mtc.reason}`);
|
|
117
209
|
});
|
|
118
210
|
|
|
119
211
|
cc.command("bridge-status <bridge-id> <status>")
|
|
@@ -184,6 +276,8 @@ export function registerCrossChainCommand(program) {
|
|
|
184
276
|
.option("-b, --to-asset <asset>", "Target asset", "native")
|
|
185
277
|
.option("-c, --counterparty <address>", "Counterparty address")
|
|
186
278
|
.option("-t, --timeout <ms>", "Timeout in ms", parseInt)
|
|
279
|
+
.option("--mtc", "On success, stage an MTC swap-init envelope")
|
|
280
|
+
.option("--mtc-config-dir <dir>", "Override MTC config root")
|
|
187
281
|
.option("--json", "JSON output")
|
|
188
282
|
.action((fromChain, toChain, amount, opts) => {
|
|
189
283
|
const db = _dbFromCtx(cc);
|
|
@@ -196,12 +290,27 @@ export function registerCrossChainCommand(program) {
|
|
|
196
290
|
counterpartyAddress: opts.counterparty,
|
|
197
291
|
timeoutMs: opts.timeout,
|
|
198
292
|
});
|
|
199
|
-
|
|
293
|
+
const mtc =
|
|
294
|
+
result.swapId &&
|
|
295
|
+
_maybeStageMtc(opts, () => ({
|
|
296
|
+
bridge_op: "swap-init",
|
|
297
|
+
src_chain: fromChain,
|
|
298
|
+
dst_chain: toChain,
|
|
299
|
+
swap_id: result.swapId,
|
|
300
|
+
amount: String(amount),
|
|
301
|
+
asset: opts.fromAsset || "native",
|
|
302
|
+
issued_at: new Date().toISOString(),
|
|
303
|
+
}));
|
|
304
|
+
if (opts.json)
|
|
305
|
+
return console.log(JSON.stringify({ ...result, mtc }, null, 2));
|
|
200
306
|
if (result.swapId) {
|
|
201
307
|
console.log(`Swap initiated: ${result.swapId}`);
|
|
202
308
|
console.log(`Hash lock: ${result.hashLock}`);
|
|
203
309
|
console.log(`Expires: ${new Date(result.expiresAt).toISOString()}`);
|
|
204
310
|
} else console.log(`Failed: ${result.reason}`);
|
|
311
|
+
if (mtc?.staged) console.log(`MTC envelope staged: ${mtc.path}`);
|
|
312
|
+
else if (mtc && !mtc.staged)
|
|
313
|
+
console.log(`MTC stage skipped: ${mtc.reason}`);
|
|
205
314
|
});
|
|
206
315
|
|
|
207
316
|
cc.command("swap-claim <swap-id>")
|
|
@@ -292,6 +401,8 @@ export function registerCrossChainCommand(program) {
|
|
|
292
401
|
.description("Send cross-chain message")
|
|
293
402
|
.option("-p, --payload <text>", "Message payload")
|
|
294
403
|
.option("-c, --contract <address>", "Target contract address")
|
|
404
|
+
.option("--mtc", "On success, stage an MTC msg-send envelope")
|
|
405
|
+
.option("--mtc-config-dir <dir>", "Override MTC config root")
|
|
295
406
|
.option("--json", "JSON output")
|
|
296
407
|
.action((fromChain, toChain, opts) => {
|
|
297
408
|
const db = _dbFromCtx(cc);
|
|
@@ -301,9 +412,25 @@ export function registerCrossChainCommand(program) {
|
|
|
301
412
|
payload: opts.payload,
|
|
302
413
|
targetContract: opts.contract,
|
|
303
414
|
});
|
|
304
|
-
|
|
415
|
+
const mtc =
|
|
416
|
+
result.messageId &&
|
|
417
|
+
_maybeStageMtc(opts, () => ({
|
|
418
|
+
bridge_op: "msg-send",
|
|
419
|
+
src_chain: fromChain,
|
|
420
|
+
dst_chain: toChain,
|
|
421
|
+
src_tx_hash: result.messageId,
|
|
422
|
+
msg_payload: opts.payload
|
|
423
|
+
? Buffer.from(opts.payload, "utf-8").toString("base64url")
|
|
424
|
+
: null,
|
|
425
|
+
issued_at: new Date().toISOString(),
|
|
426
|
+
}));
|
|
427
|
+
if (opts.json)
|
|
428
|
+
return console.log(JSON.stringify({ ...result, mtc }, null, 2));
|
|
305
429
|
if (result.messageId) console.log(`Message sent: ${result.messageId}`);
|
|
306
430
|
else console.log(`Failed: ${result.reason}`);
|
|
431
|
+
if (mtc?.staged) console.log(`MTC envelope staged: ${mtc.path}`);
|
|
432
|
+
else if (mtc && !mtc.staged)
|
|
433
|
+
console.log(`MTC stage skipped: ${mtc.reason}`);
|
|
307
434
|
});
|
|
308
435
|
|
|
309
436
|
cc.command("msg-status <message-id> <status>")
|
|
@@ -396,6 +523,15 @@ export function registerCrossChainCommand(program) {
|
|
|
396
523
|
console.log(`Messages: ${stats.messages.total}`);
|
|
397
524
|
});
|
|
398
525
|
|
|
526
|
+
/* ══════════════════════════════════════════════════
|
|
527
|
+
* Cross-chain bridge MTC integration (跨链桥设计 v0.1)
|
|
528
|
+
* Design: docs/design/MTC_跨链桥_v1.md
|
|
529
|
+
* Status: opt-in. Default config.enabled = false. Read commands work
|
|
530
|
+
* regardless; envelope generation requires enabled=true.
|
|
531
|
+
* ══════════════════════════════════════════════════ */
|
|
532
|
+
|
|
533
|
+
registerCrossChainMtcSubcommands(cc);
|
|
534
|
+
|
|
399
535
|
/* ══════════════════════════════════════════════════
|
|
400
536
|
* Phase 89 — Cross-Chain V2 subcommands
|
|
401
537
|
* ══════════════════════════════════════════════════ */
|
|
@@ -600,6 +736,426 @@ export function registerCrossChainCommand(program) {
|
|
|
600
736
|
registerCrossChainV2Command(cc);
|
|
601
737
|
}
|
|
602
738
|
|
|
739
|
+
function _resolveBridgeMtcDir(opts) {
|
|
740
|
+
const home = (opts && opts.configDir) || getHomeDir();
|
|
741
|
+
return getCrossChainMtcDir(home);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function registerCrossChainMtcSubcommands(cc) {
|
|
745
|
+
cc.command("mtc-status")
|
|
746
|
+
.description("Show cross-chain bridge MTC config + trust anchors + batches")
|
|
747
|
+
.option(
|
|
748
|
+
"--config-dir <dir>",
|
|
749
|
+
"Override config root (default: ~/.chainlesschain)",
|
|
750
|
+
)
|
|
751
|
+
.option("--json", "JSON output")
|
|
752
|
+
.action((opts) => {
|
|
753
|
+
const dir = _resolveBridgeMtcDir(opts);
|
|
754
|
+
const s = getBridgeMtcStatus(dir);
|
|
755
|
+
if (opts.json) return console.log(JSON.stringify(s, null, 2));
|
|
756
|
+
console.log(
|
|
757
|
+
`Enabled: ${s.enabled ? "yes" : "no (opt-in via config.enabled=true)"}`,
|
|
758
|
+
);
|
|
759
|
+
console.log(`Mode: ${s.mode}`);
|
|
760
|
+
console.log(`Algorithm: ${s.alg}`);
|
|
761
|
+
console.log(`Batch interval: ${s.batch_interval_seconds}s`);
|
|
762
|
+
console.log(`Issuer: ${s.issuer}`);
|
|
763
|
+
console.log(
|
|
764
|
+
`Trust anchors: ${s.trust_anchors.total} across ${s.trust_anchors.chain_count} chain(s)`,
|
|
765
|
+
);
|
|
766
|
+
for (const [chain, count] of Object.entries(s.trust_anchors.by_chain)) {
|
|
767
|
+
console.log(` ${chain}: ${count}`);
|
|
768
|
+
}
|
|
769
|
+
console.log(
|
|
770
|
+
`Batches: ${s.batches.total}${s.batches.latest ? ` (latest: ${s.batches.latest})` : ""}`,
|
|
771
|
+
);
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
cc.command("mtc-serve")
|
|
775
|
+
.description(
|
|
776
|
+
"Run a daemon that periodically closes staged bridge ops into batches",
|
|
777
|
+
)
|
|
778
|
+
.option("--config-dir <dir>", "Override config root")
|
|
779
|
+
.option(
|
|
780
|
+
"--interval <seconds>",
|
|
781
|
+
"Batch close interval (default: config.batch_interval_seconds)",
|
|
782
|
+
parseInt,
|
|
783
|
+
)
|
|
784
|
+
.option("--once", "Close once and exit (no daemon loop)")
|
|
785
|
+
.option("--alg <alg>", "Override config alg")
|
|
786
|
+
.option("--issuer <issuer>", "Override config issuer")
|
|
787
|
+
.option("--json", "Emit per-tick JSON results to stdout")
|
|
788
|
+
.action(async (opts) => {
|
|
789
|
+
const dir = _resolveBridgeMtcDir(opts);
|
|
790
|
+
const cfg = loadCrossChainMtcConfig(dir);
|
|
791
|
+
const intervalSec = opts.interval || cfg.batch_interval_seconds;
|
|
792
|
+
const tick = () => {
|
|
793
|
+
try {
|
|
794
|
+
const result = closeBatch(dir, {
|
|
795
|
+
alg: opts.alg,
|
|
796
|
+
issuer: opts.issuer,
|
|
797
|
+
});
|
|
798
|
+
const stamp = new Date().toISOString();
|
|
799
|
+
if (opts.json) {
|
|
800
|
+
console.log(JSON.stringify({ tick_at: stamp, ...result }, null, 2));
|
|
801
|
+
} else if (!result.skipped) {
|
|
802
|
+
console.log(
|
|
803
|
+
`[${stamp}] closed ${result.batches.length} batch(es): ${result.batches
|
|
804
|
+
.map((b) => `${b.pair}#${b.seq}(${b.count})`)
|
|
805
|
+
.join(", ")}`,
|
|
806
|
+
);
|
|
807
|
+
} else {
|
|
808
|
+
console.log(`[${stamp}] (idle: ${result.skipped.reason})`);
|
|
809
|
+
}
|
|
810
|
+
} catch (err) {
|
|
811
|
+
console.error(
|
|
812
|
+
`[${new Date().toISOString()}] tick error: ${err.message}`,
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
tick();
|
|
818
|
+
if (opts.once) return;
|
|
819
|
+
|
|
820
|
+
console.log(
|
|
821
|
+
`mtc-serve: closing every ${intervalSec}s (config-dir: ${dir}). Ctrl-C to stop.`,
|
|
822
|
+
);
|
|
823
|
+
const handle = setInterval(tick, intervalSec * 1000);
|
|
824
|
+
// Graceful shutdown
|
|
825
|
+
const stop = () => {
|
|
826
|
+
clearInterval(handle);
|
|
827
|
+
console.log("mtc-serve: stopped.");
|
|
828
|
+
process.exit(0);
|
|
829
|
+
};
|
|
830
|
+
process.on("SIGINT", stop);
|
|
831
|
+
process.on("SIGTERM", stop);
|
|
832
|
+
// Keep the event loop alive forever (until signal)
|
|
833
|
+
await new Promise(() => {});
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
cc.command("mtc-batch")
|
|
837
|
+
.description(
|
|
838
|
+
"Close currently-staged bridge ops into batches (one per chain-pair)",
|
|
839
|
+
)
|
|
840
|
+
.option("--config-dir <dir>", "Override config root")
|
|
841
|
+
.option("--alg <alg>", "Override config alg (ed25519 | slh-dsa-128f)")
|
|
842
|
+
.option("--issuer <issuer>", "Override config issuer")
|
|
843
|
+
.option("--json", "JSON output")
|
|
844
|
+
.action((opts) => {
|
|
845
|
+
const dir = _resolveBridgeMtcDir(opts);
|
|
846
|
+
const result = closeBatch(dir, {
|
|
847
|
+
alg: opts.alg,
|
|
848
|
+
issuer: opts.issuer,
|
|
849
|
+
});
|
|
850
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
851
|
+
if (result.skipped) {
|
|
852
|
+
console.log(`(no batch closed: ${result.skipped.reason})`);
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
for (const b of result.batches) {
|
|
856
|
+
console.log(
|
|
857
|
+
`✓ Closed ${b.pair}#${b.seq} ${b.count} op(s) treeHead=${b.treeHeadId}`,
|
|
858
|
+
);
|
|
859
|
+
console.log(` → ${b.dir}`);
|
|
860
|
+
}
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
cc.command("mtc-envelope")
|
|
864
|
+
.description("Build a bridge MTC envelope from a JSON file of bridge ops")
|
|
865
|
+
.requiredOption("-i, --input <path>", "JSON file: array of bridge ops")
|
|
866
|
+
.requiredOption("--src-chain <chain>", "Source chain")
|
|
867
|
+
.requiredOption("--dst-chain <chain>", "Destination chain")
|
|
868
|
+
.requiredOption("--batch-seq <n>", "Batch sequence number", parseInt)
|
|
869
|
+
.option("--issuer <issuer>", "MTCA issuer (default from config)")
|
|
870
|
+
.option("--alg <alg>", "ed25519 | slh-dsa-128f")
|
|
871
|
+
.option("--config-dir <dir>", "Override config root")
|
|
872
|
+
.option("--json", "JSON output (full landmark + envelopes)")
|
|
873
|
+
.action((opts) => {
|
|
874
|
+
const dir = _resolveBridgeMtcDir(opts);
|
|
875
|
+
const cfg = loadCrossChainMtcConfig(dir);
|
|
876
|
+
const ops = JSON.parse(fs.readFileSync(opts.input, "utf-8"));
|
|
877
|
+
if (!Array.isArray(ops)) {
|
|
878
|
+
console.error("Input file must contain a JSON array of bridge ops.");
|
|
879
|
+
process.exit(2);
|
|
880
|
+
}
|
|
881
|
+
const alg = opts.alg || cfg.alg;
|
|
882
|
+
const signer = alg === "slh-dsa-128f" ? mtcLib.slhDsa : mtcLib.ed25519;
|
|
883
|
+
const keys = signer.generateKeyPair();
|
|
884
|
+
const result = assembleBridgeBatch(ops, keys, {
|
|
885
|
+
src_chain: opts.srcChain,
|
|
886
|
+
dst_chain: opts.dstChain,
|
|
887
|
+
batch_seq: opts.batchSeq,
|
|
888
|
+
issuer: opts.issuer || cfg.issuer,
|
|
889
|
+
signer,
|
|
890
|
+
});
|
|
891
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
892
|
+
console.log(`Namespace: ${result.namespace}`);
|
|
893
|
+
console.log(`Tree head id: ${result.treeHeadId}`);
|
|
894
|
+
console.log(`Tree size: ${result.envelopes.length}`);
|
|
895
|
+
console.log(`Algorithm: ${alg}`);
|
|
896
|
+
console.log(`(JSON envelope + landmark omitted; pass --json to emit)`);
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
cc.command("mtc-verify <envelope-path> <landmark-path>")
|
|
900
|
+
.description("Verify a bridge MTC envelope against a landmark file")
|
|
901
|
+
.option("--json", "JSON output")
|
|
902
|
+
.action((envPath, lmPath, opts) => {
|
|
903
|
+
const envelope = JSON.parse(fs.readFileSync(envPath, "utf-8"));
|
|
904
|
+
const landmark = JSON.parse(fs.readFileSync(lmPath, "utf-8"));
|
|
905
|
+
const cache = new mtcLib.LandmarkCache({
|
|
906
|
+
signatureVerifier: mtcLib.alwaysAcceptSignatureVerifier,
|
|
907
|
+
});
|
|
908
|
+
try {
|
|
909
|
+
cache.ingest(landmark);
|
|
910
|
+
} catch (err) {
|
|
911
|
+
const out = {
|
|
912
|
+
ok: false,
|
|
913
|
+
code: err.code || "LANDMARK_REJECT",
|
|
914
|
+
error: err.message,
|
|
915
|
+
};
|
|
916
|
+
if (opts.json) return console.log(JSON.stringify(out, null, 2));
|
|
917
|
+
console.log(`✗ Landmark ingest failed: ${err.message}`);
|
|
918
|
+
process.exit(2);
|
|
919
|
+
}
|
|
920
|
+
const result = verifyBridgeEnvelope(envelope, cache);
|
|
921
|
+
if (opts.json) {
|
|
922
|
+
console.log(JSON.stringify(result, null, 2));
|
|
923
|
+
if (!result.ok) process.exit(2);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
if (result.ok) {
|
|
927
|
+
console.log(`✓ Envelope verified.`);
|
|
928
|
+
console.log(` Bridge op: ${result.bridge_op}`);
|
|
929
|
+
console.log(` Tree size: ${result.treeHead?.tree_size}`);
|
|
930
|
+
console.log(` Issuer: ${result.treeHead?.issuer}`);
|
|
931
|
+
} else {
|
|
932
|
+
console.log(`✗ Verification failed: ${result.code}`);
|
|
933
|
+
process.exit(2);
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
// v0.2 — Multi-hop bridge envelope (envelope-of-envelope)
|
|
938
|
+
cc.command("mtc-multihop-build")
|
|
939
|
+
.description(
|
|
940
|
+
"Build a multi-hop bridge envelope from a JSON file of leg envelopes",
|
|
941
|
+
)
|
|
942
|
+
.requiredOption(
|
|
943
|
+
"-i, --input <path>",
|
|
944
|
+
"JSON file: array of single-hop bridge envelopes (≥ 2)",
|
|
945
|
+
)
|
|
946
|
+
.option("--route-id <id>", "Optional route id (default: auto-generated)")
|
|
947
|
+
.option("--total-amount <amount>", "Optional cumulative amount string")
|
|
948
|
+
.option("--asset <asset>", "Optional asset symbol")
|
|
949
|
+
.option("--out <path>", "Write multi-hop envelope to file")
|
|
950
|
+
.option("--json", "JSON output (default unless --out given)")
|
|
951
|
+
.action((opts) => {
|
|
952
|
+
try {
|
|
953
|
+
const legs = JSON.parse(fs.readFileSync(opts.input, "utf-8"));
|
|
954
|
+
const wrapper = buildMultiHopBridgeEnvelope(legs, {
|
|
955
|
+
route_id: opts.routeId,
|
|
956
|
+
total_amount: opts.totalAmount,
|
|
957
|
+
asset: opts.asset,
|
|
958
|
+
});
|
|
959
|
+
const json = JSON.stringify(wrapper, null, 2);
|
|
960
|
+
if (opts.out) {
|
|
961
|
+
fs.writeFileSync(opts.out, json, "utf-8");
|
|
962
|
+
if (opts.json) console.log(json);
|
|
963
|
+
else
|
|
964
|
+
console.log(
|
|
965
|
+
`✓ Multi-hop envelope written: ${opts.out} (${wrapper.leg_count} legs, route: ${wrapper.chain_path.join(" → ")})`,
|
|
966
|
+
);
|
|
967
|
+
} else {
|
|
968
|
+
console.log(json);
|
|
969
|
+
}
|
|
970
|
+
} catch (err) {
|
|
971
|
+
console.error(`mtc-multihop-build failed: ${err.message}`);
|
|
972
|
+
process.exit(1);
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
cc.command("mtc-multihop-verify <wrapper-path>")
|
|
977
|
+
.description(
|
|
978
|
+
"Verify a multi-hop bridge envelope against per-leg landmark files",
|
|
979
|
+
)
|
|
980
|
+
.requiredOption(
|
|
981
|
+
"--landmarks <path>",
|
|
982
|
+
"JSON file: array of {landmark} entries, one per leg in order",
|
|
983
|
+
)
|
|
984
|
+
.option("--json", "JSON output")
|
|
985
|
+
.action((wrapperPath, opts) => {
|
|
986
|
+
try {
|
|
987
|
+
const wrapper = JSON.parse(fs.readFileSync(wrapperPath, "utf-8"));
|
|
988
|
+
const lmEntries = JSON.parse(fs.readFileSync(opts.landmarks, "utf-8"));
|
|
989
|
+
const result = verifyMultiHopBridgeEnvelope(wrapper, lmEntries);
|
|
990
|
+
if (opts.json) {
|
|
991
|
+
console.log(JSON.stringify(result, null, 2));
|
|
992
|
+
if (!result.ok) process.exit(2);
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
if (result.ok) {
|
|
996
|
+
console.log(
|
|
997
|
+
`✓ Multi-hop verified (${wrapper.leg_count} legs, route: ${wrapper.chain_path.join(" → ")})`,
|
|
998
|
+
);
|
|
999
|
+
} else {
|
|
1000
|
+
console.log(
|
|
1001
|
+
`✗ Multi-hop verification failed: ${result.code || "LEG_FAIL"}`,
|
|
1002
|
+
);
|
|
1003
|
+
process.exit(2);
|
|
1004
|
+
}
|
|
1005
|
+
} catch (err) {
|
|
1006
|
+
console.error(`mtc-multihop-verify failed: ${err.message}`);
|
|
1007
|
+
process.exit(1);
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
// v0.2 — Gas-aware batch trigger advisor
|
|
1012
|
+
cc.command("mtc-gas-check <target-chain>")
|
|
1013
|
+
.description(
|
|
1014
|
+
"Heuristic: should the bridge MTCA close its batch now given current gas + staged ops?",
|
|
1015
|
+
)
|
|
1016
|
+
.requiredOption("--staged-count <n>", "Currently staged ops", parseInt)
|
|
1017
|
+
.option(
|
|
1018
|
+
"--current-gas-usd <usd>",
|
|
1019
|
+
"Observed gas cost in USD (default: chain baseline)",
|
|
1020
|
+
parseFloat,
|
|
1021
|
+
)
|
|
1022
|
+
.option(
|
|
1023
|
+
"--hard-close-floor <n>",
|
|
1024
|
+
"Always close at or above this staged count (default: 50)",
|
|
1025
|
+
parseInt,
|
|
1026
|
+
)
|
|
1027
|
+
.option(
|
|
1028
|
+
"--defer-multiplier <m>",
|
|
1029
|
+
"Defer when current_gas > baseline * this (default: 1.5)",
|
|
1030
|
+
parseFloat,
|
|
1031
|
+
)
|
|
1032
|
+
.option("--json", "JSON output")
|
|
1033
|
+
.action((targetChain, opts) => {
|
|
1034
|
+
try {
|
|
1035
|
+
const r = shouldCloseBatchGasAware({
|
|
1036
|
+
target_chain: targetChain,
|
|
1037
|
+
staged_count: opts.stagedCount,
|
|
1038
|
+
current_gas_usd: opts.currentGasUsd,
|
|
1039
|
+
hard_close_floor: opts.hardCloseFloor,
|
|
1040
|
+
defer_multiplier: opts.deferMultiplier,
|
|
1041
|
+
});
|
|
1042
|
+
if (opts.json) return console.log(JSON.stringify(r, null, 2));
|
|
1043
|
+
console.log(
|
|
1044
|
+
`${r.close ? "✓ CLOSE" : "✗ DEFER"} reason=${r.reason} baseline=$${r.baseline_usd} current=$${r.current_usd} staged=${r.staged_count}`,
|
|
1045
|
+
);
|
|
1046
|
+
} catch (err) {
|
|
1047
|
+
console.error(`mtc-gas-check failed: ${err.message}`);
|
|
1048
|
+
process.exit(1);
|
|
1049
|
+
}
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
// v0.2 — SLA Manager integration: emit SLA-shaped metrics
|
|
1053
|
+
cc.command("mtc-sla")
|
|
1054
|
+
.description(
|
|
1055
|
+
"Emit cc sla-compatible operational metrics for the bridge MTCA",
|
|
1056
|
+
)
|
|
1057
|
+
.option("--config-dir <dir>", "Override config root")
|
|
1058
|
+
.option("--json", "JSON output")
|
|
1059
|
+
.action((opts) => {
|
|
1060
|
+
const dir = _resolveBridgeMtcDir(opts);
|
|
1061
|
+
const m = getBridgeMtcSlaMetrics(dir);
|
|
1062
|
+
if (opts.json) return console.log(JSON.stringify(m, null, 2));
|
|
1063
|
+
console.log(`SLA Status: ${m.sla_status}`);
|
|
1064
|
+
console.log(`Enabled: ${m.enabled}`);
|
|
1065
|
+
console.log(`Mode: ${m.mode}`);
|
|
1066
|
+
console.log(`Staged pending: ${m.staged_pending_count}`);
|
|
1067
|
+
console.log(`Batches total: ${m.batches_total}`);
|
|
1068
|
+
console.log(`Batches last hour: ${m.batches_last_hour}`);
|
|
1069
|
+
console.log(
|
|
1070
|
+
`Last batch: ${m.seconds_since_last_batch !== null ? `${m.seconds_since_last_batch}s ago` : "—"}`,
|
|
1071
|
+
);
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
const taParent = cc
|
|
1075
|
+
.command("mtc-trust-anchor")
|
|
1076
|
+
.description("Manage Independent-mode trust anchors (per source chain)");
|
|
1077
|
+
|
|
1078
|
+
taParent
|
|
1079
|
+
.command("add <chain> <pubkey-id>")
|
|
1080
|
+
.description("Add a trust anchor for a source chain")
|
|
1081
|
+
.requiredOption("--alg <alg>", "ed25519 | slh-dsa-128f")
|
|
1082
|
+
.requiredOption("--issuer <issuer>", "MTCA issuer string for this anchor")
|
|
1083
|
+
.option("--jwk <path>", "Optional JWK file for the public key")
|
|
1084
|
+
.option("--config-dir <dir>", "Override config root")
|
|
1085
|
+
.option("--json", "JSON output")
|
|
1086
|
+
.action((chain, pubkeyId, opts) => {
|
|
1087
|
+
const dir = _resolveBridgeMtcDir(opts);
|
|
1088
|
+
let pubkeyJwk = null;
|
|
1089
|
+
if (opts.jwk) {
|
|
1090
|
+
pubkeyJwk = JSON.parse(fs.readFileSync(opts.jwk, "utf-8"));
|
|
1091
|
+
}
|
|
1092
|
+
const r = addTrustAnchor(dir, chain, {
|
|
1093
|
+
pubkey_id: pubkeyId,
|
|
1094
|
+
alg: opts.alg,
|
|
1095
|
+
issuer: opts.issuer,
|
|
1096
|
+
pubkey_jwk: pubkeyJwk,
|
|
1097
|
+
});
|
|
1098
|
+
if (opts.json) return console.log(JSON.stringify(r, null, 2));
|
|
1099
|
+
if (r.added) {
|
|
1100
|
+
console.log(`✓ Added trust anchor for ${chain}: ${pubkeyId}`);
|
|
1101
|
+
} else {
|
|
1102
|
+
console.log(`(already exists for ${chain}: ${pubkeyId})`);
|
|
1103
|
+
}
|
|
1104
|
+
console.log(`Total anchors for ${chain}: ${r.total_for_chain}`);
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
taParent
|
|
1108
|
+
.command("list [chain]")
|
|
1109
|
+
.description("List trust anchors (optionally filter by chain)")
|
|
1110
|
+
.option("--config-dir <dir>", "Override config root")
|
|
1111
|
+
.option("--json", "JSON output")
|
|
1112
|
+
.action((chain, opts) => {
|
|
1113
|
+
const dir = _resolveBridgeMtcDir(opts);
|
|
1114
|
+
const result = listTrustAnchors(dir, chain);
|
|
1115
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
1116
|
+
if (chain) {
|
|
1117
|
+
if (!result || result.length === 0) {
|
|
1118
|
+
console.log(`(no trust anchors for ${chain})`);
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
for (const a of result) {
|
|
1122
|
+
console.log(
|
|
1123
|
+
` ${a.pubkey_id} alg=${a.alg} issuer=${a.issuer} added=${a.added_at}`,
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
} else {
|
|
1127
|
+
const chains = Object.keys(result);
|
|
1128
|
+
if (chains.length === 0) {
|
|
1129
|
+
console.log("(no trust anchors)");
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
for (const c of chains) {
|
|
1133
|
+
console.log(`${c}:`);
|
|
1134
|
+
for (const a of result[c]) {
|
|
1135
|
+
console.log(` ${a.pubkey_id} alg=${a.alg} issuer=${a.issuer}`);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
taParent
|
|
1142
|
+
.command("remove <chain> <pubkey-id>")
|
|
1143
|
+
.description("Remove a trust anchor by pubkey-id")
|
|
1144
|
+
.option("--config-dir <dir>", "Override config root")
|
|
1145
|
+
.option("--json", "JSON output")
|
|
1146
|
+
.action((chain, pubkeyId, opts) => {
|
|
1147
|
+
const dir = _resolveBridgeMtcDir(opts);
|
|
1148
|
+
const r = removeTrustAnchor(dir, chain, pubkeyId);
|
|
1149
|
+
if (opts.json) return console.log(JSON.stringify(r, null, 2));
|
|
1150
|
+
if (r.removed) {
|
|
1151
|
+
console.log(`✓ Removed trust anchor for ${chain}: ${pubkeyId}`);
|
|
1152
|
+
} else {
|
|
1153
|
+
console.log(`(no matching trust anchor for ${chain}: ${pubkeyId})`);
|
|
1154
|
+
}
|
|
1155
|
+
console.log(`Remaining anchors for ${chain}: ${r.total_for_chain}`);
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
|
|
603
1159
|
import {
|
|
604
1160
|
XCHAIN_CHANNEL_MATURITY_V2,
|
|
605
1161
|
XCHAIN_TRANSFER_LIFECYCLE_V2,
|