Merge branch 'dev' into pr3052

This commit is contained in:
Hsiao-Wei Wang 2022-11-07 10:10:16 -05:00
commit 95ee291f58
No known key found for this signature in database
GPG Key ID: AE3D6B174F971DE4
58 changed files with 1205 additions and 355 deletions

View File

@ -41,6 +41,8 @@ CURRENT_DIR = ${CURDIR}
LINTER_CONFIG_FILE = $(CURRENT_DIR)/linter.ini
GENERATOR_ERROR_LOG_FILE = $(CURRENT_DIR)/$(TEST_VECTOR_DIR)/testgen_error_log.txt
SCRIPTS_DIR = ${CURRENT_DIR}/scripts
export DAPP_SKIP_BUILD:=1
export DAPP_SRC:=$(SOLIDITY_DEPOSIT_CONTRACT_DIR)
export DAPP_LIB:=$(SOLIDITY_DEPOSIT_CONTRACT_DIR)/lib
@ -64,6 +66,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
@ -193,6 +197,14 @@ $(TEST_VECTOR_DIR):
$(TEST_VECTOR_DIR)/:
$(info ignoring duplicate tests dir)
gen_kzg_setups:
cd $(SCRIPTS_DIR); \
if ! test -d venv; then python3 -m venv venv; fi; \
. venv/bin/activate; \
pip3 install -r requirements.txt; \
python3 ./gen_kzg_trusted_setups.py --secret=1337 --length=4 --output-dir ${CURRENT_DIR}/presets/minimal/trusted_setups; \
python3 ./gen_kzg_trusted_setups.py --secret=1337 --length=4096 --output-dir ${CURRENT_DIR}/presets/mainnet/trusted_setups
# For any generator, build it using the run_generator function.
# (creation of output dir is a dependency)
gen_%: $(TEST_VECTOR_DIR)

View File

