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

In-protocol deposits flow (no queue approach) #3177

Merged
merged 26 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c53ab45
Add in-protocol deposit processing
mkalinin Nov 24, 2022
b3c771c
Preserve deposits per epoch boundary
mkalinin Dec 7, 2022
5ea983a
Fix toc, add more comments
mkalinin Dec 7, 2022
8cc293c
Fix wording
mkalinin Dec 7, 2022
9d2a8f7
Give deposits EIP a name
mkalinin Dec 19, 2022
48f120c
Set a higher limit to deposit receipts list
mkalinin Dec 21, 2022
6eb118d
Fix finality check in deposit processing
mkalinin Dec 22, 2022
e64607f
Merge branch 'dev' into deposits
mkalinin Feb 21, 2023
d5c7474
Move EIP6110 to features
mkalinin Feb 21, 2023
08c7287
Get rid of pending_deposits queue
mkalinin Feb 22, 2023
23c10cf
Remove state.deposit_receipt_next_index variable
mkalinin Feb 23, 2023
b22c892
Cosmetic renaming
mkalinin Feb 23, 2023
a1daac0
Make EIP-6110 executable and fix linter errors
hwwhww Feb 23, 2023
7d6831e
Fix initialize_beacon_state_from_eth1 definition
mkalinin Feb 23, 2023
703fdfc
Fix linter
mkalinin Feb 23, 2023
fda0eae
Add EIP6110 to pylint and mypy scope
hwwhww Feb 23, 2023
9d690a4
Fix typo
hwwhww Feb 24, 2023
de5be63
Apply suggestions from code review
mkalinin Feb 28, 2023
fae77eb
Apply @hwwhww suggestions
mkalinin Feb 28, 2023
7bb65f8
Cosmetic fix
mkalinin Feb 28, 2023
4a59bcf
Merge branch 'dev' into deposits
hwwhww Feb 28, 2023
c445fa9
Apply suggestions from code review
mkalinin Mar 2, 2023
13f3654
Apply suggestions from @djrtwo
mkalinin Mar 2, 2023
00557c5
Remove unnecessary eth1_deposit_index bump
mkalinin Mar 2, 2023
0da79bd
Provide validator guide for EIP6110
mkalinin Mar 9, 2023
0ae18d8
Update specs/_features/eip6110/validator.md
djrtwo Mar 14, 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
109 changes: 10 additions & 99 deletions specs/_features/eip6110/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Block processing](#block-processing)
- [Modified `process_operations`](#modified-process_operations)
- [New `get_validator_from_deposit_data`](#new-get_validator_from_deposit_data)
- [New `apply_deposit`](#new-apply_deposit)
- [New `process_deposit_receipt`](#new-process_deposit_receipt)
- [Modified `process_deposit`](#modified-process_deposit)
- [Modified `process_execution_payload`](#modified-process_execution_payload)
- [Testing](#testing)

Expand All @@ -36,7 +33,7 @@
This is the beacon chain specification of in-protocol deposits processing mechanism.
This mechanism relies on the changes proposed by [EIP-6110](http://eips.ethereum.org/EIPS/eip-6110).

*Note:* This specification is under development and should be used with care.
*Note:* This specification is built upon [Capella](../../capella/beacon_chain.md) and is under active development.

djrtwo marked this conversation as resolved.
Show resolved Hide resolved
## Constants

Expand All @@ -46,7 +43,7 @@ The following values are (non-configurable) constants used throughout the specif

| Name | Value |
| - | - |
| `NOT_SET_DEPOSIT_RECEIPTS_START_INDEX` | `uint64(2**64 - 1)` |
| `UNSET_DEPOSIT_RECEIPTS_START_INDEX` | `uint64(2**64 - 1)` |

## Preset

Expand Down Expand Up @@ -166,7 +163,7 @@ class BeaconState(Container):
next_withdrawal_validator_index: ValidatorIndex
# Deep history valid from Capella onwards
historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT]
# EIP-6110
# [New in EIP-6110]
deposit_receipts_start_index: uint64
```

Expand All @@ -192,10 +189,11 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:

```python
def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
# [New in EIP-6110]
# Prevent potential underflow introduced by mixing two deposit processing flows
if state.eth1_data.deposit_count > state.eth1_deposit_index:
assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)
# [Modified in EIP-6110]
# Disable former deposit mechanism once all prior deposits are processed
eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_receipts_start_index)
if state.eth1_deposit_index < eth1_deposit_index_limit:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! yes, much cleaner and easier to reason about. 🙏

assert len(body.deposits) == min(MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index)
else:
assert len(body.deposits) == 0

Expand All @@ -215,68 +213,14 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
for_ops(body.execution_payload.deposit_receipts, process_deposit_receipt)
```

#### New `get_validator_from_deposit_data`

```python
def get_validator_from_deposit_data(pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64) -> Validator:
effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)

return Validator(
pubkey=pubkey,
withdrawal_credentials=withdrawal_credentials,
activation_eligibility_epoch=FAR_FUTURE_EPOCH,
activation_epoch=FAR_FUTURE_EPOCH,
exit_epoch=FAR_FUTURE_EPOCH,
withdrawable_epoch=FAR_FUTURE_EPOCH,
effective_balance=effective_balance,
)
```

#### New `apply_deposit`

```python
def apply_deposit(state: BeaconState,
pubkey: BLSPubkey,
withdrawal_credentials: Bytes32,
amount: uint64,
signature: BLSSignature,
) -> None:
validator_pubkeys = [validator.pubkey for validator in state.validators]
if pubkey not in validator_pubkeys:
# Verify the deposit signature (proof of possession) which is not checked by the deposit contract
deposit_message = DepositMessage(
pubkey=pubkey,
withdrawal_credentials=withdrawal_credentials,
amount=amount,
)
domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks
signing_root = compute_signing_root(deposit_message, domain)
# Initialize validator if the deposit signature is valid
if bls.Verify(pubkey, signing_root, signature):
state.validators.append(get_validator_from_deposit_data(pubkey, withdrawal_credentials, amount))
state.balances.append(amount)
state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000))
state.current_epoch_participation.append(ParticipationFlags(0b0000_0000))
state.inactivity_scores.append(uint64(0))
else:
# Increase balance by deposit amount
index = ValidatorIndex(validator_pubkeys.index(pubkey))
increase_balance(state, index, amount)
```


#### New `process_deposit_receipt`

```python
def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) -> None:
# Set deposit receipt start index
if state.deposit_receipts_start_index == NOT_SET_DEPOSIT_RECEIPTS_START_INDEX:
if state.deposit_receipts_start_index == UNSET_DEPOSIT_RECEIPTS_START_INDEX:
state.deposit_receipts_start_index = deposit_receipt.index

# Signify the end of transition to in-protocol deposit logic
if state.eth1_deposit_index >= state.deposit_receipts_start_index:
state.eth1_deposit_index = deposit_receipt.index + 1

apply_deposit(
state=state,
pubkey=deposit_receipt.pubkey,
Expand All @@ -286,39 +230,6 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt)
)
```

#### Modified `process_deposit`

*Note*: The function `process_deposit` is modified to prevent deposits from being processed the second time (due to `process_deposit_receipt`).

```python
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
# [New in EIP-6110]
# Skip already processed deposits
if state.eth1_deposit_index >= state.deposit_receipts_start_index:
state.eth1_deposit_index += 1
return

# Verify the Merkle branch
assert is_valid_merkle_branch(
leaf=hash_tree_root(deposit.data),
branch=deposit.proof,
depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in
index=state.eth1_deposit_index,
root=state.eth1_data.deposit_root,
)

# Deposits must be processed in order
state.eth1_deposit_index += 1

apply_deposit(
state=state,
pubkey=deposit.data.pubkey,
withdrawal_credentials=deposit.data.withdrawal_credentials,
amount=deposit.data.amount,
signature=deposit.data.signature,
)
```

#### Modified `process_execution_payload`

*Note*: The function `process_execution_payload` is modified to use the new `ExecutionPayloadHeader` type.
Expand Down Expand Up @@ -380,7 +291,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32,
eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))),
latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy
deposit_receipts_start_index=NOT_SET_DEPOSIT_RECEIPTS_START_INDEX, # [New in EIP6110]
deposit_receipts_start_index=UNSET_DEPOSIT_RECEIPTS_START_INDEX, # [New in EIP6110]
)

# Process deposits
Expand Down
2 changes: 1 addition & 1 deletion specs/_features/eip6110/fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def upgrade_to_eip6110(pre: capella.BeaconState) -> BeaconState:
# Deep history valid from Capella onwards
historical_summaries=pre.historical_summaries,
# EIP-6110
deposit_receipts_start_index=NOT_SET_DEPOSIT_RECEIPTS_START_INDEX, # [New in EIP-6110]
deposit_receipts_start_index=UNSET_DEPOSIT_RECEIPTS_START_INDEX, # [New in EIP-6110]
)

return post
Expand Down
42 changes: 42 additions & 0 deletions specs/_features/eip6110/validator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# EIP-6110 -- Honest Validator

## Table of contents

<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Introduction](#introduction)
- [Prerequisites](#prerequisites)
- [Block proposal](#block-proposal)
- [Deposits](#deposits)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->

## Introduction

This document represents the changes to be made in the code of an "honest validator" to implement EIP-6110.

## Prerequisites

This document is an extension of the [Capella -- Honest Validator](../../capella/validator.md) guide.
All behaviors and definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden.

All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [EIP-6110](./beacon-chain.md) are requisite for this document and used throughout.
Please see related Beacon Chain doc before continuing and use them as a reference throughout.

## Block proposal

### Deposits

The expected number of deposits MUST be changed from `min(MAX_DEPOSITS, eth1_data.deposit_count - state.eth1_deposit_index)` to the result of the following function:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to myself: I just figured out that this PR kind of deprecates MAX_DEPOSITS preset. MAX_DEPOSITS doesn't represent the upper bound of deposits anymore.


```python
def get_eth1_deposit_count(state: BeaconState) -> uint64:
eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_receipts_start_index)
if state.eth1_deposit_index < eth1_deposit_index_limit:
return min(MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index)
else:
return 0
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
```
36 changes: 13 additions & 23 deletions specs/altair/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
- [Modified `slash_validator`](#modified-slash_validator)
- [Block processing](#block-processing)
- [Modified `process_attestation`](#modified-process_attestation)
- [Modified `process_deposit`](#modified-process_deposit)
- [Modified `apply_deposit`](#modified-apply_deposit)
- [Sync aggregate processing](#sync-aggregate-processing)
- [Epoch processing](#epoch-processing)
- [Justification and finalization](#justification-and-finalization)
Expand Down Expand Up @@ -489,39 +489,29 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
```

