@hardkas/artifacts 0.5.5-alpha → 0.6.1-alpha
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.d.ts +392 -33
- package/dist/index.js +341 -80
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
|
+
// package.json
|
|
2
|
+
var package_default = {
|
|
3
|
+
name: "@hardkas/artifacts",
|
|
4
|
+
version: "0.6.1-alpha",
|
|
5
|
+
type: "module",
|
|
6
|
+
license: "MIT",
|
|
7
|
+
author: "Javier Rodriguez",
|
|
8
|
+
repository: {
|
|
9
|
+
type: "git",
|
|
10
|
+
url: "git+https://github.com/KasLabDevs/HardKas.git",
|
|
11
|
+
directory: "packages/artifacts"
|
|
12
|
+
},
|
|
13
|
+
homepage: "https://github.com/KasLabDevs/HardKas/tree/main/packages/artifacts#readme",
|
|
14
|
+
bugs: {
|
|
15
|
+
url: "https://github.com/KasLabDevs/HardKas/issues"
|
|
16
|
+
},
|
|
17
|
+
files: [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
exports: {
|
|
23
|
+
".": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
types: "./dist/index.d.ts",
|
|
26
|
+
scripts: {
|
|
27
|
+
build: "tsup src/index.ts --format esm --dts --clean",
|
|
28
|
+
dev: "tsup src/index.ts --format esm --watch --dts",
|
|
29
|
+
test: "vitest run",
|
|
30
|
+
lint: "tsc --noEmit"
|
|
31
|
+
},
|
|
32
|
+
dependencies: {
|
|
33
|
+
"@hardkas/core": "workspace:*",
|
|
34
|
+
"@hardkas/tx-builder": "workspace:*",
|
|
35
|
+
zod: "^3.24.1"
|
|
36
|
+
},
|
|
37
|
+
devDependencies: {
|
|
38
|
+
tsup: "^8.3.5",
|
|
39
|
+
typescript: "^5.6.3",
|
|
40
|
+
vitest: "^2.1.4"
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
1
44
|
// src/constants.ts
|
|
2
|
-
var HARDKAS_VERSION =
|
|
45
|
+
var HARDKAS_VERSION = package_default.version;
|
|
3
46
|
var ARTIFACT_SCHEMAS = {
|
|
4
47
|
LOCALNET_STATE: "hardkas.localnetState.v1",
|
|
5
48
|
REAL_ACCOUNT_STORE: "hardkas.realAccountStore.v1",
|
|
@@ -133,6 +176,7 @@ function createIgraDeployPlanId(hash) {
|
|
|
133
176
|
|
|
134
177
|
// src/canonical.ts
|
|
135
178
|
import { createHash } from "crypto";
|
|
179
|
+
import { deterministicCompare } from "@hardkas/core";
|
|
136
180
|
var SEMANTIC_EXCLUSIONS = /* @__PURE__ */ new Set([
|
|
137
181
|
"contentHash",
|
|
138
182
|
"artifactId",
|
|
@@ -140,6 +184,8 @@ var SEMANTIC_EXCLUSIONS = /* @__PURE__ */ new Set([
|
|
|
140
184
|
"lineage",
|
|
141
185
|
"createdAt",
|
|
142
186
|
"rpcUrl",
|
|
187
|
+
"rpcHost",
|
|
188
|
+
"latencyMs",
|
|
143
189
|
"indexedAt",
|
|
144
190
|
"file_path",
|
|
145
191
|
"file_mtime_ms",
|
|
@@ -148,7 +194,15 @@ var SEMANTIC_EXCLUSIONS = /* @__PURE__ */ new Set([
|
|
|
148
194
|
// Exclude hash version from hash
|
|
149
195
|
"parentArtifactId",
|
|
150
196
|
"signedId",
|
|
151
|
-
"deployedAt"
|
|
197
|
+
"deployedAt",
|
|
198
|
+
"tracePath",
|
|
199
|
+
"receiptPath",
|
|
200
|
+
"events",
|
|
201
|
+
"status",
|
|
202
|
+
"sourceSignedId",
|
|
203
|
+
"submittedAt",
|
|
204
|
+
"confirmedAt",
|
|
205
|
+
"dagContext"
|
|
152
206
|
]);
|
|
153
207
|
var CURRENT_HASH_VERSION = 3;
|
|
154
208
|
function canonicalStringify(obj, version = CURRENT_HASH_VERSION) {
|
|
@@ -160,7 +214,7 @@ function canonicalStringify(obj, version = CURRENT_HASH_VERSION) {
|
|
|
160
214
|
return JSON.stringify(obj.toString());
|
|
161
215
|
}
|
|
162
216
|
if (typeof obj === "string" && version >= 3) {
|
|
163
|
-
const normalized = obj.normalize("NFC").replace(/\r\n/g, "\n");
|
|
217
|
+
const normalized = obj.normalize("NFC").replace(/\r\n/g, "\n").replace(/\\/g, "/");
|
|
164
218
|
return JSON.stringify(normalized);
|
|
165
219
|
}
|
|
166
220
|
return JSON.stringify(obj);
|
|
@@ -168,9 +222,22 @@ function canonicalStringify(obj, version = CURRENT_HASH_VERSION) {
|
|
|
168
222
|
if (Array.isArray(obj)) {
|
|
169
223
|
return "[" + obj.map((item) => canonicalStringify(item, version)).join(",") + "]";
|
|
170
224
|
}
|
|
225
|
+
if (obj instanceof Map) {
|
|
226
|
+
throw new Error("Map is not canonicalizable. Use a plain object.");
|
|
227
|
+
}
|
|
228
|
+
if (obj instanceof Set) {
|
|
229
|
+
throw new Error("Set is not canonicalizable. Use an array.");
|
|
230
|
+
}
|
|
231
|
+
if (obj instanceof Date) {
|
|
232
|
+
throw new Error("Date must be serialized explicitly.");
|
|
233
|
+
}
|
|
234
|
+
const proto = Object.getPrototypeOf(obj);
|
|
235
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
236
|
+
throw new Error("Non-plain object encountered in canonicalizer.");
|
|
237
|
+
}
|
|
171
238
|
const sortedKeys = Object.keys(obj).filter(
|
|
172
239
|
(key) => !SEMANTIC_EXCLUSIONS.has(key) && obj[key] !== void 0
|
|
173
|
-
).sort();
|
|
240
|
+
).sort(deterministicCompare);
|
|
174
241
|
const result = sortedKeys.map((key) => {
|
|
175
242
|
const value = obj[key];
|
|
176
243
|
return JSON.stringify(key) + ":" + canonicalStringify(value, version);
|
|
@@ -293,6 +360,7 @@ var TxReceiptSchema = BaseArtifactSchema.extend({
|
|
|
293
360
|
tracePath: z.string().optional(),
|
|
294
361
|
rpcUrl: z.string().optional(),
|
|
295
362
|
sourceSignedId: z.string().optional(),
|
|
363
|
+
errors: z.array(z.string()).optional(),
|
|
296
364
|
metadata: z.any().optional()
|
|
297
365
|
});
|
|
298
366
|
var SignedTxSchema = BaseArtifactSchema.extend({
|
|
@@ -325,6 +393,42 @@ var TxTraceSchema = BaseArtifactSchema.extend({
|
|
|
325
393
|
})),
|
|
326
394
|
dagContext: DagContextSchema.optional()
|
|
327
395
|
});
|
|
396
|
+
var WorkflowSchema = BaseArtifactSchema.extend({
|
|
397
|
+
schema: z.literal("hardkas.workflow.v1"),
|
|
398
|
+
workflowId: z.string(),
|
|
399
|
+
status: z.enum(["pending", "running", "completed", "failed"]),
|
|
400
|
+
inputs: z.record(z.any()).optional(),
|
|
401
|
+
steps: z.array(z.object({
|
|
402
|
+
type: z.string(),
|
|
403
|
+
status: z.enum(["pending", "success", "failed", "skipped"]),
|
|
404
|
+
startedAt: z.string().datetime().optional(),
|
|
405
|
+
completedAt: z.string().datetime().optional(),
|
|
406
|
+
producedArtifactId: z.string().optional(),
|
|
407
|
+
error: z.string().optional()
|
|
408
|
+
})),
|
|
409
|
+
parentArtifacts: z.array(z.string()).optional(),
|
|
410
|
+
producedArtifacts: z.array(z.string()),
|
|
411
|
+
generationRange: z.object({
|
|
412
|
+
start: z.string().optional(),
|
|
413
|
+
end: z.string().optional()
|
|
414
|
+
}).optional(),
|
|
415
|
+
policy: z.object({
|
|
416
|
+
allowNetwork: z.boolean(),
|
|
417
|
+
allowMainnet: z.boolean(),
|
|
418
|
+
allowExternalWallet: z.boolean(),
|
|
419
|
+
requireDryRun: z.boolean()
|
|
420
|
+
}).optional(),
|
|
421
|
+
generationId: z.string().optional(),
|
|
422
|
+
replayResult: z.object({
|
|
423
|
+
verified: z.boolean(),
|
|
424
|
+
stateHash: z.string().optional()
|
|
425
|
+
}).optional(),
|
|
426
|
+
errorEnvelope: z.object({
|
|
427
|
+
code: z.string(),
|
|
428
|
+
message: z.string(),
|
|
429
|
+
redacted: z.boolean()
|
|
430
|
+
}).optional()
|
|
431
|
+
});
|
|
328
432
|
|
|
329
433
|
// src/verify.ts
|
|
330
434
|
import fs from "fs";
|
|
@@ -414,8 +518,11 @@ function verifyLineage(artifact, parent, options = {}) {
|
|
|
414
518
|
};
|
|
415
519
|
const lineage = artifact.lineage;
|
|
416
520
|
if (!lineage) {
|
|
417
|
-
const
|
|
418
|
-
|
|
521
|
+
const isWorkflow = artifact.schema === "hardkas.workflow.v1";
|
|
522
|
+
const severity = options.strict && !isWorkflow ? "error" : "warning";
|
|
523
|
+
if (!isWorkflow || options.strict) {
|
|
524
|
+
addIssue("MISSING_LINEAGE", "Artifact has no lineage metadata", severity);
|
|
525
|
+
}
|
|
419
526
|
return {
|
|
420
527
|
ok: issues.every((i) => i.severity !== "error"),
|
|
421
528
|
issues
|
|
@@ -488,12 +595,20 @@ function verifyLineage(artifact, parent, options = {}) {
|
|
|
488
595
|
// src/verify.ts
|
|
489
596
|
var defaultClock = {
|
|
490
597
|
now: () => Date.now()
|
|
598
|
+
// hardkas-determinism-allow: default clock ambient source
|
|
491
599
|
};
|
|
600
|
+
function deterministicCompare2(a, b) {
|
|
601
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
602
|
+
}
|
|
492
603
|
function sortUtxosByOutpoint(utxos) {
|
|
493
604
|
return [...utxos].sort((a, b) => {
|
|
494
|
-
const
|
|
495
|
-
const
|
|
496
|
-
|
|
605
|
+
const aRec = a;
|
|
606
|
+
const bRec = b;
|
|
607
|
+
const aOutpoint = aRec.outpoint;
|
|
608
|
+
const bOutpoint = bRec.outpoint;
|
|
609
|
+
const aId = aRec.id || (aOutpoint ? `${aOutpoint.transactionId}:${aOutpoint.index}` : "");
|
|
610
|
+
const bId = bRec.id || (bOutpoint ? `${bOutpoint.transactionId}:${bOutpoint.index}` : "");
|
|
611
|
+
return deterministicCompare2(aId, bId);
|
|
497
612
|
});
|
|
498
613
|
}
|
|
499
614
|
async function verifyArtifactIntegrity(artifactOrPath) {
|
|
@@ -525,6 +640,10 @@ async function verifyArtifactIntegrity(artifactOrPath) {
|
|
|
525
640
|
result.artifactType = v.schema;
|
|
526
641
|
result.version = v.version;
|
|
527
642
|
result.expectedHash = v.contentHash;
|
|
643
|
+
if (v.schema === "hardkas.replayReport.v1") {
|
|
644
|
+
result.ok = true;
|
|
645
|
+
return result;
|
|
646
|
+
}
|
|
528
647
|
if (!v.version || !v.schema) {
|
|
529
648
|
addError("ARTIFACT_SCHEMA_MISSING", "Missing version or schema (Artifact might be v1 or legacy)");
|
|
530
649
|
return result;
|
|
@@ -560,6 +679,9 @@ async function verifyArtifactIntegrity(artifactOrPath) {
|
|
|
560
679
|
case "hardkas.signedTx":
|
|
561
680
|
schema = SignedTxSchema;
|
|
562
681
|
break;
|
|
682
|
+
case "hardkas.workflow.v1":
|
|
683
|
+
schema = WorkflowSchema;
|
|
684
|
+
break;
|
|
563
685
|
}
|
|
564
686
|
if (schema) {
|
|
565
687
|
const validation = schema.safeParse(v);
|
|
@@ -577,8 +699,10 @@ async function verifyArtifactIntegrity(artifactOrPath) {
|
|
|
577
699
|
} catch (e) {
|
|
578
700
|
if (e instanceof SyntaxError) {
|
|
579
701
|
addError("ARTIFACT_JSON_INVALID", `Invalid JSON: ${e.message}`);
|
|
580
|
-
} else {
|
|
702
|
+
} else if (e instanceof Error) {
|
|
581
703
|
addError("ARTIFACT_ID_INVALID", `Unexpected verification error: ${e.message}`);
|
|
704
|
+
} else {
|
|
705
|
+
addError("ARTIFACT_ID_INVALID", `Unexpected verification error: ${String(e)}`);
|
|
582
706
|
}
|
|
583
707
|
return result;
|
|
584
708
|
}
|
|
@@ -626,19 +750,19 @@ function verifyArtifactSemantics(artifact, context = {}) {
|
|
|
626
750
|
}
|
|
627
751
|
}
|
|
628
752
|
const lineageAudit = verifyLineage(v, context.parent, { strict });
|
|
629
|
-
if (!lineageAudit.ok || strict && !v.lineage) {
|
|
753
|
+
if (!lineageAudit.ok || strict && !v.lineage && v.schema !== "hardkas.workflow.v1") {
|
|
630
754
|
lineageAudit.issues.forEach((issue) => {
|
|
631
755
|
addIssue(issue);
|
|
632
756
|
});
|
|
633
757
|
}
|
|
634
758
|
if (strict) {
|
|
635
759
|
if (!v.workflowId) addIssue({ code: "MISSING_WORKFLOW_ID", severity: "error", message: "Strict mode requires workflowId" });
|
|
636
|
-
if (!v.assumptionLevel) addIssue({ code: "MISSING_ASSUMPTION_LEVEL", severity: "error", message: "Strict mode requires assumptionLevel" });
|
|
637
|
-
if (!v.executionMode) addIssue({ code: "MISSING_EXECUTION_MODE", severity: "error", message: "Strict mode requires executionMode" });
|
|
760
|
+
if (!v.assumptionLevel && v.schema !== "hardkas.workflow.v1") addIssue({ code: "MISSING_ASSUMPTION_LEVEL", severity: "error", message: "Strict mode requires assumptionLevel" });
|
|
761
|
+
if (!v.executionMode && !v.mode) addIssue({ code: "MISSING_EXECUTION_MODE", severity: "error", message: "Strict mode requires executionMode" });
|
|
638
762
|
} else {
|
|
639
763
|
if (!v.workflowId) addIssue({ code: "MISSING_WORKFLOW_ID", severity: "warning", message: "Missing workflowId" });
|
|
640
|
-
if (!v.assumptionLevel) addIssue({ code: "MISSING_ASSUMPTION_LEVEL", severity: "warning", message: "Missing assumptionLevel" });
|
|
641
|
-
if (!v.executionMode) addIssue({ code: "MISSING_EXECUTION_MODE", severity: "warning", message: "Missing executionMode" });
|
|
764
|
+
if (!v.assumptionLevel && v.schema !== "hardkas.workflow.v1") addIssue({ code: "MISSING_ASSUMPTION_LEVEL", severity: "warning", message: "Missing assumptionLevel" });
|
|
765
|
+
if (!v.executionMode && !v.mode) addIssue({ code: "MISSING_EXECUTION_MODE", severity: "warning", message: "Missing executionMode" });
|
|
642
766
|
}
|
|
643
767
|
const networkId = context.networkId || v.networkId;
|
|
644
768
|
const networkIdStr = networkId;
|
|
@@ -800,7 +924,7 @@ var ReplayInvariant = class {
|
|
|
800
924
|
};
|
|
801
925
|
|
|
802
926
|
// src/invariants/watcher.ts
|
|
803
|
-
import { createEventEnvelope } from "@hardkas/core";
|
|
927
|
+
import { createEventEnvelope, asWorkflowId, asCorrelationId, asNetworkId, asEventSequence } from "@hardkas/core";
|
|
804
928
|
var InvariantWatcher = class {
|
|
805
929
|
invariants;
|
|
806
930
|
eventBus;
|
|
@@ -853,9 +977,12 @@ var InvariantWatcher = class {
|
|
|
853
977
|
const integrityEvent = createEventEnvelope({
|
|
854
978
|
kind: "integrity.violation",
|
|
855
979
|
domain: "integrity",
|
|
856
|
-
workflowId:
|
|
857
|
-
correlationId: sourceEvent.correlationId,
|
|
858
|
-
networkId: sourceEvent.networkId,
|
|
980
|
+
workflowId: asWorkflowId("system-watcher"),
|
|
981
|
+
correlationId: asCorrelationId(sourceEvent.correlationId),
|
|
982
|
+
networkId: asNetworkId(sourceEvent.networkId),
|
|
983
|
+
sequenceNumber: asEventSequence(0),
|
|
984
|
+
// Root event for watcher
|
|
985
|
+
sourceSubsystem: "artifact_watcher",
|
|
859
986
|
payload: {
|
|
860
987
|
violationCode: violation.code,
|
|
861
988
|
severity: violation.severity,
|
|
@@ -869,30 +996,148 @@ var InvariantWatcher = class {
|
|
|
869
996
|
};
|
|
870
997
|
|
|
871
998
|
// src/migration.ts
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
999
|
+
var migrationRegistry = [];
|
|
1000
|
+
function registerMigrationStep(step) {
|
|
1001
|
+
const existing = migrationRegistry.find(
|
|
1002
|
+
(s) => s.fromVersion === step.fromVersion && s.toVersion === step.toVersion
|
|
1003
|
+
);
|
|
1004
|
+
if (existing) {
|
|
1005
|
+
throw new Error(
|
|
1006
|
+
`Duplicate migration step: ${step.fromVersion} \u2192 ${step.toVersion} already registered`
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
migrationRegistry.push(step);
|
|
1010
|
+
}
|
|
1011
|
+
function getRegisteredMigrationSteps() {
|
|
1012
|
+
return [...migrationRegistry];
|
|
1013
|
+
}
|
|
1014
|
+
registerMigrationStep({
|
|
1015
|
+
fromVersion: "0.1.0",
|
|
1016
|
+
toVersion: ARTIFACT_VERSION,
|
|
1017
|
+
description: "Legacy v1 schemas to canonical 1.0.0-alpha format",
|
|
1018
|
+
transform(artifact) {
|
|
1019
|
+
const migrated = { ...artifact };
|
|
1020
|
+
if (typeof migrated.schema === "string" && migrated.schema.endsWith(".v1")) {
|
|
1021
|
+
if (migrated.schema !== "hardkas.workflow.v1") {
|
|
1022
|
+
migrated.schema = migrated.schema.replace(/\.v1$/, "");
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
migrated.version = ARTIFACT_VERSION;
|
|
1026
|
+
if (migrated.schema === "hardkas.txPlan" && migrated.selectedUtxos !== void 0) {
|
|
1027
|
+
migrated.inputs = migrated.selectedUtxos;
|
|
1028
|
+
delete migrated.selectedUtxos;
|
|
1029
|
+
}
|
|
1030
|
+
if (migrated.schema === "hardkas.snapshot" && Array.isArray(migrated.utxos)) {
|
|
1031
|
+
migrated.utxos = sortUtxosByOutpoint(migrated.utxos);
|
|
1032
|
+
}
|
|
1033
|
+
if (!migrated.hardkasVersion) {
|
|
1034
|
+
migrated.hardkasVersion = HARDKAS_VERSION;
|
|
1035
|
+
}
|
|
1036
|
+
if (!migrated.createdAt) {
|
|
1037
|
+
migrated.createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1038
|
+
}
|
|
1039
|
+
if (migrated.hashVersion === void 0 || migrated.hashVersion === null) {
|
|
1040
|
+
migrated.hashVersion = CURRENT_HASH_VERSION;
|
|
1041
|
+
}
|
|
1042
|
+
return migrated;
|
|
875
1043
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1044
|
+
});
|
|
1045
|
+
function detectArtifactVersion(artifact) {
|
|
1046
|
+
if (typeof artifact.version === "string" && artifact.version.length > 0) {
|
|
1047
|
+
return artifact.version;
|
|
879
1048
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
v2Artifact.inputs = v1Artifact.selectedUtxos;
|
|
883
|
-
delete v2Artifact.selectedUtxos;
|
|
1049
|
+
if (typeof artifact.schema === "string" && artifact.schema.endsWith(".v1")) {
|
|
1050
|
+
return "0.1.0";
|
|
884
1051
|
}
|
|
885
|
-
|
|
886
|
-
|
|
1052
|
+
return "0.1.0";
|
|
1053
|
+
}
|
|
1054
|
+
function getMigrationPath(fromVersion, toVersion) {
|
|
1055
|
+
if (fromVersion === toVersion) {
|
|
1056
|
+
return [];
|
|
887
1057
|
}
|
|
888
|
-
|
|
889
|
-
|
|
1058
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1059
|
+
const queue = [
|
|
1060
|
+
{ version: fromVersion, path: [] }
|
|
1061
|
+
];
|
|
1062
|
+
visited.add(fromVersion);
|
|
1063
|
+
while (queue.length > 0) {
|
|
1064
|
+
const current = queue.shift();
|
|
1065
|
+
const outgoing = migrationRegistry.filter(
|
|
1066
|
+
(s) => s.fromVersion === current.version
|
|
1067
|
+
);
|
|
1068
|
+
for (const step of outgoing) {
|
|
1069
|
+
const newPath = [...current.path, step];
|
|
1070
|
+
if (step.toVersion === toVersion) {
|
|
1071
|
+
return newPath;
|
|
1072
|
+
}
|
|
1073
|
+
if (!visited.has(step.toVersion)) {
|
|
1074
|
+
visited.add(step.toVersion);
|
|
1075
|
+
queue.push({ version: step.toVersion, path: newPath });
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
890
1078
|
}
|
|
891
|
-
|
|
892
|
-
|
|
1079
|
+
return [];
|
|
1080
|
+
}
|
|
1081
|
+
function canMigrate(artifact, targetVersion = ARTIFACT_VERSION) {
|
|
1082
|
+
const currentVersion = detectArtifactVersion(artifact);
|
|
1083
|
+
if (currentVersion === targetVersion) {
|
|
1084
|
+
return true;
|
|
893
1085
|
}
|
|
894
|
-
|
|
895
|
-
return
|
|
1086
|
+
const path4 = getMigrationPath(currentVersion, targetVersion);
|
|
1087
|
+
return path4.length > 0;
|
|
1088
|
+
}
|
|
1089
|
+
function migrateArtifactPayload(artifact, targetVersion = ARTIFACT_VERSION) {
|
|
1090
|
+
const currentVersion = detectArtifactVersion(artifact);
|
|
1091
|
+
if (currentVersion === targetVersion) {
|
|
1092
|
+
return {
|
|
1093
|
+
artifact,
|
|
1094
|
+
migrated: false,
|
|
1095
|
+
originalContentHash: artifact.contentHash,
|
|
1096
|
+
appliedSteps: []
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
const path4 = getMigrationPath(currentVersion, targetVersion);
|
|
1100
|
+
if (path4.length === 0) {
|
|
1101
|
+
throw new Error(
|
|
1102
|
+
`No migration path from version "${currentVersion}" to "${targetVersion}". Registered steps: [${migrationRegistry.map((s) => `${s.fromVersion}\u2192${s.toVersion}`).join(", ")}]`
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
const originalContentHash = artifact.contentHash;
|
|
1106
|
+
const originalLineage = artifact.lineage;
|
|
1107
|
+
let current = { ...artifact };
|
|
1108
|
+
const appliedSteps = [];
|
|
1109
|
+
for (const step of path4) {
|
|
1110
|
+
current = step.transform(current);
|
|
1111
|
+
appliedSteps.push({
|
|
1112
|
+
fromVersion: step.fromVersion,
|
|
1113
|
+
toVersion: step.toVersion,
|
|
1114
|
+
description: step.description
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
if (originalContentHash) {
|
|
1118
|
+
current.originalContentHash = originalContentHash;
|
|
1119
|
+
}
|
|
1120
|
+
if (originalLineage && typeof originalLineage === "object") {
|
|
1121
|
+
const migratedLineage = current.lineage;
|
|
1122
|
+
if (migratedLineage && typeof migratedLineage === "object") {
|
|
1123
|
+
migratedLineage.rootArtifactId = originalLineage.rootArtifactId;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
current.hashVersion = CURRENT_HASH_VERSION;
|
|
1127
|
+
current.contentHash = calculateContentHash(current, CURRENT_HASH_VERSION);
|
|
1128
|
+
return {
|
|
1129
|
+
artifact: current,
|
|
1130
|
+
migrated: true,
|
|
1131
|
+
originalContentHash,
|
|
1132
|
+
appliedSteps
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
function migrateToCanonical(v1Artifact) {
|
|
1136
|
+
if (v1Artifact.version === ARTIFACT_VERSION) {
|
|
1137
|
+
return v1Artifact;
|
|
1138
|
+
}
|
|
1139
|
+
const result = migrateArtifactPayload(v1Artifact, ARTIFACT_VERSION);
|
|
1140
|
+
return result.artifact;
|
|
896
1141
|
}
|
|
897
1142
|
|
|
898
1143
|
// src/io.ts
|
|
@@ -968,6 +1213,17 @@ function formatTxPlanArtifact(artifact) {
|
|
|
968
1213
|
lines.push(`Outputs: ${artifact.outputs.length}`);
|
|
969
1214
|
lines.push(`Fee: ${formatSompi(BigInt(artifact.estimatedFeeSompi))}`);
|
|
970
1215
|
lines.push(`Mass: ${artifact.estimatedMass}`);
|
|
1216
|
+
lines.push("");
|
|
1217
|
+
lines.push("Deterministic Planning Specifications:");
|
|
1218
|
+
lines.push(" Coin Selection:");
|
|
1219
|
+
lines.push(" deterministic canonical ordering enabled");
|
|
1220
|
+
lines.push(" Input Ordering:");
|
|
1221
|
+
lines.push(" amountSompi ASC");
|
|
1222
|
+
lines.push(" txid ASC");
|
|
1223
|
+
lines.push(" index ASC");
|
|
1224
|
+
lines.push(" Output Ordering:");
|
|
1225
|
+
lines.push(" amountSompi ASC");
|
|
1226
|
+
lines.push(" address ASC");
|
|
971
1227
|
return lines.join("\n");
|
|
972
1228
|
}
|
|
973
1229
|
function formatTxReceiptArtifact(artifact) {
|
|
@@ -1006,40 +1262,30 @@ function formatSignedTxArtifact(artifact) {
|
|
|
1006
1262
|
|
|
1007
1263
|
// src/conversions.ts
|
|
1008
1264
|
function utxoToArtifact(utxo) {
|
|
1009
|
-
|
|
1265
|
+
return {
|
|
1010
1266
|
outpoint: {
|
|
1011
1267
|
transactionId: utxo.outpoint.transactionId,
|
|
1012
1268
|
index: utxo.outpoint.index
|
|
1013
1269
|
},
|
|
1014
1270
|
address: utxo.address,
|
|
1015
1271
|
amountSompi: utxo.amountSompi.toString(),
|
|
1016
|
-
scriptPublicKey: utxo.scriptPublicKey
|
|
1272
|
+
scriptPublicKey: utxo.scriptPublicKey,
|
|
1273
|
+
...utxo.blockDaaScore !== void 0 ? { blockDaaScore: utxo.blockDaaScore.toString() } : {},
|
|
1274
|
+
...utxo.isCoinbase !== void 0 ? { isCoinbase: utxo.isCoinbase } : {}
|
|
1017
1275
|
};
|
|
1018
|
-
if (utxo.blockDaaScore !== void 0) {
|
|
1019
|
-
artifact.blockDaaScore = utxo.blockDaaScore.toString();
|
|
1020
|
-
}
|
|
1021
|
-
if (utxo.isCoinbase !== void 0) {
|
|
1022
|
-
artifact.isCoinbase = utxo.isCoinbase;
|
|
1023
|
-
}
|
|
1024
|
-
return artifact;
|
|
1025
1276
|
}
|
|
1026
1277
|
function utxoFromArtifact(artifact) {
|
|
1027
|
-
|
|
1278
|
+
return {
|
|
1028
1279
|
outpoint: {
|
|
1029
1280
|
transactionId: artifact.outpoint.transactionId,
|
|
1030
1281
|
index: artifact.outpoint.index
|
|
1031
1282
|
},
|
|
1032
1283
|
address: artifact.address,
|
|
1033
1284
|
amountSompi: safeParseBigInt(artifact.amountSompi, "UtxoArtifact.amountSompi"),
|
|
1034
|
-
scriptPublicKey: artifact.scriptPublicKey
|
|
1285
|
+
scriptPublicKey: artifact.scriptPublicKey,
|
|
1286
|
+
...artifact.blockDaaScore !== void 0 ? { blockDaaScore: safeParseBigInt(artifact.blockDaaScore, "UtxoArtifact.blockDaaScore") } : {},
|
|
1287
|
+
...artifact.isCoinbase !== void 0 ? { isCoinbase: artifact.isCoinbase } : {}
|
|
1035
1288
|
};
|
|
1036
|
-
if (artifact.blockDaaScore !== void 0) {
|
|
1037
|
-
utxo.blockDaaScore = safeParseBigInt(artifact.blockDaaScore, "UtxoArtifact.blockDaaScore");
|
|
1038
|
-
}
|
|
1039
|
-
if (artifact.isCoinbase !== void 0) {
|
|
1040
|
-
utxo.isCoinbase = artifact.isCoinbase;
|
|
1041
|
-
}
|
|
1042
|
-
return utxo;
|
|
1043
1289
|
}
|
|
1044
1290
|
function txOutputToArtifact(output) {
|
|
1045
1291
|
return {
|
|
@@ -1068,7 +1314,7 @@ function createTxPlanArtifact(options) {
|
|
|
1068
1314
|
hardkasVersion: HARDKAS_VERSION,
|
|
1069
1315
|
version: ARTIFACT_VERSION,
|
|
1070
1316
|
hashVersion: CURRENT_HASH_VERSION,
|
|
1071
|
-
createdAt:
|
|
1317
|
+
createdAt: new Date(options.ctx.clock.now()).toISOString(),
|
|
1072
1318
|
networkId: options.networkId,
|
|
1073
1319
|
mode: options.mode,
|
|
1074
1320
|
from: {
|
|
@@ -1109,13 +1355,13 @@ function createTxPlanArtifact(options) {
|
|
|
1109
1355
|
}
|
|
1110
1356
|
|
|
1111
1357
|
// src/signed-tx.ts
|
|
1112
|
-
function createSimulatedSignedTxArtifact(plan, payload) {
|
|
1358
|
+
function createSimulatedSignedTxArtifact(plan, payload, ctx) {
|
|
1113
1359
|
const artifact = {
|
|
1114
1360
|
schema: "hardkas.signedTx",
|
|
1115
1361
|
hardkasVersion: HARDKAS_VERSION,
|
|
1116
1362
|
version: ARTIFACT_VERSION,
|
|
1117
1363
|
hashVersion: CURRENT_HASH_VERSION,
|
|
1118
|
-
createdAt:
|
|
1364
|
+
createdAt: new Date(ctx.clock.now()).toISOString(),
|
|
1119
1365
|
status: "signed",
|
|
1120
1366
|
sourcePlanId: plan.planId,
|
|
1121
1367
|
networkId: plan.networkId,
|
|
@@ -1134,13 +1380,13 @@ function createSimulatedSignedTxArtifact(plan, payload) {
|
|
|
1134
1380
|
artifact.contentHash = hash;
|
|
1135
1381
|
return artifact;
|
|
1136
1382
|
}
|
|
1137
|
-
function createSimulatedTxReceipt(plan, txId, extra) {
|
|
1383
|
+
function createSimulatedTxReceipt(plan, txId, ctx, extra) {
|
|
1138
1384
|
const artifact = {
|
|
1139
1385
|
schema: "hardkas.txReceipt",
|
|
1140
1386
|
hardkasVersion: HARDKAS_VERSION,
|
|
1141
1387
|
version: ARTIFACT_VERSION,
|
|
1142
1388
|
hashVersion: CURRENT_HASH_VERSION,
|
|
1143
|
-
createdAt:
|
|
1389
|
+
createdAt: new Date(ctx.clock.now()).toISOString(),
|
|
1144
1390
|
txId,
|
|
1145
1391
|
status: "accepted",
|
|
1146
1392
|
mode: "simulated",
|
|
@@ -1203,7 +1449,8 @@ function validateSignedTxArtifact(value) {
|
|
|
1203
1449
|
if (!v.signedTransaction) {
|
|
1204
1450
|
errors.push("Missing signedTransaction object");
|
|
1205
1451
|
} else {
|
|
1206
|
-
|
|
1452
|
+
const st = v.signedTransaction;
|
|
1453
|
+
if (!["kaspa-sdk", "hex", "simulated", "unknown"].includes(st.format)) {
|
|
1207
1454
|
errors.push("Invalid signedTransaction.format");
|
|
1208
1455
|
}
|
|
1209
1456
|
}
|
|
@@ -1265,7 +1512,8 @@ function assertDecimalBigIntString2(value, field, errors) {
|
|
|
1265
1512
|
}
|
|
1266
1513
|
|
|
1267
1514
|
// src/explain.ts
|
|
1268
|
-
async function explainArtifact(
|
|
1515
|
+
async function explainArtifact(artifactUnknown) {
|
|
1516
|
+
const artifact = artifactUnknown;
|
|
1269
1517
|
const schema = artifact.schema || "unknown";
|
|
1270
1518
|
const type = schema.split(".")[1] || "unknown";
|
|
1271
1519
|
const integrity = await verifyArtifactIntegrity(artifact);
|
|
@@ -1336,6 +1584,7 @@ async function explainArtifact(artifact) {
|
|
|
1336
1584
|
// src/igra-io.ts
|
|
1337
1585
|
import fs3 from "fs/promises";
|
|
1338
1586
|
import path2 from "path";
|
|
1587
|
+
import { deterministicCompare as deterministicCompare3 } from "@hardkas/core";
|
|
1339
1588
|
function getDefaultL2ReceiptsDir(cwd = process.cwd()) {
|
|
1340
1589
|
return path2.join(cwd, ".hardkas", "l2-receipts");
|
|
1341
1590
|
}
|
|
@@ -1371,7 +1620,7 @@ async function listIgraTxReceiptArtifacts(options) {
|
|
|
1371
1620
|
} catch (e) {
|
|
1372
1621
|
}
|
|
1373
1622
|
}
|
|
1374
|
-
return receipts.sort((a, b) => b.createdAt
|
|
1623
|
+
return receipts.sort((a, b) => deterministicCompare3(b.createdAt, a.createdAt));
|
|
1375
1624
|
} catch (e) {
|
|
1376
1625
|
if (e.code === "ENOENT") return [];
|
|
1377
1626
|
throw e;
|
|
@@ -1440,7 +1689,7 @@ function isPrimitive(val) {
|
|
|
1440
1689
|
|
|
1441
1690
|
// src/deployment.ts
|
|
1442
1691
|
function createDeploymentRecord(opts) {
|
|
1443
|
-
const
|
|
1692
|
+
const recordDraft = {
|
|
1444
1693
|
schema: "hardkas.deployment.v1",
|
|
1445
1694
|
label: opts.label,
|
|
1446
1695
|
networkId: opts.networkId,
|
|
@@ -1449,28 +1698,33 @@ function createDeploymentRecord(opts) {
|
|
|
1449
1698
|
hardkasVersion: HARDKAS_VERSION,
|
|
1450
1699
|
version: ARTIFACT_VERSION,
|
|
1451
1700
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1452
|
-
mode: "real"
|
|
1701
|
+
mode: "real",
|
|
1702
|
+
...opts.txId ? { txId: opts.txId } : {},
|
|
1703
|
+
...opts.planArtifactId ? { planArtifactId: opts.planArtifactId } : {},
|
|
1704
|
+
...opts.receiptArtifactId ? { receiptArtifactId: opts.receiptArtifactId } : {},
|
|
1705
|
+
...opts.deployedAddresses ? { deployedAddresses: opts.deployedAddresses } : {},
|
|
1706
|
+
...opts.deployer ? { deployer: opts.deployer } : {},
|
|
1707
|
+
...opts.payloadHash ? { payloadHash: opts.payloadHash } : {},
|
|
1708
|
+
...opts.notes ? { notes: opts.notes } : {}
|
|
1709
|
+
};
|
|
1710
|
+
const contentHash = calculateContentHash(recordDraft, CURRENT_HASH_VERSION);
|
|
1711
|
+
return {
|
|
1712
|
+
...recordDraft,
|
|
1713
|
+
contentHash
|
|
1453
1714
|
};
|
|
1454
|
-
if (opts.txId) record.txId = opts.txId;
|
|
1455
|
-
if (opts.planArtifactId) record.planArtifactId = opts.planArtifactId;
|
|
1456
|
-
if (opts.receiptArtifactId) record.receiptArtifactId = opts.receiptArtifactId;
|
|
1457
|
-
if (opts.deployedAddresses) record.deployedAddresses = opts.deployedAddresses;
|
|
1458
|
-
if (opts.deployer) record.deployer = opts.deployer;
|
|
1459
|
-
if (opts.payloadHash) record.payloadHash = opts.payloadHash;
|
|
1460
|
-
if (opts.notes) record.notes = opts.notes;
|
|
1461
|
-
record.contentHash = calculateContentHash(record, CURRENT_HASH_VERSION);
|
|
1462
|
-
return record;
|
|
1463
1715
|
}
|
|
1464
1716
|
function updateDeploymentStatus(record, newStatus, txId) {
|
|
1465
|
-
const
|
|
1717
|
+
const updatedDraft = {
|
|
1466
1718
|
...record,
|
|
1467
1719
|
status: newStatus,
|
|
1468
|
-
deployedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1720
|
+
deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1721
|
+
...txId ? { txId } : {}
|
|
1722
|
+
};
|
|
1723
|
+
const contentHash = calculateContentHash(updatedDraft, CURRENT_HASH_VERSION);
|
|
1724
|
+
return {
|
|
1725
|
+
...updatedDraft,
|
|
1726
|
+
contentHash
|
|
1469
1727
|
};
|
|
1470
|
-
if (txId) updated.txId = txId;
|
|
1471
|
-
delete updated.contentHash;
|
|
1472
|
-
updated.contentHash = calculateContentHash(updated, CURRENT_HASH_VERSION);
|
|
1473
|
-
return updated;
|
|
1474
1728
|
}
|
|
1475
1729
|
|
|
1476
1730
|
// src/deployment-store.ts
|
|
@@ -1562,6 +1816,7 @@ export {
|
|
|
1562
1816
|
TxPlanSchema,
|
|
1563
1817
|
TxReceiptSchema,
|
|
1564
1818
|
TxTraceSchema,
|
|
1819
|
+
WorkflowSchema,
|
|
1565
1820
|
assertDecimalBigIntString,
|
|
1566
1821
|
assertEvmAddress,
|
|
1567
1822
|
assertEvmTxHash,
|
|
@@ -1574,6 +1829,7 @@ export {
|
|
|
1574
1829
|
assertValidTxReceiptArtifact,
|
|
1575
1830
|
bigIntReplacer,
|
|
1576
1831
|
calculateContentHash,
|
|
1832
|
+
canMigrate,
|
|
1577
1833
|
canonicalStringify,
|
|
1578
1834
|
createDeploymentRecord,
|
|
1579
1835
|
createIgraDeployPlanId,
|
|
@@ -1584,6 +1840,7 @@ export {
|
|
|
1584
1840
|
createTxPlanArtifact,
|
|
1585
1841
|
defaultClock,
|
|
1586
1842
|
deleteDeployment,
|
|
1843
|
+
detectArtifactVersion,
|
|
1587
1844
|
diffArtifacts,
|
|
1588
1845
|
explainArtifact,
|
|
1589
1846
|
formatSignedTxArtifact,
|
|
@@ -1593,17 +1850,21 @@ export {
|
|
|
1593
1850
|
getDefaultL2ReceiptsDir,
|
|
1594
1851
|
getDefaultReceiptPath,
|
|
1595
1852
|
getL2ReceiptPath,
|
|
1853
|
+
getMigrationPath,
|
|
1854
|
+
getRegisteredMigrationSteps,
|
|
1596
1855
|
isIgraTxPlanArtifact,
|
|
1597
1856
|
listDeployments,
|
|
1598
1857
|
listIgraTxReceiptArtifacts,
|
|
1599
1858
|
loadDeployment,
|
|
1600
1859
|
loadIgraTxReceiptArtifact,
|
|
1860
|
+
migrateArtifactPayload,
|
|
1601
1861
|
migrateToCanonical,
|
|
1602
1862
|
readArtifact,
|
|
1603
1863
|
readSignedTxArtifact,
|
|
1604
1864
|
readTxPlanArtifact,
|
|
1605
1865
|
readTxReceiptArtifact,
|
|
1606
1866
|
recomputeMass,
|
|
1867
|
+
registerMigrationStep,
|
|
1607
1868
|
saveDeployment,
|
|
1608
1869
|
saveIgraTxReceiptArtifact,
|
|
1609
1870
|
sortUtxosByOutpoint,
|