Skip to content

Commit 8889651

Browse files
committed
Add circuit commitment to ENS registry and verify against vkey
- EnsSpecEntry gains optional circuits field: SHA-256 hashes of verification keys keyed by function name - Admin page presets include circuit hashes for transfer and approve - prover.ts: add getVkeyHash() to compute SHA-256 of the vkey JSON - Proof step: after proof verification, checks if the local vkey hash matches the hash committed in the ENS registry record - Shows green "Circuit commitment matches ENS registry" when hashes match, or amber warning when not found The user needs to re-register USDC via the admin page to include the circuits field in the ENS text record.
1 parent 9d1f682 commit 8889651

4 files changed

Lines changed: 93 additions & 1 deletion

File tree

src/app/clear-signing/admin/page.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ const PRESET_SPECS: Record<string, EnsSpecEntry> = {
2424
type: "token",
2525
decimals: 6,
2626
symbol: "USDC",
27+
circuits: {
28+
transfer: "7a97aa1eb1a3aa3650a45efe1142ef01f45bf8c3b162c382c33f74bfd21d1435",
29+
approve: "dd2bcbb99a4830637a7ec903932d134e83d094cd2e51e6ffb38cd48e8251ef0f",
30+
},
2731
},
2832
"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D": {
2933
specName: "UniswapV2Router",

src/app/clear-signing/engine/ens.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ export type EnsSpecEntry = {
2323
type: "token" | "protocol" | "contract" | "ens";
2424
decimals?: number;
2525
symbol?: string;
26+
/** SHA-256 hashes of verification keys, keyed by function name.
27+
* Pins the exact circuit that was compiled from this spec. */
28+
circuits?: Record<string, string>;
2629
};
2730

2831
// ─── Internals ──────────────────────────────────────────────────────────────

src/app/clear-signing/engine/prover.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,33 @@ function getCircuitName(
7878
return CIRCUIT_REGISTRY[`${specName}:${functionName}`] ?? null;
7979
}
8080

81+
/**
82+
* Compute the SHA-256 hash of a circuit's verification key.
83+
* Used to verify that the circuit matches the ENS registry commitment.
84+
*/
85+
export async function getVkeyHash(
86+
specName: string,
87+
functionName: string
88+
): Promise<string | null> {
89+
const circuitName = getCircuitName(specName, functionName);
90+
if (!circuitName) return null;
91+
92+
const vkeyPath = `/circuits/${circuitName}/vkey.json`;
93+
try {
94+
const response = await fetch(vkeyPath);
95+
const vkeyText = await response.text();
96+
const hashBuffer = await crypto.subtle.digest(
97+
"SHA-256",
98+
new TextEncoder().encode(vkeyText)
99+
);
100+
return Array.from(new Uint8Array(hashBuffer))
101+
.map((b) => b.toString(16).padStart(2, "0"))
102+
.join("");
103+
} catch {
104+
return null;
105+
}
106+
}
107+
81108
// ─── Uint256 Splitting ──────────────────────────────────────────────────────
82109

83110
/**

src/app/clear-signing/page.tsx

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import {
1313
import {
1414
hasCircuit,
1515
generateAndVerifyProof,
16+
getVkeyHash,
1617
type ProofResult,
1718
} from "./engine/prover";
19+
import { readSpecFromEns, type EnsSpecEntry } from "./engine/ens";
1820
import { DEMO_EXAMPLE } from "./engine/examples";
1921
import type {
2022
IntentSpec,
@@ -52,6 +54,12 @@ type Step =
5254
status: "pending" | "success" | "error" | "unavailable";
5355
result: ProofResult | null;
5456
error?: string;
57+
/** Circuit commitment verification against ENS */
58+
circuitCheck?: {
59+
ensHash: string | null;
60+
localHash: string | null;
61+
match: boolean;
62+
};
5563
};
5664

5765
// ─── Helpers ────────────────────────────────────────────────────────────────
@@ -734,6 +742,42 @@ function ProofStep({
734742
</p>
735743
</div>
736744

745+
{step.circuitCheck && (
746+
<div className={`px-4 py-3 rounded-lg border ${
747+
step.circuitCheck.match
748+
? "border-emerald-200 bg-emerald-50"
749+
: "border-amber-200 bg-amber-50"
750+
}`}>
751+
<p className={`text-[13px] font-medium ${
752+
step.circuitCheck.match ? "text-emerald-800" : "text-amber-800"
753+
}`}>
754+
{step.circuitCheck.match
755+
? "Circuit commitment matches ENS registry"
756+
: "Circuit commitment not found in ENS registry"}
757+
</p>
758+
<div className="mt-1.5 font-mono text-[11px] space-y-0.5">
759+
<div className="flex gap-2">
760+
<span className={step.circuitCheck.match ? "text-emerald-600" : "text-amber-600"}>
761+
vkey hash:
762+
</span>
763+
<span className="break-all text-secondary">
764+
{step.circuitCheck.localHash?.slice(0, 16)}...
765+
</span>
766+
</div>
767+
{step.circuitCheck.ensHash && (
768+
<div className="flex gap-2">
769+
<span className={step.circuitCheck.match ? "text-emerald-600" : "text-amber-600"}>
770+
ENS record:
771+
</span>
772+
<span className="break-all text-secondary">
773+
{step.circuitCheck.ensHash.slice(0, 16)}...
774+
</span>
775+
</div>
776+
)}
777+
</div>
778+
</div>
779+
)}
780+
737781
<details>
738782
<summary className="text-[13px] text-amber-600 cursor-pointer select-none hover:text-amber-700 transition-colors">
739783
View trust assumption
@@ -902,11 +946,25 @@ export default function ClearSigningPage() {
902946
params,
903947
emitted
904948
);
949+
950+
// Verify circuit commitment against ENS registry
951+
let circuitCheck: { ensHash: string | null; localHash: string | null; match: boolean } | undefined;
952+
const ensEntry = await readSpecFromEns(contractAddress);
953+
const localHash = await getVkeyHash(spec.contractName, binding.intentFnName);
954+
if (ensEntry?.circuits && localHash) {
955+
const ensHash = ensEntry.circuits[binding.intentFnName] ?? null;
956+
circuitCheck = {
957+
ensHash,
958+
localHash,
959+
match: ensHash === localHash,
960+
};
961+
}
962+
905963
setSteps((prev) => {
906964
const updated = [...prev];
907965
const idx = updated.findIndex((s) => s.kind === "proof");
908966
if (idx >= 0) {
909-
updated[idx] = { kind: "proof", status: "success", result };
967+
updated[idx] = { kind: "proof", status: "success", result, circuitCheck };
910968
}
911969
return updated;
912970
});

0 commit comments

Comments
 (0)