Added new Merge tests

This commit is contained in:
Dmitrii Shmatko
2021-09-14 11:51:37 +03:00
parent 9acf60fe07
commit 7ff173bd0b
7 changed files with 502 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils import bls
from eth2spec.utils.bls import only_with_bls
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
from eth2spec.utils.ssz.ssz_typing import uint256
def get_proposer_index_maybe(spec, state, slot, proposer_index=None):
@@ -122,3 +123,14 @@ def get_state_and_beacon_parent_root_at_slot(spec, state, slot):
previous_block_header.state_root = hash_tree_root(state)
beacon_parent_root = hash_tree_root(previous_block_header)
return state, beacon_parent_root
def prepare_empty_pow_block(spec):
return spec.PowBlock(
block_hash=spec.Hash32(),
parent_hash=spec.Hash32(),
is_processed=False,
is_valid=True,
total_difficulty=uint256(0),
difficulty=uint256(0)
)

View File

@@ -1,3 +1,6 @@
from remerkleable.byte_arrays import ByteVector
def build_empty_execution_payload(spec, state, randao_mix=None):
"""
Assuming a pre-state of the same slot, build a valid ExecutionPayload without any transactions.
@@ -64,3 +67,12 @@ def build_state_with_execution_payload_header(spec, state, execution_payload_hea
pre_state.latest_execution_payload_header = execution_payload_header
return pre_state
# damages last byte of the data by changing one bit
def screw_up_bytes(data: ByteVector):
assert len(data) > 0
length = data.vector_length()
raw_data = data.encode_bytes()
raw_data = raw_data[0:len(raw_data) - 1] + bytes([(raw_data[len(raw_data) - 1] ^ 1)])
return ByteVector[length](*raw_data)

View File

@@ -1,8 +1,12 @@
from remerkleable.byte_arrays import Bytes32
from eth2spec.utils.ssz.ssz_typing import uint64
from eth2spec.test.helpers.execution_payload import (
build_empty_execution_payload,
get_execution_payload_header,
build_state_with_incomplete_transition,
build_state_with_complete_transition,
screw_up_bytes
)
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later
from eth2spec.test.helpers.state import next_slot
@@ -199,3 +203,182 @@ def test_bad_timestamp_regular_payload(spec, state):
execution_payload.timestamp = execution_payload.timestamp + 1
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@spec_state_test
def test_bad_randao_first_payload(spec, state):
# pre-state
state = build_state_with_incomplete_transition(spec, state)
next_slot(spec, state)
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
good_randao: Bytes32 = execution_payload.random
bad_randao = screw_up_bytes(good_randao)
# still valid because randao is ignored on this stage
execution_payload.random = bad_randao
yield from run_execution_payload_processing(spec, state, execution_payload)
@with_merge_and_later
@spec_state_test
def test_bad_randao_regular_payload(spec, state):
# pre-state
state = build_state_with_complete_transition(spec, state)
next_slot(spec, state)
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
good_randao: Bytes32 = execution_payload.random
bad_randao = screw_up_bytes(good_randao)
execution_payload.random = bad_randao
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@spec_state_test
def test_gaslimit_zero_first_payload(spec, state):
# pre-state
state = build_state_with_incomplete_transition(spec, state)
next_slot(spec, state)
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
execution_payload.gas_limit = uint64(0)
yield from run_execution_payload_processing(spec, state, execution_payload)
@with_merge_and_later
@spec_state_test
def test_gaslimit_max_first_payload(spec, state):
# pre-state
state = build_state_with_incomplete_transition(spec, state)
next_slot(spec, state)
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
execution_payload.gas_limit = uint64(2**64 - 1)
yield from run_execution_payload_processing(spec, state, execution_payload)
@with_merge_and_later
@spec_state_test
def test_gaslimit_upper_plus_regular_payload(spec, state):
# pre-state
state = build_state_with_complete_transition(spec, state)
next_slot(spec, state)
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
execution_payload.gas_limit = execution_payload.gas_limit + \
execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@spec_state_test
def test_gaslimit_upper_regular_payload(spec, state):
# pre-state
state = build_state_with_complete_transition(spec, state)
next_slot(spec, state)
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
execution_payload.gas_limit = execution_payload.gas_limit + \
execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR - uint64(1)
yield from run_execution_payload_processing(spec, state, execution_payload)
@with_merge_and_later
@spec_state_test
def test_gaslimit_lower_minus_regular_payload(spec, state):
# pre-state
state = build_state_with_complete_transition(spec, state)
next_slot(spec, state)
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
execution_payload.gas_limit = execution_payload.gas_limit - \
execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@spec_state_test
def test_gaslimit_lower_regular_payload(spec, state):
# pre-state
state = build_state_with_complete_transition(spec, state)
next_slot(spec, state)
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
execution_payload.gas_limit = execution_payload.gas_limit - \
execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR + uint64(1)
yield from run_execution_payload_processing(spec, state, execution_payload)
@with_merge_and_later
@spec_state_test
def test_gaslimit_minimum_regular_payload(spec, state):
# pre-state
state = build_state_with_complete_transition(spec, state)
next_slot(spec, state)
state.latest_execution_payload_header.gas_limit = spec.MIN_GAS_LIMIT
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
execution_payload.gas_limit = execution_payload.gas_limit
yield from run_execution_payload_processing(spec, state, execution_payload)
@with_merge_and_later
@spec_state_test
def test_gaslimit_minimum_minus_regular_payload(spec, state):
# pre-state
state = build_state_with_complete_transition(spec, state)
next_slot(spec, state)
state.latest_execution_payload_header.gas_limit = spec.MIN_GAS_LIMIT
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
execution_payload.gas_limit = execution_payload.gas_limit - uint64(1)
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@spec_state_test
def test_gasused_gaslimit_regular_payload(spec, state):
# pre-state
state = build_state_with_complete_transition(spec, state)
next_slot(spec, state)
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
execution_payload.gas_used = execution_payload.gas_limit
yield from run_execution_payload_processing(spec, state, execution_payload)
@with_merge_and_later
@spec_state_test
def test_gasused_gaslimit_plus_regular_payload(spec, state):
# pre-state
state = build_state_with_complete_transition(spec, state)
next_slot(spec, state)
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
execution_payload.gas_used = execution_payload.gas_limit + uint64(1)
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)

View File

@@ -0,0 +1,174 @@
from eth2spec.utils.ssz.ssz_typing import uint64, uint256
from eth2spec.test.helpers.block import (
prepare_empty_pow_block
)
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later
def create_transition_store(spec):
anchor_block = prepare_empty_pow_block(spec)
transition_store = spec.get_transition_store(anchor_block)
return transition_store
class BlockNotFoundException(Exception):
pass
def run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload,
valid=True, block_lookup_success=True):
"""
Run ``process_merge_execution_payload``, yielding:
- transition store ('transition_store')
- current block ('block')
- parent block ('parent_block')
- execution payload ('payload')
If ``valid == False``, run expecting ``AssertionError``
If ``block_lookup_success == False``, run expecting ``BlockNotFoundException``
"""
yield 'transition_store', transition_store
yield 'block', block
yield 'parent_block', parent_block
yield 'payload', payload
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:
spec.process_merge_execution_payload(transition_store, 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_fail_invalid(spec, state):
transition_store = create_transition_store(spec)
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1)
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = transition_store.terminal_total_difficulty
assert spec.is_valid_terminal_pow_block(transition_store, block, parent_block)
block.is_valid = False
assert not spec.is_valid_terminal_pow_block(transition_store, block, parent_block)
@with_merge_and_later
@spec_state_test
def test_valid_terminal_pow_block_fail_before_terminal(spec, state):
transition_store = create_transition_store(spec)
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(2)
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1)
assert not spec.is_valid_terminal_pow_block(transition_store, block, parent_block)
@with_merge_and_later
@spec_state_test
def test_valid_terminal_pow_block_fail_just_after_terminal(spec, state):
transition_store = create_transition_store(spec)
parent_block = prepare_empty_pow_block(spec)
parent_block.total_difficulty = transition_store.terminal_total_difficulty
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.total_difficulty = transition_store.terminal_total_difficulty + uint256(1)
assert not spec.is_valid_terminal_pow_block(transition_store, block, parent_block)
@with_merge_and_later
@spec_state_test
def test_process_merge_execution_payload_success(spec, state):
transition_store = create_transition_store(spec)
parent_block = prepare_empty_pow_block(spec)
parent_block.block_hash = spec.Hash32(spec.hash(b'01'))
parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1)
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.is_processed = True
block.total_difficulty = transition_store.terminal_total_difficulty
payload = spec.ExecutionPayload()
payload.parent_hash = block.block_hash
yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload)
block.is_processed = False
yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, valid=False)
@with_merge_and_later
@spec_state_test
def test_process_merge_execution_payload_fail_block_lookup(spec, state):
transition_store = create_transition_store(spec)
parent_block = prepare_empty_pow_block(spec)
parent_block.block_hash = spec.Hash32(spec.hash(b'01'))
parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1)
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.is_processed = True
block.total_difficulty = transition_store.terminal_total_difficulty
payload = spec.ExecutionPayload()
payload.parent_hash = spec.Hash32(spec.hash(b'02'))
yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, block_lookup_success=False)
@with_merge_and_later
@spec_state_test
def test_process_merge_execution_payload_fail_parent_block_lookup(spec, state):
transition_store = create_transition_store(spec)
parent_block = prepare_empty_pow_block(spec)
parent_block.block_hash = spec.Hash32(spec.hash(b'01'))
parent_block.total_difficulty = transition_store.terminal_total_difficulty - uint256(1)
block = prepare_empty_pow_block(spec)
block.parent_hash = spec.Hash32(spec.hash(b'00'))
block.is_processed = True
block.total_difficulty = transition_store.terminal_total_difficulty
payload = spec.ExecutionPayload()
payload.parent_hash = block.block_hash
yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, block_lookup_success=False)
@with_merge_and_later
@spec_state_test
def test_process_merge_execution_payload_fail_after_terminal(spec, state):
transition_store = create_transition_store(spec)
parent_block = prepare_empty_pow_block(spec)
parent_block.block_hash = spec.Hash32(spec.hash(b'01'))
parent_block.total_difficulty = transition_store.terminal_total_difficulty
block = prepare_empty_pow_block(spec)
block.parent_hash = parent_block.block_hash
block.is_processed = True
block.total_difficulty = transition_store.terminal_total_difficulty + 1
payload = spec.ExecutionPayload()
payload.parent_hash = block.block_hash
yield from run_process_merge_execution_payload(spec, transition_store, block, parent_block, payload, valid=False)

View File

@@ -0,0 +1,121 @@
from eth2spec.utils.ssz.ssz_typing import uint64, uint256
from eth2spec.test.helpers.execution_payload import (
build_empty_execution_payload,
build_state_with_incomplete_transition,
build_state_with_complete_transition,
)
from eth2spec.test.helpers.block import (
prepare_empty_pow_block
)
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later
@with_merge_and_later
@spec_state_test
def test_fail_merge_complete(spec, state):
state = build_state_with_incomplete_transition(spec, state)
assert not spec.is_merge_complete(state)
@with_merge_and_later
@spec_state_test
def test_success_merge_complete(spec, state):
state = build_state_with_complete_transition(spec, state)
assert spec.is_merge_complete(state)
@with_merge_and_later
@spec_state_test
def test_fail_merge_block_false_false(spec, state):
state = build_state_with_complete_transition(spec, state)
execution_payload = spec.ExecutionPayload()
body = spec.BeaconBlockBody()
body.execution_payload = execution_payload
assert not spec.is_merge_block(state, body)
@with_merge_and_later
@spec_state_test
def test_fail_merge_block_false_true(spec, state):
state = build_state_with_complete_transition(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
body = spec.BeaconBlockBody()
body.execution_payload = execution_payload
assert not spec.is_merge_block(state, body)
@with_merge_and_later
@spec_state_test
def test_fail_merge_block_true_false(spec, state):
state = build_state_with_incomplete_transition(spec, state)
execution_payload = spec.ExecutionPayload()
body = spec.BeaconBlockBody()
body.execution_payload = execution_payload
assert not spec.is_merge_block(state, body)
@with_merge_and_later
@spec_state_test
def test_success_merge_block(spec, state):
state = build_state_with_incomplete_transition(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
body = spec.BeaconBlockBody()
body.execution_payload = execution_payload
assert spec.is_merge_block(state, body)
@with_merge_and_later
@spec_state_test
def test_fail_execution_enabled_false_false(spec, state):
state = build_state_with_incomplete_transition(spec, state)
execution_payload = spec.ExecutionPayload()
body = spec.BeaconBlockBody()
body.execution_payload = execution_payload
assert not spec.is_execution_enabled(state, body)
@with_merge_and_later
@spec_state_test
def test_success_execution_enabled_true_false(spec, state):
state = build_state_with_incomplete_transition(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
body = spec.BeaconBlockBody()
body.execution_payload = execution_payload
assert spec.is_execution_enabled(state, body)
@with_merge_and_later
@spec_state_test
def test_success_execution_enabled_false_true(spec, state):
state = build_state_with_complete_transition(spec, state)
execution_payload = spec.ExecutionPayload()
body = spec.BeaconBlockBody()
body.execution_payload = execution_payload
assert spec.is_execution_enabled(state, body)
@with_merge_and_later
@spec_state_test
def test_success_execution_enabled_true_true(spec, state):
state = build_state_with_complete_transition(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
body = spec.BeaconBlockBody()
body.execution_payload = execution_payload
assert spec.is_execution_enabled(state, body)
def compute_terminal_total_difficulty_reference(spec) -> uint256:
seconds_per_voting_period = spec.EPOCHS_PER_ETH1_VOTING_PERIOD * spec.SLOTS_PER_EPOCH * spec.config.SECONDS_PER_SLOT
pow_blocks_per_voting_period = seconds_per_voting_period // spec.config.SECONDS_PER_ETH1_BLOCK
pow_blocks_to_merge = spec.TARGET_SECONDS_TO_MERGE // spec.config.SECONDS_PER_ETH1_BLOCK
pow_blocks_after_anchor_block = spec.config.ETH1_FOLLOW_DISTANCE + pow_blocks_per_voting_period + pow_blocks_to_merge
return spec.config.MIN_ANCHOR_POW_BLOCK_DIFFICULTY * uint256(pow_blocks_after_anchor_block)
@with_merge_and_later
@spec_state_test
def test_zero_difficulty(spec, state):
anchor_pow_block = prepare_empty_pow_block(spec)
reference_td = compute_terminal_total_difficulty_reference(spec)
assert spec.compute_terminal_total_difficulty(anchor_pow_block) == reference_td