@ -2,7 +2,7 @@
[![Join the chat at https://discord.gg/qGpsxSA](https://img.shields.io/badge/chat-on%20discord-blue.svg)](https://discord.gg/qGpsxSA) [![Join the chat at https://gitter.im/ethereum/sharding](https://badges.gitter.im/ethereum/sharding.svg)](https://gitter.im/ethereum/sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
To learn more about proof-of-stake and sharding, see the [PoS FAQ](https://eth.wiki/en/concepts/proof-of-stake-faqs), [sharding FAQ](https://eth.wiki/sharding/Sharding-FAQs) and the [research compendium](https://notes.ethereum.org/s/H1PGqDhpm).
To learn more about proof-of-stake and sharding, see the [PoS documentation](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/), [sharding documentation](https://ethereum.org/en/upgrades/sharding/) and the [research compendium](https://notes.ethereum.org/s/H1PGqDhpm).
This repository hosts the current Ethereum proof-of-stake specifications. Discussions about design rationale and proposed changes can be brought up and discussed as issues. Solidified, agreed-upon changes to the spec can be made through pull requests.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"setup_G1": ["0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", "0x854262641262cb9e056a8512808ea6864d903dbcad713fd6da8dddfa5ce40d85612c912063ace060ed8c4bf005bab839", "0x86f708eee5ae0cf40be36993e760d9cb3b2371f22db3209947c5d21ea68e55186b30871c50bf11ef29e5248bf42d5678", "0x94f9c0bafb23cbbf34a93a64243e3e0f934b57593651f3464de7dc174468123d9698f1b9dfa22bb5b6eb96eae002f29f"], "setup_G2": ["0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", "0x99aca9fb2f7760cecb892bf7262c176b334824f5727f680bba701a33e322cb6667531410dfc7c8e4321a3f0ea8af48cb1436638a2093123f046f0f504cc2a864825542873edbbc5d7ed17af125a4f2cf6433c6f4f61b81173726981dd989761d", "0x88e2e982982bf8231e747e9dfcd14c05bd02623d1332734d2af26246c6869fb56ee6c994843f593178a040495ba61f4a083b0e18110b1d9f5224783d8f9a895e8ee744e87929430e9ba96bd29251cbf61240b256d1525600f3d562894d93d659", "0xa2d33775e3d9e6af0d1b27d389e6c021a578e617a3d6627686db6288d4b3dffd7a847a00f7ef01828b7f42885b660e4204923402aca18fbae74ccd4e9c50dd8c2281b38dc09c022342ed1ac695d53f7081cb21f05fdfc0a3508c04759196fcd3"], "setup_G1_lagrange": ["0x91131b2e3c1e5f0b51df8970e67080032f411571b66d301436c46f25bbfddf9ca16756430dc470bdb0d85b47fedcdbc1", "0x934d35b2a46e169915718b77127b0d4efbacdad7fdde4593af7d21d37ebcb77fe6c8dde6b8a9537854d70ef1f291a585", "0x9410ca1d0342fe7419f02194281df45e1c1ff42fd8b439de5644cc312815c21ddd2e3eeb63fb807cf837e68b76668bd5", "0xb163df7e9baeb60f69b6ee5faa538c3a564b62eb8cde6a3616083c8cb2171eedd583c9143e7e916df59bf27da5e024e8"], "roots_of_unity": [1, 3465144826073652318776269530687742778270252468765361963008, 52435875175126190479447740508185965837690552500527637822603658699938581184512, 52435875175126190475982595682112313518914282969839895044333406231173219221505]}

View File

@ -0,0 +1,36 @@
import os
from pathlib import Path
from eth2spec.utils.kzg import (
dump_kzg_trusted_setup_files,
)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument(
"--secret",
dest="secret",
type=int,
required=True,
help='the secret of trusted setup',
)
parser.add_argument(
"--length",
dest="length",
type=int,
required=True,
help='the length of trusted setup',
)
parser.add_argument(
"-o",
"--output-dir",
dest="output_dir",
required=True,
help='the output directory',
)
args = parser.parse_args()
dump_kzg_trusted_setup_files(args.secret, args.length, args.output_dir)

1
scripts/requirements.txt Normal file
View File

@ -0,0 +1 @@
../[generator]

View File

@ -7,13 +7,14 @@ import os
import re
import string
import textwrap
from typing import Dict, NamedTuple, List, Sequence, Optional, TypeVar
from typing import Dict, NamedTuple, List, Sequence, Optional, TypeVar, Tuple
from abc import ABC, abstractmethod
import ast
import subprocess
import sys
import copy
from collections import OrderedDict
import json
# NOTE: have to programmatically include third-party dependencies in `setup.py`.
@ -121,7 +122,7 @@ def _get_self_type_from_source(source: str) -> Optional[str]:
return args[0].annotation.id
def _get_class_info_from_source(source: str) -> (str, Optional[str]):
def _get_class_info_from_source(source: str) -> Tuple[str, Optional[str]]:
class_def = ast.parse(source).body[0]
base = class_def.bases[0]
if isinstance(base, ast.Name):
@ -140,6 +141,28 @@ def _is_constant_id(name: str) -> bool:
return all(map(lambda c: c in string.ascii_uppercase + '_' + string.digits, name[1:]))
def _load_kzg_trusted_setups(preset_name):
"""
[TODO] it's not the final mainnet trusted setup.
We will update it after the KZG ceremony.
"""
file_path = str(Path(__file__).parent) + '/presets/' + preset_name + '/trusted_setups/testing_trusted_setups.json'
with open(file_path, 'r') as f:
json_data = json.load(f)
trusted_setup_G1 = json_data['setup_G1']
trusted_setup_G2 = json_data['setup_G2']
trusted_setup_G1_lagrange = json_data['setup_G1_lagrange']
roots_of_unity = json_data['roots_of_unity']
return trusted_setup_G1, trusted_setup_G2, trusted_setup_G1_lagrange, roots_of_unity
ALL_KZG_SETUPS = {
'minimal': _load_kzg_trusted_setups('minimal'),
'mainnet': _load_kzg_trusted_setups('mainnet')
}
ETH2_SPEC_COMMENT_PREFIX = "eth2spec:"
@ -167,7 +190,16 @@ def _parse_value(name: str, typed_value: str, type_hint: Optional[str]=None) ->
return VariableDefinition(type_name=type_name, value=typed_value[i+1:-1], comment=comment, type_hint=type_hint)
def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str]) -> SpecObject:
def _update_constant_vars_with_kzg_setups(constant_vars, preset_name):
comment = "noqa: E501"
kzg_setups = ALL_KZG_SETUPS[preset_name]
constant_vars['KZG_SETUP_G1'] = VariableDefinition(constant_vars['KZG_SETUP_G1'].value, str(kzg_setups[0]), comment, None)
constant_vars['KZG_SETUP_G2'] = VariableDefinition(constant_vars['KZG_SETUP_G2'].value, str(kzg_setups[1]), comment, None)
constant_vars['KZG_SETUP_LAGRANGE'] = VariableDefinition(constant_vars['KZG_SETUP_LAGRANGE'].value, str(kzg_setups[2]), comment, None)
constant_vars['ROOTS_OF_UNITY'] = VariableDefinition(constant_vars['ROOTS_OF_UNITY'].value, str(kzg_setups[3]), comment, None)
def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], preset_name=str) -> SpecObject:
functions: Dict[str, str] = {}
protocols: Dict[str, ProtocolDefinition] = {}
constant_vars: Dict[str, VariableDefinition] = {}
@ -232,7 +264,7 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str]) ->
if not _is_constant_id(name):
# Check for short type declarations
if value.startswith(("uint", "Bytes", "ByteList", "Union", "Vector", "List")):
if value.startswith(("uint", "Bytes", "ByteList", "Union", "Vector", "List", "ByteVector")):
custom_types[name] = value
continue
@ -256,6 +288,10 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str]) ->
if comment == "skip":
should_skip = True
# Load KZG trusted setup from files
if any('KZG_SETUP' in name for name in constant_vars):
_update_constant_vars_with_kzg_setups(constant_vars, preset_name)
return SpecObject(
functions=functions,
protocols=protocols,
@ -590,7 +626,6 @@ class EIP4844SpecBuilder(CapellaSpecBuilder):
return super().imports(preset_name) + f'''
from eth2spec.utils import kzg
from eth2spec.capella import {preset_name} as capella
from eth2spec.utils.ssz.ssz_impl import serialize as ssz_serialize
'''
@ -645,19 +680,19 @@ get_expected_withdrawals = get_empty_list_result(get_expected_withdrawals)
# End
#
def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> BlobsSidecar:
pass'''
def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> Optional[BlobsSidecar]:
return "TEST"'''
@classmethod
def hardcoded_custom_type_dep_constants(cls, spec_object) -> str:
constants = {
'BYTES_PER_FIELD_ELEMENT': spec_object.constant_vars['BYTES_PER_FIELD_ELEMENT'].value,
'FIELD_ELEMENTS_PER_BLOB': spec_object.preset_vars['FIELD_ELEMENTS_PER_BLOB'].value,
'MAX_BLOBS_PER_BLOCK': spec_object.preset_vars['MAX_BLOBS_PER_BLOCK'].value,
}
return {**super().hardcoded_custom_type_dep_constants(spec_object), **constants}
spec_builders = {
builder.fork: builder
for builder in (Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, EIP4844SpecBuilder)
@ -908,7 +943,7 @@ def _build_spec(preset_name: str, fork: str,
source_files: Sequence[Path], preset_files: Sequence[Path], config_file: Path) -> str:
preset = load_preset(preset_files)
config = load_config(config_file)
all_specs = [get_spec(spec, preset, config) for spec in source_files]
all_specs = [get_spec(spec, preset, config, preset_name) for spec in source_files]
spec_object = all_specs[0]
for value in all_specs[1:]:
@ -1159,7 +1194,7 @@ setup(
"pycryptodome==3.15.0",
"py_ecc==6.0.0",
"milagro_bls_binding==1.9.0",
"remerkleable==0.1.24",
"remerkleable==0.1.25",
RUAMEL_YAML_VERSION,
"lru-dict==1.1.8",
MARKO_VERSION,

View File

@ -84,13 +84,13 @@ def create_light_client_update(state: BeaconState,
header = state.latest_block_header.copy()
header.state_root = hash_tree_root(state)
assert hash_tree_root(header) == hash_tree_root(block.message)
update_signature_period = compute_sync_committee_period(compute_epoch_at_slot(block.message.slot))
update_signature_period = compute_sync_committee_period_at_slot(block.message.slot)
assert attested_state.slot == attested_state.latest_block_header.slot
attested_header = attested_state.latest_block_header.copy()
attested_header.state_root = hash_tree_root(attested_state)
assert hash_tree_root(attested_header) == block.message.parent_root
update_attested_period = compute_sync_committee_period(compute_epoch_at_slot(attested_header.slot))
update_attested_period = compute_sync_committee_period_at_slot(attested_header.slot)
# `next_sync_committee` is only useful if the message is signed by the current sync committee
if update_attested_period == update_signature_period:
@ -133,7 +133,7 @@ def create_light_client_update(state: BeaconState,
Full nodes SHOULD provide the best derivable `LightClientUpdate` (according to `is_better_update`) for each sync committee period covering any epochs in range `[max(ALTAIR_FORK_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch]` where `current_epoch` is defined by the current wall-clock time. Full nodes MAY also provide `LightClientUpdate` for other sync committee periods.
- `LightClientUpdate` are assigned to sync committee periods based on their `attested_header.slot`
- `LightClientUpdate` are only considered if `compute_sync_committee_period(compute_epoch_at_slot(update.attested_header.slot)) == compute_sync_committee_period(compute_epoch_at_slot(update.signature_slot))`
- `LightClientUpdate` are only considered if `compute_sync_committee_period_at_slot(update.attested_header.slot) == compute_sync_committee_period_at_slot(update.signature_slot)`
- Only `LightClientUpdate` with `next_sync_committee` as selected by fork choice are provided, regardless of ranking by `is_better_update`. To uniquely identify a non-finalized sync committee fork, all of `period`, `current_sync_committee` and `next_sync_committee` need to be incorporated, as sync committees may reappear over time.
### `create_light_client_finality_update`

View File

@ -71,6 +71,17 @@ For light clients, the following validations MUST additionally pass before forwa
Light clients SHOULD call `process_light_client_finality_update` even if the message is ignored.
The gossip `ForkDigest`-context is determined based on `compute_fork_version(compute_epoch_at_slot(finality_update.attested_header.slot))`.
Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
[0]: # (eth2spec: skip)
| `fork_version` | Message SSZ type |
| ------------------------------- | ------------------------------------ |
| `GENESIS_FORK_VERSION` | n/a |
| `ALTAIR_FORK_VERSION` and later | `altair.LightClientFinalityUpdate` |
###### `light_client_optimistic_update`
This topic is used to propagate the latest `LightClientOptimisticUpdate` to light clients, allowing them to keep track of the latest `optimistic_header`.
@ -88,6 +99,17 @@ For light clients, the following validations MUST additionally pass before forwa
Light clients SHOULD call `process_light_client_optimistic_update` even if the message is ignored.
The gossip `ForkDigest`-context is determined based on `compute_fork_version(compute_epoch_at_slot(optimistic_update.attested_header.slot))`.
Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
[0]: # (eth2spec: skip)
| `fork_version` | Message SSZ type |
| ------------------------------- | ------------------------------------ |
| `GENESIS_FORK_VERSION` | n/a |
| `ALTAIR_FORK_VERSION` and later | `altair.LightClientOptimisticUpdate` |
### The Req/Resp domain
#### Messages

View File

@ -77,9 +77,9 @@ Additional documents describe how the light client sync protocol can be used:
```python
class LightClientBootstrap(Container):
# The requested beacon block header
# Header matching the requested beacon block root
header: BeaconBlockHeader
# Current sync committee corresponding to `header`
# Current sync committee corresponding to `header.state_root`
current_sync_committee: SyncCommittee
current_sync_committee_branch: Vector[Bytes32, floorlog2(CURRENT_SYNC_COMMITTEE_INDEX)]
```
@ -88,12 +88,12 @@ class LightClientBootstrap(Container):
```python
class LightClientUpdate(Container):
# The beacon block header that is attested to by the sync committee
# Header attested to by the sync committee
attested_header: BeaconBlockHeader
# Next sync committee corresponding to `attested_header`
# Next sync committee corresponding to `attested_header.state_root`
next_sync_committee: SyncCommittee
next_sync_committee_branch: Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_INDEX)]
# The finalized beacon block header attested to by Merkle branch
# Finalized header corresponding to `attested_header.state_root`
finalized_header: BeaconBlockHeader
finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)]
# Sync committee aggregate signature
@ -106,9 +106,9 @@ class LightClientUpdate(Container):
```python
class LightClientFinalityUpdate(Container):
# The beacon block header that is attested to by the sync committee
# Header attested to by the sync committee
attested_header: BeaconBlockHeader
# The finalized beacon block header attested to by Merkle branch
# Finalized header corresponding to `attested_header.state_root`
finalized_header: BeaconBlockHeader
finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)]
# Sync committee aggregate signature
@ -121,7 +121,7 @@ class LightClientFinalityUpdate(Container):
```python
class LightClientOptimisticUpdate(Container):
# The beacon block header that is attested to by the sync committee
# Header attested to by the sync committee
attested_header: BeaconBlockHeader
# Sync committee aggregate signature
sync_aggregate: SyncAggregate
@ -134,9 +134,9 @@ class LightClientOptimisticUpdate(Container):
```python
@dataclass
class LightClientStore(object):
# Beacon block header that is finalized
# Header that is finalized
finalized_header: BeaconBlockHeader
# Sync committees corresponding to the header
# Sync committees corresponding to the finalized header
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
# Best available header to switch finalized head to if we see nothing else

View File

@ -110,7 +110,7 @@ The following gossip validation from prior specifications MUST NOT be applied if
### Transitioning the gossip
See gossip transition details found in the [Altair document](../altair/p2p-interface.md#transitioning-the-gossip) for
details on how to handle transitioning gossip topics for EIP-4844.
details on how to handle transitioning gossip topics.
## The Req/Resp domain

View File

@ -110,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
```
@ -258,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,
)

View File

@ -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,
@ -110,7 +127,7 @@ 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),

View File

@ -0,0 +1,110 @@
# Capella -- Networking
This document contains the networking specification for Capella.
The specification of these changes continues in the same format as the network specifications of previous upgrades, and assumes them as pre-requisite.
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Modifications in Capella](#modifications-in-capella)
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
- [Topics and messages](#topics-and-messages)
- [Global topics](#global-topics)
- [`beacon_block`](#beacon_block)
- [`bls_to_execution_change`](#bls_to_execution_change)
- [Transitioning the gossip](#transitioning-the-gossip)
- [The Req/Resp domain](#the-reqresp-domain)
- [Messages](#messages)
- [BeaconBlocksByRange v2](#beaconblocksbyrange-v2)
- [BeaconBlocksByRoot v2](#beaconblocksbyroot-v2)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
# Modifications in Capella
## The gossip domain: gossipsub
A new topic is added to support the gossip of withdrawal credential change messages. And an existing topic is upgraded for updated types in Capella.
### Topics and messages
Topics follow the same specification as in prior upgrades. All existing topics remain stable except the beacon block topic which is updated with the modified type.
The new topics along with the type of the `data` field of a gossipsub message are given in this table:
| Name | Message Type |
| - | - |
| `beacon_block` | `SignedBeaconBlock` (modified) |
| `bls_to_execution_change` | `SignedBLSToExecutionChange` |
Note that the `ForkDigestValue` path segment of the topic separates the old and the new `beacon_block` topics.
#### Global topics
Capella changes the type of the global beacon block topic and adds one global topic to propagate withdrawal credential change messages to all potential proposers of beacon blocks.
##### `beacon_block`
The *type* of the payload of this topic changes to the (modified) `SignedBeaconBlock` found in Capella.
Specifically, this type changes with the addition of `bls_to_execution_changes` to the inner `BeaconBlockBody`.
See Capella [state transition document](./beacon-chain.md#beaconblockbody) for further details.
##### `bls_to_execution_change`
This topic is used to propagate signed bls to execution change messages to be included in future blocks.
The following validations MUST pass before forwarding the `signed_bls_to_execution_change` on the network:
- _[IGNORE]_ The `signed_bls_to_execution_change` is the first valid signed bls to execution change received
for the validator with index `signed_bls_to_execution_change.message.validator_index`.
- _[REJECT]_ All of the conditions within `process_bls_to_execution_change` pass validation.
### Transitioning the gossip
See gossip transition details found in the [Altair document](../altair/p2p-interface.md#transitioning-the-gossip) for
details on how to handle transitioning gossip topics for Capella.
## The Req/Resp domain
### Messages
#### BeaconBlocksByRange v2
**Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_range/2/`
The Capella fork-digest is introduced to the `context` enum to specify Capella block type.
Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
[0]: # (eth2spec: skip)
| `fork_version` | Chunk SSZ type |
| ------------------------ | -------------------------- |
| `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` |
| `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` |
| `BELLATRIX_FORK_VERSION` | `bellatrix.SignedBeaconBlock` |
| `CAPELLA_FORK_VERSION` | `capella.SignedBeaconBlock` |
#### BeaconBlocksByRoot v2
**Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_root/2/`
The Capella fork-digest is introduced to the `context` enum to specify Capella block type.
Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
[1]: # (eth2spec: skip)
| `fork_version` | Chunk SSZ type |
| ------------------------ | -------------------------- |
| `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` |
| `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` |
| `BELLATRIX_FORK_VERSION` | `bellatrix.SignedBeaconBlock` |
| `CAPELLA_FORK_VERSION` | `capella.SignedBeaconBlock` |

View File

@ -18,6 +18,7 @@
- [Block proposal](#block-proposal)
- [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody)
- [ExecutionPayload](#executionpayload)
- [BLS to execution changes](#bls-to-execution-changes)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
@ -106,3 +107,7 @@ def prepare_execution_payload(state: BeaconState,
payload_attributes=payload_attributes,
)
```
##### BLS to execution changes
Up to `MAX_BLS_TO_EXECUTION_CHANGES`, [`BLSToExecutionChange`](./beacon-chain.md#blstoexecutionchange) objects can be included in the `block`. The BLS to execution changes must satisfy the verification conditions found in [BLS to execution change processing](./beacon-chain.md#new-process_bls_to_execution_change).

View File

@ -23,6 +23,8 @@
- [`ExecutionPayloadHeader`](#executionpayloadheader)
- [Helper functions](#helper-functions)
- [Misc](#misc)
- [`validate_blobs_sidecar`](#validate_blobs_sidecar)
- [`is_data_available`](#is_data_available)
- [`kzg_commitment_to_versioned_hash`](#kzg_commitment_to_versioned_hash)
- [`tx_peek_blob_versioned_hashes`](#tx_peek_blob_versioned_hashes)
- [`verify_kzg_commitments_against_transactions`](#verify_kzg_commitments_against_transactions)
@ -45,9 +47,7 @@ This upgrade adds blobs to the beacon chain as part of EIP-4844. This is an exte
| Name | SSZ equivalent | Description |
| - | - | - |
| `Blob` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | |
| `VersionedHash` | `Bytes32` | |
| `KZGCommitment` | `Bytes48` | Same as BLS standard "is valid pubkey" check but also allows `0x00..00` for point-at-infinity |
## Constants
@ -56,7 +56,6 @@ This upgrade adds blobs to the beacon chain as part of EIP-4844. This is an exte
| Name | Value |
| - | - |
| `BLOB_TX_TYPE` | `uint8(0x05)` |
| `FIELD_ELEMENTS_PER_BLOB` | `uint64(4096)` |
| `VERSIONED_HASH_VERSION_KZG` | `Bytes1(0x01)` |
### Domain types
@ -154,6 +153,43 @@ class ExecutionPayloadHeader(Container):
### Misc
#### `validate_blobs_sidecar`
```python
def validate_blobs_sidecar(slot: Slot,
beacon_block_root: Root,
expected_kzg_commitments: Sequence[KZGCommitment],
blobs_sidecar: BlobsSidecar) -> None:
assert slot == blobs_sidecar.beacon_block_slot
assert beacon_block_root == blobs_sidecar.beacon_block_root
blobs = blobs_sidecar.blobs
kzg_aggregated_proof = blobs_sidecar.kzg_aggregated_proof
assert len(expected_kzg_commitments) == len(blobs)
assert verify_aggregate_kzg_proof(blobs, expected_kzg_commitments, kzg_aggregated_proof)
```
#### `is_data_available`
The implementation of `is_data_available` is meant to change with later sharding upgrades.
Initially, it requires every verifying actor to retrieve the matching `BlobsSidecar`,
and validate the sidecar with `validate_blobs_sidecar`.
Without the sidecar the block may be processed further optimistically,
but MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded.
```python
def is_data_available(slot: Slot, beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool:
# `retrieve_blobs_sidecar` is implementation dependent, raises an exception if not available.
sidecar = retrieve_blobs_sidecar(slot, beacon_block_root)
if sidecar == "TEST":
return True # For testing; remove once we have a way to inject `BlobsSidecar` into tests
validate_blobs_sidecar(slot, beacon_block_root, blob_kzg_commitments, sidecar)
return True
```
#### `kzg_commitment_to_versioned_hash`
```python
@ -171,10 +207,10 @@ See [the full details of `blob_versioned_hashes` offset calculation](https://gis
def tx_peek_blob_versioned_hashes(opaque_tx: Transaction) -> Sequence[VersionedHash]:
assert opaque_tx[0] == BLOB_TX_TYPE
message_offset = 1 + uint32.decode_bytes(opaque_tx[1:5])
# field offset: 32 + 8 + 32 + 32 + 8 + 4 + 32 + 4 + 4 = 156
# field offset: 32 + 8 + 32 + 32 + 8 + 4 + 32 + 4 + 4 + 32 = 188
blob_versioned_hashes_offset = (
message_offset
+ uint32.decode_bytes(opaque_tx[(message_offset + 156):(message_offset + 160)])
+ uint32.decode_bytes(opaque_tx[(message_offset + 188):(message_offset + 192)])
)
return [
VersionedHash(opaque_tx[x:(x + 32)])
@ -209,6 +245,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_operations(state, block.body)
process_sync_aggregate(state, block.body.sync_aggregate)
process_blob_kzg_commitments(state, block.body) # [New in EIP-4844]
# New in EIP-4844, note: Can sync optimistically without this condition, see note on `is_data_available`
assert is_data_available(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments)
```
#### Execution payload

View File

@ -13,12 +13,11 @@ The specification of these changes continues in the same format as the network s
- [Configuration](#configuration)
- [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 +32,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 epochs, ~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
@ -50,12 +49,12 @@ class BlobsSidecar(Container):
kzg_aggregated_proof: KZGProof
```
### `SignedBlobsSidecar`
### `SignedBeaconBlockAndBlobsSidecar`
```python
class SignedBlobsSidecar(Container):
message: BlobsSidecar
signature: BLSSignature
class SignedBeaconBlockAndBlobsSidecar(Container):
beacon_block: SignedBeaconBlock
blobs_sidecar: BlobsSidecar
```
## The gossip domain: gossipsub
@ -75,47 +74,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

View File

@ -10,6 +10,8 @@
- [Custom types](#custom-types)
- [Constants](#constants)
- [Preset](#preset)
- [Blob](#blob)
- [Crypto](#crypto)
- [Trusted setup](#trusted-setup)
- [Helper functions](#helper-functions)
- [Bit-reversal permutation](#bit-reversal-permutation)
@ -18,16 +20,22 @@
- [`bit_reversal_permutation`](#bit_reversal_permutation)
- [BLS12-381 helpers](#bls12-381-helpers)
- [`bytes_to_bls_field`](#bytes_to_bls_field)
- [`blob_to_polynomial`](#blob_to_polynomial)
- [`hash_to_bls_field`](#hash_to_bls_field)
- [`bls_modular_inverse`](#bls_modular_inverse)
- [`div`](#div)
- [`g1_lincomb`](#g1_lincomb)
- [`vector_lincomb`](#vector_lincomb)
- [`poly_lincomb`](#poly_lincomb)
- [`compute_powers`](#compute_powers)
- [Polynomials](#polynomials)
- [`evaluate_polynomial_in_evaluation_form`](#evaluate_polynomial_in_evaluation_form)
- [KZG](#kzg)
- [`blob_to_kzg_commitment`](#blob_to_kzg_commitment)
- [`verify_kzg_proof`](#verify_kzg_proof)
- [`compute_kzg_proof`](#compute_kzg_proof)
- [Polynomials](#polynomials)
- [`evaluate_polynomial_in_evaluation_form`](#evaluate_polynomial_in_evaluation_form)
- [`compute_aggregated_poly_and_commitment`](#compute_aggregated_poly_and_commitment)
- [`compute_aggregate_kzg_proof`](#compute_aggregate_kzg_proof)
- [`verify_aggregate_kzg_proof`](#verify_aggregate_kzg_proof)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
@ -46,16 +54,31 @@ This document specifies basic polynomial operations and KZG polynomial commitmen
| `BLSFieldElement` | `uint256` | `x < BLS_MODULUS` |
| `KZGCommitment` | `Bytes48` | Same as BLS standard "is valid pubkey" check but also allows `0x00..00` for point-at-infinity |
| `KZGProof` | `Bytes48` | Same as for `KZGCommitment` |
| `Polynomial` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | a polynomial in evaluation form |
| `Blob` | `ByteVector[BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB]` | a basic blob data |
## Constants
| Name | Value | Notes |
| - | - | - |
| `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` | Scalar field modulus of BLS12-381 |
| `ROOTS_OF_UNITY` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | Roots of unity of order FIELD_ELEMENTS_PER_BLOB over the BLS12-381 field |
| `BYTES_PER_FIELD_ELEMENT` | `uint64(32)` | Bytes used to encode a BLS scalar field element |
## Preset
### Blob
| Name | Value |
| - | - |
| `FIELD_ELEMENTS_PER_BLOB` | `uint64(4096)` |
| `FIAT_SHAMIR_PROTOCOL_DOMAIN` | `b'FSBLOBVERIFY_V1_'` |
### Crypto
| Name | Value | Notes |
| - | - | - |
| `ROOTS_OF_UNITY` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | Roots of unity of order FIELD_ELEMENTS_PER_BLOB over the BLS12-381 field |
### Trusted setup
The trusted setup is part of the preset: during testing a `minimal` insecure variant may be used,
@ -91,7 +114,7 @@ def is_power_of_two(value: int) -> bool:
```python
def reverse_bits(n: int, order: int) -> int:
"""
Reverse the bit order of an integer n
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
@ -117,9 +140,51 @@ def bit_reversal_permutation(sequence: Sequence[T]) -> Sequence[T]:
```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.
Convert 32-byte value to a BLS field scalar. The output is not uniform over the BLS field.
"""
return int.from_bytes(b, "little") % BLS_MODULUS
return int.from_bytes(b, ENDIANNESS) % BLS_MODULUS
```
#### `blob_to_polynomial`
```python
def blob_to_polynomial(blob: Blob) -> Polynomial:
"""
Convert a blob to list of BLS field scalars.
"""
polynomial = Polynomial()
for i in range(FIELD_ELEMENTS_PER_BLOB):
value = int.from_bytes(blob[i * BYTES_PER_FIELD_ELEMENT: (i + 1) * BYTES_PER_FIELD_ELEMENT], ENDIANNESS)
assert value < BLS_MODULUS
polynomial[i] = value
return polynomial
```
#### `hash_to_bls_field`
```python
def hash_to_bls_field(polys: Sequence[Polynomial],
comms: Sequence[KZGCommitment]) -> BLSFieldElement:
"""
Compute 32-byte hash of serialized polynomials and commitments concatenated.
This hash is then converted to a BLS field element, where the result is not uniform over the BLS field.
Return the BLS field element.
"""
# Append the number of polynomials and the degree of each polynomial as a domain separator
num_polys = int.to_bytes(len(polys), 8, ENDIANNESS)
degree_poly = int.to_bytes(FIELD_ELEMENTS_PER_BLOB, 8, ENDIANNESS)
data = FIAT_SHAMIR_PROTOCOL_DOMAIN + degree_poly + num_polys
# Append each polynomial which is composed by field elements
for poly in polys:
for field_element in poly:
data += int.to_bytes(field_element, BYTES_PER_FIELD_ELEMENT, ENDIANNESS)
# Append serialized G1 points
for commitment in comms:
data += commitment
return bytes_to_bls_field(hash(data))
```
#### `bls_modular_inverse`
@ -137,7 +202,9 @@ def bls_modular_inverse(x: BLSFieldElement) -> BLSFieldElement:
```python
def div(x: BLSFieldElement, y: BLSFieldElement) -> BLSFieldElement:
"""Divide two field elements: `x` by `y`"""
"""
Divide two field elements: ``x`` by `y``.
"""
return (int(x) * int(bls_modular_inverse(y))) % BLS_MODULUS
```
@ -155,22 +222,65 @@ def g1_lincomb(points: Sequence[KZGCommitment], scalars: Sequence[BLSFieldElemen
return KZGCommitment(bls.G1_to_bytes48(result))
```
#### `vector_lincomb`
#### `poly_lincomb`
```python
def vector_lincomb(vectors: Sequence[Sequence[BLSFieldElement]],
scalars: Sequence[BLSFieldElement]) -> Sequence[BLSFieldElement]:
def poly_lincomb(polys: Sequence[Polynomial],
scalars: Sequence[BLSFieldElement]) -> Polynomial:
"""
Given a list of ``vectors``, interpret it as a 2D matrix and compute the linear combination
of each column with `scalars`: return the resulting vector.
Given a list of ``polynomials``, interpret it as a 2D matrix and compute the linear combination
of each column with `scalars`: return the resulting polynomials.
"""
result = [0] * len(vectors[0])
for v, s in zip(vectors, scalars):
result = [0] * len(polys[0])
for v, s in zip(polys, scalars):
for i, x in enumerate(v):
result[i] = (result[i] + int(s) * int(x)) % BLS_MODULUS
return [BLSFieldElement(x) for x in result]
```
#### `compute_powers`
```python
def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]:
"""
Return ``x`` to power of [0, n-1].
"""
current_power = 1
powers = []
for _ in range(n):
powers.append(BLSFieldElement(current_power))
current_power = current_power * int(x) % BLS_MODULUS
return powers
```
### Polynomials
#### `evaluate_polynomial_in_evaluation_form`
```python
def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial,
z: BLSFieldElement) -> BLSFieldElement:
"""
Evaluate a polynomial (in evaluation form) at an arbitrary point ``z``.
Uses the barycentric formula:
f(z) = (z**WIDTH - 1) / WIDTH * sum_(i=0)^WIDTH (f(DOMAIN[i]) * DOMAIN[i]) / (z - DOMAIN[i])
"""
width = len(polynomial)
assert width == FIELD_ELEMENTS_PER_BLOB
inverse_width = bls_modular_inverse(width)
# 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_brp[i]), (int(z) - roots_of_unity_brp[i]))
result = result * (pow(z, width, BLS_MODULUS) - 1) * inverse_width % BLS_MODULUS
return result
```
### KZG
KZG core functions. These are also defined in EIP-4844 execution specs.
@ -179,7 +289,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(bit_reversal_permutation(KZG_SETUP_LAGRANGE), blob)
return g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), blob_to_polynomial(blob))
```
#### `verify_kzg_proof`
@ -204,53 +314,81 @@ def verify_kzg_proof(polynomial_kzg: KZGCommitment,
#### `compute_kzg_proof`
```python
def compute_kzg_proof(polynomial: Sequence[BLSFieldElement], z: BLSFieldElement) -> KZGProof:
def compute_kzg_proof(polynomial: Polynomial, z: BLSFieldElement) -> KZGProof:
"""
Compute KZG proof at point `z` with `polynomial` being in evaluation form
Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z)
"""
# To avoid SSZ overflow/underflow, convert element into int
polynomial = [int(i) for i in polynomial]
z = int(z)
# Shift our polynomial first (in evaluation form we can't handle the division remainder)
y = evaluate_polynomial_in_evaluation_form(polynomial, z)
polynomial_shifted = [(p - int(y)) % BLS_MODULUS for p in polynomial]
# Make sure we won't divide by zero during division
assert z not in ROOTS_OF_UNITY
denominator_poly = [(x - z) % BLS_MODULUS for x in bit_reversal_permutation(ROOTS_OF_UNITY)]
denominator_poly = [(int(x) - z) % BLS_MODULUS for x in bit_reversal_permutation(ROOTS_OF_UNITY)]
# Calculate quotient polynomial by doing point-by-point division
quotient_polynomial = [div(a, b) for a, b in zip(polynomial_shifted, denominator_poly)]
return KZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), quotient_polynomial))
```
### Polynomials
#### `evaluate_polynomial_in_evaluation_form`
#### `compute_aggregated_poly_and_commitment`
```python
def evaluate_polynomial_in_evaluation_form(polynomial: Sequence[BLSFieldElement],
z: BLSFieldElement) -> BLSFieldElement:
def compute_aggregated_poly_and_commitment(
blobs: Sequence[Blob],
kzg_commitments: Sequence[KZGCommitment]) -> Tuple[Polynomial, KZGCommitment, BLSFieldElement]:
"""
Evaluate a polynomial (in evaluation form) at an arbitrary point `z`
Uses the barycentric formula:
f(z) = (1 - z**WIDTH) / WIDTH * sum_(i=0)^WIDTH (f(DOMAIN[i]) * DOMAIN[i]) / (z - DOMAIN[i])
Return (1) the aggregated polynomial, (2) the aggregated KZG commitment,
and (3) the polynomial evaluation random challenge.
"""
width = len(polynomial)
assert width == FIELD_ELEMENTS_PER_BLOB
inverse_width = bls_modular_inverse(width)
# Convert blobs to polynomials
polynomials = [blob_to_polynomial(blob) for blob in blobs]
# Make sure we won't divide by zero during division
assert z not in ROOTS_OF_UNITY
# Generate random linear combination challenges
r = hash_to_bls_field(polynomials, kzg_commitments)
r_powers = compute_powers(r, len(kzg_commitments))
evaluation_challenge = int(r_powers[-1]) * r % BLS_MODULUS
roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY)
# Create aggregated polynomial in evaluation form
aggregated_poly = Polynomial(poly_lincomb(polynomials, r_powers))
result = 0
for i in range(width):
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
# Compute commitment to aggregated polynomial
aggregated_poly_commitment = KZGCommitment(g1_lincomb(kzg_commitments, r_powers))
return aggregated_poly, aggregated_poly_commitment, evaluation_challenge
```
#### `compute_aggregate_kzg_proof`
```python
def compute_aggregate_kzg_proof(blobs: Sequence[Blob]) -> KZGProof:
commitments = [blob_to_kzg_commitment(blob) for blob in blobs]
aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment(
blobs,
commitments
)
return compute_kzg_proof(aggregated_poly, evaluation_challenge)
```
#### `verify_aggregate_kzg_proof`
```python
def verify_aggregate_kzg_proof(blobs: Sequence[Blob],
expected_kzg_commitments: Sequence[KZGCommitment],
kzg_aggregated_proof: KZGCommitment) -> bool:
aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment(
blobs,
expected_kzg_commitments,
)
# Evaluate aggregated polynomial at `evaluation_challenge` (evaluation function checks for div-by-zero)
y = evaluate_polynomial_in_evaluation_form(aggregated_poly, evaluation_challenge)
# Verify aggregated proof
return verify_kzg_proof(aggregated_poly_commitment, evaluation_challenge, y, kzg_aggregated_proof)
```

View File

@ -10,26 +10,17 @@
- [Introduction](#introduction)
- [Prerequisites](#prerequisites)
- [Custom types](#custom-types)
- [Containers](#containers)
- [`BlobsAndCommitments`](#blobsandcommitments)
- [`PolynomialAndCommitment`](#polynomialandcommitment)
- [Helpers](#helpers)
- [Protocols](#protocols)
- [`ExecutionEngine`](#executionengine)
- [`get_payload`](#get_payload)
- [`is_data_available`](#is_data_available)
- [`hash_to_bls_field`](#hash_to_bls_field)
- [`compute_powers`](#compute_powers)
- [`compute_aggregated_poly_and_commitment`](#compute_aggregated_poly_and_commitment)
- [`validate_blobs_sidecar`](#validate_blobs_sidecar)
- [`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 -->
@ -46,31 +37,6 @@ All behaviors and definitions defined in this document, and documents it extends
All terminology, constants, functions, and protocol mechanics defined in the updated [Beacon Chain doc of EIP4844](./beacon-chain.md) are requisite for this document and used throughout.
Please see related Beacon Chain doc before continuing and use them as a reference throughout.
## Custom types
| Name | SSZ equivalent | Description |
| - | - | - |
| `Polynomial` | `List[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | a polynomial in evaluation form |
## Containers
### `BlobsAndCommitments`
```python
class BlobsAndCommitments(Container):
blobs: List[Blob, MAX_BLOBS_PER_BLOCK]
kzg_commitments: List[KZGCommitment, MAX_BLOBS_PER_BLOCK]
```
### `PolynomialAndCommitment`
```python
class PolynomialAndCommitment(Container):
polynomial: Polynomial
kzg_commitment: KZGCommitment
```
## Helpers
## Protocols
@ -81,131 +47,12 @@ class PolynomialAndCommitment(Container):
`get_payload` returns the upgraded EIP-4844 `ExecutionPayload` type.
### `is_data_available`
The implementation of `is_data_available` is meant to change with later sharding upgrades.
Initially, it requires every verifying actor to retrieve the matching `BlobsSidecar`,
and validate the sidecar with `validate_blobs_sidecar`.
Without the sidecar the block may be processed further optimistically,
but MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded.
```python
def is_data_available(slot: Slot, beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool:
# `retrieve_blobs_sidecar` is implementation dependent, raises an exception if not available.
sidecar = retrieve_blobs_sidecar(slot, beacon_block_root)
validate_blobs_sidecar(slot, beacon_block_root, blob_kzg_commitments, sidecar)
return True
```
### `hash_to_bls_field`
```python
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 bytes_to_bls_field(hash(ssz_serialize(x)))
```
### `compute_powers`
```python
def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]:
"""
Return ``x`` to power of [0, n-1].
"""
current_power = 1
powers = []
for _ in range(n):
powers.append(BLSFieldElement(current_power))
current_power = current_power * int(x) % BLS_MODULUS
return powers
```
### `compute_aggregated_poly_and_commitment`
```python
def compute_aggregated_poly_and_commitment(
blobs: Sequence[Blob],
kzg_commitments: Sequence[KZGCommitment]) -> Tuple[Polynomial, KZGCommitment]:
"""
Return the aggregated polynomial and aggregated KZG commitment.
"""
# Generate random linear combination challenges
r = hash_to_bls_field(BlobsAndCommitments(blobs=blobs, kzg_commitments=kzg_commitments))
r_powers = compute_powers(r, len(kzg_commitments))
# Create aggregated polynomial in evaluation form
aggregated_poly = Polynomial(vector_lincomb(blobs, r_powers))
# Compute commitment to aggregated polynomial
aggregated_poly_commitment = KZGCommitment(g1_lincomb(kzg_commitments, r_powers))
return aggregated_poly, aggregated_poly_commitment
```
### `validate_blobs_sidecar`
```python
def validate_blobs_sidecar(slot: Slot,
beacon_block_root: Root,
expected_kzg_commitments: Sequence[KZGCommitment],
blobs_sidecar: BlobsSidecar) -> None:
assert slot == blobs_sidecar.beacon_block_slot
assert beacon_block_root == blobs_sidecar.beacon_block_root
blobs = blobs_sidecar.blobs
kzg_aggregated_proof = blobs_sidecar.kzg_aggregated_proof
assert len(expected_kzg_commitments) == len(blobs)
aggregated_poly, aggregated_poly_commitment = compute_aggregated_poly_and_commitment(
blobs,
expected_kzg_commitments,
)
# Generate challenge `x` and evaluate the aggregated polynomial at `x`
x = hash_to_bls_field(
PolynomialAndCommitment(polynomial=aggregated_poly, kzg_commitment=aggregated_poly_commitment)
)
# Evaluate aggregated polynomial at `x` (evaluation function checks for div-by-zero)
y = evaluate_polynomial_in_evaluation_form(aggregated_poly, x)
# Verify aggregated proof
assert verify_kzg_proof(aggregated_poly_commitment, x, y, kzg_aggregated_proof)
```
### `compute_proof_from_blobs`
```python
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(
polynomial=aggregated_poly,
kzg_commitment=aggregated_poly_commitment,
))
return compute_kzg_proof(aggregated_poly, x)
```
### `get_blobs_and_kzg_commitments`
The interface to retrieve blobs and corresponding kzg commitments.
Note: This API is *unstable*. `get_blobs_and_kzg_commitments` and `get_payload` may be unified.
Implementers may also retrieve blobs individually per transaction.
```python
def get_blobs_and_kzg_commitments(payload_id: PayloadId) -> Tuple[Sequence[BLSFieldElement], Sequence[KZGCommitment]]:
...
```
## 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`
@ -229,37 +76,30 @@ 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(
beacon_block_root=hash_tree_root(block),
beacon_block_slot=block.slot,
blobs=blobs,
kzg_aggregated_proof=compute_proof_from_blobs(blobs),
kzg_aggregated_proof=compute_aggregate_kzg_proof(blobs),
)
```
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 `signed_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.

View File

@ -1 +1 @@
1.2.0
1.3.0-alpha.0

View File

@ -63,11 +63,11 @@ def get_checks(store):
return {
"finalized_header": {
'slot': int(store.finalized_header.slot),
'root': encode_hex(store.finalized_header.hash_tree_root()),
'beacon_root': encode_hex(store.finalized_header.hash_tree_root()),
},
"optimistic_header": {
'slot': int(store.optimistic_header.slot),
'root': encode_hex(store.optimistic_header.hash_tree_root()),
'beacon_root': encode_hex(store.optimistic_header.hash_tree_root()),
},
}
@ -110,7 +110,7 @@ def compute_start_slot_at_sync_committee_period(spec, sync_committee_period):
def compute_start_slot_at_next_sync_committee_period(spec, state):
sync_committee_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(state.slot))
sync_committee_period = spec.compute_sync_committee_period_at_slot(state.slot)
return compute_start_slot_at_sync_committee_period(spec, sync_committee_period + 1)

View File

@ -37,14 +37,14 @@ def test_process_light_client_update_not_timeout(spec, state):
# Ensure that finality checkpoint is genesis
assert state.finalized_checkpoint.epoch == 0
# Finality is unchanged
finality_header = spec.BeaconBlockHeader()
finalized_header = spec.BeaconBlockHeader()
finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))]
update = spec.LightClientUpdate(
attested_header=attested_header,
next_sync_committee=next_sync_committee,
next_sync_committee_branch=next_sync_committee_branch,
finalized_header=finality_header,
finalized_header=finalized_header,
finality_branch=finality_branch,
sync_aggregate=sync_aggregate,
signature_slot=signature_slot,
@ -68,8 +68,8 @@ def test_process_light_client_update_at_period_boundary(spec, state):
# Forward to slot before next sync committee period so that next block is final one in period
next_slots(spec, state, spec.UPDATE_TIMEOUT - 2)
store_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(store.optimistic_header.slot))
update_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(state.slot))
store_period = spec.compute_sync_committee_period_at_slot(store.optimistic_header.slot)
update_period = spec.compute_sync_committee_period_at_slot(state.slot)
assert store_period == update_period
attested_block = state_transition_with_full_block(spec, state, False, False)
@ -81,14 +81,14 @@ def test_process_light_client_update_at_period_boundary(spec, state):
next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))]
# Finality is unchanged
finality_header = spec.BeaconBlockHeader()
finalized_header = spec.BeaconBlockHeader()
finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))]
update = spec.LightClientUpdate(
attested_header=attested_header,
next_sync_committee=next_sync_committee,
next_sync_committee_branch=next_sync_committee_branch,
finalized_header=finality_header,
finalized_header=finalized_header,
finality_branch=finality_branch,
sync_aggregate=sync_aggregate,
signature_slot=signature_slot,
@ -112,8 +112,8 @@ def test_process_light_client_update_timeout(spec, state):
# Forward to next sync committee period
next_slots(spec, state, spec.UPDATE_TIMEOUT)
store_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(store.optimistic_header.slot))
update_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(state.slot))
store_period = spec.compute_sync_committee_period_at_slot(store.optimistic_header.slot)
update_period = spec.compute_sync_committee_period_at_slot(state.slot)
assert store_period + 1 == update_period
attested_block = state_transition_with_full_block(spec, state, False, False)
@ -126,14 +126,14 @@ def test_process_light_client_update_timeout(spec, state):
next_sync_committee = state.next_sync_committee
next_sync_committee_branch = spec.compute_merkle_proof_for_state(state, spec.NEXT_SYNC_COMMITTEE_INDEX)
# Finality is unchanged
finality_header = spec.BeaconBlockHeader()
finalized_header = spec.BeaconBlockHeader()
finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))]
update = spec.LightClientUpdate(
attested_header=attested_header,
next_sync_committee=next_sync_committee,
next_sync_committee_branch=next_sync_committee_branch,
finalized_header=finality_header,
finalized_header=finalized_header,
finality_branch=finality_branch,
sync_aggregate=sync_aggregate,
signature_slot=signature_slot,
@ -164,8 +164,8 @@ def test_process_light_client_update_finality_updated(spec, state):
# Ensure that finality checkpoint has changed
assert state.finalized_checkpoint.epoch == 3
# Ensure that it's same period
store_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(store.optimistic_header.slot))
update_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(state.slot))
store_period = spec.compute_sync_committee_period_at_slot(store.optimistic_header.slot)
update_period = spec.compute_sync_committee_period_at_slot(state.slot)
assert store_period == update_period
attested_block = blocks[-1]

