Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: pectra compatibility #1053

Open
wants to merge 11 commits into
base: feat/prooftra
Choose a base branch
from
Open

Conversation

ypatil12
Copy link
Collaborator

@ypatil12 ypatil12 commented Jan 28, 2025

Updates checkpoint proof system to be Pectra compatible. The breaking change to EigenPods is the BeaconState container increasing to have 37 fields, which results in the tree height to be > 5.

Overview

We need to solve for the following cases:

  • Prevent deneb proofs from being submitted to pectra blocks
  • Ensure that the PECTRA_FORK_TIMESTAMP is the first timestamp at or after the pectra hard fork for which there is a non missed slot. Checkpoint proofs store the proof timestamp. If there are missed slots at the hard fork timestamp, it's that the beaconTimestamp is post pectra but the block header is deneb.

To do this, here is the upgrade process:

  1. Pause checkpoint starting & credential proofs
  2. Upgrade after fork is hit
  3. Run script to detect the first timestamp at or after the pectra hard fork for which there is a non missed slot
  4. Set pectra fork timestamp to the first timestamp at which there is a pectra block header
  5. Unpause

EigenPod

  • Updated balance container and validator container proofs to pass in a proof timestamp & pectra fork timestamp to check against which tree height to use for the beacon state
  • Modify storing variables in memory to handle stack too deep errors
  • Note that since the 4788 oracle returns the PARENT beacon block root, our check against the pectra fork timestamp returns the previous tree length for proofs that are <= pectraForkTimestamp

Deprecation Plan

  • Post pectra, we can upgrade the EigenPod to deprecate the fork timestamp case handling once all in progress pre-Pectra checkpoints have been completed

TODOs

  • Unit Tests
  • Integration Tests simulating upgrade
  • Mekong Deployment
  • Update Integration Test User to use validators >32 ETH

@ypatil12 ypatil12 mentioned this pull request Jan 28, 2025
3 tasks
@ypatil12 ypatil12 force-pushed the slashing-magnitudes branch from 7a6f8a6 to fbe5ee2 Compare January 28, 2025 18:23
@ypatil12 ypatil12 changed the base branch from slashing-magnitudes to dev January 28, 2025 21:48
@ypatil12 ypatil12 force-pushed the yash/pectra-compatibility branch 4 times, most recently from 8b3076f to 8679ebb Compare January 28, 2025 21:55
@ypatil12 ypatil12 changed the base branch from dev to feat/prooftra January 29, 2025 22:11
@ypatil12 ypatil12 changed the base branch from feat/prooftra to dev January 29, 2025 22:31
@ypatil12 ypatil12 force-pushed the yash/pectra-compatibility branch from 8679ebb to 392022e Compare January 30, 2025 01:14
@ypatil12 ypatil12 changed the base branch from dev to feat/prooftra January 30, 2025 01:15
@ypatil12 ypatil12 force-pushed the yash/pectra-compatibility branch 4 times, most recently from d71e908 to 2e9ab01 Compare January 30, 2025 02:12
@ypatil12 ypatil12 marked this pull request as ready for review January 30, 2025 20:34
eigenmikem
eigenmikem previously approved these changes Feb 4, 2025
Copy link

@eigenmikem eigenmikem left a comment

Choose a reason for hiding this comment

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

AFAIK the fork timestamp has never been altered once broadcast, at least not in the last few years, so I think hardcoding it is fine

@ypatil12 ypatil12 force-pushed the yash/pectra-compatibility branch from d849720 to f5dbfb8 Compare February 4, 2025 23:16
@@ -254,6 +255,7 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC
for (uint256 i = 0; i < validatorIndices.length; i++) {
// forgefmt: disable-next-item
totalAmountToBeRestakedWei += _verifyWithdrawalCredentials(
beaconTimestamp,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, I wonder about the usage of getParentBlockRoot above.

Can beaconTimestamp belong to the block after the fork, but the proof itself is over the parent block (aka pre-fork)?

Copy link
Collaborator

Choose a reason for hiding this comment

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

For the record I think I'm correct here. 2 scenarios:

  1. I try to verify withdrawal credentials the block after the hard fork. getParentBlockRoot grabs a block from before the hard fork; that's what my proof needs to be against.
  2. I start a checkpoint just after the hard fork. The checkpoint block root used for proofs is from a block just before the hard fork.

Naive solution: have the fork selector logic in the proofs library check proofTimestamp - 12. However, this runs into potential issues with skipped slots -- I might have a proof timestamp 2 blocks after the hard fork, but there was a skipped slot right at the fork, so the proof block is from before the fork.

I think, unfortunately, we need to pause proofs just before the fork, and unpause just after the fork (once we see the first valid block). Although I'm not sure this entirely fixes the issue... will think more.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we need to pause + upgrade just after the fork + unpause? ugh.

Copy link
Collaborator Author

@ypatil12 ypatil12 Feb 6, 2025

Choose a reason for hiding this comment

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

Synced offline, here is what we need to do:

  • Prevent deneb proofs from being submitted to pectra blocks
  • Ensure that the PECTRA_FORK_TIMESTAMP is the first timestamp at or after the pectra hard fork for which there is a non-missed slot. Checkpoint proofs store the proof timestamp. If there are missed slots at the hard fork timestamp, it's possible, like Alex mentions above, that the beaconTimestamp is post pectra but the block header is deneb.

To do this, here is the process:

  1. Pause checkpoint starting & credential proofs
  2. Upgrade after fork is hit
  3. Set pectra fork timestamp to the first timestamp at which there is a pectra block header
  4. Unpause

Copy link
Collaborator

Choose a reason for hiding this comment

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

image

Copy link
Collaborator

Choose a reason for hiding this comment

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

Using the image above, we want to determine what proof type to use, given the proof timestamp tp. Because tp is used to look up a parent block in the EIP-4788 oracle, its proof type corresponds to the last non-skipped block. So, if:

  • tp > t1, use pectra logic
  • tp <= t1, use dencun logic

Given our beacon state tree height getter: https://github.com/layr-labs/eigenlayer-contracts/blob/b4852c74cdbe43fea2f7330ea0dc752fcf10b6e9/src/contracts/libraries/BeaconChainProofs.sol#L327-L334

... pectraForkTimestamp should be set to the first valid, non-skipped block after the Pectra hard fork.

Copy link
Collaborator

@wadealexc wadealexc left a comment

Choose a reason for hiding this comment

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

Is this PR targeting testnet?

We're going to need a different PR for mainnet, since that's landing before slashing, right? (This EigenPod file is compatible with testnet slashing, but not mainnet)

@ypatil12
Copy link
Collaborator Author

ypatil12 commented Feb 5, 2025

Is this PR targeting testnet?

Yup, targeting testnet. There will be a separate mainnet PR. cc @wadealexc

Comment on lines +355 to +357

/// @notice Returns the timestamp of the Pectra fork, read from the `EigenPodManager` contract
function getPectraForkTimestamp() external view returns (uint64);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need this added to the EigenPod interface?

It already exists on the EPM - why duplicate?

Comment on lines +327 to +334
/// @dev We check if the proofTimestamp is <= pectraForkTimestamp because a `proofTimestamp` at the `pectraForkTimestamp`
/// is considered to be Pre-Pectra given the EIP-4788 oracle returns the parent block.
function getBeaconStateTreeHeight(
uint64 proofTimestamp,
uint64 pectraForkTimestamp
) internal pure returns (uint256) {
return proofTimestamp <= pectraForkTimestamp ? DENEB_BEACON_STATE_TREE_HEIGHT : PECTRA_BEACON_STATE_TREE_HEIGHT;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just making some notes:

Before we set pectraForkTimestamp in the EPM, it returns 0. This will cause all proofs to be interpreted as pectra proofs. However, we expect contracts to be paused if this value is unset, so that's fine 👍

Additionally, if the contracts are unpaused but the value is still unset, this logic means all proofs are interpreted as pectra proofs, even if we're working with a dencun block. This is good fallback behavior, as the worst case means some proofs will revert until we set the timestamp. 👍

It's the reverse behavior we wanted to avoid - where dencun proof sizes are allowed for pectra blocks. That shouldn't be possible here 👍

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