diff --git a/README.md b/README.md index a6f23db9d..fa103394d 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,6 @@ The following are the broad design goals for Ethereum 2.0: ## For spec contributors - Documentation on the different components used during spec writing can be found here: * [YAML Test Generators](test_generators/README.md) * [Executable Python Spec, with Py-tests](test_libs/pyspec/README.md) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index af446d575..a9459fb64 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -23,6 +23,11 @@ MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 65536 MIN_GENESIS_TIME: 1578009600 +# Fork Choice +# --------------------------------------------------------------- +# 2**3 (= 8) +SAFE_SLOTS_TO_UPDATE_JUSTIFIED: 8 + # Deposit contract # --------------------------------------------------------------- @@ -53,7 +58,7 @@ BLS_WITHDRAWAL_PREFIX: 0x00 # --------------------------------------------------------------- # 12 seconds SECONDS_PER_SLOT: 12 -# 2**0 (= 1) slots 6 seconds +# 2**0 (= 1) slots 12 seconds MIN_ATTESTATION_INCLUSION_DELAY: 1 # 2**5 (= 32) slots 6.4 minutes SLOTS_PER_EPOCH: 32 diff --git a/configs/minimal.yaml b/configs/minimal.yaml index 53599e83a..fbc961ab1 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -21,6 +21,12 @@ MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 64 # Jan 3, 2020 MIN_GENESIS_TIME: 1578009600 +# +# +# Fork Choice +# --------------------------------------------------------------- +# 2**1 (= 1) +SAFE_SLOTS_TO_UPDATE_JUSTIFIED: 2 # Deposit contract diff --git a/scripts/build_spec.py b/scripts/build_spec.py index e05907014..9ed9d3ae4 100644 --- a/scripts/build_spec.py +++ b/scripts/build_spec.py @@ -24,14 +24,13 @@ from eth2spec.utils.ssz.ssz_impl import ( signing_root, ) from eth2spec.utils.ssz.ssz_typing import ( - bit, boolean, Container, List, Vector, uint64, + boolean, Container, List, Vector, uint64, Bytes1, Bytes4, Bytes8, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector, ) from eth2spec.utils.bls import ( bls_aggregate_signatures, bls_aggregate_pubkeys, bls_verify, - bls_verify_multiple, bls_sign, ) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 9f0f21435..4a952a2c6 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -26,7 +26,6 @@ - [`Checkpoint`](#checkpoint) - [`Validator`](#validator) - [`AttestationData`](#attestationdata) - - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) - [`IndexedAttestation`](#indexedattestation) - [`PendingAttestation`](#pendingattestation) - [`Eth1Data`](#eth1data) @@ -55,7 +54,6 @@ - [`hash_tree_root`](#hash_tree_root) - [`signing_root`](#signing_root) - [`bls_verify`](#bls_verify) - - [`bls_verify_multiple`](#bls_verify_multiple) - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) - [Predicates](#predicates) - [`is_active_validator`](#is_active_validator) @@ -204,8 +202,8 @@ The following values are (non-configurable) constants used throughout the specif | `SLOTS_PER_EPOCH` | `2**5` (= 32) | slots | 6.4 minutes | | `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | | `MAX_SEED_LOOKAHEAD` | `2**2` (= 4) | epochs | 25.6 minutes | -| `SLOTS_PER_ETH1_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours | -| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~13 hours | +| `SLOTS_PER_ETH1_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~3.4 hours | +| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~27 hours | | `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | | `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days | | `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes | @@ -308,20 +306,11 @@ class AttestationData(Container): target: Checkpoint ``` -#### `AttestationDataAndCustodyBit` - -```python -class AttestationDataAndCustodyBit(Container): - data: AttestationData - custody_bit: bit # Challengeable bit (SSZ-bool, 1 byte) for the custody of shard data -``` - #### `IndexedAttestation` ```python class IndexedAttestation(Container): - custody_bit_0_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE] # Indices with custody bit equal to 0 - custody_bit_1_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE] # Indices with custody bit equal to 1 + attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE] data: AttestationData signature: BLSSignature ``` @@ -399,7 +388,6 @@ class AttesterSlashing(Container): class Attestation(Container): aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] data: AttestationData - custody_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] signature: BLSSignature ``` @@ -553,10 +541,6 @@ def bytes_to_int(data: bytes) -> uint64: `bls_verify` is a function for verifying a BLS signature, as defined in the [BLS Signature spec](../bls_signature.md#bls_verify). -#### `bls_verify_multiple` - -`bls_verify_multiple` is a function for verifying a BLS signature constructed from multiple messages, as defined in the [BLS Signature spec](../bls_signature.md#bls_verify_multiple). - #### `bls_aggregate_pubkeys` `bls_aggregate_pubkeys` is a function for aggregating multiple BLS public keys into a single aggregate key, as defined in the [BLS Signature spec](../bls_signature.md#bls_aggregate_pubkeys). @@ -605,31 +589,18 @@ def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: Indexe """ Check if ``indexed_attestation`` has valid indices and signature. """ - bit_0_indices = indexed_attestation.custody_bit_0_indices - bit_1_indices = indexed_attestation.custody_bit_1_indices + indices = indexed_attestation.attesting_indices - # Verify no index has custody bit equal to 1 [to be removed in phase 1] - if not len(bit_1_indices) == 0: # [to be removed in phase 1] - return False # [to be removed in phase 1] # Verify max number of indices - if not len(bit_0_indices) + len(bit_1_indices) <= MAX_VALIDATORS_PER_COMMITTEE: - return False - # Verify index sets are disjoint - if not len(set(bit_0_indices).intersection(bit_1_indices)) == 0: + if not len(indices) <= MAX_VALIDATORS_PER_COMMITTEE: return False # Verify indices are sorted - if not (bit_0_indices == sorted(bit_0_indices) and bit_1_indices == sorted(bit_1_indices)): + if not indices == sorted(indices): return False # Verify aggregate signature - if not bls_verify_multiple( - pubkeys=[ - bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_0_indices]), - bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_1_indices]), - ], - message_hashes=[ - hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)), - hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)), - ], + if not bls_verify( + pubkey=bls_aggregate_pubkeys([state.validators[i].pubkey for i in indices]), + message_hash=hash_tree_root(indexed_attestation.data), signature=indexed_attestation.signature, domain=get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch), ): @@ -922,13 +893,9 @@ def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> Ind Return the indexed attestation corresponding to ``attestation``. """ attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) - custody_bit_1_indices = get_attesting_indices(state, attestation.data, attestation.custody_bits) - assert custody_bit_1_indices.issubset(attesting_indices) - custody_bit_0_indices = attesting_indices.difference(custody_bit_1_indices) return IndexedAttestation( - custody_bit_0_indices=sorted(custody_bit_0_indices), - custody_bit_1_indices=sorted(custody_bit_1_indices), + attesting_indices=sorted(attesting_indices), data=attestation.data, signature=attestation.signature, ) @@ -1026,7 +993,7 @@ Before the Ethereum 2.0 genesis has been triggered, and for every Ethereum 1.0 b - `eth1_block_hash` is the hash of the Ethereum 1.0 block - `eth1_timestamp` is the Unix timestamp corresponding to `eth1_block_hash` -- `deposits` is the sequence of all deposits, ordered chronologically, up to the block with hash `eth1_block_hash` +- `deposits` is the sequence of all deposits, ordered chronologically, up to (and including) the block with hash `eth1_block_hash` ```python def initialize_beacon_state_from_eth1(eth1_block_hash: Hash, @@ -1460,9 +1427,8 @@ def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSla assert is_valid_indexed_attestation(state, attestation_2) slashed_any = False - attesting_indices_1 = attestation_1.custody_bit_0_indices + attestation_1.custody_bit_1_indices - attesting_indices_2 = attestation_2.custody_bit_0_indices + attestation_2.custody_bit_1_indices - for index in sorted(set(attesting_indices_1).intersection(attesting_indices_2)): + indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices) + for index in sorted(indices): if is_slashable_validator(state.validators[index], get_current_epoch(state)): slash_validator(state, index) slashed_any = True @@ -1479,7 +1445,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH committee = get_beacon_committee(state, data.slot, data.index) - assert len(attestation.aggregation_bits) == len(attestation.custody_bits) == len(committee) + assert len(attestation.aggregation_bits) == len(committee) pending_attestation = PendingAttestation( data=data, diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index b909ce732..051c6ef8d 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -43,6 +43,12 @@ The head block root associated with a `store` is defined as `get_head(store)`. A 4) **Manual forks**: Manual forks may arbitrarily change the fork choice rule but are expected to be enacted at epoch transitions, with the fork details reflected in `state.fork`. 5) **Implementation**: The implementation found in this specification is constructed for ease of understanding rather than for optimization in computation, space, or any other resource. A number of optimized alternatives can be found [here](https://github.com/protolambda/lmd-ghost). +### Configuration + +| Name | Value | Unit | Duration | +| - | - | :-: | :-: | +| `SAFE_SLOTS_TO_UPDATE_JUSTIFIED` | `2**3` (= 8) | slots | 96 seconds | + ### Helpers #### `LatestMessage` @@ -60,8 +66,10 @@ class LatestMessage(object): @dataclass class Store(object): time: uint64 + genesis_time: uint64 justified_checkpoint: Checkpoint finalized_checkpoint: Checkpoint + best_justified_checkpoint: Checkpoint blocks: Dict[Hash, BeaconBlock] = field(default_factory=dict) block_states: Dict[Hash, BeaconState] = field(default_factory=dict) checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict) @@ -78,14 +86,30 @@ def get_genesis_store(genesis_state: BeaconState) -> Store: finalized_checkpoint = Checkpoint(epoch=GENESIS_EPOCH, root=root) return Store( time=genesis_state.genesis_time, + genesis_time=genesis_state.genesis_time, justified_checkpoint=justified_checkpoint, finalized_checkpoint=finalized_checkpoint, + best_justified_checkpoint=justified_checkpoint, blocks={root: genesis_block}, block_states={root: genesis_state.copy()}, checkpoint_states={justified_checkpoint: genesis_state.copy()}, ) ``` +#### `get_current_slot` + +```python +def get_current_slot(store: Store) -> Slot: + return Slot((store.time - store.genesis_time) // SECONDS_PER_SLOT) +``` + +#### `compute_slots_since_epoch_start` + +```python +def compute_slots_since_epoch_start(slot: Slot) -> int: + return slot - compute_start_slot_at_epoch(compute_epoch_at_slot(slot)) +``` + #### `get_ancestor` ```python @@ -130,13 +154,50 @@ def get_head(store: Store) -> Hash: head = max(children, key=lambda root: (get_latest_attesting_balance(store, root), root)) ``` +#### `should_update_justified_checkpoint` + +```python +def should_update_justified_checkpoint(store: Store, new_justified_checkpoint: Checkpoint) -> bool: + """ + To address the bouncing attack, only update conflicting justified + checkpoints in the fork choice if in the early slots of the epoch. + Otherwise, delay incorporation of new justified checkpoint until next epoch boundary. + + See https://ethresear.ch/t/prevention-of-bouncing-attack-on-ffg/6114 for more detailed analysis and discussion. + """ + if compute_slots_since_epoch_start(get_current_slot(store)) < SAFE_SLOTS_TO_UPDATE_JUSTIFIED: + return True + + new_justified_block = store.blocks[new_justified_checkpoint.root] + if new_justified_block.slot <= compute_start_slot_at_epoch(store.justified_checkpoint.epoch): + return False + if not ( + get_ancestor(store, new_justified_checkpoint.root, store.blocks[store.justified_checkpoint.root].slot) == + store.justified_checkpoint.root + ): + return False + + return True +``` + ### Handlers #### `on_tick` ```python def on_tick(store: Store, time: uint64) -> None: + previous_slot = get_current_slot(store) + + # update store time store.time = time + + current_slot = get_current_slot(store) + # Not a new epoch, return + if not (current_slot > previous_slot and compute_slots_since_epoch_start(current_slot) == 0): + return + # Update store.justified_checkpoint if a better checkpoint is known + if store.best_justified_checkpoint.epoch > store.justified_checkpoint.epoch: + store.justified_checkpoint = store.best_justified_checkpoint ``` #### `on_block` @@ -164,7 +225,9 @@ def on_block(store: Store, block: BeaconBlock) -> None: # Update justified checkpoint if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch: - store.justified_checkpoint = state.current_justified_checkpoint + store.best_justified_checkpoint = state.current_justified_checkpoint + if should_update_justified_checkpoint(store, state.current_justified_checkpoint): + store.justified_checkpoint = state.current_justified_checkpoint # Update finalized checkpoint if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch: @@ -177,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 @@ -199,7 +267,7 @@ def on_attestation(store: Store, attestation: Attestation) -> None: assert is_valid_indexed_attestation(target_state, indexed_attestation) # Update latest messages - for i in indexed_attestation.custody_bit_0_indices + indexed_attestation.custody_bit_1_indices: + for i in indexed_attestation.attesting_indices: if i not in store.latest_messages or target.epoch > store.latest_messages[i].epoch: store.latest_messages[i] = LatestMessage(epoch=target.epoch, root=attestation.data.beacon_block_root) ``` diff --git a/specs/networking/p2p-interface.md b/specs/networking/p2p-interface.md index 5bccd25a9..30ca43fd3 100644 --- a/specs/networking/p2p-interface.md +++ b/specs/networking/p2p-interface.md @@ -320,7 +320,7 @@ Here, `result` represents the 1-byte response code. The token of the negotiated protocol ID specifies the type of encoding to be used for the req/resp interaction. Two values are possible at this time: -- `ssz`: the contents are [SSZ-encoded](../simple-serialize.md). This encoding type MUST be supported by all clients. For objects containing a single field, only the field is SSZ-encoded not a container with a single field. For example, the `BeaconBlocksByRoot` request is an SSZ-encoded list of `HashTreeRoots`'s. +- `ssz`: the contents are [SSZ-encoded](../simple-serialize.md). This encoding type MUST be supported by all clients. For objects containing a single field, only the field is SSZ-encoded not a container with a single field. For example, the `BeaconBlocksByRoot` request is an SSZ-encoded list of `Bytes32`'s. - `ssz_snappy`: The contents are SSZ-encoded and then compressed with [Snappy](https://github.com/google/snappy). MAY be supported in the interoperability testnet; MUST be supported in mainnet. #### SSZ-encoding strategy (with or without Snappy) @@ -344,20 +344,20 @@ constituents individually as `response_chunk`s. For example, the Request, Response Content: ``` ( - head_fork_version: bytes4 - finalized_root: bytes32 + head_fork_version: Bytes4 + finalized_root: Bytes32 finalized_epoch: uint64 - head_root: bytes32 + head_root: Bytes32 head_slot: uint64 ) ``` -The fields are: +The fields are, as seen by the client at the time of sending the message: - `head_fork_version`: The beacon_state `Fork` version. -- `finalized_root`: The latest finalized root the node knows about. -- `finalized_epoch`: The latest finalized epoch the node knows about. -- `head_root`: The block hash tree root corresponding to the head of the chain as seen by the sending node. -- `head_slot`: The slot corresponding to the `head_root`. +- `finalized_root`: `state.finalized_checkpoint.root` for the state corresponding to the head block. +- `finalized_epoch`: `state.finalized_checkpoint.epoch` for the state corresponding to the head block. +- `head_root`: The signing root of the current head block. +- `head_slot`: The slot of the block corresponding to the `head_root`. The dialing client MUST send a `Status` request upon connection. @@ -403,7 +403,7 @@ The response MUST consist of a single `response_chunk`. Request Content: ``` ( - head_block_root: HashTreeRoot + head_block_root: Bytes32 start_slot: uint64 count: uint64 step: uint64 @@ -417,7 +417,7 @@ Response Content: ) ``` -Requests count beacon blocks from the peer starting from `start_slot` on the chain defined by `head_block_root`. The response MUST contain no more than count blocks. `step` defines the slot increment between blocks. For example, requesting blocks starting at `start_slot` 2 with a step value of 2 would return the blocks at [2, 4, 6, …]. In cases where a slot is empty for a given slot number, no block is returned. For example, if slot 4 were empty in the previous example, the returned array would contain [2, 6, …]. A step value of 1 returns all blocks on the range `[start_slot, start_slot + count)`. +Requests count beacon blocks from the peer starting from `start_slot` on the chain defined by `head_block_root` (= `signing_root(BeaconBlock)`). The response MUST contain no more than count blocks. `step` defines the slot increment between blocks. For example, requesting blocks starting at `start_slot` 2 with a step value of 2 would return the blocks at [2, 4, 6, …]. In cases where a slot is empty for a given slot number, no block is returned. For example, if slot 4 were empty in the previous example, the returned array would contain [2, 6, …]. A step value of 1 returns all blocks on the range `[start_slot, start_slot + count)`. The request MUST be encoded as an SSZ-container. @@ -441,7 +441,7 @@ Request Content: ``` ( - []HashTreeRoot + []Bytes32 ) ``` @@ -453,7 +453,7 @@ Response Content: ) ``` -Requests blocks by their block roots. The response is a list of `BeaconBlock` whose length is less than or equal to the number of requested blocks. It may be less in the case that the responding peer is missing blocks. +Requests blocks by block root (= `signing_root(BeaconBlock)`). The response is a list of `BeaconBlock` whose length is less than or equal to the number of requested blocks. It may be less in the case that the responding peer is missing blocks. `BeaconBlocksByRoot` is primarily used to recover recent blocks (e.g. when receiving a block or attestation whose parent is unknown). diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 166534031..819f1ac98 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -45,7 +45,6 @@ - [Construct attestation](#construct-attestation) - [Data](#data) - [Aggregation bits](#aggregation-bits) - - [Custody bits](#custody-bits) - [Aggregate signature](#aggregate-signature) - [Broadcast attestation](#broadcast-attestation) - [Attestation aggregation](#attestation-aggregation) @@ -53,7 +52,6 @@ - [Construct aggregate](#construct-aggregate) - [Data](#data-1) - [Aggregation bits](#aggregation-bits-1) - - [Custody bits](#custody-bits-1) - [Aggregate signature](#aggregate-signature-1) - [Broadcast aggregate](#broadcast-aggregate) - [`AggregateAndProof`](#aggregateandproof) @@ -197,7 +195,7 @@ A validator has two primary responsibilities to the beacon chain: [proposing blo A validator is expected to propose a [`BeaconBlock`](../core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `is_proposer(state, validator_index)` returns `True`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator creates, signs, and broadcasts a `block` that is a child of `parent` that satisfies a valid [beacon chain state transition](../core/0_beacon-chain.md#beacon-chain-state-transition-function). -There is one proposer per slot, so if there are N active validators any individual validator will on average be assigned to propose once per N slots (e.g. at 312,500 validators = 10 million ETH, that's once per ~3 weeks). +There is one proposer per slot, so if there are N active validators any individual validator will on average be assigned to propose once per N slots (e.g. at 312,500 validators = 10 million ETH, that's once per ~6 weeks). #### Block header @@ -231,18 +229,22 @@ def get_epoch_signature(state: BeaconState, block: BeaconBlock, privkey: int) -> The `block.eth1_data` field is for block proposers to vote on recent Eth1 data. This recent data contains an Eth1 block hash as well as the associated deposit root (as calculated by the `get_deposit_root()` method of the deposit contract) and deposit count after execution of the corresponding Eth1 block. If over half of the block proposers in the current Eth1 voting period vote for the same `eth1_data` then `state.eth1_data` updates at the end of the voting period. Each deposit in `block.body.deposits` must verify against `state.eth1_data.eth1_deposit_root`. -Let `get_eth1_data(distance: uint64) -> Eth1Data` be the (subjective) function that returns the Eth1 data at distance `distance` relative to the Eth1 head at the start of the current Eth1 voting period. Let `previous_eth1_distance` be the distance relative to the Eth1 block corresponding to `state.eth1_data.block_hash` at the start of the current Eth1 voting period. An honest block proposer sets `block.eth1_data = get_eth1_vote(state, previous_eth1_distance)` where: +Let `get_eth1_data(distance: uint64) -> Eth1Data` be the (subjective) function that returns the Eth1 data at distance `distance` relative to the Eth1 head at the start of the current Eth1 voting period. Let `previous_eth1_distance` be the distance relative to the Eth1 block corresponding to `eth1_data.block_hash` found in the state at the _start_ of the current Eth1 voting period. Note that `eth1_data` can be updated in the middle of a voting period and thus the starting `eth1_data.block_hash` must be stored separately. + +An honest block proposer sets `block.eth1_data = get_eth1_vote(state, previous_eth1_distance)` where: ```python def get_eth1_vote(state: BeaconState, previous_eth1_distance: uint64) -> Eth1Data: new_eth1_data = [get_eth1_data(distance) for distance in range(ETH1_FOLLOW_DISTANCE, 2 * ETH1_FOLLOW_DISTANCE)] all_eth1_data = [get_eth1_data(distance) for distance in range(ETH1_FOLLOW_DISTANCE, previous_eth1_distance)] - valid_votes = [] - for slot, vote in enumerate(state.eth1_data_votes): - period_tail = slot % SLOTS_PER_ETH1_VOTING_PERIOD >= integer_squareroot(SLOTS_PER_ETH1_VOTING_PERIOD) - if vote in new_eth1_data or (period_tail and vote in all_eth1_data): - valid_votes.append(vote) + period_tail = state.slot % SLOTS_PER_ETH1_VOTING_PERIOD >= integer_squareroot(SLOTS_PER_ETH1_VOTING_PERIOD) + if period_tail: + votes_to_consider = all_eth1_data + else: + votes_to_consider = new_eth1_data + + valid_votes = [vote for vote in state.eth1_data_votes if vote in votes_to_consider] return max( valid_votes, @@ -291,6 +293,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. @@ -331,25 +335,14 @@ Set `attestation.data = attestation_data` where `attestation_data` is the `Attes *Note*: Calling `get_attesting_indices(state, attestation.data, attestation.aggregation_bits)` should return a list of length equal to 1, containing `validator_index`. -##### Custody bits - -- Let `attestation.custody_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE]` filled with zeros of length `len(committee)`. - -*Note*: This is a stub for Phase 0. - ##### Aggregate signature Set `attestation.signature = signed_attestation_data` where `signed_attestation_data` is obtained from: ```python def get_signed_attestation_data(state: BeaconState, attestation: IndexedAttestation, privkey: int) -> BLSSignature: - attestation_data_and_custody_bit = AttestationDataAndCustodyBit( - data=attestation.data, - custody_bit=0b0, - ) - domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.data.target.epoch) - return bls_sign(privkey, hash_tree_root(attestation_data_and_custody_bit), domain) + return bls_sign(privkey, hash_tree_root(attestation.data), domain) ``` #### Broadcast attestation @@ -391,12 +384,6 @@ Set `aggregate_attestation.data = attestation_data` where `attestation_data` is Let `aggregate_attestation.aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE]` of length `len(committee)`, where each bit set from each individual attestation is set to `0b1`. -##### Custody bits - -- Let `aggregate_attestation.custody_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE]` filled with zeros of length `len(committee)`. - -*Note*: This is a stub for Phase 0. - ##### Aggregate signature Set `aggregate_attestation.signature = aggregate_signature` where `aggregate_signature` is obtained from: diff --git a/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py b/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py index 77b52e7a2..9cabefb13 100644 --- a/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py +++ b/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py @@ -9,7 +9,7 @@ def test_decoder(): rng = Random(123) # check these types only, Block covers a lot of operation types already. - for typ in [spec.AttestationDataAndCustodyBit, spec.BeaconState, spec.BeaconBlock]: + for typ in [spec.Attestation, spec.BeaconState, spec.BeaconBlock]: # create a random pyspec value original = random_value.get_random_ssz_object(rng, typ, 100, 10, mode=random_value.RandomizationMode.mode_random, 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 70375ef27..8db55cce8 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 @@ -1,7 +1,5 @@ -from eth2spec.test.context import with_all_phases, spec_state_test, with_phases - - +from eth2spec.test.context import with_all_phases, spec_state_test from eth2spec.test.helpers.block import build_empty_block_for_next_slot from eth2spec.test.helpers.attestations import get_valid_attestation from eth2spec.test.helpers.state import state_transition_and_sign_block @@ -19,7 +17,7 @@ def run_on_attestation(spec, state, store, attestation, valid=True): indexed_attestation = spec.get_indexed_attestation(state, attestation) spec.on_attestation(store, attestation) assert ( - store.latest_messages[indexed_attestation.custody_bit_0_indices[0]] == + store.latest_messages[indexed_attestation.attesting_indices[0]] == spec.LatestMessage( epoch=attestation.data.target.epoch, root=attestation.data.beacon_block_root, @@ -29,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) @@ -41,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): @@ -77,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) @@ -100,7 +140,7 @@ def test_on_attestation_same_slot(spec, state): run_on_attestation(spec, state, store, attestation, False) -@with_phases(['phase0']) +@with_all_phases @spec_state_test def test_on_attestation_invalid_attestation(spec, state): store = spec.get_genesis_store(state) @@ -113,6 +153,7 @@ def test_on_attestation_invalid_attestation(spec, state): spec.on_block(store, block) attestation = get_valid_attestation(spec, state, slot=block.slot) - # make attestation invalid by setting a phase1-only custody bit - attestation.custody_bits[0] = 1 + # make invalid by using an invalid committee index + attestation.data.index = spec.MAX_COMMITTEES_PER_SLOT * spec.SLOTS_PER_EPOCH + run_on_attestation(spec, state, store, attestation, False) diff --git a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_block.py b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_block.py index 918c0f79e..e42cb1ac4 100644 --- a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_block.py +++ b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_block.py @@ -132,3 +132,74 @@ def test_on_block_before_finalized(spec, state): block = build_empty_block_for_next_slot(spec, state) state_transition_and_sign_block(spec, state, block) run_on_block(spec, store, block, False) + + +@with_all_phases +@spec_state_test +def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state): + # Initialization + store = spec.get_genesis_store(state) + time = 100 + spec.on_tick(store, time) + + next_epoch(spec, state) + spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT) + state, store, last_block = apply_next_epoch_with_attestations(spec, state, store) + next_epoch(spec, state) + spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT) + last_block_root = signing_root(last_block) + + # Mock the justified checkpoint + just_state = store.block_states[last_block_root] + new_justified = spec.Checkpoint( + epoch=just_state.current_justified_checkpoint.epoch + 1, + root=b'\x77' * 32, + ) + just_state.current_justified_checkpoint = new_justified + + block = build_empty_block_for_next_slot(spec, just_state) + state_transition_and_sign_block(spec, deepcopy(just_state), block) + assert spec.get_current_slot(store) % spec.SLOTS_PER_EPOCH < spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED + run_on_block(spec, store, block) + + assert store.justified_checkpoint == new_justified + + +@with_all_phases +@spec_state_test +def test_on_block_outside_safe_slots_and_old_block(spec, state): + # Initialization + store = spec.get_genesis_store(state) + time = 100 + spec.on_tick(store, time) + + next_epoch(spec, state) + spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT) + state, store, last_block = apply_next_epoch_with_attestations(spec, state, store) + next_epoch(spec, state) + spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT) + last_block_root = signing_root(last_block) + + # Mock justified block in store + just_block = build_empty_block_for_next_slot(spec, state) + # Slot is same as justified checkpoint so does not trigger an override in the store + just_block.slot = spec.compute_start_slot_at_epoch(store.justified_checkpoint.epoch) + store.blocks[just_block.hash_tree_root()] = just_block + + # Mock the justified checkpoint + just_state = store.block_states[last_block_root] + new_justified = spec.Checkpoint( + epoch=just_state.current_justified_checkpoint.epoch + 1, + root=just_block.hash_tree_root(), + ) + just_state.current_justified_checkpoint = new_justified + + block = build_empty_block_for_next_slot(spec, just_state) + state_transition_and_sign_block(spec, deepcopy(just_state), block) + + spec.on_tick(store, store.time + spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED * spec.SECONDS_PER_SLOT) + assert spec.get_current_slot(store) % spec.SLOTS_PER_EPOCH >= spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED + run_on_block(spec, store, block) + + assert store.justified_checkpoint != new_justified + assert store.best_justified_checkpoint == new_justified diff --git a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_tick.py b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_tick.py new file mode 100644 index 000000000..77222f65c --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_tick.py @@ -0,0 +1,105 @@ +from eth2spec.test.context import with_all_phases, spec_state_test + + +def run_on_tick(spec, store, time, new_justified_checkpoint=False): + previous_justified_checkpoint = store.justified_checkpoint + + spec.on_tick(store, time) + + assert store.time == time + + if new_justified_checkpoint: + assert store.justified_checkpoint == store.best_justified_checkpoint + assert store.justified_checkpoint.epoch > previous_justified_checkpoint.epoch + assert store.justified_checkpoint.root != previous_justified_checkpoint.root + else: + assert store.justified_checkpoint == previous_justified_checkpoint + + +@with_all_phases +@spec_state_test +def test_basic(spec, state): + store = spec.get_genesis_store(state) + run_on_tick(spec, store, store.time + 1) + + +@with_all_phases +@spec_state_test +def test_update_justified_single(spec, state): + store = spec.get_genesis_store(state) + seconds_per_epoch = spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH + + store.best_justified_checkpoint = spec.Checkpoint( + epoch=store.justified_checkpoint.epoch + 1, + root=b'\x55' * 32, + ) + + run_on_tick(spec, store, store.time + seconds_per_epoch, True) + + +@with_all_phases +@spec_state_test +def test_no_update_same_slot_at_epoch_boundary(spec, state): + store = spec.get_genesis_store(state) + seconds_per_epoch = spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH + + store.best_justified_checkpoint = spec.Checkpoint( + epoch=store.justified_checkpoint.epoch + 1, + root=b'\x55' * 32, + ) + + # set store time to already be at epoch boundary + store.time = seconds_per_epoch + + run_on_tick(spec, store, store.time + 1) + + +@with_all_phases +@spec_state_test +def test_no_update_not_epoch_boundary(spec, state): + store = spec.get_genesis_store(state) + + store.best_justified_checkpoint = spec.Checkpoint( + epoch=store.justified_checkpoint.epoch + 1, + root=b'\x55' * 32, + ) + + run_on_tick(spec, store, store.time + spec.SECONDS_PER_SLOT) + + +@with_all_phases +@spec_state_test +def test_no_update_new_justified_equal_epoch(spec, state): + store = spec.get_genesis_store(state) + seconds_per_epoch = spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH + + store.best_justified_checkpoint = spec.Checkpoint( + epoch=store.justified_checkpoint.epoch + 1, + root=b'\x55' * 32, + ) + + store.justified_checkpoint = spec.Checkpoint( + epoch=store.best_justified_checkpoint.epoch, + root=b'\44' * 32, + ) + + run_on_tick(spec, store, store.time + seconds_per_epoch) + + +@with_all_phases +@spec_state_test +def test_no_update_new_justified_later_epoch(spec, state): + store = spec.get_genesis_store(state) + seconds_per_epoch = spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH + + store.best_justified_checkpoint = spec.Checkpoint( + epoch=store.justified_checkpoint.epoch + 1, + root=b'\x55' * 32, + ) + + store.justified_checkpoint = spec.Checkpoint( + epoch=store.best_justified_checkpoint.epoch + 1, + root=b'\44' * 32, + ) + + run_on_tick(spec, store, store.time + seconds_per_epoch) diff --git a/test_libs/pyspec/eth2spec/test/helpers/attestations.py b/test_libs/pyspec/eth2spec/test/helpers/attestations.py index d36e62ace..299ca32ff 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/attestations.py +++ b/test_libs/pyspec/eth2spec/test/helpers/attestations.py @@ -54,11 +54,9 @@ def get_valid_attestation(spec, state, slot=None, index=None, signed=False): committee_size = len(beacon_committee) aggregation_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*([0] * committee_size)) - custody_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*([0] * committee_size)) attestation = spec.Attestation( aggregation_bits=aggregation_bits, data=attestation_data, - custody_bits=custody_bits, ) fill_aggregate_attestation(spec, state, attestation) if signed: @@ -83,7 +81,7 @@ def sign_aggregate_attestation(spec, state, attestation_data, participants: List def sign_indexed_attestation(spec, state, indexed_attestation): - participants = indexed_attestation.custody_bit_0_indices + indexed_attestation.custody_bit_1_indices + participants = indexed_attestation.attesting_indices indexed_attestation.signature = sign_aggregate_attestation(spec, state, indexed_attestation.data, participants) @@ -97,14 +95,9 @@ def sign_attestation(spec, state, attestation): attestation.signature = sign_aggregate_attestation(spec, state, attestation.data, participants) -def get_attestation_signature(spec, state, attestation_data, privkey, custody_bit=0b0): - message_hash = spec.AttestationDataAndCustodyBit( - data=attestation_data, - custody_bit=custody_bit, - ).hash_tree_root() - +def get_attestation_signature(spec, state, attestation_data, privkey): return bls_sign( - message_hash=message_hash, + message_hash=attestation_data.hash_tree_root(), privkey=privkey, domain=spec.get_domain( state=state, diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py index 04fa880e0..f19bc66d3 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py @@ -2,7 +2,7 @@ from eth2spec.test.context import ( spec_state_test, expect_assertion_error, always_bls, never_bls, - with_all_phases, with_phases, + with_all_phases, spec_test, low_balances, with_custom_state, @@ -274,35 +274,6 @@ def test_bad_source_root(spec, state): yield from run_attestation_processing(spec, state, attestation, False) -@with_all_phases -@spec_state_test -def test_inconsistent_bits(spec, state): - attestation = get_valid_attestation(spec, state) - state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - - custody_bits = attestation.custody_bits[:] - custody_bits.append(False) - - attestation.custody_bits = custody_bits - - sign_attestation(spec, state, attestation) - - yield from run_attestation_processing(spec, state, attestation, False) - - -@with_phases(['phase0']) -@spec_state_test -def test_non_empty_custody_bits(spec, state): - attestation = get_valid_attestation(spec, state) - state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - - attestation.custody_bits = attestation.aggregation_bits[:] - - sign_attestation(spec, state, attestation) - - yield from run_attestation_processing(spec, state, attestation, False) - - @with_all_phases @spec_state_test def test_empty_aggregation_bits(spec, state): @@ -344,32 +315,3 @@ def test_too_few_aggregation_bits(spec, state): attestation.aggregation_bits = attestation.aggregation_bits[:-1] yield from run_attestation_processing(spec, state, attestation, False) - - -@with_all_phases -@spec_state_test -def test_too_many_custody_bits(spec, state): - attestation = get_valid_attestation(spec, state, signed=True) - state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - - # one too many bits - attestation.custody_bits.append(0b0) - - yield from run_attestation_processing(spec, state, attestation, False) - - -@with_all_phases -@spec_state_test -def test_too_few_custody_bits(spec, state): - attestation = get_valid_attestation(spec, state) - state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - - attestation.custody_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]( - *([0b1] + [0b0] * (len(attestation.custody_bits) - 1))) - - sign_attestation(spec, state, attestation) - - # one too few bits - attestation.custody_bits = attestation.custody_bits[:-1] - - yield from run_attestation_processing(spec, state, attestation, False) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py index 20a510648..85e807ec0 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py @@ -25,10 +25,7 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True) yield 'post', None return - slashed_indices = ( - attester_slashing.attestation_1.custody_bit_0_indices - + attester_slashing.attestation_1.custody_bit_1_indices - ) + slashed_indices = attester_slashing.attestation_1.attesting_indices proposer_index = spec.get_beacon_proposer_index(state) pre_proposer_balance = get_balance(state, proposer_index) @@ -112,10 +109,7 @@ def test_success_surround(spec, state): @always_bls def test_success_already_exited_recent(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) - slashed_indices = ( - attester_slashing.attestation_1.custody_bit_0_indices - + attester_slashing.attestation_1.custody_bit_1_indices - ) + slashed_indices = attester_slashing.attestation_1.attesting_indices for index in slashed_indices: spec.initiate_validator_exit(state, index) @@ -127,10 +121,7 @@ def test_success_already_exited_recent(spec, state): @always_bls def test_success_already_exited_long_ago(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) - slashed_indices = ( - attester_slashing.attestation_1.custody_bit_0_indices - + attester_slashing.attestation_1.custody_bit_1_indices - ) + slashed_indices = attester_slashing.attestation_1.attesting_indices for index in slashed_indices: spec.initiate_validator_exit(state, index) state.validators[index].withdrawable_epoch = spec.get_current_epoch(state) + 2 @@ -190,38 +181,23 @@ def test_participants_already_slashed(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) # set all indices to slashed - attestation_1 = attester_slashing.attestation_1 - validator_indices = attestation_1.custody_bit_0_indices + attestation_1.custody_bit_1_indices + validator_indices = attester_slashing.attestation_1.attesting_indices for index in validator_indices: state.validators[index].slashed = True yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -@with_all_phases -@spec_state_test -def test_custody_bit_0_and_1_intersect(spec, state): - attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True) - - attester_slashing.attestation_1.custody_bit_1_indices.append( - attester_slashing.attestation_1.custody_bit_0_indices[0] - ) - - sign_indexed_attestation(spec, state, attester_slashing.attestation_1) - - yield from run_attester_slashing_processing(spec, state, attester_slashing, False) - - @with_all_phases @spec_state_test @always_bls def test_att1_bad_extra_index(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) - indices = attester_slashing.attestation_1.custody_bit_0_indices + indices = attester_slashing.attestation_1.attesting_indices options = list(set(range(len(state.validators))) - set(indices)) indices.append(options[len(options) // 2]) # add random index, not previously in attestation. - attester_slashing.attestation_1.custody_bit_0_indices = sorted(indices) + attester_slashing.attestation_1.attesting_indices = sorted(indices) # Do not sign the modified attestation (it's ok to slash if attester signed, not if they did not), # see if the bad extra index is spotted, and slashing is aborted. @@ -234,10 +210,10 @@ def test_att1_bad_extra_index(spec, state): def test_att1_bad_replaced_index(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) - indices = attester_slashing.attestation_1.custody_bit_0_indices + indices = attester_slashing.attestation_1.attesting_indices options = list(set(range(len(state.validators))) - set(indices)) indices[3] = options[len(options) // 2] # replace with random index, not previously in attestation. - attester_slashing.attestation_1.custody_bit_0_indices = sorted(indices) + attester_slashing.attestation_1.attesting_indices = sorted(indices) # Do not sign the modified attestation (it's ok to slash if attester signed, not if they did not), # see if the bad replaced index is spotted, and slashing is aborted. @@ -250,10 +226,10 @@ def test_att1_bad_replaced_index(spec, state): def test_att2_bad_extra_index(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) - indices = attester_slashing.attestation_2.custody_bit_0_indices + indices = attester_slashing.attestation_2.attesting_indices options = list(set(range(len(state.validators))) - set(indices)) indices.append(options[len(options) // 2]) # add random index, not previously in attestation. - attester_slashing.attestation_2.custody_bit_0_indices = sorted(indices) + attester_slashing.attestation_2.attesting_indices = sorted(indices) # Do not sign the modified attestation (it's ok to slash if attester signed, not if they did not), # see if the bad extra index is spotted, and slashing is aborted. @@ -266,10 +242,10 @@ def test_att2_bad_extra_index(spec, state): def test_att2_bad_replaced_index(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) - indices = attester_slashing.attestation_2.custody_bit_0_indices + indices = attester_slashing.attestation_2.attesting_indices options = list(set(range(len(state.validators))) - set(indices)) indices[3] = options[len(options) // 2] # replace with random index, not previously in attestation. - attester_slashing.attestation_2.custody_bit_0_indices = sorted(indices) + attester_slashing.attestation_2.attesting_indices = sorted(indices) # Do not sign the modified attestation (it's ok to slash if attester signed, not if they did not), # see if the bad replaced index is spotted, and slashing is aborted. @@ -278,10 +254,10 @@ def test_att2_bad_replaced_index(spec, state): @with_all_phases @spec_state_test -def test_unsorted_att_1_bit0(spec, state): +def test_unsorted_att_1(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True) - indices = attester_slashing.attestation_1.custody_bit_0_indices + indices = attester_slashing.attestation_1.attesting_indices assert len(indices) >= 3 indices[1], indices[2] = indices[2], indices[1] # unsort second and third index sign_indexed_attestation(spec, state, attester_slashing.attestation_1) @@ -291,15 +267,12 @@ def test_unsorted_att_1_bit0(spec, state): @with_all_phases @spec_state_test -def test_unsorted_att_2_bit0(spec, state): +def test_unsorted_att_2(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False) - indices = attester_slashing.attestation_2.custody_bit_0_indices + indices = attester_slashing.attestation_2.attesting_indices assert len(indices) >= 3 indices[1], indices[2] = indices[2], indices[1] # unsort second and third index sign_indexed_attestation(spec, state, attester_slashing.attestation_2) yield from run_attester_slashing_processing(spec, state, attester_slashing, False) - - -# note: unsorted indices for custody bit 0 are to be introduced in phase 1 testing. diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py index 99ba9d284..7d844b63b 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py @@ -141,7 +141,7 @@ def test_duplicate_attestation(spec, state): attestation = get_valid_attestation(spec, state, signed=True) indexed_attestation = spec.get_indexed_attestation(state, attestation) - participants = indexed_attestation.custody_bit_0_indices + indexed_attestation.custody_bit_1_indices + participants = indexed_attestation.attesting_indices assert len(participants) > 0 diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index bffb1c1fc..bbc4bba3a 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -188,8 +188,7 @@ def test_attester_slashing(spec, state): pre_state = deepcopy(state) attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) - validator_index = (attester_slashing.attestation_1.custody_bit_0_indices - + attester_slashing.attestation_1.custody_bit_1_indices)[0] + validator_index = attester_slashing.attestation_1.attesting_indices[0] assert not state.validators[validator_index].slashed