-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrunner_node.js
More file actions
88 lines (80 loc) · 3.13 KB
/
Copy pathrunner_node.js
File metadata and controls
88 lines (80 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* execution_ref_v1 runner (Node.js / TypeScript reference impl).
*
* Validates the decision-bound execution-evidence primitive:
*
* execution_ref = "sha256:" + SHA-256(JCS({decision_ref, action_type, scope,
* outcome, executed_at_ms}))
*
* A PASS here against the same expected hashes the Python runner checks proves
* byte-for-byte Python+TypeScript parity. Checks: positive construction; closed
* outcome enum; each negative recomputes DIFFERENT; an RFC 3339 string timestamp
* is REJECTED (Substrate Rule 2); cross-set composition vs spend_decision_v1.
*
* npm install @algovoi/execution-ref // any substrate version
* // or, once published: npm install @algovoi/substrate@>=1.0.0 (native)
* node runner_node.js [execution_ref_v1.json]
*/
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
// execution_ref ships natively in @algovoi/substrate 1.0.0+; until then (and on
// any substrate version) the standalone @algovoi/execution-ref provides it.
let executionRef, ExecutionRefError;
try {
({ executionRef, ExecutionRefError } = await import("@algovoi/substrate"));
if (typeof executionRef !== "function") throw new Error("not native in this substrate");
} catch {
({ executionRef, ExecutionRefError } = await import("@algovoi/execution-ref"));
}
const ref = (v) =>
executionRef({
decision_ref: v.decision_ref,
action_type: v.action_type,
scope: v.scope,
outcome: v.outcome,
executed_at_ms: v.executed_at_ms,
});
const here = dirname(fileURLToPath(import.meta.url));
const vf = process.argv[2] || join(here, "execution_ref_v1.json");
const d = JSON.parse(readFileSync(vf, "utf-8"));
const fails = [];
let total = 0;
// 1. positive construction
for (const v of d.vectors) {
total += 1;
if (ref(v) !== v.expected_execution_ref) fails.push(v.id);
}
// 2. closed outcome enum
total += 1;
const v0 = d.vectors[0];
let enumOk = false;
try {
executionRef({ decision_ref: v0.decision_ref, action_type: "p", scope: "s", outcome: "DONE", executed_at_ms: 0 });
} catch (e) {
if (e instanceof ExecutionRefError) enumOk = true;
}
if (!enumOk) fails.push("closed-enum");
// 3 + 4. negatives
for (const n of d.negatives) {
total += 1;
if (n.must === "differ") {
const got = ref(n);
if (got === n.claimed_execution_ref || got !== n.recomputes_to) fails.push(n.id);
} else if (n.must === "reject") {
let rejected = false;
try { ref(n); } catch (e) { if (e instanceof ExecutionRefError) rejected = true; }
if (!rejected) fails.push(`${n.id} (accepted, should reject)`);
}
}
// 5. cross-set composition vs spend_decision_v1
total += 1;
const sd = JSON.parse(readFileSync(join(here, "..", "spend_decision_v1", "spend_decision_v1.json"), "utf-8"));
const sdRefs = new Set(sd.vectors.map((x) => x.expected_decision_ref));
const used = d.vectors.map((v) => v.decision_ref);
if (!used.every((r) => sdRefs.has(r))) fails.push("cross-set-composition");
if (fails.length) {
console.log(`FAIL (${fails.length}/${total}): ${fails.join(", ")}`);
process.exit(1);
}
console.log(`${total}/${total} PASS`);