diff --git a/specs/merge/validator.md b/specs/merge/validator.md index bd3c9585e..678f7fe00 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -49,11 +49,10 @@ Please see related Beacon Chain doc before continuing and use them as a referenc ### `get_pow_block_at_terminal_total_difficulty` ```python -def get_pow_block_at_terminal_total_difficulty(pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: +def get_pow_block_at_terminal_total_difficulty(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlock]: # `pow_chain` abstractly represents all blocks in the PoW chain for block in pow_chain: - parent = get_pow_block(block.parent_hash) - assert parent is not None + parent = pow_chain[block.parent_hash] block_reached_ttd = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY parent_reached_ttd = parent.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY if block_reached_ttd and not parent_reached_ttd: @@ -65,13 +64,13 @@ def get_pow_block_at_terminal_total_difficulty(pow_chain: Sequence[PowBlock]) -> ### `get_terminal_pow_block` ```python -def get_terminal_pow_block(pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: +def get_terminal_pow_block(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlock]: if TERMINAL_BLOCK_HASH != Hash32(): # Terminal block hash override takes precedence over terminal total difficulty - pow_block_overrides = [block for block in pow_chain if block.block_hash == TERMINAL_BLOCK_HASH] - if not any(pow_block_overrides): + if TERMINAL_BLOCK_HASH in pow_chain: + return pow_chain[TERMINAL_BLOCK_HASH] + else: return None - return pow_block_overrides[0] return get_pow_block_at_terminal_total_difficulty(pow_chain) ``` @@ -133,14 +132,14 @@ To obtain an execution payload, a block proposer building a block on top of a `s 1. Set `payload_id = prepare_execution_payload(state, pow_chain, finalized_block_hash, 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 list that abstractly represents all blocks in the PoW chain + * `pow_chain` is a `Dict[Hash32, PowBlock]` dictionary that abstractly represents all blocks in the PoW chain with block hash as the dictionary key * `finalized_block_hash` is the hash of the latest finalized execution payload (`Hash32()` if none yet finalized) * `fee_recipient` is the value suggested to be used for the `coinbase` field of the execution payload ```python def prepare_execution_payload(state: BeaconState, - pow_chain: Sequence[PowBlock], + pow_chain: Dict[Hash32, PowBlock], finalized_block_hash: Hash32, fee_recipient: ExecutionAddress, execution_engine: ExecutionEngine) -> Optional[PayloadId]: diff --git a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py index c30320066..8d9e67998 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py +++ b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py @@ -275,10 +275,38 @@ def test_invalid_current_source_root(spec, state): state.previous_justified_checkpoint = spec.Checkpoint(epoch=3, root=b'\x01' * 32) state.current_justified_checkpoint = spec.Checkpoint(epoch=4, root=b'\x32' * 32) - attestation = get_valid_attestation(spec, state, slot=(spec.SLOTS_PER_EPOCH * 3) + 1) + next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY) + + attestation = get_valid_attestation(spec, state, slot=spec.SLOTS_PER_EPOCH * 5) + + # Test logic sanity checks: + assert attestation.data.target.epoch == spec.get_current_epoch(state) + assert state.current_justified_checkpoint.root != state.previous_justified_checkpoint.root + assert attestation.data.source.root == state.current_justified_checkpoint.root + + # Make attestation source root invalid: should be current justified, not previous one + attestation.data.source.root = state.previous_justified_checkpoint.root + + sign_attestation(spec, state, attestation) + + yield from run_attestation_processing(spec, state, attestation, False) + + +@with_all_phases +@spec_state_test +def test_invalid_previous_source_root(spec, state): + next_slots(spec, state, spec.SLOTS_PER_EPOCH * 5) + + state.finalized_checkpoint.epoch = 2 + + state.previous_justified_checkpoint = spec.Checkpoint(epoch=3, root=b'\x01' * 32) + state.current_justified_checkpoint = spec.Checkpoint(epoch=4, root=b'\x32' * 32) + + attestation = get_valid_attestation(spec, state, slot=(spec.SLOTS_PER_EPOCH * 4) + 1) next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY) # Test logic sanity checks: + assert attestation.data.target.epoch == spec.get_previous_epoch(state) assert state.current_justified_checkpoint.root != state.previous_justified_checkpoint.root assert attestation.data.source.root == state.previous_justified_checkpoint.root