fix(evm): reject repeated byte-string keys in Solidity metadata (#4024)

cborg's rejectDuplicateMapKeys compares Map keys by identity, and each byte-string key decodes to its own Uint8Array, so a terminal metadata map with the same byte key twice was reported as decoded. Compare the root map's scalar keys by kind and value and report a repeat as not-decoded, like repeated text keys.
This commit is contained in:
Wade authored and GitHub committed 2026-10-22 03:51:02 +08:00
1 parent 1a945ce9ff
commit 8fe8622e09
2 files changed
+30 -2

No files matched your search

+15
View File
@@ -107,12 +107,27 @@ it("keeps the compiler version beside a shortest-form empty text", () => {
expect(result.entries).toHaveLength(2);
});
it("decodes distinct byte keys and distinct nested-container keys", () => {
expect(
inspectSolidityMetadata(bytecode("a2410101410202")).entries.map(
({ key }) => key,
),
).toEqual([
{ kind: "bytes", value: "01" },
{ kind: "bytes", value: "02" },
]);
expect(inspectSolidityMetadata(bytecode("a2810101810202"))).toMatchObject({
status: "partial",
});
});
it.each([
["invalid text", "a1617861ff"],
["truncated value", "a16178"],
["unsupported tag", "a16178c000"],
["floating point", "a16178fa3f800000"],
["duplicate key", "a2617801617802"],
["duplicate byte key", "a2410101410102"],
["excessive nesting", "a16178" + "81".repeat(65) + "00"],
["impossible collection", "a161788a00"],
])("keeps exact ranges and unavailable interpretation for %s", (_name, hex) => {
+15 -2
View File
@@ -122,8 +122,21 @@ export const inspectSolidityMetadata = (
});
if (!(raw instanceof Map))
throw new Error("Terminal CBOR candidate did not decode to a root map.");
for (const [key, value] of raw as Map<unknown, unknown>)
result.entries.push({ key: scalar(key), value: scalar(value) });
// cborg compares Map keys by identity, so repeated byte-string keys (each
// its own Uint8Array) pass its duplicate check; compare scalars by value.
const seen = new Set<string>();
for (const [key, value] of raw as Map<unknown, unknown>) {
const entry = { key: scalar(key), value: scalar(value) };
if (entry.key.kind !== "uninterpreted") {
const identity = JSON.stringify(entry.key);
if (seen.has(identity))
throw new Error(
`CBOR decode error: found repeat map key ${identity}`,
);
seen.add(identity);
}
result.entries.push(entry);
}
result.status = result.entries.some(
({ key, value }) =>
key.kind === "uninterpreted" || value.kind === "uninterpreted",