#### Modified `process_deposit`
#### Modified `apply_deposit`

*Note*: The function `process_deposit` is modified to initialize `inactivity_scores`, `previous_epoch_participation`, and `current_epoch_participation`.
*Note*: The function `apply_deposit` is modified to initialize `inactivity_scores`, `previous_epoch_participation`, and `current_epoch_participation`.

```python
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
# Verify the Merkle branch
assert is_valid_merkle_branch(
leaf=hash_tree_root(deposit.data),
branch=deposit.proof,
depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in
index=state.eth1_deposit_index,
root=state.eth1_data.deposit_root,
)

# Deposits must be processed in order
state.eth1_deposit_index += 1

pubkey = deposit.data.pubkey
amount = deposit.data.amount
def apply_deposit(state: BeaconState,
pubkey: BLSPubkey,
withdrawal_credentials: Bytes32,
amount: uint64,
signature: BLSSignature) -> None:
validator_pubkeys = [validator.pubkey for validator in state.validators]
if pubkey not in validator_pubkeys:
# Verify the deposit signature (proof of possession) which is not checked by the deposit contract
deposit_message = DepositMessage(
pubkey=deposit.data.pubkey,
withdrawal_credentials=deposit.data.withdrawal_credentials,
amount=deposit.data.amount,
pubkey=pubkey,
withdrawal_credentials=withdrawal_credentials,
amount=amount,
)
domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks
signing_root = compute_signing_root(deposit_message, domain)
# Initialize validator if the deposit signature is valid
if bls.Verify(pubkey, signing_root, deposit.data.signature):
state.validators.append(get_validator_from_deposit(deposit))
if bls.Verify(pubkey, signing_root, signature):
state.validators.append(get_validator_from_deposit(pubkey, withdrawal_credentials, amount))
state.balances.append(amount)
# [New in Altair]
state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000))
Expand Down
60 changes: 36 additions & 24 deletions specs/phase0/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -1835,13 +1835,12 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
##### Deposits

```python
def get_validator_from_deposit(deposit: Deposit) -> Validator:
amount = deposit.data.amount
def get_validator_from_deposit(pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64) -> Validator:
effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)

