mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-03-01 18:50:35 +00:00
Add deneb fc tests and update test format
This commit is contained in:
parent
726b453332
commit
2210cea734
@ -0,0 +1,105 @@
|
||||
from random import Random
|
||||
|
||||
from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
with_deneb_and_later,
|
||||
)
|
||||
|
||||
from eth2spec.test.helpers.block import (
|
||||
build_empty_block_for_next_slot,
|
||||
)
|
||||
from eth2spec.test.helpers.fork_choice import (
|
||||
BlobData,
|
||||
get_genesis_forkchoice_store_and_block,
|
||||
on_tick_and_append_step,
|
||||
tick_and_add_block,
|
||||
with_blob_data,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
state_transition_and_sign_block,
|
||||
)
|
||||
from eth2spec.test.helpers.sharding import (
|
||||
get_sample_opaque_tx
|
||||
)
|
||||
|
||||
|
||||
def get_block_with_blob(spec, state, rng=None):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
opaque_tx, blobs, blob_kzg_commitments, blob_kzg_proofs = get_sample_opaque_tx(spec, blob_count=1, rng=rng)
|
||||
block.body.execution_payload.transactions = [opaque_tx]
|
||||
# block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload)
|
||||
block.body.blob_kzg_commitments = blob_kzg_commitments
|
||||
return block, blobs, blob_kzg_proofs
|
||||
|
||||
|
||||
@with_deneb_and_later
|
||||
@spec_state_test
|
||||
def test_simple_blob_data(spec, state):
|
||||
rng = Random(1234)
|
||||
|
||||
test_steps = []
|
||||
# Initialization
|
||||
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
|
||||
yield 'anchor_state', state
|
||||
yield 'anchor_block', anchor_block
|
||||
current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
|
||||
on_tick_and_append_step(spec, store, current_time, test_steps)
|
||||
assert store.time == current_time
|
||||
|
||||
# On receiving a block of `GENESIS_SLOT + 1` slot
|
||||
block, blobs, blob_kzg_proofs = get_block_with_blob(spec, state, rng=rng)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
blob_data = BlobData(blobs, blob_kzg_proofs)
|
||||
|
||||
def run_func_1():
|
||||
yield from tick_and_add_block(spec, store, signed_block, test_steps, blob_data=blob_data)
|
||||
|
||||
yield from with_blob_data(spec, blob_data, run_func_1)
|
||||
|
||||
assert spec.get_head(store) == signed_block.message.hash_tree_root()
|
||||
|
||||
# On receiving a block of next epoch
|
||||
store.time = current_time + spec.config.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
|
||||
block, blobs, blob_kzg_proofs = get_block_with_blob(spec, state, rng=rng)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
blob_data = BlobData(blobs, blob_kzg_proofs)
|
||||
|
||||
def run_func_2():
|
||||
yield from tick_and_add_block(spec, store, signed_block, test_steps, blob_data=blob_data)
|
||||
|
||||
yield from with_blob_data(spec, blob_data, run_func_2)
|
||||
|
||||
assert spec.get_head(store) == signed_block.message.hash_tree_root()
|
||||
|
||||
yield 'steps', test_steps
|
||||
|
||||
|
||||
@with_deneb_and_later
|
||||
@spec_state_test
|
||||
def test_invalid_incorrect_proof(spec, state):
|
||||
rng = Random(1234)
|
||||
|
||||
test_steps = []
|
||||
# Initialization
|
||||
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
|
||||
yield 'anchor_state', state
|
||||
yield 'anchor_block', anchor_block
|
||||
current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
|
||||
on_tick_and_append_step(spec, store, current_time, test_steps)
|
||||
assert store.time == current_time
|
||||
|
||||
# On receiving a block of `GENESIS_SLOT + 1` slot
|
||||
block, blobs, blob_kzg_proofs = get_block_with_blob(spec, state, rng=rng)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
# Insert incorrect proof
|
||||
blob_kzg_proofs = [b'\xc0' + b'\x00' * 47]
|
||||
blob_data = BlobData(blobs, blob_kzg_proofs)
|
||||
|
||||
def run_func_1():
|
||||
yield from tick_and_add_block(spec, store, signed_block, test_steps, blob_data=blob_data, valid=False)
|
||||
|
||||
yield from with_blob_data(spec, blob_data, run_func_1)
|
||||
|
||||
assert spec.get_head(store) != signed_block.message.hash_tree_root()
|
||||
|
||||
yield 'steps', test_steps
|
@ -1,3 +1,5 @@
|
||||
from typing import NamedTuple, Sequence, Any
|
||||
|
||||
from eth_utils import encode_hex
|
||||
from eth2spec.test.exceptions import BlockNotFoundException
|
||||
from eth2spec.test.helpers.attestations import (
|
||||
@ -7,6 +9,33 @@ from eth2spec.test.helpers.attestations import (
|
||||
)
|
||||
|
||||
|
||||
class BlobData(NamedTuple):
|
||||
blobs: Sequence[Any]
|
||||
proofs: Sequence[bytes]
|
||||
|
||||
|
||||
def with_blob_data(spec, blob_data, func):
|
||||
def retrieve_blobs_and_proofs(beacon_block_root):
|
||||
return blob_data.blobs, blob_data.proofs
|
||||
|
||||
retrieve_blobs_and_proofs_backup = spec.retrieve_blobs_and_proofs
|
||||
spec.retrieve_blobs_and_proofs = retrieve_blobs_and_proofs
|
||||
|
||||
class AtomicBoolean():
|
||||
value = False
|
||||
is_called = AtomicBoolean()
|
||||
|
||||
def wrap(flag: AtomicBoolean):
|
||||
yield from func()
|
||||
flag.value = True
|
||||
|
||||
try:
|
||||
yield from wrap(is_called)
|
||||
finally:
|
||||
spec.retrieve_blobs_and_proofs = retrieve_blobs_and_proofs_backup
|
||||
assert is_called.value
|
||||
|
||||
|
||||
def get_anchor_root(spec, state):
|
||||
anchor_block_header = state.latest_block_header.copy()
|
||||
if anchor_block_header.state_root == spec.Bytes32():
|
||||
@ -15,7 +44,8 @@ def get_anchor_root(spec, state):
|
||||
|
||||
|
||||
def tick_and_add_block(spec, store, signed_block, test_steps, valid=True,
|
||||
merge_block=False, block_not_found=False, is_optimistic=False):
|
||||
merge_block=False, block_not_found=False, is_optimistic=False,
|
||||
blob_data=None):
|
||||
pre_state = store.block_states[signed_block.message.parent_root]
|
||||
if merge_block:
|
||||
assert spec.is_merge_transition_block(pre_state, signed_block.message.body)
|
||||
@ -30,6 +60,7 @@ def tick_and_add_block(spec, store, signed_block, test_steps, valid=True,
|
||||
valid=valid,
|
||||
block_not_found=block_not_found,
|
||||
is_optimistic=is_optimistic,
|
||||
blob_data=blob_data,
|
||||
)
|
||||
|
||||
return post_state
|
||||
@ -94,6 +125,13 @@ def get_attester_slashing_file_name(attester_slashing):
|
||||
return f"attester_slashing_{encode_hex(attester_slashing.hash_tree_root())}"
|
||||
|
||||
|
||||
def get_blobs_file_name(blobs=None, blobs_root=None):
|
||||
if blobs:
|
||||
return f"blobs_{encode_hex(blobs.hash_tree_root())}"
|
||||
else:
|
||||
return f"blobs_{encode_hex(blobs_root)}"
|
||||
|
||||
|
||||
def on_tick_and_append_step(spec, store, time, test_steps):
|
||||
spec.on_tick(store, time)
|
||||
test_steps.append({'tick': int(time)})
|
||||
@ -119,35 +157,53 @@ def add_block(spec,
|
||||
test_steps,
|
||||
valid=True,
|
||||
block_not_found=False,
|
||||
is_optimistic=False):
|
||||
is_optimistic=False,
|
||||
blob_data=None):
|
||||
"""
|
||||
Run on_block and on_attestation
|
||||
"""
|
||||
yield get_block_file_name(signed_block), signed_block
|
||||
|
||||
# Check blob_data
|
||||
if blob_data is not None:
|
||||
assert len(blob_data.blobs) == len(blob_data.proofs)
|
||||
blobs = spec.List[spec.Blob, spec.MAX_BLOBS_PER_BLOCK](blob_data.blobs)
|
||||
blobs_root = blobs.hash_tree_root()
|
||||
yield get_blobs_file_name(blobs_root=blobs_root), blobs
|
||||
|
||||
is_blob_data_test = blob_data is not None
|
||||
|
||||
def _append_step(is_blob_data_test, valid=True):
|
||||
if is_blob_data_test:
|
||||
test_steps.append({
|
||||
'block': get_block_file_name(signed_block),
|
||||
'blobs': get_blobs_file_name(blobs_root=blobs_root),
|
||||
'proofs': [encode_hex(proof) for proof in blob_data.proofs],
|
||||
'valid': valid,
|
||||
})
|
||||
else:
|
||||
test_steps.append({
|
||||
'block': get_block_file_name(signed_block),
|
||||
'valid': valid,
|
||||
})
|
||||
|
||||
if not valid:
|
||||
if is_optimistic:
|
||||
run_on_block(spec, store, signed_block, valid=True)
|
||||
test_steps.append({
|
||||
'block': get_block_file_name(signed_block),
|
||||
'valid': False,
|
||||
})
|
||||
_append_step(is_blob_data_test, valid=False)
|
||||
else:
|
||||
try:
|
||||
run_on_block(spec, store, signed_block, valid=True)
|
||||
except (AssertionError, BlockNotFoundException) as e:
|
||||
if isinstance(e, BlockNotFoundException) and not block_not_found:
|
||||
assert False
|
||||
test_steps.append({
|
||||
'block': get_block_file_name(signed_block),
|
||||
'valid': False,
|
||||
})
|
||||
_append_step(is_blob_data_test, valid=False)
|
||||
return
|
||||
else:
|
||||
assert False
|
||||
else:
|
||||
run_on_block(spec, store, signed_block, valid=True)
|
||||
test_steps.append({'block': get_block_file_name(signed_block)})
|
||||
_append_step(is_blob_data_test)
|
||||
|
||||
# An on_block step implies receiving block's attestations
|
||||
for attestation in signed_block.message.body.attestations:
|
||||
|
@ -34,9 +34,6 @@ from eth2spec.test.helpers.state import (
|
||||
)
|
||||
|
||||
|
||||
rng = random.Random(1001)
|
||||
|
||||
|
||||
@with_altair_and_later
|
||||
@spec_state_test
|
||||
def test_genesis(spec, state):
|
||||
@ -271,6 +268,7 @@ def test_proposer_boost_correct_head(spec, state):
|
||||
next_slots(spec, state_2, 2)
|
||||
block_2 = build_empty_block_for_next_slot(spec, state_2)
|
||||
signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2)
|
||||
rng = random.Random(1001)
|
||||
while spec.hash_tree_root(block_1) >= spec.hash_tree_root(block_2):
|
||||
block_2.body.graffiti = spec.Bytes32(hex(rng.getrandbits(8 * 32))[2:].zfill(64))
|
||||
signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2)
|
||||
@ -339,6 +337,7 @@ def test_discard_equivocations_on_attester_slashing(spec, state):
|
||||
next_slots(spec, state_2, 2)
|
||||
block_2 = build_empty_block_for_next_slot(spec, state_2)
|
||||
signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2)
|
||||
rng = random.Random(1001)
|
||||
while spec.hash_tree_root(block_1) >= spec.hash_tree_root(block_2):
|
||||
block_2.body.graffiti = spec.Bytes32(hex(rng.getrandbits(8 * 32))[2:].zfill(64))
|
||||
signed_block_2 = state_transition_and_sign_block(spec, state_2.copy(), block_2)
|
||||
|
@ -2,6 +2,30 @@
|
||||
|
||||
The aim of the fork choice tests is to provide test coverage of the various components of the fork choice.
|
||||
|
||||
## 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 -->
|
||||
|
||||
- [Test case format](#test-case-format)
|
||||
- [`meta.yaml`](#metayaml)
|
||||
- [`anchor_state.ssz_snappy`](#anchor_statessz_snappy)
|
||||
- [`anchor_block.ssz_snappy`](#anchor_blockssz_snappy)
|
||||
- [`steps.yaml`](#stepsyaml)
|
||||
- [`on_tick` execution step](#on_tick-execution-step)
|
||||
- [`on_attestation` execution step](#on_attestation-execution-step)
|
||||
- [`on_block` execution step](#on_block-execution-step)
|
||||
- [`on_merge_block` execution step](#on_merge_block-execution-step)
|
||||
- [`on_attester_slashing` execution step](#on_attester_slashing-execution-step)
|
||||
- [`on_payload_info` execution step](#on_payload_info-execution-step)
|
||||
- [Checks step](#checks-step)
|
||||
- [`attestation_<32-byte-root>.ssz_snappy`](#attestation_32-byte-rootssz_snappy)
|
||||
- [`block_<32-byte-root>.ssz_snappy`](#block_32-byte-rootssz_snappy)
|
||||
- [Condition](#condition)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- /TOC -->
|
||||
|
||||
## Test case format
|
||||
|
||||
### `meta.yaml`
|
||||
@ -61,12 +85,18 @@ The parameter that is required for executing `on_block(store, block)`.
|
||||
{
|
||||
block: string -- the name of the `block_<32-byte-root>.ssz_snappy` file.
|
||||
To execute `on_block(store, block)` with the given attestation.
|
||||
blobs: string -- optional, the name of the `blobs_<32-byte-root>.ssz_snappy` file.
|
||||
The blobs file content is a `List[Blob, MAX_BLOBS_PER_BLOCK]` SSZ object.
|
||||
proofs: array of byte48 hex string -- optional, the proofs of blob commitments.
|
||||
valid: bool -- optional, default to `true`.
|
||||
If it's `false`, this execution step is expected to be invalid.
|
||||
}
|
||||
```
|
||||
|
||||
The file is located in the same folder (see below).
|
||||
|
||||
`blobs` and `proofs` are new fields from Deneb EIP-4844. These are the expected values from `retrieve_blobs_and_proofs()` helper inside `is_data_available()` helper.
|
||||
|
||||
After this step, the `store` object may have been updated.
|
||||
|
||||
#### `on_merge_block` execution step
|
||||
|
@ -19,7 +19,13 @@ if __name__ == "__main__":
|
||||
]}
|
||||
bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods)
|
||||
capella_mods = bellatrix_mods # No additional Capella specific fork choice tests
|
||||
deneb_mods = capella_mods # No additional Deneb specific fork choice tests
|
||||
|
||||
# Deneb adds `is_data_available` tests
|
||||
_new_deneb_mods = {key: 'eth2spec.test.deneb.fork_choice.test_' + key for key in [
|
||||
'on_block',
|
||||
]}
|
||||
deneb_mods = combine_mods(_new_deneb_mods, capella_mods)
|
||||
|
||||
eip6110_mods = deneb_mods # No additional EIP6110 specific fork choice tests
|
||||
|
||||
all_mods = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user