-
Notifications
You must be signed in to change notification settings - Fork 90
New Compounding Staking Strategy post Pectra upgrade #2559
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
base: master
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #2559 +/- ##
==========================================
+ Coverage 38.26% 41.82% +3.55%
==========================================
Files 112 122 +10
Lines 5331 5691 +360
Branches 1412 1509 +97
==========================================
+ Hits 2040 2380 +340
- Misses 3289 3309 +20
Partials 2 2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
contracts/contracts/strategies/NativeStaking/CompoundingValidatorManager.sol
Outdated
Show resolved
Hide resolved
contracts/contracts/strategies/NativeStaking/CompoundingValidatorManager.sol
Outdated
Show resolved
Hide resolved
contracts/contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol
Show resolved
Hide resolved
contracts/contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol
Outdated
Show resolved
Hide resolved
contracts/contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol
Outdated
Show resolved
Hide resolved
contracts/contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol
Outdated
Show resolved
Hide resolved
contracts/contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol
Show resolved
Hide resolved
Renamed HH tasks copyBeaconRoot and added mockBeaconRoot
Restored extra check of timestamp in future in BeaconRoots lib
Added unit tests for verifyDeposit
contracts/contracts/strategies/NativeStaking/CompoundingValidatorManager.sol
Outdated
Show resolved
Hide resolved
/// @notice Mapping of the root of a deposit (depositDataRoot) to its data | ||
mapping(bytes32 => DepositData) public deposits; | ||
/// @notice List of deposit roots that are still to be verified as processed on the beacon chain | ||
bytes32[] public depositsRoots; |
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.
It could be useful to have a function that returns the length of this list.
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.
Unfortunately adding a function that returns the number of deposits will make the contract too big
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 added it on my invariant repo and it didn't make the contract too big, I mean, the deployment didn't revert.
Maybe foundry optimize it a bit by default, which reduce the size of the code in comparison with Hardhat or something.
// Delete the last deposit from the list | ||
depositsRoots.pop(); | ||
|
||
emit DepositVerified(depositDataRoot, deposit.amountGwei * 1 gwei); |
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.
deposit.amountGwei * 1 gwei
It will overflow when amountGwei is above 18 gwei, it could be fixed with uint256(deposit.amountGwei) * 1 gwei
.
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.
Nice pickup. I've fixed
require( | ||
consolidationLastPubKeyHash == bytes32(0), | ||
"Consolidation in progress" | ||
); |
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 think this require is not reachable (i.e. it will never revert).
We are looking for a situation where consolidationLastPubKeyHash != bytes32(0)
.
This can happen only in the situation where requestConsolidation()
has been called. But when requestConsolidation()
is called, it pause
the contract which prevent the snapBalance
to be called.
But still not 100% sure. What do you think?
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.
That's true and it takes 0.078 Kb off the contract size!
I've removed now pause is exclusively used for consolidations.
Added depth to the proof logs Added dryrun option to verify tasks
validator[depositData.pubKeyHash].state == | ||
ValidatorState.EXITED | ||
) { | ||
_removeDeposit(depositID, depositData); |
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.
🟡 Reading the beacon spec it seems to me that deposits to exited_validators - which in beacon context means they have stopped performing their tasks but are not yet withdrawable. As the safety period needs to pass, which is needed to apply any slashing penalties, they arponed (as we already know). Once a validator becomes withdrawable the postponed deposit is applied (increases the balance) of the said validator without consuming churn. That is done by just increasing its balance.
Depending on the validator id and where the beacon chain sweep cycle is & also on the size of deposit queue + deposited amounts what can happen is:
- deposit to a validator is done
- validator is being slashed unknown to strategy contract
- the deposit becomes a pending deposit being postponed
- validator has become withdrawable
- beacon chain sweep cycle processes it and sets it to 0 balance (the postponed deposit is still in the beacon chain)
- we create a
snapBalances
andverifyBalances
and assume that the validator's balance + the pending deposit has been credited back to the strategy contract. In reality only the validator's balance has been credited back. Our strategy would show an ETH deficit until the postponed deposit is processed - the beacon chain processes the deposit and adds it to the validator balance
- the beacon chain sweep cycle processes that validator again, crediting the strategy contract with the deposit value
- only once we create a
snapBalances
andverifyBalances
again will we show the correct balances.
I think there is nothing we should do to address this issue, since it is (almost) not possible to prove non existence of a pending deposit (or postponed deposit) in a deposit queue. Also the strategy contract would not double count the ETH balance but rather just show a deficit for the time it takes the beacon chain to process the deposit and sweep it again. It would be helpful to have this recorded as a comment though.
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.
Oh now I see OZ has somewhat pointed to this option with M-01
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.
Our fix for M-01 does prevent this from happening if we trigger an exit. But not if the validator is slashed.
* simplify snapping balances * type simplification
* Updated Natspec of verifyDeposit * Moved 3rd party contract interactions to after storage variables are updated
* improve comments * add CEI pattern * add comment * 70b contract size optimisation * add comment * prettier
…as the first pending deposit if the deposit queue is empty
#2639) * add the support to verify BLS signature before depositing to validator * prettier
* verifyDeposit removed check if the validator of the first deposit was exiting * Fixed the verifyDeposit unit test data
A new staking strategy that uses merkle proofs to verify the operations of validators running on the beacon chain.
Objectives
Links
Relevant Pectra Release changes
Contracts
The scope of the contracts of this build is
CompoundingStakingSSVStrategy
is the new staking contract.CompoundingValidatorManager
is inherited byCompoundingStakingSSVStrategy
CompoundingStakingStrategyView
has two view functions forCompoundingStakingSSVStrategy
BeaconProofs
called fromCompoundingValidatorManager
The following libraries have also be built
BeaconRoots
BeaconProofsLib
PartialWithdrawal
Merkle
Endian
Hoodi
States
Beacon chain data
Beacon Container Specs
To get the consolidate beacon chain specification for the last Pectra release
build/lib/eth2spec/electra/mainnet.py
will have the full, consolidated changes. This includes all the beacon chain container definitions. eg BeaconBlock, BeaconBlockBody, BeaconState, Validator...Processes
Register a SSV validator
Initial deposit to a new validator
Deposit more to existing validator
Verify validator
Verify deposit to validator
Update strategy balances
Withdrawals
Admin
Build
Keeping the contract under 24Kb has been a problem during development. The following will show the contract size of the compile contracts including
CompoundingStakingSSVStrategy
.export CONTRACT_SIZE=true npx hardhat compile
Testing
Unit Tests
Are main strategy unit tests in
contracts/test/strategies/compoundingSSVStaking.js
There are also unit tests of the beacon merkle proofs in
contracts/test/beacon/beaconProofs.js
yarn test
Fork Tests
There are no fork tests of the strategy as its hard to mock the beacon chain. But there are fork tests of various beacon chain contracts and libraries. These includes:
Hoodi Testnet
Hoodi faucet: https://hoodi-faucet.pk910.de/
SSV faucet: https://faucet.ssv.network/
Contracts have been deployed to Hoodi with deployment scripts in
contracts/deploy/hoodi
export DEPLOYER_PK= yarn run deploy:hoodi npx hardhat etherscan-verify --network hoodi --api-url https://api-hoodi.etherscan.io
Hoodi testing using Hardhat tasks
Deployment
Hoodi
Code Change Checklist
To be completed before internal review begins:
Internal review: