Skip to content

Commit

Permalink
feat: add hardcoded fork timestamp; tests pass for deneb container
Browse files Browse the repository at this point in the history
  • Loading branch information
ypatil12 committed Jan 30, 2025
1 parent 37873be commit 2e9ab01
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 11 deletions.
33 changes: 27 additions & 6 deletions src/contracts/libraries/BeaconChainProofs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ library BeaconChainProofs {
/// | HEIGHT: VALIDATOR_TREE_HEIGHT
/// individual validators
uint256 internal constant BEACON_BLOCK_HEADER_TREE_HEIGHT = 3;
uint256 internal constant BEACON_STATE_TREE_HEIGHT = 6;
uint256 internal constant DENEB_BEACON_STATE_TREE_HEIGHT = 5;
uint256 internal constant PECTRA_BEACON_STATE_TREE_HEIGHT = 6;
uint256 internal constant BALANCE_TREE_HEIGHT = 38;
uint256 internal constant VALIDATOR_TREE_HEIGHT = 40;

Expand Down Expand Up @@ -134,17 +135,20 @@ library BeaconChainProofs {
/// @param validatorFieldsProof a merkle proof of inclusion of `validatorFields` under `beaconStateRoot`
/// @param validatorIndex the validator's unique index
function verifyValidatorFields(
uint64 proofTimestamp,
bytes32 beaconStateRoot,
bytes32[] calldata validatorFields,
bytes calldata validatorFieldsProof,
uint40 validatorIndex
) internal view {
require(validatorFields.length == VALIDATOR_FIELDS_LENGTH, InvalidValidatorFieldsLength());

uint256 beaconStateTreeHeight = getBeaconStateTreeHeight(proofTimestamp);

/// Note: the reason we use `VALIDATOR_TREE_HEIGHT + 1` here is because the merklization process for
/// this container includes hashing the root of the validator tree with the length of the validator list
require(
validatorFieldsProof.length == 32 * ((VALIDATOR_TREE_HEIGHT + 1) + BEACON_STATE_TREE_HEIGHT),
validatorFieldsProof.length == 32 * ((VALIDATOR_TREE_HEIGHT + 1) + beaconStateTreeHeight),
InvalidProofLength()
);

Expand Down Expand Up @@ -185,10 +189,15 @@ library BeaconChainProofs {
/// against the same balance container root.
/// @param beaconBlockRoot merkle root of the beacon block
/// @param proof a beacon balance container root and merkle proof of its inclusion under `beaconBlockRoot`
function verifyBalanceContainer(bytes32 beaconBlockRoot, BalanceContainerProof calldata proof) internal view {
function verifyBalanceContainer(
uint64 proofTimestamp,
bytes32 beaconBlockRoot,
BalanceContainerProof calldata proof
) internal view {
uint256 beaconStateTreeHeight = getBeaconStateTreeHeight(proofTimestamp);

require(
proof.proof.length == 32 * (BEACON_BLOCK_HEADER_TREE_HEIGHT + BEACON_STATE_TREE_HEIGHT),
InvalidProofLength()
proof.proof.length == 32 * (BEACON_BLOCK_HEADER_TREE_HEIGHT + beaconStateTreeHeight), InvalidProofLength()
);

/// This proof combines two proofs, so its index accounts for the relative position of leaves in two trees:
Expand All @@ -197,7 +206,7 @@ library BeaconChainProofs {
/// -- beaconStateRoot
/// | HEIGHT: BEACON_STATE_TREE_HEIGHT
/// ---- balancesContainerRoot
uint256 index = (STATE_ROOT_INDEX << (BEACON_STATE_TREE_HEIGHT)) | BALANCE_CONTAINER_INDEX;
uint256 index = (STATE_ROOT_INDEX << (beaconStateTreeHeight)) | BALANCE_CONTAINER_INDEX;

require(
Merkle.verifyInclusionSha256({
Expand Down Expand Up @@ -312,4 +321,16 @@ library BeaconChainProofs {
) internal pure returns (uint64) {
return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_EXIT_EPOCH_INDEX]);
}

/// @notice The timestamp of the Pectra hard fork - this is updated on a per-network basis
uint64 public constant PECTRA_FORK_TIMESTAMP = 1_739_352_768;

/// @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
) internal pure returns (uint256) {
return
proofTimestamp <= PECTRA_FORK_TIMESTAMP ? DENEB_BEACON_STATE_TREE_HEIGHT : PECTRA_BEACON_STATE_TREE_HEIGHT;
}
}
5 changes: 5 additions & 0 deletions src/contracts/pods/EigenPod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC

// Verify `balanceContainerProof` against `beaconBlockRoot`
BeaconChainProofs.verifyBalanceContainer({
proofTimestamp: checkpointTimestamp,
beaconBlockRoot: checkpoint.beaconBlockRoot,
proof: balanceContainerProof
});
Expand Down Expand Up @@ -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,
stateRootProof.beaconStateRoot,
validatorIndices[i],
validatorFieldsProofs[i],
Expand Down Expand Up @@ -341,6 +343,7 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC

// Verify Validator container proof against `beaconStateRoot`
BeaconChainProofs.verifyValidatorFields({
proofTimestamp: beaconTimestamp,
beaconStateRoot: stateRootProof.beaconStateRoot,
validatorFields: proof.validatorFields,
validatorFieldsProof: proof.proof,
Expand Down Expand Up @@ -419,6 +422,7 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC
* @param validatorFields are the fields of the "Validator Container", refer to consensus specs
*/
function _verifyWithdrawalCredentials(
uint64 beaconTimestamp,
bytes32 beaconStateRoot,
uint40 validatorIndex,
bytes calldata validatorFieldsProof,
Expand Down Expand Up @@ -484,6 +488,7 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC

// Verify passed-in validatorFields against verified beaconStateRoot:
BeaconChainProofs.verifyValidatorFields({
proofTimestamp: beaconTimestamp,
beaconStateRoot: beaconStateRoot,
validatorFields: validatorFields,
validatorFieldsProof: validatorFieldsProof,
Expand Down
2 changes: 2 additions & 0 deletions src/test/harnesses/EigenPodHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ contract EigenPodHarness is EigenPod {
}

function verifyWithdrawalCredentials(
uint64 beaconTimestamp,
bytes32 beaconStateRoot,
uint40 validatorIndex,
bytes calldata validatorFieldsProof,
bytes32[] calldata validatorFields
) public returns (uint256) {
return _verifyWithdrawalCredentials(
beaconTimestamp,
beaconStateRoot,
validatorIndex,
validatorFieldsProof,
Expand Down
10 changes: 5 additions & 5 deletions src/test/integration/mocks/BeaconChainMock.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ contract BeaconChainMock is Logger {

uint immutable BLOCKROOT_PROOF_LEN = 32 * BeaconChainProofs.BEACON_BLOCK_HEADER_TREE_HEIGHT;
uint immutable VAL_FIELDS_PROOF_LEN = 32 * (
(BeaconChainProofs.VALIDATOR_TREE_HEIGHT + 1) + BeaconChainProofs.BEACON_STATE_TREE_HEIGHT
(BeaconChainProofs.VALIDATOR_TREE_HEIGHT + 1) + BeaconChainProofs.DENEB_BEACON_STATE_TREE_HEIGHT
);
uint immutable BALANCE_CONTAINER_PROOF_LEN = 32 * (
BeaconChainProofs.BEACON_BLOCK_HEADER_TREE_HEIGHT + BeaconChainProofs.BEACON_STATE_TREE_HEIGHT
BeaconChainProofs.BEACON_BLOCK_HEADER_TREE_HEIGHT + BeaconChainProofs.DENEB_BEACON_STATE_TREE_HEIGHT
);
uint immutable BALANCE_PROOF_LEN = 32 * (BeaconChainProofs.BALANCE_TREE_HEIGHT + 1);

Expand Down Expand Up @@ -409,7 +409,7 @@ contract BeaconChainMock is Logger {
// Build merkle tree for BeaconState
bytes32 beaconStateRoot = _buildMerkleTree({
leaves: _getBeaconStateLeaves(validatorsRoot, balanceContainerRoot),
treeHeight: BeaconChainProofs.BEACON_STATE_TREE_HEIGHT,
treeHeight: BeaconChainProofs.DENEB_BEACON_STATE_TREE_HEIGHT,
tree: trees[curTimestamp].stateTree
});
// console.log("-- beacon state root", beaconStateRoot);
Expand Down Expand Up @@ -595,7 +595,7 @@ contract BeaconChainMock is Logger {

uint totalHeight = BALANCE_CONTAINER_PROOF_LEN / 32;
uint depth = 0;
for (uint i = 0; i < BeaconChainProofs.BEACON_STATE_TREE_HEIGHT; i++) {
for (uint i = 0; i < BeaconChainProofs.DENEB_BEACON_STATE_TREE_HEIGHT; i++) {
bytes32 sibling = trees[curTimestamp].stateTree.siblings[curNode];

// proof[j] = sibling;
Expand Down Expand Up @@ -661,7 +661,7 @@ contract BeaconChainMock is Logger {
// Validator container root -> beacon state root
for (
uint j = depth;
j < 1 + BeaconChainProofs.VALIDATOR_TREE_HEIGHT + BeaconChainProofs.BEACON_STATE_TREE_HEIGHT;
j < 1 + BeaconChainProofs.VALIDATOR_TREE_HEIGHT + BeaconChainProofs.DENEB_BEACON_STATE_TREE_HEIGHT;
j++
) {
bytes32 sibling = trees[curTimestamp].stateTree.siblings[curNode];
Expand Down
1 change: 1 addition & 0 deletions src/test/unit/EigenPodUnit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1783,6 +1783,7 @@ contract EigenPodUnitTests_proofParsingTests is EigenPodHarnessSetup, ProofParsi
oracleTimestamp = uint64(block.timestamp);

eigenPodHarness.verifyWithdrawalCredentials(
oracleTimestamp,
beaconStateRoot,
validatorIndex,
validatorFieldsProof,
Expand Down

0 comments on commit 2e9ab01

Please sign in to comment.