Refine validate_merge_block unit tests

This commit is contained in:
Mikhail Kalinin 2021-11-05 19:52:21 +06:00
parent 116017d4cb
commit 56bb393eb5
6 changed files with 220 additions and 155 deletions

View File

@ -1,7 +1,5 @@
from random import Random
from eth_utils import encode_hex
from eth2spec.test.exceptions import BlockNotFoundException
from eth2spec.utils.ssz.ssz_typing import uint256
from eth2spec.test.helpers.attestations import (
next_epoch_with_attestations,
next_slots_with_attestations,
@ -247,15 +245,6 @@ def apply_next_slots_with_attestations(spec,
return post_state, store, last_signed_block
def prepare_empty_pow_block(spec, rng=Random(3131)):
return spec.PowBlock(
block_hash=spec.Hash32(spec.hash(bytearray(rng.getrandbits(8) for _ in range(32)))),
parent_hash=spec.Hash32(spec.hash(bytearray(rng.getrandbits(8) for _ in range(32)))),
total_difficulty=uint256(0),
difficulty=uint256(0)
)
def get_pow_block_file_name(pow_block):
return f"pow_block_{encode_hex(pow_block.block_hash)}"

View File

@ -0,0 +1,20 @@
from random import Random
from eth2spec.utils.ssz.ssz_typing import uint256
def prepare_empty_pow_block(spec, rng=Random(3131)):
return spec.PowBlock(
block_hash=spec.Hash32(spec.hash(bytearray(rng.getrandbits(8) for _ in range(32)))),
parent_hash=spec.Hash32(spec.hash(bytearray(rng.getrandbits(8) for _ in range(32)))),
total_difficulty=uint256(0),
difficulty=uint256(0)
)
def prepare_random_pow_chain(spec, length, rng=Random(3131)):
assert length > 0
chain = [prepare_empty_pow_block(spec, rng)]
for i in range(1, length):
chain.append(prepare_empty_pow_block(spec, rng))
chain[i].parent_hash = chain[i - 1].block_hash
return chain

View File

@ -13,9 +13,11 @@ from eth2spec.test.helpers.state import (
state_transition_and_sign_block,
)
from eth2spec.test.helpers.fork_choice import (
prepare_empty_pow_block,
add_pow_block,
)
from eth2spec.test.helpers.pow_block import (
prepare_empty_pow_block,
)
from eth2spec.test.helpers.execution_payload import (
build_state_with_incomplete_transition,
)

View File

@ -0,0 +1,44 @@
from eth2spec.utils.ssz.ssz_typing import uint256
from eth2spec.test.helpers.pow_block import (
prepare_empty_pow_block,
)
from eth2spec.test.context import (
spec_state_test,
with_merge_and_later,
)
@with_merge_and_later
@spec_state_test
def test_is_valid_terminal_pow_block_success_valid(spec, state):
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
assert spec.is_valid_terminal_pow_block(block, parent_block)
@with_merge_and_later
@spec_state_test
def test_is_valid_terminal_pow_block_fail_before_terminal(spec, state):
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2)
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
assert not spec.is_valid_terminal_pow_block(block, parent_block)
@with_merge_and_later
@spec_state_test
def test_is_valid_terminal_pow_block_fail_just_after_terminal(spec, state):
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1)
assert not spec.is_valid_terminal_pow_block(block, parent_block)

View File

