mirror of
https://github.com/ethereum/consensus-specs.git
synced 2026-01-09 18:58:01 -05:00
Add PeerDAS tests (#4391)
This commit is contained in:
@@ -32,6 +32,7 @@ dependencies = [
|
||||
|
||||
[project.optional-dependencies]
|
||||
test = [
|
||||
"deepdiff==8.5.0",
|
||||
"pytest-cov==6.2.1",
|
||||
"pytest-xdist==3.7.0",
|
||||
"pytest==8.4.1",
|
||||
|
||||
@@ -4,20 +4,12 @@ from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
with_all_phases_from_except,
|
||||
)
|
||||
from eth2spec.test.helpers.blob import (
|
||||
get_sample_blob_tx,
|
||||
)
|
||||
from eth2spec.test.helpers.block import (
|
||||
build_empty_block_for_next_slot,
|
||||
)
|
||||
from eth2spec.test.helpers.blob import get_block_with_blob
|
||||
from eth2spec.test.helpers.constants import (
|
||||
DENEB,
|
||||
EIP7732,
|
||||
FULU,
|
||||
)
|
||||
from eth2spec.test.helpers.execution_payload import (
|
||||
compute_el_block_hash,
|
||||
)
|
||||
from eth2spec.test.helpers.fork_choice import (
|
||||
BlobData,
|
||||
get_genesis_forkchoice_store_and_block,
|
||||
@@ -29,19 +21,6 @@ from eth2spec.test.helpers.state import (
|
||||
)
|
||||
|
||||
|
||||
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_blob_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, state
|
||||
)
|
||||
block.body.blob_kzg_commitments = blob_kzg_commitments
|
||||
return block, blobs, blob_kzg_proofs
|
||||
|
||||
|
||||
# TODO(jtraglia): Use with_all_phases_from_to_except after EIP7732 is based on Fulu.
|
||||
# This applies to every other test in this file too.
|
||||
@with_all_phases_from_except(DENEB, [FULU, EIP7732])
|
||||
|
||||
@@ -1 +1,337 @@
|
||||
# TODO: add new data availability tests.
|
||||
from random import Random
|
||||
|
||||
from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
with_fulu_and_later,
|
||||
)
|
||||
from eth2spec.test.helpers.blob import get_block_with_blob_and_sidecars
|
||||
from eth2spec.test.helpers.fork_choice import (
|
||||
BlobData,
|
||||
get_genesis_forkchoice_store_and_block,
|
||||
on_tick_and_append_step,
|
||||
tick_and_add_block_with_data,
|
||||
)
|
||||
|
||||
|
||||
def flip_one_bit_in_bytes(data: bytes, index: int = 0) -> bytes:
|
||||
"""
|
||||
Flip one bit in a bytes object at the given index.
|
||||
"""
|
||||
constr = data.__class__
|
||||
byte_index = index // 8
|
||||
bit_index = 7 - (index % 8)
|
||||
byte = data[byte_index]
|
||||
flipped_byte = byte ^ (1 << bit_index)
|
||||
|
||||
return constr(bytes(data[:byte_index]) + bytes([flipped_byte]) + bytes(data[byte_index + 1 :]))
|
||||
|
||||
|
||||
def get_alt_sidecars(spec, state):
|
||||
"""
|
||||
Get alternative sidecars for negative test cases.
|
||||
"""
|
||||
rng = Random(4321)
|
||||
_, _, _, _, alt_sidecars = get_block_with_blob_and_sidecars(spec, state, rng=rng, blob_count=2)
|
||||
return alt_sidecars
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__ok(spec, state):
|
||||
"""
|
||||
Similar to test_simple_blob_data, but in PeerDAS version that is from Fulu onwards.
|
||||
It covers code related to the blob sidecars because on_block calls `is_data_available`
|
||||
and we are calling `get_data_column_sidecars_from_block` in the test itself.
|
||||
"""
|
||||
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
|
||||
_, blobs, blob_kzg_proofs, signed_block, sidecars = get_block_with_blob_and_sidecars(
|
||||
spec, state, rng=rng, blob_count=2
|
||||
)
|
||||
blob_data = BlobData(blobs, blob_kzg_proofs, sidecars)
|
||||
|
||||
yield from tick_and_add_block_with_data(spec, store, signed_block, test_steps, blob_data)
|
||||
|
||||
assert spec.get_head(store) == signed_block.message.hash_tree_root()
|
||||
|
||||
# On receiving a block of next epoch
|
||||
_, blobs, blob_kzg_proofs, signed_block, sidecars = get_block_with_blob_and_sidecars(
|
||||
spec, state, rng=rng, blob_count=2
|
||||
)
|
||||
blob_data = BlobData(blobs, blob_kzg_proofs, sidecars)
|
||||
|
||||
yield from tick_and_add_block_with_data(spec, store, signed_block, test_steps, blob_data)
|
||||
|
||||
assert spec.get_head(store) == signed_block.message.hash_tree_root()
|
||||
|
||||
yield "steps", test_steps
|
||||
|
||||
|
||||
def run_on_block_peerdas_invalid_test(spec, state, fn):
|
||||
"""
|
||||
Run a invalid PeerDAS on_block test with a sidecars mutation function.
|
||||
"""
|
||||
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
|
||||
|
||||
_, blobs, blob_kzg_proofs, signed_block, sidecars = get_block_with_blob_and_sidecars(
|
||||
spec, state, rng=rng, blob_count=2
|
||||
)
|
||||
sidecars = fn(sidecars)
|
||||
blob_data = BlobData(blobs, blob_kzg_proofs, [])
|
||||
|
||||
yield from tick_and_add_block_with_data(
|
||||
spec, store, signed_block, test_steps, blob_data, valid=False
|
||||
)
|
||||
assert spec.get_head(store) != signed_block.message.hash_tree_root()
|
||||
|
||||
yield "steps", test_steps
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__not_available(spec, state):
|
||||
"""
|
||||
Test is_data_available throws an exception when not enough columns are sampled.
|
||||
"""
|
||||
yield from run_on_block_peerdas_invalid_test(
|
||||
spec,
|
||||
state,
|
||||
# Empty sidecars will trigger the simulation of not enough columns being sampled
|
||||
lambda _: [],
|
||||
)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_zero_blobs(spec, state):
|
||||
"""
|
||||
Test is_data_available returns false when there are no blobs in the sidecars.
|
||||
"""
|
||||
|
||||
def invalid_zero_blobs(sidecars):
|
||||
sidecars[0].column = []
|
||||
sidecars[0].kzg_commitments = []
|
||||
sidecars[0].kzg_proofs = []
|
||||
return sidecars
|
||||
|
||||
yield from run_on_block_peerdas_invalid_test(spec, state, invalid_zero_blobs)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_index_1(spec, state):
|
||||
"""
|
||||
Test invalid index in sidecars for negative PeerDAS on_block test.
|
||||
"""
|
||||
|
||||
def invalid_index(sidecars):
|
||||
sidecars[0].index = 128 # Invalid index
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_index)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_index_2(spec, state):
|
||||
"""
|
||||
Test invalid index in sidecars for negative PeerDAS on_block test.
|
||||
"""
|
||||
|
||||
def invalid_index(sidecars):
|
||||
sidecars[0].index = 256 # Invalid index
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_index)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_mismatch_len_column_1(spec, state):
|
||||
"""
|
||||
Test mismatch length in column for negative PeerDAS on_block test.
|
||||
"""
|
||||
|
||||
def invalid_mismatch_len_column(sidecars):
|
||||
sidecars[0].column = sidecars[0].column[1:]
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_mismatch_len_column)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_mismatch_len_column_2(spec, state):
|
||||
"""
|
||||
Test mismatch length in column for negative PeerDAS on_block test.
|
||||
"""
|
||||
|
||||
def invalid_mismatch_len_column(sidecars):
|
||||
sidecars[1].column = sidecars[1].column[1:]
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_mismatch_len_column)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_mismatch_len_kzg_commitments_1(spec, state):
|
||||
"""
|
||||
Test mismatch length in kzg_commitments for negative PeerDAS on_block test.
|
||||
"""
|
||||
|
||||
def invalid_mismatch_len_kzg_commitments(sidecars):
|
||||
sidecars[0].kzg_commitments = sidecars[0].kzg_commitments[1:]
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_mismatch_len_kzg_commitments)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_mismatch_len_kzg_commitments_2(spec, state):
|
||||
"""
|
||||
Test mismatch length in kzg_commitments for negative PeerDAS on_block test.
|
||||
"""
|
||||
|
||||
def invalid_mismatch_len_kzg_commitments(sidecars):
|
||||
sidecars[1].kzg_commitments = sidecars[1].kzg_commitments[1:]
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_mismatch_len_kzg_commitments)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_mismatch_len_kzg_proofs_1(spec, state):
|
||||
"""
|
||||
Test mismatch length in kzg_proofs for negative PeerDAS on_block test.
|
||||
"""
|
||||
|
||||
def invalid_mismatch_len_kzg_proofs(sidecars):
|
||||
sidecars[0].kzg_proofs = sidecars[0].kzg_proofs[1:]
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_mismatch_len_kzg_proofs)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_mismatch_len_kzg_proofs_2(spec, state):
|
||||
"""
|
||||
Test mismatch length in kzg_proofs for negative PeerDAS on_block test.
|
||||
"""
|
||||
|
||||
def invalid_mismatch_len_kzg_proofs(sidecars):
|
||||
sidecars[1].kzg_proofs = sidecars[1].kzg_proofs[1:]
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_mismatch_len_kzg_proofs)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_wrong_column_1(spec, state):
|
||||
"""
|
||||
Test wrong column for negative PeerDAS on_block test.
|
||||
"""
|
||||
|
||||
def invalid_wrong_column(sidecars):
|
||||
sidecars[0].column[0] = flip_one_bit_in_bytes(sidecars[0].column[0], 80)
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_wrong_column)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_wrong_column_2(spec, state):
|
||||
"""
|
||||
Test wrong column for negative PeerDAS on_block test.
|
||||
"""
|
||||
|
||||
def invalid_wrong_column(sidecars):
|
||||
sidecars[1].column[1] = flip_one_bit_in_bytes(sidecars[1].column[1], 20)
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_wrong_column)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_wrong_commitment_1(spec, state):
|
||||
"""
|
||||
Test wrong commitment for negative PeerDAS on_block test.
|
||||
"""
|
||||
alt_sidecars = get_alt_sidecars(spec, state)
|
||||
|
||||
def invalid_wrong_commitment(sidecars):
|
||||
sidecars[0].kzg_commitments[0] = alt_sidecars[0].kzg_commitments[0]
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_wrong_commitment)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_wrong_commitment_2(spec, state):
|
||||
"""
|
||||
Test wrong commitment for negative PeerDAS on_block test.
|
||||
"""
|
||||
alt_sidecars = get_alt_sidecars(spec, state)
|
||||
|
||||
def invalid_wrong_commitment(sidecars):
|
||||
sidecars[1].kzg_commitments[1] = alt_sidecars[1].kzg_commitments[1]
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_wrong_commitment)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_wrong_proof_1(spec, state):
|
||||
"""
|
||||
Test wrong proof for negative PeerDAS on_block test.
|
||||
"""
|
||||
alt_sidecars = get_alt_sidecars(spec, state)
|
||||
|
||||
def invalid_wrong_proof(sidecars):
|
||||
sidecars[0].kzg_proofs[0] = alt_sidecars[0].kzg_proofs[0]
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_wrong_proof)
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_on_block_peerdas__invalid_wrong_proof_2(spec, state):
|
||||
"""
|
||||
Test wrong proof for negative PeerDAS on_block test.
|
||||
"""
|
||||
alt_sidecars = get_alt_sidecars(spec, state)
|
||||
|
||||
def invalid_wrong_proof(sidecars):
|
||||
sidecars[1].kzg_proofs[1] = alt_sidecars[1].kzg_proofs[1]
|
||||
return sidecars
|
||||
|
||||
run_on_block_peerdas_invalid_test(spec, state, invalid_wrong_proof)
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import random
|
||||
|
||||
from deepdiff import DeepDiff
|
||||
|
||||
from eth2spec.test.context import (
|
||||
single_phase,
|
||||
spec_state_test,
|
||||
spec_test,
|
||||
with_fulu_and_later,
|
||||
)
|
||||
from eth2spec.test.helpers.blob import (
|
||||
get_block_with_blob_and_sidecars,
|
||||
get_sample_blob,
|
||||
)
|
||||
from eth2spec.test.helpers.fork_choice import BlobData, with_blob_data
|
||||
|
||||
|
||||
def chunks(lst, n):
|
||||
@@ -65,3 +70,62 @@ def test_recover_matrix(spec):
|
||||
|
||||
# Ensure that the recovered matrix matches the original matrix
|
||||
assert recovered_matrix == matrix
|
||||
|
||||
|
||||
def run_is_data_available_peerdas_test(spec, blob_data):
|
||||
def callback():
|
||||
yield spec.is_data_available(spec.Root(b"\x00" * 32))
|
||||
|
||||
return next(with_blob_data(spec, blob_data, callback))
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_is_data_available_peerdas(spec, state):
|
||||
rng = random.Random(1234)
|
||||
_, blobs, blob_kzg_proofs, _, sidecars = get_block_with_blob_and_sidecars(
|
||||
spec, state, rng=rng, blob_count=2
|
||||
)
|
||||
blob_data = BlobData(blobs, blob_kzg_proofs, sidecars)
|
||||
|
||||
result = run_is_data_available_peerdas_test(spec, blob_data)
|
||||
|
||||
assert result is True, "Data should be available for the block with blob data"
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_get_data_column_sidecars(spec, state):
|
||||
rng = random.Random(1234)
|
||||
_, blobs, _, signed_block, sidecars = get_block_with_blob_and_sidecars(
|
||||
spec, state, rng=rng, blob_count=2
|
||||
)
|
||||
|
||||
sidecars_result = spec.get_data_column_sidecars(
|
||||
signed_block_header=spec.compute_signed_block_header(signed_block),
|
||||
kzg_commitments=sidecars[0].kzg_commitments,
|
||||
kzg_commitments_inclusion_proof=sidecars[0].kzg_commitments_inclusion_proof,
|
||||
cells_and_kzg_proofs=[spec.compute_cells_and_kzg_proofs(blob) for blob in blobs],
|
||||
)
|
||||
|
||||
assert len(sidecars_result) == len(sidecars), (
|
||||
"Should return the same number of sidecars as input"
|
||||
)
|
||||
assert DeepDiff(sidecars, sidecars_result) == {}, "Sidecars should match the expected sidecars"
|
||||
|
||||
|
||||
@with_fulu_and_later
|
||||
@spec_state_test
|
||||
def test_get_data_column_sidecars_from_column_sidecar(spec, state):
|
||||
rng = random.Random(1234)
|
||||
_, blobs, _, _, sidecars = get_block_with_blob_and_sidecars(spec, state, rng=rng, blob_count=2)
|
||||
|
||||
sidecars_result = spec.get_data_column_sidecars_from_column_sidecar(
|
||||
sidecar=sidecars[0],
|
||||
cells_and_kzg_proofs=[spec.compute_cells_and_kzg_proofs(blob) for blob in blobs],
|
||||
)
|
||||
|
||||
assert len(sidecars_result) == len(sidecars), (
|
||||
"Should return the same number of sidecars as input"
|
||||
)
|
||||
assert DeepDiff(sidecars, sidecars_result) == {}, "Sidecars should match the expected sidecars"
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import random
|
||||
from functools import cache
|
||||
from random import Random
|
||||
|
||||
from rlp import encode, Serializable
|
||||
from rlp.sedes import big_endian_int, Binary, binary, CountableList, List as RLPList
|
||||
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
||||
from eth2spec.test.helpers.execution_payload import compute_el_block_hash
|
||||
from eth2spec.test.helpers.forks import (
|
||||
is_post_electra,
|
||||
is_post_fulu,
|
||||
)
|
||||
from eth2spec.test.helpers.state import state_transition_and_sign_block
|
||||
|
||||
|
||||
class Eip4844RlpTransaction(Serializable):
|
||||
@@ -126,3 +131,32 @@ def get_max_blob_count(spec, state):
|
||||
return spec.config.MAX_BLOBS_PER_BLOCK_ELECTRA
|
||||
else:
|
||||
return spec.config.MAX_BLOBS_PER_BLOCK
|
||||
|
||||
|
||||
def get_block_with_blob(spec, state, rng: Random | None = None, blob_count=1):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
opaque_tx, blobs, blob_kzg_commitments, blob_kzg_proofs = get_sample_blob_tx(
|
||||
spec, blob_count=blob_count, rng=rng or random.Random(5566)
|
||||
)
|
||||
block.body.execution_payload.transactions = [opaque_tx]
|
||||
block.body.execution_payload.block_hash = compute_el_block_hash(
|
||||
spec, block.body.execution_payload, state
|
||||
)
|
||||
block.body.blob_kzg_commitments = blob_kzg_commitments
|
||||
return block, blobs, blob_kzg_proofs
|
||||
|
||||
|
||||
def get_block_with_blob_and_sidecars(spec, state, rng=None, blob_count=1):
|
||||
block, blobs, blob_kzg_proofs = get_block_with_blob(spec, state, rng=rng, blob_count=blob_count)
|
||||
cells_and_kzg_proofs = [_cached_compute_cells_and_kzg_proofs(spec, blob) for blob in blobs]
|
||||
|
||||
# We need a signed block to call `get_data_column_sidecars_from_block`
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
sidecars = spec.get_data_column_sidecars_from_block(signed_block, cells_and_kzg_proofs)
|
||||
return block, blobs, blob_kzg_proofs, signed_block, sidecars
|
||||
|
||||
|
||||
@cache
|
||||
def _cached_compute_cells_and_kzg_proofs(spec, blob):
|
||||
return spec.compute_cells_and_kzg_proofs(blob)
|
||||
|
||||
@@ -3,13 +3,14 @@ from typing import Any, NamedTuple
|
||||
|
||||
from eth_utils import encode_hex
|
||||
|
||||
from eth2spec.fulu.mainnet import DataColumnSidecar
|
||||
from eth2spec.test.exceptions import BlockNotFoundException
|
||||
from eth2spec.test.helpers.attestations import (
|
||||
next_epoch_with_attestations,
|
||||
next_slots_with_attestations,
|
||||
state_transition_with_full_block,
|
||||
)
|
||||
from eth2spec.test.helpers.forks import is_post_eip7732
|
||||
from eth2spec.test.helpers.forks import is_post_eip7732, is_post_fulu
|
||||
from eth2spec.test.helpers.state import (
|
||||
payload_state_transition,
|
||||
payload_state_transition_no_store,
|
||||
@@ -30,16 +31,29 @@ class BlobData(NamedTuple):
|
||||
"""
|
||||
|
||||
blobs: Sequence[Any]
|
||||
proofs: Sequence[bytes]
|
||||
proofs: Sequence[bytes] | None = None
|
||||
sidecars: Sequence[DataColumnSidecar] | None = None
|
||||
|
||||
|
||||
def with_blob_data(spec, blob_data, func):
|
||||
def with_blob_data(spec, blob_data: BlobData, func):
|
||||
if not is_post_fulu(spec):
|
||||
if blob_data.proofs is None:
|
||||
raise ValueError("blob_data.proofs must be provided when pre FULU fork")
|
||||
yield from with_blob_data_deneb(spec, blob_data, func)
|
||||
else:
|
||||
if blob_data.sidecars is None:
|
||||
raise ValueError("blob_data.sidecars must be provided when post FULU fork")
|
||||
yield from with_blob_data_fulu(spec, blob_data, func)
|
||||
|
||||
|
||||
def with_blob_data_deneb(spec, blob_data: BlobData, func):
|
||||
"""
|
||||
This helper runs the given ``func`` with monkeypatched ``retrieve_blobs_and_proofs``
|
||||
that returns ``blob_data.blobs, blob_data.proofs``.
|
||||
"""
|
||||
|
||||
def retrieve_blobs_and_proofs(beacon_block_root):
|
||||
def retrieve_blobs_and_proofs(_):
|
||||
assert blob_data.proofs is not None, "blob_data.proofs must be provided"
|
||||
return blob_data.blobs, blob_data.proofs
|
||||
|
||||
retrieve_blobs_and_proofs_backup = spec.retrieve_blobs_and_proofs
|
||||
@@ -61,6 +75,37 @@ def with_blob_data(spec, blob_data, func):
|
||||
assert is_called.value
|
||||
|
||||
|
||||
def with_blob_data_fulu(spec, blob_data: BlobData, func):
|
||||
"""
|
||||
This helper runs the given ``func`` with monkeypatched ``retrieve_column_sidecars``
|
||||
that returns ``blob_data``.
|
||||
"""
|
||||
|
||||
def retrieve_column_sidecars(_):
|
||||
assert blob_data.sidecars is not None, "blob_data.sidecars must be provided"
|
||||
if len(blob_data.sidecars) == 0:
|
||||
assert False, "Simulation: not all required columns have been sampled"
|
||||
return blob_data.sidecars
|
||||
|
||||
retrieve_column_sidecars_backup = spec.retrieve_column_sidecars
|
||||
spec.retrieve_column_sidecars = retrieve_column_sidecars
|
||||
|
||||
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_column_sidecars = retrieve_column_sidecars_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():
|
||||
@@ -77,7 +122,7 @@ def tick_and_add_block(
|
||||
merge_block=False,
|
||||
block_not_found=False,
|
||||
is_optimistic=False,
|
||||
blob_data=None,
|
||||
blob_data: BlobData | None = None,
|
||||
):
|
||||
pre_state = get_store_full_state(spec, store, signed_block.message.parent_root)
|
||||
if merge_block:
|
||||
@@ -189,6 +234,20 @@ def get_blobs_file_name(blobs=None, blobs_root=None):
|
||||
return f"blobs_{encode_hex(blobs_root)}"
|
||||
|
||||
|
||||
def get_sidecars_file_names(sidecars: Sequence[DataColumnSidecar]) -> Sequence[str]:
|
||||
"""
|
||||
Returns the file names for sidecars.
|
||||
"""
|
||||
return [get_sidecar_file_name(sidecar) for sidecar in sidecars]
|
||||
|
||||
|
||||
def get_sidecar_file_name(sidecar: DataColumnSidecar) -> str:
|
||||
"""
|
||||
Returns the file name for a single sidecar.
|
||||
"""
|
||||
return f"sidecar_{encode_hex(sidecar.hash_tree_root())}"
|
||||
|
||||
|
||||
def on_tick_and_append_step(spec, store, time, test_steps):
|
||||
assert time >= store.time
|
||||
spec.on_tick(store, time)
|
||||
@@ -237,18 +296,23 @@ def add_block(
|
||||
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
|
||||
if blob_data.sidecars is not None:
|
||||
for sidecar in blob_data.sidecars:
|
||||
yield get_sidecar_file_name(sidecar), sidecar
|
||||
|
||||
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,
|
||||
}
|
||||
)
|
||||
def _append_step(valid=True):
|
||||
if blob_data is not None:
|
||||
step = {
|
||||
"block": get_block_file_name(signed_block),
|
||||
"blobs": get_blobs_file_name(blobs_root=blobs_root),
|
||||
"valid": valid,
|
||||
}
|
||||
if blob_data.proofs is not None:
|
||||
step["proofs"] = [encode_hex(proof) for proof in blob_data.proofs]
|
||||
if blob_data.sidecars is not None:
|
||||
step["sidecars"] = get_sidecars_file_names(blob_data.sidecars)
|
||||
|
||||
test_steps.append(step)
|
||||
else:
|
||||
test_steps.append(
|
||||
{
|
||||
@@ -260,20 +324,20 @@ def add_block(
|
||||
if not valid:
|
||||
if is_optimistic:
|
||||
run_on_block(spec, store, signed_block, valid=True)
|
||||
_append_step(is_blob_data_test, valid=False)
|
||||
_append_step(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
|
||||
_append_step(is_blob_data_test, valid=False)
|
||||
_append_step(valid=False)
|
||||
return
|
||||
else:
|
||||
assert False
|
||||
else:
|
||||
run_on_block(spec, store, signed_block, valid=True)
|
||||
_append_step(is_blob_data_test)
|
||||
_append_step()
|
||||
|
||||
# An on_block step implies receiving block's attestations
|
||||
for attestation in signed_block.message.body.attestations:
|
||||
|
||||
@@ -84,6 +84,7 @@ The parameter that is required for executing `on_block(store, block)`.
|
||||
blobs: string -- optional, the name of the `blobs_<32-byte-root>.ssz_snappy` file.
|
||||
The blobs file content is a `List[Blob, MAX_BLOB_COMMITMENTS_PER_BLOCK]` SSZ object.
|
||||
proofs: array of byte48 hex string -- optional, the proofs of blob commitments.
|
||||
sidecars: string -- optional, array of the names of the `sidecar_<32-byte-root>.ssz_snappy` files.
|
||||
valid: bool -- optional, default to `true`.
|
||||
If it's `false`, this execution step is expected to be invalid.
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user