From 04fb9926e819535d32d5c22ed8616c4a0b8a25e9 Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Fri, 12 Jun 2020 17:16:08 +0100 Subject: [PATCH] Remove custody bits from phase 1 and tests --- specs/phase1/beacon-chain.md | 131 +----------------- specs/phase1/custody-game.md | 18 +-- specs/phase1/fork-choice.md | 34 ----- .../test/fork_choice/test_on_attestation.py | 7 +- .../eth2spec/test/helpers/attestations.py | 82 +---------- .../test/helpers/attester_slashings.py | 24 +--- .../test_process_attester_slashing.py | 7 +- .../test_process_attestation.py | 17 +-- 8 files changed, 27 insertions(+), 293 deletions(-) diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index 61e30e102..04c28f5ab 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -52,15 +52,12 @@ - [`get_shard_committee`](#get_shard_committee) - [`get_light_client_committee`](#get_light_client_committee) - [`get_shard_proposer_index`](#get_shard_proposer_index) - - [`get_indexed_attestation`](#get_indexed_attestation) - [`get_committee_count_delta`](#get_committee_count_delta) - [`get_start_shard`](#get_start_shard) - [`get_shard`](#get_shard) - [`get_latest_slot_for_shard`](#get_latest_slot_for_shard) - [`get_offset_slots`](#get_offset_slots) - [Predicates](#predicates) - - [`verify_attestation_custody`](#verify_attestation_custody) - - [Updated `is_valid_indexed_attestation`](#updated-is_valid_indexed_attestation) - [`is_on_time_attestation`](#is_on_time_attestation) - [`is_winning_attestation`](#is_winning_attestation) - [`optional_aggregate_verify`](#optional_aggregate_verify) @@ -77,7 +74,6 @@ - [`verify_empty_shard_transition`](#verify_empty_shard_transition) - [`process_shard_transitions`](#process_shard_transitions) - [New default validator for deposits](#new-default-validator-for-deposits) - - [New Attester slashing processing](#new-attester-slashing-processing) - [Light client processing](#light-client-processing) - [Epoch transition](#epoch-transition) - [Phase 1 final updates](#phase-1-final-updates) @@ -192,7 +188,6 @@ class AttestationData(Container): class Attestation(Container): aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] data: AttestationData - custody_bits_blocks: List[Bitlist[MAX_VALIDATORS_PER_COMMITTEE], MAX_SHARD_BLOCKS_PER_ATTESTATION] signature: BLSSignature ``` @@ -212,8 +207,9 @@ class PendingAttestation(Container): ```python class IndexedAttestation(Container): - committee: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE] - attestation: Attestation + attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE] + data: AttestationData + signature: BLSSignature ``` ### Extended `AttesterSlashing` @@ -599,17 +595,6 @@ def get_shard_proposer_index(beacon_state: BeaconState, slot: Slot, shard: Shard return committee[r % len(committee)] ``` -#### `get_indexed_attestation` - -```python -def get_indexed_attestation(beacon_state: BeaconState, attestation: Attestation) -> IndexedAttestation: - committee = get_beacon_committee(beacon_state, attestation.data.slot, attestation.data.index) - return IndexedAttestation( - committee=committee, - attestation=attestation, - ) -``` - #### `get_committee_count_delta` ```python @@ -679,65 +664,6 @@ def get_offset_slots(state: BeaconState, shard: Shard) -> Sequence[Slot]: ### Predicates -#### `verify_attestation_custody` - -```python -def verify_attestation_custody(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool: - """ - Check if ``indexed_attestation`` has valid signature against non-empty custody bits. - """ - attestation = indexed_attestation.attestation - aggregation_bits = attestation.aggregation_bits - domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.data.target.epoch) - all_pubkeys = [] - all_signing_roots = [] - for block_index, custody_bits in enumerate(attestation.custody_bits_blocks): - assert len(custody_bits) == len(indexed_attestation.committee) - for participant, aggregation_bit, custody_bit in zip( - indexed_attestation.committee, aggregation_bits, custody_bits - ): - if aggregation_bit: - all_pubkeys.append(state.validators[participant].pubkey) - # Note: only 2N distinct message hashes - attestation_wrapper = AttestationCustodyBitWrapper( - attestation_data_root=hash_tree_root(attestation.data), - block_index=block_index, - bit=custody_bit, - ) - all_signing_roots.append(compute_signing_root(attestation_wrapper, domain)) - else: - assert not custody_bit - return bls.AggregateVerify(all_pubkeys, all_signing_roots, signature=attestation.signature) -``` - -#### Updated `is_valid_indexed_attestation` - -Note that this replaces the Phase 0 `is_valid_indexed_attestation`. - -```python -def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool: - """ - Check if ``indexed_attestation`` has valid indices and signature. - """ - # Verify aggregate signature - attestation = indexed_attestation.attestation - aggregation_bits = attestation.aggregation_bits - if not any(aggregation_bits) or len(aggregation_bits) != len(indexed_attestation.committee): - return False - - if len(attestation.custody_bits_blocks) == 0: - # fall back on phase0 behavior if there is no shard data. - domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.data.target.epoch) - all_pubkeys = [] - for participant, aggregation_bit in zip(indexed_attestation.committee, aggregation_bits): - if aggregation_bit: - all_pubkeys.append(state.validators[participant].pubkey) - signing_root = compute_signing_root(indexed_attestation.attestation.data, domain) - return bls.FastAggregateVerify(all_pubkeys, signing_root, signature=attestation.signature) - else: - return verify_attestation_custody(state, indexed_attestation) -``` - #### `is_on_time_attestation` ```python @@ -855,16 +781,11 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None: else: assert attestation.data.source == state.previous_justified_checkpoint - # Type 1: on-time attestations, the custody bits should be non-empty. - if attestation.custody_bits_blocks != []: - # Ensure on-time attestation - assert is_on_time_attestation(state, attestation) - # Correct data root count - shard = get_shard(state, attestation) - assert len(attestation.custody_bits_blocks) == len(get_offset_slots(state, shard)) + # Type 1: on-time attestations + if is_on_time_attestation(state, attestation): # Correct parent block root assert data.beacon_block_root == get_block_root_at_slot(state, compute_previous_slot(state.slot)) - # Type 2: no shard transition, no custody bits + # Type 2: no shard transition else: # Ensure delayed attestation assert data.slot < compute_previous_slot(state.slot) @@ -1087,46 +1008,6 @@ def get_validator_from_deposit(state: BeaconState, deposit: Deposit) -> Validato ) ``` -##### New Attester slashing processing - -```python -def get_indices_from_committee( - committee: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE], - bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Sequence[ValidatorIndex]: - assert len(bits) == len(committee) - return [validator_index for i, validator_index in enumerate(committee) if bits[i]] -``` - -```python -def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None: - indexed_attestation_1 = attester_slashing.attestation_1 - indexed_attestation_2 = attester_slashing.attestation_2 - - assert is_slashable_attestation_data( - indexed_attestation_1.attestation.data, - indexed_attestation_2.attestation.data, - ) - assert is_valid_indexed_attestation(state, indexed_attestation_1) - assert is_valid_indexed_attestation(state, indexed_attestation_2) - - indices_1 = get_indices_from_committee( - indexed_attestation_1.committee, - indexed_attestation_1.attestation.aggregation_bits, - ) - indices_2 = get_indices_from_committee( - indexed_attestation_2.committee, - indexed_attestation_2.attestation.aggregation_bits, - ) - - slashed_any = False - indices = set(indices_1).intersection(indices_2) - for index in sorted(indices): - if is_slashable_validator(state.validators[index], get_current_epoch(state)): - slash_validator(state, index) - slashed_any = True - assert slashed_any -``` - #### Light client processing ```python diff --git a/specs/phase1/custody-game.md b/specs/phase1/custody-game.md index bab202cf1..ca0e9c508 100644 --- a/specs/phase1/custody-game.md +++ b/specs/phase1/custody-game.md @@ -60,6 +60,7 @@ This document details the beacon chain additions and changes in Phase 1 of Ether | `CUSTODY_PRIME` | `2 ** 256 - 189` | - | | `CUSTODY_SECRETS` | `3` | - | | `BYTES_PER_CUSTODY_ATOM` | `32` | bytes | +| `CUSTODY_PROBABILITY_EXPONENT` | `10` | - | ## Configuration @@ -141,7 +142,6 @@ class CustodyChunkResponse(Container): ```python class CustodySlashing(Container): - # Attestation.custody_bits_blocks[data_index][committee.index(malefactor_index)] is the target custody bit to check. # (Attestation.data.shard_transition_root as ShardTransition).shard_data_roots[data_index] is the root of the data. data_index: uint64 malefactor_index: ValidatorIndex @@ -277,7 +277,8 @@ def compute_custody_bit(key: BLSSignature, data: ByteList[MAX_SHARD_BLOCK_SIZE]) custody_atoms = get_custody_atoms(data) secrets = get_custody_secrets(key) uhf = universal_hash_function(custody_atoms, secrets) - return legendre_bit(uhf + secrets[0], CUSTODY_PRIME) + legendre_bits = [legendre_bit(uhf + secrets[0], CUSTODY_PRIME) for i in range(CUSTODY_PROBABILITY_EXPONENT)] + return all(legendre_bits) ``` ### `get_randao_epoch_for_custody_period` @@ -518,9 +519,6 @@ def process_custody_slashing(state: BeaconState, signed_custody_slashing: Signed # Verify the attestation assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation)) - # TODO: custody_slashing.data is not chunked like shard blocks yet, result is lots of padding. - # ??? What does this mean? - # TODO: can do a single combined merkle proof of data being attested. # Verify the shard transition is indeed attested by the attestation shard_transition = custody_slashing.shard_transition @@ -545,18 +543,14 @@ def process_custody_slashing(state: BeaconState, signed_custody_slashing: Signed signing_root = compute_signing_root(epoch_to_sign, domain) assert bls.Verify(malefactor.pubkey, signing_root, custody_slashing.malefactor_secret) - # Get the custody bit - custody_bits = attestation.custody_bits_blocks[custody_slashing.data_index] - committee = get_beacon_committee(state, attestation.data.slot, attestation.data.index) - claimed_custody_bit = custody_bits[committee.index(custody_slashing.malefactor_index)] - # Compute the custody bit computed_custody_bit = compute_custody_bit(custody_slashing.malefactor_secret, custody_slashing.data) - + # Verify the claim - if claimed_custody_bit != computed_custody_bit: + if computed_custody_bit == 1: # Slash the malefactor, reward the other committee members slash_validator(state, custody_slashing.malefactor_index) + committee = get_beacon_committee(state, attestation.data.slot, attestation.data.index) others_count = len(committee) - 1 whistleblower_reward = Gwei(malefactor.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT // others_count) for attester_index in attesters: diff --git a/specs/phase1/fork-choice.md b/specs/phase1/fork-choice.md index 3f9fbdbfb..f92640ebb 100644 --- a/specs/phase1/fork-choice.md +++ b/specs/phase1/fork-choice.md @@ -9,11 +9,9 @@ - [Introduction](#introduction) -- [Fork choice](#fork-choice) - [Helpers](#helpers) - [Extended `LatestMessage`](#extended-latestmessage) - [Updated `update_latest_messages`](#updated-update_latest_messages) - - [Handlers](#handlers) @@ -22,12 +20,6 @@ This document is the beacon chain fork choice spec for part of Ethereum 2.0 Phase 1. -## Fork choice - -Due to the changes in the structure of `IndexedAttestation` in Phase 1, `on_attestation` must be re-specified to handle this. The bulk of `on_attestation` has been moved out into a few helpers to reduce code duplication where possible. - -The rest of the fork choice remains stable. - ### Helpers #### Extended `LatestMessage` @@ -54,29 +46,3 @@ def update_latest_messages(store: Store, attesting_indices: Sequence[ValidatorIn epoch=target.epoch, root=beacon_block_root, shard=shard, shard_root=attestation.data.shard_head_root ) ``` - -### Handlers - -```python -def on_attestation(store: Store, attestation: Attestation) -> None: - """ - Run ``on_attestation`` upon receiving a new ``attestation`` from either within a block or directly on the wire. - - An ``attestation`` that is asserted as invalid may be valid at a later time, - consider scheduling it for later processing in such case. - """ - validate_on_attestation(store, attestation) - store_target_checkpoint_state(store, attestation.data.target) - - # Get state at the `target` to fully validate attestation - target_state = store.checkpoint_states[attestation.data.target] - indexed_attestation = get_indexed_attestation(target_state, attestation) - assert is_valid_indexed_attestation(target_state, indexed_attestation) - - # Update latest messages for attesting indices - attesting_indices = [ - index for i, index in enumerate(indexed_attestation.committee) - if attestation.aggregation_bits[i] - ] - update_latest_messages(store, attesting_indices, attestation) -``` diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py index a5334c5c7..2c50544d8 100644 --- a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py +++ b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py @@ -16,18 +16,13 @@ def run_on_attestation(spec, state, store, attestation, valid=True): indexed_attestation = spec.get_indexed_attestation(state, attestation) spec.on_attestation(store, attestation) + sample_index = indexed_attestation.attesting_indices[0] if spec.fork == PHASE0: - sample_index = indexed_attestation.attesting_indices[0] latest_message = spec.LatestMessage( epoch=attestation.data.target.epoch, root=attestation.data.beacon_block_root, ) else: - attesting_indices = [ - index for i, index in enumerate(indexed_attestation.committee) - if attestation.aggregation_bits[i] - ] - sample_index = attesting_indices[0] latest_message = spec.LatestMessage( epoch=attestation.data.target.epoch, root=attestation.data.beacon_block_root, diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index 17ef717bc..b9c91eaa0 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -2,14 +2,13 @@ from lru import LRU from typing import List -from eth2spec.test.context import expect_assertion_error, PHASE0, PHASE1 +from eth2spec.test.context import expect_assertion_error, PHASE1 from eth2spec.test.helpers.state import state_transition_and_sign_block, next_epoch, next_slot from eth2spec.test.helpers.block import build_empty_block_for_next_slot from eth2spec.test.helpers.shard_transitions import get_shard_transition_of_committee from eth2spec.test.helpers.keys import privkeys from eth2spec.utils import bls from eth2spec.utils.ssz.ssz_typing import Bitlist -from eth2spec.test.helpers.custody import get_custody_test_vector def run_attestation_processing(spec, state, attestation, valid=True): @@ -98,35 +97,7 @@ def build_attestation_data(spec, state, slot, index, shard_transition=None, on_t def convert_to_valid_on_time_attestation(spec, state, attestation, shard_transition, - signed=False, valid_custody_bits=None): - shard = spec.get_shard(state, attestation) - offset_slots = spec.compute_offset_slots(spec.get_latest_slot_for_shard(state, shard), state.slot + 1) - - if valid_custody_bits is not None: - beacon_committee = spec.get_beacon_committee( - state, - attestation.data.slot, - attestation.data.index, - ) - custody_secrets = [None for i in beacon_committee] - for i in range(len(beacon_committee)): - period = spec.get_custody_period_for_validator(beacon_committee[i], attestation.data.target.epoch) - epoch_to_sign = spec.get_randao_epoch_for_custody_period(period, beacon_committee[i]) - domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch_to_sign) - signing_root = spec.compute_signing_root(spec.Epoch(epoch_to_sign), domain) - custody_secrets[i] = bls.Sign(privkeys[beacon_committee[i]], signing_root) - - for i in range(len(offset_slots)): - attestation.custody_bits_blocks.append( - Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]([0 for _ in attestation.aggregation_bits]) - ) - if valid_custody_bits is not None: - test_vector = get_custody_test_vector(shard_transition.shard_block_lengths[i]) - for j in range(len(attestation.custody_bits_blocks[i])): - if attestation.aggregation_bits[j]: - attestation.custody_bits_blocks[i][j] = \ - spec.compute_custody_bit(custody_secrets[j], test_vector) ^ (not valid_custody_bits) - + signed=False): if signed: sign_attestation(spec, state, attestation) @@ -134,7 +105,7 @@ def convert_to_valid_on_time_attestation(spec, state, attestation, shard_transit def get_valid_on_time_attestation(spec, state, slot=None, index=None, - shard_transition=None, valid_custody_bits=None, signed=False): + shard_transition=None, signed=False): ''' Construct on-time attestation for next slot ''' @@ -149,7 +120,6 @@ def get_valid_on_time_attestation(spec, state, slot=None, index=None, slot=slot, index=index, shard_transition=shard_transition, - valid_custody_bits=valid_custody_bits, signed=signed, on_time=True, ) @@ -174,7 +144,6 @@ def get_valid_attestation(spec, index=None, filter_participant_set=None, shard_transition=None, - valid_custody_bits=None, signed=False, on_time=True): # If filter_participant_set filters everything, the attestation has 0 participants, and cannot be signed. @@ -207,7 +176,6 @@ def get_valid_attestation(spec, attestation = convert_to_valid_on_time_attestation( spec, state, attestation, shard_transition, - valid_custody_bits=valid_custody_bits, signed=signed, ) @@ -230,43 +198,9 @@ def sign_aggregate_attestation(spec, state, attestation_data, participants: List def sign_indexed_attestation(spec, state, indexed_attestation): - if spec.fork == PHASE0: - participants = indexed_attestation.attesting_indices - data = indexed_attestation.data - indexed_attestation.signature = sign_aggregate_attestation(spec, state, data, participants) - else: - participants = spec.get_indices_from_committee( - indexed_attestation.committee, - indexed_attestation.attestation.aggregation_bits, - ) - data = indexed_attestation.attestation.data - if any(indexed_attestation.attestation.custody_bits_blocks): - sign_on_time_attestation(spec, state, indexed_attestation.attestation) - else: - indexed_attestation.attestation.signature = sign_aggregate_attestation(spec, state, data, participants) - - -def sign_on_time_attestation(spec, state, attestation): - if not any(attestation.custody_bits_blocks): - sign_attestation(spec, state, attestation) - return - - committee = spec.get_beacon_committee(state, attestation.data.slot, attestation.data.index) - signatures = [] - for block_index, custody_bits in enumerate(attestation.custody_bits_blocks): - for participant, abit, cbit in zip(committee, attestation.aggregation_bits, custody_bits): - if not abit: - continue - signatures.append(get_attestation_custody_signature( - spec, - state, - attestation.data, - block_index, - cbit, - privkeys[participant] - )) - - attestation.signature = bls.Aggregate(signatures) + participants = indexed_attestation.attesting_indices + data = indexed_attestation.data + indexed_attestation.signature = sign_aggregate_attestation(spec, state, data, participants) def get_attestation_custody_signature(spec, state, attestation_data, block_index, bit, privkey): @@ -283,10 +217,6 @@ def get_attestation_custody_signature(spec, state, attestation_data, block_index def sign_attestation(spec, state, attestation): - if spec.fork == PHASE1 and any(attestation.custody_bits_blocks): - sign_on_time_attestation(spec, state, attestation) - return - participants = spec.get_attesting_indices( state, attestation.data, diff --git a/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py b/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py index e743ca8ff..43763895f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py @@ -1,4 +1,3 @@ -from eth2spec.test.context import PHASE1 from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation, sign_indexed_attestation @@ -41,34 +40,19 @@ def get_indexed_attestation_participants(spec, indexed_att): """ Wrapper around index-attestation to return the list of participant indices, regardless of spec phase. """ - if spec.fork == PHASE1: - return list(spec.get_indices_from_committee( - indexed_att.committee, - indexed_att.attestation.aggregation_bits, - )) - else: - return list(indexed_att.attesting_indices) + return list(indexed_att.attesting_indices) def set_indexed_attestation_participants(spec, indexed_att, participants): """ Wrapper around index-attestation to return the list of participant indices, regardless of spec phase. """ - if spec.fork == PHASE1: - indexed_att.attestation.aggregation_bits = [bool(i in participants) for i in indexed_att.committee] - else: - indexed_att.attesting_indices = participants + indexed_att.attesting_indices = participants def get_attestation_1_data(spec, att_slashing): - if spec.fork == PHASE1: - return att_slashing.attestation_1.attestation.data - else: - return att_slashing.attestation_1.data + return att_slashing.attestation_1.data def get_attestation_2_data(spec, att_slashing): - if spec.fork == PHASE1: - return att_slashing.attestation_2.attestation.data - else: - return att_slashing.attestation_2.data + return att_slashing.attestation_2.data diff --git a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py index 11ead6033..063514498 100644 --- a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py +++ b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py @@ -1,5 +1,5 @@ from eth2spec.test.context import ( - PHASE0, PHASE1, + PHASE0, spec_state_test, expect_assertion_error, always_bls, with_all_phases, with_phases ) from eth2spec.test.helpers.attestations import sign_indexed_attestation @@ -162,10 +162,7 @@ def test_same_data(spec, state): indexed_att_1 = attester_slashing.attestation_1 att_2_data = get_attestation_2_data(spec, attester_slashing) - if spec.fork == PHASE1: - indexed_att_1.attestation.data = att_2_data - else: - indexed_att_1.data = att_2_data + indexed_att_1.data = att_2_data sign_indexed_attestation(spec, state, attester_slashing.attestation_1) yield from run_attester_slashing_processing(spec, state, attester_slashing, False) diff --git a/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_attestation.py b/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_attestation.py index ed4328327..34ff28412 100644 --- a/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_attestation.py +++ b/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_attestation.py @@ -25,22 +25,9 @@ def test_on_time_success(spec, state): @with_all_phases_except(['phase0']) @spec_state_test @always_bls -def test_on_time_empty_custody_bits_blocks(spec, state): +def test_late_success(spec, state): attestation = get_valid_late_attestation(spec, state, signed=True) - assert not any(attestation.custody_bits_blocks) - - transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY) - - yield from run_attestation_processing(spec, state, attestation, False) - - -@with_all_phases_except(['phase0']) -@spec_state_test -@always_bls -def test_late_with_custody_bits_blocks(spec, state): - attestation = get_valid_on_time_attestation(spec, state, signed=True) - transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY + 1) - yield from run_attestation_processing(spec, state, attestation, False) + yield from run_attestation_processing(spec, state, attestation)