diff --git a/configs/constant_presets/mainnet.yaml b/configs/constant_presets/mainnet.yaml index 38f10c8fc..f9acf5bf7 100644 --- a/configs/constant_presets/mainnet.yaml +++ b/configs/constant_presets/mainnet.yaml @@ -10,7 +10,7 @@ SHARD_COUNT: 1024 # 2**7 (= 128) TARGET_COMMITTEE_SIZE: 128 # 2**12 (= 4,096) -MAX_INDICES_PER_ATTESTATION: 4096 +MAX_VALIDATORS_PER_COMMITTEE: 4096 # 2**2 (= 4) MIN_PER_EPOCH_CHURN_LIMIT: 4 # 2**16 (= 65,536) diff --git a/configs/constant_presets/minimal.yaml b/configs/constant_presets/minimal.yaml index 3aa4a6b71..27ed9d15e 100644 --- a/configs/constant_presets/minimal.yaml +++ b/configs/constant_presets/minimal.yaml @@ -9,7 +9,7 @@ SHARD_COUNT: 8 # [customized] unsecure, but fast TARGET_COMMITTEE_SIZE: 4 # 2**12 (= 4,096) -MAX_INDICES_PER_ATTESTATION: 4096 +MAX_VALIDATORS_PER_COMMITTEE: 4096 # 2**2 (= 4) MIN_PER_EPOCH_CHURN_LIMIT: 4 # 2**16 (= 65,536) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6f28c0e3d..619bd3c01 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -34,6 +34,7 @@ - [`Eth1Data`](#eth1data) - [`HistoricalBatch`](#historicalbatch) - [`DepositData`](#depositdata) + - [`CompactCommittee`](#compactcommittee) - [`BeaconBlockHeader`](#beaconblockheader) - [Beacon operations](#beacon-operations) - [`ProposerSlashing`](#proposerslashing) @@ -69,7 +70,7 @@ - [`get_block_root_at_slot`](#get_block_root_at_slot) - [`get_block_root`](#get_block_root) - [`get_randao_mix`](#get_randao_mix) - - [`get_active_index_root`](#get_active_index_root) + - [`get_compact_committees_root`](#get_compact_committees_root) - [`generate_seed`](#generate_seed) - [`get_beacon_proposer_index`](#get_beacon_proposer_index) - [`verify_merkle_branch`](#verify_merkle_branch) @@ -186,7 +187,7 @@ The following values are (non-configurable) constants used throughout the specif | - | - | | `SHARD_COUNT` | `2**10` (= 1,024) | | `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | -| `MAX_INDICES_PER_ATTESTATION` | `2**12` (= 4,096) | +| `MAX_VALIDATORS_PER_COMMITTEE` | `2**12` (= 4,096) | | `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) | | `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) | | `SHUFFLE_ROUND_COUNT` | `90` | @@ -350,8 +351,8 @@ class AttestationDataAndCustodyBit(Container): ```python class IndexedAttestation(Container): - custody_bit_0_indices: List[ValidatorIndex, MAX_INDICES_PER_ATTESTATION] # Indices with custody bit equal to 0 - custody_bit_1_indices: List[ValidatorIndex, MAX_INDICES_PER_ATTESTATION] # Indices with custody bit equal to 1 + 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 data: AttestationData signature: BLSSignature ``` @@ -360,7 +361,7 @@ class IndexedAttestation(Container): ```python class PendingAttestation(Container): - aggregation_bits: Bitlist[MAX_INDICES_PER_ATTESTATION] + aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] data: AttestationData inclusion_delay: Slot proposer_index: ValidatorIndex @@ -393,6 +394,14 @@ class DepositData(Container): signature: BLSSignature ``` +#### `CompactCommittee` + +```python +class CompactCommittee(Container): + pubkeys: List[Bytes48, MAX_VALIDATORS_PER_COMMITTEE] + compact_validators: List[uint64, MAX_VALIDATORS_PER_COMMITTEE] +``` + #### `BeaconBlockHeader` ```python @@ -427,9 +436,9 @@ class AttesterSlashing(Container): ```python class Attestation(Container): - aggregation_bits: Bitlist[MAX_INDICES_PER_ATTESTATION] + aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] data: AttestationData - custody_bits: Bitlist[MAX_INDICES_PER_ATTESTATION] + custody_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] signature: BLSSignature ``` @@ -517,7 +526,7 @@ class BeaconState(Container): # Shuffling start_shard: Shard randao_mixes: Vector[Hash, EPOCHS_PER_HISTORICAL_VECTOR] - active_index_roots: Vector[Hash, EPOCHS_PER_HISTORICAL_VECTOR] # Active registry digests for light clients + compact_committees_roots: Vector[Hash, EPOCHS_PER_HISTORICAL_VECTOR] # Committee digests for light clients # Slashings slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances # Attestations @@ -691,6 +700,9 @@ def get_shard_delta(state: BeaconState, epoch: Epoch) -> int: ```python def get_epoch_start_shard(state: BeaconState, epoch: Epoch) -> Shard: + """ + Return the start shard of the 0th committee in an epoch. + """ assert epoch <= get_current_epoch(state) + 1 check_epoch = Epoch(get_current_epoch(state) + 1) shard = Shard((state.start_shard + get_shard_delta(state, get_current_epoch(state))) % SHARD_COUNT) @@ -744,17 +756,25 @@ def get_randao_mix(state: BeaconState, return state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR] ``` -### `get_active_index_root` +### `get_compact_committees_root` ```python -def get_active_index_root(state: BeaconState, - epoch: Epoch) -> Hash: +def get_compact_committees_root(state: BeaconState, epoch: Epoch) -> Hash: """ - Return the index root at a recent ``epoch``. - ``epoch`` expected to be between - (current_epoch - EPOCHS_PER_HISTORICAL_VECTOR + ACTIVATION_EXIT_DELAY, current_epoch + ACTIVATION_EXIT_DELAY]. + Return the compact committee root for the current epoch. """ - return state.active_index_roots[epoch % EPOCHS_PER_HISTORICAL_VECTOR] + committees = [CompactCommittee() for _ in range(SHARD_COUNT)] + start_shard = get_epoch_start_shard(state, epoch) + for committee_number in range(get_epoch_committee_count(state, epoch)): + shard = Shard((start_shard + committee_number) % SHARD_COUNT) + for index in get_crosslink_committee(state, epoch, shard): + validator = state.validators[index] + committees[shard].pubkeys.append(validator.pubkey) + compact_balance = validator.effective_balance // EFFECTIVE_BALANCE_INCREMENT + # `index` (top 6 bytes) + `slashed` (16th bit) + `compact_balance` (bottom 15 bits) + compact_validator = uint64((index << 16) + (validator.slashed << 15) + compact_balance) + committees[shard].compact_validators.append(compact_validator) + return hash_tree_root(Vector[CompactCommittee, SHARD_COUNT](committees)) ``` ### `generate_seed` @@ -767,7 +787,7 @@ def generate_seed(state: BeaconState, """ return hash( get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD)) + - get_active_index_root(state, epoch) + + hash_tree_root(List[ValidatorIndex, VALIDATOR_REGISTRY_LIMIT](get_active_validator_indices(state, epoch))) + int_to_bytes(epoch, length=32) ) ``` @@ -867,7 +887,7 @@ def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> S ```python def get_attesting_indices(state: BeaconState, data: AttestationData, - bits: Bitlist[MAX_INDICES_PER_ATTESTATION]) -> Set[ValidatorIndex]: + bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]: """ Return the set of attesting indices corresponding to ``data`` and ``bitfield``. """ @@ -946,7 +966,7 @@ def validate_indexed_attestation(state: BeaconState, indexed_attestation: Indexe # Verify no index has custody bit equal to 1 [to be removed in phase 1] assert len(bit_1_indices) == 0 # Verify max number of indices - assert len(bit_0_indices) + len(bit_1_indices) <= MAX_INDICES_PER_ATTESTATION + assert len(bit_0_indices) + len(bit_1_indices) <= MAX_VALIDATORS_PER_COMMITTEE # Verify index sets are disjoint assert len(set(bit_0_indices).intersection(bit_1_indices)) == 0 # Verify indices are sorted @@ -1148,12 +1168,9 @@ def get_genesis_beacon_state(deposits: Sequence[Deposit], genesis_time: int, eth validator.activation_epoch = GENESIS_EPOCH # Populate active_index_roots - genesis_active_index_root = hash_tree_root( - List[ValidatorIndex, VALIDATOR_REGISTRY_LIMIT](get_active_validator_indices(state, GENESIS_EPOCH)) - ) + genesis_committee_root = get_compact_committees_root(state, GENESIS_EPOCH) for index in range(EPOCHS_PER_HISTORICAL_VECTOR): - state.active_index_roots[index] = genesis_active_index_root - + state.compact_committees_roots[index] = genesis_committee_root return state ``` @@ -1475,7 +1492,7 @@ def process_slashings(state: BeaconState) -> None: ```python def process_final_updates(state: BeaconState) -> None: current_epoch = get_current_epoch(state) - next_epoch = current_epoch + 1 + next_epoch = Shard(current_epoch + 1) # Reset eth1 data votes if (state.slot + 1) % SLOTS_PER_ETH1_VOTING_PERIOD == 0: state.eth1_data_votes = [] @@ -1488,12 +1505,8 @@ def process_final_updates(state: BeaconState) -> None: # Update start shard state.start_shard = Shard((state.start_shard + get_shard_delta(state, current_epoch)) % SHARD_COUNT) # Set active index root - index_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % EPOCHS_PER_HISTORICAL_VECTOR - state.active_index_roots[index_root_position] = hash_tree_root( - List[ValidatorIndex, VALIDATOR_REGISTRY_LIMIT]( - get_active_validator_indices(state, Epoch(next_epoch + ACTIVATION_EXIT_DELAY)) - ) - ) + committee_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % EPOCHS_PER_HISTORICAL_VECTOR + state.compact_committees_roots[committee_root_position] = get_compact_committees_root(state, next_epoch) # Reset slashings state.slashings[next_epoch % EPOCHS_PER_SLASHINGS_VECTOR] = Gwei(0) # Set randao mix diff --git a/specs/light_client/sync_protocol.md b/specs/light_client/sync_protocol.md index 3f5a37289..9a089720b 100644 --- a/specs/light_client/sync_protocol.md +++ b/specs/light_client/sync_protocol.md @@ -31,7 +31,7 @@ We define an "expansion" of an object as an object where a field in an object th We define two expansions: -* `ExtendedBeaconState`, which is identical to a `BeaconState` except `active_index_roots: List[Bytes32]` is replaced by `active_indices: List[List[ValidatorIndex]]`, where `BeaconState.active_index_roots[i] = hash_tree_root(ExtendedBeaconState.active_indices[i])`. +* `ExtendedBeaconState`, which is identical to a `BeaconState` except `compact_committees_roots: List[Bytes32]` is replaced by `active_indices: List[List[ValidatorIndex]]`, where `BeaconState.compact_committees_roots[i] = hash_tree_root(ExtendedBeaconState.active_indices[i])`. * `ExtendedBeaconBlock`, which is identical to a `BeaconBlock` except `state_root` is replaced with the corresponding `state: ExtendedBeaconState`. ### `get_active_validator_indices` @@ -40,7 +40,7 @@ Note that there is now a new way to compute `get_active_validator_indices`: ```python def get_active_validator_indices(state: ExtendedBeaconState, epoch: Epoch) -> List[ValidatorIndex]: - return state.active_indices[epoch % ACTIVE_INDEX_ROOTS_LENGTH] + return state.active_indices[epoch % EPOCHS_PER_HISTORICAL_VECTOR] ``` Note that it takes `state` instead of `state.validators` as an argument. This does not affect its use in `get_shuffled_committee`, because `get_shuffled_committee` has access to the full `state` as one of its arguments. diff --git a/test_libs/pyspec/eth2spec/test/helpers/attestations.py b/test_libs/pyspec/eth2spec/test/helpers/attestations.py index 6fdf1538b..ffd8d1afa 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/attestations.py +++ b/test_libs/pyspec/eth2spec/test/helpers/attestations.py @@ -67,8 +67,8 @@ def get_valid_attestation(spec, state, slot=None, signed=False): ) committee_size = len(crosslink_committee) - aggregation_bits = Bitlist[spec.MAX_INDICES_PER_ATTESTATION](*([0] * committee_size)) - custody_bits = Bitlist[spec.MAX_INDICES_PER_ATTESTATION](*([0] * committee_size)) + 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, diff --git a/test_libs/pyspec/eth2spec/test/helpers/genesis.py b/test_libs/pyspec/eth2spec/test/helpers/genesis.py index 9c64a9cc1..7ffacd7d0 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/genesis.py +++ b/test_libs/pyspec/eth2spec/test/helpers/genesis.py @@ -43,9 +43,9 @@ def create_genesis_state(spec, num_validators): validator.activation_eligibility_epoch = spec.GENESIS_EPOCH validator.activation_epoch = spec.GENESIS_EPOCH - genesis_active_index_root = hash_tree_root(List[spec.ValidatorIndex, spec.VALIDATOR_REGISTRY_LIMIT]( + genesis_compact_committees_root = hash_tree_root(List[spec.ValidatorIndex, spec.VALIDATOR_REGISTRY_LIMIT]( spec.get_active_validator_indices(state, spec.GENESIS_EPOCH))) for index in range(spec.EPOCHS_PER_HISTORICAL_VECTOR): - state.active_index_roots[index] = genesis_active_index_root + state.compact_committees_roots[index] = genesis_compact_committees_root return 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 41207fdf4..df2f0627f 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 @@ -306,7 +306,7 @@ def test_empty_aggregation_bits(spec, state): attestation = get_valid_attestation(spec, state) state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - attestation.aggregation_bits = Bitlist[spec.MAX_INDICES_PER_ATTESTATION]( + attestation.aggregation_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]( *([0b0] * len(attestation.aggregation_bits))) sign_attestation(spec, state, attestation)