'get_sync_committee -> get_next_sync_committee

This commit is contained in:
Danny Ryan
2021-05-12 09:44:13 -06:00
parent 1a7aa898ab
commit a8791f04c7
4 changed files with 88 additions and 87 deletions

View File

@@ -32,8 +32,8 @@
- [`add_flag`](#add_flag)
- [`has_flag`](#has_flag)
- [Beacon state accessors](#beacon-state-accessors)
- [`get_sync_committee_indices`](#get_sync_committee_indices)
- [`get_sync_committee`](#get_sync_committee)
- [`get_next_sync_committee_indices`](#get_next_sync_committee_indices)
- [`get_next_sync_committee`](#get_next_sync_committee)
- [`get_base_reward_per_increment`](#get_base_reward_per_increment)
- [`get_base_reward`](#get_base_reward)
- [`get_unslashed_participating_indices`](#get_unslashed_participating_indices)
@@ -270,15 +270,15 @@ def has_flag(flags: ParticipationFlags, flag_index: int) -> bool:
### Beacon state accessors
#### `get_sync_committee_indices`
#### `get_next_sync_committee_indices`
```python
def get_sync_committee_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]:
def get_next_sync_committee_indices(state: BeaconState) -> Sequence[ValidatorIndex]:
"""
Return the sequence of sync committee indices (which may include duplicate indices)
for a given ``state`` and ``epoch`` at a sync committee period boundary.
for the next sync committee, given a ``state`` at a sync committee period boundary.
"""
assert epoch % EPOCHS_PER_SYNC_COMMITTEE_PERIOD == 0
epoch = Epoch(get_current_epoch(state) + 1)
MAX_RANDOM_BYTE = 2**8 - 1
active_validator_indices = get_active_validator_indices(state, epoch)
@@ -297,25 +297,25 @@ def get_sync_committee_indices(state: BeaconState, epoch: Epoch) -> Sequence[Val
return sync_committee_indices
```
#### `get_sync_committee`
#### `get_next_sync_committee`
```python
def get_sync_committee(state: BeaconState, epoch: Epoch) -> SyncCommittee:
def get_next_sync_committee(state: BeaconState) -> SyncCommittee:
"""
Return the *next* sync committee for a given ``state`` and ``epoch``.
Return the *next* sync committee for a given ``state``.
``SyncCommittee`` contains an aggregate pubkey that enables
resource-constrained clients to save some computation when verifying
the sync committee's signature.
``SyncCommittee`` can also contain duplicate pubkeys, when ``get_sync_committee_indices``
``SyncCommittee`` can also contain duplicate pubkeys, when ``get_next_sync_committee_indices``
returns duplicate indices. Implementations must take care when handling
optimizations relating to aggregation and verification in the presence of duplicates.
Note: This function should only be called at sync committee period boundaries, as
``get_sync_committee_indices`` is not stable within a given period.
``get_next_sync_committee_indices`` is not stable within a given period.
"""
indices = get_sync_committee_indices(state, epoch)
indices = get_next_sync_committee_indices(state)
pubkeys = [state.validators[index].pubkey for index in indices]
aggregate_pubkey = bls.AggregatePKs(pubkeys)
return SyncCommittee(pubkeys=pubkeys, aggregate_pubkey=aggregate_pubkey)
@@ -688,7 +688,7 @@ def process_sync_committee_updates(state: BeaconState) -> None:
next_epoch = get_current_epoch(state) + Epoch(1)
if next_epoch % EPOCHS_PER_SYNC_COMMITTEE_PERIOD == 0:
state.current_sync_committee = state.next_sync_committee
state.next_sync_committee = get_sync_committee(state, next_epoch)
state.next_sync_committee = get_next_sync_committee(state)
```
## Initialize state for pure Altair testnets and test vectors
@@ -733,13 +733,9 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
state.genesis_validators_root = hash_tree_root(state.validators)
# [New in Altair] Fill in sync committees
current_period = get_current_epoch(state) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
previous_period = current_period - min(1, current_period)
current_base_epoch = current_period * EPOCHS_PER_SYNC_COMMITTEE_PERIOD
previous_base_epoch = previous_period * EPOCHS_PER_SYNC_COMMITTEE_PERIOD
state.current_sync_committee = get_sync_committee(state, previous_base_epoch)
state.next_sync_committee = get_sync_committee(state, current_base_epoch)
# Note: A duplicate committee is assigned for the current and next committee at genesis
state.current_sync_committee = get_next_sync_committee(state)
state.next_sync_committee = get_next_sync_committee(state)
return state
```

View File

@@ -86,12 +86,8 @@ def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState:
)
# Fill in sync committees
current_period = epoch // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
previous_period = current_period - min(1, current_period)
current_base_epoch = current_period * EPOCHS_PER_SYNC_COMMITTEE_PERIOD
previous_base_epoch = previous_period * EPOCHS_PER_SYNC_COMMITTEE_PERIOD
post.current_sync_committee = get_sync_committee(post, previous_base_epoch)
post.next_sync_committee = get_sync_committee(post, current_base_epoch)
# Note: A duplicate committee is assigned for the current and next committee at the fork boundary
post.current_sync_committee = get_next_sync_committee(post)
post.next_sync_committee = get_next_sync_committee(post)
return post
```

View File

@@ -49,9 +49,9 @@ def get_committee_indices(spec, state, duplicates=False):
"""
state = state.copy()
current_epoch = spec.get_current_epoch(state)
randao_index = current_epoch % spec.EPOCHS_PER_HISTORICAL_VECTOR
randao_index = (current_epoch + 1) % spec.EPOCHS_PER_HISTORICAL_VECTOR
while True:
committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
committee = spec.get_next_sync_committee_indices(state)
if duplicates:
if len(committee) != len(set(committee)):
return committee
@@ -61,23 +61,32 @@ def get_committee_indices(spec, state, duplicates=False):
state.randao_mixes[randao_index] = hash(state.randao_mixes[randao_index])
def compute_committee_indices(spec, state, committee):
"""
Given a ``committee``, calculate and return the related indices
"""
all_pubkeys = [v.pubkey for v in state.validators]
committee_indices = [all_pubkeys.index(pubkey) for pubkey in committee.pubkeys]
return committee_indices
@with_altair_and_later
@spec_state_test
@always_bls
def test_invalid_signature_missing_participant(spec, state):
committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
committee_indices = compute_committee_indices(spec, state, state.current_sync_committee)
rng = random.Random(2020)
random_participant = rng.choice(committee)
random_participant = rng.choice(committee_indices)
block = build_empty_block_for_next_slot(spec, state)
# Exclude one participant whose signature was included.
block.body.sync_aggregate = spec.SyncAggregate(
sync_committee_bits=[index != random_participant for index in committee],
sync_committee_bits=[index != random_participant for index in committee_indices],
sync_committee_signature=compute_aggregate_sync_committee_signature(
spec,
state,
block.slot - 1,
committee, # full committee signs
committee_indices, # full committee signs
)
)
yield from run_sync_committee_processing(spec, state, block, expect_exception=True)
@@ -87,31 +96,38 @@ def test_invalid_signature_missing_participant(spec, state):
@spec_state_test
@always_bls
def test_invalid_signature_extra_participant(spec, state):
committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
committee_indices = compute_committee_indices(spec, state, state.current_sync_committee)
rng = random.Random(3030)
random_participant = rng.choice(committee)
random_participant = rng.choice(committee_indices)
block = build_empty_block_for_next_slot(spec, state)
# Exclude one signature even though the block claims the entire committee participated.
block.body.sync_aggregate = spec.SyncAggregate(
sync_committee_bits=[True] * len(committee),
sync_committee_bits=[True] * len(committee_indices),
sync_committee_signature=compute_aggregate_sync_committee_signature(
spec,
state,
block.slot - 1,
[index for index in committee if index != random_participant],
[index for index in committee_indices if index != random_participant],
)
)
yield from run_sync_committee_processing(spec, state, block, expect_exception=True)
def compute_sync_committee_inclusion_reward(spec, state, participant_index, committee, committee_bits):
def compute_sync_committee_inclusion_reward(spec,
state,
participant_index,
committee_indices,
committee_bits):
total_active_increments = spec.get_total_active_balance(state) // spec.EFFECTIVE_BALANCE_INCREMENT
total_base_rewards = spec.Gwei(spec.get_base_reward_per_increment(state) * total_active_increments)
max_epoch_rewards = spec.Gwei(total_base_rewards * spec.SYNC_REWARD_WEIGHT // spec.WEIGHT_DENOMINATOR)
included_indices = [index for index, bit in zip(committee, committee_bits) if bit]
max_slot_rewards = spec.Gwei(max_epoch_rewards * len(included_indices) // len(committee) // spec.SLOTS_PER_EPOCH)
included_indices = [index for index, bit in zip(committee_indices, committee_bits) if bit]
max_slot_rewards = spec.Gwei(
max_epoch_rewards * len(included_indices)
// len(committee_indices) // spec.SLOTS_PER_EPOCH
)
# Compute the participant and proposer sync rewards
committee_effective_balance = sum([state.validators[index].effective_balance for index in included_indices])
@@ -120,23 +136,23 @@ def compute_sync_committee_inclusion_reward(spec, state, participant_index, comm
return spec.Gwei(max_slot_rewards * effective_balance // committee_effective_balance)
def compute_sync_committee_participant_reward(spec, state, participant_index, committee, committee_bits):
included_indices = [index for index, bit in zip(committee, committee_bits) if bit]
def compute_sync_committee_participant_reward(spec, state, participant_index, committee_indices, committee_bits):
included_indices = [index for index, bit in zip(committee_indices, committee_bits) if bit]
multiplicities = Counter(included_indices)
inclusion_reward = compute_sync_committee_inclusion_reward(
spec, state, participant_index, committee, committee_bits,
spec, state, participant_index, committee_indices, committee_bits,
)
return spec.Gwei(inclusion_reward * multiplicities[participant_index])
def compute_sync_committee_proposer_reward(spec, state, committee, committee_bits):
def compute_sync_committee_proposer_reward(spec, state, committee_indices, committee_bits):
proposer_reward = 0
for index, bit in zip(committee, committee_bits):
for index, bit in zip(committee_indices, committee_bits):
if not bit:
continue
inclusion_reward = compute_sync_committee_inclusion_reward(
spec, state, index, committee, committee_bits,
spec, state, index, committee_indices, committee_bits,
)
proposer_reward_denominator = (
(spec.WEIGHT_DENOMINATOR - spec.PROPOSER_WEIGHT)
@@ -147,15 +163,15 @@ def compute_sync_committee_proposer_reward(spec, state, committee, committee_bit
return proposer_reward
def validate_sync_committee_rewards(spec, pre_state, post_state, committee, committee_bits, proposer_index):
def validate_sync_committee_rewards(spec, pre_state, post_state, committee_indices, committee_bits, proposer_index):
for index in range(len(post_state.validators)):
reward = 0
if index in committee:
if index in committee_indices:
reward += compute_sync_committee_participant_reward(
spec,
pre_state,
index,
committee,
committee_indices,
committee_bits,
)
@@ -163,14 +179,14 @@ def validate_sync_committee_rewards(spec, pre_state, post_state, committee, comm
reward += compute_sync_committee_proposer_reward(
spec,
pre_state,
committee,
committee_indices,
committee_bits,
)
assert post_state.balances[index] == pre_state.balances[index] + reward
def run_successful_sync_committee_test(spec, state, committee, committee_bits):
def run_successful_sync_committee_test(spec, state, committee_indices, committee_bits):
pre_state = state.copy()
block = build_empty_block_for_next_slot(spec, state)
@@ -180,7 +196,7 @@ def run_successful_sync_committee_test(spec, state, committee, committee_bits):
spec,
state,
block.slot - 1,
[index for index, bit in zip(committee, committee_bits) if bit],
[index for index, bit in zip(committee_indices, committee_bits) if bit],
)
)
@@ -190,7 +206,7 @@ def run_successful_sync_committee_test(spec, state, committee, committee_bits):
spec,
pre_state,
state,
committee,
committee_indices,
committee_bits,
block.proposer_index,
)
@@ -200,60 +216,60 @@ def run_successful_sync_committee_test(spec, state, committee, committee_bits):
@with_configs([MINIMAL], reason="to create nonduplicate committee")
@spec_state_test
def test_sync_committee_rewards_nonduplicate_committee(spec, state):
committee = get_committee_indices(spec, state, duplicates=False)
committee_size = len(committee)
committee_indices = get_committee_indices(spec, state, duplicates=False)
committee_size = len(committee_indices)
committee_bits = [True] * committee_size
active_validator_count = len(spec.get_active_validator_indices(state, spec.get_current_epoch(state)))
# Preconditions of this test case
assert active_validator_count >= spec.SYNC_COMMITTEE_SIZE
assert committee_size == len(set(committee))
assert committee_size == len(set(committee_indices))
yield from run_successful_sync_committee_test(spec, state, committee, committee_bits)
yield from run_successful_sync_committee_test(spec, state, committee_indices, committee_bits)
@with_altair_and_later
@with_configs([MAINNET], reason="to create duplicate committee")
@spec_state_test
def test_sync_committee_rewards_duplicate_committee(spec, state):
committee = get_committee_indices(spec, state, duplicates=True)
committee_size = len(committee)
committee_indices = get_committee_indices(spec, state, duplicates=True)
committee_size = len(committee_indices)
committee_bits = [True] * committee_size
active_validator_count = len(spec.get_active_validator_indices(state, spec.get_current_epoch(state)))
# Preconditions of this test case
assert active_validator_count < spec.SYNC_COMMITTEE_SIZE
assert committee_size > len(set(committee))
assert committee_size > len(set(committee_indices))
yield from run_successful_sync_committee_test(spec, state, committee, committee_bits)
yield from run_successful_sync_committee_test(spec, state, committee_indices, committee_bits)
@with_altair_and_later
@spec_state_test
@always_bls
def test_sync_committee_rewards_not_full_participants(spec, state):
committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
committee_indices = compute_committee_indices(spec, state, state.current_sync_committee)
rng = random.Random(1010)
committee_bits = [rng.choice([True, False]) for _ in committee]
committee_bits = [rng.choice([True, False]) for _ in committee_indices]
yield from run_successful_sync_committee_test(spec, state, committee, committee_bits)
yield from run_successful_sync_committee_test(spec, state, committee_indices, committee_bits)
@with_altair_and_later
@spec_state_test
@always_bls
def test_sync_committee_rewards_empty_participants(spec, state):
committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
committee_bits = [False for _ in committee]
committee_indices = compute_committee_indices(spec, state, state.current_sync_committee)
committee_bits = [False for _ in committee_indices]
yield from run_successful_sync_committee_test(spec, state, committee, committee_bits)
yield from run_successful_sync_committee_test(spec, state, committee_indices, committee_bits)
@with_altair_and_later
@spec_state_test
@always_bls
def test_invalid_signature_past_block(spec, state):
committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
committee_indices = compute_committee_indices(spec, state, state.current_sync_committee)
blocks = []
for _ in range(2):
@@ -261,12 +277,12 @@ def test_invalid_signature_past_block(spec, state):
block = build_empty_block_for_next_slot(spec, state)
# Valid sync committee signature here...
block.body.sync_aggregate = spec.SyncAggregate(
sync_committee_bits=[True] * len(committee),
sync_committee_bits=[True] * len(committee_indices),
sync_committee_signature=compute_aggregate_sync_committee_signature(
spec,
state,
block.slot - 1,
committee,
committee_indices,
)
)
@@ -276,12 +292,12 @@ def test_invalid_signature_past_block(spec, state):
invalid_block = build_empty_block_for_next_slot(spec, state)
# Invalid signature from a slot other than the previous
invalid_block.body.sync_aggregate = spec.SyncAggregate(
sync_committee_bits=[True] * len(committee),
sync_committee_bits=[True] * len(committee_indices),
sync_committee_signature=compute_aggregate_sync_committee_signature(
spec,
state,
invalid_block.slot - 2,
committee,
committee_indices,
)
)
@@ -306,19 +322,18 @@ def test_invalid_signature_previous_committee(spec, state):
transition_to(spec, state, slot_in_future_sync_committee_period)
# Use the previous sync committee to produce the signature.
pubkeys = [validator.pubkey for validator in state.validators]
# Ensure that the pubkey sets are different.
assert set(old_sync_committee.pubkeys) != set(state.current_sync_committee.pubkeys)
committee = [pubkeys.index(pubkey) for pubkey in old_sync_committee.pubkeys]
committee_indices = compute_committee_indices(spec, state, old_sync_committee)
block = build_empty_block_for_next_slot(spec, state)
block.body.sync_aggregate = spec.SyncAggregate(
sync_committee_bits=[True] * len(committee),
sync_committee_bits=[True] * len(committee_indices),
sync_committee_signature=compute_aggregate_sync_committee_signature(
spec,
state,
block.slot - 1,
committee,
committee_indices,
)
)
@@ -345,14 +360,12 @@ def test_valid_signature_future_committee(spec, state):
sync_committee = state.current_sync_committee
next_sync_committee = state.next_sync_committee
expected_next_sync_committee = spec.get_sync_committee(state, epoch_in_future_sync_committee_period)
assert next_sync_committee == expected_next_sync_committee
assert next_sync_committee != sync_committee
assert sync_committee != old_current_sync_committee
assert sync_committee != old_next_sync_committee
pubkeys = [validator.pubkey for validator in state.validators]
committee_indices = [pubkeys.index(pubkey) for pubkey in sync_committee.pubkeys]
committee_indices = compute_committee_indices(spec, state, sync_committee)
block = build_empty_block_for_next_slot(spec, state)
block.body.sync_aggregate = spec.SyncAggregate(

View File

@@ -69,12 +69,8 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
if spec.fork not in FORKS_BEFORE_ALTAIR:
# Fill in sync committees
current_period = spec.get_current_epoch(state) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
previous_period = current_period - min(1, current_period)
current_base_epoch = current_period * spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
previous_base_epoch = previous_period * spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
state.current_sync_committee = spec.get_sync_committee(state, previous_base_epoch)
state.next_sync_committee = spec.get_sync_committee(state, current_base_epoch)
# Note: A duplicate committee is assigned for the current and next committee at genesis
state.current_sync_committee = spec.get_next_sync_committee(state)
state.next_sync_committee = spec.get_next_sync_committee(state)
return state