mirror of
https://github.com/ethereum/consensus-specs.git
synced 2026-02-04 12:45:04 -05:00
4
Makefile
4
Makefile
@@ -64,6 +64,8 @@ partial_clean:
|
||||
rm -rf $(ETH2SPEC_MODULE_DIR)/phase0
|
||||
rm -rf $(ETH2SPEC_MODULE_DIR)/altair
|
||||
rm -rf $(ETH2SPEC_MODULE_DIR)/bellatrix
|
||||
rm -rf $(ETH2SPEC_MODULE_DIR)/capella
|
||||
rm -rf $(ETH2SPEC_MODULE_DIR)/eip4844
|
||||
rm -rf $(COV_HTML_OUT_DIR)
|
||||
rm -rf $(TEST_REPORT_DIR)
|
||||
rm -rf eth2spec.egg-info dist build
|
||||
@@ -143,7 +145,7 @@ lint: pyspec
|
||||
|
||||
lint_generators: pyspec
|
||||
. venv/bin/activate; cd $(TEST_GENERATORS_DIR); \
|
||||
flake8 --config $(LINTER_CONFIG_FILE)
|
||||
flake8 --config $(LINTER_CONFIG_FILE)
|
||||
|
||||
compile_deposit_contract:
|
||||
@cd $(SOLIDITY_DEPOSIT_CONTRACT_DIR)
|
||||
|
||||
@@ -21,4 +21,4 @@ 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: 16
|
||||
MAX_WITHDRAWALS_PER_PAYLOAD: 8
|
||||
|
||||
47
setup.py
47
setup.py
@@ -20,7 +20,7 @@ from collections import OrderedDict
|
||||
def installPackage(package: str):
|
||||
subprocess.check_call([sys.executable, '-m', 'pip', 'install', package])
|
||||
|
||||
RUAMEL_YAML_VERSION = "ruamel.yaml==0.16.5"
|
||||
RUAMEL_YAML_VERSION = "ruamel.yaml==0.17.21"
|
||||
try:
|
||||
import ruamel.yaml
|
||||
except ImportError:
|
||||
@@ -78,6 +78,7 @@ class VariableDefinition(NamedTuple):
|
||||
type_name: Optional[str]
|
||||
value: str
|
||||
comment: Optional[str] # e.g. "noqa: E501"
|
||||
type_hint: Optional[str] # e.g., "Final"
|
||||
|
||||
|
||||
class SpecObject(NamedTuple):
|
||||
@@ -152,18 +153,18 @@ def _get_eth2_spec_comment(child: LinkRefDef) -> Optional[str]:
|
||||
return title[len(ETH2_SPEC_COMMENT_PREFIX):].strip()
|
||||
|
||||
|
||||
def _parse_value(name: str, typed_value: str) -> VariableDefinition:
|
||||
def _parse_value(name: str, typed_value: str, type_hint: Optional[str]=None) -> VariableDefinition:
|
||||
comment = None
|
||||
if name == "BLS12_381_Q":
|
||||
comment = "noqa: E501"
|
||||
|
||||
typed_value = typed_value.strip()
|
||||
if '(' not in typed_value:
|
||||
return VariableDefinition(type_name=None, value=typed_value, comment=comment)
|
||||
return VariableDefinition(type_name=None, value=typed_value, comment=comment, type_hint=type_hint)
|
||||
i = typed_value.index('(')
|
||||
type_name = typed_value[:i]
|
||||
|
||||
return VariableDefinition(type_name=type_name, value=typed_value[i+1:-1], comment=comment)
|
||||
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:
|
||||
@@ -241,10 +242,13 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str]) ->
|
||||
|
||||
value_def = _parse_value(name, value)
|
||||
if name in preset:
|
||||
preset_vars[name] = VariableDefinition(value_def.type_name, preset[name], value_def.comment)
|
||||
preset_vars[name] = VariableDefinition(value_def.type_name, preset[name], value_def.comment, None)
|
||||
elif name in config:
|
||||
config_vars[name] = VariableDefinition(value_def.type_name, config[name], value_def.comment)
|
||||
config_vars[name] = VariableDefinition(value_def.type_name, config[name], value_def.comment, None)
|
||||
else:
|
||||
if name == 'ENDIANNESS':
|
||||
# Deal with mypy Literal typing check
|
||||
value_def = _parse_value(name, value, type_hint='Final')
|
||||
constant_vars[name] = value_def
|
||||
|
||||
elif isinstance(child, LinkRefDef):
|
||||
@@ -337,7 +341,7 @@ from dataclasses import (
|
||||
field,
|
||||
)
|
||||
from typing import (
|
||||
Any, Callable, Dict, Set, Sequence, Tuple, Optional, TypeVar, NamedTuple
|
||||
Any, Callable, Dict, Set, Sequence, Tuple, Optional, TypeVar, NamedTuple, Final
|
||||
)
|
||||
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy, uint_to_bytes
|
||||
@@ -589,9 +593,16 @@ from eth2spec.bellatrix import {preset_name} as bellatrix
|
||||
from eth2spec.utils.ssz.ssz_impl import serialize as ssz_serialize
|
||||
'''
|
||||
|
||||
|
||||
@classmethod
|
||||
def preparations(cls):
|
||||
return super().preparations() + '\n' + '''
|
||||
T = TypeVar('T') # For generic function
|
||||
'''
|
||||
|
||||
@classmethod
|
||||
def sundry_functions(cls) -> str:
|
||||
return super().sundry_functions() + '''
|
||||
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
|
||||
@@ -696,7 +707,10 @@ def objects_to_spec(preset_name: str,
|
||||
|
||||
def format_constant(name: str, vardef: VariableDefinition) -> str:
|
||||
if vardef.type_name is None:
|
||||
out = f'{name} = {vardef.value}'
|
||||
if vardef.type_hint is None:
|
||||
out = f'{name} = {vardef.value}'
|
||||
else:
|
||||
out = f'{name}: {vardef.type_hint} = {vardef.value}'
|
||||
else:
|
||||
out = f'{name} = {vardef.type_name}({vardef.value})'
|
||||
if vardef.comment is not None:
|
||||
@@ -1108,19 +1122,18 @@ setup(
|
||||
python_requires=">=3.8, <4",
|
||||
extras_require={
|
||||
"test": ["pytest>=4.4", "pytest-cov", "pytest-xdist"],
|
||||
"lint": ["flake8==3.7.7", "mypy==0.812", "pylint==2.12.2"],
|
||||
"generator": ["python-snappy==0.5.4", "filelock"],
|
||||
"lint": ["flake8==5.0.4", "mypy==0.981", "pylint==2.15.3"],
|
||||
"generator": ["python-snappy==0.6.1", "filelock"],
|
||||
},
|
||||
install_requires=[
|
||||
"eth-utils>=1.3.0,<2",
|
||||
"eth-typing>=2.1.0,<3.0.0",
|
||||
"pycryptodome==3.9.4",
|
||||
"py_ecc==5.2.0",
|
||||
"eth-utils>=2.0.0,<3",
|
||||
"eth-typing>=3.2.0,<4.0.0",
|
||||
"pycryptodome==3.15.0",
|
||||
"py_ecc==6.0.0",
|
||||
"milagro_bls_binding==1.9.0",
|
||||
"dataclasses==0.6",
|
||||
"remerkleable==0.1.24",
|
||||
RUAMEL_YAML_VERSION,
|
||||
"lru-dict==1.1.6",
|
||||
"lru-dict==1.1.8",
|
||||
MARKO_VERSION,
|
||||
]
|
||||
)
|
||||
|
||||
@@ -146,7 +146,7 @@ This function is a predicate indicating the presence or absence of the validator
|
||||
*Note*: Being assigned to a sync committee for a given `slot` means that the validator produces and broadcasts signatures for `slot - 1` for inclusion in `slot`.
|
||||
This means that when assigned to an `epoch` sync committee signatures must be produced and broadcast for slots on range `[compute_start_slot_at_epoch(epoch) - 1, compute_start_slot_at_epoch(epoch) + SLOTS_PER_EPOCH - 1)`
|
||||
rather than for the range `[compute_start_slot_at_epoch(epoch), compute_start_slot_at_epoch(epoch) + SLOTS_PER_EPOCH)`.
|
||||
To reduce complexity during the Altair fork, sync committees are not expected to produce signatures for `compute_epoch_at_slot(ALTAIR_FORK_EPOCH) - 1`.
|
||||
To reduce complexity during the Altair fork, sync committees are not expected to produce signatures for `compute_start_slot_at_epoch(ALTAIR_FORK_EPOCH) - 1`.
|
||||
|
||||
```python
|
||||
def compute_sync_committee_period(epoch: Epoch) -> uint64:
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# Bellatrix -- The Beacon Chain
|
||||
|
||||
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||
|
||||
## Table of contents
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# Bellatrix -- Fork Choice
|
||||
|
||||
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||
|
||||
## Table of contents
|
||||
<!-- TOC -->
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# Bellatrix -- Fork Logic
|
||||
|
||||
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||
|
||||
## Table of contents
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# Bellatrix -- Honest Validator
|
||||
|
||||
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||
|
||||
## Table of contents
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
- [Extended Containers](#extended-containers)
|
||||
- [`ExecutionPayload`](#executionpayload)
|
||||
- [`ExecutionPayloadHeader`](#executionpayloadheader)
|
||||
- [`Validator`](#validator)
|
||||
- [`BeaconBlockBody`](#beaconblockbody)
|
||||
- [`BeaconState`](#beaconstate)
|
||||
- [Helpers](#helpers)
|
||||
@@ -111,6 +110,7 @@ We define the following Python custom types for type hinting and readability:
|
||||
```python
|
||||
class Withdrawal(Container):
|
||||
index: WithdrawalIndex
|
||||
validator_index: ValidatorIndex
|
||||
address: ExecutionAddress
|
||||
amount: Gwei
|
||||
```
|
||||
@@ -180,22 +180,6 @@ class ExecutionPayloadHeader(Container):
|
||||
withdrawals_root: Root # [New in Capella]
|
||||
```
|
||||
|
||||
#### `Validator`
|
||||
|
||||
```python
|
||||
class Validator(Container):
|
||||
pubkey: BLSPubkey
|
||||
withdrawal_credentials: Bytes32 # Commitment to pubkey for withdrawals
|
||||
effective_balance: Gwei # Balance at stake
|
||||
slashed: boolean
|
||||
# Status epochs
|
||||
activation_eligibility_epoch: Epoch # When criteria for activation were met
|
||||
activation_epoch: Epoch
|
||||
exit_epoch: Epoch
|
||||
withdrawable_epoch: Epoch # When validator can withdraw funds
|
||||
fully_withdrawn_epoch: Epoch # [New in Capella]
|
||||
```
|
||||
|
||||
#### `BeaconBlockBody`
|
||||
|
||||
```python
|
||||
@@ -275,6 +259,7 @@ def withdraw_balance(state: BeaconState, validator_index: ValidatorIndex, 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,
|
||||
)
|
||||
@@ -297,13 +282,14 @@ def has_eth1_withdrawal_credential(validator: Validator) -> bool:
|
||||
#### `is_fully_withdrawable_validator`
|
||||
|
||||
```python
|
||||
def is_fully_withdrawable_validator(validator: Validator, epoch: Epoch) -> bool:
|
||||
def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch: Epoch) -> bool:
|
||||
"""
|
||||
Check if ``validator`` is fully withdrawable.
|
||||
"""
|
||||
return (
|
||||
has_eth1_withdrawal_credential(validator)
|
||||
and validator.withdrawable_epoch <= epoch < validator.fully_withdrawn_epoch
|
||||
and validator.withdrawable_epoch <= epoch
|
||||
and balance > 0
|
||||
)
|
||||
```
|
||||
|
||||
@@ -349,11 +335,11 @@ def process_epoch(state: BeaconState) -> None:
|
||||
```python
|
||||
def process_full_withdrawals(state: BeaconState) -> None:
|
||||
current_epoch = get_current_epoch(state)
|
||||
for index, validator in enumerate(state.validators):
|
||||
if is_fully_withdrawable_validator(validator, current_epoch):
|
||||
# TODO, consider the zero-balance case
|
||||
withdraw_balance(state, ValidatorIndex(index), state.balances[index])
|
||||
validator.fully_withdrawn_epoch = current_epoch
|
||||
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
|
||||
|
||||
@@ -70,6 +70,23 @@ In particular, the outer `state_transition` function defined in the Phase 0 docu
|
||||
```python
|
||||
def upgrade_to_capella(pre: bellatrix.BeaconState) -> BeaconState:
|
||||
epoch = bellatrix.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,
|
||||
block_hash=pre.latest_execution_payload_header.block_hash,
|
||||
transactions_root=pre.latest_execution_payload_header.transactions_root,
|
||||
withdrawals_root=Root(), # [New in Capella]
|
||||
)
|
||||
post = BeaconState(
|
||||
# Versioning
|
||||
genesis_time=pre.genesis_time,
|
||||
@@ -90,7 +107,7 @@ def upgrade_to_capella(pre: bellatrix.BeaconState) -> BeaconState:
|
||||
eth1_data_votes=pre.eth1_data_votes,
|
||||
eth1_deposit_index=pre.eth1_deposit_index,
|
||||
# Registry
|
||||
validators=[],
|
||||
validators=pre.validators,
|
||||
balances=pre.balances,
|
||||
# Randomness
|
||||
randao_mixes=pre.randao_mixes,
|
||||
@@ -110,26 +127,12 @@ def upgrade_to_capella(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,
|
||||
# Withdrawals
|
||||
withdrawal_queue=[],
|
||||
next_withdrawal_index=WithdrawalIndex(0),
|
||||
next_partial_withdrawal_validator_index=ValidatorIndex(0),
|
||||
)
|
||||
|
||||
for pre_validator in pre.validators:
|
||||
post_validator = Validator(
|
||||
pubkey=pre_validator.pubkey,
|
||||
withdrawal_credentials=pre_validator.withdrawal_credentials,
|
||||
effective_balance=pre_validator.effective_balance,
|
||||
slashed=pre_validator.slashed,
|
||||
activation_eligibility_epoch=pre_validator.activation_eligibility_epoch,
|
||||
activation_epoch=pre_validator.activation_epoch,
|
||||
exit_epoch=pre_validator.exit_epoch,
|
||||
withdrawable_epoch=pre_validator.withdrawable_epoch,
|
||||
fully_withdrawn_epoch=FAR_FUTURE_EPOCH,
|
||||
)
|
||||
post.validators.append(post_validator)
|
||||
|
||||
return post
|
||||
```
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
- [Containers](#containers)
|
||||
- [Extended containers](#extended-containers)
|
||||
- [`BeaconBlockBody`](#beaconblockbody)
|
||||
- [`ExecutionPayload`](#executionpayload)
|
||||
- [`ExecutionPayloadHeader`](#executionpayloadheader)
|
||||
- [Helper functions](#helper-functions)
|
||||
- [Misc](#misc)
|
||||
- [`kzg_commitment_to_versioned_hash`](#kzg_commitment_to_versioned_hash)
|
||||
@@ -26,6 +28,8 @@
|
||||
- [`verify_kzg_commitments_against_transactions`](#verify_kzg_commitments_against_transactions)
|
||||
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
|
||||
- [Block processing](#block-processing)
|
||||
- [Execution payload](#execution-payload)
|
||||
- [`process_execution_payload`](#process_execution_payload)
|
||||
- [Blob KZG commitments](#blob-kzg-commitments)
|
||||
- [Testing](#testing)
|
||||
|
||||
@@ -96,6 +100,52 @@ class BeaconBlockBody(Container):
|
||||
blob_kzg_commitments: List[KZGCommitment, MAX_BLOBS_PER_BLOCK] # [New in EIP-4844]
|
||||
```
|
||||
|
||||
#### `ExecutionPayload`
|
||||
|
||||
```python
|
||||
class ExecutionPayload(Container):
|
||||
# Execution block header fields
|
||||
parent_hash: Hash32
|
||||
fee_recipient: ExecutionAddress # 'beneficiary' in the yellow paper
|
||||
state_root: Bytes32
|
||||
receipts_root: Bytes32
|
||||
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
|
||||
prev_randao: Bytes32 # 'difficulty' in the yellow paper
|
||||
block_number: uint64 # 'number' in the yellow paper
|
||||
gas_limit: uint64
|
||||
gas_used: uint64
|
||||
timestamp: uint64
|
||||
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
|
||||
base_fee_per_gas: uint256
|
||||
excess_blobs: uint64 # [New in EIP-4844]
|
||||
# Extra payload fields
|
||||
block_hash: Hash32 # Hash of execution block
|
||||
transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]
|
||||
```
|
||||
|
||||
#### `ExecutionPayloadHeader`
|
||||
|
||||
```python
|
||||
class ExecutionPayloadHeader(Container):
|
||||
# Execution block header fields
|
||||
parent_hash: Hash32
|
||||
fee_recipient: ExecutionAddress
|
||||
state_root: Bytes32
|
||||
receipts_root: Bytes32
|
||||
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
|
||||
prev_randao: Bytes32
|
||||
block_number: uint64
|
||||
gas_limit: uint64
|
||||
gas_used: uint64
|
||||
timestamp: uint64
|
||||
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
|
||||
base_fee_per_gas: uint256
|
||||
excess_blobs: uint64 # [New in EIP-4844]
|
||||
# Extra payload fields
|
||||
block_hash: Hash32 # Hash of execution block
|
||||
transactions_root: Root
|
||||
```
|
||||
|
||||
## Helper functions
|
||||
|
||||
### Misc
|
||||
@@ -156,6 +206,41 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
||||
process_blob_kzg_commitments(state, block.body) # [New in EIP-4844]
|
||||
```
|
||||
|
||||
#### Execution payload
|
||||
|
||||
##### `process_execution_payload`
|
||||
|
||||
```python
|
||||
def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None:
|
||||
# Verify consistency of the parent hash with respect to the previous execution payload header
|
||||
if is_merge_transition_complete(state):
|
||||
assert payload.parent_hash == state.latest_execution_payload_header.block_hash
|
||||
# Verify prev_randao
|
||||
assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state))
|
||||
# Verify timestamp
|
||||
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,
|
||||
fee_recipient=payload.fee_recipient,
|
||||
state_root=payload.state_root,
|
||||
receipts_root=payload.receipts_root,
|
||||
logs_bloom=payload.logs_bloom,
|
||||
prev_randao=payload.prev_randao,
|
||||
block_number=payload.block_number,
|
||||
gas_limit=payload.gas_limit,
|
||||
gas_used=payload.gas_used,
|
||||
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]
|
||||
block_hash=payload.block_hash,
|
||||
transactions_root=hash_tree_root(payload.transactions),
|
||||
)
|
||||
```
|
||||
|
||||
#### Blob KZG commitments
|
||||
|
||||
```python
|
||||
|
||||
@@ -14,11 +14,11 @@ The specification of these changes continues in the same format as the network s
|
||||
- [Containers](#containers)
|
||||
- [`BlobsSidecar`](#blobssidecar)
|
||||
- [`SignedBlobsSidecar`](#signedblobssidecar)
|
||||
- [`SignedBeaconBlockAndBlobsSidecar`](#signedbeaconblockandblobssidecar)
|
||||
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
|
||||
- [Topics and messages](#topics-and-messages)
|
||||
- [Global topics](#global-topics)
|
||||
- [`beacon_block`](#beacon_block)
|
||||
- [`blobs_sidecar`](#blobs_sidecar)
|
||||
- [`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)
|
||||
@@ -33,10 +33,10 @@ The specification of these changes continues in the same format as the network s
|
||||
|
||||
## Configuration
|
||||
|
||||
| Name | Value | Description |
|
||||
|------------------------------------------|-------------------------------|---------------------------------------------------------------------|
|
||||
| `MAX_REQUEST_BLOBS_SIDECARS` | `2**7` (= 128) | Maximum number of blobs sidecars in a single request |
|
||||
| `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` | `2**13` (= 8192, ~1.2 months) | The minimum epoch range over which a node must serve blobs sidecars |
|
||||
| Name | Value | Description |
|
||||
|------------------------------------------|-----------------------------------|---------------------------------------------------------------------|
|
||||
| `MAX_REQUEST_BLOBS_SIDECARS` | `2**7` (= 128) | Maximum number of blobs sidecars in a single request |
|
||||
| `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` | `2**12` (= 4096 epochs, ~18 days) | The minimum epoch range over which a node must serve blobs sidecars |
|
||||
|
||||
## Containers
|
||||
|
||||
@@ -58,6 +58,14 @@ class SignedBlobsSidecar(Container):
|
||||
signature: BLSSignature
|
||||
```
|
||||
|
||||
### `SignedBeaconBlockAndBlobsSidecar`
|
||||
|
||||
```python
|
||||
class SignedBeaconBlockAndBlobsSidecar(Container):
|
||||
beacon_block: SignedBeaconBlock
|
||||
blobs_sidecar: BlobsSidecar
|
||||
```
|
||||
|
||||
## The gossip domain: gossipsub
|
||||
|
||||
Some gossip meshes are upgraded in the fork of EIP4844 to support upgraded types.
|
||||
@@ -75,47 +83,30 @@ The new topics along with the type of the `data` field of a gossipsub message ar
|
||||
|
||||
| Name | Message Type |
|
||||
| - | - |
|
||||
| `beacon_block` | `SignedBeaconBlock` (modified) |
|
||||
| `blobs_sidecar` | `SignedBlobsSidecar` (new) |
|
||||
| `beacon_block_and_blobs_sidecar` | `SignedBeaconBlockAndBlobsSidecar` (new) |
|
||||
|
||||
Note that the `ForkDigestValue` path segment of the topic separates the old and the new `beacon_block` topics.
|
||||
|
||||
#### Global topics
|
||||
|
||||
EIP4844 changes the type of the global beacon block topic and introduces a new global topic for blobs-sidecars.
|
||||
EIP4844 introduces a new global topic for beacon block and blobs-sidecars.
|
||||
|
||||
##### `beacon_block`
|
||||
##### `beacon_block_and_blobs_sidecar`
|
||||
|
||||
The *type* of the payload of this topic changes to the (modified) `SignedBeaconBlock` found in EIP4844.
|
||||
This topic is used to propagate new signed and coupled beacon blocks and blobs sidecars to all nodes on the networks.
|
||||
|
||||
In addition to the gossip validations for this topic from prior specifications,
|
||||
the following validations MUST pass before forwarding the `signed_beacon_block` on the network.
|
||||
Alias `block = signed_beacon_block.message`, `execution_payload = block.body.execution_payload`.
|
||||
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)`
|
||||
- _[REJECT]_ The KZG commitments correspond to the versioned hashes in the transactions list.
|
||||
-- i.e. `verify_kzg_commitments_against_transactions(block.body.execution_payload.transactions, block.body.blob_kzg_commitments)`
|
||||
|
||||
##### `blobs_sidecar`
|
||||
|
||||
This topic is used to propagate data blobs included in any given beacon block.
|
||||
|
||||
The following validations MUST pass before forwarding the `signed_blobs_sidecar` on the network;
|
||||
Alias `sidecar = signed_blobs_sidecar.message`.
|
||||
- _[IGNORE]_ the `sidecar.beacon_block_slot` is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `sidecar.beacon_block_slot == current_slot`.
|
||||
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)`
|
||||
- _[REJECT]_ the beacon proposer signature, `signed_blobs_sidecar.signature`, is valid -- i.e.
|
||||
- Let `domain = get_domain(state, DOMAIN_BLOBS_SIDECAR, sidecar.beacon_block_slot // SLOTS_PER_EPOCH)`
|
||||
- Let `signing_root = compute_signing_root(sidecar, domain)`
|
||||
- Verify `bls.Verify(proposer_pubkey, signing_root, signed_blobs_sidecar.signature) is True`,
|
||||
where `proposer_pubkey` is the pubkey of the beacon block proposer of `sidecar.beacon_block_slot`
|
||||
- _[IGNORE]_ The sidecar is the first sidecar with valid signature received for the `(proposer_index, sidecar.beacon_block_slot)` combination,
|
||||
where `proposer_index` is the validator index of the beacon block proposer of `sidecar.beacon_block_slot`
|
||||
|
||||
Note that a sidecar may be propagated before or after the corresponding beacon block.
|
||||
|
||||
Once both sidecar and beacon block are received, `validate_blobs_sidecar` can unlock the data-availability fork-choice dependency.
|
||||
Once the sidecar and beacon block are received together, `validate_blobs_sidecar` can unlock the data-availability fork-choice dependency.
|
||||
|
||||
### Transitioning the gossip
|
||||
|
||||
@@ -207,10 +198,7 @@ or disconnected at any time.
|
||||
|
||||
*Note*: The above requirement implies that nodes that start from a recent weak subjectivity checkpoint
|
||||
MUST backfill the local blobs database to at least epoch `current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS`
|
||||
to be fully compliant with `BlobsSidecarsByRange` requests. To safely perform such a
|
||||
backfill of blocks to the recent state, the node MUST validate both (1) the
|
||||
proposer signatures and (2) that the blocks form a valid chain up to the most
|
||||
recent block referenced in the weak subjectivity state.
|
||||
to be fully compliant with `BlobsSidecarsByRange` requests.
|
||||
|
||||
*Note*: Although clients that bootstrap from a weak subjectivity checkpoint can begin
|
||||
participating in the networking immediately, other peers MAY
|
||||
|
||||
@@ -12,7 +12,12 @@
|
||||
- [Preset](#preset)
|
||||
- [Trusted setup](#trusted-setup)
|
||||
- [Helper functions](#helper-functions)
|
||||
- [Bit-reversal permutation](#bit-reversal-permutation)
|
||||
- [`is_power_of_two`](#is_power_of_two)
|
||||
- [`reverse_bits`](#reverse_bits)
|
||||
- [`bit_reversal_permutation`](#bit_reversal_permutation)
|
||||
- [BLS12-381 helpers](#bls12-381-helpers)
|
||||
- [`bytes_to_bls_field`](#bytes_to_bls_field)
|
||||
- [`bls_modular_inverse`](#bls_modular_inverse)
|
||||
- [`div`](#div)
|
||||
- [`g1_lincomb`](#g1_lincomb)
|
||||
@@ -64,8 +69,59 @@ but reusing the `mainnet` settings in public networks is a critical security req
|
||||
|
||||
## Helper functions
|
||||
|
||||
### Bit-reversal permutation
|
||||
|
||||
All polynomials (which are always given in Lagrange form) should be interpreted as being in
|
||||
bit-reversal permutation. In practice, clients can implement this by storing the lists
|
||||
`KZG_SETUP_LAGRANGE` and `ROOTS_OF_UNITY` in bit-reversal permutation, so these functions only
|
||||
have to be called once at startup.
|
||||
|
||||
#### `is_power_of_two`
|
||||
|
||||
```python
|
||||
def is_power_of_two(value: int) -> bool:
|
||||
"""
|
||||
Check if ``value`` is a power of two integer.
|
||||
"""
|
||||
return (value > 0) and (value & (value - 1) == 0)
|
||||
```
|
||||
|
||||
#### `reverse_bits`
|
||||
|
||||
```python
|
||||
def reverse_bits(n: int, order: int) -> int:
|
||||
"""
|
||||
Reverse the bit order of an integer n
|
||||
"""
|
||||
assert is_power_of_two(order)
|
||||
# Convert n to binary with the same number of bits as "order" - 1, then reverse its bit order
|
||||
return int(('{:0' + str(order.bit_length() - 1) + 'b}').format(n)[::-1], 2)
|
||||
```
|
||||
|
||||
#### `bit_reversal_permutation`
|
||||
|
||||
```python
|
||||
def bit_reversal_permutation(sequence: Sequence[T]) -> Sequence[T]:
|
||||
"""
|
||||
Return a copy with bit-reversed permutation. The permutation is an involution (inverts itself).
|
||||
|
||||
The input and output are a sequence of generic type ``T`` objects.
|
||||
"""
|
||||
return [sequence[reverse_bits(i, len(sequence))] for i in range(len(sequence))]
|
||||
```
|
||||
|
||||
### BLS12-381 helpers
|
||||
|
||||
#### `bytes_to_bls_field`
|
||||
|
||||
```python
|
||||
def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement:
|
||||
"""
|
||||
Convert bytes to a BLS field scalar. The output is not uniform over the BLS field.
|
||||
"""
|
||||
return int.from_bytes(b, "little") % BLS_MODULUS
|
||||
```
|
||||
|
||||
#### `bls_modular_inverse`
|
||||
|
||||
```python
|
||||
@@ -123,7 +179,7 @@ KZG core functions. These are also defined in EIP-4844 execution specs.
|
||||
|
||||
```python
|
||||
def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment:
|
||||
return g1_lincomb(KZG_SETUP_LAGRANGE, blob)
|
||||
return g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), blob)
|
||||
```
|
||||
|
||||
#### `verify_kzg_proof`
|
||||
@@ -149,7 +205,9 @@ def verify_kzg_proof(polynomial_kzg: KZGCommitment,
|
||||
|
||||
```python
|
||||
def compute_kzg_proof(polynomial: Sequence[BLSFieldElement], z: BLSFieldElement) -> KZGProof:
|
||||
"""Compute KZG proof at point `z` with `polynomial` being in evaluation form"""
|
||||
"""
|
||||
Compute KZG proof at point `z` with `polynomial` being in evaluation form
|
||||
"""
|
||||
|
||||
# To avoid SSZ overflow/underflow, convert element into int
|
||||
polynomial = [int(i) for i in polynomial]
|
||||
@@ -161,11 +219,11 @@ def compute_kzg_proof(polynomial: Sequence[BLSFieldElement], z: BLSFieldElement)
|
||||
|
||||
# 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 ROOTS_OF_UNITY]
|
||||
denominator_poly = [(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)]
|
||||
return KZGProof(g1_lincomb(KZG_SETUP_LAGRANGE, quotient_polynomial))
|
||||
return KZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), quotient_polynomial))
|
||||
```
|
||||
|
||||
### Polynomials
|
||||
@@ -187,9 +245,11 @@ def evaluate_polynomial_in_evaluation_form(polynomial: Sequence[BLSFieldElement]
|
||||
# Make sure we won't divide by zero during division
|
||||
assert z not in ROOTS_OF_UNITY
|
||||
|
||||
roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY)
|
||||
|
||||
result = 0
|
||||
for i in range(width):
|
||||
result += div(int(polynomial[i]) * int(ROOTS_OF_UNITY[i]), (z - ROOTS_OF_UNITY[i]))
|
||||
result += div(int(polynomial[i]) * int(roots_of_unity_brp[i]), (z - roots_of_unity_brp[i]))
|
||||
result = result * (pow(z, width, BLS_MODULUS) - 1) * inverse_width % BLS_MODULUS
|
||||
return result
|
||||
```
|
||||
|
||||
@@ -23,10 +23,12 @@
|
||||
- [`compute_proof_from_blobs`](#compute_proof_from_blobs)
|
||||
- [`get_blobs_and_kzg_commitments`](#get_blobs_and_kzg_commitments)
|
||||
- [Beacon chain responsibilities](#beacon-chain-responsibilities)
|
||||
- [Block proposal](#block-proposal)
|
||||
- [Block and sidecar proposal](#block-and-sidecar-proposal)
|
||||
- [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody)
|
||||
- [Blob KZG commitments](#blob-kzg-commitments)
|
||||
- [Beacon Block publishing time](#beacon-block-publishing-time)
|
||||
- [Constructing the `SignedBeaconBlockAndBlobsSidecar`](#constructing-the-signedbeaconblockandblobssidecar)
|
||||
- [Block](#block)
|
||||
- [Sidecar](#sidecar)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- /TOC -->
|
||||
@@ -96,7 +98,7 @@ def hash_to_bls_field(x: Container) -> BLSFieldElement:
|
||||
Compute 32-byte hash of serialized container and convert it to BLS field.
|
||||
The output is not uniform over the BLS field.
|
||||
"""
|
||||
return int.from_bytes(hash(ssz_serialize(x)), "little") % BLS_MODULUS
|
||||
return bytes_to_bls_field(hash(ssz_serialize(x)))
|
||||
```
|
||||
|
||||
### `compute_powers`
|
||||
@@ -117,7 +119,7 @@ def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]:
|
||||
|
||||
```python
|
||||
def compute_aggregated_poly_and_commitment(
|
||||
blobs: Sequence[Sequence[BLSFieldElement]],
|
||||
blobs: Sequence[Blob],
|
||||
kzg_commitments: Sequence[KZGCommitment]) -> Tuple[Polynomial, KZGCommitment]:
|
||||
"""
|
||||
Return the aggregated polynomial and aggregated KZG commitment.
|
||||
@@ -167,7 +169,7 @@ def validate_blobs_sidecar(slot: Slot,
|
||||
### `compute_proof_from_blobs`
|
||||
|
||||
```python
|
||||
def compute_proof_from_blobs(blobs: Sequence[BLSFieldElement]) -> KZGProof:
|
||||
def compute_proof_from_blobs(blobs: Sequence[Blob]) -> KZGProof:
|
||||
commitments = [blob_to_kzg_commitment(blob) for blob in blobs]
|
||||
aggregated_poly, aggregated_poly_commitment = compute_aggregated_poly_and_commitment(blobs, commitments)
|
||||
x = hash_to_bls_field(PolynomialAndCommitment(
|
||||
@@ -192,9 +194,9 @@ def get_blobs_and_kzg_commitments(payload_id: PayloadId) -> Tuple[Sequence[BLSFi
|
||||
## Beacon chain responsibilities
|
||||
|
||||
All validator responsibilities remain unchanged other than those noted below.
|
||||
Namely, the blob handling and the addition of `BlobsSidecar`.
|
||||
Namely, the blob handling and the addition of `SignedBeaconBlockAndBlobsSidecar`.
|
||||
|
||||
### Block proposal
|
||||
### Block and sidecar proposal
|
||||
|
||||
#### Constructing the `BeaconBlockBody`
|
||||
|
||||
@@ -206,7 +208,7 @@ use the `payload_id` to retrieve `blobs` and `blob_kzg_commitments` via `get_blo
|
||||
|
||||
```python
|
||||
def validate_blobs_and_kzg_commitments(execution_payload: ExecutionPayload,
|
||||
blobs: Sequence[BLSFieldElement],
|
||||
blobs: Sequence[Blob],
|
||||
blob_kzg_commitments: Sequence[KZGCommitment]) -> None:
|
||||
# Optionally sanity-check that the KZG commitments match the versioned hashes in the transactions
|
||||
assert verify_kzg_commitments_against_transactions(execution_payload.transactions, blob_kzg_commitments)
|
||||
@@ -218,13 +220,16 @@ def validate_blobs_and_kzg_commitments(execution_payload: ExecutionPayload,
|
||||
|
||||
3. If valid, set `block.body.blob_kzg_commitments = blob_kzg_commitments`.
|
||||
|
||||
Note that the `blobs` should be held with the block in preparation of publishing.
|
||||
Without the `blobs`, the published block will effectively be ignored by honest validators.
|
||||
#### Constructing the `SignedBeaconBlockAndBlobsSidecar`
|
||||
To construct a `SignedBeaconBlockAndBlobsSidecar`, a `signed_beacon_block_and_blobs_sidecar` is defined with the necessary context for block and sidecar proposal.
|
||||
|
||||
### Beacon Block publishing time
|
||||
##### Block
|
||||
Set `signed_beacon_block_and_blobs_sidecar.beacon_block = block` where `block` is obtained above.
|
||||
|
||||
Before publishing a prepared beacon block proposal, the corresponding blobs are packaged into a sidecar object for distribution to the network:
|
||||
##### Sidecar
|
||||
Coupled with block, the corresponding blobs are packaged into a sidecar object for distribution to the network.
|
||||
|
||||
Set `signed_beacon_block_and_blobs_sidecar.blobs_sidecar = sidecar` where `sidecar` is obtained from:
|
||||
```python
|
||||
def get_blobs_sidecar(block: BeaconBlock, blobs: Sequence[Blob]) -> BlobsSidecar:
|
||||
return BlobsSidecar(
|
||||
@@ -235,20 +240,10 @@ def get_blobs_sidecar(block: BeaconBlock, blobs: Sequence[Blob]) -> BlobsSidecar
|
||||
)
|
||||
```
|
||||
|
||||
And then signed:
|
||||
This `signed_beacon_block_and_blobs_sidecar` is then published to the global `beacon_block_and_blobs_sidecar` topic.
|
||||
|
||||
```python
|
||||
def get_signed_blobs_sidecar(state: BeaconState, blobs_sidecar: BlobsSidecar, privkey: int) -> SignedBlobsSidecar:
|
||||
domain = get_domain(state, DOMAIN_BLOBS_SIDECAR, blobs_sidecar.beacon_block_slot // SLOTS_PER_EPOCH)
|
||||
signing_root = compute_signing_root(blobs_sidecar, domain)
|
||||
signature = bls.Sign(privkey, signing_root)
|
||||
return SignedBlobsSidecar(message=blobs_sidecar, signature=signature)
|
||||
```
|
||||
|
||||
This `signed_blobs_sidecar` is then published to the global `blobs_sidecar` topic as soon as the `beacon_block` is published.
|
||||
|
||||
After publishing the sidecar peers on the network may request the sidecar through sync-requests, or a local user may be interested.
|
||||
The validator MUST hold on to blobs for `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` epochs and serve when capable,
|
||||
After publishing the peers on the network may request the sidecar through sync-requests, or a local user may be interested.
|
||||
The validator MUST hold on to sidecars for `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` epochs and serve when capable,
|
||||
to ensure the data-availability of these blobs throughout the network.
|
||||
|
||||
After `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` nodes MAY prune the blobs and/or stop serving them.
|
||||
After `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` nodes MAY prune the sidecars and/or stop serving them.
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.2.0
|
||||
1.3.0-alpha.0
|
||||
|
||||
@@ -46,7 +46,7 @@ def test_from_syncing_to_invalid(spec, state):
|
||||
|
||||
# Block 0
|
||||
block_0 = build_empty_block_for_next_slot(spec, state)
|
||||
block_0.body.execution_payload.block_hash = spec.hash(bytes(f'block_0', 'UTF-8'))
|
||||
block_0.body.execution_payload.block_hash = spec.hash(bytes('block_0', 'UTF-8'))
|
||||
signed_block = state_transition_and_sign_block(spec, state, block_0)
|
||||
yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, status=PayloadStatusV1Status.VALID)
|
||||
assert spec.get_head(mega_store.fc_store) == mega_store.opt_store.head_block_root
|
||||
@@ -84,7 +84,7 @@ def test_from_syncing_to_invalid(spec, state):
|
||||
|
||||
# Now add block 4 to chain `b` with INVALID
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.execution_payload.block_hash = spec.hash(bytes(f'chain_b_3', 'UTF-8'))
|
||||
block.body.execution_payload.block_hash = spec.hash(bytes('chain_b_3', 'UTF-8'))
|
||||
block.body.execution_payload.parent_hash = signed_blocks_b[-1].message.body.execution_payload.block_hash
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
payload_status = PayloadStatusV1(
|
||||
|
||||
0
tests/core/pyspec/eth2spec/test/capella/__init__.py
Normal file
0
tests/core/pyspec/eth2spec/test/capella/__init__.py
Normal file
@@ -1,5 +1,5 @@
|
||||
from eth2spec.utils import bls
|
||||
from eth2spec.test.helpers.keys import pubkeys, privkeys, pubkey_to_privkey
|
||||
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
|
||||
|
||||
@@ -37,31 +37,6 @@ def run_bls_to_execution_change_processing(spec, state, signed_address_change, v
|
||||
yield 'post', state
|
||||
|
||||
|
||||
def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubkey=None):
|
||||
if validator_index is None:
|
||||
validator_index = 0
|
||||
|
||||
if withdrawal_pubkey is None:
|
||||
key_index = -1 - validator_index
|
||||
withdrawal_pubkey = pubkeys[key_index]
|
||||
withdrawal_privkey = privkeys[key_index]
|
||||
else:
|
||||
withdrawal_privkey = pubkey_to_privkey[withdrawal_pubkey]
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
signing_root = spec.compute_signing_root(address_change, domain)
|
||||
return spec.SignedBLSToExecutionChange(
|
||||
message=address_change,
|
||||
signature=bls.Sign(withdrawal_privkey, signing_root),
|
||||
)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success(spec, state):
|
||||
@@ -82,7 +57,9 @@ def test_success_not_activated(spec, state):
|
||||
signed_address_change = get_signed_address_change(spec, state)
|
||||
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change)
|
||||
|
||||
assert not spec.is_fully_withdrawable_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
validator = state.validators[validator_index]
|
||||
balance = state.balances[validator_index]
|
||||
assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@@ -98,7 +75,9 @@ def test_success_in_activation_queue(spec, state):
|
||||
signed_address_change = get_signed_address_change(spec, state)
|
||||
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change)
|
||||
|
||||
assert not spec.is_fully_withdrawable_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
validator = state.validators[validator_index]
|
||||
balance = state.balances[validator_index]
|
||||
assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@@ -126,7 +105,9 @@ def test_success_exited(spec, state):
|
||||
signed_address_change = get_signed_address_change(spec, state, validator_index=validator_index)
|
||||
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change)
|
||||
|
||||
assert not spec.is_fully_withdrawable_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
validator = state.validators[validator_index]
|
||||
balance = state.balances[validator_index]
|
||||
assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@@ -142,7 +123,9 @@ def test_success_withdrawable(spec, state):
|
||||
signed_address_change = get_signed_address_change(spec, state, validator_index=validator_index)
|
||||
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change)
|
||||
|
||||
assert spec.is_fully_withdrawable_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
validator = state.validators[validator_index]
|
||||
balance = state.balances[validator_index]
|
||||
assert spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state))
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@@ -185,7 +168,7 @@ def test_fail_incorrect_from_bls_pubkey(spec, state):
|
||||
@always_bls
|
||||
def test_fail_bad_signature(spec, state):
|
||||
signed_address_change = get_signed_address_change(spec, state)
|
||||
# Mutate sigature
|
||||
# Mutate signature
|
||||
signed_address_change.signature = spec.BLSSignature(b'\x42' * 96)
|
||||
|
||||
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
with_capella_and_later,
|
||||
)
|
||||
from eth2spec.test.helpers.state import next_epoch_via_block
|
||||
from eth2spec.test.helpers.deposits import (
|
||||
prepare_state_and_deposit,
|
||||
run_deposit_processing,
|
||||
)
|
||||
from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_top_up_to_withdrawn_validator(spec, state):
|
||||
validator_index = 0
|
||||
|
||||
# Fully withdraw validator
|
||||
set_validator_fully_withdrawable(spec, state, validator_index)
|
||||
assert state.balances[validator_index] > 0
|
||||
next_epoch_via_block(spec, state)
|
||||
assert state.balances[validator_index] == 0
|
||||
assert state.validators[validator_index].effective_balance > 0
|
||||
next_epoch_via_block(spec, state)
|
||||
assert state.validators[validator_index].effective_balance == 0
|
||||
|
||||
# Make a top-up balance to validator
|
||||
amount = spec.MAX_EFFECTIVE_BALANCE // 4
|
||||
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
|
||||
|
||||
yield from run_deposit_processing(spec, state, deposit, validator_index)
|
||||
|
||||
assert state.balances[validator_index] == amount
|
||||
assert state.validators[validator_index].effective_balance == 0
|
||||
|
||||
validator = state.validators[validator_index]
|
||||
balance = state.balances[validator_index]
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
assert spec.is_fully_withdrawable_validator(validator, balance, current_epoch)
|
||||
@@ -9,10 +9,11 @@ from eth2spec.test.helpers.state import next_slot
|
||||
|
||||
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,
|
||||
)
|
||||
@@ -110,6 +111,7 @@ def test_fail_empty_queue_non_empty_withdrawals(spec, state):
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
withdrawal = spec.Withdrawal(
|
||||
index=0,
|
||||
validator_index=0,
|
||||
address=b'\x30' * 20,
|
||||
amount=420,
|
||||
)
|
||||
@@ -235,7 +237,7 @@ def test_fail_many_dequeued_incorrectly(spec, state):
|
||||
if i % 3 == 0:
|
||||
withdrawal.index += 1
|
||||
elif i % 3 == 1:
|
||||
withdrawal.address = (i).to_bytes(20, 'big')
|
||||
withdrawal.address = i.to_bytes(20, 'big')
|
||||
else:
|
||||
withdrawal.amount += 1
|
||||
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
from random import Random
|
||||
|
||||
from eth2spec.test.context import (
|
||||
with_capella_and_later,
|
||||
spec_state_test,
|
||||
)
|
||||
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
|
||||
|
||||
|
||||
def set_validator_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
|
||||
validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:]
|
||||
|
||||
assert spec.is_fully_withdrawable_validator(validator, withdrawable_epoch)
|
||||
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, spec.get_current_epoch(state))
|
||||
if spec.is_fully_withdrawable_validator(validator, state.balances[index], spec.get_current_epoch(state))
|
||||
]
|
||||
|
||||
if num_expected_withdrawals is not None:
|
||||
@@ -29,11 +30,11 @@ def run_process_full_withdrawals(spec, state, num_expected_withdrawals=None):
|
||||
else:
|
||||
num_expected_withdrawals = len(to_be_withdrawn_indices)
|
||||
|
||||
yield from run_epoch_processing_with(spec, state, 'process_full_withdrawals')
|
||||
yield 'pre', state
|
||||
spec.process_full_withdrawals(state)
|
||||
yield 'post', state
|
||||
|
||||
for index in to_be_withdrawn_indices:
|
||||
validator = state.validators[index]
|
||||
assert validator.fully_withdrawn_epoch == spec.get_current_epoch(state)
|
||||
assert state.balances[index] == 0
|
||||
|
||||
assert len(state.withdrawal_queue) == len(pre_withdrawal_queue) + num_expected_withdrawals
|
||||
@@ -42,13 +43,49 @@ def run_process_full_withdrawals(spec, state, num_expected_withdrawals=None):
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_no_withdrawals(spec, state):
|
||||
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):
|
||||
@@ -56,7 +93,7 @@ def test_no_withdrawals_but_some_next_epoch(spec, state):
|
||||
|
||||
# Make a few validators withdrawable at the *next* epoch
|
||||
for index in range(3):
|
||||
set_validator_withdrawable(spec, state, index, current_epoch + 1)
|
||||
set_validator_fully_withdrawable(spec, state, index, current_epoch + 1)
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, 0)
|
||||
|
||||
@@ -65,7 +102,7 @@ def test_no_withdrawals_but_some_next_epoch(spec, state):
|
||||
@spec_state_test
|
||||
def test_single_withdrawal(spec, state):
|
||||
# Make one validator withdrawable
|
||||
set_validator_withdrawable(spec, state, 0)
|
||||
set_validator_fully_withdrawable(spec, state, 0)
|
||||
|
||||
assert state.next_withdrawal_index == 0
|
||||
yield from run_process_full_withdrawals(spec, state, 1)
|
||||
@@ -78,7 +115,7 @@ def test_single_withdrawal(spec, state):
|
||||
def test_multi_withdrawal(spec, state):
|
||||
# Make a few validators withdrawable
|
||||
for index in range(3):
|
||||
set_validator_withdrawable(spec, state, index)
|
||||
set_validator_fully_withdrawable(spec, state, index)
|
||||
|
||||
yield from run_process_full_withdrawals(spec, state, 3)
|
||||
|
||||
@@ -88,6 +125,50 @@ def test_multi_withdrawal(spec, state):
|
||||
def test_all_withdrawal(spec, state):
|
||||
# Make all validators withdrawable
|
||||
for index in range(len(state.validators)):
|
||||
set_validator_withdrawable(spec, state, index)
|
||||
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))
|
||||
|
||||
@@ -8,15 +8,10 @@ from eth2spec.test.context import (
|
||||
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
|
||||
|
||||
|
||||
def set_validator_partially_withdrawable(spec, state, index, rng=random.Random(666)):
|
||||
validator = state.validators[index]
|
||||
validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:]
|
||||
validator.effective_balance = spec.MAX_EFFECTIVE_BALANCE
|
||||
state.balances[index] = spec.MAX_EFFECTIVE_BALANCE + rng.randint(1, 100000000)
|
||||
|
||||
assert spec.is_partially_withdrawable_validator(validator, state.balances[index])
|
||||
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):
|
||||
@@ -62,6 +57,49 @@ def test_success_no_withdrawable(spec, state):
|
||||
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):
|
||||
@@ -155,7 +193,7 @@ def test_success_max_partial_withdrawable(spec, state):
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_presets([MINIMAL], reason="not no enough validators with mainnet config")
|
||||
@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
|
||||
@@ -180,7 +218,7 @@ def run_random_partial_withdrawals_test(spec, state, rng):
|
||||
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)
|
||||
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``,
|
||||
|
||||
438
tests/core/pyspec/eth2spec/test/capella/random/test_random.py
Normal file
438
tests/core/pyspec/eth2spec/test/capella/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 CAPELLA
|
||||
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([CAPELLA])
|
||||
@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_capella
|
||||
# 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_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# 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_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# 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_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# 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_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# 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_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# 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_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# 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_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# 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_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# 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_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# 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_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# 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_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# 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_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # 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([CAPELLA])
|
||||
@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_capella
|
||||
# epochs:1,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:no_block
|
||||
# epochs:0,slots:0,with-block:random_block_capella
|
||||
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_capella', '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_capella', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_capella'} # noqa: E501
|
||||
yield from run_generated_randomized_test(
|
||||
spec,
|
||||
state,
|
||||
scenario,
|
||||
)
|
||||
135
tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py
Normal file
135
tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py
Normal file
@@ -0,0 +1,135 @@
|
||||
from eth2spec.test.context import (
|
||||
with_capella_and_later, spec_state_test
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change
|
||||
from eth2spec.test.helpers.withdrawals import (
|
||||
set_validator_fully_withdrawable,
|
||||
set_validator_partially_withdrawable,
|
||||
)
|
||||
from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_successful_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
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.bls_to_execution_changes.append(signed_address_change)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
post_credentials = state.validators[index].withdrawal_credentials
|
||||
assert pre_credentials != post_credentials
|
||||
assert post_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX
|
||||
assert post_credentials[1:12] == b'\x00' * 11
|
||||
assert post_credentials[12:] == signed_address_change.message.to_execution_address
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@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):
|
||||
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
|
||||
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
||||
|
||||
index = 0
|
||||
signed_address_change = get_signed_address_change(spec, state, validator_index=index)
|
||||
signed_exit = prepare_signed_exits(spec, state, [index])[0]
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.voluntary_exits.append(signed_exit)
|
||||
block.body.bls_to_execution_changes.append(signed_address_change)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
validator = state.validators[index]
|
||||
balance = state.balances[index]
|
||||
current_epoch = spec.get_current_epoch(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)
|
||||
@@ -12,7 +12,6 @@ from eth2spec.test.helpers.sharding import (
|
||||
get_sample_opaque_tx,
|
||||
get_sample_blob,
|
||||
)
|
||||
from eth2spec.test.helpers.keys import privkeys
|
||||
|
||||
|
||||
@with_eip4844_and_later
|
||||
@@ -38,8 +37,6 @@ def _run_validate_blobs_sidecar_test(spec, state, blob_count):
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
blobs_sidecar = spec.get_blobs_sidecar(block, blobs)
|
||||
privkey = privkeys[1]
|
||||
spec.get_signed_blobs_sidecar(state, blobs_sidecar, privkey)
|
||||
expected_commitments = [spec.blob_to_kzg_commitment(blobs[i]) for i in range(blob_count)]
|
||||
spec.validate_blobs_sidecar(block.slot, block.hash_tree_root(), expected_commitments, blobs_sidecar)
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
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):
|
||||
if validator_index is None:
|
||||
validator_index = 0
|
||||
|
||||
if withdrawal_pubkey is None:
|
||||
key_index = -1 - validator_index
|
||||
withdrawal_pubkey = pubkeys[key_index]
|
||||
withdrawal_privkey = privkeys[key_index]
|
||||
else:
|
||||
withdrawal_privkey = pubkey_to_privkey[withdrawal_pubkey]
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
signing_root = spec.compute_signing_root(address_change, domain)
|
||||
return spec.SignedBLSToExecutionChange(
|
||||
message=address_change,
|
||||
signature=bls.Sign(withdrawal_privkey, signing_root),
|
||||
)
|
||||
@@ -16,7 +16,7 @@ def run_fork_test(post_spec, pre_state):
|
||||
# Eth1
|
||||
'eth1_data', 'eth1_data_votes', 'eth1_deposit_index',
|
||||
# Registry
|
||||
'balances',
|
||||
'validators', 'balances',
|
||||
# Randomness
|
||||
'randao_mixes',
|
||||
# Slashings
|
||||
@@ -36,7 +36,7 @@ def run_fork_test(post_spec, pre_state):
|
||||
assert getattr(pre_state, field) == getattr(post_state, field)
|
||||
|
||||
# Modified fields
|
||||
modified_fields = ['fork', 'validators']
|
||||
modified_fields = ['fork']
|
||||
for field in modified_fields:
|
||||
assert getattr(pre_state, field) != getattr(post_state, field)
|
||||
|
||||
@@ -50,7 +50,6 @@ def run_fork_test(post_spec, pre_state):
|
||||
]
|
||||
for field in stable_validator_fields:
|
||||
assert getattr(pre_validator, field) == getattr(post_validator, field)
|
||||
assert post_validator.fully_withdrawn_epoch == post_spec.FAR_FUTURE_EPOCH
|
||||
|
||||
assert pre_state.fork.current_version == post_state.fork.previous_version
|
||||
assert post_state.fork.current_version == post_spec.config.CAPELLA_FORK_VERSION
|
||||
|
||||
@@ -24,11 +24,12 @@ ALL_PHASES = (
|
||||
EIP4844,
|
||||
)
|
||||
# The forks that output to the test vectors.
|
||||
TESTGEN_FORKS = (PHASE0, ALTAIR, BELLATRIX)
|
||||
TESTGEN_FORKS = (PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844)
|
||||
|
||||
FORKS_BEFORE_ALTAIR = (PHASE0,)
|
||||
FORKS_BEFORE_BELLATRIX = (PHASE0, ALTAIR)
|
||||
FORKS_BEFORE_CAPELLA = (PHASE0, ALTAIR, BELLATRIX)
|
||||
|
||||
# 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,
|
||||
|
||||
@@ -188,8 +188,12 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef
|
||||
"""
|
||||
pre_validator_count = len(state.validators)
|
||||
pre_balance = 0
|
||||
is_top_up = False
|
||||
# is a top-up
|
||||
if validator_index < pre_validator_count:
|
||||
is_top_up = True
|
||||
pre_balance = get_balance(state, validator_index)
|
||||
pre_effective_balance = state.validators[validator_index].effective_balance
|
||||
|
||||
yield 'pre', state
|
||||
yield 'deposit', deposit
|
||||
@@ -219,9 +223,12 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef
|
||||
assert len(state.balances) == pre_validator_count + 1
|
||||
assert get_balance(state, validator_index) == pre_balance + deposit.data.amount
|
||||
|
||||
effective = min(spec.MAX_EFFECTIVE_BALANCE,
|
||||
pre_balance + deposit.data.amount)
|
||||
effective -= effective % spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
assert state.validators[validator_index].effective_balance == effective
|
||||
if is_top_up:
|
||||
# Top-ups do not change effective balance
|
||||
assert state.validators[validator_index].effective_balance == pre_effective_balance
|
||||
else:
|
||||
effective_balance = min(spec.MAX_EFFECTIVE_BALANCE, deposit.data.amount)
|
||||
effective_balance -= effective_balance % spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
assert state.validators[validator_index].effective_balance == effective_balance
|
||||
|
||||
assert state.eth1_deposit_index == state.eth1_data.deposit_count
|
||||
|
||||
@@ -20,9 +20,6 @@ def build_mock_validator(spec, i: int, balance: int):
|
||||
effective_balance=min(balance - balance % spec.EFFECTIVE_BALANCE_INCREMENT, spec.MAX_EFFECTIVE_BALANCE)
|
||||
)
|
||||
|
||||
if spec.fork in (CAPELLA):
|
||||
validator.fully_withdrawn_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing
|
||||
from eth2spec.test.helpers.attestations import get_valid_attestation
|
||||
from eth2spec.test.helpers.deposits import build_deposit, deposit_from_context
|
||||
from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits
|
||||
from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change
|
||||
|
||||
|
||||
def run_slash_and_exit(spec, state, slash_index, exit_index, valid=True):
|
||||
@@ -126,13 +127,15 @@ def get_random_deposits(spec, state, rng, num_deposits=None):
|
||||
# First build deposit data leaves
|
||||
for i in range(num_deposits):
|
||||
index = len(state.validators) + i
|
||||
withdrawal_pubkey = pubkeys[-1 - index]
|
||||
withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(withdrawal_pubkey)[1:]
|
||||
_, root, deposit_data_leaves = build_deposit(
|
||||
spec,
|
||||
deposit_data_leaves,
|
||||
pubkeys[index],
|
||||
privkeys[index],
|
||||
spec.MAX_EFFECTIVE_BALANCE,
|
||||
withdrawal_credentials=b'\x00' * 32,
|
||||
withdrawal_credentials=withdrawal_credentials,
|
||||
signed=True,
|
||||
)
|
||||
|
||||
@@ -200,6 +203,20 @@ def get_random_sync_aggregate(spec, state, slot, block_root=None, fraction_parti
|
||||
)
|
||||
|
||||
|
||||
def get_random_bls_to_execution_changes(spec, state, rng=Random(2188), num_address_changes=0):
|
||||
bls_indices = [
|
||||
index
|
||||
for index, validator in enumerate(state.validators)
|
||||
if validator.withdrawal_credentials[:1] == spec.BLS_WITHDRAWAL_PREFIX
|
||||
]
|
||||
assert len(bls_indices) > 0
|
||||
|
||||
return [
|
||||
get_signed_address_change(spec, state, validator_index=validator_index)
|
||||
for validator_index in rng.sample(bls_indices, min(num_address_changes, len(bls_indices)))
|
||||
]
|
||||
|
||||
|
||||
def build_random_block_from_state_for_next_slot(spec, state, rng=Random(2188), deposits=None):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
proposer_slashings = get_random_proposer_slashings(spec, state, rng)
|
||||
|
||||
31
tests/core/pyspec/eth2spec/test/helpers/withdrawals.py
Normal file
31
tests/core/pyspec/eth2spec/test/helpers/withdrawals.py
Normal file
@@ -0,0 +1,31 @@
|
||||
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
|
||||
if validator.exit_epoch > withdrawable_epoch:
|
||||
validator.exit_epoch = withdrawable_epoch
|
||||
|
||||
if validator.withdrawal_credentials[0:1] == spec.BLS_WITHDRAWAL_PREFIX:
|
||||
validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:]
|
||||
|
||||
if state.balances[index] == 0:
|
||||
state.balances[index] = 10000000000
|
||||
|
||||
assert spec.is_fully_withdrawable_validator(validator, state.balances[index], withdrawable_epoch)
|
||||
|
||||
|
||||
def set_eth1_withdrawal_credential_with_balance(spec, state, index, balance):
|
||||
validator = state.validators[index]
|
||||
validator.withdrawal_credentials = spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:]
|
||||
validator.effective_balance = min(balance, spec.MAX_EFFECTIVE_BALANCE)
|
||||
state.balances[index] = balance
|
||||
|
||||
|
||||
def set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000):
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, index, spec.MAX_EFFECTIVE_BALANCE + excess_balance)
|
||||
validator = state.validators[index]
|
||||
|
||||
assert spec.is_partially_withdrawable_validator(validator, state.balances[index])
|
||||
@@ -141,13 +141,57 @@ def test_invalid_sig_new_deposit(spec, state):
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_success_top_up(spec, state):
|
||||
def test_success_top_up__max_effective_balance(spec, state):
|
||||
validator_index = 0
|
||||
amount = spec.MAX_EFFECTIVE_BALANCE // 4
|
||||
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
|
||||
|
||||
state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE
|
||||
state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE
|
||||
|
||||
yield from run_deposit_processing(spec, state, deposit, validator_index)
|
||||
|
||||
assert state.balances[validator_index] == spec.MAX_EFFECTIVE_BALANCE + amount
|
||||
assert state.validators[validator_index].effective_balance == spec.MAX_EFFECTIVE_BALANCE
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_success_top_up__less_effective_balance(spec, state):
|
||||
validator_index = 0
|
||||
amount = spec.MAX_EFFECTIVE_BALANCE // 4
|
||||
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
|
||||
|
||||
initial_balance = spec.MAX_EFFECTIVE_BALANCE - 1000
|
||||
initial_effective_balance = spec.MAX_EFFECTIVE_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
state.balances[validator_index] = initial_balance
|
||||
state.validators[validator_index].effective_balance = initial_effective_balance
|
||||
|
||||
yield from run_deposit_processing(spec, state, deposit, validator_index)
|
||||
|
||||
assert state.balances[validator_index] == initial_balance + amount
|
||||
# unchanged effective balance
|
||||
assert state.validators[validator_index].effective_balance == initial_effective_balance
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_success_top_up__zero_balance(spec, state):
|
||||
validator_index = 0
|
||||
amount = spec.MAX_EFFECTIVE_BALANCE // 4
|
||||
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
|
||||
|
||||
initial_balance = 0
|
||||
initial_effective_balance = 0
|
||||
state.balances[validator_index] = initial_balance
|
||||
state.validators[validator_index].effective_balance = initial_effective_balance
|
||||
|
||||
yield from run_deposit_processing(spec, state, deposit, validator_index)
|
||||
|
||||
assert state.balances[validator_index] == initial_balance + amount
|
||||
# unchanged effective balance
|
||||
assert state.validators[validator_index].effective_balance == initial_effective_balance
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
|
||||
@@ -196,7 +196,7 @@ def test_ex_ante_attestations_is_greater_than_proposer_boost_with_boost(spec, st
|
||||
def test_ex_ante_sandwich_without_attestations(spec, state):
|
||||
"""
|
||||
Simple Sandwich test with boost and no attestations.
|
||||
Obejcts:
|
||||
Objects:
|
||||
Block A - slot N
|
||||
Block B (parent A) - slot N+1
|
||||
Block C (parent A) - slot N+2
|
||||
|
||||
@@ -9,6 +9,7 @@ from typing import Callable
|
||||
|
||||
from eth2spec.test.helpers.multi_operations import (
|
||||
build_random_block_from_state_for_next_slot,
|
||||
get_random_bls_to_execution_changes,
|
||||
get_random_sync_aggregate,
|
||||
prepare_state_and_get_random_deposits,
|
||||
)
|
||||
@@ -71,6 +72,15 @@ def randomize_state_bellatrix(spec, state, stats, exit_fraction=0.1, slash_fract
|
||||
return scenario_state
|
||||
|
||||
|
||||
def randomize_state_capella(spec, state, stats, exit_fraction=0.1, slash_fraction=0.1):
|
||||
scenario_state = randomize_state_bellatrix(spec,
|
||||
state,
|
||||
stats,
|
||||
exit_fraction=exit_fraction,
|
||||
slash_fraction=slash_fraction)
|
||||
return scenario_state
|
||||
|
||||
|
||||
# epochs
|
||||
|
||||
def epochs_until_leak(spec):
|
||||
@@ -195,6 +205,16 @@ def random_block_bellatrix(spec, state, signed_blocks, scenario_state):
|
||||
return block
|
||||
|
||||
|
||||
def random_block_capella(spec, state, signed_blocks, scenario_state, rng=Random(3456)):
|
||||
block = random_block_bellatrix(spec, state, signed_blocks, scenario_state)
|
||||
block.body.bls_to_execution_changes = get_random_bls_to_execution_changes(
|
||||
spec,
|
||||
state,
|
||||
num_address_changes=rng.randint(1, spec.MAX_BLS_TO_EXECUTION_CHANGES)
|
||||
)
|
||||
return block
|
||||
|
||||
|
||||
# validations
|
||||
|
||||
def no_op_validation(_spec, _state):
|
||||
|
||||
@@ -45,5 +45,7 @@ Sub-transitions:
|
||||
- `participation_record_updates` (Phase 0 only)
|
||||
- `participation_flag_updates` (Altair)
|
||||
- `sync_committee_updates` (Altair)
|
||||
- `full_withdrawals` (Capella)
|
||||
- `partial_withdrawals` (Capella)
|
||||
|
||||
The resulting state should match the expected `post` state.
|
||||
|
||||
@@ -43,6 +43,7 @@ Operations:
|
||||
| `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` |
|
||||
| `sync_aggregate` | `SyncAggregate` | `sync_aggregate` | `process_sync_aggregate(state, sync_aggregate)` (new in Altair) |
|
||||
| `execution_payload` | `ExecutionPayload` | `execution_payload` | `process_execution_payload(state, execution_payload)` (new in Bellatrix) |
|
||||
| `bls_to_execution_change` | `SignedBLSToExecutionChange` | `signed_address_change` | `process_bls_to_execution_change(state, signed_address_change)` (new in Capella) |
|
||||
|
||||
Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here.
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ def case01_sign():
|
||||
# Edge case: privkey == 0
|
||||
expect_exception(bls.Sign, ZERO_PRIVKEY, message)
|
||||
expect_exception(milagro_bls.Sign, ZERO_PRIVKEY_BYTES, message)
|
||||
yield f'sign_case_zero_privkey', {
|
||||
yield 'sign_case_zero_privkey', {
|
||||
'input': {
|
||||
'privkey': encode_hex(ZERO_PRIVKEY_BYTES),
|
||||
'message': encode_hex(message),
|
||||
@@ -153,7 +153,7 @@ def case02_verify():
|
||||
# Invalid pubkey and signature with the point at infinity
|
||||
assert not bls.Verify(G1_POINT_AT_INFINITY, SAMPLE_MESSAGE, G2_POINT_AT_INFINITY)
|
||||
assert not milagro_bls.Verify(G1_POINT_AT_INFINITY, SAMPLE_MESSAGE, G2_POINT_AT_INFINITY)
|
||||
yield f'verify_infinity_pubkey_and_infinity_signature', {
|
||||
yield 'verify_infinity_pubkey_and_infinity_signature', {
|
||||
'input': {
|
||||
'pubkey': encode_hex(G1_POINT_AT_INFINITY),
|
||||
'message': encode_hex(SAMPLE_MESSAGE),
|
||||
@@ -177,7 +177,7 @@ def case03_aggregate():
|
||||
expect_exception(bls.Aggregate, [])
|
||||
# No signatures to aggregate. Follow IETF BLS spec, return `None` to represent INVALID.
|
||||
# https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.8
|
||||
yield f'aggregate_na_signatures', {
|
||||
yield 'aggregate_na_signatures', {
|
||||
'input': [],
|
||||
'output': None,
|
||||
}
|
||||
@@ -185,7 +185,7 @@ def case03_aggregate():
|
||||
# Valid to aggregate G2 point at infinity
|
||||
aggregate_sig = bls.Aggregate([G2_POINT_AT_INFINITY])
|
||||
assert aggregate_sig == milagro_bls.Aggregate([G2_POINT_AT_INFINITY]) == G2_POINT_AT_INFINITY
|
||||
yield f'aggregate_infinity_signature', {
|
||||
yield 'aggregate_infinity_signature', {
|
||||
'input': [encode_hex(G2_POINT_AT_INFINITY)],
|
||||
'output': encode_hex(aggregate_sig),
|
||||
}
|
||||
@@ -244,7 +244,7 @@ def case04_fast_aggregate_verify():
|
||||
# Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == Z1_SIGNATURE
|
||||
assert not bls.FastAggregateVerify([], message, G2_POINT_AT_INFINITY)
|
||||
assert not milagro_bls.FastAggregateVerify([], message, G2_POINT_AT_INFINITY)
|
||||
yield f'fast_aggregate_verify_na_pubkeys_and_infinity_signature', {
|
||||
yield 'fast_aggregate_verify_na_pubkeys_and_infinity_signature', {
|
||||
'input': {
|
||||
'pubkeys': [],
|
||||
'message': encode_hex(message),
|
||||
@@ -256,7 +256,7 @@ def case04_fast_aggregate_verify():
|
||||
# Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == 0x00...
|
||||
assert not bls.FastAggregateVerify([], message, ZERO_SIGNATURE)
|
||||
assert not milagro_bls.FastAggregateVerify([], message, ZERO_SIGNATURE)
|
||||
yield f'fast_aggregate_verify_na_pubkeys_and_zero_signature', {
|
||||
yield 'fast_aggregate_verify_na_pubkeys_and_zero_signature', {
|
||||
'input': {
|
||||
'pubkeys': [],
|
||||
'message': encode_hex(message),
|
||||
@@ -272,7 +272,7 @@ def case04_fast_aggregate_verify():
|
||||
aggregate_signature = bls.Aggregate(signatures)
|
||||
assert not bls.FastAggregateVerify(pubkeys_with_infinity, SAMPLE_MESSAGE, aggregate_signature)
|
||||
assert not milagro_bls.FastAggregateVerify(pubkeys_with_infinity, SAMPLE_MESSAGE, aggregate_signature)
|
||||
yield f'fast_aggregate_verify_infinity_pubkey', {
|
||||
yield 'fast_aggregate_verify_infinity_pubkey', {
|
||||
'input': {
|
||||
'pubkeys': [encode_hex(pubkey) for pubkey in pubkeys_with_infinity],
|
||||
'message': encode_hex(SAMPLE_MESSAGE),
|
||||
@@ -300,7 +300,7 @@ def case05_aggregate_verify():
|
||||
aggregate_signature = bls.Aggregate(sigs)
|
||||
assert bls.AggregateVerify(pubkeys, messages, aggregate_signature)
|
||||
assert milagro_bls.AggregateVerify(pubkeys, messages, aggregate_signature)
|
||||
yield f'aggregate_verify_valid', {
|
||||
yield 'aggregate_verify_valid', {
|
||||
'input': {
|
||||
'pubkeys': pubkeys_serial,
|
||||
'messages': messages_serial,
|
||||
@@ -312,7 +312,7 @@ def case05_aggregate_verify():
|
||||
tampered_signature = aggregate_signature[:4] + b'\xff\xff\xff\xff'
|
||||
assert not bls.AggregateVerify(pubkey, messages, tampered_signature)
|
||||
assert not milagro_bls.AggregateVerify(pubkeys, messages, tampered_signature)
|
||||
yield f'aggregate_verify_tampered_signature', {
|
||||
yield 'aggregate_verify_tampered_signature', {
|
||||
'input': {
|
||||
'pubkeys': pubkeys_serial,
|
||||
'messages': messages_serial,
|
||||
@@ -324,7 +324,7 @@ def case05_aggregate_verify():
|
||||
# Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == Z1_SIGNATURE
|
||||
assert not bls.AggregateVerify([], [], G2_POINT_AT_INFINITY)
|
||||
assert not milagro_bls.AggregateVerify([], [], G2_POINT_AT_INFINITY)
|
||||
yield f'aggregate_verify_na_pubkeys_and_infinity_signature', {
|
||||
yield 'aggregate_verify_na_pubkeys_and_infinity_signature', {
|
||||
'input': {
|
||||
'pubkeys': [],
|
||||
'messages': [],
|
||||
@@ -336,7 +336,7 @@ def case05_aggregate_verify():
|
||||
# Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == 0x00...
|
||||
assert not bls.AggregateVerify([], [], ZERO_SIGNATURE)
|
||||
assert not milagro_bls.AggregateVerify([], [], ZERO_SIGNATURE)
|
||||
yield f'aggregate_verify_na_pubkeys_and_zero_signature', {
|
||||
yield 'aggregate_verify_na_pubkeys_and_zero_signature', {
|
||||
'input': {
|
||||
'pubkeys': [],
|
||||
'messages': [],
|
||||
@@ -350,7 +350,7 @@ def case05_aggregate_verify():
|
||||
messages_with_sample = messages + [SAMPLE_MESSAGE]
|
||||
assert not bls.AggregateVerify(pubkeys_with_infinity, messages_with_sample, aggregate_signature)
|
||||
assert not milagro_bls.AggregateVerify(pubkeys_with_infinity, messages_with_sample, aggregate_signature)
|
||||
yield f'aggregate_verify_infinity_pubkey', {
|
||||
yield 'aggregate_verify_infinity_pubkey', {
|
||||
'input': {
|
||||
'pubkeys': [encode_hex(pubkey) for pubkey in pubkeys_with_infinity],
|
||||
'messages': [encode_hex(message) for message in messages_with_sample],
|
||||
@@ -375,7 +375,7 @@ def case06_eth_aggregate_pubkeys():
|
||||
# Valid pubkeys
|
||||
aggregate_pubkey = spec.eth_aggregate_pubkeys(PUBKEYS)
|
||||
assert aggregate_pubkey == milagro_bls._AggregatePKs(PUBKEYS)
|
||||
yield f'eth_aggregate_pubkeys_valid_pubkeys', {
|
||||
yield 'eth_aggregate_pubkeys_valid_pubkeys', {
|
||||
'input': [encode_hex(pubkey) for pubkey in PUBKEYS],
|
||||
'output': encode_hex(aggregate_pubkey),
|
||||
}
|
||||
@@ -383,7 +383,7 @@ def case06_eth_aggregate_pubkeys():
|
||||
# Invalid pubkeys -- len(pubkeys) == 0
|
||||
expect_exception(spec.eth_aggregate_pubkeys, [])
|
||||
expect_exception(milagro_bls._AggregatePKs, [])
|
||||
yield f'eth_aggregate_pubkeys_empty_list', {
|
||||
yield 'eth_aggregate_pubkeys_empty_list', {
|
||||
'input': [],
|
||||
'output': None,
|
||||
}
|
||||
@@ -391,7 +391,7 @@ def case06_eth_aggregate_pubkeys():
|
||||
# Invalid pubkeys -- [ZERO_PUBKEY]
|
||||
expect_exception(spec.eth_aggregate_pubkeys, [ZERO_PUBKEY])
|
||||
expect_exception(milagro_bls._AggregatePKs, [ZERO_PUBKEY])
|
||||
yield f'eth_aggregate_pubkeys_zero_pubkey', {
|
||||
yield 'eth_aggregate_pubkeys_zero_pubkey', {
|
||||
'input': [encode_hex(ZERO_PUBKEY)],
|
||||
'output': None,
|
||||
}
|
||||
@@ -399,7 +399,7 @@ def case06_eth_aggregate_pubkeys():
|
||||
# Invalid pubkeys -- G1 point at infinity
|
||||
expect_exception(spec.eth_aggregate_pubkeys, [G1_POINT_AT_INFINITY])
|
||||
expect_exception(milagro_bls._AggregatePKs, [G1_POINT_AT_INFINITY])
|
||||
yield f'eth_aggregate_pubkeys_infinity_pubkey', {
|
||||
yield 'eth_aggregate_pubkeys_infinity_pubkey', {
|
||||
'input': [encode_hex(G1_POINT_AT_INFINITY)],
|
||||
'output': None,
|
||||
}
|
||||
@@ -408,7 +408,7 @@ def case06_eth_aggregate_pubkeys():
|
||||
x40_pubkey = b'\x40' + b'\00' * 47
|
||||
expect_exception(spec.eth_aggregate_pubkeys, [x40_pubkey])
|
||||
expect_exception(milagro_bls._AggregatePKs, [x40_pubkey])
|
||||
yield f'eth_aggregate_pubkeys_x40_pubkey', {
|
||||
yield 'eth_aggregate_pubkeys_x40_pubkey', {
|
||||
'input': [encode_hex(x40_pubkey)],
|
||||
'output': None,
|
||||
}
|
||||
@@ -466,7 +466,7 @@ def case07_eth_fast_aggregate_verify():
|
||||
|
||||
# NOTE: Unlike `FastAggregateVerify`, len(pubkeys) == 0 and signature == G2_POINT_AT_INFINITY is VALID
|
||||
assert spec.eth_fast_aggregate_verify([], message, G2_POINT_AT_INFINITY)
|
||||
yield f'eth_fast_aggregate_verify_na_pubkeys_and_infinity_signature', {
|
||||
yield 'eth_fast_aggregate_verify_na_pubkeys_and_infinity_signature', {
|
||||
'input': {
|
||||
'pubkeys': [],
|
||||
'message': encode_hex(message),
|
||||
@@ -477,7 +477,7 @@ def case07_eth_fast_aggregate_verify():
|
||||
|
||||
# Invalid pubkeys and signature -- len(pubkeys) == 0 and signature == 0x00...
|
||||
assert not spec.eth_fast_aggregate_verify([], message, ZERO_SIGNATURE)
|
||||
yield f'eth_fast_aggregate_verify_na_pubkeys_and_zero_signature', {
|
||||
yield 'eth_fast_aggregate_verify_na_pubkeys_and_zero_signature', {
|
||||
'input': {
|
||||
'pubkeys': [],
|
||||
'message': encode_hex(message),
|
||||
@@ -492,7 +492,7 @@ def case07_eth_fast_aggregate_verify():
|
||||
signatures = [bls.Sign(privkey, SAMPLE_MESSAGE) for privkey in PRIVKEYS]
|
||||
aggregate_signature = bls.Aggregate(signatures)
|
||||
assert not spec.eth_fast_aggregate_verify(pubkeys_with_infinity, SAMPLE_MESSAGE, aggregate_signature)
|
||||
yield f'eth_fast_aggregate_verify_infinity_pubkey', {
|
||||
yield 'eth_fast_aggregate_verify_infinity_pubkey', {
|
||||
'input': {
|
||||
'pubkeys': [encode_hex(pubkey) for pubkey in pubkeys_with_infinity],
|
||||
'message': encode_hex(SAMPLE_MESSAGE),
|
||||
|
||||
@@ -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
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -27,6 +27,12 @@ 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)
|
||||
|
||||
# TODO Custody Game testgen is disabled for now
|
||||
# custody_game_mods = {**{key: 'eth2spec.test.custody_game.epoch_processing.test_process_' + key for key in [
|
||||
# 'reveal_deadlines',
|
||||
@@ -38,6 +44,7 @@ if __name__ == "__main__":
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="epoch_processing", all_mods=all_mods)
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
phase_0_mods = {'finality': 'eth2spec.test.phase0.finality.test_finality'}
|
||||
altair_mods = phase_0_mods # No additional Altair specific finality tests
|
||||
bellatrix_mods = altair_mods # No additional Bellatrix specific finality tests
|
||||
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
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_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
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -17,11 +17,12 @@ if __name__ == "__main__":
|
||||
'on_merge_block',
|
||||
]}
|
||||
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
|
||||
|
||||
capella_mods = bellatrix_mods # No additional Capella specific fork choice tests
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="fork_choice", all_mods=all_mods)
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
from typing import Iterable
|
||||
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, MINIMAL, MAINNET
|
||||
from eth2spec.test.helpers.constants import (
|
||||
PHASE0, ALTAIR, BELLATRIX, CAPELLA,
|
||||
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.gen_helpers.gen_base import gen_runner, gen_typing
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import generate_from_tests
|
||||
|
||||
@@ -33,6 +37,8 @@ def _get_fork_tests_providers():
|
||||
yield create_provider(test_altair_fork_random, preset, PHASE0, ALTAIR)
|
||||
yield create_provider(test_bellatrix_fork_basic, preset, ALTAIR, BELLATRIX)
|
||||
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)
|
||||
|
||||
|
||||
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
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -8,18 +8,19 @@ if __name__ == "__main__":
|
||||
'validity',
|
||||
]}
|
||||
|
||||
# we have new unconditional lines in `initialize_beacon_state_from_eth1` and we want to test it
|
||||
altair_mods = phase_0_mods
|
||||
|
||||
# we have new unconditional lines in `initialize_beacon_state_from_eth1` and we want to test it
|
||||
_new_bellatrix_mods = {key: 'eth2spec.test.bellatrix.genesis.test_' + key for key in [
|
||||
'initialization',
|
||||
]}
|
||||
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
|
||||
|
||||
capella_mods = bellatrix_mods # No additional Capella specific genesis tests
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="genesis", all_mods=all_mods)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
|
||||
|
||||
@@ -9,10 +9,12 @@ if __name__ == "__main__":
|
||||
'update_ranking',
|
||||
]}
|
||||
bellatrix_mods = altair_mods
|
||||
capella_mods = bellatrix_mods
|
||||
|
||||
all_mods = {
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_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
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -24,6 +24,13 @@ if __name__ == "__main__":
|
||||
]}
|
||||
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
|
||||
|
||||
_new_capella_mods = {key: 'eth2spec.test.capella.block_processing.test_process_' + key for key in [
|
||||
'deposit',
|
||||
'bls_to_execution_change',
|
||||
'withdrawals',
|
||||
]}
|
||||
capella_mods = combine_mods(_new_capella_mods, bellatrix_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',
|
||||
@@ -38,6 +45,7 @@ if __name__ == "__main__":
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="operations", all_mods=all_mods)
|
||||
|
||||
@@ -5,6 +5,8 @@ all:
|
||||
rm -f ../../core/pyspec/eth2spec/test/phase0/random/test_random.py
|
||||
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
|
||||
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
|
||||
|
||||
@@ -20,9 +20,11 @@ from eth2spec.test.utils.randomized_block_tests import (
|
||||
randomize_state,
|
||||
randomize_state_altair,
|
||||
randomize_state_bellatrix,
|
||||
randomize_state_capella,
|
||||
random_block,
|
||||
random_block_altair_with_cycling_sync_committee_participation,
|
||||
random_block_bellatrix,
|
||||
random_block_capella,
|
||||
last_slot_in_epoch,
|
||||
random_slot_in_epoch,
|
||||
penultimate_slot_in_epoch,
|
||||
@@ -32,7 +34,7 @@ from eth2spec.test.utils.randomized_block_tests import (
|
||||
transition_to_leaking,
|
||||
transition_without_leak,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
# Ensure this many blocks are present in *each* randomized scenario
|
||||
@@ -263,5 +265,12 @@ if __name__ == "__main__":
|
||||
state_randomizer=randomize_state_bellatrix,
|
||||
block_randomizer=random_block_bellatrix,
|
||||
)
|
||||
if CAPELLA in sys.argv:
|
||||
did_generate = True
|
||||
run_generate_tests_to_std_out(
|
||||
CAPELLA,
|
||||
state_randomizer=randomize_state_capella,
|
||||
block_randomizer=random_block_capella,
|
||||
)
|
||||
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
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
|
||||
|
||||
@@ -12,11 +12,15 @@ if __name__ == "__main__":
|
||||
bellatrix_mods = {key: 'eth2spec.test.bellatrix.random.test_' + key for key in [
|
||||
'random',
|
||||
]}
|
||||
capella_mods = {key: 'eth2spec.test.capella.random.test_' + key for key in [
|
||||
'random',
|
||||
]}
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_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
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -15,11 +15,13 @@ if __name__ == "__main__":
|
||||
# Note: Block rewards are non-epoch rewards and are tested as part of block processing tests.
|
||||
# Transaction fees are part of the execution-layer.
|
||||
bellatrix_mods = altair_mods
|
||||
capella_mods = bellatrix_mods
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_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
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
|
||||
|
||||
|
||||
@@ -18,10 +18,16 @@ if __name__ == "__main__":
|
||||
]}
|
||||
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
|
||||
|
||||
_new_capella_mods = {key: 'eth2spec.test.capella.sanity.test_' + key for key in [
|
||||
'blocks',
|
||||
]}
|
||||
capella_mods = combine_mods(_new_capella_mods, bellatrix_mods)
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="sanity", all_mods=all_mods)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
from eth2spec.test.helpers.constants import BELLATRIX
|
||||
from eth2spec.test.helpers.constants import BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bellatrix_mods = {key: 'eth2spec.test.bellatrix.sync.test_' + key for key in [
|
||||
'optimistic',
|
||||
]}
|
||||
capella_mods = bellatrix_mods
|
||||
|
||||
all_mods = {
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="sync", all_mods=all_mods)
|
||||
|
||||
Reference in New Issue
Block a user