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

Hexen's Audit Addressal + Further testing #219

Merged
merged 33 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a3888af
fixed hexens issues, need to add regression tests
Sidu28 Oct 4, 2023
b5471c8
added index regression tests
Sidu28 Oct 4, 2023
8f9e4ea
created a unit test file
Sidu28 Oct 4, 2023
228d1e2
added more tests
Sidu28 Oct 4, 2023
ce1594d
fixed testDecrementMoreThanRestakedExecutionLayerGwei
Sidu28 Oct 4, 2023
68c4b10
added testIncrementWithdrawableRestakedExecutionLayerGwei
Sidu28 Oct 4, 2023
2a73f92
added testDecrementWithdrawableRestakedExecutionLayerGwei
Sidu28 Oct 4, 2023
7354d6d
changes
Sidu28 Oct 5, 2023
2389d83
fixing tests
Sidu28 Oct 5, 2023
3307157
fixed tests for hexens fixes
Sidu28 Oct 5, 2023
4086295
added two more tests
Sidu28 Oct 5, 2023
ee0014d
raddressing EIG 7
Sidu28 Oct 5, 2023
6dffb15
added test
Sidu28 Oct 5, 2023
3acc5ea
added regression test for EIG14
Sidu28 Oct 6, 2023
51f9667
added check for validator balance update against withdrawable epoch
Sidu28 Oct 9, 2023
954eb8f
Merge branch 'eig-14-addressal' of https://github.com/Layr-Labs/eigen…
Sidu28 Oct 9, 2023
68b9215
added reg test
Sidu28 Oct 9, 2023
34df945
fin
Sidu28 Oct 9, 2023
505b37b
fixed breaking tests
Sidu28 Oct 9, 2023
381b75c
almost all tests working
Sidu28 Oct 9, 2023
a257f46
almost all tests working
Sidu28 Oct 9, 2023
753fba7
fixed flaky test
Sidu28 Oct 9, 2023
12c71fe
pushing again
Sidu28 Oct 10, 2023
abc94f0
removed extraneous function in tests
Sidu28 Oct 10, 2023
882e5d8
cleanup, test
Sidu28 Oct 10, 2023
008019c
updated regression test
Sidu28 Oct 10, 2023
98bdc5c
hmm
Sidu28 Oct 10, 2023
deca191
fixed balanceRoot
Sidu28 Oct 10, 2023
c3d57b5
Merge pull request #230 from Layr-Labs/verifybalanceupdatebugfix
Sidu28 Oct 10, 2023
0403881
Merge branch 'eig-14-addressal' of https://github.com/Layr-Labs/eigen…
Sidu28 Oct 10, 2023
a453970
Merge pull request #226 from Layr-Labs/eig-14-addressal
Sidu28 Oct 10, 2023
9af7c3a
fixed breaking tests
Sidu28 Oct 10, 2023
873362f
moved test
Sidu28 Oct 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/core/EigenPodManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Note: the functions of the `EigenPodManager` and `EigenPod` contracts are tightl
* The calculation subtracts an offset (`RESTAKED_BALANCE_OFFSET_GWEI`) from the validator's proven balance, and round down to the nearest ETH
* Related: `uint64 RESTAKED_BALANCE_OFFSET_GWEI`
* As of M2, this is 0.75 ETH (in Gwei)
* Related: `uint64 MAX_VALIDATOR_BALANCE_GWEI`
* Related: `uint64 MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR`
* As of M2, this is 31 ETH (in Gwei)
* This is the maximum amount of restaked ETH a single validator can be credited with in EigenLayer
* `_podWithdrawalCredentials() -> (bytes memory)`:
Expand Down Expand Up @@ -363,7 +363,7 @@ Whether each withdrawal is a full or partial withdrawal is determined by the val
* The validator in question is recorded as having a proven withdrawal at the timestamp given by `withdrawalProof.timestampRoot`
* This is to prevent the same withdrawal from being proven twice
* If this is a full withdrawal:
* Any withdrawal amount in excess of `_calculateRestakedBalanceGwei(MAX_VALIDATOR_BALANCE_GWEI)` is immediately withdrawn (see [`DelayedWithdrawalRouter.createDelayedWithdrawal`](#delayedwithdrawalroutercreatedelayedwithdrawal))
* Any withdrawal amount in excess of `_calculateRestakedBalanceGwei(MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR)` is immediately withdrawn (see [`DelayedWithdrawalRouter.createDelayedWithdrawal`](#delayedwithdrawalroutercreatedelayedwithdrawal))
* The remainder must be withdrawn through `EigenPodManager.queueWithdrawal`, but in the meantime is added to `EigenPod.withdrawableRestakedExecutionLayerGwei`
* If the amount being withdrawn is not equal to the current accounted-for validator balance, a `shareDelta` is calculated to be sent to ([`EigenPodManager.recordBeaconChainETHBalanceUpdate`](#eigenpodmanagerrecordbeaconchainethbalanceupdate)).
* The validator's info is updated to reflect its `WITHDRAWN` status:
Expand Down
6 changes: 3 additions & 3 deletions docs/outdated/EigenPods.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The following sections are all related to managing Consensus Layer (CL) and Exec
When EigenPod contracts are initially deployed, the "restaking" functionality is turned off - the withdrawal credential proof has not been initiated yet. In this "non-restaking" mode, the contract may be used by its owner freely to withdraw validator balances from the beacon chain via the `withdrawBeforeRestaking` function. This function routes the withdrawn balance directly to the `DelayedWithdrawalRouter` contract. Once the EigenPod's owner verifies that their withdrawal credentials are pointed to the EigenPod via `verifyWithdrawalCredentialsAndBalance`, the `hasRestaked` flag will be set to true and any withdrawals must now be proven for via the `verifyAndProcessWithdrawal` function.

### Merkle Proof of Correctly Pointed Withdrawal Credentials
After staking an Ethereum validator with its withdrawal credentials pointed to their EigenPod, a staker must show that the new validator exists and has its withdrawal credentials pointed to the EigenPod, by proving it against a beacon state root with a call to `verifyWithdrawalCredentialsAndBalance`. The EigenPod will verify the proof (along with checking for replays and other conditions) and, if the ETH validator's effective balance is proven to be greater than or equal to `MAX_VALIDATOR_BALANCE_GWEI`, then the EigenPod will pass the validator's effective balance value through its own hysteresis calculation (see [here](#hysteresis)), which effectively underestimates the effective balance of the validator by 1 ETH. Then a call is made to the EigenPodManager to forward a call to the StrategyManager, crediting the staker with those shares of the virtual beacon chain ETH strategy.
After staking an Ethereum validator with its withdrawal credentials pointed to their EigenPod, a staker must show that the new validator exists and has its withdrawal credentials pointed to the EigenPod, by proving it against a beacon state root with a call to `verifyWithdrawalCredentialsAndBalance`. The EigenPod will verify the proof (along with checking for replays and other conditions) and, if the ETH validator's effective balance is proven to be greater than or equal to `MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR`, then the EigenPod will pass the validator's effective balance value through its own hysteresis calculation (see [here](#hysteresis)), which effectively underestimates the effective balance of the validator by 1 ETH. Then a call is made to the EigenPodManager to forward a call to the StrategyManager, crediting the staker with those shares of the virtual beacon chain ETH strategy.

### Effective Restaked Balance - Hysteresis {#hysteresis}
To convey to EigenLayer that an EigenPod has validator(s) restaked on it, anyone can submit a proof against a beacon chain state root the proves that a validator has their withdrawal credentials pointed to the pod. The proof is verified and the EigenPod calls the EigenPodMananger that calls the StrategyManager which records the validators proven balance run through the hysteresis function worth of ETH in the "beaconChainETH" strategy. Each EigenPod keeps track of all of the validators by the hash of their public key. For each validator, their validator index and current balance in EigenLayer is kept track of.
Expand All @@ -54,9 +54,9 @@ We also must prove the `executionPayload.blockNumber > mostRecentWithdrawalBlock

In this second case, in order to withdraw their balance from the EigenPod, stakers must provide a valid proof of their full withdrawal against a beacon chain state root. Full withdrawals are differentiated from partial withdrawals by checking against the validator in question's 'withdrawable epoch'; if the validator's withdrawable epoch is less than or equal to the slot's epoch, then the validator has fully withdrawn because a full withdrawal is only processable at or after the withdrawable epoch has passed. Once the full withdrawal is successfully verified, there are 2 cases, each handled slightly differently:

1. If the withdrawn amount is greater than `MAX_VALIDATOR_BALANCE_GWEI_GWEI`, then `MAX_VALIDATOR_BALANCE_GWEI` is held for processing through EigenLayer's normal withdrawal path, while the excess amount above `MAX_VALIDATOR_BALANCE_GWEI` is marked as instantly withdrawable.
1. If the withdrawn amount is greater than `MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR`, then `MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR` is held for processing through EigenLayer's normal withdrawal path, while the excess amount above `MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR` is marked as instantly withdrawable.

2. If the withdrawn amount is less than `MAX_VALIDATOR_BALANCE_GWEI`, then the amount being withdrawn is held for processing through EigenLayer's normal withdrawal path.
2. If the withdrawn amount is less than `MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR`, then the amount being withdrawn is held for processing through EigenLayer's normal withdrawal path.

### The EigenPod Invariant
The core complexity of the EigenPods system is to ensure that EigenLayer continuously has an accurate picture of the state of the beacon chain balances repointed to it. In other words, the invariant that governs this system is:
Expand Down
11 changes: 6 additions & 5 deletions script/M1_Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ contract Deployer_M1 is Script, Test {

// IMMUTABLES TO SET
uint256 REQUIRED_BALANCE_WEI;
uint256 MAX_VALIDATOR_BALANCE_GWEI;
uint256 MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR;
uint256 EFFECTIVE_RESTAKED_BALANCE_OFFSET_GWEI;
uint64 GENESIS_TIME = 1616508000;
uint64 GOERLI_GENESIS_TIME = 1616508000;

// OTHER DEPLOYMENT PARAMETERS
uint256 STRATEGY_MANAGER_INIT_PAUSED_STATUS;
Expand Down Expand Up @@ -113,7 +113,7 @@ contract Deployer_M1 is Script, Test {
DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32(stdJson.readUint(config_data, ".strategyManager.init_withdrawal_delay_blocks"));

REQUIRED_BALANCE_WEI = stdJson.readUint(config_data, ".eigenPod.REQUIRED_BALANCE_WEI");
MAX_VALIDATOR_BALANCE_GWEI = stdJson.readUint(config_data, ".eigenPod.MAX_VALIDATOR_BALANCE_GWEI");
MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = stdJson.readUint(config_data, ".eigenPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR");
EFFECTIVE_RESTAKED_BALANCE_OFFSET_GWEI = stdJson.readUint(config_data, ".eigenPod.EFFECTIVE_RESTAKED_BALANCE_OFFSET_GWEI");

// tokens to deploy strategies for
Expand Down Expand Up @@ -176,8 +176,9 @@ contract Deployer_M1 is Script, Test {
ethPOSDeposit,
delayedWithdrawalRouter,
eigenPodManager,
uint64(MAX_VALIDATOR_BALANCE_GWEI),
uint64(EFFECTIVE_RESTAKED_BALANCE_OFFSET_GWEI)
uint64(MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR),
uint64(EFFECTIVE_RESTAKED_BALANCE_OFFSET_GWEI),
GOERLI_GENESIS_TIME
);

eigenPodBeacon = new UpgradeableBeacon(address(eigenPodImplementation));
Expand Down
2 changes: 1 addition & 1 deletion script/M1_deploy.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
{
"PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS": 50400,
"REQUIRED_BALANCE_WEI": "31000000000000000000",
"MAX_VALIDATOR_BALANCE_GWEI": "32000000000",
"MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": "31000000000",
"EFFECTIVE_RESTAKED_BALANCE_OFFSET_GWEI": "750000000"
},
"eigenPodManager":
Expand Down
17 changes: 9 additions & 8 deletions script/testing/M2_Deploy_From_Scratch.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ contract Deployer_M2 is Script, Test {
StrategyBaseTVLLimits[] public deployedStrategyArray;

// IMMUTABLES TO SET
uint64 MAX_VALIDATOR_BALANCE_GWEI;
uint64 MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR;
uint64 RESTAKED_BALANCE_OFFSET_GWEI;
uint64 GENESIS_TIME = 1616508000;
uint64 GOERLI_GENESIS_TIME = 1616508000;

// OTHER DEPLOYMENT PARAMETERS
uint256 STRATEGY_MANAGER_INIT_PAUSED_STATUS;
Expand Down Expand Up @@ -113,7 +113,7 @@ contract Deployer_M2 is Script, Test {
STRATEGY_MANAGER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32(stdJson.readUint(config_data, ".strategyManager.init_withdrawal_delay_blocks"));
DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32(stdJson.readUint(config_data, ".strategyManager.init_withdrawal_delay_blocks"));

MAX_VALIDATOR_BALANCE_GWEI = uint64(stdJson.readUint(config_data, ".eigenPod.MAX_VALIDATOR_BALANCE_GWEI"));
MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = uint64(stdJson.readUint(config_data, ".eigenPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR"));
RESTAKED_BALANCE_OFFSET_GWEI = uint64(stdJson.readUint(config_data, ".eigenPod.RESTAKED_BALANCE_OFFSET_GWEI"));

// tokens to deploy strategies for
Expand Down Expand Up @@ -176,8 +176,9 @@ contract Deployer_M2 is Script, Test {
ethPOSDeposit,
delayedWithdrawalRouter,
eigenPodManager,
MAX_VALIDATOR_BALANCE_GWEI,
RESTAKED_BALANCE_OFFSET_GWEI
MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR,
RESTAKED_BALANCE_OFFSET_GWEI,
GOERLI_GENESIS_TIME
);

eigenPodBeacon = new UpgradeableBeacon(address(eigenPodImplementation));
Expand Down Expand Up @@ -444,9 +445,9 @@ contract Deployer_M2 is Script, Test {
// "strategyManager: withdrawalDelayBlocks initialized incorrectly");
// require(delayedWithdrawalRouter.withdrawalDelayBlocks() == 7 days / 12 seconds,
// "delayedWithdrawalRouter: withdrawalDelayBlocks initialized incorrectly");
// uint256 MAX_VALIDATOR_BALANCE_GWEI = 31 ether;
require(eigenPodImplementation.MAX_VALIDATOR_BALANCE_GWEI() == 31 gwei,
"eigenPod: MAX_VALIDATOR_BALANCE_GWEI initialized incorrectly");
// uint256 MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = 31 ether;
require(eigenPodImplementation.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR() == 31 gwei,
"eigenPod: MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR initialized incorrectly");

require(strategyManager.strategyWhitelister() == operationsMultisig,
"strategyManager: strategyWhitelister address not set correctly");
Expand Down
2 changes: 1 addition & 1 deletion script/testing/M2_deploy_from_scratch.anvil.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"eigenPod": {
"PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS": 1,
"MAX_VALIDATOR_BALANCE_GWEI": "31000000000",
"MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": "31000000000",
"RESTAKED_BALANCE_OFFSET_GWEI": "750000000"
},
"eigenPodManager": {
Expand Down
2 changes: 1 addition & 1 deletion script/testing/M2_deploy_from_scratch.mainnet.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"eigenPod":
{
"PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS": 50400,
"MAX_VALIDATOR_BALANCE_GWEI": "31000000000",
"MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": "31000000000",
"RESTAKED_BALANCE_OFFSET_GWEI": "750000000"

},
Expand Down
5 changes: 3 additions & 2 deletions script/upgrade/GoerliM2Upgrade.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ contract GoerliM2Deployment is Script, Test {
_ethPOS: ethPOS,
_delayedWithdrawalRouter: delayedWithdrawalRouter,
_eigenPodManager: eigenPodManager,
_MAX_VALIDATOR_BALANCE_GWEI: 31 gwei,
_RESTAKED_BALANCE_OFFSET_GWEI: 0.5 gwei
_MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR: 31 gwei,
_RESTAKED_BALANCE_OFFSET_GWEI: 0.5 gwei,
_GENESIS_TIME: 1616508000
});

// write the output to a contract
Expand Down
3 changes: 2 additions & 1 deletion script/upgrade/GoerliUpgrade1.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ contract GoerliUpgrade1 is Script, Test {
delayedWithdrawalRouter,
eigenPodManager,
32e9,
75e7
75e7,
1616508000
)
);

Expand Down
5 changes: 4 additions & 1 deletion src/contracts/interfaces/IEigenPod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,14 @@ interface IEigenPod {


/// @notice The max amount of eth, in gwei, that can be restaked per validator
function MAX_VALIDATOR_BALANCE_GWEI() external view returns (uint64);
function MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR() external view returns (uint64);

/// @notice the amount of execution layer ETH in this contract that is staked in EigenLayer (i.e. withdrawn from beaconchain but not EigenLayer),
function withdrawableRestakedExecutionLayerGwei() external view returns (uint64);

/// @notice any ETH deposited into the EigenPod contract via the `receive` fallback function
function nonBeaconChainETHBalanceWei() external view returns (uint256);

/// @notice Used to initialize the pointers to contracts crucial to the pod's functionality, in beacon proxy construction from EigenPodManager
function initialize(address owner) external;

Expand Down
5 changes: 5 additions & 0 deletions src/contracts/libraries/BeaconChainProofs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,11 @@ library BeaconChainProofs {
"BeaconChainProofs.verifyWithdrawal: withdrawalIndex is too large"
);

require(
withdrawalProof.historicalSummaryIndex < 2 ** HISTORICAL_SUMMARIES_TREE_HEIGHT,
"BeaconChainProofs.verifyWithdrawal: historicalSummaryIndex is too large"
);

Sidu28 marked this conversation as resolved.
Show resolved Hide resolved
require(
withdrawalProof.withdrawalProof.length ==
32 * (EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT + WITHDRAWALS_TREE_HEIGHT + 1),
Expand Down
Loading
Loading