mirror of
https://github.com/ethereum/consensus-specs.git
synced 2026-02-01 13:34:57 -05:00
Merge branch 'dev' into lc-blockfuncs
This commit is contained in:
10
Makefile
10
Makefile
@@ -41,6 +41,8 @@ CURRENT_DIR = ${CURDIR}
|
||||
LINTER_CONFIG_FILE = $(CURRENT_DIR)/linter.ini
|
||||
GENERATOR_ERROR_LOG_FILE = $(CURRENT_DIR)/$(TEST_VECTOR_DIR)/testgen_error_log.txt
|
||||
|
||||
SCRIPTS_DIR = ${CURRENT_DIR}/scripts
|
||||
|
||||
export DAPP_SKIP_BUILD:=1
|
||||
export DAPP_SRC:=$(SOLIDITY_DEPOSIT_CONTRACT_DIR)
|
||||
export DAPP_LIB:=$(SOLIDITY_DEPOSIT_CONTRACT_DIR)/lib
|
||||
@@ -195,6 +197,14 @@ $(TEST_VECTOR_DIR):
|
||||
$(TEST_VECTOR_DIR)/:
|
||||
$(info ignoring duplicate tests dir)
|
||||
|
||||
gen_kzg_setups:
|
||||
cd $(SCRIPTS_DIR); \
|
||||
if ! test -d venv; then python3 -m venv venv; fi; \
|
||||
. venv/bin/activate; \
|
||||
pip3 install -r requirements.txt; \
|
||||
python3 ./gen_kzg_trusted_setups.py --secret=1337 --length=4 --output-dir ${CURRENT_DIR}/presets/minimal/trusted_setups; \
|
||||
python3 ./gen_kzg_trusted_setups.py --secret=1337 --length=4096 --output-dir ${CURRENT_DIR}/presets/mainnet/trusted_setups
|
||||
|
||||
# For any generator, build it using the run_generator function.
|
||||
# (creation of output dir is a dependency)
|
||||
gen_%: $(TEST_VECTOR_DIR)
|
||||
|
||||
@@ -1,23 +1,11 @@
|
||||
# Mainnet preset - Capella
|
||||
|
||||
# Misc
|
||||
# ---------------------------------------------------------------
|
||||
# 2**8 (= 256) withdrawals
|
||||
MAX_PARTIAL_WITHDRAWALS_PER_EPOCH: 256
|
||||
|
||||
|
||||
# State list lengths
|
||||
# ---------------------------------------------------------------
|
||||
# 2**40 (= 1,099,511,627,776) withdrawals
|
||||
WITHDRAWAL_QUEUE_LIMIT: 1099511627776
|
||||
|
||||
|
||||
# Max operations per block
|
||||
# ---------------------------------------------------------------
|
||||
# 2**4 (= 16)
|
||||
MAX_BLS_TO_EXECUTION_CHANGES: 16
|
||||
|
||||
|
||||
# Execution
|
||||
# ---------------------------------------------------------------
|
||||
# 2**4 (= 16) withdrawals
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,17 +1,5 @@
|
||||
# Minimal preset - Capella
|
||||
|
||||
# Misc
|
||||
# ---------------------------------------------------------------
|
||||
# [customized] 16 for more interesting tests at low validator count
|
||||
MAX_PARTIAL_WITHDRAWALS_PER_EPOCH: 16
|
||||
|
||||
|
||||
# State list lengths
|
||||
# ---------------------------------------------------------------
|
||||
# 2**40 (= 1,099,511,627,776) withdrawals
|
||||
WITHDRAWAL_QUEUE_LIMIT: 1099511627776
|
||||
|
||||
|
||||
# Max operations per block
|
||||
# ---------------------------------------------------------------
|
||||
# 2**4 (= 16)
|
||||
@@ -20,5 +8,5 @@ MAX_BLS_TO_EXECUTION_CHANGES: 16
|
||||
|
||||
# Execution
|
||||
# ---------------------------------------------------------------
|
||||
# [customized] Lower than MAX_PARTIAL_WITHDRAWALS_PER_EPOCH so not all processed in one block
|
||||
MAX_WITHDRAWALS_PER_PAYLOAD: 8
|
||||
# [customized] 2**2 (= 4)
|
||||
MAX_WITHDRAWALS_PER_PAYLOAD: 4
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"setup_G1": ["0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", "0x854262641262cb9e056a8512808ea6864d903dbcad713fd6da8dddfa5ce40d85612c912063ace060ed8c4bf005bab839", "0x86f708eee5ae0cf40be36993e760d9cb3b2371f22db3209947c5d21ea68e55186b30871c50bf11ef29e5248bf42d5678", "0x94f9c0bafb23cbbf34a93a64243e3e0f934b57593651f3464de7dc174468123d9698f1b9dfa22bb5b6eb96eae002f29f"], "setup_G2": ["0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", "0x99aca9fb2f7760cecb892bf7262c176b334824f5727f680bba701a33e322cb6667531410dfc7c8e4321a3f0ea8af48cb1436638a2093123f046f0f504cc2a864825542873edbbc5d7ed17af125a4f2cf6433c6f4f61b81173726981dd989761d", "0x88e2e982982bf8231e747e9dfcd14c05bd02623d1332734d2af26246c6869fb56ee6c994843f593178a040495ba61f4a083b0e18110b1d9f5224783d8f9a895e8ee744e87929430e9ba96bd29251cbf61240b256d1525600f3d562894d93d659", "0xa2d33775e3d9e6af0d1b27d389e6c021a578e617a3d6627686db6288d4b3dffd7a847a00f7ef01828b7f42885b660e4204923402aca18fbae74ccd4e9c50dd8c2281b38dc09c022342ed1ac695d53f7081cb21f05fdfc0a3508c04759196fcd3"], "setup_G1_lagrange": ["0x91131b2e3c1e5f0b51df8970e67080032f411571b66d301436c46f25bbfddf9ca16756430dc470bdb0d85b47fedcdbc1", "0x934d35b2a46e169915718b77127b0d4efbacdad7fdde4593af7d21d37ebcb77fe6c8dde6b8a9537854d70ef1f291a585", "0x9410ca1d0342fe7419f02194281df45e1c1ff42fd8b439de5644cc312815c21ddd2e3eeb63fb807cf837e68b76668bd5", "0xb163df7e9baeb60f69b6ee5faa538c3a564b62eb8cde6a3616083c8cb2171eedd583c9143e7e916df59bf27da5e024e8"], "roots_of_unity": [1, 3465144826073652318776269530687742778270252468765361963008, 52435875175126190479447740508185965837690552500527637822603658699938581184512, 52435875175126190475982595682112313518914282969839895044333406231173219221505]}
|
||||
36
scripts/gen_kzg_trusted_setups.py
Normal file
36
scripts/gen_kzg_trusted_setups.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from eth2spec.utils.kzg import (
|
||||
dump_kzg_trusted_setup_files,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--secret",
|
||||
dest="secret",
|
||||
type=int,
|
||||
required=True,
|
||||
help='the secret of trusted setup',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--length",
|
||||
dest="length",
|
||||
type=int,
|
||||
required=True,
|
||||
help='the length of trusted setup',
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output-dir",
|
||||
dest="output_dir",
|
||||
required=True,
|
||||
help='the output directory',
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
dump_kzg_trusted_setup_files(args.secret, args.length, args.output_dir)
|
||||
1
scripts/requirements.txt
Normal file
1
scripts/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
../[generator]
|
||||
86
setup.py
86
setup.py
@@ -7,13 +7,14 @@ import os
|
||||
import re
|
||||
import string
|
||||
import textwrap
|
||||
from typing import Dict, NamedTuple, List, Sequence, Optional, TypeVar
|
||||
from typing import Dict, NamedTuple, List, Sequence, Optional, TypeVar, Tuple
|
||||
from abc import ABC, abstractmethod
|
||||
import ast
|
||||
import subprocess
|
||||
import sys
|
||||
import copy
|
||||
from collections import OrderedDict
|
||||
import json
|
||||
|
||||
|
||||
# NOTE: have to programmatically include third-party dependencies in `setup.py`.
|
||||
@@ -121,7 +122,7 @@ def _get_self_type_from_source(source: str) -> Optional[str]:
|
||||
return args[0].annotation.id
|
||||
|
||||
|
||||
def _get_class_info_from_source(source: str) -> (str, Optional[str]):
|
||||
def _get_class_info_from_source(source: str) -> Tuple[str, Optional[str]]:
|
||||
class_def = ast.parse(source).body[0]
|
||||
base = class_def.bases[0]
|
||||
if isinstance(base, ast.Name):
|
||||
@@ -140,6 +141,28 @@ def _is_constant_id(name: str) -> bool:
|
||||
return all(map(lambda c: c in string.ascii_uppercase + '_' + string.digits, name[1:]))
|
||||
|
||||
|
||||
def _load_kzg_trusted_setups(preset_name):
|
||||
"""
|
||||
[TODO] it's not the final mainnet trusted setup.
|
||||
We will update it after the KZG ceremony.
|
||||
"""
|
||||
file_path = str(Path(__file__).parent) + '/presets/' + preset_name + '/trusted_setups/testing_trusted_setups.json'
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
json_data = json.load(f)
|
||||
|
||||
trusted_setup_G1 = json_data['setup_G1']
|
||||
trusted_setup_G2 = json_data['setup_G2']
|
||||
trusted_setup_G1_lagrange = json_data['setup_G1_lagrange']
|
||||
roots_of_unity = json_data['roots_of_unity']
|
||||
|
||||
return trusted_setup_G1, trusted_setup_G2, trusted_setup_G1_lagrange, roots_of_unity
|
||||
|
||||
ALL_KZG_SETUPS = {
|
||||
'minimal': _load_kzg_trusted_setups('minimal'),
|
||||
'mainnet': _load_kzg_trusted_setups('mainnet')
|
||||
}
|
||||
|
||||
ETH2_SPEC_COMMENT_PREFIX = "eth2spec:"
|
||||
|
||||
|
||||
@@ -167,7 +190,16 @@ def _parse_value(name: str, typed_value: str, type_hint: Optional[str]=None) ->
|
||||
return VariableDefinition(type_name=type_name, value=typed_value[i+1:-1], comment=comment, type_hint=type_hint)
|
||||
|
||||
|
||||
def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str]) -> SpecObject:
|
||||
def _update_constant_vars_with_kzg_setups(constant_vars, preset_name):
|
||||
comment = "noqa: E501"
|
||||
kzg_setups = ALL_KZG_SETUPS[preset_name]
|
||||
constant_vars['KZG_SETUP_G1'] = VariableDefinition(constant_vars['KZG_SETUP_G1'].value, str(kzg_setups[0]), comment, None)
|
||||
constant_vars['KZG_SETUP_G2'] = VariableDefinition(constant_vars['KZG_SETUP_G2'].value, str(kzg_setups[1]), comment, None)
|
||||
constant_vars['KZG_SETUP_LAGRANGE'] = VariableDefinition(constant_vars['KZG_SETUP_LAGRANGE'].value, str(kzg_setups[2]), comment, None)
|
||||
constant_vars['ROOTS_OF_UNITY'] = VariableDefinition(constant_vars['ROOTS_OF_UNITY'].value, str(kzg_setups[3]), comment, None)
|
||||
|
||||
|
||||
def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], preset_name=str) -> SpecObject:
|
||||
functions: Dict[str, str] = {}
|
||||
protocols: Dict[str, ProtocolDefinition] = {}
|
||||
constant_vars: Dict[str, VariableDefinition] = {}
|
||||
@@ -256,6 +288,10 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str]) ->
|
||||
if comment == "skip":
|
||||
should_skip = True
|
||||
|
||||
# Load KZG trusted setup from files
|
||||
if any('KZG_SETUP' in name for name in constant_vars):
|
||||
_update_constant_vars_with_kzg_setups(constant_vars, preset_name)
|
||||
|
||||
return SpecObject(
|
||||
functions=functions,
|
||||
protocols=protocols,
|
||||
@@ -582,14 +618,13 @@ from eth2spec.bellatrix import {preset_name} as bellatrix
|
||||
#
|
||||
# EIP4844SpecBuilder
|
||||
#
|
||||
class EIP4844SpecBuilder(BellatrixSpecBuilder):
|
||||
class EIP4844SpecBuilder(CapellaSpecBuilder):
|
||||
fork: str = EIP4844
|
||||
|
||||
@classmethod
|
||||
def imports(cls, preset_name: str):
|
||||
return super().imports(preset_name) + f'''
|
||||
from eth2spec.utils import kzg
|
||||
from eth2spec.bellatrix import {preset_name} as bellatrix
|
||||
from eth2spec.capella import {preset_name} as capella
|
||||
'''
|
||||
|
||||
|
||||
@@ -602,19 +637,31 @@ T = TypeVar('T') # For generic function
|
||||
@classmethod
|
||||
def sundry_functions(cls) -> str:
|
||||
return super().sundry_functions() + '\n\n' + '''
|
||||
# TODO: for mainnet, load pre-generated trusted setup file to reduce building time.
|
||||
# TESTING_FIELD_ELEMENTS_PER_BLOB is hardcoded copy from minimal presets
|
||||
TESTING_FIELD_ELEMENTS_PER_BLOB = 4
|
||||
TESTING_SECRET = 1337
|
||||
TESTING_KZG_SETUP_G1 = kzg.generate_setup(bls.G1, TESTING_SECRET, TESTING_FIELD_ELEMENTS_PER_BLOB)
|
||||
TESTING_KZG_SETUP_G2 = kzg.generate_setup(bls.G2, TESTING_SECRET, TESTING_FIELD_ELEMENTS_PER_BLOB)
|
||||
TESTING_KZG_SETUP_LAGRANGE = kzg.get_lagrange(TESTING_KZG_SETUP_G1)
|
||||
#
|
||||
# Temporarily disable Withdrawals functions for EIP4844 testnets
|
||||
#
|
||||
|
||||
KZG_SETUP_G1 = [bls.G1_to_bytes48(p) for p in TESTING_KZG_SETUP_G1]
|
||||
KZG_SETUP_G2 = [bls.G2_to_bytes96(p) for p in TESTING_KZG_SETUP_G2]
|
||||
KZG_SETUP_LAGRANGE = TESTING_KZG_SETUP_LAGRANGE
|
||||
ROOTS_OF_UNITY = kzg.compute_roots_of_unity(TESTING_FIELD_ELEMENTS_PER_BLOB)
|
||||
|
||||
def no_op(fn): # type: ignore
|
||||
def wrapper(*args, **kw): # type: ignore
|
||||
return None
|
||||
return wrapper
|
||||
|
||||
|
||||
def get_empty_list_result(fn): # type: ignore
|
||||
def wrapper(*args, **kw): # type: ignore
|
||||
return []
|
||||
return wrapper
|
||||
|
||||
|
||||
process_withdrawals = no_op(process_withdrawals)
|
||||
process_bls_to_execution_change = no_op(process_bls_to_execution_change)
|
||||
get_expected_withdrawals = get_empty_list_result(get_expected_withdrawals)
|
||||
|
||||
|
||||
#
|
||||
# End
|
||||
#
|
||||
|
||||
def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> Optional[BlobsSidecar]:
|
||||
return "TEST"'''
|
||||
@@ -629,7 +676,6 @@ def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> Optional[Blob
|
||||
return {**super().hardcoded_custom_type_dep_constants(spec_object), **constants}
|
||||
|
||||
|
||||
|
||||
spec_builders = {
|
||||
builder.fork: builder
|
||||
for builder in (Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, EIP4844SpecBuilder)
|
||||
@@ -880,7 +926,7 @@ def _build_spec(preset_name: str, fork: str,
|
||||
source_files: Sequence[Path], preset_files: Sequence[Path], config_file: Path) -> str:
|
||||
preset = load_preset(preset_files)
|
||||
config = load_config(config_file)
|
||||
all_specs = [get_spec(spec, preset, config) for spec in source_files]
|
||||
all_specs = [get_spec(spec, preset, config, preset_name) for spec in source_files]
|
||||
|
||||
spec_object = all_specs[0]
|
||||
for value in all_specs[1:]:
|
||||
@@ -967,7 +1013,7 @@ class PySpecCommand(Command):
|
||||
specs/bellatrix/p2p-interface.md
|
||||
sync/optimistic.md
|
||||
"""
|
||||
if self.spec_fork == CAPELLA:
|
||||
if self.spec_fork in (CAPELLA, EIP4844):
|
||||
self.md_doc_paths += """
|
||||
specs/capella/beacon-chain.md
|
||||
specs/capella/fork.md
|
||||
|
||||
@@ -59,7 +59,7 @@ New global topics are added to provide light clients with the latest updates.
|
||||
This topic is used to propagate the latest `LightClientFinalityUpdate` to light clients, allowing them to keep track of the latest `finalized_header`.
|
||||
|
||||
The following validations MUST pass before forwarding the `finality_update` on the network.
|
||||
- _[IGNORE]_ No other `finality_update` with a lower or equal `finalized_header.slot` was already forwarded on the network
|
||||
- _[IGNORE]_ The `finalized_header.slot` is greater than that of all previously forwarded `finality_update`s
|
||||
- _[IGNORE]_ The `finality_update` is received after the block at `signature_slot` was given enough time to propagate through the network -- i.e. validate that one-third of `finality_update.signature_slot` has transpired (`SECONDS_PER_SLOT / INTERVALS_PER_SLOT` seconds after the start of the slot, with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance)
|
||||
|
||||
For full nodes, the following validations MUST additionally pass before forwarding the `finality_update` on the network.
|
||||
@@ -87,7 +87,7 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
|
||||
This topic is used to propagate the latest `LightClientOptimisticUpdate` to light clients, allowing them to keep track of the latest `optimistic_header`.
|
||||
|
||||
The following validations MUST pass before forwarding the `optimistic_update` on the network.
|
||||
- _[IGNORE]_ No other `optimistic_update` with a lower or equal `attested_header.slot` was already forwarded on the network
|
||||
- _[IGNORE]_ The `attested_header.slot` is greater than that of all previously forwarded `optimistic_update`s
|
||||
- _[IGNORE]_ The `optimistic_update` is received after the block at `signature_slot` was given enough time to propagate through the network -- i.e. validate that one-third of `optimistic_update.signature_slot` has transpired (`SECONDS_PER_SLOT / INTERVALS_PER_SLOT` seconds after the start of the slot, with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance)
|
||||
|
||||
For full nodes, the following validations MUST additionally pass before forwarding the `optimistic_update` on the network.
|
||||
|
||||
@@ -11,11 +11,8 @@
|
||||
- [Constants](#constants)
|
||||
- [Domain types](#domain-types)
|
||||
- [Preset](#preset)
|
||||
- [Misc](#misc)
|
||||
- [State list lengths](#state-list-lengths)
|
||||
- [Max operations per block](#max-operations-per-block)
|
||||
- [Execution](#execution)
|
||||
- [Configuration](#configuration)
|
||||
- [Containers](#containers)
|
||||
- [New containers](#new-containers)
|
||||
- [`Withdrawal`](#withdrawal)
|
||||
@@ -27,17 +24,13 @@
|
||||
- [`BeaconBlockBody`](#beaconblockbody)
|
||||
- [`BeaconState`](#beaconstate)
|
||||
- [Helpers](#helpers)
|
||||
- [Beacon state mutators](#beacon-state-mutators)
|
||||
- [`withdraw_balance`](#withdraw_balance)
|
||||
- [Predicates](#predicates)
|
||||
- [`has_eth1_withdrawal_credential`](#has_eth1_withdrawal_credential)
|
||||
- [`is_fully_withdrawable_validator`](#is_fully_withdrawable_validator)
|
||||
- [`is_partially_withdrawable_validator`](#is_partially_withdrawable_validator)
|
||||
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
|
||||
- [Epoch processing](#epoch-processing)
|
||||
- [Full withdrawals](#full-withdrawals)
|
||||
- [Partial withdrawals](#partial-withdrawals)
|
||||
- [Block processing](#block-processing)
|
||||
- [New `get_expected_withdrawals`](#new-get_expected_withdrawals)
|
||||
- [New `process_withdrawals`](#new-process_withdrawals)
|
||||
- [Modified `process_execution_payload`](#modified-process_execution_payload)
|
||||
- [Modified `process_operations`](#modified-process_operations)
|
||||
@@ -75,18 +68,6 @@ We define the following Python custom types for type hinting and readability:
|
||||
|
||||
## Preset
|
||||
|
||||
### Misc
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `MAX_PARTIAL_WITHDRAWALS_PER_EPOCH` | `uint64(2**8)` (= 256) |
|
||||
|
||||
### State list lengths
|
||||
|
||||
| Name | Value | Unit |
|
||||
| - | - | :-: |
|
||||
| `WITHDRAWAL_QUEUE_LIMIT` | `uint64(2**40)` (= 1,099,511,627,776) | withdrawals enqueued in state |
|
||||
|
||||
### Max operations per block
|
||||
|
||||
| Name | Value |
|
||||
@@ -99,8 +80,6 @@ We define the following Python custom types for type hinting and readability:
|
||||
| - | - | - |
|
||||
| `MAX_WITHDRAWALS_PER_PAYLOAD` | `uint64(2**4)` (= 16) | Maximum amount of withdrawals allowed in each payload |
|
||||
|
||||
## Configuration
|
||||
|
||||
## Containers
|
||||
|
||||
### New containers
|
||||
@@ -241,32 +220,12 @@ class BeaconState(Container):
|
||||
# Execution
|
||||
latest_execution_payload_header: ExecutionPayloadHeader
|
||||
# Withdrawals
|
||||
withdrawal_queue: List[Withdrawal, WITHDRAWAL_QUEUE_LIMIT] # [New in Capella]
|
||||
next_withdrawal_index: WithdrawalIndex # [New in Capella]
|
||||
next_partial_withdrawal_validator_index: ValidatorIndex # [New in Capella]
|
||||
next_withdrawal_validator_index: ValidatorIndex # [New in Capella]
|
||||
```
|
||||
|
||||
## Helpers
|
||||
|
||||
### Beacon state mutators
|
||||
|
||||
#### `withdraw_balance`
|
||||
|
||||
```python
|
||||
def withdraw_balance(state: BeaconState, validator_index: ValidatorIndex, amount: Gwei) -> None:
|
||||
# Decrease the validator's balance
|
||||
decrease_balance(state, validator_index, amount)
|
||||
# Create a corresponding withdrawal receipt
|
||||
withdrawal = Withdrawal(
|
||||
index=state.next_withdrawal_index,
|
||||
validator_index=validator_index,
|
||||
address=ExecutionAddress(state.validators[validator_index].withdrawal_credentials[12:]),
|
||||
amount=amount,
|
||||
)
|
||||
state.next_withdrawal_index = WithdrawalIndex(state.next_withdrawal_index + 1)
|
||||
state.withdrawal_queue.append(withdrawal)
|
||||
```
|
||||
|
||||
### Predicates
|
||||
|
||||
#### `has_eth1_withdrawal_credential`
|
||||
@@ -307,66 +266,6 @@ def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) ->
|
||||
|
||||
## Beacon chain state transition function
|
||||
|
||||
### Epoch processing
|
||||
|
||||
```python
|
||||
def process_epoch(state: BeaconState) -> None:
|
||||
process_justification_and_finalization(state)
|
||||
process_inactivity_updates(state)
|
||||
process_rewards_and_penalties(state)
|
||||
process_registry_updates(state)
|
||||
process_slashings(state)
|
||||
process_eth1_data_reset(state)
|
||||
process_effective_balance_updates(state)
|
||||
process_slashings_reset(state)
|
||||
process_randao_mixes_reset(state)
|
||||
process_historical_roots_update(state)
|
||||
process_participation_flag_updates(state)
|
||||
process_sync_committee_updates(state)
|
||||
process_full_withdrawals(state) # [New in Capella]
|
||||
process_partial_withdrawals(state) # [New in Capella]
|
||||
|
||||
```
|
||||
|
||||
#### Full withdrawals
|
||||
|
||||
*Note*: The function `process_full_withdrawals` is new.
|
||||
|
||||
```python
|
||||
def process_full_withdrawals(state: BeaconState) -> None:
|
||||
current_epoch = get_current_epoch(state)
|
||||
for index in range(len(state.validators)):
|
||||
balance = state.balances[index]
|
||||
validator = state.validators[index]
|
||||
if is_fully_withdrawable_validator(validator, balance, current_epoch):
|
||||
withdraw_balance(state, ValidatorIndex(index), balance)
|
||||
```
|
||||
|
||||
#### Partial withdrawals
|
||||
|
||||
*Note*: The function `process_partial_withdrawals` is new.
|
||||
|
||||
```python
|
||||
def process_partial_withdrawals(state: BeaconState) -> None:
|
||||
partial_withdrawals_count = 0
|
||||
# Begin where we left off last time
|
||||
validator_index = state.next_partial_withdrawal_validator_index
|
||||
for _ in range(len(state.validators)):
|
||||
balance = state.balances[validator_index]
|
||||
validator = state.validators[validator_index]
|
||||
if is_partially_withdrawable_validator(validator, balance):
|
||||
withdraw_balance(state, validator_index, balance - MAX_EFFECTIVE_BALANCE)
|
||||
partial_withdrawals_count += 1
|
||||
|
||||
# Iterate to next validator to check for partial withdrawal
|
||||
validator_index = ValidatorIndex((validator_index + 1) % len(state.validators))
|
||||
# Exit if performed maximum allowable withdrawals
|
||||
if partial_withdrawals_count == MAX_PARTIAL_WITHDRAWALS_PER_EPOCH:
|
||||
break
|
||||
|
||||
state.next_partial_withdrawal_validator_index = validator_index
|
||||
```
|
||||
|
||||
### Block processing
|
||||
|
||||
```python
|
||||
@@ -377,23 +276,58 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
||||
process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in Capella]
|
||||
process_randao(state, block.body)
|
||||
process_eth1_data(state, block.body)
|
||||
process_operations(state, block.body)
|
||||
process_operations(state, block.body) # [Modified in Capella]
|
||||
process_sync_aggregate(state, block.body.sync_aggregate)
|
||||
```
|
||||
|
||||
#### New `get_expected_withdrawals`
|
||||
|
||||
```python
|
||||
def get_expected_withdrawals(state: BeaconState) -> Sequence[Withdrawal]:
|
||||
epoch = get_current_epoch(state)
|
||||
withdrawal_index = state.next_withdrawal_index
|
||||
validator_index = state.next_withdrawal_validator_index
|
||||
withdrawals: List[Withdrawal] = []
|
||||
for _ in range(len(state.validators)):
|
||||
validator = state.validators[validator_index]
|
||||
balance = state.balances[validator_index]
|
||||
if is_fully_withdrawable_validator(validator, balance, epoch):
|
||||
withdrawals.append(Withdrawal(
|
||||
index=withdrawal_index,
|
||||
validator_index=validator_index,
|
||||
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
|
||||
amount=balance,
|
||||
))
|
||||
withdrawal_index += WithdrawalIndex(1)
|
||||
elif is_partially_withdrawable_validator(validator, balance):
|
||||
withdrawals.append(Withdrawal(
|
||||
index=withdrawal_index,
|
||||
validator_index=validator_index,
|
||||
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
|
||||
amount=balance - MAX_EFFECTIVE_BALANCE,
|
||||
))
|
||||
withdrawal_index += WithdrawalIndex(1)
|
||||
if len(withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
|
||||
break
|
||||
validator_index = ValidatorIndex((validator_index + 1) % len(state.validators))
|
||||
return withdrawals
|
||||
```
|
||||
|
||||
#### New `process_withdrawals`
|
||||
|
||||
```python
|
||||
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
||||
num_withdrawals = min(MAX_WITHDRAWALS_PER_PAYLOAD, len(state.withdrawal_queue))
|
||||
dequeued_withdrawals = state.withdrawal_queue[:num_withdrawals]
|
||||
expected_withdrawals = get_expected_withdrawals(state)
|
||||
assert len(payload.withdrawals) == len(expected_withdrawals)
|
||||
|
||||
assert len(dequeued_withdrawals) == len(payload.withdrawals)
|
||||
for dequeued_withdrawal, withdrawal in zip(dequeued_withdrawals, payload.withdrawals):
|
||||
assert dequeued_withdrawal == withdrawal
|
||||
|
||||
# Remove dequeued withdrawals from state
|
||||
state.withdrawal_queue = state.withdrawal_queue[num_withdrawals:]
|
||||
for expected_withdrawal, withdrawal in zip(expected_withdrawals, payload.withdrawals):
|
||||
assert withdrawal == expected_withdrawal
|
||||
decrease_balance(state, withdrawal.validator_index, withdrawal.amount)
|
||||
if len(expected_withdrawals) > 0:
|
||||
latest_withdrawal = expected_withdrawals[-1]
|
||||
state.next_withdrawal_index = WithdrawalIndex(latest_withdrawal.index + 1)
|
||||
next_validator_index = ValidatorIndex((latest_withdrawal.validator_index + 1) % len(state.validators))
|
||||
state.next_withdrawal_validator_index = next_validator_index
|
||||
```
|
||||
|
||||
#### Modified `process_execution_payload`
|
||||
|
||||
@@ -129,9 +129,8 @@ def upgrade_to_capella(pre: bellatrix.BeaconState) -> BeaconState:
|
||||
# Execution-layer
|
||||
latest_execution_payload_header=latest_execution_payload_header,
|
||||
# Withdrawals
|
||||
withdrawal_queue=[],
|
||||
next_withdrawal_index=WithdrawalIndex(0),
|
||||
next_partial_withdrawal_validator_index=ValidatorIndex(0),
|
||||
next_withdrawal_validator_index=ValidatorIndex(0),
|
||||
)
|
||||
|
||||
return post
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
- [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody)
|
||||
- [ExecutionPayload](#executionpayload)
|
||||
- [BLS to execution changes](#bls-to-execution-changes)
|
||||
- [Enabling validator withdrawals](#enabling-validator-withdrawals)
|
||||
- [Changing from BLS to execution withdrawal credentials](#changing-from-bls-to-execution-withdrawal-credentials)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- /TOC -->
|
||||
@@ -59,12 +61,8 @@ All validator responsibilities remain unchanged other than those noted below.
|
||||
expected withdrawals for the slot must be gathered from the `state` (utilizing the
|
||||
helper `get_expected_withdrawals`) and passed into the `ExecutionEngine` within `prepare_execution_payload`.
|
||||
|
||||
|
||||
```python
|
||||
def get_expected_withdrawals(state: BeaconState) -> Sequence[Withdrawal]:
|
||||
num_withdrawals = min(MAX_WITHDRAWALS_PER_PAYLOAD, len(state.withdrawal_queue))
|
||||
return state.withdrawal_queue[:num_withdrawals]
|
||||
```
|
||||
*Note*: In this section, `state` is the state of the slot for the block proposal _without_ the block yet applied.
|
||||
That is, `state` is the `previous_state` processed through any empty slots up to the assigned slot using `process_slots(previous_state, slot)`.
|
||||
|
||||
*Note*: The only change made to `prepare_execution_payload` is to call
|
||||
`get_expected_withdrawals()` to set the new `withdrawals` field of `PayloadAttributes`.
|
||||
@@ -111,3 +109,39 @@ def prepare_execution_payload(state: BeaconState,
|
||||
##### BLS to execution changes
|
||||
|
||||
Up to `MAX_BLS_TO_EXECUTION_CHANGES`, [`BLSToExecutionChange`](./beacon-chain.md#blstoexecutionchange) objects can be included in the `block`. The BLS to execution changes must satisfy the verification conditions found in [BLS to execution change processing](./beacon-chain.md#new-process_bls_to_execution_change).
|
||||
|
||||
## Enabling validator withdrawals
|
||||
|
||||
Validator balances are fully or partially withdrawn via an automatic process.
|
||||
|
||||
For validators, there is one prerequisite for this automated process:
|
||||
withdrawal credentials pointing to an execution layer address, i.e. having an `ETH1_ADDRESS_WITHDRAWAL_PREFIX`.
|
||||
|
||||
If a validator has a `BLS_WITHDRAWAL_PREFIX` withdrawal credential prefix, to participate in withdrawals the validator must
|
||||
create a one-time message to change their withdrawal credential from the version authenticated with a BLS key to the
|
||||
version compatible with the execution layer. This message -- a `BLSToExecutionChange` -- is available starting in Capella
|
||||
|
||||
Validators who wish to enable withdrawals **MUST** assemble, sign, and broadcast this message so that it is accepted
|
||||
on the beacon chain. Validators who do not want to enable withdrawals and have the `BLS_WITHDRAWAL_PREFIX` version of
|
||||
withdrawal credentials can delay creating this message until they are ready to enable withdrawals.
|
||||
|
||||
### Changing from BLS to execution withdrawal credentials
|
||||
|
||||
First, the validator must construct a valid [`BLSToExecutionChange`](./beacon-chain.md#blstoexecutionchange) `message`.
|
||||
This `message` contains the `validator_index` for the validator who wishes to change their credentials, the `from_bls_pubkey` -- the BLS public key corresponding to the **withdrawal BLS secret key** used to form the `BLS_WITHDRAWAL_PREFIX` withdrawal credential, and the `to_execution_address` specifying the execution layer address to which the validator's balances will be withdrawn.
|
||||
|
||||
*Note*: The withdrawal key pair used to construct the `BLS_WITHDRAWAL_PREFIX` withdrawal credential should be distinct from the signing key pair used to operate the validator under typical circumstances. Consult your validator deposit tooling documentation for further details if you are not aware of the difference.
|
||||
|
||||
*Warning*: This message can only be included on-chain once and is
|
||||
irreversible so ensure the correctness and accessibility to `to_execution_address`.
|
||||
|
||||
Next, the validator signs the assembled `message: BLSToExecutionChange` with the **withdrawal BLS secret key** and this
|
||||
`signature` is placed into a `SignedBLSToExecutionChange` message along with the inner `BLSToExecutionChange` `message`.
|
||||
Note that the `SignedBLSToExecutionChange` message should pass all of the validations in [`process_bls_to_execution_change`](./beacon-chain.md#new-process_bls_to_execution_change).
|
||||
|
||||
The `SignedBLSToExecutionChange` message should then be submitted to the consensus layer network. Once included on-chain,
|
||||
the withdrawal credential change takes effect. No further action is required for a validator to enter into the automated
|
||||
withdrawal process.
|
||||
|
||||
*Note*: A node *should* prioritize locally received `BLSToExecutionChange` operations to ensure these changes make it on-chain
|
||||
through self published blocks even if the rest of the network censors.
|
||||
|
||||
@@ -122,7 +122,7 @@ This backbone is based on a pure function of the *node* identity and time:
|
||||
peers on a vertical topic can be found by searching the local peerstore for identities that hash to the desired topic(s),
|
||||
assuming the peerstore already has a large enough variety of peers.
|
||||
- Nodes can be held accountable for contributing to the backbone:
|
||||
peers that particpate in DAS but are not active on the appropriate backbone topics can be scored down.
|
||||
peers that participate in DAS but are not active on the appropriate backbone topics can be scored down.
|
||||
*Note: This is experimental, DAS should be light enough for all participants to run, but scoring needs to undergo testing*
|
||||
|
||||
A node should anticipate backbone topics to subscribe to based their own identity.
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
- [Custom types](#custom-types)
|
||||
- [Constants](#constants)
|
||||
- [Blob](#blob)
|
||||
- [Domain types](#domain-types)
|
||||
- [Preset](#preset)
|
||||
- [Execution](#execution)
|
||||
- [Configuration](#configuration)
|
||||
@@ -34,13 +33,14 @@
|
||||
- [`process_execution_payload`](#process_execution_payload)
|
||||
- [Blob KZG commitments](#blob-kzg-commitments)
|
||||
- [Testing](#testing)
|
||||
- [Disabling Withdrawals](#disabling-withdrawals)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- /TOC -->
|
||||
|
||||
## Introduction
|
||||
|
||||
This upgrade adds blobs to the beacon chain as part of EIP-4844.
|
||||
This upgrade adds blobs to the beacon chain as part of EIP-4844. This is an extension of the Capella upgrade.
|
||||
|
||||
## Custom types
|
||||
|
||||
@@ -57,12 +57,6 @@ This upgrade adds blobs to the beacon chain as part of EIP-4844.
|
||||
| `BLOB_TX_TYPE` | `uint8(0x05)` |
|
||||
| `VERSIONED_HASH_VERSION_KZG` | `Bytes1(0x01)` |
|
||||
|
||||
### Domain types
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `DOMAIN_BLOBS_SIDECAR` | `DomainType('0x0a000000')` |
|
||||
|
||||
## Preset
|
||||
|
||||
### Execution
|
||||
@@ -95,7 +89,8 @@ class BeaconBlockBody(Container):
|
||||
voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
|
||||
sync_aggregate: SyncAggregate
|
||||
# Execution
|
||||
execution_payload: ExecutionPayload
|
||||
execution_payload: ExecutionPayload # [Modified in EIP-4844]
|
||||
bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]
|
||||
blob_kzg_commitments: List[KZGCommitment, MAX_BLOBS_PER_BLOCK] # [New in EIP-4844]
|
||||
```
|
||||
|
||||
@@ -116,10 +111,11 @@ class ExecutionPayload(Container):
|
||||
timestamp: uint64
|
||||
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
|
||||
base_fee_per_gas: uint256
|
||||
excess_blobs: uint64 # [New in EIP-4844]
|
||||
excess_data_gas: uint256 # [New in EIP-4844]
|
||||
# Extra payload fields
|
||||
block_hash: Hash32 # Hash of execution block
|
||||
transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]
|
||||
withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]
|
||||
```
|
||||
|
||||
#### `ExecutionPayloadHeader`
|
||||
@@ -139,10 +135,11 @@ class ExecutionPayloadHeader(Container):
|
||||
timestamp: uint64
|
||||
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
|
||||
base_fee_per_gas: uint256
|
||||
excess_blobs: uint64 # [New in EIP-4844]
|
||||
excess_data_gas: uint256 # [New in EIP-4844]
|
||||
# Extra payload fields
|
||||
block_hash: Hash32 # Hash of execution block
|
||||
transactions_root: Root
|
||||
withdrawals_root: Root
|
||||
```
|
||||
|
||||
## Helper functions
|
||||
@@ -203,10 +200,10 @@ See [the full details of `blob_versioned_hashes` offset calculation](https://gis
|
||||
def tx_peek_blob_versioned_hashes(opaque_tx: Transaction) -> Sequence[VersionedHash]:
|
||||
assert opaque_tx[0] == BLOB_TX_TYPE
|
||||
message_offset = 1 + uint32.decode_bytes(opaque_tx[1:5])
|
||||
# field offset: 32 + 8 + 32 + 32 + 8 + 4 + 32 + 4 + 4 = 156
|
||||
# field offset: 32 + 8 + 32 + 32 + 8 + 4 + 32 + 4 + 4 + 32 = 188
|
||||
blob_versioned_hashes_offset = (
|
||||
message_offset
|
||||
+ uint32.decode_bytes(opaque_tx[(message_offset + 156):(message_offset + 160)])
|
||||
+ uint32.decode_bytes(opaque_tx[(message_offset + 188):(message_offset + 192)])
|
||||
)
|
||||
return [
|
||||
VersionedHash(opaque_tx[x:(x + 32)])
|
||||
@@ -234,7 +231,8 @@ def verify_kzg_commitments_against_transactions(transactions: Sequence[Transacti
|
||||
def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
||||
process_block_header(state, block)
|
||||
if is_execution_enabled(state, block.body):
|
||||
process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE)
|
||||
process_withdrawals(state, block.body.execution_payload)
|
||||
process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in EIP-4844]
|
||||
process_randao(state, block.body)
|
||||
process_eth1_data(state, block.body)
|
||||
process_operations(state, block.body)
|
||||
@@ -260,6 +258,7 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe
|
||||
assert payload.timestamp == compute_timestamp_at_slot(state, state.slot)
|
||||
# Verify the execution payload is valid
|
||||
assert execution_engine.notify_new_payload(payload)
|
||||
|
||||
# Cache execution payload header
|
||||
state.latest_execution_payload_header = ExecutionPayloadHeader(
|
||||
parent_hash=payload.parent_hash,
|
||||
@@ -274,9 +273,10 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe
|
||||
timestamp=payload.timestamp,
|
||||
extra_data=payload.extra_data,
|
||||
base_fee_per_gas=payload.base_fee_per_gas,
|
||||
excess_blobs=payload.excess_blobs, # [New in EIP-4844]
|
||||
excess_data_gas=payload.excess_data_gas, # [New in EIP-4844]
|
||||
block_hash=payload.block_hash,
|
||||
transactions_root=hash_tree_root(payload.transactions),
|
||||
withdrawals_root=hash_tree_root(payload.withdrawals),
|
||||
)
|
||||
```
|
||||
|
||||
@@ -342,3 +342,10 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32,
|
||||
|
||||
return state
|
||||
```
|
||||
|
||||
### Disabling Withdrawals
|
||||
During testing we avoid Capella-specific updates to the state transition. We do this by replacing the following functions with a no-op implementation:
|
||||
- `process_withdrawals`
|
||||
- `process_bls_to_execution_change`
|
||||
|
||||
The `get_expected_withdrawals` function is also modified to return an empty withdrawals list. As such, the PayloadAttributes used to update forkchoice does not contain withdrawals.
|
||||
|
||||
@@ -44,6 +44,8 @@ def compute_fork_version(epoch: Epoch) -> Version:
|
||||
"""
|
||||
if epoch >= EIP4844_FORK_EPOCH:
|
||||
return EIP4844_FORK_VERSION
|
||||
if epoch >= CAPELLA_FORK_EPOCH:
|
||||
return CAPELLA_FORK_VERSION
|
||||
if epoch >= BELLATRIX_FORK_EPOCH:
|
||||
return BELLATRIX_FORK_VERSION
|
||||
if epoch >= ALTAIR_FORK_EPOCH:
|
||||
@@ -62,12 +64,29 @@ Note that for the pure EIP-4844 networks, we don't apply `upgrade_to_eip4844` si
|
||||
|
||||
### Upgrading the state
|
||||
|
||||
Since the `eip4844.BeaconState` format is equal to the `bellatrix.BeaconState` format, we only have to update `BeaconState.fork`.
|
||||
Since the `eip4844.BeaconState` format is equal to the `capella.BeaconState` format, we only have to update `BeaconState.fork`.
|
||||
|
||||
```python
|
||||
def upgrade_to_eip4844(pre: bellatrix.BeaconState) -> BeaconState:
|
||||
# TODO: if Capella gets scheduled, add sync it with Capella.BeaconState
|
||||
epoch = bellatrix.get_current_epoch(pre)
|
||||
def upgrade_to_eip4844(pre: capella.BeaconState) -> BeaconState:
|
||||
epoch = capella.get_current_epoch(pre)
|
||||
latest_execution_payload_header = ExecutionPayloadHeader(
|
||||
parent_hash=pre.latest_execution_payload_header.parent_hash,
|
||||
fee_recipient=pre.latest_execution_payload_header.fee_recipient,
|
||||
state_root=pre.latest_execution_payload_header.state_root,
|
||||
receipts_root=pre.latest_execution_payload_header.receipts_root,
|
||||
logs_bloom=pre.latest_execution_payload_header.logs_bloom,
|
||||
prev_randao=pre.latest_execution_payload_header.prev_randao,
|
||||
block_number=pre.latest_execution_payload_header.block_number,
|
||||
gas_limit=pre.latest_execution_payload_header.gas_limit,
|
||||
gas_used=pre.latest_execution_payload_header.gas_used,
|
||||
timestamp=pre.latest_execution_payload_header.timestamp,
|
||||
extra_data=pre.latest_execution_payload_header.extra_data,
|
||||
base_fee_per_gas=pre.latest_execution_payload_header.base_fee_per_gas,
|
||||
excess_data_gas=uint256(0), # [New in EIP-4844]
|
||||
block_hash=pre.latest_execution_payload_header.block_hash,
|
||||
transactions_root=pre.latest_execution_payload_header.transactions_root,
|
||||
withdrawals_root=pre.latest_execution_payload_header.withdrawals_root,
|
||||
)
|
||||
post = BeaconState(
|
||||
# Versioning
|
||||
genesis_time=pre.genesis_time,
|
||||
@@ -108,7 +127,10 @@ def upgrade_to_eip4844(pre: bellatrix.BeaconState) -> BeaconState:
|
||||
current_sync_committee=pre.current_sync_committee,
|
||||
next_sync_committee=pre.next_sync_committee,
|
||||
# Execution-layer
|
||||
latest_execution_payload_header=pre.latest_execution_payload_header,
|
||||
latest_execution_payload_header=latest_execution_payload_header, # [Modified in EIP4844]
|
||||
# Withdrawals
|
||||
next_withdrawal_index=pre.next_withdrawal_index,
|
||||
next_withdrawal_validator_index=pre.next_withdrawal_validator_index,
|
||||
)
|
||||
|
||||
return post
|
||||
|
||||
@@ -17,12 +17,14 @@ The specification of these changes continues in the same format as the network s
|
||||
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
|
||||
- [Topics and messages](#topics-and-messages)
|
||||
- [Global topics](#global-topics)
|
||||
- [`beacon_block`](#beacon_block)
|
||||
- [`beacon_block_and_blobs_sidecar`](#beacon_block_and_blobs_sidecar)
|
||||
- [Transitioning the gossip](#transitioning-the-gossip)
|
||||
- [The Req/Resp domain](#the-reqresp-domain)
|
||||
- [Messages](#messages)
|
||||
- [BeaconBlocksByRange v2](#beaconblocksbyrange-v2)
|
||||
- [BeaconBlocksByRoot v2](#beaconblocksbyroot-v2)
|
||||
- [BeaconBlockAndBlobsSidecarByRoot v1](#beaconblockandblobssidecarbyroot-v1)
|
||||
- [BlobsSidecarsByRange v1](#blobssidecarsbyrange-v1)
|
||||
- [Design decision rationale](#design-decision-rationale)
|
||||
- [Why are blobs relayed as a sidecar, separate from beacon blocks?](#why-are-blobs-relayed-as-a-sidecar-separate-from-beacon-blocks)
|
||||
@@ -64,9 +66,9 @@ Some gossip meshes are upgraded in the fork of EIP4844 to support upgraded types
|
||||
### Topics and messages
|
||||
|
||||
Topics follow the same specification as in prior upgrades.
|
||||
All topics remain stable except the beacon block topic which is updated with the modified type.
|
||||
The `beacon_block` topic is deprecated and replaced by the `beacon_block_and_blobs_sidecar` topic. All other topics remain stable.
|
||||
|
||||
The specification around the creation, validation, and dissemination of messages has not changed from the Bellatrix document unless explicitly noted here.
|
||||
The specification around the creation, validation, and dissemination of messages has not changed from the Capella document unless explicitly noted here.
|
||||
|
||||
The derivation of the `message-id` remains stable.
|
||||
|
||||
@@ -81,11 +83,19 @@ The new topics along with the type of the `data` field of a gossipsub message ar
|
||||
|
||||
EIP4844 introduces a new global topic for beacon block and blobs-sidecars.
|
||||
|
||||
##### `beacon_block`
|
||||
|
||||
This topic is deprecated and clients **MUST NOT** expose in their topic set to any peer. Implementers do not need to do
|
||||
anything beyond simply skip implementation, and it is explicitly called out as it is a departure from previous versioning
|
||||
of this topic.
|
||||
|
||||
Refer to [the section below](#transitioning-the-gossip) for details on how to transition the gossip.
|
||||
|
||||
##### `beacon_block_and_blobs_sidecar`
|
||||
|
||||
This topic is used to propagate new signed and coupled beacon blocks and blobs sidecars to all nodes on the networks.
|
||||
|
||||
The following validations MUST pass before forwarding the `signed_beacon_block_and_blobs_sidecar` on the network.
|
||||
In addition to the gossip validations for the `beacon_block` topic from prior specifications, the following validations MUST pass before forwarding the `signed_beacon_block_and_blobs_sidecar` on the network.
|
||||
Alias `signed_beacon_block = signed_beacon_block_and_blobs_sidecar.beacon_block`, `block = signed_beacon_block.message`, `execution_payload = block.body.execution_payload`.
|
||||
- _[REJECT]_ The KZG commitments of the blobs are all correctly encoded compressed BLS G1 Points.
|
||||
-- i.e. `all(bls.KeyValidate(commitment) for commitment in block.body.blob_kzg_commitments)`
|
||||
@@ -96,8 +106,8 @@ Alias `sidecar = signed_beacon_block_and_blobs_sidecar.blobs_sidecar`.
|
||||
- _[IGNORE]_ the `sidecar.beacon_block_slot` is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `sidecar.beacon_block_slot == block.slot`.
|
||||
- _[REJECT]_ the `sidecar.blobs` are all well formatted, i.e. the `BLSFieldElement` in valid range (`x < BLS_MODULUS`).
|
||||
- _[REJECT]_ The KZG proof is a correctly encoded compressed BLS G1 Point -- i.e. `bls.KeyValidate(blobs_sidecar.kzg_aggregated_proof)`
|
||||
|
||||
Once the sidecar and beacon block are received together, `validate_blobs_sidecar` can unlock the data-availability fork-choice dependency.
|
||||
- _[REJECT]_ The KZG commitments in the block are valid against the provided blobs sidecar.
|
||||
-- i.e. `validate_blobs_sidecar(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments, sidecar)`
|
||||
|
||||
### Transitioning the gossip
|
||||
|
||||
@@ -123,13 +133,15 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
|
||||
| `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` |
|
||||
| `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` |
|
||||
| `BELLATRIX_FORK_VERSION` | `bellatrix.SignedBeaconBlock` |
|
||||
| `CAPELLA_FORK_VERSION` | `capella.SignedBeaconBlock` |
|
||||
| `EIP4844_FORK_VERSION` | `eip4844.SignedBeaconBlock` |
|
||||
|
||||
#### BeaconBlocksByRoot v2
|
||||
|
||||
**Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_root/2/`
|
||||
|
||||
The EIP-4844 fork-digest is introduced to the `context` enum to specify EIP-4844 beacon block type.
|
||||
After `EIP4844_FORK_EPOCH`, `BeaconBlocksByRootV2` is replaced by `BeaconBlockAndBlobsSidecarByRootV1`
|
||||
clients MUST support requesting blocks by root for pre-fork-epoch blocks.
|
||||
|
||||
Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
|
||||
|
||||
@@ -140,7 +152,43 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
|
||||
| `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` |
|
||||
| `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` |
|
||||
| `BELLATRIX_FORK_VERSION` | `bellatrix.SignedBeaconBlock` |
|
||||
| `EIP4844_FORK_VERSION` | `eip4844.SignedBeaconBlock` |
|
||||
| `CAPELLA_FORK_VERSION` | `capella.SignedBeaconBlock` |
|
||||
|
||||
#### BeaconBlockAndBlobsSidecarByRoot v1
|
||||
|
||||
**Protocol ID:** `/eth2/beacon_chain/req/beacon_block_and_blobs_sidecar_by_root/1/`
|
||||
|
||||
Request Content:
|
||||
|
||||
```
|
||||
(
|
||||
List[Root, MAX_REQUEST_BLOCKS]
|
||||
)
|
||||
```
|
||||
|
||||
Response Content:
|
||||
|
||||
```
|
||||
(
|
||||
List[SignedBeaconBlockAndBlobsSidecar, MAX_REQUEST_BLOCKS]
|
||||
)
|
||||
```
|
||||
|
||||
Requests blocks by block root (= `hash_tree_root(SignedBeaconBlockAndBlobsSidecar.beacon_block.message)`).
|
||||
The response is a list of `SignedBeaconBlockAndBlobsSidecar` whose length is less than or equal to the number of requests.
|
||||
It may be less in the case that the responding peer is missing blocks and sidecars.
|
||||
|
||||
No more than `MAX_REQUEST_BLOCKS` may be requested at a time.
|
||||
|
||||
`BeaconBlockAndBlobsSidecarByRoot` is primarily used to recover recent blocks and sidecars (e.g. when receiving a block or attestation whose parent is unknown).
|
||||
|
||||
The response MUST consist of zero or more `response_chunk`.
|
||||
Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlockAndBlobsSidecar` payload.
|
||||
|
||||
Clients MUST support requesting blocks and sidecars since the latest finalized epoch.
|
||||
|
||||
Clients MUST respond with at least one block and sidecar, if they have it.
|
||||
Clients MAY limit the number of blocks and sidecars in the response.
|
||||
|
||||
#### BlobsSidecarsByRange v1
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
- [KZG](#kzg)
|
||||
- [`blob_to_kzg_commitment`](#blob_to_kzg_commitment)
|
||||
- [`verify_kzg_proof`](#verify_kzg_proof)
|
||||
- [`verify_kzg_proof_impl`](#verify_kzg_proof_impl)
|
||||
- [`compute_kzg_proof`](#compute_kzg_proof)
|
||||
- [`compute_aggregated_poly_and_commitment`](#compute_aggregated_poly_and_commitment)
|
||||
- [`compute_aggregate_kzg_proof`](#compute_aggregate_kzg_proof)
|
||||
@@ -45,6 +46,8 @@
|
||||
|
||||
This document specifies basic polynomial operations and KZG polynomial commitment operations as they are needed for the EIP-4844 specification. The implementations are not optimized for performance, but readability. All practical implementations should optimize the polynomial operations.
|
||||
|
||||
Functions flagged as "Public method" MUST be provided by the underlying KZG library as public functions. All other functions are private functions used internally by the KZG library.
|
||||
|
||||
## Custom types
|
||||
|
||||
| Name | SSZ equivalent | Description |
|
||||
@@ -276,7 +279,7 @@ def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial,
|
||||
|
||||
result = 0
|
||||
for i in range(width):
|
||||
result += div(int(polynomial[i]) * int(roots_of_unity_brp[i]), (int(z) - roots_of_unity_brp[i]))
|
||||
result += div(int(polynomial[i]) * int(roots_of_unity_brp[i]), (int(z) - int(roots_of_unity_brp[i])))
|
||||
result = result * (pow(z, width, BLS_MODULUS) - 1) * inverse_width % BLS_MODULUS
|
||||
return result
|
||||
```
|
||||
@@ -289,6 +292,9 @@ KZG core functions. These are also defined in EIP-4844 execution specs.
|
||||
|
||||
```python
|
||||
def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment:
|
||||
"""
|
||||
Public method.
|
||||
"""
|
||||
return g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), blob_to_polynomial(blob))
|
||||
```
|
||||
|
||||
@@ -296,11 +302,27 @@ def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment:
|
||||
|
||||
```python
|
||||
def verify_kzg_proof(polynomial_kzg: KZGCommitment,
|
||||
z: BLSFieldElement,
|
||||
y: BLSFieldElement,
|
||||
z: Bytes32,
|
||||
y: Bytes32,
|
||||
kzg_proof: KZGProof) -> bool:
|
||||
"""
|
||||
Verify KZG proof that ``p(z) == y`` where ``p(z)`` is the polynomial represented by ``polynomial_kzg``.
|
||||
Receives inputs as bytes.
|
||||
Public method.
|
||||
"""
|
||||
return verify_kzg_proof_impl(polynomial_kzg, bytes_to_bls_field(z), bytes_to_bls_field(y), kzg_proof)
|
||||
```
|
||||
|
||||
|
||||
#### `verify_kzg_proof_impl`
|
||||
|
||||
```python
|
||||
def verify_kzg_proof_impl(polynomial_kzg: KZGCommitment,
|
||||
z: BLSFieldElement,
|
||||
y: BLSFieldElement,
|
||||
kzg_proof: KZGProof) -> bool:
|
||||
"""
|
||||
Verify KZG proof that ``p(z) == y`` where ``p(z)`` is the polynomial represented by ``polynomial_kzg``.
|
||||
"""
|
||||
# Verify: P - y = Q * (X - z)
|
||||
X_minus_z = bls.add(bls.bytes96_to_G2(KZG_SETUP_G2[1]), bls.multiply(bls.G2, BLS_MODULUS - z))
|
||||
@@ -329,7 +351,7 @@ def compute_kzg_proof(polynomial: Polynomial, z: BLSFieldElement) -> KZGProof:
|
||||
|
||||
# Make sure we won't divide by zero during division
|
||||
assert z not in ROOTS_OF_UNITY
|
||||
denominator_poly = [(x - z) % BLS_MODULUS for x in bit_reversal_permutation(ROOTS_OF_UNITY)]
|
||||
denominator_poly = [(int(x) - z) % BLS_MODULUS for x in bit_reversal_permutation(ROOTS_OF_UNITY)]
|
||||
|
||||
# Calculate quotient polynomial by doing point-by-point division
|
||||
quotient_polynomial = [div(a, b) for a, b in zip(polynomial_shifted, denominator_poly)]
|
||||
@@ -367,6 +389,9 @@ def compute_aggregated_poly_and_commitment(
|
||||
|
||||
```python
|
||||
def compute_aggregate_kzg_proof(blobs: Sequence[Blob]) -> KZGProof:
|
||||
"""
|
||||
Public method.
|
||||
"""
|
||||
commitments = [blob_to_kzg_commitment(blob) for blob in blobs]
|
||||
aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment(
|
||||
blobs,
|
||||
@@ -381,6 +406,9 @@ def compute_aggregate_kzg_proof(blobs: Sequence[Blob]) -> KZGProof:
|
||||
def verify_aggregate_kzg_proof(blobs: Sequence[Blob],
|
||||
expected_kzg_commitments: Sequence[KZGCommitment],
|
||||
kzg_aggregated_proof: KZGCommitment) -> bool:
|
||||
"""
|
||||
Public method.
|
||||
"""
|
||||
aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment(
|
||||
blobs,
|
||||
expected_kzg_commitments,
|
||||
@@ -390,5 +418,5 @@ def verify_aggregate_kzg_proof(blobs: Sequence[Blob],
|
||||
y = evaluate_polynomial_in_evaluation_form(aggregated_poly, evaluation_challenge)
|
||||
|
||||
# Verify aggregated proof
|
||||
return verify_kzg_proof(aggregated_poly_commitment, evaluation_challenge, y, kzg_aggregated_proof)
|
||||
return verify_kzg_proof_impl(aggregated_poly_commitment, evaluation_challenge, y, kzg_aggregated_proof)
|
||||
```
|
||||
|
||||
@@ -29,7 +29,7 @@ This document represents the changes to be made in the code of an "honest valida
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This document is an extension of the [Bellatrix -- Honest Validator](../bellatrix/validator.md) guide.
|
||||
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 EIP4844](./beacon-chain.md) are requisite for this document and used throughout.
|
||||
@@ -60,7 +60,7 @@ Namely, the blob handling and the addition of `SignedBeaconBlockAndBlobsSidecar`
|
||||
|
||||
##### Blob KZG commitments
|
||||
|
||||
1. After retrieving the execution payload from the execution engine as specified in Bellatrix,
|
||||
1. After retrieving the execution payload from the execution engine as specified in Capella,
|
||||
use the `payload_id` to retrieve `blobs` and `blob_kzg_commitments` via `get_blobs_and_kzg_commitments(payload_id)`.
|
||||
2. Validate `blobs` and `blob_kzg_commitments`:
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
- [`interpolate_polynomial`](#interpolate_polynomial)
|
||||
- [`evaluate_polynomial_in_evaluation_form`](#evaluate_polynomial_in_evaluation_form)
|
||||
- [KZG Operations](#kzg-operations)
|
||||
- [Elliptic curve helper functoins](#elliptic-curve-helper-functoins)
|
||||
- [Elliptic curve helper functions](#elliptic-curve-helper-functions)
|
||||
- [`elliptic_curve_lincomb`](#elliptic_curve_lincomb)
|
||||
- [Hash to field](#hash-to-field)
|
||||
- [`hash_to_bls_field`](#hash_to_bls_field)
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
## Introduction
|
||||
|
||||
This document specifies basic polynomial operations and KZG polynomial commitment operations as they are needed for the sharding specification. The implementations are not optimized for performance, but readability. All practical implementations should optimize the polynomial operations, and hints what the best known algorithms for these implementations are are included below.
|
||||
This document specifies basic polynomial operations and KZG polynomial commitment operations as they are needed for the sharding specification. The implementations are not optimized for performance, but readability. All practical implementations should optimize the polynomial operations, and hints what the best known algorithms for these implementations are included below.
|
||||
|
||||
## Constants
|
||||
|
||||
@@ -313,7 +313,7 @@ def evaluate_polynomial_in_evaluation_form(poly: BLSPolynomialByEvaluations, x:
|
||||
|
||||
We are using the KZG10 polynomial commitment scheme (Kate, Zaverucha and Goldberg, 2010: https://www.iacr.org/archive/asiacrypt2010/6477178/6477178.pdf).
|
||||
|
||||
### Elliptic curve helper functoins
|
||||
### Elliptic curve helper functions
|
||||
|
||||
#### `elliptic_curve_lincomb`
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ This ensures that blocks are only optimistically imported if one or more of the
|
||||
following are true:
|
||||
|
||||
1. The parent of the block has execution enabled.
|
||||
1. The current slot (as per the system clock) is at least
|
||||
2. The current slot (as per the system clock) is at least
|
||||
`SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY` ahead of the slot of the block being
|
||||
imported.
|
||||
|
||||
|
||||
@@ -10,12 +10,12 @@ Use an OS that has Python 3.8 or above. For example, Debian 11 (bullseye)
|
||||
```sh
|
||||
sudo apt install -y make git wget python3-venv gcc python3-dev
|
||||
```
|
||||
1. Download the latest [consensus specs](https://github.com/ethereum/consensus-specs)
|
||||
2. Download the latest [consensus specs](https://github.com/ethereum/consensus-specs)
|
||||
```sh
|
||||
git clone https://github.com/ethereum/consensus-specs.git
|
||||
cd consensus-specs
|
||||
```
|
||||
1. Create the specifications and tests:
|
||||
3. Create the specifications and tests:
|
||||
```sh
|
||||
make install_test
|
||||
make pyspec
|
||||
@@ -31,12 +31,12 @@ To read more about creating the environment, [see here](core/pyspec/README.md).
|
||||
cd ~/consensus-specs
|
||||
. venv/bin/activate
|
||||
```
|
||||
1. Run a sanity check test against Altair fork:
|
||||
2. Run a sanity check test against Altair fork:
|
||||
```sh
|
||||
cd tests/core/pyspec
|
||||
python -m pytest -k test_empty_block_transition --fork altair eth2spec
|
||||
```
|
||||
1. The output should be similar to:
|
||||
3. The output should be similar to:
|
||||
```
|
||||
============================= test session starts ==============================
|
||||
platform linux -- Python 3.9.2, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
|
||||
@@ -114,7 +114,7 @@ In Python `yield` is used by [generators](https://wiki.python.org/moin/Generator
|
||||
we can treat it as a partial return statement that doesn't stop the function's processing, only adds to a list
|
||||
of return values. Here we add two values, the string `'pre'` and the initial state, to the list of return values.
|
||||
|
||||
[You can read more about test generators and how the are used here](generators).
|
||||
[You can read more about test generators and how they are used here](generators).
|
||||
|
||||
```python
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
@@ -417,7 +417,7 @@ In the last line you can see two conditions being asserted:
|
||||
|
||||
1. `data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot` which verifies that the attestation doesn't
|
||||
arrive too early.
|
||||
1. `state.slot <= data.slot + SLOTS_PER_EPOCH` which verifies that the attestation doesn't
|
||||
2. `state.slot <= data.slot + SLOTS_PER_EPOCH` which verifies that the attestation doesn't
|
||||
arrive too late.
|
||||
|
||||
This is how the consensus layer tests deal with edge cases, by asserting the conditions required for the
|
||||
@@ -431,7 +431,7 @@ Now we'll write a similar test that verifies that being `SLOTS_PER_EPOCH` away i
|
||||
`test_after_epoch_slots` function. We need two changes:
|
||||
|
||||
1. Call `transition_to_slot_via_block` with one less slot to advance
|
||||
1. Don't tell `run_attestation_processing` to return an empty post state.
|
||||
2. Don't tell `run_attestation_processing` to return an empty post state.
|
||||
|
||||
The modified function is:
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.3.0-alpha.0
|
||||
1.3.0-alpha.1
|
||||
|
||||
@@ -4,7 +4,6 @@ import time
|
||||
import shutil
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from filelock import FileLock
|
||||
import sys
|
||||
import json
|
||||
from typing import Iterable, AnyStr, Any, Callable
|
||||
@@ -13,6 +12,7 @@ from ruamel.yaml import (
|
||||
YAML,
|
||||
)
|
||||
|
||||
from filelock import FileLock
|
||||
from snappy import compress
|
||||
|
||||
from eth2spec.test import context
|
||||
@@ -141,6 +141,10 @@ def run_generator(generator_name, test_providers: Iterable[TestProvider]):
|
||||
tprov.prepare()
|
||||
|
||||
for test_case in tprov.make_cases():
|
||||
# If preset list is assigned, filter by presets.
|
||||
if len(presets) != 0 and test_case.preset_name not in presets:
|
||||
continue
|
||||
|
||||
case_dir = (
|
||||
Path(output_dir) / Path(test_case.preset_name) / Path(test_case.fork_name)
|
||||
/ Path(test_case.runner_name) / Path(test_case.handler_name)
|
||||
@@ -179,7 +183,16 @@ def run_generator(generator_name, test_providers: Iterable[TestProvider]):
|
||||
try:
|
||||
fn(case_dir)
|
||||
except IOError as e:
|
||||
sys.exit(f'Error when dumping test "{case_dir}", part "{name}", kind "{out_kind}": {e}')
|
||||
error_message = (
|
||||
f'[Error] error when dumping test "{case_dir}", part "{name}", kind "{out_kind}": {e}'
|
||||
)
|
||||
# Write to error log file
|
||||
with log_file.open("a+") as f:
|
||||
f.write(error_message)
|
||||
traceback.print_exc(file=f)
|
||||
f.write('\n')
|
||||
|
||||
sys.exit(error_message)
|
||||
|
||||
meta = dict()
|
||||
|
||||
@@ -210,13 +223,13 @@ def run_generator(generator_name, test_providers: Iterable[TestProvider]):
|
||||
if not written_part:
|
||||
print(f"test case {case_dir} did not produce any test case parts")
|
||||
except Exception as e:
|
||||
print(f"ERROR: failed to generate vector(s) for test {case_dir}: {e}")
|
||||
traceback.print_exc()
|
||||
# Write to log file
|
||||
error_message = f"[ERROR] failed to generate vector(s) for test {case_dir}: {e}"
|
||||
# Write to error log file
|
||||
with log_file.open("a+") as f:
|
||||
f.write(f"ERROR: failed to generate vector(s) for test {case_dir}: {e}")
|
||||
f.write(error_message)
|
||||
traceback.print_exc(file=f)
|
||||
f.write('\n')
|
||||
traceback.print_exc()
|
||||
else:
|
||||
# If no written_part, the only file was incomplete_tag_file. Clear the existing case_dir folder.
|
||||
if not written_part:
|
||||
|
||||
@@ -49,7 +49,7 @@ def generate_from_tests(runner_name: str, handler_name: str, src: Any,
|
||||
preset_name=preset_name,
|
||||
runner_name=runner_name,
|
||||
handler_name=handler_name,
|
||||
suite_name='pyspec_tests',
|
||||
suite_name=getattr(tfn, 'suite_name', 'pyspec_tests'),
|
||||
case_name=case_name,
|
||||
# TODO: with_all_phases and other per-phase tooling, should be replaced with per-fork equivalent.
|
||||
case_fn=lambda: tfn(generator_mode=True, phase=phase, preset=preset_name, bls_active=bls_active)
|
||||
|
||||
@@ -212,7 +212,7 @@ def slash_some_validators_for_inactivity_scores_test(spec, state, rng=Random(404
|
||||
next_epoch_via_block(spec, future_state)
|
||||
|
||||
proposer_index = spec.get_beacon_proposer_index(future_state)
|
||||
# Slash ~1/4 of validaors
|
||||
# Slash ~1/4 of validators
|
||||
for validator_index in range(len(state.validators)):
|
||||
if rng.choice(range(4)) == 0 and validator_index != proposer_index:
|
||||
spec.slash_validator(state, validator_index)
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
with_altair_and_later,
|
||||
with_test_suite_name,
|
||||
)
|
||||
|
||||
|
||||
@with_test_suite_name("BeaconState")
|
||||
@with_altair_and_later
|
||||
@spec_state_test
|
||||
def test_current_sync_committee_merkle_proof(spec, state):
|
||||
yield "state", state
|
||||
current_sync_committee_branch = spec.compute_merkle_proof_for_state(state, spec.CURRENT_SYNC_COMMITTEE_INDEX)
|
||||
yield "object", state
|
||||
current_sync_committee_branch = \
|
||||
spec.compute_merkle_proof_for_state(state, spec.CURRENT_SYNC_COMMITTEE_INDEX)
|
||||
yield "proof", {
|
||||
"leaf": "0x" + state.current_sync_committee.hash_tree_root().hex(),
|
||||
"leaf_index": spec.CURRENT_SYNC_COMMITTEE_INDEX,
|
||||
@@ -23,11 +26,13 @@ def test_current_sync_committee_merkle_proof(spec, state):
|
||||
)
|
||||
|
||||
|
||||
@with_test_suite_name("BeaconState")
|
||||
@with_altair_and_later
|
||||
@spec_state_test
|
||||
def test_next_sync_committee_merkle_proof(spec, state):
|
||||
yield "state", state
|
||||
next_sync_committee_branch = spec.compute_merkle_proof_for_state(state, spec.NEXT_SYNC_COMMITTEE_INDEX)
|
||||
yield "object", state
|
||||
next_sync_committee_branch = \
|
||||
spec.compute_merkle_proof_for_state(state, spec.NEXT_SYNC_COMMITTEE_INDEX)
|
||||
yield "proof", {
|
||||
"leaf": "0x" + state.next_sync_committee.hash_tree_root().hex(),
|
||||
"leaf_index": spec.NEXT_SYNC_COMMITTEE_INDEX,
|
||||
@@ -42,11 +47,13 @@ def test_next_sync_committee_merkle_proof(spec, state):
|
||||
)
|
||||
|
||||
|
||||
@with_test_suite_name("BeaconState")
|
||||
@with_altair_and_later
|
||||
@spec_state_test
|
||||
def test_finality_root_merkle_proof(spec, state):
|
||||
yield "state", state
|
||||
finality_branch = spec.compute_merkle_proof_for_state(state, spec.FINALIZED_ROOT_INDEX)
|
||||
yield "object", state
|
||||
finality_branch = \
|
||||
spec.compute_merkle_proof_for_state(state, spec.FINALIZED_ROOT_INDEX)
|
||||
yield "proof", {
|
||||
"leaf": "0x" + state.finalized_checkpoint.root.hex(),
|
||||
"leaf_index": spec.FINALIZED_ROOT_INDEX,
|
||||
|
||||
@@ -136,6 +136,6 @@ def test_inactivity_scores_full_participation_leaking(spec, state):
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
# Full particiaption during a leak so all scores should decrease by 1
|
||||
# Full participation during a leak so all scores should decrease by 1
|
||||
for pre, post in zip(previous_inactivity_scores, state.inactivity_scores):
|
||||
assert post == pre - 1
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from eth2spec.test.context import (
|
||||
is_post_capella,
|
||||
is_post_eip4844,
|
||||
spec_configured_state_test,
|
||||
spec_state_test_with_matching_config,
|
||||
with_all_phases,
|
||||
with_phases,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import ALTAIR
|
||||
from eth2spec.test.helpers.forks import (
|
||||
is_post_capella, is_post_eip4844,
|
||||
)
|
||||
|
||||
|
||||
@with_phases([ALTAIR])
|
||||
|
||||
@@ -55,7 +55,7 @@ def test_is_assigned_to_sync_committee(spec, state):
|
||||
disqualified_pubkeys = set(
|
||||
filter(lambda key: key not in sync_committee_pubkeys, active_pubkeys)
|
||||
)
|
||||
# NOTE: only check `disqualified_pubkeys` if SYNC_COMMITEE_SIZE < validator count
|
||||
# NOTE: only check `disqualified_pubkeys` if SYNC_COMMITTEE_SIZE < validator count
|
||||
if disqualified_pubkeys:
|
||||
sample_size = 3
|
||||
assert validator_count >= sample_size
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from eth2spec.test.helpers.constants import CAPELLA
|
||||
from eth2spec.test.helpers.keys import pubkeys
|
||||
from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change
|
||||
|
||||
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_capella_and_later, always_bls
|
||||
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_phases, always_bls
|
||||
|
||||
|
||||
def run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=True):
|
||||
@@ -37,14 +38,14 @@ def run_bls_to_execution_change_processing(spec, state, signed_address_change, v
|
||||
yield 'post', state
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success(spec, state):
|
||||
signed_address_change = get_signed_address_change(spec, state)
|
||||
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_not_activated(spec, state):
|
||||
validator_index = 3
|
||||
@@ -62,7 +63,7 @@ def test_success_not_activated(spec, state):
|
||||
assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_in_activation_queue(spec, state):
|
||||
validator_index = 3
|
||||
@@ -80,7 +81,7 @@ def test_success_in_activation_queue(spec, state):
|
||||
assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_in_exit_queue(spec, state):
|
||||
validator_index = 3
|
||||
@@ -93,7 +94,7 @@ def test_success_in_exit_queue(spec, state):
|
||||
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_exited(spec, state):
|
||||
validator_index = 4
|
||||
@@ -110,7 +111,7 @@ def test_success_exited(spec, state):
|
||||
assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_withdrawable(spec, state):
|
||||
validator_index = 4
|
||||
@@ -128,7 +129,7 @@ def test_success_withdrawable(spec, state):
|
||||
assert spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_val_index_out_of_range(spec, state):
|
||||
# Create for one validator beyond the validator list length
|
||||
@@ -137,7 +138,7 @@ def test_fail_val_index_out_of_range(spec, state):
|
||||
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_already_0x01(spec, state):
|
||||
# Create for one validator beyond the validator list length
|
||||
@@ -149,7 +150,7 @@ def test_fail_already_0x01(spec, state):
|
||||
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_incorrect_from_bls_pubkey(spec, state):
|
||||
# Create for one validator beyond the validator list length
|
||||
@@ -163,7 +164,7 @@ def test_fail_incorrect_from_bls_pubkey(spec, state):
|
||||
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_fail_bad_signature(spec, state):
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
with_capella_and_later,
|
||||
with_phases,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import CAPELLA
|
||||
from eth2spec.test.helpers.state import next_epoch_via_block
|
||||
from eth2spec.test.helpers.deposits import (
|
||||
prepare_state_and_deposit,
|
||||
@@ -10,7 +11,7 @@ from eth2spec.test.helpers.deposits import (
|
||||
from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_top_up_to_withdrawn_validator(spec, state):
|
||||
validator_index = 0
|
||||
|
||||
@@ -1,28 +1,54 @@
|
||||
import random
|
||||
|
||||
from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
expect_assertion_error,
|
||||
with_presets,
|
||||
with_phases,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import MINIMAL, CAPELLA
|
||||
from eth2spec.test.helpers.execution_payload import (
|
||||
build_empty_execution_payload,
|
||||
)
|
||||
|
||||
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_capella_and_later
|
||||
|
||||
from eth2spec.test.helpers.state import next_slot
|
||||
from eth2spec.test.helpers.random import (
|
||||
randomize_state,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_epoch,
|
||||
next_slot,
|
||||
)
|
||||
from eth2spec.test.helpers.withdrawals import (
|
||||
prepare_expected_withdrawals,
|
||||
set_eth1_withdrawal_credential_with_balance,
|
||||
set_validator_fully_withdrawable,
|
||||
set_validator_partially_withdrawable,
|
||||
)
|
||||
|
||||
|
||||
def prepare_withdrawal_queue(spec, state, num_withdrawals):
|
||||
pre_queue_len = len(state.withdrawal_queue)
|
||||
validator_len = len(state.validators)
|
||||
for i in range(num_withdrawals):
|
||||
withdrawal = spec.Withdrawal(
|
||||
index=i + 5,
|
||||
validator_index=(i + 1000) % validator_len,
|
||||
address=b'\x42' * 20,
|
||||
amount=200000 + i,
|
||||
)
|
||||
state.withdrawal_queue.append(withdrawal)
|
||||
def verify_post_state(state, spec, expected_withdrawals,
|
||||
fully_withdrawable_indices, partial_withdrawals_indices):
|
||||
# Consider verifying also the condition when no withdrawals are expected.
|
||||
if len(expected_withdrawals) == 0:
|
||||
return
|
||||
|
||||
assert len(state.withdrawal_queue) == num_withdrawals + pre_queue_len
|
||||
expected_withdrawals_validator_indices = [withdrawal.validator_index for withdrawal in expected_withdrawals]
|
||||
assert state.next_withdrawal_index == expected_withdrawals[-1].index + 1
|
||||
next_withdrawal_validator_index = (expected_withdrawals_validator_indices[-1] + 1) % len(state.validators)
|
||||
assert state.next_withdrawal_validator_index == next_withdrawal_validator_index
|
||||
for index in fully_withdrawable_indices:
|
||||
if index in expected_withdrawals_validator_indices:
|
||||
assert state.balances[index] == 0
|
||||
else:
|
||||
assert state.balances[index] > 0
|
||||
for index in partial_withdrawals_indices:
|
||||
if index in expected_withdrawals_validator_indices:
|
||||
assert state.balances[index] == spec.MAX_EFFECTIVE_BALANCE
|
||||
else:
|
||||
assert state.balances[index] > spec.MAX_EFFECTIVE_BALANCE
|
||||
|
||||
|
||||
def run_withdrawals_processing(spec, state, execution_payload, valid=True):
|
||||
def run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=None,
|
||||
fully_withdrawable_indices=None, partial_withdrawals_indices=None, valid=True):
|
||||
"""
|
||||
Run ``process_execution_payload``, yielding:
|
||||
- pre-state ('pre')
|
||||
@@ -30,10 +56,12 @@ def run_withdrawals_processing(spec, state, execution_payload, valid=True):
|
||||
- post-state ('post').
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
"""
|
||||
expected_withdrawals = spec.get_expected_withdrawals(state)
|
||||
assert len(expected_withdrawals) <= spec.MAX_WITHDRAWALS_PER_PAYLOAD
|
||||
if num_expected_withdrawals is not None:
|
||||
assert len(expected_withdrawals) == num_expected_withdrawals
|
||||
|
||||
pre_withdrawal_queue = state.withdrawal_queue.copy()
|
||||
num_withdrawals = min(spec.MAX_WITHDRAWALS_PER_PAYLOAD, len(pre_withdrawal_queue))
|
||||
|
||||
pre_state = state.copy()
|
||||
yield 'pre', state
|
||||
yield 'execution_payload', execution_payload
|
||||
|
||||
@@ -46,18 +74,23 @@ def run_withdrawals_processing(spec, state, execution_payload, valid=True):
|
||||
|
||||
yield 'post', state
|
||||
|
||||
if len(pre_withdrawal_queue) == 0:
|
||||
assert len(state.withdrawal_queue) == 0
|
||||
elif len(pre_withdrawal_queue) <= num_withdrawals:
|
||||
assert len(state.withdrawal_queue) == 0
|
||||
else:
|
||||
assert state.withdrawal_queue == pre_withdrawal_queue[num_withdrawals:]
|
||||
if len(expected_withdrawals) == 0:
|
||||
assert state == pre_state
|
||||
elif len(expected_withdrawals) < spec.MAX_WITHDRAWALS_PER_PAYLOAD:
|
||||
assert len(spec.get_expected_withdrawals(state)) == 0
|
||||
elif len(expected_withdrawals) > spec.MAX_WITHDRAWALS_PER_PAYLOAD:
|
||||
raise ValueError('len(expected_withdrawals) should not be greater than MAX_WITHDRAWALS_PER_PAYLOAD')
|
||||
|
||||
if fully_withdrawable_indices is not None or partial_withdrawals_indices is not None:
|
||||
verify_post_state(state, spec, expected_withdrawals, fully_withdrawable_indices, partial_withdrawals_indices)
|
||||
|
||||
return expected_withdrawals
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_empty_queue(spec, state):
|
||||
assert len(state.withdrawal_queue) == 0
|
||||
def test_success_zero_expected_withdrawals(spec, state):
|
||||
assert len(spec.get_expected_withdrawals(state)) == 0
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
@@ -65,48 +98,98 @@ def test_success_empty_queue(spec, state):
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_one_in_queue(spec, state):
|
||||
prepare_withdrawal_queue(spec, state, 1)
|
||||
def test_success_one_full_withdrawal(spec, state):
|
||||
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals(
|
||||
spec, state, num_full_withdrawals=1)
|
||||
assert len(fully_withdrawable_indices) == 1
|
||||
assert len(partial_withdrawals_indices) == 0
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||
yield from run_withdrawals_processing(
|
||||
spec, state, execution_payload,
|
||||
fully_withdrawable_indices=fully_withdrawable_indices,
|
||||
partial_withdrawals_indices=partial_withdrawals_indices)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_max_per_slot_in_queue(spec, state):
|
||||
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||
def test_success_one_partial_withdrawal(spec, state):
|
||||
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals(
|
||||
spec, state, num_partial_withdrawals=1)
|
||||
assert len(fully_withdrawable_indices) == 0
|
||||
assert len(partial_withdrawals_indices) == 1
|
||||
for index in partial_withdrawals_indices:
|
||||
assert state.balances[index] > spec.MAX_EFFECTIVE_BALANCE
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||
yield from run_withdrawals_processing(
|
||||
spec, state, execution_payload,
|
||||
fully_withdrawable_indices=fully_withdrawable_indices,
|
||||
partial_withdrawals_indices=partial_withdrawals_indices
|
||||
)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_a_lot_in_queue(spec, state):
|
||||
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||
def test_success_max_per_slot(spec, state):
|
||||
num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 2
|
||||
num_partial_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD - num_full_withdrawals
|
||||
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals(
|
||||
spec, state,
|
||||
num_full_withdrawals=num_full_withdrawals, num_partial_withdrawals=num_partial_withdrawals)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||
yield from run_withdrawals_processing(
|
||||
spec, state, execution_payload,
|
||||
fully_withdrawable_indices=fully_withdrawable_indices,
|
||||
partial_withdrawals_indices=partial_withdrawals_indices)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_all_fully_withdrawable(spec, state):
|
||||
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals(
|
||||
spec, state, num_full_withdrawals=len(state.validators))
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(
|
||||
spec, state, execution_payload,
|
||||
fully_withdrawable_indices=fully_withdrawable_indices,
|
||||
partial_withdrawals_indices=partial_withdrawals_indices)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_all_partially_withdrawable(spec, state):
|
||||
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals(
|
||||
spec, state, num_partial_withdrawals=len(state.validators))
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(
|
||||
spec, state, execution_payload,
|
||||
fully_withdrawable_indices=fully_withdrawable_indices,
|
||||
partial_withdrawals_indices=partial_withdrawals_indices)
|
||||
|
||||
|
||||
#
|
||||
# Failure cases in which the number of withdrawals in the execution_payload is incorrect
|
||||
#
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_empty_queue_non_empty_withdrawals(spec, state):
|
||||
assert len(state.withdrawal_queue) == 0
|
||||
|
||||
def test_fail_non_withdrawable_non_empty_withdrawals(spec, state):
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
withdrawal = spec.Withdrawal(
|
||||
@@ -120,10 +203,10 @@ def test_fail_empty_queue_non_empty_withdrawals(spec, state):
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_one_in_queue_none_in_withdrawals(spec, state):
|
||||
prepare_withdrawal_queue(spec, state, 1)
|
||||
def test_fail_one_expected_full_withdrawal_and_none_in_withdrawals(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_full_withdrawals=1)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
@@ -132,10 +215,22 @@ def test_fail_one_in_queue_none_in_withdrawals(spec, state):
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_one_in_queue_two_in_withdrawals(spec, state):
|
||||
prepare_withdrawal_queue(spec, state, 1)
|
||||
def test_fail_one_expected_partial_withdrawal_and_none_in_withdrawals(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_partial_withdrawals=1)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.withdrawals = []
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_one_expected_full_withdrawal_and_duplicate_in_withdrawals(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_full_withdrawals=2)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
@@ -144,10 +239,22 @@ def test_fail_one_in_queue_two_in_withdrawals(spec, state):
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_max_per_slot_in_queue_one_less_in_withdrawals(spec, state):
|
||||
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||
def test_fail_two_expected_partial_withdrawal_and_duplicate_in_withdrawals(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_partial_withdrawals=2)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.withdrawals.append(execution_payload.withdrawals[0].copy())
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_max_per_slot_full_withdrawals_and_one_less_in_withdrawals(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
@@ -156,10 +263,47 @@ def test_fail_max_per_slot_in_queue_one_less_in_withdrawals(spec, state):
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_a_lot_in_queue_too_few_in_withdrawals(spec, state):
|
||||
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||
def test_fail_max_per_slot_partial_withdrawals_and_one_less_in_withdrawals(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.withdrawals = execution_payload.withdrawals[:-1]
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_a_lot_fully_withdrawable_too_few_in_withdrawals(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.withdrawals = execution_payload.withdrawals[:-1]
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_a_lot_partially_withdrawable_too_few_in_withdrawals(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.withdrawals = execution_payload.withdrawals[:-1]
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_a_lot_mixed_withdrawable_in_queue_too_few_in_withdrawals(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4,
|
||||
num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
@@ -172,10 +316,10 @@ def test_fail_a_lot_in_queue_too_few_in_withdrawals(spec, state):
|
||||
# Failure cases in which the withdrawals in the execution_payload are incorrect
|
||||
#
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_incorrect_dequeue_index(spec, state):
|
||||
prepare_withdrawal_queue(spec, state, 1)
|
||||
def test_fail_incorrect_withdrawal_index(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_full_withdrawals=1)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
@@ -184,10 +328,10 @@ def test_fail_incorrect_dequeue_index(spec, state):
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_incorrect_dequeue_address(spec, state):
|
||||
prepare_withdrawal_queue(spec, state, 1)
|
||||
def test_fail_incorrect_address_full(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_full_withdrawals=1)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
@@ -196,10 +340,22 @@ def test_fail_incorrect_dequeue_address(spec, state):
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_incorrect_dequeue_amount(spec, state):
|
||||
prepare_withdrawal_queue(spec, state, 1)
|
||||
def test_fail_incorrect_address_partial(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_partial_withdrawals=1)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.withdrawals[0].address = b'\xff' * 20
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_incorrect_amount_full(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_full_withdrawals=1)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
@@ -208,10 +364,22 @@ def test_fail_incorrect_dequeue_amount(spec, state):
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_one_of_many_dequeued_incorrectly(spec, state):
|
||||
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||
def test_fail_incorrect_amount_partial(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_full_withdrawals=1)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.withdrawals[0].amount += 1
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_one_of_many_incorrectly_full(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
@@ -226,10 +394,28 @@ def test_fail_one_of_many_dequeued_incorrectly(spec, state):
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_many_dequeued_incorrectly(spec, state):
|
||||
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||
def test_fail_one_of_many_incorrectly_partial(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
num_withdrawals = len(execution_payload.withdrawals)
|
||||
|
||||
# Pick withdrawal in middle of list and mutate
|
||||
withdrawal = execution_payload.withdrawals[num_withdrawals // 2]
|
||||
withdrawal.index += 1
|
||||
withdrawal.address = b'\x99' * 20
|
||||
withdrawal.amount += 4000000
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_many_incorrectly_full(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
@@ -242,3 +428,367 @@ def test_fail_many_dequeued_incorrectly(spec, state):
|
||||
withdrawal.amount += 1
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_fail_many_incorrectly_partial(spec, state):
|
||||
prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
for i, withdrawal in enumerate(execution_payload.withdrawals):
|
||||
if i % 3 == 0:
|
||||
withdrawal.index += 1
|
||||
elif i % 3 == 1:
|
||||
withdrawal.address = i.to_bytes(20, 'big')
|
||||
else:
|
||||
withdrawal.amount += 1
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
#
|
||||
# More full withdrawal cases
|
||||
#
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_withdrawable_epoch_but_0_balance(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, 0, current_epoch)
|
||||
|
||||
state.validators[0].effective_balance = 10000000000
|
||||
state.balances[0] = 0
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, 0, current_epoch)
|
||||
|
||||
state.validators[0].effective_balance = 0
|
||||
state.balances[0] = 0
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, 0, current_epoch)
|
||||
|
||||
state.validators[0].effective_balance = 0
|
||||
state.balances[0] = 100000000
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_no_withdrawals_but_some_next_epoch(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
|
||||
# Make a few validators withdrawable at the *next* epoch
|
||||
for index in range(3):
|
||||
set_validator_fully_withdrawable(spec, state, index, current_epoch + 1)
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_all_withdrawal(spec, state):
|
||||
# Make all validators withdrawable
|
||||
for index in range(len(state.validators)):
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(
|
||||
spec, state, execution_payload,
|
||||
num_expected_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||
|
||||
|
||||
def run_random_full_withdrawals_test(spec, state, rng):
|
||||
randomize_state(spec, state, rng)
|
||||
for index in range(len(state.validators)):
|
||||
# 50% withdrawable
|
||||
if rng.choice([True, False]):
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
validator = state.validators[index]
|
||||
# 12.5% unset credentials
|
||||
if rng.randint(0, 7) == 0:
|
||||
validator.withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:]
|
||||
# 12.5% not enough balance
|
||||
if rng.randint(0, 7) == 0:
|
||||
state.balances[index] = 0
|
||||
# 12.5% not close enough epoch
|
||||
if rng.randint(0, 7) == 0:
|
||||
validator.withdrawable_epoch += 1
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_random_full_withdrawals_0(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, random.Random(444))
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_random_full_withdrawals_1(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, random.Random(420))
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_random_full_withdrawals_2(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, random.Random(200))
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_random_full_withdrawals_3(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, random.Random(2000000))
|
||||
|
||||
|
||||
#
|
||||
# More partial withdrawal cases
|
||||
#
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_no_max_effective_balance(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
# To be partially withdrawable, the validator's effective balance must be maxed out
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, spec.MAX_EFFECTIVE_BALANCE - 1)
|
||||
validator = state.validators[validator_index]
|
||||
|
||||
assert validator.effective_balance < spec.MAX_EFFECTIVE_BALANCE
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_no_excess_balance(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
# To be partially withdrawable, the validator needs an excess balance
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, spec.MAX_EFFECTIVE_BALANCE)
|
||||
validator = state.validators[validator_index]
|
||||
|
||||
assert validator.effective_balance == spec.MAX_EFFECTIVE_BALANCE
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_excess_balance_but_no_max_effective_balance(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
validator = state.validators[validator_index]
|
||||
|
||||
# To be partially withdrawable, the validator needs both a maxed out effective balance and an excess balance
|
||||
validator.effective_balance = spec.MAX_EFFECTIVE_BALANCE - 1
|
||||
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_not_yet_active(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].activation_epoch += 4
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_in_exit_queue(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].exit_epoch = spec.get_current_epoch(state) + 1
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state) + 1)
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_exited(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].exit_epoch = spec.get_current_epoch(state)
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_active_and_slashed(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].slashed = True
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_exited_and_slashed(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].slashed = True
|
||||
state.validators[validator_index].exit_epoch = spec.get_current_epoch(state)
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_two_partial_withdrawable(spec, state):
|
||||
set_validator_partially_withdrawable(spec, state, 0)
|
||||
set_validator_partially_withdrawable(spec, state, 1)
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=2)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_success_max_partial_withdrawable(spec, state):
|
||||
# Sanity check that this test works for this state
|
||||
assert len(state.validators) >= spec.MAX_WITHDRAWALS_PER_PAYLOAD
|
||||
|
||||
for i in range(spec.MAX_WITHDRAWALS_PER_PAYLOAD):
|
||||
set_validator_partially_withdrawable(spec, state, i)
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(
|
||||
spec, state, execution_payload, num_expected_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@with_presets([MINIMAL], reason="not enough validators with mainnet config")
|
||||
@spec_state_test
|
||||
def test_success_max_plus_one_withdrawable(spec, state):
|
||||
# Sanity check that this test works for this state
|
||||
assert len(state.validators) >= spec.MAX_WITHDRAWALS_PER_PAYLOAD + 1
|
||||
|
||||
# More than MAX_WITHDRAWALS_PER_PAYLOAD partially withdrawable
|
||||
for i in range(spec.MAX_WITHDRAWALS_PER_PAYLOAD + 1):
|
||||
set_validator_partially_withdrawable(spec, state, i)
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
# Should only have MAX_WITHDRAWALS_PER_PAYLOAD withdrawals created
|
||||
yield from run_withdrawals_processing(
|
||||
spec, state, execution_payload, num_expected_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||
|
||||
|
||||
def run_random_partial_withdrawals_test(spec, state, rng):
|
||||
for _ in range(rng.randint(0, 2)):
|
||||
next_epoch(spec, state)
|
||||
randomize_state(spec, state, rng)
|
||||
|
||||
num_validators = len(state.validators)
|
||||
state.next_withdrawal_validator_index = rng.randint(0, num_validators - 1)
|
||||
|
||||
num_partially_withdrawable = rng.randint(0, num_validators - 1)
|
||||
partially_withdrawable_indices = rng.sample(range(num_validators), num_partially_withdrawable)
|
||||
for index in partially_withdrawable_indices:
|
||||
set_validator_partially_withdrawable(spec, state, index, excess_balance=rng.randint(1, 1000000000))
|
||||
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
# Note: due to the randomness and other block processing, some of these set as "partially withdrawable"
|
||||
# may not be partially withdrawable once we get to ``process_withdrawals``,
|
||||
# thus *not* using the optional third param in this call
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_random_0(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(0))
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_random_partial_withdrawals_1(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(1))
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_random_partial_withdrawals_2(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(2))
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_random_partial_withdrawals_3(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(3))
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_random_partial_withdrawals_4(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(4))
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_random_partial_withdrawals_5(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(5))
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
from random import Random
|
||||
|
||||
from eth2spec.test.context import (
|
||||
with_capella_and_later,
|
||||
spec_state_test,
|
||||
)
|
||||
from eth2spec.test.helpers.random import (
|
||||
randomize_state,
|
||||
)
|
||||
from eth2spec.test.helpers.epoch_processing import (
|
||||
run_epoch_processing_to,
|
||||
)
|
||||
from eth2spec.test.helpers.withdrawals import (
|
||||
set_validator_fully_withdrawable,
|
||||
)
|
||||
|
||||
|
||||
def run_process_full_withdrawals(spec, state, num_expected_withdrawals=None):
|
||||
run_epoch_processing_to(spec, state, 'process_full_withdrawals')
|
||||
|
||||
pre_next_withdrawal_index = state.next_withdrawal_index
|
||||
pre_withdrawal_queue = state.withdrawal_queue.copy()
|
||||
to_be_withdrawn_indices = [
|
||||
index for index, validator in enumerate(state.validators)
|
||||
if spec.is_fully_withdrawable_validator(validator, state.balances[index], spec.get_current_epoch(state))
|
||||
]
|
||||
|
||||
if num_expected_withdrawals is not None:
|
||||
assert len(to_be_withdrawn_indices) == num_expected_withdrawals
|
||||
else:
|
||||
num_expected_withdrawals = len(to_be_withdrawn_indices)
|
||||
|
||||
yield 'pre', state
|
||||
spec.process_full_withdrawals(state)
|
||||
yield 'post', state
|
||||
|
||||
for index in to_be_withdrawn_indices:
|
||||
assert state.balances[index] == 0
|
||||
|
||||
assert len(state.withdrawal_queue) == len(pre_withdrawal_queue) + num_expected_withdrawals
|
||||
assert state.next_withdrawal_index == pre_next_withdrawal_index + num_expected_withdrawals
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_no_withdrawable_validators(spec, state):
|
||||
pre_validators = state.validators.copy()
|
||||
yield from run_process_full_withdrawals(spec, state, 0)
|
||||
|
||||
assert pre_validators == state.validators
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_withdrawable_epoch_but_0_balance(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, 0, current_epoch)
|
||||
|
||||
state.validators[0].effective_balance = 10000000000
|
||||
state.balances[0] = 0
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, 0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, 0, current_epoch)
|
||||
|
||||
state.validators[0].effective_balance = 0
|
||||
state.balances[0] = 0
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, 0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, 0, current_epoch)
|
||||
|
||||
state.validators[0].effective_balance = 0
|
||||
state.balances[0] = 100000000
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_no_withdrawals_but_some_next_epoch(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
|
||||
# Make a few validators withdrawable at the *next* epoch
|
||||
for index in range(3):
|
||||
set_validator_fully_withdrawable(spec, state, index, current_epoch + 1)
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, 0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_single_withdrawal(spec, state):
|
||||
# Make one validator withdrawable
|
||||
set_validator_fully_withdrawable(spec, state, 0)
|
||||
|
||||
assert state.next_withdrawal_index == 0
|
||||
yield from run_process_full_withdrawals(spec, state, 1)
|
||||
|
||||
assert state.next_withdrawal_index == 1
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_multi_withdrawal(spec, state):
|
||||
# Make a few validators withdrawable
|
||||
for index in range(3):
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, 3)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_all_withdrawal(spec, state):
|
||||
# Make all validators withdrawable
|
||||
for index in range(len(state.validators)):
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, len(state.validators))
|
||||
|
||||
|
||||
def run_random_full_withdrawals_test(spec, state, rng):
|
||||
randomize_state(spec, state, rng)
|
||||
for index in range(len(state.validators)):
|
||||
# 50% withdrawable
|
||||
if rng.choice([True, False]):
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
validator = state.validators[index]
|
||||
# 12.5% unset credentials
|
||||
if rng.randint(0, 7) == 0:
|
||||
validator.withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:]
|
||||
# 12.5% not enough balance
|
||||
if rng.randint(0, 7) == 0:
|
||||
state.balances[index] = 0
|
||||
# 12.5% not close enough epoch
|
||||
if rng.randint(0, 7) == 0:
|
||||
validator.withdrawable_epoch += 1
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, None)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_withdrawals_0(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, Random(444))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_withdrawals_1(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, Random(420))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_withdrawals_2(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, Random(200))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_withdrawals_3(spec, state):
|
||||
yield from run_random_full_withdrawals_test(spec, state, Random(2000000))
|
||||
@@ -1,262 +0,0 @@
|
||||
import random
|
||||
from eth2spec.test.helpers.constants import MINIMAL
|
||||
from eth2spec.test.context import (
|
||||
with_capella_and_later,
|
||||
spec_state_test,
|
||||
with_presets,
|
||||
)
|
||||
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_to
|
||||
from eth2spec.test.helpers.state import next_epoch
|
||||
from eth2spec.test.helpers.random import randomize_state
|
||||
from eth2spec.test.helpers.withdrawals import (
|
||||
set_validator_partially_withdrawable,
|
||||
set_eth1_withdrawal_credential_with_balance,
|
||||
)
|
||||
|
||||
|
||||
def run_process_partial_withdrawals(spec, state, num_expected_withdrawals=None):
|
||||
# Run rest of epoch processing before predicting partial withdrawals as
|
||||
# balance changes can affect withdrawability
|
||||
run_epoch_processing_to(spec, state, 'process_partial_withdrawals')
|
||||
|
||||
pre_next_withdrawal_index = state.next_withdrawal_index
|
||||
pre_withdrawal_queue = state.withdrawal_queue.copy()
|
||||
|
||||
partially_withdrawable_indices = [
|
||||
index for index, validator in enumerate(state.validators)
|
||||
if spec.is_partially_withdrawable_validator(validator, state.balances[index])
|
||||
]
|
||||
num_partial_withdrawals = min(len(partially_withdrawable_indices), spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH)
|
||||
|
||||
if num_expected_withdrawals is not None:
|
||||
assert num_partial_withdrawals == num_expected_withdrawals
|
||||
else:
|
||||
num_expected_withdrawals = num_partial_withdrawals
|
||||
|
||||
yield 'pre', state
|
||||
spec.process_partial_withdrawals(state)
|
||||
yield 'post', state
|
||||
|
||||
post_partially_withdrawable_indices = [
|
||||
index for index, validator in enumerate(state.validators)
|
||||
if spec.is_partially_withdrawable_validator(validator, state.balances[index])
|
||||
]
|
||||
|
||||
assert len(partially_withdrawable_indices) - num_partial_withdrawals == len(post_partially_withdrawable_indices)
|
||||
|
||||
assert len(state.withdrawal_queue) == len(pre_withdrawal_queue) + num_expected_withdrawals
|
||||
assert state.next_withdrawal_index == pre_next_withdrawal_index + num_expected_withdrawals
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_no_withdrawable(spec, state):
|
||||
pre_validators = state.validators.copy()
|
||||
yield from run_process_partial_withdrawals(spec, state, 0)
|
||||
|
||||
assert pre_validators == state.validators
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_no_max_effective_balance(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
# To be partially withdrawable, the validator's effective balance must be maxed out
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, spec.MAX_EFFECTIVE_BALANCE - 1)
|
||||
validator = state.validators[validator_index]
|
||||
|
||||
assert validator.effective_balance < spec.MAX_EFFECTIVE_BALANCE
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_no_excess_balance(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
# To be partially withdrawable, the validator needs an excess balance
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, spec.MAX_EFFECTIVE_BALANCE)
|
||||
validator = state.validators[validator_index]
|
||||
|
||||
assert validator.effective_balance == spec.MAX_EFFECTIVE_BALANCE
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_excess_balance_but_no_max_effective_balance(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
validator = state.validators[validator_index]
|
||||
|
||||
# To be partially withdrawable, the validator needs both a maxed out effective balance and an excess balance
|
||||
validator.effective_balance = spec.MAX_EFFECTIVE_BALANCE - 1
|
||||
|
||||
assert not spec.is_partially_withdrawable_validator(validator, state.balances[validator_index])
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 0)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_not_yet_active(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].activation_epoch += 4
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_in_exit_queue(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].exit_epoch = spec.get_current_epoch(state) + 1
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state) + 1)
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_exited(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].exit_epoch = spec.get_current_epoch(state)
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_active_and_slashed(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].slashed = True
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_one_partial_withdrawable_exited_and_slashed(spec, state):
|
||||
validator_index = len(state.validators) // 2
|
||||
state.validators[validator_index].slashed = True
|
||||
state.validators[validator_index].exit_epoch = spec.get_current_epoch(state)
|
||||
set_validator_partially_withdrawable(spec, state, validator_index)
|
||||
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 1)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_two_partial_withdrawable(spec, state):
|
||||
set_validator_partially_withdrawable(spec, state, 0)
|
||||
set_validator_partially_withdrawable(spec, state, 1)
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, 2)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_max_partial_withdrawable(spec, state):
|
||||
# Sanity check that this test works for this state
|
||||
assert len(state.validators) >= spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH
|
||||
|
||||
for i in range(spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH):
|
||||
set_validator_partially_withdrawable(spec, state, i)
|
||||
|
||||
yield from run_process_partial_withdrawals(spec, state, spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_presets([MINIMAL], reason="not enough validators with mainnet config")
|
||||
@spec_state_test
|
||||
def test_success_max_plus_one_withdrawable(spec, state):
|
||||
# Sanity check that this test works for this state
|
||||
assert len(state.validators) >= spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH + 1
|
||||
|
||||
# More than MAX_PARTIAL_WITHDRAWALS_PER_EPOCH partially withdrawable
|
||||
for i in range(spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH + 1):
|
||||
set_validator_partially_withdrawable(spec, state, i)
|
||||
|
||||
# Should only have MAX_PARTIAL_WITHDRAWALS_PER_EPOCH withdrawals created
|
||||
yield from run_process_partial_withdrawals(spec, state, spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH)
|
||||
|
||||
|
||||
def run_random_partial_withdrawals_test(spec, state, rng):
|
||||
for _ in range(rng.randint(0, 2)):
|
||||
next_epoch(spec, state)
|
||||
randomize_state(spec, state, rng)
|
||||
|
||||
num_validators = len(state.validators)
|
||||
state.next_partial_withdrawal_validator_index = rng.randint(0, num_validators - 1)
|
||||
|
||||
num_partially_withdrawable = rng.randint(0, num_validators - 1)
|
||||
partially_withdrawable_indices = rng.sample(range(num_validators), num_partially_withdrawable)
|
||||
for index in partially_withdrawable_indices:
|
||||
set_validator_partially_withdrawable(spec, state, index, excess_balance=rng.randint(1, 1000000000))
|
||||
|
||||
# Note: due to the randomness and other epoch processing, some of these set as "partially withdrawable"
|
||||
# may not be partially withdrawable once we get to ``process_partial_withdrawals``,
|
||||
# thus *not* using the optional third param in this call
|
||||
yield from run_process_partial_withdrawals(spec, state)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_0(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(0))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_1(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(1))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_2(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(2))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_3(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(3))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_4(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(4))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_random_5(spec, state):
|
||||
yield from run_random_partial_withdrawals_test(spec, state, random.Random(5))
|
||||
@@ -0,0 +1,84 @@
|
||||
from random import Random
|
||||
|
||||
from eth2spec.test.context import (
|
||||
with_phases,
|
||||
with_custom_state,
|
||||
with_presets,
|
||||
spec_test, with_state,
|
||||
low_balances, misc_balances, large_validator_set,
|
||||
)
|
||||
from eth2spec.test.utils import with_meta_tags
|
||||
from eth2spec.test.helpers.constants import (
|
||||
BELLATRIX, CAPELLA,
|
||||
MINIMAL,
|
||||
)
|
||||
from eth2spec.test.helpers.capella.fork import (
|
||||
CAPELLA_FORK_TEST_META_TAGS,
|
||||
run_fork_test,
|
||||
)
|
||||
from eth2spec.test.helpers.random import randomize_state
|
||||
|
||||
|
||||
@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS)
|
||||
def test_capella_fork_random_0(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(1010))
|
||||
yield from run_fork_test(phases[CAPELLA], state)
|
||||
|
||||
|
||||
@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS)
|
||||
def test_capella_fork_random_1(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(2020))
|
||||
yield from run_fork_test(phases[CAPELLA], state)
|
||||
|
||||
|
||||
@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS)
|
||||
def test_capella_fork_random_2(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(3030))
|
||||
yield from run_fork_test(phases[CAPELLA], state)
|
||||
|
||||
|
||||
@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS)
|
||||
def test_capella_fork_random_3(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(4040))
|
||||
yield from run_fork_test(phases[CAPELLA], state)
|
||||
|
||||
|
||||
@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA])
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS)
|
||||
def test_capella_fork_random_low_balances(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(5050))
|
||||
yield from run_fork_test(phases[CAPELLA], state)
|
||||
|
||||
|
||||
@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA])
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS)
|
||||
def test_capella_fork_random_misc_balances(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(6060))
|
||||
yield from run_fork_test(phases[CAPELLA], state)
|
||||
|
||||
|
||||
@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA])
|
||||
@with_presets([MINIMAL],
|
||||
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS)
|
||||
def test_capella_fork_random_large_validator_set(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(7070))
|
||||
yield from run_fork_test(phases[CAPELLA], state)
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
from eth2spec.test.context import (
|
||||
with_capella_and_later, spec_state_test
|
||||
with_phases, spec_state_test
|
||||
)
|
||||
|
||||
from eth2spec.test.helpers.constants import CAPELLA
|
||||
from eth2spec.test.helpers.state import (
|
||||
state_transition_and_sign_block,
|
||||
)
|
||||
from eth2spec.test.helpers.block import (
|
||||
build_empty_block_for_next_slot, build_empty_block,
|
||||
build_empty_block_for_next_slot,
|
||||
build_empty_block,
|
||||
)
|
||||
from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_slot,
|
||||
)
|
||||
from eth2spec.test.helpers.withdrawals import (
|
||||
set_validator_fully_withdrawable,
|
||||
set_validator_partially_withdrawable,
|
||||
prepare_expected_withdrawals,
|
||||
)
|
||||
from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
#
|
||||
# BLSToExecutionChange
|
||||
#
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_successful_bls_change(spec, state):
|
||||
def test_success_bls_change(spec, state):
|
||||
index = 0
|
||||
signed_address_change = get_signed_address_change(spec, state, validator_index=index)
|
||||
pre_credentials = state.validators[index].withdrawal_credentials
|
||||
@@ -39,76 +48,9 @@ def test_successful_bls_change(spec, state):
|
||||
assert post_credentials[12:] == signed_address_change.message.to_execution_address
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_full_withdrawal_in_epoch_transition(spec, state):
|
||||
index = 0
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, index, current_epoch)
|
||||
yield 'pre', state
|
||||
|
||||
# trigger epoch transition
|
||||
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert state.balances[index] == 0
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_partial_withdrawal_in_epoch_transition(spec, state):
|
||||
index = state.next_withdrawal_index
|
||||
set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000)
|
||||
pre_balance = state.balances[index]
|
||||
pre_withdrawal_queue_len = len(state.withdrawal_queue)
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
# trigger epoch transition
|
||||
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert state.balances[index] < pre_balance
|
||||
# Potentially less than due to sync committee penalty
|
||||
assert state.balances[index] <= spec.MAX_EFFECTIVE_BALANCE
|
||||
# Withdrawal is processed within the context of the block so queue empty
|
||||
assert len(state.withdrawal_queue) == pre_withdrawal_queue_len
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_many_partial_withdrawals_in_epoch_transition(spec, state):
|
||||
assert len(state.validators) > spec.MAX_WITHDRAWALS_PER_PAYLOAD
|
||||
assert spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH > spec.MAX_WITHDRAWALS_PER_PAYLOAD
|
||||
|
||||
for i in range(spec.MAX_WITHDRAWALS_PER_PAYLOAD + 1):
|
||||
index = (i + state.next_withdrawal_index) % len(state.validators)
|
||||
set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000)
|
||||
|
||||
pre_withdrawal_queue_len = len(state.withdrawal_queue)
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
# trigger epoch transition
|
||||
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
# All new partial withdrawals processed except 1
|
||||
assert len(state.withdrawal_queue) == pre_withdrawal_queue_len + 1
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_exit_and_bls_change(spec, state):
|
||||
def test_success_exit_and_bls_change(spec, state):
|
||||
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
|
||||
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
||||
|
||||
@@ -133,3 +75,184 @@ def test_exit_and_bls_change(spec, state):
|
||||
assert not spec.is_fully_withdrawable_validator(validator, balance, current_epoch)
|
||||
assert validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||
assert spec.is_fully_withdrawable_validator(validator, balance, validator.withdrawable_epoch)
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_invalid_duplicate_bls_changes_same_block(spec, state):
|
||||
index = 0
|
||||
signed_address_change = get_signed_address_change(spec, state, validator_index=index)
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
|
||||
# Double BLSToExecutionChange of the same validator
|
||||
for _ in range(2):
|
||||
block.body.bls_to_execution_changes.append(signed_address_change)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_invalid_two_bls_changes_of_different_addresses_same_validator_same_block(spec, state):
|
||||
index = 0
|
||||
|
||||
signed_address_change_1 = get_signed_address_change(spec, state, validator_index=index,
|
||||
to_execution_address=b'\x12' * 20)
|
||||
signed_address_change_2 = get_signed_address_change(spec, state, validator_index=index,
|
||||
to_execution_address=b'\x34' * 20)
|
||||
assert signed_address_change_1 != signed_address_change_2
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
|
||||
block.body.bls_to_execution_changes.append(signed_address_change_1)
|
||||
block.body.bls_to_execution_changes.append(signed_address_change_2)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
#
|
||||
# Withdrawals
|
||||
#
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_full_withdrawal_in_epoch_transition(spec, state):
|
||||
index = 0
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
set_validator_fully_withdrawable(spec, state, index, current_epoch)
|
||||
assert len(spec.get_expected_withdrawals(state)) == 1
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
# trigger epoch transition
|
||||
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert state.balances[index] == 0
|
||||
assert len(spec.get_expected_withdrawals(state)) == 0
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_partial_withdrawal_in_epoch_transition(spec, state):
|
||||
index = state.next_withdrawal_index
|
||||
set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000)
|
||||
pre_balance = state.balances[index]
|
||||
|
||||
assert len(spec.get_expected_withdrawals(state)) == 1
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
# trigger epoch transition
|
||||
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert state.balances[index] < pre_balance
|
||||
# Potentially less than due to sync committee penalty
|
||||
assert state.balances[index] <= spec.MAX_EFFECTIVE_BALANCE
|
||||
assert len(spec.get_expected_withdrawals(state)) == 0
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_many_partial_withdrawals_in_epoch_transition(spec, state):
|
||||
assert len(state.validators) > spec.MAX_WITHDRAWALS_PER_PAYLOAD
|
||||
|
||||
for i in range(spec.MAX_WITHDRAWALS_PER_PAYLOAD + 1):
|
||||
index = (i + state.next_withdrawal_index) % len(state.validators)
|
||||
set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000)
|
||||
|
||||
assert len(spec.get_expected_withdrawals(state)) == spec.MAX_WITHDRAWALS_PER_PAYLOAD
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
# trigger epoch transition
|
||||
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert len(spec.get_expected_withdrawals(state)) == 1
|
||||
|
||||
|
||||
def _perform_valid_withdrawal(spec, state):
|
||||
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals(
|
||||
spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4,
|
||||
num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||
|
||||
next_slot(spec, state)
|
||||
pre_next_withdrawal_index = state.next_withdrawal_index
|
||||
|
||||
expected_withdrawals = spec.get_expected_withdrawals(state)
|
||||
|
||||
pre_state = state.copy()
|
||||
|
||||
# Block 1
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
signed_block_1 = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
withdrawn_indices = [withdrawal.validator_index for withdrawal in expected_withdrawals]
|
||||
fully_withdrawable_indices = list(set(fully_withdrawable_indices).difference(set(withdrawn_indices)))
|
||||
partial_withdrawals_indices = list(set(partial_withdrawals_indices).difference(set(withdrawn_indices)))
|
||||
assert state.next_withdrawal_index == pre_next_withdrawal_index + spec.MAX_WITHDRAWALS_PER_PAYLOAD
|
||||
|
||||
withdrawn_indices = [withdrawal.validator_index for withdrawal in expected_withdrawals]
|
||||
fully_withdrawable_indices = list(set(fully_withdrawable_indices).difference(set(withdrawn_indices)))
|
||||
partial_withdrawals_indices = list(set(partial_withdrawals_indices).difference(set(withdrawn_indices)))
|
||||
assert state.next_withdrawal_index == pre_next_withdrawal_index + spec.MAX_WITHDRAWALS_PER_PAYLOAD
|
||||
|
||||
return pre_state, signed_block_1, pre_next_withdrawal_index
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_withdrawal_success_two_blocks(spec, state):
|
||||
pre_state, signed_block_1, pre_next_withdrawal_index = _perform_valid_withdrawal(spec, state)
|
||||
|
||||
yield 'pre', pre_state
|
||||
|
||||
# Block 2
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
signed_block_2 = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
assert state.next_withdrawal_index == pre_next_withdrawal_index + spec.MAX_WITHDRAWALS_PER_PAYLOAD * 2
|
||||
|
||||
yield 'blocks', [signed_block_1, signed_block_2]
|
||||
yield 'post', state
|
||||
|
||||
|
||||
@with_phases([CAPELLA])
|
||||
@spec_state_test
|
||||
def test_withdrawal_fail_second_block_payload_isnt_compatible(spec, state):
|
||||
_perform_valid_withdrawal(spec, state)
|
||||
|
||||
# Block 2
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
|
||||
# Modify state.next_withdrawal_index to incorrect number
|
||||
state.next_withdrawal_index += 1
|
||||
|
||||
# Only need to output the state transition of signed_block_2
|
||||
yield 'pre', state
|
||||
|
||||
signed_block_2 = state_transition_and_sign_block(spec, state, block, expect_fail=True)
|
||||
|
||||
yield 'blocks', [signed_block_2]
|
||||
yield 'post', None
|
||||
|
||||
@@ -13,9 +13,10 @@ from .exceptions import SkippedTest
|
||||
from .helpers.constants import (
|
||||
PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844, SHARDING,
|
||||
MINIMAL, MAINNET,
|
||||
ALL_PHASES, FORKS_BEFORE_ALTAIR, FORKS_BEFORE_BELLATRIX,
|
||||
ALL_PHASES,
|
||||
ALL_FORK_UPGRADES,
|
||||
)
|
||||
from .helpers.forks import is_post_fork
|
||||
from .helpers.typing import SpecForkName, PresetBaseName
|
||||
from .helpers.genesis import create_genesis_state
|
||||
from .utils import (
|
||||
@@ -305,6 +306,7 @@ def config_fork_epoch_overrides(spec, state):
|
||||
elif _check_current_version(spec, state, EIP4844):
|
||||
overrides['ALTAIR_FORK_EPOCH'] = spec.GENESIS_EPOCH
|
||||
overrides['BELLATRIX_FORK_EPOCH'] = spec.GENESIS_EPOCH
|
||||
overrides['CAPELLA_FORK_EPOCH'] = spec.GENESIS_EPOCH
|
||||
overrides['EIP4844_FORK_EPOCH'] = spec.GENESIS_EPOCH
|
||||
elif _check_current_version(spec, state, SHARDING):
|
||||
overrides['ALTAIR_FORK_EPOCH'] = spec.GENESIS_EPOCH
|
||||
@@ -408,6 +410,15 @@ def with_all_phases(fn):
|
||||
return with_phases(ALL_PHASES)(fn)
|
||||
|
||||
|
||||
def with_all_phases_from(earliest_phase):
|
||||
"""
|
||||
A decorator factory for running a tests with every phase except the ones listed
|
||||
"""
|
||||
def decorator(fn):
|
||||
return with_phases([phase for phase in ALL_PHASES if is_post_fork(phase, earliest_phase)])(fn)
|
||||
return decorator
|
||||
|
||||
|
||||
def with_all_phases_except(exclusion_phases):
|
||||
"""
|
||||
A decorator factory for running a tests with every phase except the ones listed
|
||||
@@ -417,6 +428,12 @@ def with_all_phases_except(exclusion_phases):
|
||||
return decorator
|
||||
|
||||
|
||||
with_altair_and_later = with_all_phases_from(ALTAIR)
|
||||
with_bellatrix_and_later = with_all_phases_from(BELLATRIX)
|
||||
with_capella_and_later = with_all_phases_from(CAPELLA)
|
||||
with_eip4844_and_later = with_all_phases_from(EIP4844)
|
||||
|
||||
|
||||
def _get_preset_targets(kw):
|
||||
preset_name = DEFAULT_TEST_PRESET
|
||||
if 'preset' in kw:
|
||||
@@ -587,28 +604,6 @@ def with_config_overrides(config_overrides):
|
||||
return decorator
|
||||
|
||||
|
||||
def is_post_altair(spec):
|
||||
return spec.fork not in FORKS_BEFORE_ALTAIR
|
||||
|
||||
|
||||
def is_post_bellatrix(spec):
|
||||
return spec.fork not in FORKS_BEFORE_BELLATRIX
|
||||
|
||||
|
||||
def is_post_capella(spec):
|
||||
return spec.fork == CAPELLA
|
||||
|
||||
|
||||
def is_post_eip4844(spec):
|
||||
return spec.fork == EIP4844
|
||||
|
||||
|
||||
with_altair_and_later = with_all_phases_except([PHASE0])
|
||||
with_bellatrix_and_later = with_all_phases_except([PHASE0, ALTAIR])
|
||||
with_capella_and_later = with_all_phases_except([PHASE0, ALTAIR, BELLATRIX, EIP4844])
|
||||
with_eip4844_and_later = with_all_phases_except([PHASE0, ALTAIR, BELLATRIX, CAPELLA])
|
||||
|
||||
|
||||
def only_generator(reason):
|
||||
def _decorator(inner):
|
||||
def _wrapper(*args, **kwargs):
|
||||
@@ -620,6 +615,13 @@ def only_generator(reason):
|
||||
return _decorator
|
||||
|
||||
|
||||
def with_test_suite_name(suite_name: str):
|
||||
def _decorator(inner):
|
||||
inner.suite_name = suite_name
|
||||
return inner
|
||||
return _decorator
|
||||
|
||||
|
||||
#
|
||||
# Fork transition state tests
|
||||
#
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change
|
||||
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_eip4844_and_later
|
||||
|
||||
|
||||
def run_bls_to_execution_change_processing_no_op(spec, state, signed_address_change, valid=True):
|
||||
"""
|
||||
Run ``process_bls_to_execution_change``, yielding:
|
||||
- pre-state ('pre')
|
||||
- address-change ('address_change')
|
||||
- post-state ('post').
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
"""
|
||||
pre_state = state.copy()
|
||||
|
||||
# yield pre-state
|
||||
yield 'pre', state
|
||||
|
||||
yield 'address_change', signed_address_change
|
||||
|
||||
# If the address_change is invalid, processing is aborted, and there is no post-state.
|
||||
if not valid:
|
||||
expect_assertion_error(lambda: spec.process_bls_to_execution_change(state, signed_address_change))
|
||||
yield 'post', None
|
||||
return
|
||||
|
||||
# process address change
|
||||
spec.process_bls_to_execution_change(state, signed_address_change)
|
||||
|
||||
# yield post-state
|
||||
yield 'post', state
|
||||
|
||||
# Make sure state has NOT been changed
|
||||
assert state == pre_state
|
||||
|
||||
|
||||
@with_eip4844_and_later
|
||||
@spec_state_test
|
||||
def test_no_op(spec, state):
|
||||
signed_address_change = get_signed_address_change(spec, state)
|
||||
yield from run_bls_to_execution_change_processing_no_op(spec, state, signed_address_change)
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_eip4844_and_later
|
||||
from eth2spec.test.helpers.execution_payload import (
|
||||
build_empty_execution_payload,
|
||||
)
|
||||
from eth2spec.test.helpers.state import next_slot
|
||||
|
||||
|
||||
def run_withdrawals_processing(spec, state, execution_payload, valid=True):
|
||||
"""
|
||||
Run ``process_execution_payload``, yielding:
|
||||
- pre-state ('pre')
|
||||
- execution payload ('execution_payload')
|
||||
- post-state ('post').
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
"""
|
||||
pre_state = state.copy()
|
||||
|
||||
yield 'pre', state
|
||||
yield 'execution_payload', execution_payload
|
||||
|
||||
if not valid:
|
||||
expect_assertion_error(lambda: spec.process_withdrawals(state, execution_payload))
|
||||
yield 'post', None
|
||||
return
|
||||
|
||||
spec.process_withdrawals(state, execution_payload)
|
||||
|
||||
yield 'post', state
|
||||
|
||||
# Make sure state has NOT been changed
|
||||
assert state == pre_state
|
||||
|
||||
|
||||
@with_eip4844_and_later
|
||||
@spec_state_test
|
||||
def test_no_op(spec, state):
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||
@@ -0,0 +1,82 @@
|
||||
from eth2spec.test.context import (
|
||||
with_phases,
|
||||
with_custom_state,
|
||||
with_presets,
|
||||
spec_test, with_state,
|
||||
low_balances, misc_balances, large_validator_set,
|
||||
)
|
||||
from eth2spec.test.utils import with_meta_tags
|
||||
from eth2spec.test.helpers.constants import (
|
||||
CAPELLA, EIP4844,
|
||||
MINIMAL,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_epoch,
|
||||
next_epoch_via_block,
|
||||
)
|
||||
from eth2spec.test.helpers.eip4844.fork import (
|
||||
EIP4844_FORK_TEST_META_TAGS,
|
||||
run_fork_test,
|
||||
)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_fork_base_state(spec, phases, state):
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_fork_next_epoch(spec, phases, state):
|
||||
next_epoch(spec, state)
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_fork_next_epoch_with_block(spec, phases, state):
|
||||
next_epoch_via_block(spec, state)
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_fork_many_next_epoch(spec, phases, state):
|
||||
for _ in range(3):
|
||||
next_epoch(spec, state)
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@spec_test
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_fork_random_low_balances(spec, phases, state):
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@spec_test
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_fork_random_misc_balances(spec, phases, state):
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@with_presets([MINIMAL],
|
||||
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
|
||||
@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@spec_test
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_fork_random_large_validator_set(spec, phases, state):
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
@@ -0,0 +1,84 @@
|
||||
from random import Random
|
||||
|
||||
from eth2spec.test.context import (
|
||||
with_phases,
|
||||
with_custom_state,
|
||||
with_presets,
|
||||
spec_test, with_state,
|
||||
low_balances, misc_balances, large_validator_set,
|
||||
)
|
||||
from eth2spec.test.utils import with_meta_tags
|
||||
from eth2spec.test.helpers.constants import (
|
||||
CAPELLA, EIP4844,
|
||||
MINIMAL,
|
||||
)
|
||||
from eth2spec.test.helpers.eip4844.fork import (
|
||||
EIP4844_FORK_TEST_META_TAGS,
|
||||
run_fork_test,
|
||||
)
|
||||
from eth2spec.test.helpers.random import randomize_state
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_eip4844_fork_random_0(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(1010))
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_eip4844_fork_random_1(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(2020))
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_eip4844_fork_random_2(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(3030))
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_eip4844_fork_random_3(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(4040))
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_eip4844_fork_random_low_balances(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(5050))
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_eip4844_fork_random_misc_balances(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(6060))
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[EIP4844])
|
||||
@with_presets([MINIMAL],
|
||||
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@with_meta_tags(EIP4844_FORK_TEST_META_TAGS)
|
||||
def test_eip4844_fork_random_large_validator_set(spec, phases, state):
|
||||
randomize_state(spec, state, rng=Random(7070))
|
||||
yield from run_fork_test(phases[EIP4844], state)
|
||||
438
tests/core/pyspec/eth2spec/test/eip4844/random/test_random.py
Normal file
438
tests/core/pyspec/eth2spec/test/eip4844/random/test_random.py
Normal file
@@ -0,0 +1,438 @@
|
||||
"""
|
||||
This module is generated from the ``random`` test generator.
|
||||
Please do not edit this file manually.
|
||||
See the README for that generator for more information.
|
||||
"""
|
||||
|
||||
from eth2spec.test.helpers.constants import EIP4844
|
||||
from eth2spec.test.context import (
|
||||
misc_balances_in_default_range_with_many_validators,
|
||||
with_phases,
|
||||
zero_activation_threshold,
|
||||
only_generator,
|
||||
)
|
||||
from eth2spec.test.context import (
|
||||
always_bls,
|
||||
spec_test,
|
||||
with_custom_state,
|
||||
single_phase,
|
||||
)
|
||||
from eth2spec.test.utils.randomized_block_tests import (
|
||||
run_generated_randomized_test,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_0(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:random_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_1(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:random_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_2(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:last_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_3(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:last_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:last_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_4(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:last_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_5(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:random_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:random_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_6(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_7(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_8(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:epochs_until_leak,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:random_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_9(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:epochs_until_leak,slots:0,with-block:no_block
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:random_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_10(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:epochs_until_leak,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:last_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_11(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:epochs_until_leak,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:last_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:last_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_12(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:epochs_until_leak,slots:0,with-block:no_block
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:last_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_13(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:epochs_until_leak,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:random_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:random_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_14(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:epochs_until_leak,slots:0,with-block:no_block
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
|
||||
|
||||
@only_generator("randomized test for broad coverage, not point-to-point CI")
|
||||
@with_phases([EIP4844])
|
||||
@with_custom_state(
|
||||
balances_fn=misc_balances_in_default_range_with_many_validators,
|
||||
threshold_fn=zero_activation_threshold
|
||||
)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
def test_randomized_15(spec, state):
|
||||
# scenario as high-level, informal text:
|
||||
# epochs:epochs_until_leak,slots:0,with-block:no_block
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_eip4844
|
||||
scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
@@ -17,4 +17,4 @@ def test_verify_kzg_proof(spec, state):
|
||||
proof = spec.compute_kzg_proof(polynomial, x)
|
||||
|
||||
y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x)
|
||||
assert spec.verify_kzg_proof(commitment, x, y, proof)
|
||||
assert spec.verify_kzg_proof_impl(commitment, x, y, proof)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
from eth2spec.test.helpers.constants import (
|
||||
EIP4844,
|
||||
MINIMAL,
|
||||
)
|
||||
from eth2spec.test.helpers.sharding import (
|
||||
get_sample_opaque_tx,
|
||||
)
|
||||
from eth2spec.test.context import (
|
||||
with_phases,
|
||||
spec_state_test,
|
||||
with_presets,
|
||||
)
|
||||
|
||||
|
||||
@with_phases([EIP4844])
|
||||
@spec_state_test
|
||||
@with_presets([MINIMAL])
|
||||
def test_tx_peek_blob_versioned_hashes(spec, state):
|
||||
otx, blobs, commitments = get_sample_opaque_tx(spec)
|
||||
data_hashes = spec.tx_peek_blob_versioned_hashes(otx)
|
||||
expected = [spec.kzg_commitment_to_versioned_hash(blob_commitment) for blob_commitment in commitments]
|
||||
assert expected == data_hashes
|
||||
@@ -2,9 +2,10 @@ from lru import LRU
|
||||
|
||||
from typing import List
|
||||
|
||||
from eth2spec.test.context import expect_assertion_error, is_post_altair
|
||||
from eth2spec.test.context import expect_assertion_error
|
||||
from eth2spec.test.helpers.state import state_transition_and_sign_block, next_epoch, next_slot
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
||||
from eth2spec.test.helpers.forks import is_post_altair
|
||||
from eth2spec.test.helpers.keys import privkeys
|
||||
from eth2spec.utils import bls
|
||||
from eth2spec.utils.ssz.ssz_typing import Bitlist
|
||||
@@ -254,7 +255,7 @@ def state_transition_with_full_block(spec,
|
||||
sync_aggregate=None,
|
||||
block=None):
|
||||
"""
|
||||
Build and apply a block with attestions at the calculated `slot_to_attest` of current epoch and/or previous epoch.
|
||||
Build and apply a block with attestations at the calculated `slot_to_attest` of current epoch and/or previous epoch.
|
||||
"""
|
||||
if block is None:
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from eth2spec.test.context import is_post_altair, is_post_bellatrix
|
||||
from eth2spec.test.helpers.execution_payload import build_empty_execution_payload
|
||||
from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix
|
||||
from eth2spec.test.helpers.keys import privkeys
|
||||
from eth2spec.utils import bls
|
||||
from eth2spec.utils.bls import only_with_bls
|
||||
|
||||
@@ -2,7 +2,7 @@ from eth2spec.utils import bls
|
||||
from eth2spec.test.helpers.keys import pubkeys, privkeys, pubkey_to_privkey
|
||||
|
||||
|
||||
def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubkey=None):
|
||||
def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubkey=None, to_execution_address=None):
|
||||
if validator_index is None:
|
||||
validator_index = 0
|
||||
|
||||
@@ -13,11 +13,14 @@ def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubk
|
||||
else:
|
||||
withdrawal_privkey = pubkey_to_privkey[withdrawal_pubkey]
|
||||
|
||||
if to_execution_address is None:
|
||||
to_execution_address = b'\x42' * 20
|
||||
|
||||
domain = spec.get_domain(state, spec.DOMAIN_BLS_TO_EXECUTION_CHANGE)
|
||||
address_change = spec.BLSToExecutionChange(
|
||||
validator_index=validator_index,
|
||||
from_bls_pubkey=withdrawal_pubkey,
|
||||
to_execution_address=b'\x42' * 20,
|
||||
to_execution_address=to_execution_address,
|
||||
)
|
||||
|
||||
signing_root = spec.compute_signing_root(address_change, domain)
|
||||
|
||||
@@ -26,18 +26,16 @@ ALL_PHASES = (
|
||||
# The forks that output to the test vectors.
|
||||
TESTGEN_FORKS = (PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844)
|
||||
|
||||
FORKS_BEFORE_ALTAIR = (PHASE0,)
|
||||
FORKS_BEFORE_BELLATRIX = (PHASE0, ALTAIR)
|
||||
|
||||
# TODO: no EIP4844 fork tests now. Should add when we figure out the content of Capella.
|
||||
ALL_FORK_UPGRADES = {
|
||||
# pre_fork_name: post_fork_name
|
||||
PHASE0: ALTAIR,
|
||||
ALTAIR: BELLATRIX,
|
||||
BELLATRIX: CAPELLA,
|
||||
CAPELLA: EIP4844,
|
||||
}
|
||||
ALL_PRE_POST_FORKS = ALL_FORK_UPGRADES.items()
|
||||
AFTER_BELLATRIX_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items() if key not in FORKS_BEFORE_ALTAIR}
|
||||
AFTER_BELLATRIX_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items() if key != PHASE0}
|
||||
AFTER_BELLATRIX_PRE_POST_FORKS = AFTER_BELLATRIX_UPGRADES.items()
|
||||
|
||||
#
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
from random import Random
|
||||
|
||||
from eth2spec.test.context import (
|
||||
is_post_altair,
|
||||
expect_assertion_error,
|
||||
)
|
||||
from eth2spec.test.context import expect_assertion_error
|
||||
from eth2spec.test.helpers.forks import is_post_altair
|
||||
from eth2spec.test.helpers.keys import pubkeys, privkeys
|
||||
from eth2spec.test.helpers.state import get_balance
|
||||
from eth2spec.utils import bls
|
||||
|
||||
63
tests/core/pyspec/eth2spec/test/helpers/eip4844/fork.py
Normal file
63
tests/core/pyspec/eth2spec/test/helpers/eip4844/fork.py
Normal file
@@ -0,0 +1,63 @@
|
||||
from eth2spec.test.helpers.constants import (
|
||||
EIP4844,
|
||||
)
|
||||
|
||||
|
||||
EIP4844_FORK_TEST_META_TAGS = {
|
||||
'fork': EIP4844,
|
||||
}
|
||||
|
||||
|
||||
def run_fork_test(post_spec, pre_state):
|
||||
yield 'pre', pre_state
|
||||
|
||||
post_state = post_spec.upgrade_to_eip4844(pre_state)
|
||||
|
||||
# Stable fields
|
||||
stable_fields = [
|
||||
'genesis_time', 'genesis_validators_root', 'slot',
|
||||
# History
|
||||
'latest_block_header', 'block_roots', 'state_roots', 'historical_roots',
|
||||
# Eth1
|
||||
'eth1_data', 'eth1_data_votes', 'eth1_deposit_index',
|
||||
# Registry
|
||||
'validators', 'balances',
|
||||
# Randomness
|
||||
'randao_mixes',
|
||||
# Slashings
|
||||
'slashings',
|
||||
# Participation
|
||||
'previous_epoch_participation', 'current_epoch_participation',
|
||||
# Finality
|
||||
'justification_bits', 'previous_justified_checkpoint', 'current_justified_checkpoint', 'finalized_checkpoint',
|
||||
# Inactivity
|
||||
'inactivity_scores',
|
||||
# Sync
|
||||
'current_sync_committee', 'next_sync_committee',
|
||||
# Withdrawals
|
||||
'next_withdrawal_index', 'next_withdrawal_validator_index',
|
||||
]
|
||||
for field in stable_fields:
|
||||
assert getattr(pre_state, field) == getattr(post_state, field)
|
||||
|
||||
# Modified fields
|
||||
modified_fields = ['fork', 'latest_execution_payload_header']
|
||||
for field in modified_fields:
|
||||
assert getattr(pre_state, field) != getattr(post_state, field)
|
||||
|
||||
assert len(pre_state.validators) == len(post_state.validators)
|
||||
for pre_validator, post_validator in zip(pre_state.validators, post_state.validators):
|
||||
stable_validator_fields = [
|
||||
'pubkey', 'withdrawal_credentials',
|
||||
'effective_balance',
|
||||
'slashed',
|
||||
'activation_eligibility_epoch', 'activation_epoch', 'exit_epoch', 'withdrawable_epoch',
|
||||
]
|
||||
for field in stable_validator_fields:
|
||||
assert getattr(pre_validator, field) == getattr(post_validator, field)
|
||||
|
||||
assert pre_state.fork.current_version == post_state.fork.previous_version
|
||||
assert post_state.fork.current_version == post_spec.config.EIP4844_FORK_VERSION
|
||||
assert post_state.fork.epoch == post_spec.get_current_epoch(post_state)
|
||||
|
||||
yield 'post', post_state
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
from eth2spec.test.context import is_post_altair
|
||||
from eth2spec.test.helpers.forks import is_post_altair
|
||||
|
||||
|
||||
def get_process_calls(spec):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from eth2spec.debug.random_value import get_random_bytes_list
|
||||
from eth2spec.test.context import is_post_capella
|
||||
from eth2spec.test.helpers.forks import is_post_capella
|
||||
|
||||
|
||||
def build_empty_execution_payload(spec, state, randao_mix=None):
|
||||
@@ -30,8 +30,7 @@ def build_empty_execution_payload(spec, state, randao_mix=None):
|
||||
transactions=empty_txs,
|
||||
)
|
||||
if is_post_capella(spec):
|
||||
num_withdrawals = min(spec.MAX_WITHDRAWALS_PER_PAYLOAD, len(state.withdrawal_queue))
|
||||
payload.withdrawals = state.withdrawal_queue[:num_withdrawals]
|
||||
payload.withdrawals = spec.get_expected_withdrawals(state)
|
||||
|
||||
# TODO: real RLP + block hash logic would be nice, requires RLP and keccak256 dependency however.
|
||||
payload.block_hash = spec.Hash32(spec.hash(payload.hash_tree_root() + b"FAKE RLP HASH"))
|
||||
|
||||
33
tests/core/pyspec/eth2spec/test/helpers/forks.py
Normal file
33
tests/core/pyspec/eth2spec/test/helpers/forks.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from .constants import (
|
||||
PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844,
|
||||
)
|
||||
|
||||
|
||||
def is_post_fork(a, b):
|
||||
if a == EIP4844:
|
||||
return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844]
|
||||
if a == CAPELLA:
|
||||
return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA]
|
||||
if a == BELLATRIX:
|
||||
return b in [PHASE0, ALTAIR, BELLATRIX]
|
||||
if a == ALTAIR:
|
||||
return b in [PHASE0, ALTAIR]
|
||||
if a == PHASE0:
|
||||
return b in [PHASE0]
|
||||
raise ValueError("Unknown fork name %s" % a)
|
||||
|
||||
|
||||
def is_post_altair(spec):
|
||||
return is_post_fork(spec.fork, ALTAIR)
|
||||
|
||||
|
||||
def is_post_bellatrix(spec):
|
||||
return is_post_fork(spec.fork, BELLATRIX)
|
||||
|
||||
|
||||
def is_post_capella(spec):
|
||||
return is_post_fork(spec.fork, CAPELLA)
|
||||
|
||||
|
||||
def is_post_eip4844(spec):
|
||||
return is_post_fork(spec.fork, EIP4844)
|
||||
@@ -1,6 +1,8 @@
|
||||
from eth2spec.test.helpers.constants import (
|
||||
ALTAIR, BELLATRIX, CAPELLA, EIP4844,
|
||||
FORKS_BEFORE_ALTAIR, FORKS_BEFORE_BELLATRIX,
|
||||
)
|
||||
from eth2spec.test.helpers.forks import (
|
||||
is_post_altair, is_post_bellatrix,
|
||||
)
|
||||
from eth2spec.test.helpers.keys import pubkeys
|
||||
|
||||
@@ -88,7 +90,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
|
||||
if validator.effective_balance >= activation_threshold:
|
||||
validator.activation_eligibility_epoch = spec.GENESIS_EPOCH
|
||||
validator.activation_epoch = spec.GENESIS_EPOCH
|
||||
if spec.fork not in FORKS_BEFORE_ALTAIR:
|
||||
if is_post_altair(spec):
|
||||
state.previous_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
|
||||
state.current_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
|
||||
state.inactivity_scores.append(spec.uint64(0))
|
||||
@@ -96,13 +98,13 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
|
||||
# Set genesis validators root for domain separation and chain versioning
|
||||
state.genesis_validators_root = spec.hash_tree_root(state.validators)
|
||||
|
||||
if spec.fork not in FORKS_BEFORE_ALTAIR:
|
||||
if is_post_altair(spec):
|
||||
# Fill in sync committees
|
||||
# Note: A duplicate committee is assigned for the current and next committee at genesis
|
||||
state.current_sync_committee = spec.get_next_sync_committee(state)
|
||||
state.next_sync_committee = spec.get_next_sync_committee(state)
|
||||
|
||||
if spec.fork not in FORKS_BEFORE_BELLATRIX:
|
||||
if is_post_bellatrix(spec):
|
||||
# Initialize the execution payload header (with block number and genesis time set to 0)
|
||||
state.latest_execution_payload_header = get_sample_genesis_execution_payload_header(
|
||||
spec,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from eth2spec.test.context import is_post_altair, is_post_bellatrix
|
||||
from eth2spec.test.helpers.block_header import sign_block_header
|
||||
from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix
|
||||
from eth2spec.test.helpers.keys import pubkey_to_privkey
|
||||
from eth2spec.test.helpers.state import get_balance
|
||||
from eth2spec.test.helpers.sync_committee import (
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from random import Random
|
||||
|
||||
from eth2spec.test.helpers.attestations import cached_prepare_state_with_attestations
|
||||
from eth2spec.test.context import is_post_altair
|
||||
from eth2spec.test.helpers.deposits import mock_deposit
|
||||
from eth2spec.test.helpers.forks import is_post_altair
|
||||
from eth2spec.test.helpers.state import next_epoch
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from random import Random
|
||||
from lru import LRU
|
||||
|
||||
from eth2spec.phase0.mainnet import VALIDATOR_REGISTRY_LIMIT # equal everywhere, fine to import
|
||||
from eth2spec.test.context import is_post_altair, is_post_bellatrix
|
||||
from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_epoch,
|
||||
)
|
||||
@@ -206,7 +206,7 @@ def run_get_inclusion_delay_deltas(spec, state):
|
||||
rewarded_proposer_indices.add(earliest_attestation.proposer_index)
|
||||
|
||||
# Ensure all expected proposers have been rewarded
|
||||
# Track rewarde indices
|
||||
# Track reward indices
|
||||
proposing_indices = [a.proposer_index for a in eligible_attestations]
|
||||
for index in proposing_indices:
|
||||
if index in rewarded_proposer_indices:
|
||||
|
||||
@@ -34,13 +34,14 @@ class ECDSASignature(Container):
|
||||
class BlobTransaction(Container):
|
||||
chain_id: uint256
|
||||
nonce: uint64
|
||||
priority_fee_per_gas: uint256
|
||||
max_basefee_per_gas: uint256
|
||||
max_priority_fee_per_gas: uint256
|
||||
max_fee_per_gas: uint256
|
||||
gas: uint64
|
||||
to: Union[None, Bytes20] # Address = Bytes20
|
||||
value: uint256
|
||||
data: ByteList[MAX_CALLDATA_SIZE]
|
||||
access_list: List[AccessTuple, MAX_ACCESS_LIST_SIZE]
|
||||
max_fee_per_data_gas: uint256
|
||||
blob_versioned_hashes: List[Bytes32, MAX_VERSIONED_HASHES_LIST_SIZE]
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from eth2spec.test.context import expect_assertion_error, is_post_altair
|
||||
from eth2spec.test.context import expect_assertion_error
|
||||
from eth2spec.test.helpers.block import apply_empty_block, sign_block, transition_unsigned_block
|
||||
from eth2spec.test.helpers.forks import is_post_altair
|
||||
from eth2spec.test.helpers.voluntary_exits import get_unslashed_exited_validators
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import random
|
||||
|
||||
|
||||
def set_validator_fully_withdrawable(spec, state, index, withdrawable_epoch=None):
|
||||
if withdrawable_epoch is None:
|
||||
withdrawable_epoch = spec.get_current_epoch(state)
|
||||
|
||||
validator = state.validators[index]
|
||||
validator.withdrawable_epoch = withdrawable_epoch
|
||||
# set exit epoch as well to avoid interactions with other epoch process, e.g. forced ejecions
|
||||
# set exit epoch as well to avoid interactions with other epoch process, e.g. forced ejections
|
||||
if validator.exit_epoch > withdrawable_epoch:
|
||||
validator.exit_epoch = withdrawable_epoch
|
||||
|
||||
@@ -29,3 +32,19 @@ def set_validator_partially_withdrawable(spec, state, index, excess_balance=1000
|
||||
validator = state.validators[index]
|
||||
|
||||
assert spec.is_partially_withdrawable_validator(validator, state.balances[index])
|
||||
|
||||
|
||||
def prepare_expected_withdrawals(spec, state,
|
||||
num_full_withdrawals=0, num_partial_withdrawals=0, rng=random.Random(5566)):
|
||||
assert num_full_withdrawals + num_partial_withdrawals <= len(state.validators)
|
||||
all_validator_indices = list(range(len(state.validators)))
|
||||
sampled_indices = rng.sample(all_validator_indices, num_full_withdrawals + num_partial_withdrawals)
|
||||
fully_withdrawable_indices = rng.sample(sampled_indices, num_full_withdrawals)
|
||||
partial_withdrawals_indices = list(set(sampled_indices).difference(set(fully_withdrawable_indices)))
|
||||
|
||||
for index in fully_withdrawable_indices:
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
for index in partial_withdrawals_indices:
|
||||
set_validator_partially_withdrawable(spec, state, index)
|
||||
|
||||
return fully_withdrawable_indices, partial_withdrawals_indices
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from random import Random
|
||||
from eth2spec.test.context import is_post_altair, spec_state_test, with_all_phases
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.helpers.epoch_processing import (
|
||||
run_epoch_processing_with,
|
||||
)
|
||||
from eth2spec.test.helpers.forks import is_post_altair
|
||||
from eth2spec.test.helpers.state import transition_to, next_epoch_via_block, next_slot
|
||||
from eth2spec.test.helpers.voluntary_exits import get_unslashed_exited_validators
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ def test_activation_queue_no_activation_no_finality(spec, state):
|
||||
def test_activation_queue_sorting(spec, state):
|
||||
churn_limit = spec.get_validator_churn_limit(state)
|
||||
|
||||
# try to activate more than the per-epoch churn linmit
|
||||
# try to activate more than the per-epoch churn limit
|
||||
mock_activations = churn_limit * 2
|
||||
|
||||
epoch = spec.get_current_epoch(state)
|
||||
|
||||
@@ -5,6 +5,8 @@ from eth2spec.test.context import (
|
||||
with_custom_state,
|
||||
zero_activation_threshold,
|
||||
misc_balances, low_single_balance,
|
||||
)
|
||||
from eth2spec.test.helpers.forks import (
|
||||
is_post_altair,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from random import Random
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases, is_post_altair, is_post_bellatrix
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.helpers.epoch_processing import (
|
||||
run_epoch_processing_with, run_epoch_processing_to
|
||||
)
|
||||
from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix
|
||||
from eth2spec.test.helpers.random import randomize_state
|
||||
from eth2spec.test.helpers.state import has_active_balance_differential
|
||||
from eth2spec.test.helpers.voluntary_exits import get_unslashed_exited_validators
|
||||
|
||||
@@ -2,7 +2,6 @@ import random
|
||||
from eth_utils import encode_hex
|
||||
|
||||
from eth2spec.test.context import (
|
||||
is_post_altair,
|
||||
spec_state_test,
|
||||
with_all_phases,
|
||||
with_presets,
|
||||
@@ -24,6 +23,9 @@ from eth2spec.test.helpers.fork_choice import (
|
||||
tick_and_run_on_attestation,
|
||||
tick_and_add_block,
|
||||
)
|
||||
from eth2spec.test.helpers.forks import (
|
||||
is_post_altair,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_slots,
|
||||
next_epoch,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from eth2spec.test.context import (
|
||||
is_post_altair,
|
||||
single_phase,
|
||||
spec_test,
|
||||
with_presets,
|
||||
@@ -10,6 +9,9 @@ from eth2spec.test.helpers.deposits import (
|
||||
prepare_full_genesis_deposits,
|
||||
prepare_random_genesis_deposits,
|
||||
)
|
||||
from eth2spec.test.helpers.forks import (
|
||||
is_post_altair,
|
||||
)
|
||||
|
||||
|
||||
def get_post_altair_description(spec):
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from eth2spec.test.context import (
|
||||
is_post_altair,
|
||||
spec_test,
|
||||
single_phase,
|
||||
with_presets,
|
||||
@@ -9,6 +8,9 @@ from eth2spec.test.helpers.constants import MINIMAL
|
||||
from eth2spec.test.helpers.deposits import (
|
||||
prepare_full_genesis_deposits,
|
||||
)
|
||||
from eth2spec.test.helpers.forks import (
|
||||
is_post_altair,
|
||||
)
|
||||
|
||||
|
||||
def get_post_altair_description(spec):
|
||||
|
||||
@@ -30,6 +30,7 @@ from eth2spec.test.helpers.sync_committee import (
|
||||
compute_sync_committee_participant_reward_and_penalty,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import PHASE0, MINIMAL
|
||||
from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix
|
||||
from eth2spec.test.context import (
|
||||
spec_test, spec_state_test, dump_skipping_message,
|
||||
with_phases, with_all_phases, single_phase,
|
||||
@@ -37,8 +38,6 @@ from eth2spec.test.context import (
|
||||
with_presets,
|
||||
with_custom_state,
|
||||
large_validator_set,
|
||||
is_post_altair,
|
||||
is_post_bellatrix,
|
||||
)
|
||||
|
||||
|
||||
@@ -434,7 +433,7 @@ def test_proposer_slashing(spec, state):
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_double_same_proposer_slashings_same_block(spec, state):
|
||||
def test_invalid_duplicate_proposer_slashings_same_block(spec, state):
|
||||
proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True)
|
||||
slashed_index = proposer_slashing.signed_header_1.message.proposer_index
|
||||
assert not state.validators[slashed_index].slashed
|
||||
@@ -451,7 +450,7 @@ def test_double_same_proposer_slashings_same_block(spec, state):
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_double_similar_proposer_slashings_same_block(spec, state):
|
||||
def test_invalid_similar_proposer_slashings_same_block(spec, state):
|
||||
slashed_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
|
||||
# Same validator, but different slashable offences in the same block
|
||||
@@ -550,7 +549,7 @@ def test_attester_slashing(spec, state):
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_duplicate_attester_slashing(spec, state):
|
||||
def test_invalid_duplicate_attester_slashing_same_block(spec, state):
|
||||
if spec.MAX_ATTESTER_SLASHINGS < 2:
|
||||
return dump_skipping_message("Skip test if config cannot handle multiple AttesterSlashings per block")
|
||||
|
||||
@@ -745,6 +744,27 @@ def test_deposit_in_block(spec, state):
|
||||
assert state.validators[validator_index].pubkey == pubkeys[validator_index]
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_invalid_duplicate_deposit_same_block(spec, state):
|
||||
validator_index = len(state.validators)
|
||||
amount = spec.MAX_EFFECTIVE_BALANCE
|
||||
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
|
||||
# The same deposit of the same validator
|
||||
for _ in range(2):
|
||||
block.body.deposits.append(deposit)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_deposit_top_up(spec, state):
|
||||
@@ -832,6 +852,49 @@ def test_attestation(spec, state):
|
||||
assert spec.hash_tree_root(state.previous_epoch_participation) == pre_current_epoch_participation_root
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_duplicate_attestation_same_block(spec, state):
|
||||
next_epoch(spec, state)
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
attestation_block = build_empty_block(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
||||
|
||||
index = 0
|
||||
|
||||
attestation = get_valid_attestation(spec, state, index=index, signed=True)
|
||||
|
||||
if not is_post_altair(spec):
|
||||
pre_current_attestations_len = len(state.current_epoch_attestations)
|
||||
|
||||
# Add to state via block transition
|
||||
for _ in range(2):
|
||||
attestation_block.body.attestations.append(attestation)
|
||||
signed_attestation_block = state_transition_and_sign_block(spec, state, attestation_block)
|
||||
|
||||
if not is_post_altair(spec):
|
||||
assert len(state.current_epoch_attestations) == pre_current_attestations_len + 2
|
||||
# Epoch transition should move to previous_epoch_attestations
|
||||
pre_current_attestations_root = spec.hash_tree_root(state.current_epoch_attestations)
|
||||
else:
|
||||
pre_current_epoch_participation_root = spec.hash_tree_root(state.current_epoch_participation)
|
||||
|
||||
epoch_block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
signed_epoch_block = state_transition_and_sign_block(spec, state, epoch_block)
|
||||
|
||||
yield 'blocks', [signed_attestation_block, signed_epoch_block]
|
||||
yield 'post', state
|
||||
|
||||
if not is_post_altair(spec):
|
||||
assert len(state.current_epoch_attestations) == 0
|
||||
assert spec.hash_tree_root(state.previous_epoch_attestations) == pre_current_attestations_root
|
||||
else:
|
||||
for index in range(len(state.validators)):
|
||||
assert state.current_epoch_participation[index] == spec.ParticipationFlags(0b0000_0000)
|
||||
assert spec.hash_tree_root(state.previous_epoch_participation) == pre_current_epoch_participation_root
|
||||
|
||||
|
||||
# After SHARDING is enabled, a committee is computed for SHARD_COMMITTEE_PERIOD slots ago,
|
||||
# exceeding the minimal-config randao mixes memory size.
|
||||
# Applies to all voluntary-exit sanity block tests.
|
||||
@@ -867,7 +930,7 @@ def test_voluntary_exit(spec, state):
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_double_validator_exit_same_block(spec, state):
|
||||
def test_invalid_duplicate_validator_exit_same_block(spec, state):
|
||||
validator_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
|
||||
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
with_all_phases,
|
||||
is_post_altair, is_post_bellatrix,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import MAX_UINT_64
|
||||
from eth2spec.test.helpers.forks import (
|
||||
is_post_altair, is_post_bellatrix,
|
||||
)
|
||||
|
||||
|
||||
def check_bound(value, lower_bound, upper_bound):
|
||||
|
||||
@@ -20,6 +20,9 @@ from eth2spec.test.helpers.random import (
|
||||
randomize_state as randomize_state_helper,
|
||||
patch_state_to_non_leaking,
|
||||
)
|
||||
from eth2spec.test.helpers.sharding import (
|
||||
get_sample_opaque_tx,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_slot,
|
||||
next_epoch,
|
||||
@@ -78,6 +81,17 @@ def randomize_state_capella(spec, state, stats, exit_fraction=0.1, slash_fractio
|
||||
stats,
|
||||
exit_fraction=exit_fraction,
|
||||
slash_fraction=slash_fraction)
|
||||
# TODO: randomize withdrawals
|
||||
return scenario_state
|
||||
|
||||
|
||||
def randomize_state_eip4844(spec, state, stats, exit_fraction=0.1, slash_fraction=0.1):
|
||||
scenario_state = randomize_state_capella(spec,
|
||||
state,
|
||||
stats,
|
||||
exit_fraction=exit_fraction,
|
||||
slash_fraction=slash_fraction)
|
||||
# TODO: randomize execution payload
|
||||
return scenario_state
|
||||
|
||||
|
||||
@@ -215,6 +229,16 @@ def random_block_capella(spec, state, signed_blocks, scenario_state, rng=Random(
|
||||
return block
|
||||
|
||||
|
||||
def random_block_eip4844(spec, state, signed_blocks, scenario_state, rng=Random(3456)):
|
||||
block = random_block_capella(spec, state, signed_blocks, scenario_state)
|
||||
# TODO: more commitments. blob_kzg_commitments: List[KZGCommitment, MAX_BLOBS_PER_BLOCK]
|
||||
opaque_tx, _, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=1)
|
||||
block.body.execution_payload.transactions = [opaque_tx]
|
||||
block.body.blob_kzg_commitments = blob_kzg_commitments
|
||||
|
||||
return block
|
||||
|
||||
|
||||
# validations
|
||||
|
||||
def no_op_validation(_spec, _state):
|
||||
|
||||
@@ -5,6 +5,7 @@ from py_ecc.optimized_bls12_381 import ( # noqa: F401
|
||||
G2,
|
||||
Z1,
|
||||
Z2,
|
||||
FQ,
|
||||
add,
|
||||
multiply,
|
||||
neg,
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
# Ref:
|
||||
# - https://github.com/ethereum/research/blob/8f084630528ba33d92b2bc05edf5338dd193c6f1/trusted_setup/trusted_setup.py
|
||||
# - https://github.com/asn-d6/kzgverify
|
||||
import json
|
||||
import os
|
||||
from typing import (
|
||||
Tuple,
|
||||
Sequence,
|
||||
)
|
||||
from pathlib import Path
|
||||
|
||||
from eth_utils import encode_hex
|
||||
from py_ecc.optimized_bls12_381 import ( # noqa: F401
|
||||
G1,
|
||||
G2,
|
||||
@@ -11,13 +20,16 @@ from py_ecc.optimized_bls12_381 import ( # noqa: F401
|
||||
multiply,
|
||||
neg,
|
||||
)
|
||||
from py_ecc.typing import (
|
||||
Optimized_Point3D,
|
||||
)
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
PRIMITIVE_ROOT_OF_UNITY = 7
|
||||
|
||||
|
||||
def generate_setup(generator, secret, length):
|
||||
def generate_setup(generator: Optimized_Point3D, secret: int, length: int) -> Tuple[Optimized_Point3D]:
|
||||
"""
|
||||
Generate trusted setup of ``generator`` in ``length``.
|
||||
"""
|
||||
@@ -27,7 +39,7 @@ def generate_setup(generator, secret, length):
|
||||
return tuple(result)
|
||||
|
||||
|
||||
def fft(vals, modulus, domain):
|
||||
def fft(vals: Sequence[Optimized_Point3D], modulus: int, domain: int) -> Sequence[Optimized_Point3D]:
|
||||
"""
|
||||
FFT for group elements
|
||||
"""
|
||||
@@ -43,7 +55,7 @@ def fft(vals, modulus, domain):
|
||||
return o
|
||||
|
||||
|
||||
def compute_root_of_unity(length) -> int:
|
||||
def compute_root_of_unity(length: int) -> int:
|
||||
"""
|
||||
Generate a w such that ``w**length = 1``.
|
||||
"""
|
||||
@@ -51,11 +63,12 @@ def compute_root_of_unity(length) -> int:
|
||||
return pow(PRIMITIVE_ROOT_OF_UNITY, (BLS_MODULUS - 1) // length, BLS_MODULUS)
|
||||
|
||||
|
||||
def compute_roots_of_unity(field_elements_per_blob):
|
||||
def compute_roots_of_unity(field_elements_per_blob: int) -> Tuple[int]:
|
||||
"""
|
||||
Compute a list of roots of unity for a given order.
|
||||
The order must divide the BLS multiplicative group order, i.e. BLS_MODULUS - 1
|
||||
"""
|
||||
field_elements_per_blob = int(field_elements_per_blob) # to non-SSZ int
|
||||
assert (BLS_MODULUS - 1) % field_elements_per_blob == 0
|
||||
root_of_unity = compute_root_of_unity(length=field_elements_per_blob)
|
||||
|
||||
@@ -64,10 +77,10 @@ def compute_roots_of_unity(field_elements_per_blob):
|
||||
for _ in range(field_elements_per_blob):
|
||||
roots.append(current_root_of_unity)
|
||||
current_root_of_unity = current_root_of_unity * root_of_unity % BLS_MODULUS
|
||||
return roots
|
||||
return tuple(roots)
|
||||
|
||||
|
||||
def get_lagrange(setup):
|
||||
def get_lagrange(setup: Sequence[Optimized_Point3D]) -> Tuple[bytes]:
|
||||
"""
|
||||
Convert a G1 or G2 portion of a setup into the Lagrange basis.
|
||||
"""
|
||||
@@ -77,4 +90,34 @@ def get_lagrange(setup):
|
||||
# TODO: introduce an IFFT function for simplicity
|
||||
fft_output = fft(setup, BLS_MODULUS, domain)
|
||||
inv_length = pow(len(setup), BLS_MODULUS - 2, BLS_MODULUS)
|
||||
return [bls.G1_to_bytes48(multiply(fft_output[-i], inv_length)) for i in range(len(fft_output))]
|
||||
return tuple(bls.G1_to_bytes48(multiply(fft_output[-i], inv_length)) for i in range(len(fft_output)))
|
||||
|
||||
|
||||
def dump_kzg_trusted_setup_files(secret: int, length: int, output_dir: str) -> None:
|
||||
setup_g1 = generate_setup(bls.G1, secret, length)
|
||||
setup_g2 = generate_setup(bls.G2, secret, length)
|
||||
setup_g1_lagrange = get_lagrange(setup_g1)
|
||||
roots_of_unity = compute_roots_of_unity(length)
|
||||
|
||||
serailized_setup_g1 = [encode_hex(bls.G1_to_bytes48(p)) for p in setup_g1]
|
||||
serialized_setup_g2 = [encode_hex(bls.G2_to_bytes96(p)) for p in setup_g2]
|
||||
serialized_setup_g1_lagrange = [encode_hex(x) for x in setup_g1_lagrange]
|
||||
|
||||
output_dir_path = Path(output_dir)
|
||||
|
||||
if not os.path.exists(output_dir_path):
|
||||
os.makedirs(output_dir_path)
|
||||
print("Created directory: ", output_dir_path)
|
||||
|
||||
file_path = output_dir_path / 'testing_trusted_setups.json'
|
||||
|
||||
with open(file_path, 'w+') as f:
|
||||
json.dump(
|
||||
{
|
||||
"setup_G1": serailized_setup_g1,
|
||||
"setup_G2": serialized_setup_g2,
|
||||
"setup_G1_lagrange": serialized_setup_g1_lagrange,
|
||||
"roots_of_unity": roots_of_unity,
|
||||
}, f)
|
||||
|
||||
print(f'Generated trusted setup file: {file_path}\n')
|
||||
|
||||
@@ -5,24 +5,26 @@ generation and verification of merkle proofs based on static data.
|
||||
|
||||
## Test case format
|
||||
|
||||
### `state.ssz_snappy`
|
||||
Tests for each individual SSZ type are grouped into a `suite` indicating the SSZ type name.
|
||||
|
||||
An SSZ-snappy encoded `BeaconState` object from which other data is generated.
|
||||
### `object.yaml`
|
||||
|
||||
A SSZ-snappy encoded object from which other data is generated. The SSZ type can be determined from the test `suite` name.
|
||||
|
||||
### `proof.yaml`
|
||||
|
||||
A proof of the leaf value (a merkle root) at generalized-index `leaf_index` in the given `state`.
|
||||
A proof of the leaf value (a merkle root) at generalized-index `leaf_index` in the given `object`.
|
||||
|
||||
```yaml
|
||||
leaf: Bytes32 # string, hex encoded, with 0x prefix
|
||||
leaf_index: int # integer, decimal
|
||||
branch: list of Bytes32 # list, each element is a string, hex encoded, with 0x prefix
|
||||
branch: list of Bytes32 # list, each element is a string, hex encoded, with 0x prefix
|
||||
```
|
||||
|
||||
## Condition
|
||||
|
||||
A test-runner can implement the following assertions:
|
||||
- Check that `is_valid_merkle_branch` confirms `leaf` at `leaf_index` to verify
|
||||
against `has_tree_root(state)` and `proof`.
|
||||
against `hash_tree_root(object)` and `branch`.
|
||||
- If the implementation supports generating merkle proofs, check that the
|
||||
self-generated proof matches the `proof` provided with the test.
|
||||
self-generated proof matches the `branch` provided with the test.
|
||||
|
||||
@@ -40,7 +40,7 @@ Blocks must be processed in order, following the main transition function
|
||||
Blocks are encoded as `SignedBeaconBlock`s from the relevant spec version
|
||||
as indicated by the `post_fork` and `fork_block` data in the `meta.yaml`.
|
||||
|
||||
As blocks span fork boundaires, a `fork_block` number is given in
|
||||
As blocks span fork boundaries, a `fork_block` number is given in
|
||||
the `meta.yaml` to help resolve which blocks belong to which fork.
|
||||
|
||||
The `fork_block` is the index in the test data of the **last** block
|
||||
|
||||
@@ -186,7 +186,7 @@ if __name__ == "__main__":
|
||||
ALTAIR: altair_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="sanity", specs=specs, all_mods=all_mods)
|
||||
run_state_test_generators(runner_name="sanity", all_mods=all_mods)
|
||||
```
|
||||
|
||||
Here multiple phases load the configuration, and the stream of test cases is derived from a pytest file using the `eth2spec.gen_helpers.gen_from_tests.gen.run_state_test_generators` utility. Note that this helper generates all available tests of `TESTGEN_FORKS` forks of `ALL_CONFIGS` configs of the given runner.
|
||||
@@ -210,7 +210,7 @@ To add a new test generator that builds `New Tests`:
|
||||
with any dependencies it may need. Leave it empty if your generator has none.
|
||||
3. Your generator is assumed to have a `main.py` file in its root.
|
||||
By adding the base generator to your requirements, you can make a generator really easily. See docs below.
|
||||
4. Your generator is called with `-o some/file/path/for_testing/can/be_anything -c some/other/path/to_configs/`.
|
||||
4. Your generator is called with `-o some/file/path/for_testing/can/be_anything --preset-list mainnet minimal`.
|
||||
The base generator helps you handle this; you only have to define test case providers.
|
||||
5. Finally, add any linting or testing commands to the
|
||||
[circleci config file](../../.circleci/config.yml) if desired to increase code quality.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -27,11 +27,13 @@ if __name__ == "__main__":
|
||||
# so no additional tests required.
|
||||
bellatrix_mods = altair_mods
|
||||
|
||||
_new_capella_mods = {key: 'eth2spec.test.capella.epoch_processing.test_process_' + key for key in [
|
||||
'full_withdrawals',
|
||||
'partial_withdrawals',
|
||||
]}
|
||||
capella_mods = combine_mods(_new_capella_mods, altair_mods)
|
||||
# No epoch-processing changes in Capella and previous testing repeats with new types,
|
||||
# so no additional tests required.
|
||||
capella_mods = bellatrix_mods
|
||||
|
||||
# No epoch-processing changes in EIP4844 and previous testing repeats with new types,
|
||||
# so no additional tests required.
|
||||
eip4844_mods = capella_mods
|
||||
|
||||
# TODO Custody Game testgen is disabled for now
|
||||
# custody_game_mods = {**{key: 'eth2spec.test.custody_game.epoch_processing.test_process_' + key for key in [
|
||||
@@ -45,6 +47,7 @@ if __name__ == "__main__":
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
EIP4844: eip4844_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="epoch_processing", all_mods=all_mods)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -7,12 +7,14 @@ if __name__ == "__main__":
|
||||
altair_mods = phase_0_mods # No additional Altair specific finality tests
|
||||
bellatrix_mods = altair_mods # No additional Bellatrix specific finality tests
|
||||
capella_mods = bellatrix_mods # No additional Capella specific finality tests
|
||||
eip4844_mods = capella_mods # No additional EIP4844 specific finality tests
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
EIP4844: eip4844_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="finality", all_mods=all_mods)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -18,11 +18,14 @@ if __name__ == "__main__":
|
||||
]}
|
||||
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
|
||||
capella_mods = bellatrix_mods # No additional Capella specific fork choice tests
|
||||
eip4844_mods = capella_mods # No additional Capella specific fork choice tests
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
EIP4844: eip4844_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="fork_choice", all_mods=all_mods)
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
from typing import Iterable
|
||||
|
||||
from eth2spec.test.helpers.constants import (
|
||||
PHASE0, ALTAIR, BELLATRIX, CAPELLA,
|
||||
PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844,
|
||||
MINIMAL, MAINNET,
|
||||
)
|
||||
from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName
|
||||
from eth2spec.test.altair.fork import test_altair_fork_basic, test_altair_fork_random
|
||||
from eth2spec.test.bellatrix.fork import test_bellatrix_fork_basic, test_bellatrix_fork_random
|
||||
from eth2spec.test.capella.fork import test_capella_fork_basic, test_capella_fork_random
|
||||
from eth2spec.test.eip4844.fork import test_eip4844_fork_basic, test_eip4844_fork_random
|
||||
from eth2spec.gen_helpers.gen_base import gen_runner, gen_typing
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import generate_from_tests
|
||||
|
||||
@@ -39,6 +40,8 @@ def _get_fork_tests_providers():
|
||||
yield create_provider(test_bellatrix_fork_random, preset, ALTAIR, BELLATRIX)
|
||||
yield create_provider(test_capella_fork_basic, preset, BELLATRIX, CAPELLA)
|
||||
yield create_provider(test_capella_fork_random, preset, BELLATRIX, CAPELLA)
|
||||
yield create_provider(test_eip4844_fork_basic, preset, CAPELLA, EIP4844)
|
||||
yield create_provider(test_eip4844_fork_random, preset, CAPELLA, EIP4844)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -16,11 +16,13 @@ if __name__ == "__main__":
|
||||
]}
|
||||
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
|
||||
capella_mods = bellatrix_mods # No additional Capella specific genesis tests
|
||||
eip4844_mods = capella_mods # No additional EIP4844 specific genesis tests
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
EIP4844: eip4844_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="genesis", all_mods=all_mods)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, EIP4844
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
|
||||
|
||||
@@ -10,11 +10,13 @@ if __name__ == "__main__":
|
||||
]}
|
||||
bellatrix_mods = altair_mods
|
||||
capella_mods = bellatrix_mods
|
||||
eip4844_mods = capella_mods
|
||||
|
||||
all_mods = {
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
EIP4844: eip4844_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="light_client", all_mods=all_mods)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -31,6 +31,12 @@ if __name__ == "__main__":
|
||||
]}
|
||||
capella_mods = combine_mods(_new_capella_mods, bellatrix_mods)
|
||||
|
||||
_new_eip4844_mods = {key: 'eth2spec.test.eip4844.block_processing.test_process_' + key for key in [
|
||||
'bls_to_execution_change',
|
||||
'withdrawals',
|
||||
]}
|
||||
eip4844_mods = combine_mods(_new_eip4844_mods, capella_mods)
|
||||
|
||||
# TODO Custody Game testgen is disabled for now
|
||||
# _new_custody_game_mods = {key: 'eth2spec.test.custody_game.block_processing.test_process_' + key for key in [
|
||||
# 'attestation',
|
||||
@@ -46,6 +52,7 @@ if __name__ == "__main__":
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
EIP4844: eip4844_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="operations", all_mods=all_mods)
|
||||
|
||||
@@ -6,7 +6,9 @@ all:
|
||||
rm -f ../../core/pyspec/eth2spec/test/altair/random/test_random.py
|
||||
rm -f ../../core/pyspec/eth2spec/test/bellatrix/random/test_random.py
|
||||
rm -f ../../core/pyspec/eth2spec/test/capella/random/test_random.py
|
||||
rm -f ../../core/pyspec/eth2spec/test/eip4844/random/test_random.py
|
||||
python3 generate.py phase0 > ../../core/pyspec/eth2spec/test/phase0/random/test_random.py
|
||||
python3 generate.py altair > ../../core/pyspec/eth2spec/test/altair/random/test_random.py
|
||||
python3 generate.py bellatrix > ../../core/pyspec/eth2spec/test/bellatrix/random/test_random.py
|
||||
python3 generate.py capella > ../../core/pyspec/eth2spec/test/capella/random/test_random.py
|
||||
python3 generate.py eip4844 > ../../core/pyspec/eth2spec/test/eip4844/random/test_random.py
|
||||
|
||||
@@ -21,10 +21,12 @@ from eth2spec.test.utils.randomized_block_tests import (
|
||||
randomize_state_altair,
|
||||
randomize_state_bellatrix,
|
||||
randomize_state_capella,
|
||||
randomize_state_eip4844,
|
||||
random_block,
|
||||
random_block_altair_with_cycling_sync_committee_participation,
|
||||
random_block_bellatrix,
|
||||
random_block_capella,
|
||||
random_block_eip4844,
|
||||
last_slot_in_epoch,
|
||||
random_slot_in_epoch,
|
||||
penultimate_slot_in_epoch,
|
||||
@@ -34,7 +36,7 @@ from eth2spec.test.utils.randomized_block_tests import (
|
||||
transition_to_leaking,
|
||||
transition_without_leak,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844
|
||||
|
||||
|
||||
# Ensure this many blocks are present in *each* randomized scenario
|
||||
@@ -272,5 +274,12 @@ if __name__ == "__main__":
|
||||
state_randomizer=randomize_state_capella,
|
||||
block_randomizer=random_block_capella,
|
||||
)
|
||||
if EIP4844 in sys.argv:
|
||||
did_generate = True
|
||||
run_generate_tests_to_std_out(
|
||||
EIP4844,
|
||||
state_randomizer=randomize_state_eip4844,
|
||||
block_randomizer=random_block_eip4844,
|
||||
)
|
||||
if not did_generate:
|
||||
warnings.warn("no phase given for test generation")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
|
||||
|
||||
@@ -15,12 +15,16 @@ if __name__ == "__main__":
|
||||
capella_mods = {key: 'eth2spec.test.capella.random.test_' + key for key in [
|
||||
'random',
|
||||
]}
|
||||
eip4844_mods = {key: 'eth2spec.test.eip4844.random.test_' + key for key in [
|
||||
'random',
|
||||
]}
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
EIP4844: eip4844_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="random", all_mods=all_mods)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -16,12 +16,14 @@ if __name__ == "__main__":
|
||||
# Transaction fees are part of the execution-layer.
|
||||
bellatrix_mods = altair_mods
|
||||
capella_mods = bellatrix_mods
|
||||
eip4844_mods = capella_mods
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
EIP4844: eip4844_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="rewards", all_mods=all_mods)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
|
||||
|
||||
|
||||
@@ -23,11 +23,17 @@ if __name__ == "__main__":
|
||||
]}
|
||||
capella_mods = combine_mods(_new_capella_mods, bellatrix_mods)
|
||||
|
||||
_new_eip4844_mods = {key: 'eth2spec.test.eip4844.sanity.test_' + key for key in [
|
||||
'blocks',
|
||||
]}
|
||||
eip4844_mods = combine_mods(_new_eip4844_mods, capella_mods)
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
EIP4844: eip4844_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="sanity", all_mods=all_mods)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
from eth2spec.test.helpers.constants import BELLATRIX, CAPELLA
|
||||
from eth2spec.test.helpers.constants import BELLATRIX, CAPELLA, EIP4844
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -7,10 +7,12 @@ if __name__ == "__main__":
|
||||
'optimistic',
|
||||
]}
|
||||
capella_mods = bellatrix_mods
|
||||
eip4844_mods = capella_mods
|
||||
|
||||
all_mods = {
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
EIP4844: eip4844_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="sync", all_mods=all_mods)
|
||||
|
||||
Reference in New Issue
Block a user