Following post #18, the appendix @babyblueviper1 asked for. This documents the GenericCommitRevealSettler integration pattern for judgment attestations as a reference for any producer implementing the L4 composition.
Contract
GenericCommitRevealSettler on Sepolia: 0xFe7Ab6d95f7567a311B98D029373d0fc1511aCCe
Bytes-opaque commit/reveal primitive. No bond, no NodeType gate, those are ERC-8275-specific concerns handled by CommitRevealSettlerV2. This contract only verifies the preimage binding, making it usable for any record schema: contribution snapshots, judgment attestations, OCP observations, or any future type.
Hash construction
commitmentHash = keccak256(abi.encode(record, periodId, committer))
record is bytes, the abi-encoded judgment attestation core (see encoding convention below). Binding to periodId + committer prevents cross-period and cross-sender replay without the contract knowing anything about the record schema.
Encoding convention for judgment attestations
The bytes record parameter should encode the attestation fields that determine the judgment identity:
bytes memory record = abi.encode(
rawProposalHash, // bytes32 — what was proposed
verdictHash, // bytes32 — the judgment, binding to rawProposalHash
executedActionHash, // bytes32 — what was actually executed
verdictTimestamp // uint256 — verdict issuance, strictly pre-execution
);
This is the minimal set that closes the reviewed→executed gap. agentId, registry, validatorId, and recordPointer are attestation metadata and do not need to be in the committed record, they are carried in the full JudgmentExecutionAttestation struct resolved at settlement time.
Recommended preflight
Before calling submitCommit, verify the hash binding off-chain at zero gas cost:
// eth_call — no gas spent
bytes32 expected = computeCommitmentHash(record, periodId, committerAddress);
computeCommitmentHash(bytes calldata record, uint256 periodId, address committer) is a view function on the contract. Verify expected byte-exact against your locally computed hash before submitting the transaction. If they don’t match, something in the encoding is wrong — fix it before spending gas.
This is the pattern from post #18 (computeCommitmentHash via eth_call before any gas was spent). Recommend treating this as a required preflight, not an optional check.
periodId convention
Use the ledger entry number as periodId. This creates a 1:1 binding between ledger entries and settlement periods and makes getCommit(periodId, committer) directly queryable from the entry index. The periodId + committer binding in the hash construction means two producers can commit in the same period without collision.
Workflow
1. Compute record = abi.encode(rawProposalHash, verdictHash, executedActionHash, verdictTimestamp)
2. Compute commitmentHash = keccak256(abi.encode(record, periodId, committerAddress))
3. Preflight: eth_call computeCommitmentHash(record, periodId, committerAddress) → verify match
4. submitCommit(periodId, commitmentHash) // stores CommitRecord on-chain
5. Execute the governed action
6. submitReveal(periodId, record) // verifies preimage, stores recordHash
submitReveal reverts on mismatch (safe to revert — no bond). The reveal window is 48 hours from commit. Challenge period is 7 days from reveal.
Invariant check after reveal
CommitRecord memory c = getCommit(periodId, committer);
// c.committedAt > 0 — committed
// c.revealedAt > 0 — revealed
// c.committedAt < c.revealedAt — temporal ordering holds
// c.recordHash == keccak256(record) — preimage binding holds
The Revealed event emits (periodId, committer, recordHash, record) — the full bytes record is in the event log, so any observer can independently verify the preimage without a separate storage read.
recordPointer sub-paths
The recordPointer in the full attestation struct MUST expose:
-
{recordPointer}/commitment— pre-settlement: signed verdict, relay anchor,commitmentHash, block number ofsubmitCommit -
{recordPointer}/outcome— post-settlement:recordHashfromgetCommit, transaction hash ofsubmitReveal, settlement account
These are separately resolvable. A dispute verifier MUST be able to confirm commitment integrity (pre-settlement question) without accessing outcome evidence (post-settlement question). /ledger/19/commitment and /ledger/19/outcome from post #18 are the live reference implementation of this shape.
Reference implementation
-
Ledger entry: https://api.babyblueviper.com/ledger/19
-
/ledger/19/commitmentpre-settlement evidence -
/ledger/19/outcomepost-settlement evidence
-
-
Commit tx:
0x735ad616bf988a5879440ff31b8d091a60e19afbbc6ad596d62f4041e7a7870f(block 11030402) -
Reveal tx:
0x299a3f5152e767aa2aa344ef5942210707609ded2da8a02b5f3c37071b22dd8b(block 11030403) -
Contract:
GenericCommitRevealSettler0xFe7Ab6d95f7567a311B98D029373d0fc1511aCCe(Sepolia)
Two production systems, one settlement primitive. Both reference implementations live.