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

Ensure merge transition block contains 🐼 #2875

Closed
wants to merge 3 commits into from
Closed
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: 2 additions & 0 deletions specs/bellatrix/fork-choice.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ def validate_merge_block(block: BeaconBlock) -> None:
and a client software MAY delay a call to ``validate_merge_block``
until the PoW block(s) become available.
"""
assert '🐼'.encode('utf8') in block.body.graffiti

if TERMINAL_BLOCK_HASH != Hash32():
# If `TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached.
assert compute_epoch_at_slot(block.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
Expand Down
8 changes: 7 additions & 1 deletion specs/bellatrix/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,10 @@ All validator responsibilities remain unchanged other than those noted below. Na

To obtain an execution payload, a block proposer building a block on top of a `state` must take the following actions:

1. Set `payload_id = prepare_execution_payload(state, pow_chain, safe_block_hash, finalized_block_hash, suggested_fee_recipient, execution_engine)`, where:
1. Set `payload_id = prepare_execution_payload(state, pow_chain, graffiti, safe_block_hash, finalized_block_hash, suggested_fee_recipient, execution_engine)`, where:
* `state` is the state object after applying `process_slots(state, slot)` transition to the resulting state of the parent block processing
* `pow_chain` is a `Dict[Hash32, PowBlock]` dictionary that abstractly represents all blocks in the PoW chain with block hash as the dictionary key
* `graffiti` is the configured value to use as the beacon block's graffiti
* `safe_block_hash` is the return value of the `get_safe_execution_payload_hash(store: Store)` function call
* `finalized_block_hash` is the hash of the latest finalized execution payload (`Hash32()` if none yet finalized)
* `suggested_fee_recipient` is the value suggested to be used for the `fee_recipient` field of the execution payload
Expand All @@ -121,11 +122,16 @@ To obtain an execution payload, a block proposer building a block on top of a `s
```python
def prepare_execution_payload(state: BeaconState,
pow_chain: Dict[Hash32, PowBlock],
graffiti: Bytes32,
safe_block_hash: Hash32,
finalized_block_hash: Hash32,
suggested_fee_recipient: ExecutionAddress,
execution_engine: ExecutionEngine) -> Optional[PayloadId]:
if not is_merge_transition_complete(state):
if '🐼'.encode('utf8') not in graffiti:
# Incompatible graffiti for merge transition block
return None

is_terminal_block_hash_set = TERMINAL_BLOCK_HASH != Hash32()
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:
Expand Down
5 changes: 5 additions & 0 deletions specs/capella/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,16 @@ def get_expected_withdrawals(state: BeaconState) -> Sequence[Withdrawal]:
```python
def prepare_execution_payload(state: BeaconState,
pow_chain: Dict[Hash32, PowBlock],
graffiti: Bytes32,
safe_block_hash: Hash32,
finalized_block_hash: Hash32,
suggested_fee_recipient: ExecutionAddress,
execution_engine: ExecutionEngine) -> Optional[PayloadId]:
if not is_merge_transition_complete(state):
if '🐼'.encode('utf8') not in graffiti:
# Incompatible graffiti for merge transition block
return None

is_terminal_block_hash_set = TERMINAL_BLOCK_HASH != Hash32()
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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,30 @@ def test_validate_merge_block_fail_after_terminal(spec, state):
run_validate_merge_block(spec, pow_chain, block, valid=False)


@with_bellatrix_and_later
@spec_state_test
def test_validate_merge_block_success_inline_panda(spec, state):
pow_chain = prepare_random_pow_chain(spec, 2)
pow_chain.head(-1).total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
pow_chain.head().total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
block = build_empty_block_for_next_slot(spec, state)
block.body.graffiti = b'\x00' * 8 + '🐼'.encode('utf8') + b'\x00' * 20
block.body.execution_payload.parent_hash = pow_chain.head().block_hash
run_validate_merge_block(spec, pow_chain, block)


@with_bellatrix_and_later
@spec_state_test
def test_validate_merge_block_fail_no_panda(spec, state):
pow_chain = prepare_random_pow_chain(spec, 2)
pow_chain.head(-1).total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
pow_chain.head().total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
block = build_empty_block_for_next_slot(spec, state)
block.body.graffiti = b'\x00' * 32
block.body.execution_payload.parent_hash = pow_chain.head().block_hash
run_validate_merge_block(spec, pow_chain, block, valid=False)


@with_bellatrix_and_later
@spec_configured_state_test({
'TERMINAL_BLOCK_HASH': TERMINAL_BLOCK_HASH_CONFIG_VAR,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,40 @@ def test_get_pow_block_at_terminal_total_difficulty(spec, state):

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')
# 'terminal_pow_block_is_none', 'is_graffiti_compatible', '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),
(False, False, False, False, True, SAMPLE_PAYLOAD_ID),
(False, False, False, True, True, None),
(False, False, True, False, True, SAMPLE_PAYLOAD_ID),
(False, False, True, True, True, None),
(False, True, False, False, True, None),
(False, True, False, True, True, None),
(False, True, True, False, True, SAMPLE_PAYLOAD_ID),
(False, True, True, True, True, None),
(True, False, False, False, True, SAMPLE_PAYLOAD_ID),
(True, False, False, True, True, SAMPLE_PAYLOAD_ID),
(True, False, True, False, True, SAMPLE_PAYLOAD_ID),
(True, False, True, True, True, SAMPLE_PAYLOAD_ID),
(True, True, False, False, True, SAMPLE_PAYLOAD_ID),
(True, True, False, True, True, SAMPLE_PAYLOAD_ID),
(True, True, True, False, True, SAMPLE_PAYLOAD_ID),
(True, True, True, True, True, SAMPLE_PAYLOAD_ID),
(False, False, False, False, False, None),
(False, False, False, True, False, None),
(False, False, True, False, False, None),
(False, False, True, True, False, None),
(False, True, False, False, False, None),
(False, True, False, True, False, None),
(False, True, True, False, False, None),
(False, True, True, True, False, None),
(True, False, False, False, False, SAMPLE_PAYLOAD_ID),
(True, False, False, True, False, SAMPLE_PAYLOAD_ID),
(True, False, True, False, False, SAMPLE_PAYLOAD_ID),
(True, False, True, True, False, SAMPLE_PAYLOAD_ID),
(True, True, False, False, False, SAMPLE_PAYLOAD_ID),
(True, True, False, True, False, SAMPLE_PAYLOAD_ID),
(True, True, True, False, False, SAMPLE_PAYLOAD_ID),
(True, True, True, True, False, SAMPLE_PAYLOAD_ID),
]


Expand All @@ -99,6 +115,7 @@ def test_prepare_execution_payload(spec, state):
is_terminal_block_hash_set,
is_activation_epoch_reached,
terminal_pow_block_is_none,
is_graffiti_compatible,
result_payload_id,
) = result

Expand Down Expand Up @@ -141,6 +158,12 @@ def test_prepare_execution_payload(spec, state):
pow_chain.head().block_hash = _mock_terminal_block_hash
pow_chain.head().total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY

# 4. Handle `is_graffiti_compatible`
if is_graffiti_compatible:
graffiti = '🐼'.encode('utf8') * 8
else:
graffiti = b'\x00' * 32

# Dummy arguments
finalized_block_hash = b'\x56' * 32
safe_block_hash = b'\x58' * 32
Expand All @@ -158,6 +181,7 @@ def notify_forkchoice_updated(self,
payload_id = spec.prepare_execution_payload(
state=state,
pow_chain=pow_chain.to_dict(),
graffiti=graffiti,
safe_block_hash=safe_block_hash,
finalized_block_hash=finalized_block_hash,
suggested_fee_recipient=suggested_fee_recipient,
Expand Down
1 change: 1 addition & 0 deletions tests/core/pyspec/eth2spec/test/helpers/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def build_empty_block(spec, state, slot=None):
empty_block.proposer_index = spec.get_beacon_proposer_index(state)
empty_block.body.eth1_data.deposit_count = state.eth1_deposit_index
empty_block.parent_root = parent_block_root
empty_block.body.graffiti = '🐼'.encode('utf8') + b'\x00' * 28

apply_randao_reveal(spec, state, empty_block)

Expand Down