@ -1,143 +0,0 @@
from eth2spec.test.exceptions import BlockNotFoundException
from eth2spec.utils.ssz.ssz_typing import uint256
from eth2spec.test.helpers.fork_choice import (
prepare_empty_pow_block,
)
from eth2spec.test.context import spec_state_test, with_merge_and_later
# Copy of conditional merge part of `on_block(store: Store, signed_block: SignedBeaconBlock)` handler
def validate_transition_execution_payload(spec, execution_payload):
pow_block = spec.get_pow_block(execution_payload.parent_hash)
pow_parent = spec.get_pow_block(pow_block.parent_hash)
assert spec.is_valid_terminal_pow_block(pow_block, pow_parent)
def run_validate_transition_execution_payload(spec, block, parent_block, payload,
valid=True, block_lookup_success=True):
"""
Run ``validate_transition_execution_payload``
If ``valid == False``, run expecting ``AssertionError``
If ``block_lookup_success == False``, run expecting ``BlockNotFoundException``
"""
def get_pow_block(hash: spec.Bytes32) -> spec.PowBlock:
if hash == block.block_hash:
return block
elif hash == parent_block.block_hash:
return parent_block
else:
raise BlockNotFoundException()
save_pow_block = spec.get_pow_block
# Guido authorized everyone to do this
spec.get_pow_block = get_pow_block
exception_caught = False
block_not_found_exception_caught = False
try:
validate_transition_execution_payload(spec, payload)
except BlockNotFoundException:
block_not_found_exception_caught = True
except AssertionError:
exception_caught = True
except Exception as e:
spec.get_pow_block = save_pow_block
raise e
spec.get_pow_block = save_pow_block
if block_lookup_success:
assert not block_not_found_exception_caught
else:
assert block_not_found_exception_caught
if valid:
assert not exception_caught
else:
assert exception_caught
@with_merge_and_later
@spec_state_test
def test_valid_terminal_pow_block_success_valid(spec, state):
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
assert spec.is_valid_terminal_pow_block(block, parent_block)
@with_merge_and_later
@spec_state_test
def test_valid_terminal_pow_block_fail_before_terminal(spec, state):
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2)
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
assert not spec.is_valid_terminal_pow_block(block, parent_block)
@with_merge_and_later
@spec_state_test
def test_valid_terminal_pow_block_fail_just_after_terminal(spec, state):
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1)
assert not spec.is_valid_terminal_pow_block(block, parent_block)
@with_merge_and_later
@spec_state_test
def test_validate_transition_execution_payload_success(spec, state):
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
payload = spec.ExecutionPayload()
payload.parent_hash = block.block_hash
run_validate_transition_execution_payload(spec, block, parent_block, payload)
@with_merge_and_later
@spec_state_test
def test_validate_transition_execution_payload_fail_block_lookup(spec, state):
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
payload = spec.ExecutionPayload()
run_validate_transition_execution_payload(spec, block, parent_block, payload,
block_lookup_success=False)
@with_merge_and_later
@spec_state_test
def test_validate_transition_execution_payload_fail_parent_block_lookup(spec, state):
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
block = prepare_empty_pow_block(spec)
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
payload = spec.ExecutionPayload()
payload.parent_hash = block.block_hash
run_validate_transition_execution_payload(spec, block, parent_block, payload,
block_lookup_success=False)
@with_merge_and_later
@spec_state_test
def test_validate_transition_execution_payload_fail_after_terminal(spec, state):
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + 1
payload = spec.ExecutionPayload()
payload.parent_hash = block.block_hash
run_validate_transition_execution_payload(spec, block, parent_block, payload, valid=False)

View File