View File

@ -10,10 +10,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,
)
@ -111,6 +112,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,
)

View 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,
)

View File

@ -0,0 +1,20 @@
from eth2spec.test.context import (
spec_state_test,
with_eip4844_and_later,
)
from eth2spec.test.helpers.sharding import (
get_sample_blob,
)
@with_eip4844_and_later
@spec_state_test
def test_verify_kzg_proof(spec, state):
x = 3
blob = get_sample_blob(spec)
commitment = spec.blob_to_kzg_commitment(blob)
polynomial = spec.blob_to_polynomial(blob)
proof = spec.compute_kzg_proof(polynomial, x)
y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x)
assert spec.verify_kzg_proof(commitment, x, y, proof)

View File

@ -0,0 +1,23 @@
from eth2spec.test.helpers.constants import (
EIP4844,
MINIMAL,
)
from eth2spec.test.helpers.sharding import (
get_sample_opaque_tx,
)
from eth2spec.test.context import (
with_phases,
spec_state_test,
with_presets,
)
@with_phases([EIP4844])
@spec_state_test
@with_presets([MINIMAL])
def test_tx_peek_blob_versioned_hashes(spec, state):
otx, blobs, commitments = get_sample_opaque_tx(spec)
data_hashes = spec.tx_peek_blob_versioned_hashes(otx)
expected = [spec.kzg_commitment_to_versioned_hash(blob_commitment) for blob_commitment in commitments]
assert expected == data_hashes

