Attestation committee refactor
* Remove `get_crosslink_committees_at_slot` (that function's ugly man...) * Make the "base" that everything works off instead be `get_crosslink_committee` * Attestations store epoch, start shard and shard, no longer slot (slot can be calculated from the other three) * Retaining start shard in attestations allows `get_attesting_indices` to peek much further back into the past, making it useful for slashings (Phase 1) * Some two-layer-deep nested loops become one-layer-deep loops
This commit is contained in:
parent
2787fea5fe
commit
77d7aa7630
|
@ -65,7 +65,8 @@
|
||||||
- [`get_epoch_committee_count`](#get_epoch_committee_count)
|
- [`get_epoch_committee_count`](#get_epoch_committee_count)
|
||||||
- [`get_shard_delta`](#get_shard_delta)
|
- [`get_shard_delta`](#get_shard_delta)
|
||||||
- [`compute_committee`](#compute_committee)
|
- [`compute_committee`](#compute_committee)
|
||||||
- [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot)
|
- [`get_epoch_start_shard`](#get_epoch_start_shard)
|
||||||
|
- [`committee_shard_to_slot`](#committee_shard_to_slot)
|
||||||
- [`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_state_root`](#get_state_root)
|
- [`get_state_root`](#get_state_root)
|
||||||
|
@ -74,6 +75,7 @@
|
||||||
- [`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)
|
||||||
|
- [`get_crosslink_committee`](#get_crosslink_committee)
|
||||||
- [`get_attesting_indices`](#get_attesting_indices)
|
- [`get_attesting_indices`](#get_attesting_indices)
|
||||||
- [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-)
|
- [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-)
|
||||||
- [`bytes_to_int`](#bytes_to_int)
|
- [`bytes_to_int`](#bytes_to_int)
|
||||||
|
@ -307,7 +309,7 @@ The types are defined topologically to aid in facilitating an executable version
|
||||||
```python
|
```python
|
||||||
{
|
{
|
||||||
# LMD GHOST vote
|
# LMD GHOST vote
|
||||||
'slot': 'uint64',
|
'epoch': 'uint64',
|
||||||
'beacon_block_root': 'bytes32',
|
'beacon_block_root': 'bytes32',
|
||||||
|
|
||||||
# FFG vote
|
# FFG vote
|
||||||
|
@ -316,6 +318,7 @@ The types are defined topologically to aid in facilitating an executable version
|
||||||
'target_root': 'bytes32',
|
'target_root': 'bytes32',
|
||||||
|
|
||||||
# Crosslink vote
|
# Crosslink vote
|
||||||
|
'epoch_start_shard': 'uint64',
|
||||||
'shard': 'uint64',
|
'shard': 'uint64',
|
||||||
'previous_crosslink_root': 'bytes32',
|
'previous_crosslink_root': 'bytes32',
|
||||||
'crosslink_data_root': 'bytes32',
|
'crosslink_data_root': 'bytes32',
|
||||||
|
@ -805,44 +808,30 @@ def compute_committee(validator_indices: List[ValidatorIndex],
|
||||||
|
|
||||||
Note: this definition and the next few definitions are highly inefficient as algorithms, as they re-calculate many sub-expressions. Production implementations are expected to appropriately use caching/memoization to avoid redoing work.
|
Note: this definition and the next few definitions are highly inefficient as algorithms, as they re-calculate many sub-expressions. Production implementations are expected to appropriately use caching/memoization to avoid redoing work.
|
||||||
|
|
||||||
### `get_crosslink_committees_at_slot`
|
### `get_epoch_start_shard`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_crosslink_committees_at_slot(state: BeaconState,
|
def get_epoch_start_shard(state: BeaconState, epoch: Epoch) -> Shard:
|
||||||
slot: Slot) -> List[Tuple[List[ValidatorIndex], Shard]]:
|
if epoch == get_current_epoch(state):
|
||||||
"""
|
return state.latest_start_shard
|
||||||
Return the list of ``(committee, shard)`` tuples for the ``slot``.
|
elif epoch == get_previous_epoch(state):
|
||||||
"""
|
previous_shard_delta = get_shard_delta(state, epoch)
|
||||||
epoch = slot_to_epoch(slot)
|
return (state.latest_start_shard - previous_shard_delta) % SHARD_COUNT
|
||||||
current_epoch = get_current_epoch(state)
|
elif epoch == get_current_epoch(state) + 1:
|
||||||
previous_epoch = get_previous_epoch(state)
|
current_shard_delta = get_shard_delta(state, get_current_epoch(state))
|
||||||
next_epoch = current_epoch + 1
|
return (state.latest_start_shard + current_shard_delta) % SHARD_COUNT
|
||||||
|
else:
|
||||||
|
raise Exception("Not supported")
|
||||||
|
```
|
||||||
|
|
||||||
assert previous_epoch <= epoch <= next_epoch
|
### `committee_shard_to_slot`
|
||||||
indices = get_active_validator_indices(state, epoch)
|
|
||||||
|
|
||||||
if epoch == current_epoch:
|
```python
|
||||||
start_shard = state.latest_start_shard
|
def committee_shard_to_slot(state: BeaconState, epoch: Epoch, shard: Shard) -> Slot:
|
||||||
elif epoch == previous_epoch:
|
start_shard = get_epoch_start_shard(state, epoch)
|
||||||
previous_shard_delta = get_shard_delta(state, previous_epoch)
|
committees_per_slot = get_epoch_committee_count(state, epoch) // SLOTS_PER_EPOCH
|
||||||
start_shard = (state.latest_start_shard - previous_shard_delta) % SHARD_COUNT
|
offset = (shard - get_epoch_start_slot(epoch)) % SHARD_COUNT
|
||||||
elif epoch == next_epoch:
|
return get_epoch_start_slot(epoch) + offset // committees_per_slot
|
||||||
current_shard_delta = get_shard_delta(state, current_epoch)
|
|
||||||
start_shard = (state.latest_start_shard + current_shard_delta) % SHARD_COUNT
|
|
||||||
|
|
||||||
committees_per_epoch = get_epoch_committee_count(state, epoch)
|
|
||||||
committees_per_slot = committees_per_epoch // SLOTS_PER_EPOCH
|
|
||||||
offset = slot % SLOTS_PER_EPOCH
|
|
||||||
slot_start_shard = (start_shard + committees_per_slot * offset) % SHARD_COUNT
|
|
||||||
seed = generate_seed(state, epoch)
|
|
||||||
|
|
||||||
return [
|
|
||||||
(
|
|
||||||
compute_committee(indices, seed, committees_per_slot * offset + i, committees_per_epoch),
|
|
||||||
(slot_start_shard + i) % SHARD_COUNT,
|
|
||||||
)
|
|
||||||
for i in range(committees_per_slot)
|
|
||||||
]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### `get_block_root_at_slot`
|
### `get_block_root_at_slot`
|
||||||
|
@ -927,7 +916,9 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
|
||||||
Return the beacon proposer index at ``state.slot``.
|
Return the beacon proposer index at ``state.slot``.
|
||||||
"""
|
"""
|
||||||
current_epoch = get_current_epoch(state)
|
current_epoch = get_current_epoch(state)
|
||||||
first_committee, _ = get_crosslink_committees_at_slot(state, state.slot)[0]
|
committees_per_slot = get_epoch_committee_count(state, current_epoch) // SLOTS_PER_EPOCH
|
||||||
|
offset = committees_per_slot * (state.slot % EPOCH_LENGTH)
|
||||||
|
first_committee = get_crosslink_committee(state, epoch, offset)
|
||||||
MAX_RANDOM_BYTE = 2**8 - 1
|
MAX_RANDOM_BYTE = 2**8 - 1
|
||||||
i = 0
|
i = 0
|
||||||
while True:
|
while True:
|
||||||
|
@ -956,6 +947,18 @@ def verify_merkle_branch(leaf: Bytes32, proof: List[Bytes32], depth: int, index:
|
||||||
return value == root
|
return value == root
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `get_crosslink_committee`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_crosslink_committee(state: BeaconState, epoch: Epoch, offset: int):
|
||||||
|
return compute_committee(
|
||||||
|
validator_indices=get_active_validator_indices(state, epoch),
|
||||||
|
seed=generate_seed(state, epoch),
|
||||||
|
index=offset,
|
||||||
|
total_committees=get_epoch_committee_count(state, epoch)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
### `get_attesting_indices`
|
### `get_attesting_indices`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -965,10 +968,10 @@ def get_attesting_indices(state: BeaconState,
|
||||||
"""
|
"""
|
||||||
Return the sorted attesting indices corresponding to ``attestation_data`` and ``bitfield``.
|
Return the sorted attesting indices corresponding to ``attestation_data`` and ``bitfield``.
|
||||||
"""
|
"""
|
||||||
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
|
offset = (attestation_data.shard - attestation_data.epoch_start_shard) % SHARD_COUNT
|
||||||
crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
|
committee = get_crosslink_committee(state, attestation_data.epoch, offset)
|
||||||
assert verify_bitfield(bitfield, len(crosslink_committee))
|
assert verify_bitfield(bitfield, len(committee))
|
||||||
return sorted([index for i, index in enumerate(crosslink_committee) if get_bitfield_bit(bitfield, i) == 0b1])
|
return sorted([index for i, index in enumerate(committee) if get_bitfield_bit(bitfield, i) == 0b1])
|
||||||
```
|
```
|
||||||
|
|
||||||
### `int_to_bytes1`, `int_to_bytes2`, ...
|
### `int_to_bytes1`, `int_to_bytes2`, ...
|
||||||
|
@ -1088,7 +1091,7 @@ def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedA
|
||||||
hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)),
|
hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)),
|
||||||
],
|
],
|
||||||
signature=indexed_attestation.signature,
|
signature=indexed_attestation.signature,
|
||||||
domain=get_domain(state, DOMAIN_ATTESTATION, slot_to_epoch(indexed_attestation.data.slot)),
|
domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.epoch),
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1331,7 +1334,7 @@ def get_matching_target_attestations(state: BeaconState, epoch: Epoch) -> List[P
|
||||||
def get_matching_head_attestations(state: BeaconState, epoch: Epoch) -> List[PendingAttestation]:
|
def get_matching_head_attestations(state: BeaconState, epoch: Epoch) -> List[PendingAttestation]:
|
||||||
return [
|
return [
|
||||||
a for a in get_matching_source_attestations(state, epoch)
|
a for a in get_matching_source_attestations(state, epoch)
|
||||||
if a.data.beacon_block_root == get_block_root_at_slot(state, a.data.slot)
|
if a.data.beacon_block_root == get_block_root_at_slot(state, committee_shard_to_slot(state, epoch, a.data.shard))
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1351,7 +1354,7 @@ def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestat
|
||||||
```python
|
```python
|
||||||
def get_crosslink_from_attestation_data(state: BeaconState, data: AttestationData) -> Crosslink:
|
def get_crosslink_from_attestation_data(state: BeaconState, data: AttestationData) -> Crosslink:
|
||||||
return Crosslink(
|
return Crosslink(
|
||||||
epoch=min(slot_to_epoch(data.slot), state.current_crosslinks[data.shard].epoch + MAX_CROSSLINK_EPOCHS),
|
epoch=min(data.epoch, state.current_crosslinks[data.shard].epoch + MAX_CROSSLINK_EPOCHS),
|
||||||
previous_crosslink_root=data.previous_crosslink_root,
|
previous_crosslink_root=data.previous_crosslink_root,
|
||||||
crosslink_data_root=data.crosslink_data_root,
|
crosslink_data_root=data.crosslink_data_root,
|
||||||
)
|
)
|
||||||
|
@ -1444,8 +1447,10 @@ def process_crosslinks(state: BeaconState) -> None:
|
||||||
previous_epoch = get_previous_epoch(state)
|
previous_epoch = get_previous_epoch(state)
|
||||||
next_epoch = get_current_epoch(state) + 1
|
next_epoch = get_current_epoch(state) + 1
|
||||||
for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)):
|
for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)):
|
||||||
epoch = slot_to_epoch(slot)
|
for epoch in (get_previous_epoch(state), get_current_epoch(state)):
|
||||||
for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot):
|
for offset in range(get_epoch_committee_count(state, epoch)):
|
||||||
|
shard = (get_epoch_start_shard(epoch) + offset) % SHARD_COUNT
|
||||||
|
crosslink_committee = get_crosslink_committee(state, epoch, offset)
|
||||||
winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, shard, epoch)
|
winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, shard, epoch)
|
||||||
if 3 * get_total_balance(state, attesting_indices) >= 2 * get_total_balance(state, crosslink_committee):
|
if 3 * get_total_balance(state, attesting_indices) >= 2 * get_total_balance(state, crosslink_committee):
|
||||||
state.current_crosslinks[shard] = winning_crosslink
|
state.current_crosslinks[shard] = winning_crosslink
|
||||||
|
@ -1492,7 +1497,8 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
||||||
for index in get_unslashed_attesting_indices(state, matching_source_attestations):
|
for index in get_unslashed_attesting_indices(state, matching_source_attestations):
|
||||||
earliest_attestation = get_earliest_attestation(state, matching_source_attestations, index)
|
earliest_attestation = get_earliest_attestation(state, matching_source_attestations, index)
|
||||||
rewards[earliest_attestation.proposer_index] += get_base_reward(state, index) // PROPOSER_REWARD_QUOTIENT
|
rewards[earliest_attestation.proposer_index] += get_base_reward(state, index) // PROPOSER_REWARD_QUOTIENT
|
||||||
inclusion_delay = earliest_attestation.inclusion_slot - earliest_attestation.data.slot
|
attestation_slot = committee_shard_to_slot(state, earliest_attestation.data.epoch, earliest_attestation.data.shard)
|
||||||
|
inclusion_delay = earliest_attestation.inclusion_slot - attestation_slot
|
||||||
rewards[index] += get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_delay
|
rewards[index] += get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_delay
|
||||||
|
|
||||||
# Inactivity penalty
|
# Inactivity penalty
|
||||||
|
@ -1511,18 +1517,19 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
||||||
def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
||||||
rewards = [0 for index in range(len(state.validator_registry))]
|
rewards = [0 for index in range(len(state.validator_registry))]
|
||||||
penalties = [0 for index in range(len(state.validator_registry))]
|
penalties = [0 for index in range(len(state.validator_registry))]
|
||||||
for slot in range(get_epoch_start_slot(get_previous_epoch(state)), get_epoch_start_slot(get_current_epoch(state))):
|
epoch = get_previous_epoch(state)
|
||||||
epoch = slot_to_epoch(slot)
|
for offset in range(get_epoch_committee_count(state, epoch)):
|
||||||
for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot):
|
shard = (get_epoch_start_shard(epoch) + offset) % SHARD_COUNT
|
||||||
winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, shard, epoch)
|
crosslink_committee = get_crosslink_committee(state, epoch, offset)
|
||||||
attesting_balance = get_total_balance(state, attesting_indices)
|
winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, shard, epoch)
|
||||||
committee_balance = get_total_balance(state, crosslink_committee)
|
attesting_balance = get_total_balance(state, attesting_indices)
|
||||||
for index in crosslink_committee:
|
committee_balance = get_total_balance(state, crosslink_committee)
|
||||||
base_reward = get_base_reward(state, index)
|
for index in crosslink_committee:
|
||||||
if index in attesting_indices:
|
base_reward = get_base_reward(state, index)
|
||||||
rewards[index] += base_reward * attesting_balance // committee_balance
|
if index in attesting_indices:
|
||||||
else:
|
rewards[index] += base_reward * attesting_balance // committee_balance
|
||||||
penalties[index] += base_reward
|
else:
|
||||||
|
penalties[index] += base_reward
|
||||||
return [rewards, penalties]
|
return [rewards, penalties]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1770,15 +1777,19 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||||
Note that this function mutates ``state``.
|
Note that this function mutates ``state``.
|
||||||
"""
|
"""
|
||||||
data = attestation.data
|
data = attestation.data
|
||||||
|
attestation_slot = committee_shard_to_slot(state, data.epoch, attestation.shard)
|
||||||
min_slot = state.slot - SLOTS_PER_EPOCH if get_current_epoch(state) > GENESIS_EPOCH else GENESIS_SLOT
|
min_slot = state.slot - SLOTS_PER_EPOCH if get_current_epoch(state) > GENESIS_EPOCH else GENESIS_SLOT
|
||||||
assert min_slot <= data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY
|
assert min_slot <= attestation_slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY
|
||||||
|
|
||||||
# Check target epoch, source epoch, source root, and source crosslink
|
# Check target epoch, source epoch, source root, and source crosslink
|
||||||
target_epoch = slot_to_epoch(data.slot)
|
assert (data.epoch, data.source_epoch, data.source_root, data.previous_crosslink_root) in {
|
||||||
assert (target_epoch, data.source_epoch, data.source_root, data.previous_crosslink_root) in {
|
|
||||||
(get_current_epoch(state), state.current_justified_epoch, state.current_justified_root, hash_tree_root(state.current_crosslinks[data.shard])),
|
(get_current_epoch(state), state.current_justified_epoch, state.current_justified_root, hash_tree_root(state.current_crosslinks[data.shard])),
|
||||||
(get_previous_epoch(state), state.previous_justified_epoch, state.previous_justified_root, hash_tree_root(state.previous_crosslinks[data.shard])),
|
(get_previous_epoch(state), state.previous_justified_epoch, state.previous_justified_root, hash_tree_root(state.previous_crosslinks[data.shard])),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Check shard and epoch start shard
|
||||||
|
assert data.epoch_start_shard == get_epoch_start_shard(state, data.epoch)
|
||||||
|
assert (data.shard - data.epoch_start_shard) % SHARD_COUNT < get_epoch_committee_count(state, data.epoch)
|
||||||
|
|
||||||
# Check crosslink data root
|
# Check crosslink data root
|
||||||
assert data.crosslink_data_root == ZERO_HASH # [to be removed in phase 1]
|
assert data.crosslink_data_root == ZERO_HASH # [to be removed in phase 1]
|
||||||
|
|
Loading…
Reference in New Issue