temp - #498
Merged
pankajjagtapp merged 3 commits intoAug 27, 2026
Merged
Conversation
Documents the control this PR removes for pod-less nodes. A live pod reverts ValidatorNotActiveInPod on a consolidation target it has not proven, so a pod-backed node cannot consolidate into an outside validator. The pod-less path applies no target check. 322/322 behaviour tests pass. Not pushed: PR is in audit.
The target decides where a source's balance lands, and nothing below us
checks it. EIP-7251's process_consolidation_request only requires the target
to hold 0x02 credentials; there is no source/target same-address rule. A pod
vets the target only when the pod is the caller, so a pod-less node calling
the predeploy directly had no target constraint at all. One compromised
EXECUTOR_OPERATIONS_ROLE key could have moved every pod-less validator's
stake to a validator it owned.
Accept a target on three grounds, deny otherwise:
- src == target: a switch to compounding, moves no value.
- linked in etherFiNodeFromPubkeyHash: one of ours.
- ACTIVE in the source's own pod: EigenLayer merkle-verified its
credentials via verifyWithdrawalCredentials.
Cross-credential consolidation still works, which is the point of the
feature: a target on another node passes the second branch. Only targets we
have no record of are rejected.
ACTIVE rather than != INACTIVE: WITHDRAWN passes that looser check, and the
consensus layer drops a withdrawn target, burning the fee and logging a
consolidation that never happens.
Note the registry branch is only as strong as the registry, and
linkLegacyValidatorIds writes it under this same role. Moving that function
to a stricter role is required for this guard to hold against a compromised
key, and is tracked separately.
324/324 behaviour tests pass on a mainnet fork.
Completes the separation the consolidation target guard depends on. That guard accepts a target when it is linked in etherFiNodeFromPubkeyHash, and linkLegacyValidatorIds writes that map while verifying nothing about the pair: it reads DEPRECATED_etherfiNodeAddress[id] and stores whatever pubkey the caller passed. Sharing EXECUTOR_OPERATIONS_ROLE with requestConsolidation left one key able to link a validator it owned and then consolidate into it, so the guard was a speed bump rather than a control. onlyOperatingMultisig (OPERATION_MULTISIG_ROLE, 4-of-7), matching setProofSubmitter. The power to grow the trusted set is now separate from the power to use it: a compromised executor key can still shuffle value between validators we already own, which is recoverable griefing, but cannot introduce a new destination. Timelock was the alternative. Multisig is enough here because the Safe is already the trust root, and a 2-day delay on linking a newly discovered legacy key buys nothing. test_linkLegacyValidatorIds now asserts the separation directly: an address holding only EXECUTOR_OPERATIONS_ROLE is rejected. It uses a fresh address because the shared fixture actors hold several roles at once, and startPrank because a nested role-id read consumes a single-call prank. Fixtures that link as setup were granted the multisig role. Deployment order matters: the ~23k legacy backfill must run BEFORE this ships, or every batch needs a 4-of-7 signature. The ops runbook records that. Full suite: 1679/1684. The 5 failures are pre-existing and environmental (a NotRegistered setUp and 6 suites needing OP_RPC_URL), reproduced with these changes reverted.
pankajjagtapp
merged commit Aug 27, 2026
8042198
into
pankaj/feat/non-eigenpod-withdrawal-credentials
2 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Scratch PR for reviewing a diff. Not for merge. Based on
pankaj/feat/non-eigenpod-withdrawal-credentialsrather thanmaster, so thediff is only the three new commits instead of all of #485.
Addresses the review conclusion on #485: "link them all, gate that function via
multisig, and make the change to the consolidation function."
Commits
6ffd3d1c5daa413f80421985linkLegacyValidatorIdsbehind the operating multisigWhy the target guard exists
EIP-7251's
process_consolidation_requestrequires only that the target hold0x02credentials. There is no source/target same-address rule — the onlywithdrawal_credentials[12:]check in the spec is againstsource_address.A pod vets the target only when the pod is the caller, so a pod-less node
calling the predeploy directly had no target constraint at all.
test_livePod_rejectsAForeignConsolidationTargetrecords the pre-existingcontrol on the pod-backed path: mainnet's EigenPod reverts
ValidatorNotActiveInPodon an unproven target. That is what pod-less nodeswere missing.
The guard
Three grounds to accept, deny otherwise:
src == target— a switch to compounding, moves no valueetherFiNodeFromPubkeyHash— one of oursACTIVEin the source's own pod — EigenLayer merkle-verified the credentialsCross-credential consolidation still works, which is the point of the feature:
a target on another node passes the second branch.
ACTIVErather than!= INACTIVEbecauseWITHDRAWNpasses the looser check and the consensuslayer drops a withdrawn target, burning the fee and logging a consolidation
that never happens.
The role gate
The guard trusts
etherFiNodeFromPubkeyHash, andlinkLegacyValidatorIdswrites that map while verifying nothing about the pair. On the same role as
requestConsolidation, one key could link a validator it owned and thenconsolidate into it. Now
onlyOperatingMultisig(4-of-7), matchingsetProofSubmitter. The power to grow the trusted set is separate from thepower to use it.
test_linkLegacyValidatorIdsasserts that directly: an address holding onlyEXECUTOR_OPERATIONS_ROLEis rejected.Deployment order
The ~23k legacy backfill must run before this ships, or every one of ~73
batches needs a 4-of-7 signature. Ops runbook and workflow live in
protocol-ops.
Verification
Full suite 1679/1684. The 5 failures are pre-existing and environmental (a
NotRegisteredsetUp and 6 suites needingOP_RPC_URL), reproduced with thesechanges reverted.
Known limit
The registry branch is trusted bookkeeping, not proof. The backfill's beacon
verification closes that for the legacy set and the pod-
ACTIVEbranch iscryptographic, but a future mislink by the Safe would still be accepted. A
Lido-style beacon proof of the target's credentials is the full fix, as a
follow-up.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Note
High Risk
Changes staking consolidation authorization and who can populate the pubkey registry—both directly affect where validator ETH can be directed.
Overview
Closes a consolidation theft vector on pod-less nodes: EIP-7251 only checks that the target has
0x02credentials, sorequestConsolidationnow always validates each target before forwarding. Allowed targets are switch-to-compounding (src == target), any pubkey inetherFiNodeFromPubkeyHash, or anACTIVEvalidator in the source pod (not merely non-INACTIVE, to avoid withdrawn targets). Anything else reverts withUnknownConsolidationTarget.linkLegacyValidatorIdsis moved from executor ops toonlyOperatingMultisig, so the role that can extend the trusted pubkey map cannot also submit consolidations that rely on it. Tests and fixtures are updated for the new role split and for rejecting foreign consolidation targets on both pod-less and live-pod paths.Reviewed by Cursor Bugbot for commit 8042198. Bugbot is set up for automated code reviews on this repo. Configure here.