Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix spec typo and add test_prepare_execution_payload unit tests #2737

Merged
merged 1 commit into from
Nov 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion specs/merge/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def prepare_execution_payload(state: BeaconState,
execution_engine: ExecutionEngine) -> Optional[PayloadId]:
if not is_merge_complete(state):
is_terminal_block_hash_set = TERMINAL_BLOCK_HASH != Hash32()
is_activation_epoch_reached = get_current_epoch(state.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
is_activation_epoch_reached = get_current_epoch(state) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
if is_terminal_block_hash_set and not is_activation_epoch_reached:
# Terminal block hash is set but activation epoch is not yet reached, no prepare payload call is needed
return None
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from copy import deepcopy

from eth2spec.test.helpers.pow_block import (
prepare_random_pow_chain,
)
Expand Down Expand Up @@ -62,3 +64,99 @@ def test_get_pow_block_at_terminal_total_difficulty(spec, state):
assert pow_block is None
else:
raise Exception('Something is wrong')


SAMPLE_PAYLOAD_ID = b'\x12' * 8
# ('is_merge_complete', 'is_terminal_block_hash_set', 'is_activation_epoch_reached',
# 'terminal_pow_block_is_none', 'result_payload_id')
prepare_execution_payload_expected_results = [
(False, False, False, False, SAMPLE_PAYLOAD_ID),
(False, False, False, True, None),
(False, False, True, False, SAMPLE_PAYLOAD_ID),
(False, False, True, True, None),
(False, True, False, False, None),
(False, True, False, True, None),
(False, True, True, False, SAMPLE_PAYLOAD_ID),
(False, True, True, True, None),
(True, False, False, False, SAMPLE_PAYLOAD_ID),
(True, False, False, True, SAMPLE_PAYLOAD_ID),
(True, False, True, False, SAMPLE_PAYLOAD_ID),
(True, False, True, True, SAMPLE_PAYLOAD_ID),
(True, True, False, False, SAMPLE_PAYLOAD_ID),
(True, True, False, True, SAMPLE_PAYLOAD_ID),
(True, True, True, False, SAMPLE_PAYLOAD_ID),
(True, True, True, True, SAMPLE_PAYLOAD_ID),
]


@with_merge_and_later
@spec_state_test
def test_prepare_execution_payload(spec, state):
for result in prepare_execution_payload_expected_results:
(
is_merge_complete,
is_terminal_block_hash_set,
is_activation_epoch_reached,
terminal_pow_block_is_none,
result_payload_id,
) = result

# 1. Handle `is_merge_complete`
if is_merge_complete:
state.latest_execution_payload_header = spec.ExecutionPayloadHeader(random=b'\x12' * 32)
else:
state.latest_execution_payload_header = spec.ExecutionPayloadHeader()

# 2. `is_terminal_block_hash_set` and `is_activation_epoch_reached` require mocking configs in runtime
config_overrides = {}
_mock_terminal_block_hash = b'\x34' * 32
if is_terminal_block_hash_set:
config_overrides['TERMINAL_BLOCK_HASH'] = _mock_terminal_block_hash
else:
config_overrides['TERMINAL_BLOCK_HASH'] = spec.Hash32()

# Default `TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH` is too big and too close to overflow
_mock_terminal_block_hash_activation_epoch = 3
config_overrides['TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH'] = _mock_terminal_block_hash_activation_epoch
if is_activation_epoch_reached:
state.slot = _mock_terminal_block_hash_activation_epoch * spec.SLOTS_PER_EPOCH
else:
state.slot = (_mock_terminal_block_hash_activation_epoch - 1) * spec.SLOTS_PER_EPOCH

# Logic from `with_config_overrides`
old_config = spec.config
tmp_config = deepcopy(old_config._asdict())
tmp_config.update(config_overrides)
config_types = spec.Configuration.__annotations__
test_config = {k: config_types[k](v) for k, v in tmp_config.items()}
spec.config = spec.Configuration(**test_config)

# 3. Handle `terminal_pow_block_is_none`
pow_chain = prepare_random_pow_chain(spec, 2)
if terminal_pow_block_is_none:
pow_chain.head().total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - 1
else:
if is_terminal_block_hash_set:
pow_chain.head().block_hash = _mock_terminal_block_hash
pow_chain.head().total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY

# Dummy arguments
finalized_block_hash = b'\x56' * 32
suggested_fee_recipient = b'\x78' * 20

# Mock execution_engine
class TestEngine(spec.NoopExecutionEngine):
def notify_forkchoice_updated(self, parent_hash, finalized_block_hash, payload_attributes) -> bool:
return SAMPLE_PAYLOAD_ID

payload_id = spec.prepare_execution_payload(
state=state,
pow_chain=pow_chain.to_dict(),
finalized_block_hash=finalized_block_hash,
suggested_fee_recipient=suggested_fee_recipient,
execution_engine=TestEngine(),
)
assert payload_id == result_payload_id

# Restore config
spec.config = old_config