diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index 99359845a8..051c6ef8d6 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -240,6 +240,11 @@ def on_block(store: Store, block: BeaconBlock) -> None: def on_attestation(store: Store, attestation: Attestation) -> None: target = attestation.data.target + # Attestations must be from the current or previous epoch + current_epoch = compute_epoch_at_slot(get_current_slot(store)) + # Use GENESIS_EPOCH for previous when genesis to avoid underflow + previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else GENESIS_EPOCH + assert target.epoch in [current_epoch, previous_epoch] # Cannot calculate the current shuffling if have not seen the target assert target.root in store.blocks diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 236d12a885..f6c3b55daf 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -289,6 +289,8 @@ A validator is expected to create, sign, and broadcast an attestation during eac A validator should create and broadcast the `attestation` to the associated attestation subnet one-third of the way through the `slot` during which the validator is assigned―that is, `SECONDS_PER_SLOT / 3` seconds after the start of `slot`. +*Note*: Although attestations during `GENESIS_EPOCH` do not count toward FFG finality, these initial attestations do give weight to the fork choice, are rewarded fork, and should be made. + #### Attestation data First, the validator should construct `attestation_data`, an [`AttestationData`](../core/0_beacon-chain.md#attestationdata) object based upon the state at the assigned slot. diff --git a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py index 39c179b59a..8db55cce8a 100644 --- a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py +++ b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py @@ -27,10 +27,9 @@ def run_on_attestation(spec, state, store, attestation, valid=True): @with_all_phases @spec_state_test -def test_on_attestation(spec, state): +def test_on_attestation_current_epoch(spec, state): store = spec.get_genesis_store(state) - time = 100 - spec.on_tick(store, time) + spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * 2) block = build_empty_block_for_next_slot(spec, state) state_transition_and_sign_block(spec, state, block) @@ -39,9 +38,53 @@ def test_on_attestation(spec, state): spec.on_block(store, block) attestation = get_valid_attestation(spec, state, slot=block.slot) + assert attestation.data.target.epoch == spec.GENESIS_EPOCH + assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH + run_on_attestation(spec, state, store, attestation) +@with_all_phases +@spec_state_test +def test_on_attestation_previous_epoch(spec, state): + store = spec.get_genesis_store(state) + spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH) + + block = build_empty_block_for_next_slot(spec, state) + state_transition_and_sign_block(spec, state, block) + + # store block in store + spec.on_block(store, block) + + attestation = get_valid_attestation(spec, state, slot=block.slot) + assert attestation.data.target.epoch == spec.GENESIS_EPOCH + assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH + 1 + + run_on_attestation(spec, state, store, attestation) + + +@with_all_phases +@spec_state_test +def test_on_attestation_past_epoch(spec, state): + store = spec.get_genesis_store(state) + + # move time forward 2 epochs + time = store.time + 2 * spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH + spec.on_tick(store, time) + + # create and store block from 3 epochs ago + block = build_empty_block_for_next_slot(spec, state) + state_transition_and_sign_block(spec, state, block) + spec.on_block(store, block) + + # create attestation for past block + attestation = get_valid_attestation(spec, state, slot=state.slot) + assert attestation.data.target.epoch == spec.GENESIS_EPOCH + assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH + 2 + + run_on_attestation(spec, state, store, attestation, False) + + @with_all_phases @spec_state_test def test_on_attestation_target_not_in_store(spec, state): @@ -75,8 +118,7 @@ def test_on_attestation_future_epoch(spec, state): spec.on_block(store, block) # move state forward but not store - attestation_slot = block.slot + spec.SLOTS_PER_EPOCH - state.slot = attestation_slot + state.slot = block.slot + spec.SLOTS_PER_EPOCH attestation = get_valid_attestation(spec, state, slot=state.slot) run_on_attestation(spec, state, store, attestation, False)