View File

@ -10,24 +10,7 @@ from eth2spec.test.context import (
)
from eth2spec.test.helpers.sharding import (
get_sample_opaque_tx,
get_sample_blob,
)
from eth2spec.test.helpers.keys import privkeys
@with_eip4844_and_later
@spec_state_test
def test_verify_kzg_proof(spec, state):
x = 3
polynomial = get_sample_blob(spec)
polynomial = [int(i) for i in polynomial]
commitment = spec.blob_to_kzg_commitment(polynomial)
# Get the proof
proof = spec.compute_kzg_proof(polynomial, x)
y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x)
assert spec.verify_kzg_proof(commitment, x, y, proof)
def _run_validate_blobs_sidecar_test(spec, state, blob_count):
@ -38,8 +21,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)

View File

@ -24,11 +24,13 @@ 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)
FORKS_BEFORE_BELLATRIX = FORKS_BEFORE_ALTAIR + (ALTAIR,)
FORKS_BEFORE_CAPELLA = FORKS_BEFORE_BELLATRIX + (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,

View File

@ -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)

View File

@ -34,13 +34,14 @@ class ECDSASignature(Container):
class BlobTransaction(Container):
chain_id: uint256
nonce: uint64
priority_fee_per_gas: uint256
max_basefee_per_gas: uint256
max_priority_fee_per_gas: uint256
max_fee_per_gas: uint256
gas: uint64
to: Union[None, Bytes20] # Address = Bytes20
value: uint256
data: ByteList[MAX_CALLDATA_SIZE]
access_list: List[AccessTuple, MAX_ACCESS_LIST_SIZE]
max_fee_per_data_gas: uint256
blob_versioned_hashes: List[Bytes32, MAX_VERSIONED_HASHES_LIST_SIZE]
@ -53,10 +54,16 @@ def get_sample_blob(spec, rng=None):
if rng is None:
rng = random.Random(5566)
return spec.Blob([
values = [
rng.randint(0, spec.BLS_MODULUS - 1)
for _ in range(spec.FIELD_ELEMENTS_PER_BLOB)
])
]
b = bytes()
for v in values:
b += v.to_bytes(32, spec.ENDIANNESS)
return spec.Blob(b)
def get_sample_opaque_tx(spec, blob_count=1, rng=None):

