Merge pull request #1219 from ethereum/committee-roots

Compact committee roots
This commit is contained in:
Danny Ryan 2019-06-29 12:26:45 -05:00 committed by GitHub
commit e1826aae32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 52 additions and 39 deletions

View File

@ -10,7 +10,7 @@ SHARD_COUNT: 1024
# 2**7 (= 128) # 2**7 (= 128)
TARGET_COMMITTEE_SIZE: 128 TARGET_COMMITTEE_SIZE: 128
# 2**12 (= 4,096) # 2**12 (= 4,096)
MAX_INDICES_PER_ATTESTATION: 4096 MAX_VALIDATORS_PER_COMMITTEE: 4096
# 2**2 (= 4) # 2**2 (= 4)
MIN_PER_EPOCH_CHURN_LIMIT: 4 MIN_PER_EPOCH_CHURN_LIMIT: 4
# 2**16 (= 65,536) # 2**16 (= 65,536)

View File

@ -9,7 +9,7 @@ SHARD_COUNT: 8
# [customized] unsecure, but fast # [customized] unsecure, but fast
TARGET_COMMITTEE_SIZE: 4 TARGET_COMMITTEE_SIZE: 4
# 2**12 (= 4,096) # 2**12 (= 4,096)
MAX_INDICES_PER_ATTESTATION: 4096 MAX_VALIDATORS_PER_COMMITTEE: 4096
# 2**2 (= 4) # 2**2 (= 4)
MIN_PER_EPOCH_CHURN_LIMIT: 4 MIN_PER_EPOCH_CHURN_LIMIT: 4
# 2**16 (= 65,536) # 2**16 (= 65,536)

View File

