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

Exclude empty requests in requests list #3998

Open
wants to merge 10 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
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
25 changes: 20 additions & 5 deletions specs/electra/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- [Constants](#constants)
- [Misc](#misc)
- [Withdrawal prefixes](#withdrawal-prefixes)
- [Execution layer triggered requests](#execution-layer-triggered-requests)
- [Preset](#preset)
- [Gwei values](#gwei-values)
- [Rewards and penalties](#rewards-and-penalties)
Expand Down Expand Up @@ -137,6 +138,14 @@ The following values are (non-configurable) constants used throughout the specif
| - | - |
| `COMPOUNDING_WITHDRAWAL_PREFIX` | `Bytes1('0x02')` |

### Execution layer triggered requests

| Name | Value |
| - | - |
| `DEPOSIT_REQUEST_TYPE` | `Bytes1('0x00')` |
| `WITHDRAWAL_REQUEST_TYPE` | `Bytes1('0x01')` |
| `CONSOLIDATION_REQUEST_TYPE` | `Bytes1('0x02')` |

## Preset

### Gwei values
Expand Down Expand Up @@ -1146,11 +1155,17 @@ def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:

```python
def get_execution_requests_list(execution_requests: ExecutionRequests) -> Sequence[bytes]:
deposit_bytes = ssz_serialize(execution_requests.deposits)
withdrawal_bytes = ssz_serialize(execution_requests.withdrawals)
consolidation_bytes = ssz_serialize(execution_requests.consolidations)

return [deposit_bytes, withdrawal_bytes, consolidation_bytes]
requests = [
(DEPOSIT_REQUEST_TYPE, execution_requests.deposits),
(WITHDRAWAL_REQUEST_TYPE, execution_requests.withdrawals),
(CONSOLIDATION_REQUEST_TYPE, execution_requests.consolidations),
]

return [
request_type + ssz_serialize(request_data)
for request_type, request_data in requests
if len(request_data) != 0
]
```

##### Modified `process_execution_payload`
Expand Down
55 changes: 46 additions & 9 deletions specs/electra/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,18 +189,55 @@ def prepare_execution_payload(state: BeaconState,

*[New in Electra]*

1. The execution payload is obtained from the execution engine as defined above using `payload_id`. The response also includes a `execution_requests` entry containing a list of bytes. Each element on the list corresponds to one SSZ list of requests as defined
in [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685). The index of each element in the array determines the type of request.
1. The execution payload is obtained from the execution engine as defined above using `payload_id`. The response also includes a `execution_requests` entry containing a list of bytes. Each element on the list corresponds to one SSZ list of requests as defined in [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685). The first byte of each request is used to determine the request type. Requests must be ordered by request type in ascending order. As a result, there can only be at most one instance of each request type.
2. Set `block.body.execution_requests = get_execution_requests(execution_requests)`, where:

```python
def get_execution_requests(execution_requests: Sequence[bytes]) -> ExecutionRequests:
deposits = ssz_deserialize(List[DepositRequest, MAX_DEPOSIT_REQUESTS_PER_PAYLOAD], execution_requests[0])
withdrawals = ssz_deserialize(List[WithdrawalRequest, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD], execution_requests[1])
consolidations = ssz_deserialize(List[ConsolidationRequest, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD],
execution_requests[2])

return ExecutionRequests(deposits, withdrawals, consolidations)
def get_execution_requests(execution_requests_list: Sequence[bytes]) -> ExecutionRequests:
deposits = []
withdrawals = []
consolidations = []

request_types = [
DEPOSIT_REQUEST_TYPE,
WITHDRAWAL_REQUEST_TYPE,
CONSOLIDATION_REQUEST_TYPE,
]

prev_request_type = None
for request in execution_requests_list:
request_type, request_data = request[0:1], request[1:]

# Check that the request type is valid
assert request_type in request_types
# Check that the request data is not empty
assert len(request_data) != 0
# Check that requests are in strictly ascending order
# Each successive type must be greater than the last with no duplicates
assert prev_request_type is None or prev_request_type < request_type
prev_request_type = request_type

if request_type == DEPOSIT_REQUEST_TYPE:
deposits = ssz_deserialize(
List[DepositRequest, MAX_DEPOSIT_REQUESTS_PER_PAYLOAD],
request_data
)
elif request_type == WITHDRAWAL_REQUEST_TYPE:
withdrawals = ssz_deserialize(
List[WithdrawalRequest, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD],
request_data
)
elif request_type == CONSOLIDATION_REQUEST_TYPE:
consolidations = ssz_deserialize(
List[ConsolidationRequest, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD],
request_data
)

return ExecutionRequests(
deposits=deposits,
withdrawals=withdrawals,
consolidations=consolidations,
)
```

## Attesting
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
from eth2spec.test.context import (
single_phase,
spec_test,
with_electra_and_later,
)


@with_electra_and_later
@spec_test
@single_phase
def test_requests_serialization_round_trip__empty(spec):
execution_requests = spec.ExecutionRequests()
serialized_execution_requests = spec.get_execution_requests_list(execution_requests)
deserialized_execution_requests = spec.get_execution_requests(serialized_execution_requests)
assert deserialized_execution_requests == execution_requests


@with_electra_and_later
@spec_test
@single_phase
def test_requests_serialization_round_trip__one_request(spec):
execution_requests = spec.ExecutionRequests(
deposits=[spec.DepositRequest()],
)
serialized_execution_requests = spec.get_execution_requests_list(execution_requests)
deserialized_execution_requests = spec.get_execution_requests(serialized_execution_requests)
assert deserialized_execution_requests == execution_requests


@with_electra_and_later
@spec_test
@single_phase
def test_requests_serialization_round_trip__multiple_requests(spec):
execution_requests = spec.ExecutionRequests(
deposits=[spec.DepositRequest()],
withdrawals=[spec.WithdrawalRequest()],
consolidations=[spec.ConsolidationRequest()],
)
serialized_execution_requests = spec.get_execution_requests_list(execution_requests)
deserialized_execution_requests = spec.get_execution_requests(serialized_execution_requests)
assert deserialized_execution_requests == execution_requests


@with_electra_and_later
@spec_test
@single_phase
def test_requests_serialization_round_trip__one_request_with_real_data(spec):
execution_requests = spec.ExecutionRequests(
deposits=[
spec.DepositRequest(
pubkey=spec.BLSPubkey(48 * "aa"),
withdrawal_credentials=spec.Bytes32(32 * "bb"),
amount=spec.Gwei(11111111),
signature=spec.BLSSignature(96 * "cc"),
index=spec.uint64(22222222),
),
]
)
serialized_execution_requests = spec.get_execution_requests_list(execution_requests)
deserialized_execution_requests = spec.get_execution_requests(serialized_execution_requests)
assert deserialized_execution_requests == execution_requests


@with_electra_and_later
@spec_test
@single_phase
def test_requests_deserialize__reject_duplicate_request(spec):
serialized_withdrawal = 76 * b"\x0a"
serialized_execution_requests = [
spec.WITHDRAWAL_REQUEST_TYPE + serialized_withdrawal,
spec.WITHDRAWAL_REQUEST_TYPE + serialized_withdrawal,
]
try:
spec.get_execution_requests(serialized_execution_requests)
assert False, "expected exception"
except Exception:
pass


@with_electra_and_later
@spec_test
@single_phase
def test_requests_deserialize__reject_out_of_order_requests(spec):
serialized_execution_requests = [
spec.WITHDRAWAL_REQUEST_TYPE + 76 * b"\x0a",
spec.DEPOSIT_REQUEST_TYPE + 192 * b"\x0b",
]
assert int(serialized_execution_requests[0][0]) > int(serialized_execution_requests[1][0])
try:
spec.get_execution_requests(serialized_execution_requests)
assert False, "expected exception"
except Exception:
pass


@with_electra_and_later
@spec_test
@single_phase
def test_requests_deserialize__reject_empty_request(spec):
serialized_execution_requests = [b"\x01"]
try:
spec.get_execution_requests(serialized_execution_requests)
assert False, "expected exception"
except Exception:
pass


@with_electra_and_later
@spec_test
@single_phase
def test_requests_deserialize__reject_unexpected_request_type(spec):
serialized_execution_requests = [
b"\x03\xff\xff\xff",
]
try:
spec.get_execution_requests(serialized_execution_requests)
assert False, "expected exception"
except Exception:
pass