View File

@ -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):

View File

@ -5,6 +5,7 @@ from py_ecc.optimized_bls12_381 import ( # noqa: F401
G2,
Z1,
Z2,
FQ,
add,
multiply,
neg,

View File

@ -1,6 +1,15 @@
# Ref:
# - https://github.com/ethereum/research/blob/8f084630528ba33d92b2bc05edf5338dd193c6f1/trusted_setup/trusted_setup.py
# - https://github.com/asn-d6/kzgverify
import json
import os
from typing import (
Tuple,
Sequence,
)
from pathlib import Path
from eth_utils import encode_hex
from py_ecc.optimized_bls12_381 import ( # noqa: F401
G1,
G2,
@ -11,13 +20,16 @@ from py_ecc.optimized_bls12_381 import ( # noqa: F401
multiply,
neg,
)
from py_ecc.typing import (
Optimized_Point3D,
)
from eth2spec.utils import bls
PRIMITIVE_ROOT_OF_UNITY = 7
def generate_setup(generator, secret, length):
def generate_setup(generator: Optimized_Point3D, secret: int, length: int) -> Tuple[Optimized_Point3D]:
"""
Generate trusted setup of ``generator`` in ``length``.
"""
@ -27,7 +39,7 @@ def generate_setup(generator, secret, length):
return tuple(result)
def fft(vals, modulus, domain):
def fft(vals: Sequence[Optimized_Point3D], modulus: int, domain: int) -> Sequence[Optimized_Point3D]:
"""
FFT for group elements
"""
@ -43,7 +55,7 @@ def fft(vals, modulus, domain):
return o
def compute_root_of_unity(length) -> int:
def compute_root_of_unity(length: int) -> int:
"""
Generate a w such that ``w**length = 1``.
"""
@ -51,11 +63,12 @@ def compute_root_of_unity(length) -> int:
return pow(PRIMITIVE_ROOT_OF_UNITY, (BLS_MODULUS - 1) // length, BLS_MODULUS)
def compute_roots_of_unity(field_elements_per_blob):
def compute_roots_of_unity(field_elements_per_blob: int) -> Tuple[int]:
"""
Compute a list of roots of unity for a given order.
The order must divide the BLS multiplicative group order, i.e. BLS_MODULUS - 1
"""
field_elements_per_blob = int(field_elements_per_blob) # to non-SSZ int
assert (BLS_MODULUS - 1) % field_elements_per_blob == 0
root_of_unity = compute_root_of_unity(length=field_elements_per_blob)
@ -64,10 +77,10 @@ def compute_roots_of_unity(field_elements_per_blob):
for _ in range(field_elements_per_blob):
roots.append(current_root_of_unity)
current_root_of_unity = current_root_of_unity * root_of_unity % BLS_MODULUS
return roots
return tuple(roots)
def get_lagrange(setup):
def get_lagrange(setup: Sequence[Optimized_Point3D]) -> Tuple[bytes]:
"""
Convert a G1 or G2 portion of a setup into the Lagrange basis.
"""
@ -77,4 +90,34 @@ def get_lagrange(setup):
# TODO: introduce an IFFT function for simplicity
fft_output = fft(setup, BLS_MODULUS, domain)
inv_length = pow(len(setup), BLS_MODULUS - 2, BLS_MODULUS)
return [bls.G1_to_bytes48(multiply(fft_output[-i], inv_length)) for i in range(len(fft_output))]
return tuple(bls.G1_to_bytes48(multiply(fft_output[-i], inv_length)) for i in range(len(fft_output)))
def dump_kzg_trusted_setup_files(secret: int, length: int, output_dir: str) -> None:
setup_g1 = generate_setup(bls.G1, secret, length)
setup_g2 = generate_setup(bls.G2, secret, length)
setup_g1_lagrange = get_lagrange(setup_g1)
roots_of_unity = compute_roots_of_unity(length)
serailized_setup_g1 = [encode_hex(bls.G1_to_bytes48(p)) for p in setup_g1]
serialized_setup_g2 = [encode_hex(bls.G2_to_bytes96(p)) for p in setup_g2]
serialized_setup_g1_lagrange = [encode_hex(x) for x in setup_g1_lagrange]
output_dir_path = Path(output_dir)
if not os.path.exists(output_dir_path):
os.makedirs(output_dir_path)
print("Created directory: ", output_dir_path)
file_path = output_dir_path / 'testing_trusted_setups.json'
with open(file_path, 'w+') as f:
json.dump(
{
"setup_G1": serailized_setup_g1,
"setup_G2": serialized_setup_g2,
"setup_G1_lagrange": serialized_setup_g1_lagrange,
"roots_of_unity": roots_of_unity,
}, f)
print(f'Generated trusted setup file: {file_path}\n')

View File

@ -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.

View File

@ -26,11 +26,11 @@ Each step includes checks to verify the expected impact on the `store` object.
```yaml
finalized_header: {
slot: int, -- Integer value from store.finalized_header.slot
root: string, -- Encoded 32-byte value from store.finalized_header.hash_tree_root()
beacon_root: string, -- Encoded 32-byte value from store.finalized_header.hash_tree_root()
}
optimistic_header: {
slot: int, -- Integer value from store.optimistic_header.slot
root: string, -- Encoded 32-byte value from store.optimistic_header.hash_tree_root()
beacon_root: string, -- Encoded 32-byte value from store.optimistic_header.hash_tree_root()
}
```

View File

@ -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.

View File

@ -455,7 +455,7 @@ def case07_eth_fast_aggregate_verify():
tampered_signature = aggregate_signature[:-4] + b'\xff\xff\xff\xff'
identifier = f'{pubkeys_serial}_{encode_hex(message)}'
assert not spec.eth_fast_aggregate_verify(pubkeys, message, tampered_signature)
yield 'eth_fast_aggregate_verify_tampered_signature_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
yield f'eth_fast_aggregate_verify_tampered_signature_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
'input': {
'pubkeys': pubkeys_serial,
'message': encode_hex(message),

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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__":

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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")

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)