96 lines
3.6 KiB
JavaScript
96 lines
3.6 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { createHash } from "node:crypto";
|
|
import { readFile, writeFile } from "node:fs/promises";
|
|
import { isAbsolute } from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import { parseArgs } from "node:util";
|
|
|
|
const { values } = parseArgs({
|
|
options: Object.fromEntries(
|
|
["artifact", "artifact-json", "evidence", "keccak-module", "output"].map(
|
|
(key) => [key, { type: "string" }],
|
|
),
|
|
),
|
|
});
|
|
assert.ok(values.artifact, "Select the exact corpus artifact ID");
|
|
for (const key of ["artifact-json", "evidence", "keccak-module", "output"])
|
|
assert.ok(isAbsolute(values[key] ?? ""), `${key} requires an absolute path`);
|
|
const corpus = JSON.parse(
|
|
await readFile(new URL("./corpus.json", import.meta.url), "utf8"),
|
|
);
|
|
const selected = corpus.artifacts.find(({ id }) => id === values.artifact);
|
|
assert.ok(selected?.provenance.compiler_artifact_sha256);
|
|
const source = await readFile(values["artifact-json"]);
|
|
const hash = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
assert.equal(hash(source), selected.provenance.compiler_artifact_sha256);
|
|
assert.equal(source.length, selected.provenance.compiler_artifact_bytes);
|
|
const artifact = JSON.parse(source);
|
|
const carrier = Buffer.from(artifact.deployedBytecode + "\n");
|
|
assert.equal(hash(carrier), selected.sha256);
|
|
assert.equal(carrier.length, selected.bytes);
|
|
const { keccak_256 } = (await import(pathToFileURL(values["keccak-module"])))
|
|
.default;
|
|
assert.equal(
|
|
typeof keccak_256,
|
|
"function",
|
|
"Supply js-sha3's Ethereum Keccak implementation",
|
|
);
|
|
const canonicalType = (input) =>
|
|
input.type.startsWith("tuple")
|
|
? `(${input.components.map(canonicalType).join(",")})${input.type.slice(5)}`
|
|
: input.type;
|
|
const selectors = artifact.abi
|
|
.filter((entry) => entry.type === "function")
|
|
.map((entry) => {
|
|
const signature = `${entry.name}(${entry.inputs.map(canonicalType).join(",")})`;
|
|
return { signature, selector: `0x${keccak_256(signature).slice(0, 8)}` };
|
|
});
|
|
const reply = JSON.parse(await readFile(values.evidence, "utf8"));
|
|
const evidence = reply.structuredContent ?? reply;
|
|
assert.equal(evidence.operation, "inspect_evm_interface");
|
|
assert.equal(evidence.subject.digest.sha256, selected.sha256);
|
|
assert.equal(evidence.normalized_result.artifact.sha256, selected.sha256);
|
|
assert.equal(
|
|
evidence.normalized_result.bytecode.sha256,
|
|
selected.decoded_sha256,
|
|
);
|
|
assert.equal(
|
|
hash(Buffer.from(evidence.normalized_result.bytecode.hex, "hex")),
|
|
selected.decoded_sha256,
|
|
);
|
|
assert.equal(evidence.normalized_result.bytecode.bytes, selected.decoded_bytes);
|
|
assert.equal(evidence.normalized_result.evidence_kind, "inferred");
|
|
assert.equal(evidence.normalized_result.discovery_completeness, "unknown");
|
|
const observed = evidence.normalized_result.functions
|
|
.filter((entry) => entry.dispatch === "abi")
|
|
.map((entry) => entry.selector);
|
|
assert.deepEqual(
|
|
observed.toSorted(),
|
|
selectors.map((entry) => entry.selector).toSorted(),
|
|
);
|
|
await writeFile(
|
|
values.output,
|
|
JSON.stringify(
|
|
{
|
|
artifact: selected.id,
|
|
compiler_artifact_sha256: hash(source),
|
|
keccak_module_sha256: hash(await readFile(values["keccak-module"])),
|
|
evidence_id: evidence.evidence_id,
|
|
selector_set_matches_published_abi: true,
|
|
selectors,
|
|
scope:
|
|
"Independent published ABI-derived selector oracle; no deployed authenticity, inferred argument/mutability equivalence or runtime verification is established.",
|
|
},
|
|
null,
|
|
2,
|
|
) + "\n",
|
|
{ flag: "wx", mode: 0o600 },
|
|
);
|
|
process.stdout.write(
|
|
JSON.stringify({
|
|
artifact: selected.id,
|
|
selector_count: selectors.length,
|
|
selector_set_matches_published_abi: true,
|
|
}) + "\n",
|
|
);
|