return Validator(
pubkey=deposit.data.pubkey,
withdrawal_credentials=deposit.data.withdrawal_credentials,
pubkey=pubkey,
withdrawal_credentials=withdrawal_credentials,
activation_eligibility_epoch=FAR_FUTURE_EPOCH,
activation_epoch=FAR_FUTURE_EPOCH,
exit_epoch=FAR_FUTURE_EPOCH,
Expand All @@ -1851,43 +1850,56 @@ def get_validator_from_deposit(deposit: Deposit) -> Validator:
```

```python
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
# Verify the Merkle branch
assert is_valid_merkle_branch(
leaf=hash_tree_root(deposit.data),
branch=deposit.proof,
depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in
index=state.eth1_deposit_index,
root=state.eth1_data.deposit_root,
)

# Deposits must be processed in order
state.eth1_deposit_index += 1

pubkey = deposit.data.pubkey
amount = deposit.data.amount
def apply_deposit(state: BeaconState,
pubkey: BLSPubkey,
withdrawal_credentials: Bytes32,
amount: uint64,
signature: BLSSignature) -> None:
validator_pubkeys = [v.pubkey for v in state.validators]
if pubkey not in validator_pubkeys:
# Verify the deposit signature (proof of possession) which is not checked by the deposit contract
deposit_message = DepositMessage(
pubkey=deposit.data.pubkey,
withdrawal_credentials=deposit.data.withdrawal_credentials,
amount=deposit.data.amount,
pubkey=pubkey,
withdrawal_credentials=withdrawal_credentials,
amount=amount,
)
domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks
signing_root = compute_signing_root(deposit_message, domain)
if not bls.Verify(pubkey, signing_root, deposit.data.signature):
if not bls.Verify(pubkey, signing_root, signature):
return

# Add validator and balance entries
state.validators.append(get_validator_from_deposit(deposit))
state.validators.append(get_validator_from_deposit(pubkey, withdrawal_credentials, amount))
state.balances.append(amount)
else:
# Increase balance by deposit amount
index = ValidatorIndex(validator_pubkeys.index(pubkey))
increase_balance(state, index, amount)
```

```python
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
# Verify the Merkle branch
assert is_valid_merkle_branch(
leaf=hash_tree_root(deposit.data),
branch=deposit.proof,
depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in
index=state.eth1_deposit_index,
root=state.eth1_data.deposit_root,
)

# Deposits must be processed in order
state.eth1_deposit_index += 1

apply_deposit(
state=state,
pubkey=deposit.data.pubkey,
withdrawal_credentials=deposit.data.withdrawal_credentials,
amount=deposit.data.amount,
signature=deposit.data.signature,
)
```

##### Voluntary exits

```python
Expand Down