@ -0,0 +1,153 @@
from typing import Optional
from eth2spec.utils.ssz.ssz_typing import uint256, Bytes32
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot,
)
from eth2spec.test.helpers.pow_block import (
prepare_random_pow_chain,
)
from eth2spec.test.context import (
spec_state_test,
with_merge_and_later,
spec_configured_state_test
)
TERMINAL_BLOCK_HASH_CONFIG_VAR = '0x0000000000000000000000000000000000000000000000000000000000000001'
TERMINAL_BLOCK_HASH = Bytes32(TERMINAL_BLOCK_HASH_CONFIG_VAR)
def run_validate_merge_block(spec, pow_chain, beacon_block, valid=True):
"""
Run ``validate_merge_block``
If ``valid == False``, run expecting ``AssertionError``
"""
def get_pow_block(hash: spec.Bytes32) -> Optional[spec.PowBlock]:
for block in pow_chain:
if block.block_hash == hash:
return block
return None
get_pow_block_backup = spec.get_pow_block
# Guido authorized everyone to do this
spec.get_pow_block = get_pow_block
assertion_error_caught = False
try:
spec.validate_merge_block(beacon_block)
except AssertionError:
assertion_error_caught = True
except Exception as e:
spec.get_pow_block = get_pow_block_backup
raise e
spec.get_pow_block = get_pow_block_backup
if valid:
assert not assertion_error_caught
else:
assert assertion_error_caught
@with_merge_and_later
@spec_state_test
def test_validate_merge_block_success(spec, state):
pow_chain = prepare_random_pow_chain(spec, 2)
pow_chain[0].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
pow_chain[1].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
block = build_empty_block_for_next_slot(spec, state)
block.body.execution_payload.parent_hash = pow_chain[1].block_hash
run_validate_merge_block(spec, pow_chain, block)
@with_merge_and_later
@spec_state_test
def test_validate_merge_block_fail_block_lookup(spec, state):
pow_chain = prepare_random_pow_chain(spec, 2)
pow_chain[0].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
pow_chain[1].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
block = build_empty_block_for_next_slot(spec, state)
run_validate_merge_block(spec, pow_chain, block, valid=False)
@with_merge_and_later
@spec_state_test
def test_validate_merge_block_fail_parent_block_lookup(spec, state):
pow_chain = prepare_random_pow_chain(spec, 1)
pow_chain[0].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
block = build_empty_block_for_next_slot(spec, state)
block.body.execution_payload.parent_hash = pow_chain[0].block_hash
run_validate_merge_block(spec, pow_chain, block, valid=False)
@with_merge_and_later
@spec_state_test
def test_validate_merge_block_fail_after_terminal(spec, state):
pow_chain = prepare_random_pow_chain(spec, 2)
pow_chain[0].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
pow_chain[1].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1)
block = build_empty_block_for_next_slot(spec, state)
block.body.execution_payload.parent_hash = pow_chain[1].block_hash
run_validate_merge_block(spec, pow_chain, block, valid=False)
@with_merge_and_later
@spec_configured_state_test({
'TERMINAL_BLOCK_HASH': TERMINAL_BLOCK_HASH_CONFIG_VAR,
'TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH': '0'
})
def test_validate_merge_block_tbh_override_success(spec, state):
pow_chain = prepare_random_pow_chain(spec, 2)
# should fail if TTD check is reached
pow_chain[0].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2)
pow_chain[1].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
pow_chain[1].block_hash = TERMINAL_BLOCK_HASH
block = build_empty_block_for_next_slot(spec, state)
block.body.execution_payload.parent_hash = pow_chain[1].block_hash
run_validate_merge_block(spec, pow_chain, block)
@with_merge_and_later
@spec_configured_state_test({
'TERMINAL_BLOCK_HASH': TERMINAL_BLOCK_HASH_CONFIG_VAR,
'TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH': '0'
})
def test_validate_merge_block_fail_parent_hash_is_not_tbh(spec, state):
pow_chain = prepare_random_pow_chain(spec, 2)
# shouldn't fail if TTD check is reached
pow_chain[0].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
pow_chain[1].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
block = build_empty_block_for_next_slot(spec, state)
block.body.execution_payload.parent_hash = pow_chain[1].block_hash
run_validate_merge_block(spec, pow_chain, block, valid=False)
@with_merge_and_later
@spec_configured_state_test({
'TERMINAL_BLOCK_HASH': TERMINAL_BLOCK_HASH_CONFIG_VAR,
'TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH': '1'
})
def test_validate_merge_block_terminal_block_hash_fail_activation_not_reached(spec, state):
pow_chain = prepare_random_pow_chain(spec, 2)
# shouldn't fail if TTD check is reached
pow_chain[0].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
pow_chain[1].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
pow_chain[1].block_hash = TERMINAL_BLOCK_HASH
block = build_empty_block_for_next_slot(spec, state)
block.body.execution_payload.parent_hash = pow_chain[1].block_hash
run_validate_merge_block(spec, pow_chain, block, valid=False)
@with_merge_and_later
@spec_configured_state_test({
'TERMINAL_BLOCK_HASH': TERMINAL_BLOCK_HASH_CONFIG_VAR,
'TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH': '1'
})
def test_validate_merge_block_fail_activation_not_reached_parent_hash_is_not_tbh(spec, state):
pow_chain = prepare_random_pow_chain(spec, 2)
# shouldn't fail if TTD check is reached
pow_chain[0].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
pow_chain[1].total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
block = build_empty_block_for_next_slot(spec, state)
block.body.execution_payload.parent_hash = pow_chain[1].block_hash
run_validate_merge_block(spec, pow_chain, block, valid=False)