diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index eb4286a35..e5a8687f2 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -310,7 +310,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git ```python { # Validator indices - 'validator_indices': '[uint64]', + 'validator_indices': ['uint64'], # Attestation data 'data': AttestationData, # Custody bitfield @@ -631,6 +631,9 @@ Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethere ```python def slot_to_epoch(slot: SlotNumber) -> EpochNumber: + """ + Return the epoch number of the given ``slot``. + """ return slot // EPOCH_LENGTH ``` @@ -638,6 +641,9 @@ def slot_to_epoch(slot: SlotNumber) -> EpochNumber: ```python def get_current_epoch(state: BeaconState) -> EpochNumber: + """ + Return the current epoch of the given ``state``. + """ return slot_to_epoch(state.slot) ``` @@ -645,6 +651,9 @@ def get_current_epoch(state: BeaconState) -> EpochNumber: ```python def get_epoch_start_slot(epoch: EpochNumber) -> SlotNumber: + """ + Return the starting slot of the given ``epoch``. + """ return epoch * EPOCH_LENGTH ``` @@ -652,7 +661,7 @@ def get_epoch_start_slot(epoch: EpochNumber) -> SlotNumber: ```python def is_active_validator(validator: Validator, epoch: EpochNumber) -> bool: """ - Checks if ``validator`` is active. + Check if ``validator`` is active. """ return validator.activation_epoch <= epoch < validator.exit_epoch ``` @@ -662,7 +671,7 @@ def is_active_validator(validator: Validator, epoch: EpochNumber) -> bool: ```python def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber) -> List[ValidatorIndex]: """ - Gets indices of active validators from ``validators``. + Get indices of active validators from ``validators``. """ return [i for i, v in enumerate(validators) if is_active_validator(v, epoch)] ``` @@ -672,7 +681,7 @@ def get_active_validator_indices(validators: List[Validator], epoch: EpochNumber ```python def shuffle(values: List[Any], seed: Bytes32) -> List[Any]: """ - Returns the shuffled ``values`` with ``seed`` as entropy. + Return the shuffled ``values`` with ``seed`` as entropy. """ values_count = len(values) @@ -738,6 +747,9 @@ def split(values: List[Any], split_count: int) -> List[List[Any]]: ```python def get_epoch_committee_count(active_validator_count: int) -> int: + """ + Return the number of committees in one epoch. + """ return max( 1, min( @@ -754,8 +766,8 @@ def get_shuffling(seed: Bytes32, validators: List[Validator], epoch: EpochNumber) -> List[List[ValidatorIndex]] """ - Shuffles ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``. - Returns a list of ``committees_per_epoch`` committees where each + Shuffle ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``. + Return a list of ``committees_per_epoch`` committees where each committee is itself a list of validator indices. """ @@ -779,6 +791,9 @@ def get_shuffling(seed: Bytes32, ```python def get_previous_epoch_committee_count(state: BeaconState) -> int: + """ + Return the number of committees in the previous epoch of the given ``state``. + """ previous_active_validators = get_active_validator_indices( state.validator_registry, state.previous_calculation_epoch, @@ -790,6 +805,9 @@ def get_previous_epoch_committee_count(state: BeaconState) -> int: ```python def get_current_epoch_committee_count(state: BeaconState) -> int: + """ + Return the number of committees in the current epoch of the given ``state``. + """ current_active_validators = get_active_validator_indices( state.validator_registry, state.current_calculation_epoch, @@ -803,7 +821,7 @@ def get_current_epoch_committee_count(state: BeaconState) -> int: def get_crosslink_committees_at_slot(state: BeaconState, slot: SlotNumber) -> List[Tuple[List[ValidatorIndex], ShardNumber]]: """ - Returns the list of ``(committee, shard)`` tuples for the ``slot``. + Return the list of ``(committee, shard)`` tuples for the ``slot``. """ epoch = slot_to_epoch(slot) current_epoch = get_current_epoch(state) @@ -849,7 +867,7 @@ def get_crosslink_committees_at_slot(state: BeaconState, def get_block_root(state: BeaconState, slot: SlotNumber) -> Bytes32: """ - Returns the block root at a recent ``slot``. + Return the block root at a recent ``slot``. """ assert state.slot <= slot + LATEST_BLOCK_ROOTS_LENGTH assert slot < state.slot @@ -864,7 +882,7 @@ def get_block_root(state: BeaconState, def get_randao_mix(state: BeaconState, epoch: EpochNumber) -> Bytes32: """ - Returns the randao mix at a recent ``epoch``. + Return the randao mix at a recent ``epoch``. """ assert get_current_epoch(state) - LATEST_RANDAO_MIXES_LENGTH < epoch <= get_current_epoch(state) return state.latest_randao_mixes[epoch % LATEST_RANDAO_MIXES_LENGTH] @@ -876,7 +894,7 @@ def get_randao_mix(state: BeaconState, def get_active_index_root(state: BeaconState, epoch: EpochNumber) -> Bytes32: """ - Returns the index root at a recent ``epoch``. + Return the index root at a recent ``epoch``. """ assert get_current_epoch(state) - LATEST_INDEX_ROOTS_LENGTH < epoch <= get_current_epoch(state) return state.latest_index_roots[epoch % LATEST_INDEX_ROOTS_LENGTH] @@ -903,7 +921,7 @@ def generate_seed(state: BeaconState, def get_beacon_proposer_index(state: BeaconState, slot: SlotNumber) -> ValidatorIndex: """ - Returns the beacon proposer index for the ``slot``. + Return the beacon proposer index for the ``slot``. """ first_committee, _ = get_crosslink_committees_at_slot(state, slot)[0] return first_committee[slot % len(first_committee)] @@ -929,7 +947,7 @@ def get_attestation_participants(state: BeaconState, attestation_data: AttestationData, bitfield: bytes) -> List[ValidatorIndex]: """ - Returns the participant indices at for the ``attestation_data`` and ``bitfield``. + Return the participant indices at for the ``attestation_data`` and ``bitfield``. """ # Find the committee in the list with the desired shard crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot) @@ -957,7 +975,7 @@ def get_attestation_participants(state: BeaconState, ```python def get_effective_balance(state: State, index: ValidatorIndex) -> Gwei: """ - Returns the effective balance (also known as "balance at stake") for a ``validator`` with the given ``index``. + Return the effective balance (also known as "balance at stake") for a ``validator`` with the given ``index``. """ return min(state.validator_balances[index], MAX_DEPOSIT_AMOUNT) ``` @@ -967,6 +985,9 @@ def get_effective_balance(state: State, index: ValidatorIndex) -> Gwei: ```python def get_fork_version(fork: Fork, epoch: EpochNumber) -> int: + """ + Return the fork version of the given ``epoch``. + """ if epoch < fork.epoch: return fork.previous_version else: @@ -979,10 +1000,11 @@ def get_fork_version(fork: Fork, def get_domain(fork: Fork, epoch: EpochNumber, domain_type: int) -> int: - return get_fork_version( - fork, - epoch, - ) * 2**32 + domain_type + """ + Get the domain number that represents the fork meta and signature domain. + """ + fork_version = get_fork_version(fork, epoch) + return fork_version * 2**32 + domain_type ``` ### `get_bitfield_bit` @@ -1049,8 +1071,8 @@ def verify_slashable_attestation(state: BeaconState, slashable_attestation: Slas bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_indices]), ], messages=[ - hash_tree_root(AttestationDataAndCustodyBit(attestation_data=slashable_attestation.data, custody_bit=0b0)), - hash_tree_root(AttestationDataAndCustodyBit(attestation_data=slashable_attestation.data, custody_bit=0b1)), + hash_tree_root(AttestationDataAndCustodyBit(data=slashable_attestation.data, custody_bit=0b0)), + hash_tree_root(AttestationDataAndCustodyBit(data=slashable_attestation.data, custody_bit=0b1)), ], signature=slashable_attestation.aggregate_signature, domain=get_domain( @@ -1065,9 +1087,9 @@ def verify_slashable_attestation(state: BeaconState, slashable_attestation: Slas ```python def is_double_vote(attestation_data_1: AttestationData, - attestation_data_2: AttestationData) -> bool + attestation_data_2: AttestationData) -> bool: """ - Checks if the two ``AttestationData`` have the same target. + Check if ``attestation_data_1`` and ``attestation_data_2`` have the same target. """ target_epoch_1 = slot_to_epoch(attestation_data_1.slot) target_epoch_2 = slot_to_epoch(attestation_data_2.slot) @@ -1080,7 +1102,7 @@ def is_double_vote(attestation_data_1: AttestationData, def is_surround_vote(attestation_data_1: AttestationData, attestation_data_2: AttestationData) -> bool: """ - Checks if ``attestation_data_1`` surrounds ``attestation_data_2``. + Check if ``attestation_data_1`` surrounds ``attestation_data_2``. """ source_epoch_1 = attestation_data_1.justified_epoch source_epoch_2 = attestation_data_2.justified_epoch @@ -1136,6 +1158,9 @@ def validate_proof_of_possession(state: BeaconState, pubkey: BLSPubkey, proof_of_possession: BLSSignature, withdrawal_credentials: Bytes32) -> bool: + """ + Verify the given ``proof_of_possession``. + """ proof_of_possession_data = DepositInput( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, @@ -1208,16 +1233,24 @@ Note: All functions in this section mutate `state`. #### `activate_validator` ```python -def activate_validator(state: BeaconState, index: ValidatorIndex, genesis: bool) -> None: +def activate_validator(state: BeaconState, index: ValidatorIndex, is_genesis: bool) -> None: + """ + Activate the validator of the given ``index``. + Note that this function mutates ``state``. + """ validator = state.validator_registry[index] - validator.activation_epoch = GENESIS_EPOCH if genesis else get_entry_exit_effect_epoch(get_current_epoch(state)) + validator.activation_epoch = GENESIS_EPOCH if is_genesis else get_entry_exit_effect_epoch(get_current_epoch(state)) ``` #### `initiate_validator_exit` ```python def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: + """ + Initiate the validator of the given ``index``. + Note that this function mutates ``state``. + """ validator = state.validator_registry[index] validator.status_flags |= INITIATED_EXIT ``` @@ -1226,6 +1259,10 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: ```python def exit_validator(state: BeaconState, index: ValidatorIndex) -> None: + """ + Exit the validator of the given ``index``. + Note that this function mutates ``state``. + """ validator = state.validator_registry[index] # The following updates only occur if not previous exited @@ -1239,6 +1276,10 @@ def exit_validator(state: BeaconState, index: ValidatorIndex) -> None: ```python def penalize_validator(state: BeaconState, index: ValidatorIndex) -> None: + """ + Penalize the validator of the given ``index``. + Note that this function mutates ``state``. + """ exit_validator(state, index) validator = state.validator_registry[index] state.latest_penalized_balances[get_current_epoch(state) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) @@ -1254,6 +1295,10 @@ def penalize_validator(state: BeaconState, index: ValidatorIndex) -> None: ```python def prepare_validator_for_withdrawal(state: BeaconState, index: ValidatorIndex) -> None: + """ + Set the validator with the given ``index`` with ``WITHDRAWABLE`` flag. + Note that this function mutates ``state``. + """ validator = state.validator_registry[index] validator.status_flags |= WITHDRAWABLE ``` @@ -1382,6 +1427,9 @@ A valid block with slot `GENESIS_SLOT` (a "genesis block") has the following val def get_initial_beacon_state(initial_validator_deposits: List[Deposit], genesis_time: int, latest_eth1_data: Eth1Data) -> BeaconState: + """ + Get the initial ``BeaconState``. + """ state = BeaconState( # Misc slot=GENESIS_SLOT, @@ -1438,7 +1486,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Process initial activations for validator_index, _ in enumerate(state.validator_registry): if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT: - activate_validator(state, validator_index, True) + activate_validator(state, validator_index, is_genesis=True) state.latest_index_roots[GENESIS_EPOCH % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state.validator_registry, GENESIS_EPOCH)) state.current_epoch_seed = generate_seed(state, GENESIS_EPOCH) @@ -1477,6 +1525,9 @@ The beacon chain fork choice rule is a hybrid that combines justification and fi ```python def get_ancestor(store: Store, block: BeaconBlock, slot: SlotNumber) -> BeaconBlock: + """ + Get the ancestor of ``block`` with slot number ``slot``; return ``None`` if not found. + """ if block.slot == slot: return block elif block.slot < slot: @@ -1493,6 +1544,9 @@ def get_ancestor(store: Store, block: BeaconBlock, slot: SlotNumber) -> BeaconBl ```python def lmd_ghost(store: Store, start_state: BeaconState, start_block: BeaconBlock) -> BeaconBlock: + """ + Execute the LMD-GHOST algorithm to find the head ``BeaconBlock``. + """ validators = start_state.validator_registry active_validators = [ validators[i] @@ -1657,6 +1711,9 @@ For each `deposit` in `block.body.deposits`: ```python def verify_merkle_branch(leaf: Bytes32, branch: List[Bytes32], depth: int, index: int, root: Bytes32) -> bool: + """ + Verify that the given ``leaf`` is on the merkle branch ``branch``. + """ value = leaf for i in range(depth): if index // (2**i) % 2: @@ -1887,7 +1944,7 @@ def update_validator_registry(state: BeaconState) -> None: break # Activate validator - activate_validator(state, index, False) + activate_validator(state, index, is_genesis=False) # Exit validators within the allowable balance churn balance_churn = 0 @@ -1924,17 +1981,21 @@ Regardless of whether or not a validator set change happens, run the following: ```python def process_penalties_and_exits(state: BeaconState) -> None: + """ + Process the penalties and prepare the validators who are eligible to withdrawal. + Note that this function mutates ``state``. + """ current_epoch = get_current_epoch(state) # The active validators active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch) # The total effective balance of active validators - total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) + total_balance = sum(get_effective_balance(state, i) for i in active_validator_indices) for index, validator in enumerate(state.validator_registry): if current_epoch == validator.penalized_epoch + LATEST_PENALIZED_EXIT_LENGTH // 2: - e = current_epoch % LATEST_PENALIZED_EXIT_LENGTH - total_at_start = state.latest_penalized_balances[(e + 1) % LATEST_PENALIZED_EXIT_LENGTH] - total_at_end = state.latest_penalized_balances[e] + epoch_index = current_epoch % LATEST_PENALIZED_EXIT_LENGTH + total_at_start = state.latest_penalized_balances[(epoch_index + 1) % LATEST_PENALIZED_EXIT_LENGTH] + total_at_end = state.latest_penalized_balances[epoch_index] total_penalties = total_at_end - total_at_start penalty = get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance state.validator_balances[index] -= penalty