refactor: add minimal reasoning verifier hook for ERC-8183#31
refactor: add minimal reasoning verifier hook for ERC-8183#31ThoughtProof wants to merge 2 commits into
Conversation
Adds a minimal ERC-8183 hook that gates the fund stage on a condition-based wallet-state verifier. Complements existing score-based gating (TrustGateHook, erc-8183#9/erc-8183#32) and content-based verification (ReasoningVerifierHook, erc-8183#31) with a third shape: "does this wallet satisfy a named condition set right now?" - contracts/interfaces/IWalletStateVerifier.sol Minimal (bool verified, uint256 validUntil) interface keyed on (wallet, conditionsHash). Hooks stay stateless views. - contracts/hooks/WalletStateHook.sol Inherits BaseERC8183Hook + IERC8183HookMetadata. Immutable verifier + conditionsHash (deploy one hook per distinct condition set, mirrors the minConfidence immutable pattern in ReasoningVerifierHook). Overrides _preFund only — verifier.checkWalletState(caller, conditionsHash) → pass/fail + freshness, reverts otherwise. - contracts/examples/InsumerWalletStateVerifier.sol Reference IWalletStateVerifier implementation. Relayer-push model with optional RIP-7212 P256VERIFY precompile verification of off-chain ECDSA P-256 (ES256) attestation signatures. Works on Base, Arbitrum, Optimism, Polygon, Scroll, ZKsync, Celo — standard ERC-8183 L2 footprint. - test/WalletStateHook.t.sol 21 tests, all passing. Covers constructor guards, _preFund happy path, not-verified revert, expired-attestation revert, validUntil boundary, selector isolation, ERC-165 interface support, verifier relayer auth, and signature-mode flag. Stacked on top of erc-8183#30 (IACPHook → IERC8183Hook rename). Targets main; will rebase cleanly once erc-8183#30 merges.
Adds a minimal ERC-8183 hook that gates the fund stage on a condition-based wallet-state verifier. Complements existing score-based gating (TrustGateHook, erc-8183#9/erc-8183#32) and content-based verification (ReasoningVerifierHook, erc-8183#31) with a third shape: "does this wallet satisfy a named condition set right now?" - contracts/interfaces/IWalletStateVerifier.sol Minimal (bool verified, uint256 validUntil) interface keyed on (wallet, conditionsHash). Hooks stay stateless views. - contracts/hooks/WalletStateHook.sol Inherits BaseERC8183Hook + IERC8183HookMetadata. Immutable verifier + conditionsHash (deploy one hook per distinct condition set, mirrors the minConfidence immutable pattern in ReasoningVerifierHook). Overrides _preFund only — verifier.checkWalletState(caller, conditionsHash) → pass/fail + freshness, reverts otherwise. - contracts/examples/InsumerWalletStateVerifier.sol Reference IWalletStateVerifier implementation. Relayer-push model with optional RIP-7212 P256VERIFY precompile verification of off-chain ECDSA P-256 (ES256) attestation signatures. Works on Base, Arbitrum, Optimism, Polygon, Scroll, ZKsync, Celo — standard ERC-8183 L2 footprint. - test/WalletStateHook.t.sol 21 tests, all passing. Covers constructor guards, _preFund happy path, not-verified revert, expired-attestation revert, validUntil boundary, selector isolation, ERC-165 interface support, verifier relayer auth, and signature-mode flag. Stacked on top of erc-8183#30 (IACPHook → IERC8183Hook rename). Targets main; will rebase cleanly once erc-8183#30 merges.
|
Hi @ThoughtProof,
|
c1c2ff3 to
b112299
Compare
|
Rebased on current main and addressed both points:
What remains:
|
ariessa
left a comment
There was a problem hiding this comment.
Hi @ThoughtProof,
Thanks for working through the previous round of feedback. One housekeeping thing first: could you remove the test file from the PR? On the contract itself, I spent more time with it on this pass and there are a few design points I'd like to walk through before we land it.
Problem
_preSubmit checks the deliverable hash against the verifier, but the hash isn't tied to the job, the worker, or the requester. So:
- Worker A finishes Job 1, their hash H gets marked verified.
- Worker B copies H from chain and submits it for Job 2.
- The hook approves both. Worker B did no work.
It also has no consumption step. The same verified hash works for unlimited submissions.
This is fine if the verifier is run by the requester themselves, but the NatSpec frames it as a general "reasoning verifier," which is exactly the case where this breaks.
Suggestions
- Bind verification to the job. Change
verifyReasoningto take(jobId, worker, deliverable)._preSubmitalready has all three — pass them through. This closes the cross-job replay. - Make it single-use. Either the verifier consumes the record after a read, or the hook stores
mapping(uint256 => bool)so the same job can't replay. Means_preSubmitis no longer view. requiredSelectors()should return the submit selector. Right now it returns [], which tells the router the hook gates nothing.- Emit an event with
(jobId, deliverable, confidence)so there's an on-chain trace. - Cap returned confidence at 1000. A bad verifier returning
type(uint256).maxwould still pass. - If you have a use case in mind that justifies the current shape, walk me through it. Either way, please add this to the NatSpec:
- Use case — what problem does the hook solve, who deploys it?
- Flow — end-to-end happy path: where does the hash come from, who calls the verifier?
- Trust model — who operates the verifier, and what attacks does this hook explicitly not defend against?
…eliverable) Addresses all 6 review items from @ariessa (erc-8183#31): 1. Cross-job replay: verifyReasoning now takes (jobId, caller, deliverable) instead of bare canonicalHash. Prevents hash reuse across jobs. 2. Single-use: _consumed mapping per (jobId, caller) prevents replay. _preSubmit is no longer a view function. 3. requiredSelectors(): returns [submit_selector] instead of empty array, so the router correctly dispatches to this hook. 4. Event emission: ReasoningVerified(jobId, caller, deliverable, confidence) emitted on every successful verification. 5. Confidence cap: values above MAX_CONFIDENCE (1000) are capped, not rejected. 6. NatSpec: added Use Case, Flow (7-step happy path), Trust Model, and Caller Semantics documentation to the contract header. Additional: - Fixed parameter name: caller (not worker) to match BaseERC8183Hook signature. caller = msg.sender on AgenticCommerce.submit(), which may be the worker or an authorized operator. - _consumed scoped per (jobId, caller) to allow competitive submissions from different callers on the same job. - 13 tests covering all security scenarios.
|
Thanks for the thorough review @ariessa — pushed Changes:
Additional fix: Renamed the second Test coverage: 13 tests covering deployment, happy path, cross-job replay, cross-caller replay, single-use enforcement, confidence cap, selector registration, and non-submit passthrough. Let me know if anything needs further adjustment. |
Adds a minimal ERC-8183 hook that gates the fund stage on a condition-based wallet-state verifier. Complements existing score-based gating (TrustGateHook, erc-8183#9/erc-8183#32) and content-based verification (ReasoningVerifierHook, erc-8183#31) with a third shape: "does this wallet satisfy a named condition set right now?" - contracts/interfaces/IWalletStateVerifier.sol Minimal (bool verified, uint256 validUntil) interface keyed on (wallet, conditionsHash). Hooks stay stateless views. - contracts/hooks/WalletStateHook.sol Inherits BaseERC8183Hook + IERC8183HookMetadata. Immutable verifier + conditionsHash (deploy one hook per distinct condition set, mirrors the minConfidence immutable pattern in ReasoningVerifierHook). Overrides _preFund only — verifier.checkWalletState(caller, conditionsHash) → pass/fail + freshness, reverts otherwise. - contracts/examples/InsumerWalletStateVerifier.sol Reference IWalletStateVerifier implementation. Relayer-push model with optional RIP-7212 P256VERIFY precompile verification of off-chain ECDSA P-256 (ES256) attestation signatures. Works on Base, Arbitrum, Optimism, Polygon, Scroll, ZKsync, Celo — standard ERC-8183 L2 footprint. - test/WalletStateHook.t.sol 21 tests, all passing. Covers constructor guards, _preFund happy path, not-verified revert, expired-attestation revert, validUntil boundary, selector isolation, ERC-165 interface support, verifier relayer auth, and signature-mode flag. Stacked on top of erc-8183#30 (IACPHook → IERC8183Hook rename). Targets main; will rebase cleanly once erc-8183#30 merges.
Summary
This PR reintroduces the reasoning verifier hook on top of the current upstream architecture.
What changed
IReasoningVerifierinterfaceReasoningVerifierHookbuilt onBaseERC8183Hook_preSubmit(...)using the ERC-8183deliverableas the canonical hashThoughtProofReasoningVerifieras a reference implementation undercontracts/examples/Design intent
The goal is to keep the core hook small, generic, and compatible with the current hook stack:
BaseERC8183Hook-nativeCanonical hash choice
This version treats the ERC-8183
deliverableas the canonical hash checked during_preSubmit(...).That keeps the hook small and avoids adding extra hook-specific encoding requirements.
Verification
Local verification performed:
forge test --match-path test/ReasoningVerifierHook.t.sol✅