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

Update EIP-6110: put deposits into request struct, take 2 #8473

Merged
merged 2 commits into from
Apr 19, 2024
Merged
Changes from all commits
Commits
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
95 changes: 35 additions & 60 deletions EIPS/eip-6110.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ status: Draft
type: Standards Track
category: Core
created: 2022-12-09
requires: 7685
---

## Abstract
Expand Down Expand Up @@ -37,6 +38,7 @@ Advantages of in-protocol deposit processing consist of but are not limit to the
| Name | Value | Comment |
| - | - | - |
|`FORK_TIMESTAMP` | *TBD* | Mainnet |
|`DEPOSIT_REQUEST_TYPE` | `b'0'` | The [EIP-7685](./eip-7685.md) request type byte for deposit operation |

#### Configuration

Expand All @@ -60,58 +62,35 @@ The structure denoting the new deposit operation consists of the following field
4. `signature: Bytes96`
5. `index: uint64`

RLP encoding of deposit structure **MUST** be computed in the following way:
Deposits are a type of [EIP-7685](./eip-7685.md) request, therefore the encoding of the structure must be computed using the `DEPOSIT_REQUEST_TYPE` byte:

```python
rlp_encoded_deposit = RLP([
RLP(pubkey),
RLP(withdrawal_credentials),
RLP(amount),
RLP(signature),
RLP(index)
])
```

#### Block structure

Beginning with the `FORK_BLOCK`, the block body **MUST** be appended with a list of deposit operations. RLP encoding of an extended block body structure **MUST** be computed as follows:
class Deposit(object):
pubkey: Bytes48
withdrawal_credentials: Bytes32
amount: uint64
signature: Bytes96
index: uint64

```python
block_body_rlp = RLP([
rlp_encoded_field_0,
...,
# Latest block body field before `deposits`
rlp_encoded_field_n,

RLP([rlp_encoded_deposit_0, ..., rlp_encoded_deposit_k])
])
def rlp_deposit_request(deposit: Deposit) -> bytes:
return DEPOSIT_REQUEST_TYPE + rlp([
rlp(deposit.pubkey),
rlp(deposit.withdrawal_credentials),
rlp(deposit.amount),
rlp(deposit.signature),
rlp(deposit.index)
])
```

Beginning with the `FORK_BLOCK`, the block header **MUST** be appended with the new **`deposits_root`** field. The value of this field is the trie root committing to the list of deposits in the block body. **`deposits_root`** field value **MUST** be computed as follows:

```python
def compute_trie_root_from_indexed_data(data):
trie = Trie.from([(i, obj) for i, obj in enumerate(data)])
return trie.root

block.header.deposits_root = compute_trie_root_from_indexed_data(block.body.deposits)
```
The encoded deposits will be included in the header and body as generic requests following the format defined by EIP-7685.

#### Block validity

Beginning with the `FORK_BLOCK`, client software **MUST** extend block validity rule set with the following conditions:

1. Value of **`deposits_root`** block header field equals to the trie root committing to the list of deposit operations contained in the block. To illustrate:

```python
def compute_trie_root_from_indexed_data(data):
trie = Trie.from([(i, obj) for i, obj in enumerate(data)])
return trie.root
1. The list of deposit operations contained in the block **MUST** be equivalent to the list of log events emitted by each deposit transaction of the given block.

assert block.header.deposits_root == compute_trie_root_from_indexed_data(block.body.deposits)
```

2. The list of deposit operations contained in the block **MUST** be equivalent to the list of log events emitted by each deposit transaction of the given block, respecting the order of transaction inclusion. To illustrate:
2. Each deposit accumulated in the block must appear in the EIP-7685 requests list in the order they appear in the logs. To illustrate:

```python
def parse_deposit_data(deposit_event_data): bytes[]
Expand All @@ -123,36 +102,32 @@ def parse_deposit_data(deposit_event_data): bytes[]
def little_endian_to_uint64(data: bytes): uint64
return uint64(int.from_bytes(data, 'little'))

class Deposit(object):
pubkey: Bytes48
withdrawal_credentials: Bytes32
amount: uint64
signature: Bytes96
index: uint64

def event_data_to_deposit(deposit_event_data): Deposit
deposit_data = parse_deposit_data(deposit_event_data)
return Deposit(
pubkey=Bytes48(deposit_data[0]),
withdrawal_credentials=Bytes32(deposit_data[1]),
amount=little_endian_to_uint64(deposit_data[2]),
signature=Bytes96(deposit_data[3]),
index=little_endian_to_uint64(deposit_data[4])
)
deposit_data = parse_deposit_data(deposit_event_data)
return Deposit(
pubkey=Bytes48(deposit_data[0]),
withdrawal_credentials=Bytes32(deposit_data[1]),
amount=little_endian_to_uint64(deposit_data[2]),
signature=Bytes96(deposit_data[3]),
index=little_endian_to_uint64(deposit_data[4])
)

# Obtain receipts from block execution result
receipts = block.execution_result.receipts

# Retrieve all deposits made in the block
expected_deposits = []
expected_deposit_requests = []
for receipt in receipts:
for log in receipt.logs:
if log.address == DEPOSIT_CONTRACT_ADDRESS:
deposit = event_data_to_deposit(log.data)
expected_deposits.append(deposit)
deposit_request_rlp = rlp_deposit_request(deposit)
expected_deposit_requests.append(deposit_request_rlp)

deposit_requests = [req for req in block.body.requests if req[:1] == DEPOSIT_REQUEST_TYPE]

# Compare retrieved deposits to the list in the block body
assert block.body.deposits == expected_deposits
# Compare retrieved deposits to the list of deposit operations in the block
assert deposit_requests == expected_deposit_requests
```

A block that does not satisfy the above conditions **MUST** be deemed invalid.
Expand Down
Loading