Merge branch 'dev' into pr3052
This commit is contained in:
commit
95ee291f58
12
Makefile
12
Makefile
|
@ -41,6 +41,8 @@ CURRENT_DIR = ${CURDIR}
|
|||
LINTER_CONFIG_FILE = $(CURRENT_DIR)/linter.ini
|
||||
GENERATOR_ERROR_LOG_FILE = $(CURRENT_DIR)/$(TEST_VECTOR_DIR)/testgen_error_log.txt
|
||||
|
||||
SCRIPTS_DIR = ${CURRENT_DIR}/scripts
|
||||
|
||||
export DAPP_SKIP_BUILD:=1
|
||||
export DAPP_SRC:=$(SOLIDITY_DEPOSIT_CONTRACT_DIR)
|
||||
export DAPP_LIB:=$(SOLIDITY_DEPOSIT_CONTRACT_DIR)/lib
|
||||
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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]}
|
|
@ -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)
|
|
@ -0,0 +1 @@
|
|||
../[generator]
|
55
setup.py
55
setup.py
|
@ -7,13 +7,14 @@ import os
|
|||
import re
|
||||
import string
|
||||
import textwrap
|
||||
from typing import Dict, NamedTuple, List, Sequence, Optional, TypeVar
|
||||
from typing import Dict, NamedTuple, List, Sequence, Optional, TypeVar, Tuple
|
||||
from abc import ABC, abstractmethod
|
||||
import ast
|
||||
import subprocess
|
||||
import sys
|
||||
import copy
|
||||
from collections import OrderedDict
|
||||
import json
|
||||
|
||||
|
||||
# NOTE: have to programmatically include third-party dependencies in `setup.py`.
|
||||
|
@ -121,7 +122,7 @@ def _get_self_type_from_source(source: str) -> Optional[str]:
|
|||
return args[0].annotation.id
|
||||
|
||||
|
||||
def _get_class_info_from_source(source: str) -> (str, Optional[str]):
|
||||
def _get_class_info_from_source(source: str) -> Tuple[str, Optional[str]]:
|
||||
class_def = ast.parse(source).body[0]
|
||||
base = class_def.bases[0]
|
||||
if isinstance(base, ast.Name):
|
||||
|
@ -140,6 +141,28 @@ def _is_constant_id(name: str) -> bool:
|
|||
return all(map(lambda c: c in string.ascii_uppercase + '_' + string.digits, name[1:]))
|
||||
|
||||
|
||||
def _load_kzg_trusted_setups(preset_name):
|
||||
"""
|
||||
[TODO] it's not the final mainnet trusted setup.
|
||||
We will update it after the KZG ceremony.
|
||||
"""
|
||||
file_path = str(Path(__file__).parent) + '/presets/' + preset_name + '/trusted_setups/testing_trusted_setups.json'
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
json_data = json.load(f)
|
||||
|
||||
trusted_setup_G1 = json_data['setup_G1']
|
||||
trusted_setup_G2 = json_data['setup_G2']
|
||||
trusted_setup_G1_lagrange = json_data['setup_G1_lagrange']
|
||||
roots_of_unity = json_data['roots_of_unity']
|
||||
|
||||
return trusted_setup_G1, trusted_setup_G2, trusted_setup_G1_lagrange, roots_of_unity
|
||||
|
||||
ALL_KZG_SETUPS = {
|
||||
'minimal': _load_kzg_trusted_setups('minimal'),
|
||||
'mainnet': _load_kzg_trusted_setups('mainnet')
|
||||
}
|
||||
|
||||
ETH2_SPEC_COMMENT_PREFIX = "eth2spec:"
|
||||
|
||||
|
||||
|
@ -167,7 +190,16 @@ def _parse_value(name: str, typed_value: str, type_hint: Optional[str]=None) ->
|
|||
return VariableDefinition(type_name=type_name, value=typed_value[i+1:-1], comment=comment, type_hint=type_hint)
|
||||
|
||||
|
||||
def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str]) -> SpecObject:
|
||||
def _update_constant_vars_with_kzg_setups(constant_vars, preset_name):
|
||||
comment = "noqa: E501"
|
||||
kzg_setups = ALL_KZG_SETUPS[preset_name]
|
||||
constant_vars['KZG_SETUP_G1'] = VariableDefinition(constant_vars['KZG_SETUP_G1'].value, str(kzg_setups[0]), comment, None)
|
||||
constant_vars['KZG_SETUP_G2'] = VariableDefinition(constant_vars['KZG_SETUP_G2'].value, str(kzg_setups[1]), comment, None)
|
||||
constant_vars['KZG_SETUP_LAGRANGE'] = VariableDefinition(constant_vars['KZG_SETUP_LAGRANGE'].value, str(kzg_setups[2]), comment, None)
|
||||
constant_vars['ROOTS_OF_UNITY'] = VariableDefinition(constant_vars['ROOTS_OF_UNITY'].value, str(kzg_setups[3]), comment, None)
|
||||
|
||||
|
||||
def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], preset_name=str) -> SpecObject:
|
||||
functions: Dict[str, str] = {}
|
||||
protocols: Dict[str, ProtocolDefinition] = {}
|
||||
constant_vars: Dict[str, VariableDefinition] = {}
|
||||
|
@ -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,
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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` |
|
||||
|
|
@ -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).
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
```
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.2.0
|
||||
1.3.0-alpha.0
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
)
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -5,6 +5,7 @@ from py_ecc.optimized_bls12_381 import ( # noqa: F401
|
|||
G2,
|
||||
Z1,
|
||||
Z2,
|
||||
FQ,
|
||||
add,
|
||||
multiply,
|
||||
neg,
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
# Ref:
|
||||
# - https://github.com/ethereum/research/blob/8f084630528ba33d92b2bc05edf5338dd193c6f1/trusted_setup/trusted_setup.py
|
||||
# - https://github.com/asn-d6/kzgverify
|
||||
import json
|
||||
import os
|
||||
from typing import (
|
||||
Tuple,
|
||||
Sequence,
|
||||
)
|
||||
from pathlib import Path
|
||||
|
||||
from eth_utils import encode_hex
|
||||
from py_ecc.optimized_bls12_381 import ( # noqa: F401
|
||||
G1,
|
||||
G2,
|
||||
|
@ -11,13 +20,16 @@ from py_ecc.optimized_bls12_381 import ( # noqa: F401
|
|||
multiply,
|
||||
neg,
|
||||
)
|
||||
from py_ecc.typing import (
|
||||
Optimized_Point3D,
|
||||
)
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
PRIMITIVE_ROOT_OF_UNITY = 7
|
||||
|
||||
|
||||
def generate_setup(generator, secret, length):
|
||||
def generate_setup(generator: Optimized_Point3D, secret: int, length: int) -> Tuple[Optimized_Point3D]:
|
||||
"""
|
||||
Generate trusted setup of ``generator`` in ``length``.
|
||||
"""
|
||||
|
@ -27,7 +39,7 @@ def generate_setup(generator, secret, length):
|
|||
return tuple(result)
|
||||
|
||||
|
||||
def fft(vals, modulus, domain):
|
||||
def fft(vals: Sequence[Optimized_Point3D], modulus: int, domain: int) -> Sequence[Optimized_Point3D]:
|
||||
"""
|
||||
FFT for group elements
|
||||
"""
|
||||
|
@ -43,7 +55,7 @@ def fft(vals, modulus, domain):
|
|||
return o
|
||||
|
||||
|
||||
def compute_root_of_unity(length) -> int:
|
||||
def compute_root_of_unity(length: int) -> int:
|
||||
"""
|
||||
Generate a w such that ``w**length = 1``.
|
||||
"""
|
||||
|
@ -51,11 +63,12 @@ def compute_root_of_unity(length) -> int:
|
|||
return pow(PRIMITIVE_ROOT_OF_UNITY, (BLS_MODULUS - 1) // length, BLS_MODULUS)
|
||||
|
||||
|
||||
def compute_roots_of_unity(field_elements_per_blob):
|
||||
def compute_roots_of_unity(field_elements_per_blob: int) -> Tuple[int]:
|
||||
"""
|
||||
Compute a list of roots of unity for a given order.
|
||||
The order must divide the BLS multiplicative group order, i.e. BLS_MODULUS - 1
|
||||
"""
|
||||
field_elements_per_blob = int(field_elements_per_blob) # to non-SSZ int
|
||||
assert (BLS_MODULUS - 1) % field_elements_per_blob == 0
|
||||
root_of_unity = compute_root_of_unity(length=field_elements_per_blob)
|
||||
|
||||
|
@ -64,10 +77,10 @@ def compute_roots_of_unity(field_elements_per_blob):
|
|||
for _ in range(field_elements_per_blob):
|
||||
roots.append(current_root_of_unity)
|
||||
current_root_of_unity = current_root_of_unity * root_of_unity % BLS_MODULUS
|
||||
return roots
|
||||
return tuple(roots)
|
||||
|
||||
|
||||
def get_lagrange(setup):
|
||||
def get_lagrange(setup: Sequence[Optimized_Point3D]) -> Tuple[bytes]:
|
||||
"""
|
||||
Convert a G1 or G2 portion of a setup into the Lagrange basis.
|
||||
"""
|
||||
|
@ -77,4 +90,34 @@ def get_lagrange(setup):
|
|||
# TODO: introduce an IFFT function for simplicity
|
||||
fft_output = fft(setup, BLS_MODULUS, domain)
|
||||
inv_length = pow(len(setup), BLS_MODULUS - 2, BLS_MODULUS)
|
||||
return [bls.G1_to_bytes48(multiply(fft_output[-i], inv_length)) for i in range(len(fft_output))]
|
||||
return tuple(bls.G1_to_bytes48(multiply(fft_output[-i], inv_length)) for i in range(len(fft_output)))
|
||||
|
||||
|
||||
def dump_kzg_trusted_setup_files(secret: int, length: int, output_dir: str) -> None:
|
||||
setup_g1 = generate_setup(bls.G1, secret, length)
|
||||
setup_g2 = generate_setup(bls.G2, secret, length)
|
||||
setup_g1_lagrange = get_lagrange(setup_g1)
|
||||
roots_of_unity = compute_roots_of_unity(length)
|
||||
|
||||
serailized_setup_g1 = [encode_hex(bls.G1_to_bytes48(p)) for p in setup_g1]
|
||||
serialized_setup_g2 = [encode_hex(bls.G2_to_bytes96(p)) for p in setup_g2]
|
||||
serialized_setup_g1_lagrange = [encode_hex(x) for x in setup_g1_lagrange]
|
||||
|
||||
output_dir_path = Path(output_dir)
|
||||
|
||||
if not os.path.exists(output_dir_path):
|
||||
os.makedirs(output_dir_path)
|
||||
print("Created directory: ", output_dir_path)
|
||||
|
||||
file_path = output_dir_path / 'testing_trusted_setups.json'
|
||||
|
||||
with open(file_path, 'w+') as f:
|
||||
json.dump(
|
||||
{
|
||||
"setup_G1": serailized_setup_g1,
|
||||
"setup_G2": serialized_setup_g2,
|
||||
"setup_G1_lagrange": serialized_setup_g1_lagrange,
|
||||
"roots_of_unity": roots_of_unity,
|
||||
}, f)
|
||||
|
||||
print(f'Generated trusted setup file: {file_path}\n')
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -27,6 +27,12 @@ if __name__ == "__main__":
|
|||
# so no additional tests required.
|
||||
bellatrix_mods = altair_mods
|
||||
|
||||
_new_capella_mods = {key: 'eth2spec.test.capella.epoch_processing.test_process_' + key for key in [
|
||||
'full_withdrawals',
|
||||
'partial_withdrawals',
|
||||
]}
|
||||
capella_mods = combine_mods(_new_capella_mods, altair_mods)
|
||||
|
||||
# TODO Custody Game testgen is disabled for now
|
||||
# custody_game_mods = {**{key: 'eth2spec.test.custody_game.epoch_processing.test_process_' + key for key in [
|
||||
# 'reveal_deadlines',
|
||||
|
@ -38,6 +44,7 @@ if __name__ == "__main__":
|
|||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="epoch_processing", all_mods=all_mods)
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
phase_0_mods = {'finality': 'eth2spec.test.phase0.finality.test_finality'}
|
||||
altair_mods = phase_0_mods # No additional Altair specific finality tests
|
||||
bellatrix_mods = altair_mods # No additional Bellatrix specific finality tests
|
||||
altair_mods = phase_0_mods # No additional Altair specific finality tests
|
||||
bellatrix_mods = altair_mods # No additional Bellatrix specific finality tests
|
||||
capella_mods = bellatrix_mods # No additional Capella specific finality tests
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="finality", all_mods=all_mods)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -17,11 +17,12 @@ if __name__ == "__main__":
|
|||
'on_merge_block',
|
||||
]}
|
||||
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
|
||||
|
||||
capella_mods = bellatrix_mods # No additional Capella specific fork choice tests
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="fork_choice", all_mods=all_mods)
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
from typing import Iterable
|
||||
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, MINIMAL, MAINNET
|
||||
from eth2spec.test.helpers.constants import (
|
||||
PHASE0, ALTAIR, BELLATRIX, CAPELLA,
|
||||
MINIMAL, MAINNET,
|
||||
)
|
||||
from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName
|
||||
from eth2spec.test.altair.fork import test_altair_fork_basic, test_altair_fork_random
|
||||
from eth2spec.test.bellatrix.fork import test_bellatrix_fork_basic, test_bellatrix_fork_random
|
||||
from eth2spec.test.capella.fork import test_capella_fork_basic, test_capella_fork_random
|
||||
from eth2spec.gen_helpers.gen_base import gen_runner, gen_typing
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import generate_from_tests
|
||||
|
||||
|
@ -33,6 +37,8 @@ def _get_fork_tests_providers():
|
|||
yield create_provider(test_altair_fork_random, preset, PHASE0, ALTAIR)
|
||||
yield create_provider(test_bellatrix_fork_basic, preset, ALTAIR, BELLATRIX)
|
||||
yield create_provider(test_bellatrix_fork_random, preset, ALTAIR, BELLATRIX)
|
||||
yield create_provider(test_capella_fork_basic, preset, BELLATRIX, CAPELLA)
|
||||
yield create_provider(test_capella_fork_random, preset, BELLATRIX, CAPELLA)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -8,18 +8,19 @@ if __name__ == "__main__":
|
|||
'validity',
|
||||
]}
|
||||
|
||||
# we have new unconditional lines in `initialize_beacon_state_from_eth1` and we want to test it
|
||||
altair_mods = phase_0_mods
|
||||
|
||||
# we have new unconditional lines in `initialize_beacon_state_from_eth1` and we want to test it
|
||||
_new_bellatrix_mods = {key: 'eth2spec.test.bellatrix.genesis.test_' + key for key in [
|
||||
'initialization',
|
||||
]}
|
||||
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
|
||||
|
||||
capella_mods = bellatrix_mods # No additional Capella specific genesis tests
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="genesis", all_mods=all_mods)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
|
||||
|
||||
|
@ -9,10 +9,12 @@ if __name__ == "__main__":
|
|||
'update_ranking',
|
||||
]}
|
||||
bellatrix_mods = altair_mods
|
||||
capella_mods = bellatrix_mods
|
||||
|
||||
all_mods = {
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="light_client", all_mods=all_mods)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -24,6 +24,13 @@ if __name__ == "__main__":
|
|||
]}
|
||||
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
|
||||
|
||||
_new_capella_mods = {key: 'eth2spec.test.capella.block_processing.test_process_' + key for key in [
|
||||
'deposit',
|
||||
'bls_to_execution_change',
|
||||
'withdrawals',
|
||||
]}
|
||||
capella_mods = combine_mods(_new_capella_mods, bellatrix_mods)
|
||||
|
||||
# TODO Custody Game testgen is disabled for now
|
||||
# _new_custody_game_mods = {key: 'eth2spec.test.custody_game.block_processing.test_process_' + key for key in [
|
||||
# 'attestation',
|
||||
|
@ -38,6 +45,7 @@ if __name__ == "__main__":
|
|||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="operations", all_mods=all_mods)
|
||||
|
|
|
@ -5,6 +5,8 @@ all:
|
|||
rm -f ../../core/pyspec/eth2spec/test/phase0/random/test_random.py
|
||||
rm -f ../../core/pyspec/eth2spec/test/altair/random/test_random.py
|
||||
rm -f ../../core/pyspec/eth2spec/test/bellatrix/random/test_random.py
|
||||
rm -f ../../core/pyspec/eth2spec/test/capella/random/test_random.py
|
||||
python3 generate.py phase0 > ../../core/pyspec/eth2spec/test/phase0/random/test_random.py
|
||||
python3 generate.py altair > ../../core/pyspec/eth2spec/test/altair/random/test_random.py
|
||||
python3 generate.py bellatrix > ../../core/pyspec/eth2spec/test/bellatrix/random/test_random.py
|
||||
python3 generate.py capella > ../../core/pyspec/eth2spec/test/capella/random/test_random.py
|
||||
|
|
|
@ -20,9 +20,11 @@ from eth2spec.test.utils.randomized_block_tests import (
|
|||
randomize_state,
|
||||
randomize_state_altair,
|
||||
randomize_state_bellatrix,
|
||||
randomize_state_capella,
|
||||
random_block,
|
||||
random_block_altair_with_cycling_sync_committee_participation,
|
||||
random_block_bellatrix,
|
||||
random_block_capella,
|
||||
last_slot_in_epoch,
|
||||
random_slot_in_epoch,
|
||||
penultimate_slot_in_epoch,
|
||||
|
@ -32,7 +34,7 @@ from eth2spec.test.utils.randomized_block_tests import (
|
|||
transition_to_leaking,
|
||||
transition_without_leak,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
# Ensure this many blocks are present in *each* randomized scenario
|
||||
|
@ -263,5 +265,12 @@ if __name__ == "__main__":
|
|||
state_randomizer=randomize_state_bellatrix,
|
||||
block_randomizer=random_block_bellatrix,
|
||||
)
|
||||
if CAPELLA in sys.argv:
|
||||
did_generate = True
|
||||
run_generate_tests_to_std_out(
|
||||
CAPELLA,
|
||||
state_randomizer=randomize_state_capella,
|
||||
block_randomizer=random_block_capella,
|
||||
)
|
||||
if not did_generate:
|
||||
warnings.warn("no phase given for test generation")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
|
||||
|
||||
|
@ -12,11 +12,15 @@ if __name__ == "__main__":
|
|||
bellatrix_mods = {key: 'eth2spec.test.bellatrix.random.test_' + key for key in [
|
||||
'random',
|
||||
]}
|
||||
capella_mods = {key: 'eth2spec.test.capella.random.test_' + key for key in [
|
||||
'random',
|
||||
]}
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="random", all_mods=all_mods)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -15,11 +15,13 @@ if __name__ == "__main__":
|
|||
# Note: Block rewards are non-epoch rewards and are tested as part of block processing tests.
|
||||
# Transaction fees are part of the execution-layer.
|
||||
bellatrix_mods = altair_mods
|
||||
capella_mods = bellatrix_mods
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="rewards", all_mods=all_mods)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods
|
||||
|
||||
|
||||
|
@ -18,10 +18,16 @@ if __name__ == "__main__":
|
|||
]}
|
||||
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
|
||||
|
||||
_new_capella_mods = {key: 'eth2spec.test.capella.sanity.test_' + key for key in [
|
||||
'blocks',
|
||||
]}
|
||||
capella_mods = combine_mods(_new_capella_mods, bellatrix_mods)
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="sanity", all_mods=all_mods)
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
from eth2spec.test.helpers.constants import BELLATRIX
|
||||
from eth2spec.test.helpers.constants import BELLATRIX, CAPELLA
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bellatrix_mods = {key: 'eth2spec.test.bellatrix.sync.test_' + key for key in [
|
||||
'optimistic',
|
||||
]}
|
||||
capella_mods = bellatrix_mods
|
||||
|
||||
all_mods = {
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="sync", all_mods=all_mods)
|
||||
|
|
Loading…
Reference in New Issue