diff --git a/configs/mainnet/phase0.yaml b/configs/mainnet/phase0.yaml index 39cfddf77..7677e939b 100644 --- a/configs/mainnet/phase0.yaml +++ b/configs/mainnet/phase0.yaml @@ -151,3 +151,9 @@ DOMAIN_DEPOSIT: 0x03000000 DOMAIN_VOLUNTARY_EXIT: 0x04000000 DOMAIN_SELECTION_PROOF: 0x05000000 DOMAIN_AGGREGATE_AND_PROOF: 0x06000000 +<<<<<<< HEAD:configs/mainnet.yaml +# Phase 1 +DOMAIN_SHARD_PROPOSAL: 0x80000000 +DOMAIN_SHARD_COMMITTEE: 0x81000000 +DOMAIN_LIGHT_CLIENT: 0x82000000 +DOMAIN_CUSTODY_BIT_SLASHING: 0x83000000 diff --git a/configs/mainnet/phase1.yaml b/configs/mainnet/phase1.yaml index c3a4f93b9..9950af684 100644 --- a/configs/mainnet/phase1.yaml +++ b/configs/mainnet/phase1.yaml @@ -62,14 +62,26 @@ DOMAIN_CUSTODY_BIT_SLASHING: 0x83000000 # Time parameters # 2**1 (= 2) epochs, 12.8 minutes RANDAO_PENALTY_EPOCHS: 2 +# 2**15 (= 32,768) epochs, ~146 days +EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 32768 # 2**14 (= 16,384) epochs ~73 days -EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 16384 -# 2**11 (= 2,048) epochs, ~9 days -EPOCHS_PER_CUSTODY_PERIOD: 2048 +EPOCHS_PER_CUSTODY_PERIOD: 16384 # 2**11 (= 2,048) epochs, ~9 days CUSTODY_PERIOD_TO_RANDAO_PADDING: 2048 # 2**14 (= 16,384) epochs CUSTODY_RESPONSE_DEADLINE: 16384 +# 2**15 (= 32,768) epochs, ~146 days +MAX_CHUNK_CHALLENGE_DELAY: 32768 + +# Misc parameters +# 2**256 - 189 +CUSTODY_PRIME: 115792089237316195423570985008687907853269984665640564039457584007913129639747 +# 3 +CUSTODY_SECRETS: 3 +# 2**5 (= 32) bytes +BYTES_PER_CUSTODY_ATOM: 32 +# 1/1024 chance of custody bit 1 +CUSTODY_PROBABILITY_EXPONENT: 10 # Max operations # 2**8 (= 256) diff --git a/configs/minimal/phase1.yaml b/configs/minimal/phase1.yaml index 5e570d646..37636b8e6 100644 --- a/configs/minimal/phase1.yaml +++ b/configs/minimal/phase1.yaml @@ -65,13 +65,27 @@ DOMAIN_CUSTODY_BIT_SLASHING: 0x83000000 # 2**1 (= 2) epochs RANDAO_PENALTY_EPOCHS: 2 # [customized] quicker for testing -EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 4096 +EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 128 # [customized] quicker for testing -EPOCHS_PER_CUSTODY_PERIOD: 8 +EPOCHS_PER_CUSTODY_PERIOD: 64 # [customized] quicker for testing CUSTODY_PERIOD_TO_RANDAO_PADDING: 8 # [customized] quicker for testing -CUSTODY_RESPONSE_DEADLINE: 32 +CUSTODY_RESPONSE_DEADLINE: 128 +# [customize for faster testing] +MAX_CHUNK_CHALLENGE_DELAY: 128 + + +# Misc parameters +# 2**256 - 189 +CUSTODY_PRIME: 115792089237316195423570985008687907853269984665640564039457584007913129639747 +# 3 +CUSTODY_SECRETS: 3 +# 2**5 (= 32) bytes +BYTES_PER_CUSTODY_ATOM: 32 +# 1/4 chance of custody bit 1 [customized for faster testing] +CUSTODY_PROBABILITY_EXPONENT: 2 + # Max operations # 2**8 (= 256) diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index dbfbde897..a3f9ec4e9 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -53,15 +53,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) @@ -78,7 +75,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) @@ -186,7 +182,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 ``` @@ -206,8 +201,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` @@ -593,17 +589,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 @@ -673,65 +658,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 @@ -849,16 +775,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) @@ -1081,46 +1002,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 8924e980b..4cf047405 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 @@ -68,11 +69,11 @@ This document details the beacon chain additions and changes in Phase 1 of Ether | Name | Value | Unit | Duration | | - | - | :-: | :-: | | `RANDAO_PENALTY_EPOCHS` | `2**1` (= 2) | epochs | 12.8 minutes | -| `EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS` | `2**14` (= 16,384) | epochs | ~73 days | -| `EPOCHS_PER_CUSTODY_PERIOD` | `2**11` (= 2,048) | epochs | ~9 days | +| `EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS` | `2**15` (= 32,768) | epochs | ~146 days | +| `EPOCHS_PER_CUSTODY_PERIOD` | `2**14` (= 16,384) | epochs | ~73 days | | `CUSTODY_PERIOD_TO_RANDAO_PADDING` | `2**11` (= 2,048) | epochs | ~9 days | -| `MAX_CHUNK_CHALLENGE_DELAY` | `2**11` (= 2,048) | epochs | ~9 days | -| `CUSTODY_RESPONSE_DEADLINE` | `2**14` (= 16,384) | epochs | ~73 days | +| `MAX_CHUNK_CHALLENGE_DELAY` | `2**15` (= 32,768) | epochs | ~146 days | +| `CHUNK_RESPONSE_DEADLINE` | `2**14` (= 16,384) | epochs | ~73 days | ### Max operations per block @@ -140,7 +141,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 @@ -276,7 +276,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] + i, CUSTODY_PRIME) for i in range(CUSTODY_PROBABILITY_EXPONENT)] + return all(legendre_bits) ``` ### `get_randao_epoch_for_custody_period` @@ -517,9 +518,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 @@ -544,18 +542,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: @@ -576,7 +570,7 @@ def process_custody_slashing(state: BeaconState, signed_custody_slashing: Signed def process_reveal_deadlines(state: BeaconState) -> None: epoch = get_current_epoch(state) for index, validator in enumerate(state.validators): - deadline = validator.next_custody_secret_to_reveal + (CUSTODY_RESPONSE_DEADLINE // EPOCHS_PER_CUSTODY_PERIOD) + deadline = validator.next_custody_secret_to_reveal + 1 if get_custody_period_for_validator(ValidatorIndex(index), epoch) > deadline: slash_validator(state, ValidatorIndex(index)) ``` @@ -584,7 +578,7 @@ def process_reveal_deadlines(state: BeaconState) -> None: ```python def process_challenge_deadlines(state: BeaconState) -> None: for custody_chunk_challenge in state.custody_chunk_challenge_records: - if get_current_epoch(state) > custody_chunk_challenge.inclusion_epoch + CUSTODY_RESPONSE_DEADLINE: + if get_current_epoch(state) > custody_chunk_challenge.inclusion_epoch + EPOCHS_PER_CUSTODY_PERIOD: slash_validator(state, custody_chunk_challenge.responder_index, custody_chunk_challenge.challenger_index) index_in_records = state.custody_chunk_challenge_records.index(custody_chunk_challenge) state.custody_chunk_challenge_records[index_in_records] = CustodyChunkChallengeRecord() 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..07e227022 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): @@ -97,44 +96,7 @@ def build_attestation_data(spec, state, slot, index, shard_transition=None, on_t return attestation_data -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) - - if signed: - sign_attestation(spec, state, attestation) - - return attestation - - -def get_valid_on_time_attestation(spec, state, slot=None, index=None, - shard_transition=None, valid_custody_bits=None, signed=False): +def get_valid_on_time_attestation(spec, state, slot=None, index=None, shard_transition=None, signed=False): ''' Construct on-time attestation for next slot ''' @@ -149,7 +111,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 +135,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. @@ -203,14 +163,6 @@ def get_valid_attestation(spec, # fill the attestation with (optionally filtered) participants, and optionally sign it fill_aggregate_attestation(spec, state, attestation, signed=signed, filter_participant_set=filter_participant_set) - if spec.fork == PHASE1 and on_time: - attestation = convert_to_valid_on_time_attestation( - spec, state, attestation, - shard_transition, - valid_custody_bits=valid_custody_bits, - signed=signed, - ) - return attestation @@ -230,43 +182,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 +201,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/helpers/custody.py b/tests/core/pyspec/eth2spec/test/helpers/custody.py index 898cf7731..4f91e6dc5 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/custody.py +++ b/tests/core/pyspec/eth2spec/test/helpers/custody.py @@ -59,7 +59,7 @@ def bitlist_from_int(max_len, num_bits, n): return Bitlist[max_len](*[(n >> i) & 0b1 for i in range(num_bits)]) -def get_valid_custody_slashing(spec, state, attestation, shard_transition, invalid_custody_bit=False): +def get_valid_custody_slashing(spec, state, attestation, shard_transition, custody_secret, data, data_index=0): beacon_committee = spec.get_beacon_committee( state, attestation.data.slot, @@ -68,21 +68,10 @@ def get_valid_custody_slashing(spec, state, attestation, shard_transition, inval malefactor_index = beacon_committee[0] whistleblower_index = beacon_committee[-1] - epoch = spec.get_randao_epoch_for_custody_period(attestation.data.target.epoch, - malefactor_index) - - # Generate the responder key - domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch) - signing_root = spec.compute_signing_root(spec.Epoch(epoch), domain) - malefactor_key = bls.Sign(privkeys[malefactor_index], signing_root) - data_index = 0 - data = ByteList[spec.MAX_SHARD_BLOCK_SIZE]( - get_custody_test_vector(shard_transition.shard_block_lengths[data_index])) - slashing = spec.CustodySlashing( data_index=data_index, malefactor_index=malefactor_index, - malefactor_secret=malefactor_key, + malefactor_secret=custody_secret, whistleblower_index=whistleblower_index, shard_transition=shard_transition, attestation=attestation, @@ -165,9 +154,9 @@ def get_valid_custody_chunk_response(spec, state, chunk_challenge, block_length, ) -def get_custody_test_vector(bytelength): +def get_custody_test_vector(bytelength, offset=0): ints = bytelength // 4 + 1 - return (b"".join(i.to_bytes(4, "little") for i in range(ints)))[:bytelength] + return (b"".join((i + offset).to_bytes(4, "little") for i in range(ints)))[:bytelength] def get_shard_transition(spec, start_slot, block_lengths): @@ -181,3 +170,30 @@ def get_shard_transition(spec, start_slot, block_lengths): proposer_signature_aggregate=spec.BLSSignature(), ) return shard_transition + + +def get_custody_secret(spec, state, validator_index, epoch=None): + period = spec.get_custody_period_for_validator(validator_index, epoch if epoch is not None + else spec.get_current_epoch(state)) + epoch_to_sign = spec.get_randao_epoch_for_custody_period(period, validator_index) + domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch_to_sign) + signing_root = spec.compute_signing_root(spec.Epoch(epoch_to_sign), domain) + return bls.Sign(privkeys[validator_index], signing_root) + + +def get_custody_slashable_test_vector(spec, custody_secret, length, slashable=True): + test_vector = get_custody_test_vector(length) + offset = 0 + while spec.compute_custody_bit(custody_secret, test_vector) != slashable: + offset += 1 + test_vector = test_vector = get_custody_test_vector(length, offset) + return test_vector + + +def get_custody_slashable_shard_transition(spec, start_slot, block_lengths, custody_secret, slashable=True): + shard_transition = get_shard_transition(spec, start_slot, block_lengths) + slashable_test_vector = get_custody_slashable_test_vector(spec, custody_secret, + block_lengths[0], slashable=slashable) + block_data = ByteList[spec.MAX_SHARD_BLOCK_SIZE](slashable_test_vector) + shard_transition.shard_data_roots[0] = block_data.get_backing().get_left().merkle_root() + return shard_transition, slashable_test_vector 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) diff --git a/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_custody_slashing.py b/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_custody_slashing.py index 997e5ac92..ec0bac82d 100644 --- a/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_custody_slashing.py +++ b/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_custody_slashing.py @@ -1,6 +1,7 @@ from eth2spec.test.helpers.custody import ( get_valid_custody_slashing, - get_shard_transition, + get_custody_secret, + get_custody_slashable_shard_transition, ) from eth2spec.test.helpers.attestations import ( get_valid_on_time_attestation, @@ -52,15 +53,39 @@ def run_custody_slashing_processing(spec, state, custody_slashing, valid=True, c yield 'post', state -@with_all_phases_except(['phase0']) -@spec_state_test -def test_custody_slashing(spec, state): - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH) - shard = 0 +def run_standard_custody_slashing_test(spec, + state, + shard_lateness=None, + shard=None, + validator_index=None, + block_lengths=None, + slashing_message_data=None, + correct=True, + valid=True): + if shard_lateness is None: + shard_lateness = spec.SLOTS_PER_EPOCH + transition_to(spec, state, state.slot + shard_lateness) + + if shard is None: + shard = 0 + if validator_index is None: + validator_index = spec.get_beacon_committee(state, state.slot, shard)[0] + offset_slots = spec.get_offset_slots(state, shard) - shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) + if block_lengths is None: + block_lengths = [2**15 // 3] * len(offset_slots) + + custody_secret = get_custody_secret(spec, state, validator_index) + shard_transition, slashable_test_vector = get_custody_slashable_shard_transition( + spec, + state.slot, + block_lengths, + custody_secret, + slashable=correct, + ) + attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True, - shard_transition=shard_transition, valid_custody_bits=False) + shard_transition=shard_transition) transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY) @@ -68,109 +93,45 @@ def test_custody_slashing(spec, state): transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1)) - slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition) + slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition, + custody_secret, slashable_test_vector) - yield from run_custody_slashing_processing(spec, state, slashing, correct=True) + if slashing_message_data is not None: + slashing.message.data = slashing_message_data + + yield from run_custody_slashing_processing(spec, state, slashing, valid=valid, correct=correct) + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_custody_slashing(spec, state): + yield from run_standard_custody_slashing_test(spec, state) @with_all_phases_except(['phase0']) @spec_state_test def test_incorrect_custody_slashing(spec, state): - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH) - shard = 0 - offset_slots = spec.get_offset_slots(state, shard) - shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) - attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True, - shard_transition=shard_transition, valid_custody_bits=True) - - transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY) - - _, _, _ = run_attestation_processing(spec, state, attestation) - - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1)) - - slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition) - - yield from run_custody_slashing_processing(spec, state, slashing, correct=False) + yield from run_standard_custody_slashing_test(spec, state, correct=False) @with_all_phases_except(['phase0']) @spec_state_test def test_multiple_epochs_custody(spec, state): - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 3) - shard = 0 - offset_slots = spec.get_offset_slots(state, shard) - shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) - attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True, - shard_transition=shard_transition, valid_custody_bits=False) - - transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY) - - _, _, _ = run_attestation_processing(spec, state, attestation) - - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1)) - - slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition) - - yield from run_custody_slashing_processing(spec, state, slashing, correct=True) + yield from run_standard_custody_slashing_test(spec, state, shard_lateness=spec.SLOTS_PER_EPOCH * 3) @with_all_phases_except(['phase0']) @spec_state_test def test_many_epochs_custody(spec, state): - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 20) - shard = 0 - offset_slots = spec.get_offset_slots(state, shard) - shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) - attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True, - shard_transition=shard_transition, valid_custody_bits=False) - - transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY) - - _, _, _ = run_attestation_processing(spec, state, attestation) - - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1)) - - slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition) - - yield from run_custody_slashing_processing(spec, state, slashing, correct=True) - - -@with_all_phases_except(['phase0']) -@spec_state_test -def test_off_chain_attestation(spec, state): - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH) - shard = 0 - offset_slots = spec.get_offset_slots(state, shard) - shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) - attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True, - shard_transition=shard_transition, valid_custody_bits=False) - - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1)) - - slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition) - - yield from run_custody_slashing_processing(spec, state, slashing, correct=True) + yield from run_standard_custody_slashing_test(spec, state, shard_lateness=spec.SLOTS_PER_EPOCH * 10) @with_all_phases_except(['phase0']) @spec_state_test def test_invalid_custody_slashing(spec, state): - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH) - shard = 0 - offset_slots = spec.get_offset_slots(state, shard) - shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) - attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True, - shard_transition=shard_transition, valid_custody_bits=False) - - transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY) - - _, _, _ = run_attestation_processing(spec, state, attestation) - - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1)) - - slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition) - - slashing.message.data = ByteList[spec.MAX_SHARD_BLOCK_SIZE]() - - yield from run_custody_slashing_processing(spec, state, slashing, valid=False) + yield from run_standard_custody_slashing_test( + spec, + state, + slashing_message_data=ByteList[spec.MAX_SHARD_BLOCK_SIZE](), + valid=False, + ) diff --git a/tests/core/pyspec/eth2spec/test/phase_1/epoch_processing/test_process_challenge_deadlines.py b/tests/core/pyspec/eth2spec/test/phase_1/epoch_processing/test_process_challenge_deadlines.py index 2e60d167c..6590d8af9 100644 --- a/tests/core/pyspec/eth2spec/test/phase_1/epoch_processing/test_process_challenge_deadlines.py +++ b/tests/core/pyspec/eth2spec/test/phase_1/epoch_processing/test_process_challenge_deadlines.py @@ -1,12 +1,11 @@ from eth2spec.test.helpers.custody import ( get_valid_chunk_challenge, get_shard_transition, - get_valid_custody_key_reveal, ) from eth2spec.test.helpers.attestations import ( get_valid_on_time_attestation, ) -from eth2spec.test.helpers.state import next_epoch_via_block, transition_to +from eth2spec.test.helpers.state import transition_to from eth2spec.test.context import ( with_all_phases_except, spec_state_test, @@ -17,7 +16,6 @@ from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_ep from eth2spec.test.phase_1.block_processing.test_process_chunk_challenge import ( run_chunk_challenge_processing, ) -from eth2spec.test.phase_1.block_processing.test_process_custody_key_reveal import run_custody_key_reveal_processing def run_process_challenge_deadlines(spec, state): @@ -44,32 +42,15 @@ def test_validator_slashed_after_chunk_challenge(spec, state): attestation.data.index )[0] - spec.initiate_validator_exit(state, validator_index) - assert state.validators[validator_index].withdrawable_epoch < spec.FAR_FUTURE_EPOCH - - transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH) - - assert state.validators[validator_index].withdrawable_epoch == spec.FAR_FUTURE_EPOCH - - while spec.get_current_epoch(state) < state.validators[validator_index].exit_epoch: - next_epoch_via_block(spec, state) - while (state.validators[validator_index].next_custody_secret_to_reveal - <= spec.get_custody_period_for_validator( - validator_index, - state.validators[validator_index].exit_epoch - 1)): - custody_key_reveal = get_valid_custody_key_reveal(spec, state, validator_index=validator_index) - _, _, _ = run_custody_key_reveal_processing(spec, state, custody_key_reveal) - - next_epoch_via_block(spec, state) - challenge = get_valid_chunk_challenge(spec, state, attestation, shard_transition) _, _, _ = run_chunk_challenge_processing(spec, state, challenge) assert state.validators[validator_index].slashed == 0 - transition_to(spec, state, state.slot + (spec.CUSTODY_RESPONSE_DEADLINE + - spec.EPOCHS_PER_CUSTODY_PERIOD) * spec.SLOTS_PER_EPOCH) + transition_to(spec, state, state.slot + spec.MAX_CHUNK_CHALLENGE_DELAY * spec.SLOTS_PER_EPOCH) + + state.validators[validator_index].slashed = 0 yield from run_process_challenge_deadlines(spec, state) diff --git a/tests/core/pyspec/eth2spec/test/phase_1/epoch_processing/test_process_reveal_deadlines.py b/tests/core/pyspec/eth2spec/test/phase_1/epoch_processing/test_process_reveal_deadlines.py index 31d45aa41..9cc0069b9 100644 --- a/tests/core/pyspec/eth2spec/test/phase_1/epoch_processing/test_process_reveal_deadlines.py +++ b/tests/core/pyspec/eth2spec/test/phase_1/epoch_processing/test_process_reveal_deadlines.py @@ -18,11 +18,13 @@ def run_process_challenge_deadlines(spec, state): @spec_state_test def test_validator_slashed_after_reveal_deadline(spec, state): assert state.validators[0].slashed == 0 - transition_to(spec, state, spec.get_randao_epoch_for_custody_period(0, 0) * spec.SLOTS_PER_EPOCH) - transition_to(spec, state, state.slot + ((spec.CUSTODY_RESPONSE_DEADLINE) - * spec.SLOTS_PER_EPOCH)) + # Need to run at least one reveal so that not all validators are slashed (otherwise spec fails to find proposers) + custody_key_reveal = get_valid_custody_key_reveal(spec, state, validator_index=1) + _, _, _ = run_custody_key_reveal_processing(spec, state, custody_key_reveal) + + transition_to(spec, state, state.slot + spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH) state.validators[0].slashed = 0 @@ -34,15 +36,14 @@ def test_validator_slashed_after_reveal_deadline(spec, state): @with_all_phases_except(['phase0']) @spec_state_test def test_validator_not_slashed_after_reveal(spec, state): - state.slot += spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH + transition_to(spec, state, spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH) custody_key_reveal = get_valid_custody_key_reveal(spec, state) _, _, _ = run_custody_key_reveal_processing(spec, state, custody_key_reveal) assert state.validators[0].slashed == 0 - transition_to(spec, state, state.slot + ((spec.CUSTODY_RESPONSE_DEADLINE) - * spec.SLOTS_PER_EPOCH)) + transition_to(spec, state, state.slot + spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH) yield from run_process_challenge_deadlines(spec, state)