@ -34,6 +34,7 @@
- [`Eth1Data`](#eth1data) - [`Eth1Data`](#eth1data)
- [`HistoricalBatch`](#historicalbatch) - [`HistoricalBatch`](#historicalbatch)
- [`DepositData`](#depositdata) - [`DepositData`](#depositdata)
- [`CompactCommittee`](#compactcommittee)
- [`BeaconBlockHeader`](#beaconblockheader) - [`BeaconBlockHeader`](#beaconblockheader)
- [Beacon operations](#beacon-operations) - [Beacon operations](#beacon-operations)
- [`ProposerSlashing`](#proposerslashing) - [`ProposerSlashing`](#proposerslashing)
@ -69,7 +70,7 @@
- [`get_block_root_at_slot`](#get_block_root_at_slot) - [`get_block_root_at_slot`](#get_block_root_at_slot)
- [`get_block_root`](#get_block_root) - [`get_block_root`](#get_block_root)
- [`get_randao_mix`](#get_randao_mix) - [`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) - [`generate_seed`](#generate_seed)
- [`get_beacon_proposer_index`](#get_beacon_proposer_index) - [`get_beacon_proposer_index`](#get_beacon_proposer_index)
- [`verify_merkle_branch`](#verify_merkle_branch) - [`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) | | `SHARD_COUNT` | `2**10` (= 1,024) |
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | | `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) | | `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) |
| `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) | | `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) |
| `SHUFFLE_ROUND_COUNT` | `90` | | `SHUFFLE_ROUND_COUNT` | `90` |
@ -350,8 +351,8 @@ class AttestationDataAndCustodyBit(Container):
```python ```python
class IndexedAttestation(Container): class IndexedAttestation(Container):
custody_bit_0_indices: List[ValidatorIndex, MAX_INDICES_PER_ATTESTATION] # Indices with custody bit equal to 0 custody_bit_0_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE] # 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_1_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE] # Indices with custody bit equal to 1
data: AttestationData data: AttestationData
signature: BLSSignature signature: BLSSignature
``` ```
@ -360,7 +361,7 @@ class IndexedAttestation(Container):
```python ```python
class PendingAttestation(Container): class PendingAttestation(Container):
aggregation_bits: Bitlist[MAX_INDICES_PER_ATTESTATION] aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]
data: AttestationData data: AttestationData
inclusion_delay: Slot inclusion_delay: Slot
proposer_index: ValidatorIndex proposer_index: ValidatorIndex
@ -393,6 +394,14 @@ class DepositData(Container):
signature: BLSSignature signature: BLSSignature
``` ```
#### `CompactCommittee`
```python
class CompactCommittee(Container):
pubkeys: List[Bytes48, MAX_VALIDATORS_PER_COMMITTEE]
compact_validators: List[uint64, MAX_VALIDATORS_PER_COMMITTEE]
```
#### `BeaconBlockHeader` #### `BeaconBlockHeader`
```python ```python
@ -427,9 +436,9 @@ class AttesterSlashing(Container):
```python ```python
class Attestation(Container): class Attestation(Container):
aggregation_bits: Bitlist[MAX_INDICES_PER_ATTESTATION] aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]
data: AttestationData data: AttestationData
custody_bits: Bitlist[MAX_INDICES_PER_ATTESTATION] custody_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]
signature: BLSSignature signature: BLSSignature
``` ```
@ -517,7 +526,7 @@ class BeaconState(Container):
# Shuffling # Shuffling
start_shard: Shard start_shard: Shard
randao_mixes: Vector[Hash, EPOCHS_PER_HISTORICAL_VECTOR] 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
slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances
# Attestations # Attestations
@ -691,6 +700,9 @@ def get_shard_delta(state: BeaconState, epoch: Epoch) -> int:
```python ```python
def get_epoch_start_shard(state: BeaconState, epoch: Epoch) -> Shard: 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 assert epoch <= get_current_epoch(state) + 1
check_epoch = 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) 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] return state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR]
``` ```
### `get_active_index_root` ### `get_compact_committees_root`
```python ```python
def get_active_index_root(state: BeaconState, def get_compact_committees_root(state: BeaconState, epoch: Epoch) -> Hash:
epoch: Epoch) -> Hash:
""" """
Return the index root at a recent ``epoch``. Return the compact committee root for the current epoch.
``epoch`` expected to be between
(current_epoch - EPOCHS_PER_HISTORICAL_VECTOR + ACTIVATION_EXIT_DELAY, current_epoch + ACTIVATION_EXIT_DELAY].
""" """
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` ### `generate_seed`
@ -767,7 +787,7 @@ def generate_seed(state: BeaconState,
""" """
return hash( return hash(
get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD)) + 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) int_to_bytes(epoch, length=32)
) )
``` ```
@ -867,7 +887,7 @@ def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> S
```python ```python
def get_attesting_indices(state: BeaconState, def get_attesting_indices(state: BeaconState,
data: AttestationData, 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``. 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] # Verify no index has custody bit equal to 1 [to be removed in phase 1]
assert len(bit_1_indices) == 0 assert len(bit_1_indices) == 0
# Verify max number of indices # 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 # Verify index sets are disjoint
assert len(set(bit_0_indices).intersection(bit_1_indices)) == 0 assert len(set(bit_0_indices).intersection(bit_1_indices)) == 0
# Verify indices are sorted # 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 validator.activation_epoch = GENESIS_EPOCH
# Populate active_index_roots # Populate active_index_roots
genesis_active_index_root = hash_tree_root( genesis_committee_root = get_compact_committees_root(state, GENESIS_EPOCH)
List[ValidatorIndex, VALIDATOR_REGISTRY_LIMIT](get_active_validator_indices(state, GENESIS_EPOCH))
)
for index in range(EPOCHS_PER_HISTORICAL_VECTOR): 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 return state
``` ```
@ -1475,7 +1492,7 @@ def process_slashings(state: BeaconState) -> None:
```python ```python
def process_final_updates(state: BeaconState) -> None: def process_final_updates(state: BeaconState) -> None:
current_epoch = get_current_epoch(state) current_epoch = get_current_epoch(state)
next_epoch = current_epoch + 1 next_epoch = Shard(current_epoch + 1)
# Reset eth1 data votes # Reset eth1 data votes
if (state.slot + 1) % SLOTS_PER_ETH1_VOTING_PERIOD == 0: if (state.slot + 1) % SLOTS_PER_ETH1_VOTING_PERIOD == 0:
state.eth1_data_votes = [] state.eth1_data_votes = []
@ -1488,12 +1505,8 @@ def process_final_updates(state: BeaconState) -> None:
# Update start shard # Update start shard
state.start_shard = Shard((state.start_shard + get_shard_delta(state, current_epoch)) % SHARD_COUNT) state.start_shard = Shard((state.start_shard + get_shard_delta(state, current_epoch)) % SHARD_COUNT)
# Set active index root # Set active index root
index_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % EPOCHS_PER_HISTORICAL_VECTOR committee_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % EPOCHS_PER_HISTORICAL_VECTOR
state.active_index_roots[index_root_position] = hash_tree_root( state.compact_committees_roots[committee_root_position] = get_compact_committees_root(state, next_epoch)
List[ValidatorIndex, VALIDATOR_REGISTRY_LIMIT](
get_active_validator_indices(state, Epoch(next_epoch + ACTIVATION_EXIT_DELAY))
)
)
# Reset slashings # Reset slashings
state.slashings[next_epoch % EPOCHS_PER_SLASHINGS_VECTOR] = Gwei(0) state.slashings[next_epoch % EPOCHS_PER_SLASHINGS_VECTOR] = Gwei(0)
# Set randao mix # Set randao mix

View File

@ -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: 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`. * `ExtendedBeaconBlock`, which is identical to a `BeaconBlock` except `state_root` is replaced with the corresponding `state: ExtendedBeaconState`.
### `get_active_validator_indices` ### `get_active_validator_indices`
@ -40,7 +40,7 @@ Note that there is now a new way to compute `get_active_validator_indices`:
```python ```python
def get_active_validator_indices(state: ExtendedBeaconState, epoch: Epoch) -> List[ValidatorIndex]: 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. 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.

View File

@ -67,8 +67,8 @@ def get_valid_attestation(spec, state, slot=None, signed=False):
) )
committee_size = len(crosslink_committee) committee_size = len(crosslink_committee)
aggregation_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_INDICES_PER_ATTESTATION](*([0] * committee_size)) custody_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*([0] * committee_size))
attestation = spec.Attestation( attestation = spec.Attestation(
aggregation_bits=aggregation_bits, aggregation_bits=aggregation_bits,
data=attestation_data, data=attestation_data,

View File

@ -43,9 +43,9 @@ def create_genesis_state(spec, num_validators):
validator.activation_eligibility_epoch = spec.GENESIS_EPOCH validator.activation_eligibility_epoch = spec.GENESIS_EPOCH
validator.activation_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))) spec.get_active_validator_indices(state, spec.GENESIS_EPOCH)))
for index in range(spec.EPOCHS_PER_HISTORICAL_VECTOR): 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 return state

View File

@ -306,7 +306,7 @@ def test_empty_aggregation_bits(spec, state):
attestation = get_valid_attestation(spec, state) attestation = get_valid_attestation(spec, state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY 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))) *([0b0] * len(attestation.aggregation_bits)))
sign_attestation(spec, state, attestation) sign_attestation(spec, state, attestation)