bitvector[8] -> uint8, for efficient packing in flags merkle tree
This commit is contained in:
parent
1ba4917119
commit
3677073812
|
@ -55,15 +55,26 @@ This is a patch implementing the first hard fork to the beacon chain, tentativel
|
|||
and [TODO] reducing the cost of processing chains that have very little or zero participation for a long span of epochs
|
||||
* Fork choice rule changes to address weaknesses recently discovered in the existing fork choice
|
||||
|
||||
## Custom types
|
||||
|
||||
| Name | SSZ equivalent | Description |
|
||||
| - | - | - |
|
||||
| `ValidatorFlags` | `uint8` | Bitflags to track validator actions with |
|
||||
|
||||
## Constants
|
||||
|
||||
### Participation flags
|
||||
### Validator action flags
|
||||
|
||||
This is formatted as an enum, with values `2**i` that can be combined as bit-flags.
|
||||
The `0` value is reserved as default. Remaining bits in `ValidatorFlags` may be used in future hardforks.
|
||||
|
||||
**Note**: unlike Phase0, a `TIMELY_TARGET_FLAG` does not imply a `TIMELY_SOURCE_FLAG`.
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `TIMELY_HEAD_FLAG` | `0` |
|
||||
| `TIMELY_SOURCE_FLAG` | `1` |
|
||||
| `TIMELY_TARGET_FLAG` | `2` |
|
||||
| `TIMELY_HEAD_FLAG` | `ValidatorFlags(2**0)` (= 1) |
|
||||
| `TIMELY_SOURCE_FLAG` | `ValidatorFlags(2**1)` (= 2) |
|
||||
| `TIMELY_TARGET_FLAG` | `ValidatorFlags(2**2)` (= 4) |
|
||||
|
||||
### Participation rewards
|
||||
|
||||
|
@ -80,7 +91,6 @@ The reward fractions add up to 7/8, leaving the remaining 1/8 for proposer rewar
|
|||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `PARTICIPATION_FLAGS_LENGTH` | `8` |
|
||||
| `G2_POINT_AT_INFINITY` | `BLSSignature(b'\xc0' + b'\x00' * 95)` |
|
||||
|
||||
## Configuration
|
||||
|
@ -146,8 +156,8 @@ class BeaconState(Container):
|
|||
# Slashings
|
||||
slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances
|
||||
# Participation
|
||||
previous_epoch_participation: List[Bitvector[PARTICIPATION_FLAGS_LENGTH], VALIDATOR_REGISTRY_LIMIT]
|
||||
current_epoch_participation: List[Bitvector[PARTICIPATION_FLAGS_LENGTH], VALIDATOR_REGISTRY_LIMIT]
|
||||
previous_epoch_participation: List[ValidatorFlags, VALIDATOR_REGISTRY_LIMIT]
|
||||
current_epoch_participation: List[ValidatorFlags, VALIDATOR_REGISTRY_LIMIT]
|
||||
# Finality
|
||||
justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch
|
||||
previous_justified_checkpoint: Checkpoint
|
||||
|
@ -197,7 +207,15 @@ def get_flags_and_numerators() -> Sequence[Tuple[int, int]]:
|
|||
)
|
||||
```
|
||||
|
||||
```python
|
||||
def add_flags(flags: ValidatorFlags, add: ValidatorFlags) -> ValidatorFlags:
|
||||
return flags | add
|
||||
```
|
||||
|
||||
```python
|
||||
def has_flags(flags: ValidatorFlags, has: ValidatorFlags) -> bool:
|
||||
return flags & has == has
|
||||
```
|
||||
|
||||
### Beacon state accessors
|
||||
|
||||
|
@ -257,7 +275,10 @@ def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
|||
#### `get_unslashed_participating_indices`
|
||||
|
||||
```python
|
||||
def get_unslashed_participating_indices(state: BeaconState, flag: uint8, epoch: Epoch) -> Set[ValidatorIndex]:
|
||||
def get_unslashed_participating_indices(state: BeaconState, flags: ValidatorFlags, epoch: Epoch) -> Set[ValidatorIndex]:
|
||||
"""
|
||||
Retrieves the active validator indices of the given epoch, who are not slashed, and have all of the given flags.
|
||||
"""
|
||||
assert epoch in (get_previous_epoch(state), get_current_epoch(state))
|
||||
if epoch == get_current_epoch(state):
|
||||
epoch_participation = state.current_epoch_participation
|
||||
|
@ -265,7 +286,7 @@ def get_unslashed_participating_indices(state: BeaconState, flag: uint8, epoch:
|
|||
epoch_participation = state.previous_epoch_participation
|
||||
participating_indices = [
|
||||
index for index in get_active_validator_indices(state, epoch)
|
||||
if epoch_participation[index][flag]
|
||||
if has_flags(epoch_participation[index], flags)
|
||||
]
|
||||
return set(filter(lambda index: not state.validators[index].slashed, participating_indices))
|
||||
```
|
||||
|
@ -273,7 +294,9 @@ def get_unslashed_participating_indices(state: BeaconState, flag: uint8, epoch:
|
|||
#### `get_flag_deltas`
|
||||
|
||||
```python
|
||||
def get_flag_deltas(state: BeaconState, flag: uint8, numerator: uint64) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
||||
def get_flag_deltas(state: BeaconState,
|
||||
flag: ValidatorFlags,
|
||||
numerator: uint64) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
||||
"""
|
||||
Computes the rewards and penalties associated with a particular duty, by scanning through the participation
|
||||
flags to determine who participated and who did not and assigning them the appropriate rewards and penalties.
|
||||
|
@ -385,8 +408,8 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
|||
proposer_reward_numerator = 0
|
||||
for index in get_attesting_indices(state, data, attestation.aggregation_bits):
|
||||
for flag, numerator in get_flags_and_numerators():
|
||||
if flag in participation_flags and not epoch_participation[index][flag]:
|
||||
epoch_participation[index][flag] = True
|
||||
if flag in participation_flags and not has_flags(epoch_participation[index], flag):
|
||||
epoch_participation[index] = add_flags(epoch_participation[index], flag)
|
||||
proposer_reward_numerator += get_base_reward(state, index) * numerator
|
||||
|
||||
# Reward proposer
|
||||
|
@ -432,8 +455,8 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
|||
state.validators.append(get_validator_from_deposit(state, deposit))
|
||||
state.balances.append(amount)
|
||||
# [Added in hf-1] Initialize empty participation flags for new validator
|
||||
state.previous_epoch_participation.append(Bitvector[PARTICIPATION_FLAGS_LENGTH]())
|
||||
state.current_epoch_participation.append(Bitvector[PARTICIPATION_FLAGS_LENGTH]())
|
||||
state.previous_epoch_participation.append(ValidatorFlags(0))
|
||||
state.current_epoch_participation.append(ValidatorFlags(0))
|
||||
else:
|
||||
# Increase balance by deposit amount
|
||||
index = ValidatorIndex(validator_pubkeys.index(pubkey))
|
||||
|
@ -572,5 +595,5 @@ def process_participation_flag_updates(state: BeaconState) -> None:
|
|||
Call to ``process_participation_flag_updates`` added to ``process_epoch`` in HF1
|
||||
"""
|
||||
state.previous_epoch_participation = state.current_epoch_participation
|
||||
state.current_epoch_participation = [Bitvector[PARTICIPATION_FLAGS_LENGTH]() for _ in range(len(state.validators))]
|
||||
state.current_epoch_participation = [ValidatorFlags(0) for _ in range(len(state.validators))]
|
||||
```
|
||||
|
|
|
@ -67,8 +67,8 @@ def upgrade_to_lightclient_patch(pre: phase0.BeaconState) -> BeaconState:
|
|||
# Slashings
|
||||
slashings=pre.slashings,
|
||||
# Attestations
|
||||
previous_epoch_participation=[Bitvector[PARTICIPATION_FLAGS_LENGTH]() for _ in range(len(pre.validators))],
|
||||
current_epoch_participation=[Bitvector[PARTICIPATION_FLAGS_LENGTH]() for _ in range(len(pre.validators))],
|
||||
previous_epoch_participation=[ValidatorFlags(0) for _ in range(len(pre.validators))],
|
||||
current_epoch_participation=[ValidatorFlags(0) for _ in range(len(pre.validators))],
|
||||
# Finality
|
||||
justification_bits=pre.justification_bits,
|
||||
previous_justified_checkpoint=pre.previous_justified_checkpoint,
|
||||
|
|
|
@ -6,7 +6,7 @@ from eth2spec.test.context import is_post_lightclient_patch
|
|||
from eth2spec.test.helpers.attestations import cached_prepare_state_with_attestations
|
||||
from eth2spec.test.helpers.deposits import mock_deposit
|
||||
from eth2spec.test.helpers.state import next_epoch
|
||||
from eth2spec.utils.ssz.ssz_typing import Container, uint64, List, Bitvector
|
||||
from eth2spec.utils.ssz.ssz_typing import Container, uint64, List
|
||||
|
||||
|
||||
class Deltas(Container):
|
||||
|
@ -314,7 +314,7 @@ def run_test_full_but_partial_participation(spec, state, rng=Random(5522)):
|
|||
else:
|
||||
for index in range(len(state.validators)):
|
||||
if rng.choice([True, False]):
|
||||
state.previous_epoch_participation[index] = Bitvector[spec.PARTICIPATION_FLAGS_LENGTH]()
|
||||
state.previous_epoch_participation[index] = spec.ValidatorFlags(0)
|
||||
|
||||
yield from run_deltas(spec, state)
|
||||
|
||||
|
@ -328,7 +328,7 @@ def run_test_partial(spec, state, fraction_filled):
|
|||
state.previous_epoch_attestations = state.previous_epoch_attestations[:num_attestations]
|
||||
else:
|
||||
for index in range(int(len(state.validators) * fraction_filled)):
|
||||
state.previous_epoch_participation[index] = Bitvector[spec.PARTICIPATION_FLAGS_LENGTH]()
|
||||
state.previous_epoch_participation[index] = spec.ValidatorFlags(0)
|
||||
|
||||
yield from run_deltas(spec, state)
|
||||
|
||||
|
@ -394,7 +394,7 @@ def run_test_some_very_low_effective_balances_that_did_not_attest(spec, state):
|
|||
else:
|
||||
index = 0
|
||||
state.validators[index].effective_balance = 1
|
||||
state.previous_epoch_participation[index] = Bitvector[spec.PARTICIPATION_FLAGS_LENGTH]()
|
||||
state.previous_epoch_participation[index] = spec.ValidatorFlags(0)
|
||||
|
||||
yield from run_deltas(spec, state)
|
||||
|
||||
|
@ -519,16 +519,25 @@ def run_test_full_random(spec, state, rng=Random(8020)):
|
|||
for index in range(len(state.validators)):
|
||||
# ~1/3 have bad head or bad target or not timely enough
|
||||
is_timely_correct_head = rng.randint(0, 2) != 0
|
||||
state.previous_epoch_participation[index][spec.TIMELY_HEAD_FLAG] = is_timely_correct_head
|
||||
flags = state.previous_epoch_participation[index]
|
||||
|
||||
def set_flag(f, v):
|
||||
nonlocal flags
|
||||
if v:
|
||||
flags |= f
|
||||
else:
|
||||
flags &= 0xff ^ f
|
||||
|
||||
set_flag(spec.TIMELY_HEAD_FLAG, is_timely_correct_head)
|
||||
if is_timely_correct_head:
|
||||
# If timely head, then must be timely target
|
||||
state.previous_epoch_participation[index][spec.TIMELY_TARGET_FLAG] = True
|
||||
set_flag(spec.TIMELY_TARGET_FLAG, True)
|
||||
# If timely head, then must be timely source
|
||||
state.previous_epoch_participation[index][spec.TIMELY_SOURCE_FLAG] = True
|
||||
set_flag(spec.TIMELY_SOURCE_FLAG, True)
|
||||
else:
|
||||
# ~50% of remaining have bad target or not timely enough
|
||||
state.previous_epoch_participation[index][spec.TIMELY_TARGET_FLAG] = rng.choice([True, False])
|
||||
set_flag(spec.TIMELY_TARGET_FLAG, rng.choice([True, False]))
|
||||
# ~50% of remaining have bad source or not timely enough
|
||||
state.previous_epoch_participation[index][spec.TIMELY_SOURCE_FLAG] = rng.choice([True, False])
|
||||
|
||||
set_flag(spec.TIMELY_SOURCE_FLAG, rng.choice([True, False]))
|
||||
state.previous_epoch_participation[index] = flags
|
||||
yield from run_deltas(spec, state)
|
||||
|
|
|
@ -78,10 +78,10 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support
|
|||
else:
|
||||
for i, index in enumerate(committee):
|
||||
if aggregation_bits[i]:
|
||||
epoch_participation[index][spec.TIMELY_HEAD_FLAG] = True
|
||||
epoch_participation[index][spec.TIMELY_SOURCE_FLAG] = True
|
||||
epoch_participation[index] |= spec.TIMELY_HEAD_FLAG
|
||||
epoch_participation[index] |= spec.TIMELY_SOURCE_FLAG
|
||||
if not messed_up_target:
|
||||
epoch_participation[index][spec.TIMELY_TARGET_FLAG] = True
|
||||
epoch_participation[index] |= spec.TIMELY_TARGET_FLAG
|
||||
|
||||
|
||||
def get_checkpoints(spec, epoch):
|
||||
|
|
|
@ -806,7 +806,7 @@ def test_attestation(spec, state):
|
|||
assert spec.hash_tree_root(state.previous_epoch_attestations) == pre_current_attestations_root
|
||||
else:
|
||||
for index in range(len(state.validators)):
|
||||
assert state.current_epoch_participation[index] == spec.Bitvector[spec.PARTICIPATION_FLAGS_LENGTH]()
|
||||
assert state.current_epoch_participation[index] == 0
|
||||
assert spec.hash_tree_root(state.previous_epoch_participation) == pre_current_epoch_participation_root
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue