-
Notifications
You must be signed in to change notification settings - Fork 957
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
eip6110: Queue deposit requests and apply them during epoch processing #3818
base: dev
Are you sure you want to change the base?
Conversation
specs/electra/beacon-chain.md
Outdated
else: | ||
validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey)) | ||
validator = state.validators[validator_index] | ||
# Validator is exiting, do not consume the churn |
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.
From @mkalinin this line is motivated by
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.
Why doesn't this condition validator.exit_epoch < FAR_FUTURE_EPOCH
match
consensus-specs/specs/electra/beacon-chain.md
Lines 870 to 871 in 43e7344
if get_current_epoch(state) <= validator.withdrawable_epoch: | |
return False |
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.
The idea is to postpone deposit processing for an exiting validator until it has fully withdrawn. And then do the top-up without affecting WS period.
reading through I think my question is answered. we need it based on this PR |
…process_pending_deposits.py Co-authored-by: Mikhail Kalinin <noblesse.knight@gmail.com>
…ikhail into deposit-queue
@@ -1194,21 +1282,25 @@ def apply_deposit(state: BeaconState, | |||
if pubkey not in validator_pubkeys: | |||
# Verify the deposit signature (proof of possession) which is not checked by the deposit contract | |||
if is_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature): | |||
add_validator_to_registry(state, pubkey, withdrawal_credentials, amount) | |||
add_validator_to_registry(state, pubkey, withdrawal_credentials, Gwei(0)) # [Modified in Electra:EIP7251] |
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.
what about
if pubkey not in validator_pubkeys:
if !s_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature):
break
add_validator_to_registry(state, pubkey, withdrawal_credentials, Gwei(0))
state.pending_deposits.append(PendingDeposit(
pubkey=pubkey,
withdrawal_credentials=withdrawal_credentials,
amount=amount,
signature=signature,
slot=GENESIS_SLOT,
))
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.
not sure if it makes these lines more readable, or there is another intention for this change?
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.
oops missed this comment, yeah just readability, as the appending logic is duplicated as far as I could tell
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.
great work!
I think this is the next best step here
I'd also want to see performance numbers if we just rely on the churn limit instead of having an additional constant
and I'd also lean against the next suggested step around managing the queue in a more sophisticated way unless its obvious we need it for performance reasons
The most optimal DoS strategy seems to be the following:
Each of these steps yields signature verification. Current churn limit allows to create 128 validators using this strategy, that is 256 signature verifications at the epoch processing boundary (256ms). The cost of the attack is locked ETH and some amount of ETH required to activate those validators by topping them up with 30 ETH and withdraw them. In the worst case it will take yet another 15 epochs to withdraw all malicious validators. Potential outcome and the cost makes this attack ineffective to my perspective. If the churn limit is increased then the outcome may become worse but the cost will also increase. |
withdrawal_credentials=validator.withdrawal_credentials, | ||
amount=excess_balance, | ||
signature=bls.G2_POINT_AT_INFINITY, | ||
slot=GENESIS_SLOT, |
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.
Just wondering in the case of devnet where Electra is already activated at genesis, would this (or other instance of appending PendingDeposit
with GENESIS_SLOT
to state.pending_deposit
) yield an unexpected result?
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.
The protocol does not accept a block proposed in the GENESIS_SLOT
because of the following check in the process_block_header
:
assert block.slot > state.latest_block_header.slot
@@ -1370,13 +1436,32 @@ def process_deposit_request(state: BeaconState, deposit_request: DepositRequest) | |||
if state.deposit_requests_start_index == UNSET_DEPOSIT_REQUESTS_START_INDEX: | |||
state.deposit_requests_start_index = deposit_request.index | |||
|
|||
apply_deposit( |
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.
Looks like apply_deposit()
along with process_deposit()
will be abandoned in Electra after all eth1 deposits are processed and will only be used for generating genesis state for devnets.
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.
Yes, these functions are going to be deprecated and I think that we will be able/should update the genesis state generation if possible and get rid of these functions
validator_pubkeys = [v.pubkey for v in state.validators] | ||
for deposit in state.pending_deposits: | ||
validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey)) | ||
increase_balance(state, validator_index, deposit.amount) |
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 you explain why deposit.index
is no longer a reliable way to access validator index but need to use pubkey to fetch it 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.
deposit.index
is the index of the deposit record in the smart contract, it can’t be used to derive the validator_index
anyhow
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.
just signalling support , but also waiting for switch_to_compounding
piggy backing off consolidations
Hello |
|
Description
Introduces majority of things described in #3689 (comment), specifically:
MAX_PENDING_DEPOSITS_PER_EPOCH
)(pubkey, index)
cache is required.pending_deposits
queue to ensure the deposit will pass through the activation churn.process_pending_deposits
by moving deposit applying toapply_pending_deposit
Other than the above, the logic of the previous
process_pending_balance_deposits
function remains.Additionally, moves. Removesswitch_to_compounding_validator
toprocess_deposit_request
switch_to_compounding_validator
from the deposit flow in favour of #3918.Points of discussion and open questions
validatorIndex
is looked up by thepubkey
(process_pending_deposits
andapply_pending_deposit
functions), this happens for each pending deposit that is being processed. There is a less readable workaround wherevalidatorIndex
is resolved once, but I am not sure if it is necessary because client implementations are likely to use cache in both cases.MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING
is set to16
, should it be more? The activation churn is limited by256 ETH
which would mean 256 signature verifications in the worst case. The most optimal scenario of an attack resulting in 256 sig verifications would entail creation of128
1-ETH validators (the first deposit would be to create a validator with eth1 creds, the second would be to switch the validator to compounding creds); it seems that employing such an attack is quite discouraging because it requires decent amount of ETH with no substantial gain. Should we even have this limitation if the churn could do the limit?Testing
Many thanks to @james-prysm for helping to write and adjust the tests!
apply_pending_deposits
checksprocess_pending_deposit
test_process_deposit_requests
scenarious in application toprocess_pending_deposit
MAX_PENDING_DEPOSITS_PER_EPOCH_PROCESSING
checktest_process_pending_deposit
(is_deposit_applied
,is_churn_limit_reached
etc)Link to a more comprehensive test plan: https://hackmd.io/mqV7TaFyTq--vEMcB5msJw
Supersedes #3689