Skip to content

feat: edf - #174

Open
eddort wants to merge 8 commits into
developfrom
feat/edf
Open

feat: edf#174
eddort wants to merge 8 commits into
developfrom
feat/edf

Conversation

@eddort

@eddort eddort commented Aug 7, 2026

Copy link
Copy Markdown
Member

Summary

Adds EDF (LIP-37) support to exit-request verification. An oracle member can
now be a DelegationContract instead of an EOA. The member's delegate hot key
submits reports through DelegationContract.execute(). The allowlist entry
is the contract address, so a hot-key rotation needs no configuration change
from node operators, and reports from before the rotation stay valid.

Main changes

  • The verifier picks the member kind by the calldata selector of the
    consensus report transaction. A direct submitReport call keeps the
    existing EOA path unchanged. An execute() wrapper marks an EDF member.
  • For an EDF member the verifier requires all of: the execute() target is
    the consensus contract, the wrapped call is submitReport, the contract
    is in ORACLE_ADDRESSES_ALLOWLIST, and the transaction signer equals the
    contract's getDelegate() at the report block. Resolving at the report
    block keeps pre-rotation reports valid after a rotation.
  • The Exit Bus report transaction is also unwrapped when the member submits
    it through execute(); the target must be the Exit Bus.
  • Before recovering the signer, the verifier now rebuilds the report
    transaction and checks it against the hash taken from the
    ConsensusReached log, so the transaction body is authenticated.
  • getDelegate() is read at the report block and fails with an actionable
    error when the node cannot serve that state. EDF verification needs an
    archive-capable EXECUTION_NODE; there is no silent fallback.
  • Any other calldata shape on the verified path is rejected with a specific
    error.

Test coverage

  • New e2e suite verifier.edf.e2e-spec.ts runs on a Hardhat fork of
    mainnet against the real Locator, Exit Bus, and HashConsensus. It swaps
    the consensus membership through an impersonated admin and runs the full
    report lifecycle: submitReport, ConsensusReached, submitReportData,
    ValidatorExitRequest.
  • Six tests: EOA member accepted and rejected by the allowlist; delegate
    member accepted and rejected by the allowlist; rotation — the new
    delegate verifies with the same allowlist entry, the pre-rotation report
    stays valid, the revoked delegate can no longer submit; and a report
    through a deliberately permissive contract
    (src/test/contracts/PermissiveDelegation.sol, a test double whose
    execute() does not gate the sender) is rejected by the
    signer-vs-delegate check.
  • Run on 2026-08-07: e2e 6/6, unit tests 201/201, tsc --build clean.
  • Mutation checks run the same day: resolving the delegate at latest
    fails exactly the rotation test; deleting the signer-vs-delegate
    comparison fails exactly the permissive-contract test.
  • Test infrastructure added: hardhat 2.26.3 as a dev dependency,
    hardhat.config.cjs (fork URL from EXECUTION_NODE), and a
    HardhatServer helper that compiles the test contracts and runs the fork
    node.

PR notes

  • For an EDF member, ORACLE_ADDRESSES_ALLOWLIST must hold the
    DelegationContract address, not the hot key.
  • EDF verification reads contract state at historical blocks, so
    EXECUTION_NODE must be archive-capable and trusted.
  • TRUST_MODE=true still disables all report verification.
  • The pre-existing e2e suite exit-logs.e2e-spec.ts has one failing test
    on clean develop ("expected 22 to be 62", multiple-operators case). It
    fails without this change too; it is not caused by this branch.

@eddort
eddort requested a review from a team as a code owner August 7, 2026 12:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Execution Delegation Framework (EDF / LIP-37) support to the exit-request verification flow, allowing an oracle member to be a DelegationContract whose delegate key submits reports via execute(), while keeping the allowlist keyed by the contract address.

Changes:

  • Extend the exit-logs verifier to recognize and unwrap DelegationContract.execute() calls and verify the signer against getDelegate() at the report block.
  • Add a Hardhat mainnet-fork e2e test suite covering EOA vs delegation members, allowlist behavior, delegate rotation, and a permissive-contract negative case.
  • Update CI/runtime scaffolding (Hardhat config/helper, Node 22 in Docker/CI, and e2e workflow job).

Reviewed changes

Copilot reviewed 14 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
yarn.lock Adds Hardhat and transitive dependencies needed for fork-based e2e tests.
src/test/hardhat-server.ts Introduces a helper to compile contracts and run a local Hardhat fork node for e2e tests.
src/test/fixtures/delegation-contract.bytecode.json Adds DelegationContract bytecode fixture pinned to an upstream commit for e2e deployment.
src/test/fixtures/delegation-contract.abi.json Adds DelegationContract ABI fixture for interacting with the deployed fixture in tests.
src/test/contracts/PermissiveDelegation.sol Adds a test-double contract used to validate the signer-vs-delegate check.
src/services/exit-logs/verifier.ts Implements EDF-aware decoding/unwrapping, delegate resolution at a historical block, and tx-body integrity checks.
src/services/exit-logs/verifier.edf.e2e-spec.ts New Hardhat fork e2e suite covering EDF behavior and regression cases (including rotation).
src/services/exit-logs/exit-logs.e2e-spec.ts Adjusts env var fallback semantics and updates an expectation/comment for a known baseline behavior.
src/services/exit-logs/dto.ts Extends transaction DTO to include blockNumber for historical delegate resolution.
src/services/consensus-api/cl.e2e-spec.ts Makes CL e2e assertions stable over time by using finalized and range-based expectations.
package.json Adds hardhat as a dev dependency to support new fork-based e2e tests.
hardhat.config.cjs Adds minimal Hardhat configuration for compiling test contracts and forking from EXECUTION_NODE.
Dockerfile Updates build/runtime base images to Node 22.
.gitignore Ignores Hardhat artifacts and cache directories generated by e2e fork runs.
.github/workflows/tests_and_checks.yml Updates Node version to 22 and adds a dedicated e2e job running yarn test:e2e.
.github/workflows/test.yml Updates Node version to 22 for the existing workflow.
Suppressed comments (1)

src/services/exit-logs/verifier.ts:289

  • verifyTransactionIntegrity() recomputes the tx hash using only a subset of fields (gas, to, value, fees, nonce, type, chainId+ signature). For typed transactions that include additional fields (e.g., EIP-2930/EIP-1559accessList`, or newer tx types with extra payload), this can produce a hash mismatch and cause false negatives (rejecting valid reports) once such tx shapes appear in the wild.
  const verifyTransactionIntegrity = (
    tx: ReturnType<typeof txDTO>['result'],
    expectedHash: string
  ) => {
    const signature = {
      v: Number(tx.v),
      r: tx.r,
      s: tx.s,
    }

    const txData = prepareTransactionData(tx)

    const serialized = ethers.utils.serializeTransaction(txData, signature)
    const computedHash = ethers.utils.keccak256(serialized)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 34 to 37
result: obj(json.result, (result) => ({
from: str(result.from),
blockNumber: optional(() => str(result.blockNumber)),
gas: str(result.gas),
Comment on lines +4 to +6
const require = createRequire(import.meta.url)
const HARDHAT_CLI_PATH = require.resolve('hardhat/internal/cli/bootstrap')
const DEFAULT_START_TIMEOUT_MS = 120_000
Comment thread src/services/exit-logs/verifier.ts Outdated
throw new Error('Transaction is not signed by a trusted Oracle')
}

const delegate = await getDelegate(delegationContract, originTx.blockNumber)
Comment thread src/services/exit-logs/verifier.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants