-
Notifications
You must be signed in to change notification settings - Fork 370
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
base: feat/prooftra
Are you sure you want to change the base?
Conversation
7a6f8a6
to
fbe5ee2
Compare
8b3076f
to
8679ebb
Compare
8679ebb
to
392022e
Compare
d71e908
to
2e9ab01
Compare
There was a problem hiding this 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
8beb418
to
d63fded
Compare
d849720
to
f5dbfb8
Compare
@@ -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, |
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
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:
- 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. - 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 thebeaconTimestamp
is post pectra but the block header is deneb.
To do this, here is the process:
- Pause checkpoint starting & credential proofs
- Upgrade after fork is hit
- Set pectra fork timestamp to the first timestamp at which there is a pectra block header
- Unpause
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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.
There was a problem hiding this 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)
Yup, targeting testnet. There will be a separate mainnet PR. cc @wadealexc |
src/contracts/pods/EigenPod.sol
Outdated
bytes32 beaconStateRoot, | ||
uint40 validatorIndex, | ||
bytes calldata validatorFieldsProof, | ||
bytes32[] calldata validatorFields | ||
) internal returns (uint256) { | ||
bytes32 pubkeyHash = validatorFields.getPubkeyHash(); | ||
ValidatorInfo memory validatorInfo = _validatorPubkeyHashToInfo[pubkeyHash]; | ||
ValidatorInfo memory validatorInfo = _validatorPubkeyHashToInfo[validatorFields.getPubkeyHash()]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming stack too deep here
@@ -378,6 +383,7 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC | |||
} | |||
|
|||
/// @notice Called by EigenPodManager when the owner wants to create another ETH validator. | |||
/// @dev This function only supports staking to a 0x01 validator. For compounding validators, please interact directly with the deposit contract. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be backlog to add stake function with compounding validators when we implement eip7002
@@ -35,4 +37,22 @@ abstract contract UpgradeTest is IntegrationCheckUtils { | |||
isUpgraded = true; | |||
emit log("_upgradeEigenLayerContracts: slashing upgrade complete"); | |||
} | |||
|
|||
uint64 constant PECTRA_FORK_TIMESTAMP = 1739352768; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where are you getting this value? Is this the expected timestamp of the holesky fork?
Wouldn't we need to wait to determine this until after the fork? We need the first non-skipped slot, which may not be this specific value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolved in slack -- this is basically a filler value because we can't know this beforehand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually - since this is a timestamp from a week ago, this is probably messing with tests?
We should maybe just have fork timestamp be current timestamp + 12 or something - that way we know we're moving forward in time if we warp to it.
|
||
/// @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( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's consider moving this logic into EigenPod
, since it's specific to the EIP-4788 oracle (and the EPM getter), which this library doesn't handle.
We can pass the proofs library an enum (DENEB|ELECTRA
), instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/contracts/pods/EigenPod.sol
Outdated
|
||
/// @notice Returns the timestamp of the Pectra fork, read from the `EigenPodManager` contract | ||
/// @dev Specifically, this returns the timestamp of the first non-missed slot at or after the Pectra hard fork | ||
function _getPectraForkTimestamp() internal view returns (uint64) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a require(result != 0)
here?
We should never be in a state where we can call this and have it return zero, so this is a sanity check that when we unpause, we have set a value :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// 3. Upgrade EigenPodManager & EigenPod | ||
_upgradeEigenLayerContracts(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This already calls _handlePectraFork
, so you're duplicating actions here to some extent. Can we pick whether we're adding special fork logic in UpgradeTest
, or in this file?
// 2. Randomly warp to just after the fork timestamp | ||
// If we do not warp, proofs will be against deneb state | ||
if (_randBool()) { | ||
// If we warp, proofs will be against electra state | ||
cheats.warp(block.timestamp + 1); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm pretty sure this is testing against electra regardless of whether you warp or not
@@ -562,7 +562,7 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser { | |||
|
|||
function _genRandUser(string memory name, uint userType) internal returns (User user) { | |||
// Create User contract based on userType: | |||
if (forkType == LOCAL) { | |||
if (forkType == LOCAL || (forkType == MAINNET && isUpgraded)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems this has some cherry picked stuff from another branch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, will be resolved once I merge this into feat/prooftra
and rebase that off of dev
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:
To do this, here is the upgrade process:
EigenPod
pectraForkTimestamp
Deprecation Plan
TODOs