mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 07:03:58 -05:00
8577 lines
357 KiB
YAML
8577 lines
357 KiB
YAML
- name: add_flag
|
|
sources:
|
|
- file: beacon-chain/core/altair/attestation.go
|
|
search: func AddValidatorFlag(
|
|
spec: |
|
|
<spec fn="add_flag" fork="altair" hash="0e22f6e5">
|
|
def add_flag(flags: ParticipationFlags, flag_index: int) -> ParticipationFlags:
|
|
"""
|
|
Return a new ``ParticipationFlags`` adding ``flag_index`` to ``flags``.
|
|
"""
|
|
flag = ParticipationFlags(2**flag_index)
|
|
return flags | flag
|
|
</spec>
|
|
|
|
- name: add_validator_to_registry#phase0
|
|
sources:
|
|
- file: beacon-chain/core/altair/deposit.go
|
|
search: func AddValidatorToRegistry(
|
|
spec: |
|
|
<spec fn="add_validator_to_registry" fork="phase0" hash="53699dc0">
|
|
def add_validator_to_registry(
|
|
state: BeaconState, pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64
|
|
) -> None:
|
|
state.validators.append(get_validator_from_deposit(pubkey, withdrawal_credentials, amount))
|
|
state.balances.append(amount)
|
|
</spec>
|
|
|
|
- name: add_validator_to_registry#altair
|
|
sources:
|
|
- file: beacon-chain/core/altair/deposit.go
|
|
search: func AddValidatorToRegistry(
|
|
spec: |
|
|
<spec fn="add_validator_to_registry" fork="altair" hash="18054f42">
|
|
def add_validator_to_registry(
|
|
state: BeaconState, pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64
|
|
) -> None:
|
|
index = get_index_for_new_validator(state)
|
|
validator = get_validator_from_deposit(pubkey, withdrawal_credentials, amount)
|
|
set_or_append_list(state.validators, index, validator)
|
|
set_or_append_list(state.balances, index, amount)
|
|
# [New in Altair]
|
|
set_or_append_list(state.previous_epoch_participation, index, ParticipationFlags(0b0000_0000))
|
|
set_or_append_list(state.current_epoch_participation, index, ParticipationFlags(0b0000_0000))
|
|
set_or_append_list(state.inactivity_scores, index, uint64(0))
|
|
</spec>
|
|
|
|
- name: add_validator_to_registry#electra
|
|
sources:
|
|
- file: beacon-chain/core/electra/deposits.go
|
|
search: func AddValidatorToRegistry(
|
|
spec: |
|
|
<spec fn="add_validator_to_registry" fork="electra" hash="7970ce24">
|
|
def add_validator_to_registry(
|
|
state: BeaconState, pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64
|
|
) -> None:
|
|
index = get_index_for_new_validator(state)
|
|
# [Modified in Electra:EIP7251]
|
|
validator = get_validator_from_deposit(pubkey, withdrawal_credentials, amount)
|
|
set_or_append_list(state.validators, index, validator)
|
|
set_or_append_list(state.balances, index, amount)
|
|
set_or_append_list(state.previous_epoch_participation, index, ParticipationFlags(0b0000_0000))
|
|
set_or_append_list(state.current_epoch_participation, index, ParticipationFlags(0b0000_0000))
|
|
set_or_append_list(state.inactivity_scores, index, uint64(0))
|
|
</spec>
|
|
|
|
- name: apply_deposit#phase0
|
|
sources:
|
|
- file: beacon-chain/core/altair/deposit.go
|
|
search: func ApplyDeposit(
|
|
spec: |
|
|
<spec fn="apply_deposit" fork="phase0" hash="a53ef920">
|
|
def apply_deposit(
|
|
state: BeaconState,
|
|
pubkey: BLSPubkey,
|
|
withdrawal_credentials: Bytes32,
|
|
amount: uint64,
|
|
signature: BLSSignature,
|
|
) -> None:
|
|
validator_pubkeys = [v.pubkey for v in state.validators]
|
|
if pubkey not in validator_pubkeys:
|
|
# Verify the deposit signature (proof of possession) which is not checked by the deposit contract
|
|
deposit_message = DepositMessage(
|
|
pubkey=pubkey,
|
|
withdrawal_credentials=withdrawal_credentials,
|
|
amount=amount,
|
|
)
|
|
# Fork-agnostic domain since deposits are valid across forks
|
|
domain = compute_domain(DOMAIN_DEPOSIT)
|
|
signing_root = compute_signing_root(deposit_message, domain)
|
|
if bls.Verify(pubkey, signing_root, signature):
|
|
add_validator_to_registry(state, pubkey, withdrawal_credentials, amount)
|
|
else:
|
|
# Increase balance by deposit amount
|
|
index = ValidatorIndex(validator_pubkeys.index(pubkey))
|
|
increase_balance(state, index, amount)
|
|
</spec>
|
|
|
|
- name: apply_deposit#electra
|
|
sources:
|
|
- file: beacon-chain/core/electra/deposits.go
|
|
search: func ApplyDeposit(
|
|
spec: |
|
|
<spec fn="apply_deposit" fork="electra" hash="6259bfc5">
|
|
def apply_deposit(
|
|
state: BeaconState,
|
|
pubkey: BLSPubkey,
|
|
withdrawal_credentials: Bytes32,
|
|
amount: uint64,
|
|
signature: BLSSignature,
|
|
) -> None:
|
|
validator_pubkeys = [v.pubkey for v in state.validators]
|
|
if pubkey not in validator_pubkeys:
|
|
# Verify the deposit signature (proof of possession) which is not checked by the deposit contract
|
|
if is_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature):
|
|
# [Modified in Electra:EIP7251]
|
|
add_validator_to_registry(state, pubkey, withdrawal_credentials, Gwei(0))
|
|
else:
|
|
return
|
|
|
|
# [Modified in Electra:EIP7251]
|
|
# Increase balance by deposit amount
|
|
state.pending_deposits.append(
|
|
PendingDeposit(
|
|
pubkey=pubkey,
|
|
withdrawal_credentials=withdrawal_credentials,
|
|
amount=amount,
|
|
signature=signature,
|
|
slot=GENESIS_SLOT, # Use GENESIS_SLOT to distinguish from a pending deposit request
|
|
)
|
|
)
|
|
</spec>
|
|
|
|
- name: apply_light_client_update
|
|
sources:
|
|
- file: beacon-chain/light-client/lightclient.go
|
|
search: func NewLightClientUpdateFromBeaconState(
|
|
spec: |
|
|
<spec fn="apply_light_client_update" fork="altair" hash="211a52c9">
|
|
def apply_light_client_update(store: LightClientStore, update: LightClientUpdate) -> None:
|
|
store_period = compute_sync_committee_period_at_slot(store.finalized_header.beacon.slot)
|
|
update_finalized_period = compute_sync_committee_period_at_slot(
|
|
update.finalized_header.beacon.slot
|
|
)
|
|
if not is_next_sync_committee_known(store):
|
|
assert update_finalized_period == store_period
|
|
store.next_sync_committee = update.next_sync_committee
|
|
elif update_finalized_period == store_period + 1:
|
|
store.current_sync_committee = store.next_sync_committee
|
|
store.next_sync_committee = update.next_sync_committee
|
|
store.previous_max_active_participants = store.current_max_active_participants
|
|
store.current_max_active_participants = 0
|
|
if update.finalized_header.beacon.slot > store.finalized_header.beacon.slot:
|
|
store.finalized_header = update.finalized_header
|
|
if store.finalized_header.beacon.slot > store.optimistic_header.beacon.slot:
|
|
store.optimistic_header = store.finalized_header
|
|
</spec>
|
|
|
|
- name: apply_pending_deposit
|
|
sources:
|
|
- file: beacon-chain/core/electra/deposits.go
|
|
search: func ApplyPendingDeposit(
|
|
spec: |
|
|
<spec fn="apply_pending_deposit" fork="electra" hash="e60b5fed">
|
|
def apply_pending_deposit(state: BeaconState, deposit: PendingDeposit) -> None:
|
|
"""
|
|
Applies ``deposit`` to the ``state``.
|
|
"""
|
|
validator_pubkeys = [v.pubkey for v in state.validators]
|
|
if deposit.pubkey not in validator_pubkeys:
|
|
# Verify the deposit signature (proof of possession) which is not checked by the deposit contract
|
|
if is_valid_deposit_signature(
|
|
deposit.pubkey, deposit.withdrawal_credentials, deposit.amount, deposit.signature
|
|
):
|
|
add_validator_to_registry(
|
|
state, deposit.pubkey, deposit.withdrawal_credentials, deposit.amount
|
|
)
|
|
else:
|
|
validator_index = ValidatorIndex(validator_pubkeys.index(deposit.pubkey))
|
|
increase_balance(state, validator_index, deposit.amount)
|
|
</spec>
|
|
|
|
- name: block_to_light_client_header#altair
|
|
sources:
|
|
- file: beacon-chain/light-client/lightclient.go
|
|
search: func BlockToLightClientHeader(
|
|
spec: |
|
|
<spec fn="block_to_light_client_header" fork="altair" hash="94342f64">
|
|
def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader:
|
|
return LightClientHeader(
|
|
beacon=BeaconBlockHeader(
|
|
slot=block.message.slot,
|
|
proposer_index=block.message.proposer_index,
|
|
parent_root=block.message.parent_root,
|
|
state_root=block.message.state_root,
|
|
body_root=hash_tree_root(block.message.body),
|
|
),
|
|
)
|
|
</spec>
|
|
|
|
- name: block_to_light_client_header#capella
|
|
sources:
|
|
- file: beacon-chain/light-client/lightclient.go
|
|
search: func BlockToLightClientHeader(
|
|
spec: |
|
|
<spec fn="block_to_light_client_header" fork="capella" hash="597ddeac">
|
|
def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader:
|
|
epoch = compute_epoch_at_slot(block.message.slot)
|
|
|
|
if epoch >= CAPELLA_FORK_EPOCH:
|
|
payload = block.message.body.execution_payload
|
|
execution_header = ExecutionPayloadHeader(
|
|
parent_hash=payload.parent_hash,
|
|
fee_recipient=payload.fee_recipient,
|
|
state_root=payload.state_root,
|
|
receipts_root=payload.receipts_root,
|
|
logs_bloom=payload.logs_bloom,
|
|
prev_randao=payload.prev_randao,
|
|
block_number=payload.block_number,
|
|
gas_limit=payload.gas_limit,
|
|
gas_used=payload.gas_used,
|
|
timestamp=payload.timestamp,
|
|
extra_data=payload.extra_data,
|
|
base_fee_per_gas=payload.base_fee_per_gas,
|
|
block_hash=payload.block_hash,
|
|
transactions_root=hash_tree_root(payload.transactions),
|
|
withdrawals_root=hash_tree_root(payload.withdrawals),
|
|
)
|
|
execution_branch = ExecutionBranch(
|
|
compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_GINDEX)
|
|
)
|
|
else:
|
|
# Note that during fork transitions, `finalized_header` may still point to earlier forks.
|
|
# While Bellatrix blocks also contain an `ExecutionPayload` (minus `withdrawals_root`),
|
|
# it was not included in the corresponding light client data. To ensure compatibility
|
|
# with legacy data going through `upgrade_lc_header_to_capella`, leave out execution data.
|
|
execution_header = ExecutionPayloadHeader()
|
|
execution_branch = ExecutionBranch()
|
|
|
|
return LightClientHeader(
|
|
beacon=BeaconBlockHeader(
|
|
slot=block.message.slot,
|
|
proposer_index=block.message.proposer_index,
|
|
parent_root=block.message.parent_root,
|
|
state_root=block.message.state_root,
|
|
body_root=hash_tree_root(block.message.body),
|
|
),
|
|
execution=execution_header,
|
|
execution_branch=execution_branch,
|
|
)
|
|
</spec>
|
|
|
|
- name: block_to_light_client_header#deneb
|
|
sources:
|
|
- file: beacon-chain/light-client/lightclient.go
|
|
search: func BlockToLightClientHeader(
|
|
spec: |
|
|
<spec fn="block_to_light_client_header" fork="deneb" hash="b5ed5bf8">
|
|
def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader:
|
|
epoch = compute_epoch_at_slot(block.message.slot)
|
|
|
|
if epoch >= CAPELLA_FORK_EPOCH:
|
|
payload = block.message.body.execution_payload
|
|
execution_header = ExecutionPayloadHeader(
|
|
parent_hash=payload.parent_hash,
|
|
fee_recipient=payload.fee_recipient,
|
|
state_root=payload.state_root,
|
|
receipts_root=payload.receipts_root,
|
|
logs_bloom=payload.logs_bloom,
|
|
prev_randao=payload.prev_randao,
|
|
block_number=payload.block_number,
|
|
gas_limit=payload.gas_limit,
|
|
gas_used=payload.gas_used,
|
|
timestamp=payload.timestamp,
|
|
extra_data=payload.extra_data,
|
|
base_fee_per_gas=payload.base_fee_per_gas,
|
|
block_hash=payload.block_hash,
|
|
transactions_root=hash_tree_root(payload.transactions),
|
|
withdrawals_root=hash_tree_root(payload.withdrawals),
|
|
)
|
|
|
|
# [New in Deneb:EIP4844]
|
|
if epoch >= DENEB_FORK_EPOCH:
|
|
execution_header.blob_gas_used = payload.blob_gas_used
|
|
execution_header.excess_blob_gas = payload.excess_blob_gas
|
|
|
|
execution_branch = ExecutionBranch(
|
|
compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_GINDEX)
|
|
)
|
|
else:
|
|
# Note that during fork transitions, `finalized_header` may still point to earlier forks.
|
|
# While Bellatrix blocks also contain an `ExecutionPayload` (minus `withdrawals_root`),
|
|
# it was not included in the corresponding light client data. To ensure compatibility
|
|
# with legacy data going through `upgrade_lc_header_to_capella`, leave out execution data.
|
|
execution_header = ExecutionPayloadHeader()
|
|
execution_branch = ExecutionBranch()
|
|
|
|
return LightClientHeader(
|
|
beacon=BeaconBlockHeader(
|
|
slot=block.message.slot,
|
|
proposer_index=block.message.proposer_index,
|
|
parent_root=block.message.parent_root,
|
|
state_root=block.message.state_root,
|
|
body_root=hash_tree_root(block.message.body),
|
|
),
|
|
execution=execution_header,
|
|
execution_branch=execution_branch,
|
|
)
|
|
</spec>
|
|
|
|
- name: bytes_to_uint64
|
|
sources:
|
|
- file: encoding/bytesutil/integers.go
|
|
search: func FromBytes8(
|
|
spec: |
|
|
<spec fn="bytes_to_uint64" fork="phase0" hash="3099d397">
|
|
def bytes_to_uint64(data: bytes) -> uint64:
|
|
"""
|
|
Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian.
|
|
"""
|
|
return uint64(int.from_bytes(data, ENDIANNESS))
|
|
</spec>
|
|
|
|
- name: calculate_committee_fraction
|
|
sources: []
|
|
spec: |
|
|
<spec fn="calculate_committee_fraction" fork="phase0" hash="782b50e9">
|
|
def calculate_committee_fraction(state: BeaconState, committee_percent: uint64) -> Gwei:
|
|
committee_weight = get_total_active_balance(state) // SLOTS_PER_EPOCH
|
|
return Gwei((committee_weight * committee_percent) // 100)
|
|
</spec>
|
|
|
|
- name: check_if_validator_active
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func IsActiveValidator(
|
|
spec: |
|
|
<spec fn="check_if_validator_active" fork="phase0" hash="66fe85e7">
|
|
def check_if_validator_active(state: BeaconState, validator_index: ValidatorIndex) -> bool:
|
|
validator = state.validators[validator_index]
|
|
return is_active_validator(validator, get_current_epoch(state))
|
|
</spec>
|
|
|
|
- name: compute_activation_exit_epoch
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func ActivationExitEpoch(
|
|
spec: |
|
|
<spec fn="compute_activation_exit_epoch" fork="phase0" hash="7a265fe3">
|
|
def compute_activation_exit_epoch(epoch: Epoch) -> Epoch:
|
|
"""
|
|
Return the epoch during which validator activations and exits initiated in ``epoch`` take effect.
|
|
"""
|
|
return Epoch(epoch + 1 + MAX_SEED_LOOKAHEAD)
|
|
</spec>
|
|
|
|
- name: compute_columns_for_custody_group
|
|
sources:
|
|
- file: beacon-chain/core/peerdas/das_core.go
|
|
search: func ComputeColumnsForCustodyGroup(
|
|
spec: |
|
|
<spec fn="compute_columns_for_custody_group" fork="fulu" hash="af24b659">
|
|
def compute_columns_for_custody_group(custody_group: CustodyIndex) -> Sequence[ColumnIndex]:
|
|
assert custody_group < NUMBER_OF_CUSTODY_GROUPS
|
|
columns_per_group = NUMBER_OF_COLUMNS // NUMBER_OF_CUSTODY_GROUPS
|
|
return [
|
|
ColumnIndex(NUMBER_OF_CUSTODY_GROUPS * i + custody_group) for i in range(columns_per_group)
|
|
]
|
|
</spec>
|
|
|
|
- name: compute_committee
|
|
sources:
|
|
- file: beacon-chain/core/helpers/beacon_committee.go
|
|
search: func ComputeCommittee(
|
|
spec: |
|
|
<spec fn="compute_committee" fork="phase0" hash="eb55fdd0">
|
|
def compute_committee(
|
|
indices: Sequence[ValidatorIndex], seed: Bytes32, index: uint64, count: uint64
|
|
) -> Sequence[ValidatorIndex]:
|
|
"""
|
|
Return the committee corresponding to ``indices``, ``seed``, ``index``, and committee ``count``.
|
|
"""
|
|
start = (len(indices) * index) // count
|
|
end = (len(indices) * uint64(index + 1)) // count
|
|
return [
|
|
indices[compute_shuffled_index(uint64(i), uint64(len(indices)), seed)]
|
|
for i in range(start, end)
|
|
]
|
|
</spec>
|
|
|
|
- name: compute_consolidation_epoch_and_update_churn
|
|
sources:
|
|
- file: beacon-chain/core/electra/churn.go
|
|
search: func ComputeConsolidationEpochAndUpdateChurn(
|
|
spec: |
|
|
<spec fn="compute_consolidation_epoch_and_update_churn" fork="electra" hash="21eba846">
|
|
def compute_consolidation_epoch_and_update_churn(
|
|
state: BeaconState, consolidation_balance: Gwei
|
|
) -> Epoch:
|
|
earliest_consolidation_epoch = max(
|
|
state.earliest_consolidation_epoch, compute_activation_exit_epoch(get_current_epoch(state))
|
|
)
|
|
per_epoch_consolidation_churn = get_consolidation_churn_limit(state)
|
|
# New epoch for consolidations.
|
|
if state.earliest_consolidation_epoch < earliest_consolidation_epoch:
|
|
consolidation_balance_to_consume = per_epoch_consolidation_churn
|
|
else:
|
|
consolidation_balance_to_consume = state.consolidation_balance_to_consume
|
|
|
|
# Consolidation doesn't fit in the current earliest epoch.
|
|
if consolidation_balance > consolidation_balance_to_consume:
|
|
balance_to_process = consolidation_balance - consolidation_balance_to_consume
|
|
additional_epochs = (balance_to_process - 1) // per_epoch_consolidation_churn + 1
|
|
earliest_consolidation_epoch += additional_epochs
|
|
consolidation_balance_to_consume += additional_epochs * per_epoch_consolidation_churn
|
|
|
|
# Consume the balance and update state variables.
|
|
state.consolidation_balance_to_consume = (
|
|
consolidation_balance_to_consume - consolidation_balance
|
|
)
|
|
state.earliest_consolidation_epoch = earliest_consolidation_epoch
|
|
|
|
return state.earliest_consolidation_epoch
|
|
</spec>
|
|
|
|
- name: compute_domain
|
|
sources:
|
|
- file: beacon-chain/core/signing/signing_root.go
|
|
search: func ComputeDomain(
|
|
spec: |
|
|
<spec fn="compute_domain" fork="phase0" hash="948e1334">
|
|
def compute_domain(
|
|
domain_type: DomainType, fork_version: Version = None, genesis_validators_root: Root = None
|
|
) -> Domain:
|
|
"""
|
|
Return the domain for the ``domain_type`` and ``fork_version``.
|
|
"""
|
|
if fork_version is None:
|
|
fork_version = GENESIS_FORK_VERSION
|
|
if genesis_validators_root is None:
|
|
genesis_validators_root = Root() # all bytes zero by default
|
|
fork_data_root = compute_fork_data_root(fork_version, genesis_validators_root)
|
|
return Domain(domain_type + fork_data_root[:28])
|
|
</spec>
|
|
|
|
- name: compute_epoch_at_slot
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func ToEpoch(
|
|
spec: |
|
|
<spec fn="compute_epoch_at_slot" fork="phase0" hash="d8b1cda2">
|
|
def compute_epoch_at_slot(slot: Slot) -> Epoch:
|
|
"""
|
|
Return the epoch number at ``slot``.
|
|
"""
|
|
return Epoch(slot // SLOTS_PER_EPOCH)
|
|
</spec>
|
|
|
|
- name: compute_exit_epoch_and_update_churn
|
|
sources:
|
|
- file: beacon-chain/state/state-native/setters_churn.go
|
|
search: func (b *BeaconState) ExitEpochAndUpdateChurn(
|
|
spec: |
|
|
<spec fn="compute_exit_epoch_and_update_churn" fork="electra" hash="b3983004">
|
|
def compute_exit_epoch_and_update_churn(state: BeaconState, exit_balance: Gwei) -> Epoch:
|
|
earliest_exit_epoch = max(
|
|
state.earliest_exit_epoch, compute_activation_exit_epoch(get_current_epoch(state))
|
|
)
|
|
per_epoch_churn = get_activation_exit_churn_limit(state)
|
|
# New epoch for exits.
|
|
if state.earliest_exit_epoch < earliest_exit_epoch:
|
|
exit_balance_to_consume = per_epoch_churn
|
|
else:
|
|
exit_balance_to_consume = state.exit_balance_to_consume
|
|
|
|
# Exit doesn't fit in the current earliest epoch.
|
|
if exit_balance > exit_balance_to_consume:
|
|
balance_to_process = exit_balance - exit_balance_to_consume
|
|
additional_epochs = (balance_to_process - 1) // per_epoch_churn + 1
|
|
earliest_exit_epoch += additional_epochs
|
|
exit_balance_to_consume += additional_epochs * per_epoch_churn
|
|
|
|
# Consume the balance and update state variables.
|
|
state.exit_balance_to_consume = exit_balance_to_consume - exit_balance
|
|
state.earliest_exit_epoch = earliest_exit_epoch
|
|
|
|
return state.earliest_exit_epoch
|
|
</spec>
|
|
|
|
- name: compute_fork_data_root
|
|
sources:
|
|
- file: beacon-chain/core/signing/signing_root.go
|
|
search: func computeForkDataRoot(
|
|
spec: |
|
|
<spec fn="compute_fork_data_root" fork="phase0" hash="155b0991">
|
|
def compute_fork_data_root(current_version: Version, genesis_validators_root: Root) -> Root:
|
|
"""
|
|
Return the 32-byte fork data root for the ``current_version`` and ``genesis_validators_root``.
|
|
This is used primarily in signature domains to avoid collisions across forks/chains.
|
|
"""
|
|
return hash_tree_root(
|
|
ForkData(
|
|
current_version=current_version,
|
|
genesis_validators_root=genesis_validators_root,
|
|
)
|
|
)
|
|
</spec>
|
|
|
|
- name: compute_fork_digest#phase0
|
|
sources:
|
|
- file: beacon-chain/core/signing/signing_root.go
|
|
search: func ComputeForkDigest(
|
|
spec: |
|
|
<spec fn="compute_fork_digest" fork="phase0" hash="e206968f">
|
|
def compute_fork_digest(
|
|
genesis_validators_root: Root,
|
|
epoch: Epoch,
|
|
) -> ForkDigest:
|
|
"""
|
|
Return the 4-byte fork digest for the ``genesis_validators_root`` at a given ``epoch``.
|
|
|
|
This is a digest primarily used for domain separation on the p2p layer.
|
|
4-bytes suffices for practical separation of forks/chains.
|
|
"""
|
|
fork_version = compute_fork_version(epoch)
|
|
base_digest = compute_fork_data_root(fork_version, genesis_validators_root)
|
|
return ForkDigest(base_digest[:4])
|
|
</spec>
|
|
|
|
- name: compute_fork_digest#fulu
|
|
sources:
|
|
- file: config/params/fork.go
|
|
search: func ForkDigest(
|
|
- file: config/params/config.go
|
|
search: func entryWithForkDigest(
|
|
spec: |
|
|
<spec fn="compute_fork_digest" fork="fulu" hash="e916a595">
|
|
def compute_fork_digest(
|
|
genesis_validators_root: Root,
|
|
epoch: Epoch,
|
|
) -> ForkDigest:
|
|
"""
|
|
Return the 4-byte fork digest for the ``genesis_validators_root`` at a given ``epoch``.
|
|
|
|
This is a digest primarily used for domain separation on the p2p layer.
|
|
4-bytes suffices for practical separation of forks/chains.
|
|
"""
|
|
fork_version = compute_fork_version(epoch)
|
|
base_digest = compute_fork_data_root(fork_version, genesis_validators_root)
|
|
|
|
# [Modified in Fulu:EIP7892]
|
|
# Bitmask digest with hash of blob parameters
|
|
blob_parameters = get_blob_parameters(epoch)
|
|
return ForkDigest(
|
|
bytes(
|
|
xor(
|
|
base_digest,
|
|
hash(
|
|
uint_to_bytes(uint64(blob_parameters.epoch))
|
|
+ uint_to_bytes(uint64(blob_parameters.max_blobs_per_block))
|
|
),
|
|
)
|
|
)[:4]
|
|
)
|
|
</spec>
|
|
|
|
- name: compute_fork_version#altair
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func ToForkVersion(
|
|
- file: time/slots/slottime.go
|
|
search: return version.Altair
|
|
spec: |
|
|
<spec fn="compute_fork_version" fork="altair" hash="ac13fc52">
|
|
def compute_fork_version(epoch: Epoch) -> Version:
|
|
"""
|
|
Return the fork version at the given ``epoch``.
|
|
"""
|
|
if epoch >= ALTAIR_FORK_EPOCH:
|
|
return ALTAIR_FORK_VERSION
|
|
return GENESIS_FORK_VERSION
|
|
</spec>
|
|
|
|
- name: compute_fork_version#bellatrix
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func ToForkVersion(
|
|
- file: time/slots/slottime.go
|
|
search: return version.Bellatrix
|
|
spec: |
|
|
<spec fn="compute_fork_version" fork="bellatrix" hash="2e5e09be">
|
|
def compute_fork_version(epoch: Epoch) -> Version:
|
|
"""
|
|
Return the fork version at the given ``epoch``.
|
|
"""
|
|
if epoch >= BELLATRIX_FORK_EPOCH:
|
|
return BELLATRIX_FORK_VERSION
|
|
if epoch >= ALTAIR_FORK_EPOCH:
|
|
return ALTAIR_FORK_VERSION
|
|
return GENESIS_FORK_VERSION
|
|
</spec>
|
|
|
|
- name: compute_fork_version#capella
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func ToForkVersion(
|
|
- file: time/slots/slottime.go
|
|
search: return version.Capella
|
|
spec: |
|
|
<spec fn="compute_fork_version" fork="capella" hash="cf11ff98">
|
|
def compute_fork_version(epoch: Epoch) -> Version:
|
|
"""
|
|
Return the fork version at the given ``epoch``.
|
|
"""
|
|
if epoch >= CAPELLA_FORK_EPOCH:
|
|
return CAPELLA_FORK_VERSION
|
|
if epoch >= BELLATRIX_FORK_EPOCH:
|
|
return BELLATRIX_FORK_VERSION
|
|
if epoch >= ALTAIR_FORK_EPOCH:
|
|
return ALTAIR_FORK_VERSION
|
|
return GENESIS_FORK_VERSION
|
|
</spec>
|
|
|
|
- name: compute_fork_version#deneb
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func ToForkVersion(
|
|
- file: time/slots/slottime.go
|
|
search: return version.Deneb
|
|
spec: |
|
|
<spec fn="compute_fork_version" fork="deneb" hash="287e691a">
|
|
def compute_fork_version(epoch: Epoch) -> Version:
|
|
"""
|
|
Return the fork version at the given ``epoch``.
|
|
"""
|
|
if epoch >= DENEB_FORK_EPOCH:
|
|
return DENEB_FORK_VERSION
|
|
if epoch >= CAPELLA_FORK_EPOCH:
|
|
return CAPELLA_FORK_VERSION
|
|
if epoch >= BELLATRIX_FORK_EPOCH:
|
|
return BELLATRIX_FORK_VERSION
|
|
if epoch >= ALTAIR_FORK_EPOCH:
|
|
return ALTAIR_FORK_VERSION
|
|
return GENESIS_FORK_VERSION
|
|
</spec>
|
|
|
|
- name: compute_fork_version#electra
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func ToForkVersion(
|
|
- file: time/slots/slottime.go
|
|
search: return version.Electra
|
|
spec: |
|
|
<spec fn="compute_fork_version" fork="electra" hash="c142a405">
|
|
def compute_fork_version(epoch: Epoch) -> Version:
|
|
"""
|
|
Return the fork version at the given ``epoch``.
|
|
"""
|
|
if epoch >= ELECTRA_FORK_EPOCH:
|
|
return ELECTRA_FORK_VERSION
|
|
if epoch >= DENEB_FORK_EPOCH:
|
|
return DENEB_FORK_VERSION
|
|
if epoch >= CAPELLA_FORK_EPOCH:
|
|
return CAPELLA_FORK_VERSION
|
|
if epoch >= BELLATRIX_FORK_EPOCH:
|
|
return BELLATRIX_FORK_VERSION
|
|
if epoch >= ALTAIR_FORK_EPOCH:
|
|
return ALTAIR_FORK_VERSION
|
|
return GENESIS_FORK_VERSION
|
|
</spec>
|
|
|
|
- name: compute_fork_version#fulu
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func ToForkVersion(
|
|
- file: time/slots/slottime.go
|
|
search: return version.Fulu
|
|
spec: |
|
|
<spec fn="compute_fork_version" fork="fulu" hash="6d472038">
|
|
def compute_fork_version(epoch: Epoch) -> Version:
|
|
"""
|
|
Return the fork version at the given ``epoch``.
|
|
"""
|
|
if epoch >= FULU_FORK_EPOCH:
|
|
return FULU_FORK_VERSION
|
|
if epoch >= ELECTRA_FORK_EPOCH:
|
|
return ELECTRA_FORK_VERSION
|
|
if epoch >= DENEB_FORK_EPOCH:
|
|
return DENEB_FORK_VERSION
|
|
if epoch >= CAPELLA_FORK_EPOCH:
|
|
return CAPELLA_FORK_VERSION
|
|
if epoch >= BELLATRIX_FORK_EPOCH:
|
|
return BELLATRIX_FORK_VERSION
|
|
if epoch >= ALTAIR_FORK_EPOCH:
|
|
return ALTAIR_FORK_VERSION
|
|
return GENESIS_FORK_VERSION
|
|
</spec>
|
|
|
|
- name: compute_matrix
|
|
sources: []
|
|
spec: |
|
|
<spec fn="compute_matrix" fork="fulu" hash="0b88eac1">
|
|
def compute_matrix(blobs: Sequence[Blob]) -> Sequence[MatrixEntry]:
|
|
"""
|
|
Return the full, flattened sequence of matrix entries.
|
|
|
|
This helper demonstrates the relationship between blobs and the matrix of cells/proofs.
|
|
The data structure for storing cells/proofs is implementation-dependent.
|
|
"""
|
|
matrix = []
|
|
for blob_index, blob in enumerate(blobs):
|
|
cells, proofs = compute_cells_and_kzg_proofs(blob)
|
|
for cell_index, (cell, proof) in enumerate(zip(cells, proofs)):
|
|
matrix.append(
|
|
MatrixEntry(
|
|
cell=cell,
|
|
kzg_proof=proof,
|
|
column_index=cell_index,
|
|
row_index=blob_index,
|
|
)
|
|
)
|
|
return matrix
|
|
</spec>
|
|
|
|
- name: compute_new_state_root
|
|
sources:
|
|
- file: beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go
|
|
search: func (vs *Server) computeStateRoot(
|
|
spec: |
|
|
<spec fn="compute_new_state_root" fork="phase0" hash="ad3c03b7">
|
|
def compute_new_state_root(state: BeaconState, block: BeaconBlock) -> Root:
|
|
temp_state: BeaconState = state.copy()
|
|
signed_block = SignedBeaconBlock(message=block)
|
|
state_transition(temp_state, signed_block, validate_result=False)
|
|
return hash_tree_root(temp_state)
|
|
</spec>
|
|
|
|
- name: compute_on_chain_aggregate
|
|
sources:
|
|
- file: beacon-chain/rpc/prysm/v1alpha1/validator/proposer_attestations_electra.go
|
|
search: func computeOnChainAggregate(
|
|
spec: |
|
|
<spec fn="compute_on_chain_aggregate" fork="electra" hash="f020af4c">
|
|
def compute_on_chain_aggregate(network_aggregates: Sequence[Attestation]) -> Attestation:
|
|
aggregates = sorted(
|
|
network_aggregates, key=lambda a: get_committee_indices(a.committee_bits)[0]
|
|
)
|
|
|
|
data = aggregates[0].data
|
|
aggregation_bits = Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT]()
|
|
for a in aggregates:
|
|
for b in a.aggregation_bits:
|
|
aggregation_bits.append(b)
|
|
|
|
signature = bls.Aggregate([a.signature for a in aggregates])
|
|
|
|
committee_indices = [get_committee_indices(a.committee_bits)[0] for a in aggregates]
|
|
committee_flags = [(index in committee_indices) for index in range(0, MAX_COMMITTEES_PER_SLOT)]
|
|
committee_bits = Bitvector[MAX_COMMITTEES_PER_SLOT](committee_flags)
|
|
|
|
return Attestation(
|
|
aggregation_bits=aggregation_bits,
|
|
data=data,
|
|
signature=signature,
|
|
committee_bits=committee_bits,
|
|
)
|
|
</spec>
|
|
|
|
- name: compute_proposer_index#phase0
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func ComputeProposerIndex(
|
|
spec: |
|
|
<spec fn="compute_proposer_index" fork="phase0" hash="865ac464">
|
|
def compute_proposer_index(
|
|
state: BeaconState, indices: Sequence[ValidatorIndex], seed: Bytes32
|
|
) -> ValidatorIndex:
|
|
"""
|
|
Return from ``indices`` a random index sampled by effective balance.
|
|
"""
|
|
assert len(indices) > 0
|
|
MAX_RANDOM_BYTE = 2**8 - 1
|
|
i = uint64(0)
|
|
total = uint64(len(indices))
|
|
while True:
|
|
candidate_index = indices[compute_shuffled_index(i % total, total, seed)]
|
|
random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32]
|
|
effective_balance = state.validators[candidate_index].effective_balance
|
|
if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
|
|
return candidate_index
|
|
i += 1
|
|
</spec>
|
|
|
|
- name: compute_proposer_index#electra
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func ComputeProposerIndex(
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: if bState.Version() >= version.Electra {
|
|
spec: |
|
|
<spec fn="compute_proposer_index" fork="electra" hash="b54f4930">
|
|
def compute_proposer_index(
|
|
state: BeaconState, indices: Sequence[ValidatorIndex], seed: Bytes32
|
|
) -> ValidatorIndex:
|
|
"""
|
|
Return from ``indices`` a random index sampled by effective balance.
|
|
"""
|
|
assert len(indices) > 0
|
|
# [Modified in Electra]
|
|
MAX_RANDOM_VALUE = 2**16 - 1
|
|
i = uint64(0)
|
|
total = uint64(len(indices))
|
|
while True:
|
|
candidate_index = indices[compute_shuffled_index(i % total, total, seed)]
|
|
# [Modified in Electra]
|
|
random_bytes = hash(seed + uint_to_bytes(i // 16))
|
|
offset = i % 16 * 2
|
|
random_value = bytes_to_uint64(random_bytes[offset : offset + 2])
|
|
effective_balance = state.validators[candidate_index].effective_balance
|
|
# [Modified in Electra:EIP7251]
|
|
if effective_balance * MAX_RANDOM_VALUE >= MAX_EFFECTIVE_BALANCE_ELECTRA * random_value:
|
|
return candidate_index
|
|
i += 1
|
|
</spec>
|
|
|
|
- name: compute_proposer_indices
|
|
sources:
|
|
- file: beacon-chain/core/helpers/beacon_committee.go
|
|
search: func PrecomputeProposerIndices(
|
|
spec: |
|
|
<spec fn="compute_proposer_indices" fork="fulu" hash="9e13dbfe">
|
|
def compute_proposer_indices(
|
|
state: BeaconState, epoch: Epoch, seed: Bytes32, indices: Sequence[ValidatorIndex]
|
|
) -> Vector[ValidatorIndex, SLOTS_PER_EPOCH]:
|
|
"""
|
|
Return the proposer indices for the given ``epoch``.
|
|
"""
|
|
start_slot = compute_start_slot_at_epoch(epoch)
|
|
seeds = [hash(seed + uint_to_bytes(Slot(start_slot + i))) for i in range(SLOTS_PER_EPOCH)]
|
|
return [compute_proposer_index(state, indices, seed) for seed in seeds]
|
|
</spec>
|
|
|
|
- name: compute_pulled_up_tip
|
|
sources: []
|
|
spec: |
|
|
<spec fn="compute_pulled_up_tip" fork="phase0" hash="ef9c1f02">
|
|
def compute_pulled_up_tip(store: Store, block_root: Root) -> None:
|
|
state = store.block_states[block_root].copy()
|
|
# Pull up the post-state of the block to the next epoch boundary
|
|
process_justification_and_finalization(state)
|
|
|
|
store.unrealized_justifications[block_root] = state.current_justified_checkpoint
|
|
update_unrealized_checkpoints(
|
|
store, state.current_justified_checkpoint, state.finalized_checkpoint
|
|
)
|
|
|
|
# If the block is from a prior epoch, apply the realized values
|
|
block_epoch = compute_epoch_at_slot(store.blocks[block_root].slot)
|
|
current_epoch = get_current_store_epoch(store)
|
|
if block_epoch < current_epoch:
|
|
update_checkpoints(store, state.current_justified_checkpoint, state.finalized_checkpoint)
|
|
</spec>
|
|
|
|
- name: compute_shuffled_index
|
|
sources:
|
|
- file: beacon-chain/core/helpers/shuffle.go
|
|
search: func ComputeShuffledIndex(
|
|
spec: |
|
|
<spec fn="compute_shuffled_index" fork="phase0" hash="e1e6a19d">
|
|
def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> uint64:
|
|
"""
|
|
Return the shuffled index corresponding to ``seed`` (and ``index_count``).
|
|
"""
|
|
assert index < index_count
|
|
|
|
# Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf)
|
|
# See the 'generalized domain' algorithm on page 3
|
|
for current_round in range(SHUFFLE_ROUND_COUNT):
|
|
pivot = bytes_to_uint64(hash(seed + uint_to_bytes(uint8(current_round)))[0:8]) % index_count
|
|
flip = (pivot + index_count - index) % index_count
|
|
position = max(index, flip)
|
|
source = hash(
|
|
seed + uint_to_bytes(uint8(current_round)) + uint_to_bytes(uint32(position // 256))
|
|
)
|
|
byte = uint8(source[(position % 256) // 8])
|
|
bit = (byte >> (position % 8)) % 2
|
|
index = flip if bit else index
|
|
|
|
return index
|
|
</spec>
|
|
|
|
- name: compute_signed_block_header
|
|
sources:
|
|
- file: consensus-types/interfaces/utils.go
|
|
search: func SignedBeaconBlockHeaderFromBlock(
|
|
spec: |
|
|
<spec fn="compute_signed_block_header" fork="deneb" hash="552442e1">
|
|
def compute_signed_block_header(signed_block: SignedBeaconBlock) -> SignedBeaconBlockHeader:
|
|
block = signed_block.message
|
|
block_header = BeaconBlockHeader(
|
|
slot=block.slot,
|
|
proposer_index=block.proposer_index,
|
|
parent_root=block.parent_root,
|
|
state_root=block.state_root,
|
|
body_root=hash_tree_root(block.body),
|
|
)
|
|
return SignedBeaconBlockHeader(message=block_header, signature=signed_block.signature)
|
|
</spec>
|
|
|
|
- name: compute_signing_root
|
|
sources:
|
|
- file: beacon-chain/core/signing/signing_root.go
|
|
search: func ComputeSigningRoot(
|
|
spec: |
|
|
<spec fn="compute_signing_root" fork="phase0" hash="07ab9f76">
|
|
def compute_signing_root(ssz_object: SSZObject, domain: Domain) -> Root:
|
|
"""
|
|
Return the signing root for the corresponding signing data.
|
|
"""
|
|
return hash_tree_root(
|
|
SigningData(
|
|
object_root=hash_tree_root(ssz_object),
|
|
domain=domain,
|
|
)
|
|
)
|
|
</spec>
|
|
|
|
- name: compute_slots_since_epoch_start
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func SinceEpochStarts(
|
|
spec: |
|
|
<spec fn="compute_slots_since_epoch_start" fork="phase0" hash="8cd580dc">
|
|
def compute_slots_since_epoch_start(slot: Slot) -> int:
|
|
return slot - compute_start_slot_at_epoch(compute_epoch_at_slot(slot))
|
|
</spec>
|
|
|
|
- name: compute_start_slot_at_epoch
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func EpochStart(
|
|
spec: |
|
|
<spec fn="compute_start_slot_at_epoch" fork="phase0" hash="d3c7b518">
|
|
def compute_start_slot_at_epoch(epoch: Epoch) -> Slot:
|
|
"""
|
|
Return the start slot of ``epoch``.
|
|
"""
|
|
return Slot(epoch * SLOTS_PER_EPOCH)
|
|
</spec>
|
|
|
|
- name: compute_subnet_for_attestation
|
|
sources:
|
|
- file: beacon-chain/core/helpers/attestation.go
|
|
search: func ComputeSubnetForAttestation(
|
|
spec: |
|
|
<spec fn="compute_subnet_for_attestation" fork="phase0" hash="2e9cf65d">
|
|
def compute_subnet_for_attestation(
|
|
committees_per_slot: uint64, slot: Slot, committee_index: CommitteeIndex
|
|
) -> SubnetID:
|
|
"""
|
|
Compute the correct subnet for an attestation for Phase 0.
|
|
Note, this mimics expected future behavior where attestations will be mapped to their shard subnet.
|
|
"""
|
|
slots_since_epoch_start = uint64(slot % SLOTS_PER_EPOCH)
|
|
committees_since_epoch_start = committees_per_slot * slots_since_epoch_start
|
|
|
|
return SubnetID((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT)
|
|
</spec>
|
|
|
|
- name: compute_subnet_for_blob_sidecar#deneb
|
|
sources:
|
|
- file: beacon-chain/sync/validate_blob.go
|
|
search: func computeSubnetForBlobSidecar(
|
|
spec: |
|
|
<spec fn="compute_subnet_for_blob_sidecar" fork="deneb" hash="09ae77b3">
|
|
def compute_subnet_for_blob_sidecar(blob_index: BlobIndex) -> SubnetID:
|
|
return SubnetID(blob_index % BLOB_SIDECAR_SUBNET_COUNT)
|
|
</spec>
|
|
|
|
- name: compute_subnet_for_blob_sidecar#electra
|
|
sources:
|
|
- file: beacon-chain/sync/validate_blob.go
|
|
search: func computeSubnetForBlobSidecar(
|
|
- file: beacon-chain/sync/validate_blob.go
|
|
search: if slots.ToEpoch(slot) >= params.BeaconConfig().ElectraForkEpoch {
|
|
spec: |
|
|
<spec fn="compute_subnet_for_blob_sidecar" fork="electra" hash="d07c6ad7">
|
|
def compute_subnet_for_blob_sidecar(blob_index: BlobIndex) -> SubnetID:
|
|
return SubnetID(blob_index % BLOB_SIDECAR_SUBNET_COUNT_ELECTRA)
|
|
</spec>
|
|
|
|
- name: compute_subnet_for_data_column_sidecar
|
|
sources:
|
|
- file: beacon-chain/core/peerdas/p2p_interface.go
|
|
search: func ComputeSubnetForDataColumnSidecar(
|
|
spec: |
|
|
<spec fn="compute_subnet_for_data_column_sidecar" fork="fulu" hash="cc47a6bd">
|
|
def compute_subnet_for_data_column_sidecar(column_index: ColumnIndex) -> SubnetID:
|
|
return SubnetID(column_index % DATA_COLUMN_SIDECAR_SUBNET_COUNT)
|
|
</spec>
|
|
|
|
- name: compute_subnets_for_sync_committee
|
|
sources:
|
|
- file: beacon-chain/sync/validate_sync_committee_message.go
|
|
search: func (s *Service) rejectIncorrectSyncCommittee(
|
|
spec: |
|
|
<spec fn="compute_subnets_for_sync_committee" fork="altair" hash="637ad33a">
|
|
def compute_subnets_for_sync_committee(
|
|
state: BeaconState, validator_index: ValidatorIndex
|
|
) -> Set[SubnetID]:
|
|
next_slot_epoch = compute_epoch_at_slot(Slot(state.slot + 1))
|
|
if compute_sync_committee_period(get_current_epoch(state)) == compute_sync_committee_period(
|
|
next_slot_epoch
|
|
):
|
|
sync_committee = state.current_sync_committee
|
|
else:
|
|
sync_committee = state.next_sync_committee
|
|
|
|
target_pubkey = state.validators[validator_index].pubkey
|
|
sync_committee_indices = [
|
|
index for index, pubkey in enumerate(sync_committee.pubkeys) if pubkey == target_pubkey
|
|
]
|
|
return set(
|
|
[
|
|
SubnetID(index // (SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT))
|
|
for index in sync_committee_indices
|
|
]
|
|
)
|
|
</spec>
|
|
|
|
- name: compute_subscribed_subnet
|
|
sources:
|
|
- file: beacon-chain/p2p/subnets.go
|
|
search: func computeSubscribedSubnet(
|
|
spec: |
|
|
<spec fn="compute_subscribed_subnet" fork="phase0" hash="54ac02e6">
|
|
def compute_subscribed_subnet(node_id: NodeID, epoch: Epoch, index: int) -> SubnetID:
|
|
node_id_prefix = node_id >> (NODE_ID_BITS - ATTESTATION_SUBNET_PREFIX_BITS)
|
|
node_offset = node_id % EPOCHS_PER_SUBNET_SUBSCRIPTION
|
|
permutation_seed = hash(
|
|
uint_to_bytes(uint64((epoch + node_offset) // EPOCHS_PER_SUBNET_SUBSCRIPTION))
|
|
)
|
|
permutated_prefix = compute_shuffled_index(
|
|
node_id_prefix,
|
|
1 << ATTESTATION_SUBNET_PREFIX_BITS,
|
|
permutation_seed,
|
|
)
|
|
return SubnetID((permutated_prefix + index) % ATTESTATION_SUBNET_COUNT)
|
|
</spec>
|
|
|
|
- name: compute_subscribed_subnets
|
|
sources:
|
|
- file: beacon-chain/p2p/subnets.go
|
|
search: func computeSubscribedSubnets(
|
|
spec: |
|
|
<spec fn="compute_subscribed_subnets" fork="phase0" hash="e8ca2772">
|
|
def compute_subscribed_subnets(node_id: NodeID, epoch: Epoch) -> Sequence[SubnetID]:
|
|
return [compute_subscribed_subnet(node_id, epoch, index) for index in range(SUBNETS_PER_NODE)]
|
|
</spec>
|
|
|
|
- name: compute_sync_committee_period
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func SyncCommitteePeriod(
|
|
spec: |
|
|
<spec fn="compute_sync_committee_period" fork="altair" hash="c5bb031f">
|
|
def compute_sync_committee_period(epoch: Epoch) -> uint64:
|
|
return epoch // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
|
|
</spec>
|
|
|
|
- name: compute_sync_committee_period_at_slot
|
|
sources: []
|
|
spec: |
|
|
<spec fn="compute_sync_committee_period_at_slot" fork="altair" hash="6ad449b8">
|
|
def compute_sync_committee_period_at_slot(slot: Slot) -> uint64:
|
|
return compute_sync_committee_period(compute_epoch_at_slot(slot))
|
|
</spec>
|
|
|
|
- name: compute_time_at_slot
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func StartTime(
|
|
spec: |
|
|
<spec fn="compute_time_at_slot" fork="phase0" hash="7d4e9e46">
|
|
def compute_time_at_slot(state: BeaconState, slot: Slot) -> uint64:
|
|
slots_since_genesis = slot - GENESIS_SLOT
|
|
return uint64(state.genesis_time + slots_since_genesis * SECONDS_PER_SLOT)
|
|
</spec>
|
|
|
|
- name: compute_weak_subjectivity_period#phase0
|
|
sources:
|
|
- file: beacon-chain/core/helpers/weak_subjectivity.go
|
|
search: func ComputeWeakSubjectivityPeriod(
|
|
spec: |
|
|
<spec fn="compute_weak_subjectivity_period" fork="phase0" hash="3f528e06">
|
|
def compute_weak_subjectivity_period(state: BeaconState) -> uint64:
|
|
"""
|
|
Returns the weak subjectivity period for the current ``state``.
|
|
This computation takes into account the effect of:
|
|
- validator set churn (bounded by ``get_validator_churn_limit()`` per epoch), and
|
|
- validator balance top-ups (bounded by ``MAX_DEPOSITS * SLOTS_PER_EPOCH`` per epoch).
|
|
A detailed calculation can be found at:
|
|
https://github.com/runtimeverification/beacon-chain-verification/blob/master/weak-subjectivity/weak-subjectivity-analysis.pdf
|
|
"""
|
|
ws_period = MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
|
N = len(get_active_validator_indices(state, get_current_epoch(state)))
|
|
t = get_total_active_balance(state) // N // ETH_TO_GWEI
|
|
T = MAX_EFFECTIVE_BALANCE // ETH_TO_GWEI
|
|
delta = get_validator_churn_limit(state)
|
|
Delta = MAX_DEPOSITS * SLOTS_PER_EPOCH
|
|
D = SAFETY_DECAY
|
|
|
|
if T * (200 + 3 * D) < t * (200 + 12 * D):
|
|
epochs_for_validator_set_churn = (
|
|
N * (t * (200 + 12 * D) - T * (200 + 3 * D)) // (600 * delta * (2 * t + T))
|
|
)
|
|
epochs_for_balance_top_ups = N * (200 + 3 * D) // (600 * Delta)
|
|
ws_period += max(epochs_for_validator_set_churn, epochs_for_balance_top_ups)
|
|
else:
|
|
ws_period += 3 * N * D * t // (200 * Delta * (T - t))
|
|
|
|
return ws_period
|
|
</spec>
|
|
|
|
- name: compute_weak_subjectivity_period#electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="compute_weak_subjectivity_period" fork="electra" hash="8d95104c">
|
|
def compute_weak_subjectivity_period(state: BeaconState) -> uint64:
|
|
"""
|
|
Returns the weak subjectivity period for the current ``state``.
|
|
This computation takes into account the effect of:
|
|
- validator set churn (bounded by ``get_balance_churn_limit()`` per epoch)
|
|
A detailed calculation can be found at:
|
|
https://notes.ethereum.org/@CarlBeek/electra_weak_subjectivity
|
|
"""
|
|
t = get_total_active_balance(state)
|
|
delta = get_balance_churn_limit(state)
|
|
epochs_for_validator_set_churn = SAFETY_DECAY * t // (2 * delta * 100)
|
|
return MIN_VALIDATOR_WITHDRAWABILITY_DELAY + epochs_for_validator_set_churn
|
|
</spec>
|
|
|
|
- name: create_light_client_bootstrap
|
|
sources:
|
|
- file: beacon-chain/light-client/lightclient.go
|
|
search: func NewLightClientBootstrapFromBeaconState(
|
|
spec: |
|
|
<spec fn="create_light_client_bootstrap" fork="altair" hash="54e41304">
|
|
def create_light_client_bootstrap(
|
|
state: BeaconState, block: SignedBeaconBlock
|
|
) -> LightClientBootstrap:
|
|
assert compute_epoch_at_slot(state.slot) >= ALTAIR_FORK_EPOCH
|
|
|
|
assert state.slot == state.latest_block_header.slot
|
|
header = state.latest_block_header.copy()
|
|
header.state_root = hash_tree_root(state)
|
|
assert hash_tree_root(header) == hash_tree_root(block.message)
|
|
|
|
return LightClientBootstrap(
|
|
header=block_to_light_client_header(block),
|
|
current_sync_committee=state.current_sync_committee,
|
|
current_sync_committee_branch=CurrentSyncCommitteeBranch(
|
|
compute_merkle_proof(state, current_sync_committee_gindex_at_slot(state.slot))
|
|
),
|
|
)
|
|
</spec>
|
|
|
|
- name: create_light_client_finality_update
|
|
sources:
|
|
- file: beacon-chain/light-client/lightclient.go
|
|
search: func NewLightClientFinalityUpdateFromBeaconState(
|
|
spec: |
|
|
<spec fn="create_light_client_finality_update" fork="altair" hash="21956507">
|
|
def create_light_client_finality_update(update: LightClientUpdate) -> LightClientFinalityUpdate:
|
|
return LightClientFinalityUpdate(
|
|
attested_header=update.attested_header,
|
|
finalized_header=update.finalized_header,
|
|
finality_branch=update.finality_branch,
|
|
sync_aggregate=update.sync_aggregate,
|
|
signature_slot=update.signature_slot,
|
|
)
|
|
</spec>
|
|
|
|
- name: create_light_client_optimistic_update
|
|
sources:
|
|
- file: beacon-chain/light-client/lightclient.go
|
|
search: func NewLightClientOptimisticUpdateFromBeaconState(
|
|
spec: |
|
|
<spec fn="create_light_client_optimistic_update" fork="altair" hash="5a993ad2">
|
|
def create_light_client_optimistic_update(update: LightClientUpdate) -> LightClientOptimisticUpdate:
|
|
return LightClientOptimisticUpdate(
|
|
attested_header=update.attested_header,
|
|
sync_aggregate=update.sync_aggregate,
|
|
signature_slot=update.signature_slot,
|
|
)
|
|
</spec>
|
|
|
|
- name: create_light_client_update
|
|
sources:
|
|
- file: beacon-chain/light-client/lightclient.go
|
|
search: func NewLightClientUpdateFromBeaconState(
|
|
spec: |
|
|
<spec fn="create_light_client_update" fork="altair" hash="cde7d86c">
|
|
def create_light_client_update(
|
|
state: BeaconState,
|
|
block: SignedBeaconBlock,
|
|
attested_state: BeaconState,
|
|
attested_block: SignedBeaconBlock,
|
|
finalized_block: Optional[SignedBeaconBlock],
|
|
) -> LightClientUpdate:
|
|
assert compute_epoch_at_slot(attested_state.slot) >= ALTAIR_FORK_EPOCH
|
|
assert (
|
|
sum(block.message.body.sync_aggregate.sync_committee_bits)
|
|
>= MIN_SYNC_COMMITTEE_PARTICIPANTS
|
|
)
|
|
|
|
assert state.slot == state.latest_block_header.slot
|
|
header = state.latest_block_header.copy()
|
|
header.state_root = hash_tree_root(state)
|
|
assert hash_tree_root(header) == hash_tree_root(block.message)
|
|
update_signature_period = compute_sync_committee_period_at_slot(block.message.slot)
|
|
|
|
assert attested_state.slot == attested_state.latest_block_header.slot
|
|
attested_header = attested_state.latest_block_header.copy()
|
|
attested_header.state_root = hash_tree_root(attested_state)
|
|
assert (
|
|
hash_tree_root(attested_header)
|
|
== hash_tree_root(attested_block.message)
|
|
== block.message.parent_root
|
|
)
|
|
update_attested_period = compute_sync_committee_period_at_slot(attested_block.message.slot)
|
|
|
|
update = LightClientUpdate()
|
|
|
|
update.attested_header = block_to_light_client_header(attested_block)
|
|
|
|
# `next_sync_committee` is only useful if the message is signed by the current sync committee
|
|
if update_attested_period == update_signature_period:
|
|
update.next_sync_committee = attested_state.next_sync_committee
|
|
update.next_sync_committee_branch = NextSyncCommitteeBranch(
|
|
compute_merkle_proof(
|
|
attested_state, next_sync_committee_gindex_at_slot(attested_state.slot)
|
|
)
|
|
)
|
|
|
|
# Indicate finality whenever possible
|
|
if finalized_block is not None:
|
|
if finalized_block.message.slot != GENESIS_SLOT:
|
|
update.finalized_header = block_to_light_client_header(finalized_block)
|
|
assert (
|
|
hash_tree_root(update.finalized_header.beacon)
|
|
== attested_state.finalized_checkpoint.root
|
|
)
|
|
else:
|
|
assert attested_state.finalized_checkpoint.root == Bytes32()
|
|
update.finality_branch = FinalityBranch(
|
|
compute_merkle_proof(attested_state, finalized_root_gindex_at_slot(attested_state.slot))
|
|
)
|
|
|
|
update.sync_aggregate = block.message.body.sync_aggregate
|
|
update.signature_slot = block.message.slot
|
|
|
|
return update
|
|
</spec>
|
|
|
|
- name: current_sync_committee_gindex_at_slot#altair
|
|
sources:
|
|
- file: beacon-chain/state/state-native/proofs.go
|
|
search: func (b *BeaconState) CurrentSyncCommitteeGeneralizedIndex(
|
|
spec: |
|
|
<spec fn="current_sync_committee_gindex_at_slot" fork="altair" hash="caf087c0">
|
|
def current_sync_committee_gindex_at_slot(_slot: Slot) -> GeneralizedIndex:
|
|
return CURRENT_SYNC_COMMITTEE_GINDEX
|
|
</spec>
|
|
|
|
- name: current_sync_committee_gindex_at_slot#electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="current_sync_committee_gindex_at_slot" fork="electra" hash="6f71f075">
|
|
def current_sync_committee_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
|
|
epoch = compute_epoch_at_slot(slot)
|
|
|
|
# [Modified in Electra]
|
|
if epoch >= ELECTRA_FORK_EPOCH:
|
|
return CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA
|
|
return CURRENT_SYNC_COMMITTEE_GINDEX
|
|
</spec>
|
|
|
|
- name: decrease_balance
|
|
sources:
|
|
- file: beacon-chain/core/helpers/rewards_penalties.go
|
|
search: func DecreaseBalance(
|
|
spec: |
|
|
<spec fn="decrease_balance" fork="phase0" hash="3dd9c03a">
|
|
def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
|
"""
|
|
Decrease the validator balance at index ``index`` by ``delta``, with underflow protection.
|
|
"""
|
|
state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta
|
|
</spec>
|
|
|
|
- name: eth_aggregate_pubkeys
|
|
sources:
|
|
- file: crypto/bls/bls.go
|
|
search: func AggregatePublicKeys(
|
|
spec: |
|
|
<spec fn="eth_aggregate_pubkeys" fork="altair" hash="bfb5ddd0">
|
|
def eth_aggregate_pubkeys(pubkeys: Sequence[BLSPubkey]) -> BLSPubkey:
|
|
"""
|
|
Return the aggregate public key for the public keys in ``pubkeys``.
|
|
|
|
Note: the ``+`` operation should be interpreted as elliptic curve point addition, which takes as input
|
|
elliptic curve points that must be decoded from the input ``BLSPubkey``s.
|
|
This implementation is for demonstrative purposes only and ignores encoding/decoding concerns.
|
|
Refer to the BLS signature draft standard for more information.
|
|
"""
|
|
assert len(pubkeys) > 0
|
|
# Ensure that the given inputs are valid pubkeys
|
|
assert all(bls.KeyValidate(pubkey) for pubkey in pubkeys)
|
|
|
|
result = copy(pubkeys[0])
|
|
for pubkey in pubkeys[1:]:
|
|
result += pubkey
|
|
return result
|
|
</spec>
|
|
|
|
- name: eth_fast_aggregate_verify
|
|
sources:
|
|
- file: crypto/bls/blst/signature.go
|
|
search: func (s *Signature) Eth2FastAggregateVerify(
|
|
spec: |
|
|
<spec fn="eth_fast_aggregate_verify" fork="altair" hash="452a1740">
|
|
def eth_fast_aggregate_verify(
|
|
pubkeys: Sequence[BLSPubkey], message: Bytes32, signature: BLSSignature
|
|
) -> bool:
|
|
"""
|
|
Wrapper to ``bls.FastAggregateVerify`` accepting the ``G2_POINT_AT_INFINITY`` signature when ``pubkeys`` is empty.
|
|
"""
|
|
if len(pubkeys) == 0 and signature == G2_POINT_AT_INFINITY:
|
|
return True
|
|
return bls.FastAggregateVerify(pubkeys, message, signature)
|
|
</spec>
|
|
|
|
- name: filter_block_tree
|
|
sources: []
|
|
spec: |
|
|
<spec fn="filter_block_tree" fork="phase0" hash="a1924421">
|
|
def filter_block_tree(store: Store, block_root: Root, blocks: Dict[Root, BeaconBlock]) -> bool:
|
|
block = store.blocks[block_root]
|
|
children = [
|
|
root for root in store.blocks.keys() if store.blocks[root].parent_root == block_root
|
|
]
|
|
|
|
# If any children branches contain expected finalized/justified checkpoints,
|
|
# add to filtered block-tree and signal viability to parent.
|
|
if any(children):
|
|
filter_block_tree_result = [filter_block_tree(store, child, blocks) for child in children]
|
|
if any(filter_block_tree_result):
|
|
blocks[block_root] = block
|
|
return True
|
|
return False
|
|
|
|
current_epoch = get_current_store_epoch(store)
|
|
voting_source = get_voting_source(store, block_root)
|
|
|
|
# The voting source should be either at the same height as the store's justified checkpoint or
|
|
# not more than two epochs ago
|
|
correct_justified = (
|
|
store.justified_checkpoint.epoch == GENESIS_EPOCH
|
|
or voting_source.epoch == store.justified_checkpoint.epoch
|
|
or voting_source.epoch + 2 >= current_epoch
|
|
)
|
|
|
|
finalized_checkpoint_block = get_checkpoint_block(
|
|
store,
|
|
block_root,
|
|
store.finalized_checkpoint.epoch,
|
|
)
|
|
|
|
correct_finalized = (
|
|
store.finalized_checkpoint.epoch == GENESIS_EPOCH
|
|
or store.finalized_checkpoint.root == finalized_checkpoint_block
|
|
)
|
|
|
|
# If expected finalized/justified, add to viable block-tree and signal viability to parent.
|
|
if correct_justified and correct_finalized:
|
|
blocks[block_root] = block
|
|
return True
|
|
|
|
# Otherwise, branch not viable
|
|
return False
|
|
</spec>
|
|
|
|
- name: finalized_root_gindex_at_slot#altair
|
|
sources:
|
|
- file: beacon-chain/state/state-native/proofs.go
|
|
search: func FinalizedRootGeneralizedIndex(
|
|
spec: |
|
|
<spec fn="finalized_root_gindex_at_slot" fork="altair" hash="9028cbc5">
|
|
def finalized_root_gindex_at_slot(_slot: Slot) -> GeneralizedIndex:
|
|
return FINALIZED_ROOT_GINDEX
|
|
</spec>
|
|
|
|
- name: finalized_root_gindex_at_slot#electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="finalized_root_gindex_at_slot" fork="electra" hash="bc7a7fe9">
|
|
def finalized_root_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
|
|
epoch = compute_epoch_at_slot(slot)
|
|
|
|
# [Modified in Electra]
|
|
if epoch >= ELECTRA_FORK_EPOCH:
|
|
return FINALIZED_ROOT_GINDEX_ELECTRA
|
|
return FINALIZED_ROOT_GINDEX
|
|
</spec>
|
|
|
|
- name: get_activation_exit_churn_limit
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validator_churn.go
|
|
search: func ActivationExitChurnLimit(
|
|
spec: |
|
|
<spec fn="get_activation_exit_churn_limit" fork="electra" hash="e3598970">
|
|
def get_activation_exit_churn_limit(state: BeaconState) -> Gwei:
|
|
"""
|
|
Return the churn limit for the current epoch dedicated to activations and exits.
|
|
"""
|
|
return min(MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT, get_balance_churn_limit(state))
|
|
</spec>
|
|
|
|
- name: get_active_validator_indices
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func ActiveValidatorIndices(
|
|
spec: |
|
|
<spec fn="get_active_validator_indices" fork="phase0" hash="b515ea11">
|
|
def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]:
|
|
"""
|
|
Return the sequence of active validator indices at ``epoch``.
|
|
"""
|
|
return [
|
|
ValidatorIndex(i) for i, v in enumerate(state.validators) if is_active_validator(v, epoch)
|
|
]
|
|
</spec>
|
|
|
|
- name: get_aggregate_and_proof
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_aggregate_and_proof" fork="phase0" hash="bb7f6018">
|
|
def get_aggregate_and_proof(
|
|
state: BeaconState, aggregator_index: ValidatorIndex, aggregate: Attestation, privkey: int
|
|
) -> AggregateAndProof:
|
|
return AggregateAndProof(
|
|
aggregator_index=aggregator_index,
|
|
aggregate=aggregate,
|
|
selection_proof=get_slot_signature(state, aggregate.data.slot, privkey),
|
|
)
|
|
</spec>
|
|
|
|
- name: get_aggregate_and_proof_signature
|
|
sources:
|
|
- file: validator/client/aggregate.go
|
|
search: func (v *validator) aggregateAndProofSig(
|
|
spec: |
|
|
<spec fn="get_aggregate_and_proof_signature" fork="phase0" hash="6c77571c">
|
|
def get_aggregate_and_proof_signature(
|
|
state: BeaconState, aggregate_and_proof: AggregateAndProof, privkey: int
|
|
) -> BLSSignature:
|
|
aggregate = aggregate_and_proof.aggregate
|
|
domain = get_domain(
|
|
state, DOMAIN_AGGREGATE_AND_PROOF, compute_epoch_at_slot(aggregate.data.slot)
|
|
)
|
|
signing_root = compute_signing_root(aggregate_and_proof, domain)
|
|
return bls.Sign(privkey, signing_root)
|
|
</spec>
|
|
|
|
- name: get_aggregate_signature
|
|
sources:
|
|
- file: crypto/bls/bls.go
|
|
search: func AggregateSignatures(
|
|
spec: |
|
|
<spec fn="get_aggregate_signature" fork="phase0" hash="1797b223">
|
|
def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature:
|
|
signatures = [attestation.signature for attestation in attestations]
|
|
return bls.Aggregate(signatures)
|
|
</spec>
|
|
|
|
- name: get_ancestor
|
|
sources:
|
|
- file: beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go
|
|
search: func (f *ForkChoice) AncestorRoot(
|
|
spec: |
|
|
<spec fn="get_ancestor" fork="phase0" hash="0e8c06f9">
|
|
def get_ancestor(store: Store, root: Root, slot: Slot) -> Root:
|
|
block = store.blocks[root]
|
|
if block.slot > slot:
|
|
return get_ancestor(store, block.parent_root, slot)
|
|
return root
|
|
</spec>
|
|
|
|
- name: get_attestation_component_deltas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_attestation_component_deltas" fork="phase0" hash="c6d7c444">
|
|
def get_attestation_component_deltas(
|
|
state: BeaconState, attestations: Sequence[PendingAttestation]
|
|
) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
|
"""
|
|
Helper with shared logic for use by get source, target, and head deltas functions
|
|
"""
|
|
rewards = [Gwei(0)] * len(state.validators)
|
|
penalties = [Gwei(0)] * len(state.validators)
|
|
total_balance = get_total_active_balance(state)
|
|
unslashed_attesting_indices = get_unslashed_attesting_indices(state, attestations)
|
|
attesting_balance = get_total_balance(state, unslashed_attesting_indices)
|
|
for index in get_eligible_validator_indices(state):
|
|
if index in unslashed_attesting_indices:
|
|
increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from balance totals to avoid uint64 overflow
|
|
if is_in_inactivity_leak(state):
|
|
# Since full base reward will be canceled out by inactivity penalty deltas,
|
|
# optimal participation receives full base reward compensation here.
|
|
rewards[index] += get_base_reward(state, index)
|
|
else:
|
|
reward_numerator = get_base_reward(state, index) * (attesting_balance // increment)
|
|
rewards[index] += reward_numerator // (total_balance // increment)
|
|
else:
|
|
penalties[index] += get_base_reward(state, index)
|
|
return rewards, penalties
|
|
</spec>
|
|
|
|
- name: get_attestation_deltas
|
|
sources:
|
|
- file: beacon-chain/core/epoch/precompute/reward_penalty.go
|
|
search: func AttestationsDelta(
|
|
spec: |
|
|
<spec fn="get_attestation_deltas" fork="phase0" hash="17fcc0d9">
|
|
def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
|
"""
|
|
Return attestation reward/penalty deltas for each validator.
|
|
"""
|
|
source_rewards, source_penalties = get_source_deltas(state)
|
|
target_rewards, target_penalties = get_target_deltas(state)
|
|
head_rewards, head_penalties = get_head_deltas(state)
|
|
inclusion_delay_rewards, _ = get_inclusion_delay_deltas(state)
|
|
_, inactivity_penalties = get_inactivity_penalty_deltas(state)
|
|
|
|
rewards = [
|
|
source_rewards[i] + target_rewards[i] + head_rewards[i] + inclusion_delay_rewards[i]
|
|
for i in range(len(state.validators))
|
|
]
|
|
|
|
penalties = [
|
|
source_penalties[i] + target_penalties[i] + head_penalties[i] + inactivity_penalties[i]
|
|
for i in range(len(state.validators))
|
|
]
|
|
|
|
return rewards, penalties
|
|
</spec>
|
|
|
|
- name: get_attestation_participation_flag_indices#altair
|
|
sources:
|
|
- file: beacon-chain/core/altair/attestation.go
|
|
search: func AttestationParticipationFlagIndices(
|
|
spec: |
|
|
<spec fn="get_attestation_participation_flag_indices" fork="altair" hash="d697ae39">
|
|
def get_attestation_participation_flag_indices(
|
|
state: BeaconState, data: AttestationData, inclusion_delay: uint64
|
|
) -> Sequence[int]:
|
|
"""
|
|
Return the flag indices that are satisfied by an attestation.
|
|
"""
|
|
# Matching source
|
|
if data.target.epoch == get_current_epoch(state):
|
|
justified_checkpoint = state.current_justified_checkpoint
|
|
else:
|
|
justified_checkpoint = state.previous_justified_checkpoint
|
|
is_matching_source = data.source == justified_checkpoint
|
|
|
|
# Matching target
|
|
target_root = get_block_root(state, data.target.epoch)
|
|
target_root_matches = data.target.root == target_root
|
|
is_matching_target = is_matching_source and target_root_matches
|
|
|
|
# Matching head
|
|
head_root = get_block_root_at_slot(state, data.slot)
|
|
head_root_matches = data.beacon_block_root == head_root
|
|
is_matching_head = is_matching_target and head_root_matches
|
|
|
|
assert is_matching_source
|
|
|
|
participation_flag_indices = []
|
|
if is_matching_source and inclusion_delay <= integer_squareroot(SLOTS_PER_EPOCH):
|
|
participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX)
|
|
if is_matching_target and inclusion_delay <= SLOTS_PER_EPOCH:
|
|
participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX)
|
|
if is_matching_head and inclusion_delay == MIN_ATTESTATION_INCLUSION_DELAY:
|
|
participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX)
|
|
|
|
return participation_flag_indices
|
|
</spec>
|
|
|
|
- name: get_attestation_participation_flag_indices#deneb
|
|
sources:
|
|
- file: beacon-chain/core/altair/attestation.go
|
|
search: func AttestationParticipationFlagIndices(
|
|
spec: |
|
|
<spec fn="get_attestation_participation_flag_indices" fork="deneb" hash="7b46fd38">
|
|
def get_attestation_participation_flag_indices(
|
|
state: BeaconState, data: AttestationData, inclusion_delay: uint64
|
|
) -> Sequence[int]:
|
|
"""
|
|
Return the flag indices that are satisfied by an attestation.
|
|
"""
|
|
# Matching source
|
|
if data.target.epoch == get_current_epoch(state):
|
|
justified_checkpoint = state.current_justified_checkpoint
|
|
else:
|
|
justified_checkpoint = state.previous_justified_checkpoint
|
|
is_matching_source = data.source == justified_checkpoint
|
|
|
|
# Matching target
|
|
target_root = get_block_root(state, data.target.epoch)
|
|
target_root_matches = data.target.root == target_root
|
|
is_matching_target = is_matching_source and target_root_matches
|
|
|
|
# Matching head
|
|
head_root = get_block_root_at_slot(state, data.slot)
|
|
head_root_matches = data.beacon_block_root == head_root
|
|
is_matching_head = is_matching_target and head_root_matches
|
|
|
|
assert is_matching_source
|
|
|
|
participation_flag_indices = []
|
|
if is_matching_source and inclusion_delay <= integer_squareroot(SLOTS_PER_EPOCH):
|
|
participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX)
|
|
# [Modified in Deneb:EIP7045]
|
|
if is_matching_target:
|
|
participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX)
|
|
if is_matching_head and inclusion_delay == MIN_ATTESTATION_INCLUSION_DELAY:
|
|
participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX)
|
|
|
|
return participation_flag_indices
|
|
</spec>
|
|
|
|
- name: get_attestation_signature
|
|
sources:
|
|
- file: beacon-chain/core/blocks/signature.go
|
|
search: func AttestationSignatureBatch(
|
|
spec: |
|
|
<spec fn="get_attestation_signature" fork="phase0" hash="0b339b31">
|
|
def get_attestation_signature(
|
|
state: BeaconState, attestation_data: AttestationData, privkey: int
|
|
) -> BLSSignature:
|
|
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation_data.target.epoch)
|
|
signing_root = compute_signing_root(attestation_data, domain)
|
|
return bls.Sign(privkey, signing_root)
|
|
</spec>
|
|
|
|
- name: get_attesting_balance
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_attesting_balance" fork="phase0" hash="bc7890c1">
|
|
def get_attesting_balance(state: BeaconState, attestations: Sequence[PendingAttestation]) -> Gwei:
|
|
"""
|
|
Return the combined effective balance of the set of unslashed validators participating in ``attestations``.
|
|
Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
|
|
"""
|
|
return get_total_balance(state, get_unslashed_attesting_indices(state, attestations))
|
|
</spec>
|
|
|
|
- name: get_attesting_indices#phase0
|
|
sources:
|
|
- file: proto/prysm/v1alpha1/attestation/attestation_utils.go
|
|
search: func AttestingIndices(
|
|
spec: |
|
|
<spec fn="get_attesting_indices" fork="phase0" hash="abbf9f2d">
|
|
def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[ValidatorIndex]:
|
|
"""
|
|
Return the set of attesting indices corresponding to ``data`` and ``bits``.
|
|
"""
|
|
committee = get_beacon_committee(state, attestation.data.slot, attestation.data.index)
|
|
return set(index for i, index in enumerate(committee) if attestation.aggregation_bits[i])
|
|
</spec>
|
|
|
|
- name: get_attesting_indices#electra
|
|
sources:
|
|
- file: proto/prysm/v1alpha1/attestation/attestation_utils.go
|
|
search: func AttestingIndices(
|
|
spec: |
|
|
<spec fn="get_attesting_indices" fork="electra" hash="fd4b0475">
|
|
def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[ValidatorIndex]:
|
|
"""
|
|
Return the set of attesting indices corresponding to ``aggregation_bits`` and ``committee_bits``.
|
|
"""
|
|
output: Set[ValidatorIndex] = set()
|
|
committee_indices = get_committee_indices(attestation.committee_bits)
|
|
committee_offset = 0
|
|
for committee_index in committee_indices:
|
|
committee = get_beacon_committee(state, attestation.data.slot, committee_index)
|
|
committee_attesters = set(
|
|
attester_index
|
|
for i, attester_index in enumerate(committee)
|
|
if attestation.aggregation_bits[committee_offset + i]
|
|
)
|
|
output = output.union(committee_attesters)
|
|
|
|
committee_offset += len(committee)
|
|
|
|
return output
|
|
</spec>
|
|
|
|
- name: get_balance_churn_limit
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validator_churn.go
|
|
search: func BalanceChurnLimit(
|
|
spec: |
|
|
<spec fn="get_balance_churn_limit" fork="electra" hash="f4498c02">
|
|
def get_balance_churn_limit(state: BeaconState) -> Gwei:
|
|
"""
|
|
Return the churn limit for the current epoch.
|
|
"""
|
|
churn = max(
|
|
MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA, get_total_active_balance(state) // CHURN_LIMIT_QUOTIENT
|
|
)
|
|
return churn - churn % EFFECTIVE_BALANCE_INCREMENT
|
|
</spec>
|
|
|
|
- name: get_base_reward#phase0
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_base_reward" fork="phase0" hash="46418226">
|
|
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
|
total_balance = get_total_active_balance(state)
|
|
effective_balance = state.validators[index].effective_balance
|
|
return Gwei(
|
|
effective_balance
|
|
* BASE_REWARD_FACTOR
|
|
// integer_squareroot(total_balance)
|
|
// BASE_REWARDS_PER_EPOCH
|
|
)
|
|
</spec>
|
|
|
|
- name: get_base_reward#altair
|
|
sources:
|
|
- file: beacon-chain/core/altair/reward.go
|
|
search: func BaseReward(
|
|
spec: |
|
|
<spec fn="get_base_reward" fork="altair" hash="edb30df0">
|
|
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
|
"""
|
|
Return the base reward for the validator defined by ``index`` with respect to the current ``state``.
|
|
"""
|
|
increments = state.validators[index].effective_balance // EFFECTIVE_BALANCE_INCREMENT
|
|
return Gwei(increments * get_base_reward_per_increment(state))
|
|
</spec>
|
|
|
|
- name: get_base_reward_per_increment
|
|
sources:
|
|
- file: beacon-chain/core/altair/reward.go
|
|
search: func BaseRewardPerIncrement(
|
|
spec: |
|
|
<spec fn="get_base_reward_per_increment" fork="altair" hash="8b1aba66">
|
|
def get_base_reward_per_increment(state: BeaconState) -> Gwei:
|
|
return Gwei(
|
|
EFFECTIVE_BALANCE_INCREMENT
|
|
* BASE_REWARD_FACTOR
|
|
// integer_squareroot(get_total_active_balance(state))
|
|
)
|
|
</spec>
|
|
|
|
- name: get_beacon_committee
|
|
sources:
|
|
- file: beacon-chain/core/helpers/beacon_committee.go
|
|
search: func BeaconCommittee(
|
|
spec: |
|
|
<spec fn="get_beacon_committee" fork="phase0" hash="09ee43b7">
|
|
def get_beacon_committee(
|
|
state: BeaconState, slot: Slot, index: CommitteeIndex
|
|
) -> Sequence[ValidatorIndex]:
|
|
"""
|
|
Return the beacon committee at ``slot`` for ``index``.
|
|
"""
|
|
epoch = compute_epoch_at_slot(slot)
|
|
committees_per_slot = get_committee_count_per_slot(state, epoch)
|
|
return compute_committee(
|
|
indices=get_active_validator_indices(state, epoch),
|
|
seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER),
|
|
index=(slot % SLOTS_PER_EPOCH) * committees_per_slot + index,
|
|
count=committees_per_slot * SLOTS_PER_EPOCH,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_beacon_proposer_index#phase0
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func BeaconProposerIndex(
|
|
spec: |
|
|
<spec fn="get_beacon_proposer_index" fork="phase0" hash="a4562612">
|
|
def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
|
|
"""
|
|
Return the beacon proposer index at the current slot.
|
|
"""
|
|
epoch = get_current_epoch(state)
|
|
seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + uint_to_bytes(state.slot))
|
|
indices = get_active_validator_indices(state, epoch)
|
|
return compute_proposer_index(state, indices, seed)
|
|
</spec>
|
|
|
|
- name: get_beacon_proposer_index#fulu
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func BeaconProposerIndex(
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func beaconProposerIndexAtSlotFulu(
|
|
spec: |
|
|
<spec fn="get_beacon_proposer_index" fork="fulu" hash="8e1ef77d">
|
|
def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
|
|
"""
|
|
Return the beacon proposer index at the current slot.
|
|
"""
|
|
return state.proposer_lookahead[state.slot % SLOTS_PER_EPOCH]
|
|
</spec>
|
|
|
|
- name: get_beacon_proposer_indices
|
|
sources:
|
|
- file: beacon-chain/core/helpers/beacon_committee.go
|
|
search: func PrecomputeProposerIndices(
|
|
spec: |
|
|
<spec fn="get_beacon_proposer_indices" fork="fulu" hash="f5866a8f">
|
|
def get_beacon_proposer_indices(
|
|
state: BeaconState, epoch: Epoch
|
|
) -> Vector[ValidatorIndex, SLOTS_PER_EPOCH]:
|
|
"""
|
|
Return the proposer indices for the given ``epoch``.
|
|
"""
|
|
indices = get_active_validator_indices(state, epoch)
|
|
seed = get_seed(state, epoch, DOMAIN_BEACON_PROPOSER)
|
|
return compute_proposer_indices(state, epoch, seed, indices)
|
|
</spec>
|
|
|
|
- name: get_blob_parameters
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_blob_parameters" fork="fulu" hash="e390b1c8">
|
|
def get_blob_parameters(epoch: Epoch) -> BlobParameters:
|
|
"""
|
|
Return the blob parameters at a given epoch.
|
|
"""
|
|
for entry in sorted(BLOB_SCHEDULE, key=lambda e: e["EPOCH"], reverse=True):
|
|
if epoch >= entry["EPOCH"]:
|
|
return BlobParameters(entry["EPOCH"], entry["MAX_BLOBS_PER_BLOCK"])
|
|
return BlobParameters(ELECTRA_FORK_EPOCH, MAX_BLOBS_PER_BLOCK_ELECTRA)
|
|
</spec>
|
|
|
|
- name: get_blob_sidecars
|
|
sources:
|
|
- file: beacon-chain/rpc/prysm/v1alpha1/validator/proposer_deneb.go
|
|
search: func BuildBlobSidecars(
|
|
spec: |
|
|
<spec fn="get_blob_sidecars" fork="deneb" hash="0206e9a5">
|
|
def get_blob_sidecars(
|
|
signed_block: SignedBeaconBlock, blobs: Sequence[Blob], blob_kzg_proofs: Sequence[KZGProof]
|
|
) -> Sequence[BlobSidecar]:
|
|
block = signed_block.message
|
|
signed_block_header = compute_signed_block_header(signed_block)
|
|
return [
|
|
BlobSidecar(
|
|
index=index,
|
|
blob=blob,
|
|
kzg_commitment=block.body.blob_kzg_commitments[index],
|
|
kzg_proof=blob_kzg_proofs[index],
|
|
signed_block_header=signed_block_header,
|
|
kzg_commitment_inclusion_proof=compute_merkle_proof(
|
|
block.body,
|
|
get_generalized_index(BeaconBlockBody, "blob_kzg_commitments", index),
|
|
),
|
|
)
|
|
for index, blob in enumerate(blobs)
|
|
]
|
|
</spec>
|
|
|
|
- name: get_block_root
|
|
sources:
|
|
- file: beacon-chain/core/helpers/block.go
|
|
search: func BlockRoot(
|
|
spec: |
|
|
<spec fn="get_block_root" fork="phase0" hash="92504d2d">
|
|
def get_block_root(state: BeaconState, epoch: Epoch) -> Root:
|
|
"""
|
|
Return the block root at the start of a recent ``epoch``.
|
|
"""
|
|
return get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch))
|
|
</spec>
|
|
|
|
- name: get_block_root_at_slot
|
|
sources:
|
|
- file: beacon-chain/core/helpers/block.go
|
|
search: func BlockRootAtSlot(
|
|
spec: |
|
|
<spec fn="get_block_root_at_slot" fork="phase0" hash="0c5003ec">
|
|
def get_block_root_at_slot(state: BeaconState, slot: Slot) -> Root:
|
|
"""
|
|
Return the block root at a recent ``slot``.
|
|
"""
|
|
assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT
|
|
return state.block_roots[slot % SLOTS_PER_HISTORICAL_ROOT]
|
|
</spec>
|
|
|
|
- name: get_block_signature
|
|
sources:
|
|
- file: testing/util/helpers.go
|
|
search: func BlockSignature(
|
|
spec: |
|
|
<spec fn="get_block_signature" fork="phase0" hash="1f2edb6b">
|
|
def get_block_signature(state: BeaconState, block: BeaconBlock, privkey: int) -> BLSSignature:
|
|
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(block.slot))
|
|
signing_root = compute_signing_root(block, domain)
|
|
return bls.Sign(privkey, signing_root)
|
|
</spec>
|
|
|
|
- name: get_checkpoint_block
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_checkpoint_block" fork="phase0" hash="9d8daa22">
|
|
def get_checkpoint_block(store: Store, root: Root, epoch: Epoch) -> Root:
|
|
"""
|
|
Compute the checkpoint block for epoch ``epoch`` in the chain of block ``root``
|
|
"""
|
|
epoch_first_slot = compute_start_slot_at_epoch(epoch)
|
|
return get_ancestor(store, root, epoch_first_slot)
|
|
</spec>
|
|
|
|
- name: get_committee_assignment
|
|
sources:
|
|
- file: beacon-chain/core/helpers/beacon_committee.go
|
|
search: func CommitteeAssignments(
|
|
spec: |
|
|
<spec fn="get_committee_assignment" fork="phase0" hash="8b41515e">
|
|
def get_committee_assignment(
|
|
state: BeaconState, epoch: Epoch, validator_index: ValidatorIndex
|
|
) -> Optional[Tuple[Sequence[ValidatorIndex], CommitteeIndex, Slot]]:
|
|
"""
|
|
Return the committee assignment in the ``epoch`` for ``validator_index``.
|
|
``assignment`` returned is a tuple of the following form:
|
|
* ``assignment[0]`` is the list of validators in the committee
|
|
* ``assignment[1]`` is the index to which the committee is assigned
|
|
* ``assignment[2]`` is the slot at which the committee is assigned
|
|
Return None if no assignment.
|
|
"""
|
|
next_epoch = Epoch(get_current_epoch(state) + 1)
|
|
assert epoch <= next_epoch
|
|
|
|
start_slot = compute_start_slot_at_epoch(epoch)
|
|
committee_count_per_slot = get_committee_count_per_slot(state, epoch)
|
|
for slot in range(start_slot, start_slot + SLOTS_PER_EPOCH):
|
|
for index in range(committee_count_per_slot):
|
|
committee = get_beacon_committee(state, Slot(slot), CommitteeIndex(index))
|
|
if validator_index in committee:
|
|
return committee, CommitteeIndex(index), Slot(slot)
|
|
return None
|
|
</spec>
|
|
|
|
- name: get_committee_count_per_slot
|
|
sources:
|
|
- file: beacon-chain/core/helpers/beacon_committee.go
|
|
search: func SlotCommitteeCount(
|
|
spec: |
|
|
<spec fn="get_committee_count_per_slot" fork="phase0" hash="3238e1ea">
|
|
def get_committee_count_per_slot(state: BeaconState, epoch: Epoch) -> uint64:
|
|
"""
|
|
Return the number of committees in each slot for the given ``epoch``.
|
|
"""
|
|
return max(
|
|
uint64(1),
|
|
min(
|
|
MAX_COMMITTEES_PER_SLOT,
|
|
uint64(len(get_active_validator_indices(state, epoch)))
|
|
// SLOTS_PER_EPOCH
|
|
// TARGET_COMMITTEE_SIZE,
|
|
),
|
|
)
|
|
</spec>
|
|
|
|
- name: get_committee_indices
|
|
sources:
|
|
- file: beacon-chain/core/helpers/beacon_committee.go
|
|
search: func CommitteeIndices(
|
|
spec: |
|
|
<spec fn="get_committee_indices" fork="electra" hash="fa701795">
|
|
def get_committee_indices(committee_bits: Bitvector) -> Sequence[CommitteeIndex]:
|
|
return [CommitteeIndex(index) for index, bit in enumerate(committee_bits) if bit]
|
|
</spec>
|
|
|
|
- name: get_consolidation_churn_limit
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validator_churn.go
|
|
search: func ConsolidationChurnLimit(
|
|
spec: |
|
|
<spec fn="get_consolidation_churn_limit" fork="electra" hash="3d296032">
|
|
def get_consolidation_churn_limit(state: BeaconState) -> Gwei:
|
|
return get_balance_churn_limit(state) - get_activation_exit_churn_limit(state)
|
|
</spec>
|
|
|
|
- name: get_contribution_and_proof
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_contribution_and_proof" fork="altair" hash="ea4e6980">
|
|
def get_contribution_and_proof(
|
|
state: BeaconState,
|
|
aggregator_index: ValidatorIndex,
|
|
contribution: SyncCommitteeContribution,
|
|
privkey: int,
|
|
) -> ContributionAndProof:
|
|
selection_proof = get_sync_committee_selection_proof(
|
|
state,
|
|
contribution.slot,
|
|
contribution.subcommittee_index,
|
|
privkey,
|
|
)
|
|
return ContributionAndProof(
|
|
aggregator_index=aggregator_index,
|
|
contribution=contribution,
|
|
selection_proof=selection_proof,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_contribution_and_proof_signature
|
|
sources:
|
|
- file: validator/client/sync_committee.go
|
|
search: func (v *validator) signContributionAndProof(
|
|
spec: |
|
|
<spec fn="get_contribution_and_proof_signature" fork="altair" hash="2d842fca">
|
|
def get_contribution_and_proof_signature(
|
|
state: BeaconState, contribution_and_proof: ContributionAndProof, privkey: int
|
|
) -> BLSSignature:
|
|
contribution = contribution_and_proof.contribution
|
|
domain = get_domain(
|
|
state, DOMAIN_CONTRIBUTION_AND_PROOF, compute_epoch_at_slot(contribution.slot)
|
|
)
|
|
signing_root = compute_signing_root(contribution_and_proof, domain)
|
|
return bls.Sign(privkey, signing_root)
|
|
</spec>
|
|
|
|
- name: get_current_epoch
|
|
sources:
|
|
- file: beacon-chain/core/time/slot_epoch.go
|
|
search: func CurrentEpoch(
|
|
spec: |
|
|
<spec fn="get_current_epoch" fork="phase0" hash="73a94be9">
|
|
def get_current_epoch(state: BeaconState) -> Epoch:
|
|
"""
|
|
Return the current epoch.
|
|
"""
|
|
return compute_epoch_at_slot(state.slot)
|
|
</spec>
|
|
|
|
- name: get_current_slot
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func CurrentSlot(
|
|
spec: |
|
|
<spec fn="get_current_slot" fork="phase0" hash="49408b6a">
|
|
def get_current_slot(store: Store) -> Slot:
|
|
return Slot(GENESIS_SLOT + get_slots_since_genesis(store))
|
|
</spec>
|
|
|
|
- name: get_current_store_epoch
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_current_store_epoch" fork="phase0" hash="4d86e690">
|
|
def get_current_store_epoch(store: Store) -> Epoch:
|
|
return compute_epoch_at_slot(get_current_slot(store))
|
|
</spec>
|
|
|
|
- name: get_custody_groups
|
|
sources:
|
|
- file: beacon-chain/core/peerdas/das_core.go
|
|
search: func CustodyGroups(
|
|
spec: |
|
|
<spec fn="get_custody_groups" fork="fulu" hash="2e6b71d3">
|
|
def get_custody_groups(node_id: NodeID, custody_group_count: uint64) -> Sequence[CustodyIndex]:
|
|
assert custody_group_count <= NUMBER_OF_CUSTODY_GROUPS
|
|
|
|
# Skip computation if all groups are custodied
|
|
if custody_group_count == NUMBER_OF_CUSTODY_GROUPS:
|
|
return [CustodyIndex(i) for i in range(NUMBER_OF_CUSTODY_GROUPS)]
|
|
|
|
current_id = uint256(node_id)
|
|
custody_groups: List[CustodyIndex] = []
|
|
while len(custody_groups) < custody_group_count:
|
|
custody_group = CustodyIndex(
|
|
bytes_to_uint64(hash(uint_to_bytes(current_id))[0:8]) % NUMBER_OF_CUSTODY_GROUPS
|
|
)
|
|
if custody_group not in custody_groups:
|
|
custody_groups.append(custody_group)
|
|
if current_id == UINT256_MAX:
|
|
# Overflow prevention
|
|
current_id = uint256(0)
|
|
else:
|
|
current_id += 1
|
|
|
|
assert len(custody_groups) == len(set(custody_groups))
|
|
return sorted(custody_groups)
|
|
</spec>
|
|
|
|
- name: get_data_column_sidecars
|
|
sources:
|
|
- file: beacon-chain/core/peerdas/validator.go
|
|
search: func DataColumnSidecars(
|
|
spec: |
|
|
<spec fn="get_data_column_sidecars" fork="fulu" hash="317fc596">
|
|
def get_data_column_sidecars(
|
|
signed_block_header: SignedBeaconBlockHeader,
|
|
kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK],
|
|
kzg_commitments_inclusion_proof: Vector[Bytes32, KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH],
|
|
cells_and_kzg_proofs: Sequence[
|
|
Tuple[Vector[Cell, CELLS_PER_EXT_BLOB], Vector[KZGProof, CELLS_PER_EXT_BLOB]]
|
|
],
|
|
) -> Sequence[DataColumnSidecar]:
|
|
"""
|
|
Given a signed block header and the commitments, inclusion proof, cells/proofs associated with
|
|
each blob in the block, assemble the sidecars which can be distributed to peers.
|
|
"""
|
|
assert len(cells_and_kzg_proofs) == len(kzg_commitments)
|
|
|
|
sidecars = []
|
|
for column_index in range(NUMBER_OF_COLUMNS):
|
|
column_cells, column_proofs = [], []
|
|
for cells, proofs in cells_and_kzg_proofs:
|
|
column_cells.append(cells[column_index])
|
|
column_proofs.append(proofs[column_index])
|
|
sidecars.append(
|
|
DataColumnSidecar(
|
|
index=column_index,
|
|
column=column_cells,
|
|
kzg_commitments=kzg_commitments,
|
|
kzg_proofs=column_proofs,
|
|
signed_block_header=signed_block_header,
|
|
kzg_commitments_inclusion_proof=kzg_commitments_inclusion_proof,
|
|
)
|
|
)
|
|
return sidecars
|
|
</spec>
|
|
|
|
- name: get_domain
|
|
sources:
|
|
- file: beacon-chain/core/signing/domain.go
|
|
search: func Domain(
|
|
spec: |
|
|
<spec fn="get_domain" fork="phase0" hash="99ea23f6">
|
|
def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch = None) -> Domain:
|
|
"""
|
|
Return the signature domain (fork version concatenated with domain type) of a message.
|
|
"""
|
|
epoch = get_current_epoch(state) if epoch is None else epoch
|
|
fork_version = (
|
|
state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version
|
|
)
|
|
return compute_domain(domain_type, fork_version, state.genesis_validators_root)
|
|
</spec>
|
|
|
|
- name: get_eligible_validator_indices
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_eligible_validator_indices" fork="phase0" hash="22a24ce4">
|
|
def get_eligible_validator_indices(state: BeaconState) -> Sequence[ValidatorIndex]:
|
|
previous_epoch = get_previous_epoch(state)
|
|
return [
|
|
ValidatorIndex(index)
|
|
for index, v in enumerate(state.validators)
|
|
if is_active_validator(v, previous_epoch)
|
|
or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch)
|
|
]
|
|
</spec>
|
|
|
|
- name: get_epoch_signature
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_epoch_signature" fork="phase0" hash="31a86d34">
|
|
def get_epoch_signature(state: BeaconState, block: BeaconBlock, privkey: int) -> BLSSignature:
|
|
domain = get_domain(state, DOMAIN_RANDAO, compute_epoch_at_slot(block.slot))
|
|
signing_root = compute_signing_root(compute_epoch_at_slot(block.slot), domain)
|
|
return bls.Sign(privkey, signing_root)
|
|
</spec>
|
|
|
|
- name: get_eth1_pending_deposit_count
|
|
sources:
|
|
- file: beacon-chain/rpc/prysm/v1alpha1/validator/proposer_deposits.go
|
|
search: func (vs *Server) deposits(
|
|
spec: |
|
|
<spec fn="get_eth1_pending_deposit_count" fork="electra" hash="5dc54930">
|
|
def get_eth1_pending_deposit_count(state: BeaconState) -> uint64:
|
|
eth1_deposit_index_limit = min(
|
|
state.eth1_data.deposit_count, state.deposit_requests_start_index
|
|
)
|
|
if state.eth1_deposit_index < eth1_deposit_index_limit:
|
|
return min(MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index)
|
|
else:
|
|
return uint64(0)
|
|
</spec>
|
|
|
|
- name: get_eth1_vote#phase0
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_eth1_vote" fork="phase0" hash="850a63eb">
|
|
def get_eth1_vote(state: BeaconState, eth1_chain: Sequence[Eth1Block]) -> Eth1Data:
|
|
period_start = voting_period_start_time(state)
|
|
# `eth1_chain` abstractly represents all blocks in the eth1 chain sorted by ascending block height
|
|
votes_to_consider = [
|
|
get_eth1_data(block)
|
|
for block in eth1_chain
|
|
if (
|
|
is_candidate_block(block, period_start)
|
|
# Ensure cannot move back to earlier deposit contract states
|
|
and get_eth1_data(block).deposit_count >= state.eth1_data.deposit_count
|
|
)
|
|
]
|
|
|
|
# Valid votes already cast during this period
|
|
valid_votes = [vote for vote in state.eth1_data_votes if vote in votes_to_consider]
|
|
|
|
# Default vote on latest eth1 block data in the period range unless eth1 chain is not live
|
|
# Non-substantive casting for linter
|
|
state_eth1_data: Eth1Data = state.eth1_data
|
|
default_vote = (
|
|
votes_to_consider[len(votes_to_consider) - 1] if any(votes_to_consider) else state_eth1_data
|
|
)
|
|
|
|
return max(
|
|
valid_votes,
|
|
# Tiebreak by smallest distance
|
|
key=lambda v: (
|
|
valid_votes.count(v),
|
|
-valid_votes.index(v),
|
|
),
|
|
default=default_vote,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_eth1_vote#electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_eth1_vote" fork="electra" hash="978c2389">
|
|
def get_eth1_vote(state: BeaconState, eth1_chain: Sequence[Eth1Block]) -> Eth1Data:
|
|
# [New in Electra:EIP6110]
|
|
if state.eth1_deposit_index == state.deposit_requests_start_index:
|
|
return state.eth1_data
|
|
|
|
period_start = voting_period_start_time(state)
|
|
# `eth1_chain` abstractly represents all blocks in the eth1 chain sorted by ascending block height
|
|
votes_to_consider = [
|
|
get_eth1_data(block)
|
|
for block in eth1_chain
|
|
if (
|
|
is_candidate_block(block, period_start)
|
|
# Ensure cannot move back to earlier deposit contract states
|
|
and get_eth1_data(block).deposit_count >= state.eth1_data.deposit_count
|
|
)
|
|
]
|
|
|
|
# Valid votes already cast during this period
|
|
valid_votes = [vote for vote in state.eth1_data_votes if vote in votes_to_consider]
|
|
|
|
# Default vote on latest eth1 block data in the period range unless eth1 chain is not live
|
|
# Non-substantive casting for linter
|
|
state_eth1_data: Eth1Data = state.eth1_data
|
|
default_vote = (
|
|
votes_to_consider[len(votes_to_consider) - 1] if any(votes_to_consider) else state_eth1_data
|
|
)
|
|
|
|
return max(
|
|
valid_votes,
|
|
# Tiebreak by smallest distance
|
|
key=lambda v: (
|
|
valid_votes.count(v),
|
|
-valid_votes.index(v),
|
|
),
|
|
default=default_vote,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_execution_payload
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_execution_payload" fork="bellatrix" hash="abcc202e">
|
|
def get_execution_payload(
|
|
payload_id: Optional[PayloadId], execution_engine: ExecutionEngine
|
|
) -> ExecutionPayload:
|
|
if payload_id is None:
|
|
# Pre-merge, empty payload
|
|
return ExecutionPayload()
|
|
else:
|
|
return execution_engine.get_payload(payload_id).execution_payload
|
|
</spec>
|
|
|
|
- name: get_execution_requests
|
|
sources:
|
|
- file: proto/engine/v1/electra.go
|
|
search: func (ebe *ExecutionBundleElectra) GetDecodedExecutionRequests(
|
|
spec: |
|
|
<spec fn="get_execution_requests" fork="electra" hash="2c0fb728">
|
|
def get_execution_requests(execution_requests_list: Sequence[bytes]) -> ExecutionRequests:
|
|
deposits = []
|
|
withdrawals = []
|
|
consolidations = []
|
|
|
|
request_types = [
|
|
DEPOSIT_REQUEST_TYPE,
|
|
WITHDRAWAL_REQUEST_TYPE,
|
|
CONSOLIDATION_REQUEST_TYPE,
|
|
]
|
|
|
|
prev_request_type = None
|
|
for request in execution_requests_list:
|
|
request_type, request_data = request[0:1], request[1:]
|
|
|
|
# Check that the request type is valid
|
|
assert request_type in request_types
|
|
# Check that the request data is not empty
|
|
assert len(request_data) != 0
|
|
# Check that requests are in strictly ascending order
|
|
# Each successive type must be greater than the last with no duplicates
|
|
assert prev_request_type is None or prev_request_type < request_type
|
|
prev_request_type = request_type
|
|
|
|
if request_type == DEPOSIT_REQUEST_TYPE:
|
|
deposits = ssz_deserialize(
|
|
List[DepositRequest, MAX_DEPOSIT_REQUESTS_PER_PAYLOAD], request_data
|
|
)
|
|
elif request_type == WITHDRAWAL_REQUEST_TYPE:
|
|
withdrawals = ssz_deserialize(
|
|
List[WithdrawalRequest, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD], request_data
|
|
)
|
|
elif request_type == CONSOLIDATION_REQUEST_TYPE:
|
|
consolidations = ssz_deserialize(
|
|
List[ConsolidationRequest, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD], request_data
|
|
)
|
|
|
|
return ExecutionRequests(
|
|
deposits=deposits,
|
|
withdrawals=withdrawals,
|
|
consolidations=consolidations,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_execution_requests_list
|
|
sources:
|
|
- file: proto/engine/v1/electra.go
|
|
search: func EncodeExecutionRequests(
|
|
spec: |
|
|
<spec fn="get_execution_requests_list" fork="electra" hash="0a0be93f">
|
|
def get_execution_requests_list(execution_requests: ExecutionRequests) -> Sequence[bytes]:
|
|
requests = [
|
|
(DEPOSIT_REQUEST_TYPE, execution_requests.deposits),
|
|
(WITHDRAWAL_REQUEST_TYPE, execution_requests.withdrawals),
|
|
(CONSOLIDATION_REQUEST_TYPE, execution_requests.consolidations),
|
|
]
|
|
|
|
return [
|
|
request_type + ssz_serialize(request_data)
|
|
for request_type, request_data in requests
|
|
if len(request_data) != 0
|
|
]
|
|
</spec>
|
|
|
|
- name: get_expected_withdrawals#capella
|
|
sources:
|
|
- file: beacon-chain/state/state-native/getters_withdrawal.go
|
|
search: func (b *BeaconState) ExpectedWithdrawals(
|
|
spec: |
|
|
<spec fn="get_expected_withdrawals" fork="capella" hash="d6a98c14">
|
|
def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal], uint64]:
|
|
withdrawal_index = state.next_withdrawal_index
|
|
withdrawals: List[Withdrawal] = []
|
|
|
|
# Get validators sweep withdrawals
|
|
validators_sweep_withdrawals, withdrawal_index, processed_validators_sweep_count = (
|
|
get_validators_sweep_withdrawals(state, withdrawal_index, withdrawals)
|
|
)
|
|
withdrawals.extend(validators_sweep_withdrawals)
|
|
|
|
return withdrawals, processed_validators_sweep_count
|
|
</spec>
|
|
|
|
- name: get_expected_withdrawals#electra
|
|
sources:
|
|
- file: beacon-chain/state/state-native/getters_withdrawal.go
|
|
search: func (b *BeaconState) ExpectedWithdrawals(
|
|
spec: |
|
|
<spec fn="get_expected_withdrawals" fork="electra" hash="cfce862b">
|
|
def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal], uint64, uint64]:
|
|
withdrawal_index = state.next_withdrawal_index
|
|
withdrawals: List[Withdrawal] = []
|
|
|
|
# [New in Electra:EIP7251]
|
|
# Get partial withdrawals
|
|
partial_withdrawals, withdrawal_index, processed_partial_withdrawals_count = (
|
|
get_pending_partial_withdrawals(state, withdrawal_index, withdrawals)
|
|
)
|
|
withdrawals.extend(partial_withdrawals)
|
|
|
|
# Get validators sweep withdrawals
|
|
validators_sweep_withdrawals, withdrawal_index, processed_validators_sweep_count = (
|
|
get_validators_sweep_withdrawals(state, withdrawal_index, withdrawals)
|
|
)
|
|
withdrawals.extend(validators_sweep_withdrawals)
|
|
|
|
# [Modified in Electra:EIP7251]
|
|
return withdrawals, processed_partial_withdrawals_count, processed_validators_sweep_count
|
|
</spec>
|
|
|
|
- name: get_filtered_block_tree
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_filtered_block_tree" fork="phase0" hash="31b703b4">
|
|
def get_filtered_block_tree(store: Store) -> Dict[Root, BeaconBlock]:
|
|
"""
|
|
Retrieve a filtered block tree from ``store``, only returning branches
|
|
whose leaf state's justified/finalized info agrees with that in ``store``.
|
|
"""
|
|
base = store.justified_checkpoint.root
|
|
blocks: Dict[Root, BeaconBlock] = {}
|
|
filter_block_tree(store, base, blocks)
|
|
return blocks
|
|
</spec>
|
|
|
|
- name: get_finality_delay
|
|
sources:
|
|
- file: beacon-chain/core/helpers/rewards_penalties.go
|
|
search: func FinalityDelay(
|
|
spec: |
|
|
<spec fn="get_finality_delay" fork="phase0" hash="df3ca52e">
|
|
def get_finality_delay(state: BeaconState) -> uint64:
|
|
return get_previous_epoch(state) - state.finalized_checkpoint.epoch
|
|
</spec>
|
|
|
|
- name: get_flag_index_deltas
|
|
sources:
|
|
- file: beacon-chain/core/altair/epoch_precompute.go
|
|
search: func AttestationsDelta(
|
|
spec: |
|
|
<spec fn="get_flag_index_deltas" fork="altair" hash="efadf009">
|
|
def get_flag_index_deltas(
|
|
state: BeaconState, flag_index: int
|
|
) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
|
"""
|
|
Return the deltas for a given ``flag_index`` by scanning through the participation flags.
|
|
"""
|
|
rewards = [Gwei(0)] * len(state.validators)
|
|
penalties = [Gwei(0)] * len(state.validators)
|
|
previous_epoch = get_previous_epoch(state)
|
|
unslashed_participating_indices = get_unslashed_participating_indices(
|
|
state, flag_index, previous_epoch
|
|
)
|
|
weight = PARTICIPATION_FLAG_WEIGHTS[flag_index]
|
|
unslashed_participating_balance = get_total_balance(state, unslashed_participating_indices)
|
|
unslashed_participating_increments = (
|
|
unslashed_participating_balance // EFFECTIVE_BALANCE_INCREMENT
|
|
)
|
|
active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
|
|
for index in get_eligible_validator_indices(state):
|
|
base_reward = get_base_reward(state, index)
|
|
if index in unslashed_participating_indices:
|
|
if not is_in_inactivity_leak(state):
|
|
reward_numerator = base_reward * weight * unslashed_participating_increments
|
|
rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR))
|
|
elif flag_index != TIMELY_HEAD_FLAG_INDEX:
|
|
penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR)
|
|
return rewards, penalties
|
|
</spec>
|
|
|
|
- name: get_forkchoice_store
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_forkchoice_store" fork="phase0" hash="aaa3553b">
|
|
def get_forkchoice_store(anchor_state: BeaconState, anchor_block: BeaconBlock) -> Store:
|
|
assert anchor_block.state_root == hash_tree_root(anchor_state)
|
|
anchor_root = hash_tree_root(anchor_block)
|
|
anchor_epoch = get_current_epoch(anchor_state)
|
|
justified_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root)
|
|
finalized_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root)
|
|
proposer_boost_root = Root()
|
|
return Store(
|
|
time=uint64(anchor_state.genesis_time + SECONDS_PER_SLOT * anchor_state.slot),
|
|
genesis_time=anchor_state.genesis_time,
|
|
justified_checkpoint=justified_checkpoint,
|
|
finalized_checkpoint=finalized_checkpoint,
|
|
unrealized_justified_checkpoint=justified_checkpoint,
|
|
unrealized_finalized_checkpoint=finalized_checkpoint,
|
|
proposer_boost_root=proposer_boost_root,
|
|
equivocating_indices=set(),
|
|
blocks={anchor_root: copy(anchor_block)},
|
|
block_states={anchor_root: copy(anchor_state)},
|
|
checkpoint_states={justified_checkpoint: copy(anchor_state)},
|
|
unrealized_justifications={anchor_root: justified_checkpoint},
|
|
)
|
|
</spec>
|
|
|
|
- name: get_head
|
|
sources:
|
|
- file: beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go
|
|
search: func (f *ForkChoice) Head(
|
|
spec: |
|
|
<spec fn="get_head" fork="phase0" hash="9fa2b1bd">
|
|
def get_head(store: Store) -> Root:
|
|
# Get filtered block tree that only includes viable branches
|
|
blocks = get_filtered_block_tree(store)
|
|
# Execute the LMD-GHOST fork choice
|
|
head = store.justified_checkpoint.root
|
|
while True:
|
|
children = [root for root in blocks.keys() if blocks[root].parent_root == head]
|
|
if len(children) == 0:
|
|
return head
|
|
# Sort by latest attesting balance with ties broken lexicographically
|
|
# Ties broken by favoring block with lexicographically higher root
|
|
head = max(children, key=lambda root: (get_weight(store, root), root))
|
|
</spec>
|
|
|
|
- name: get_head_deltas
|
|
sources:
|
|
- file: beacon-chain/core/epoch/precompute/reward_penalty.go
|
|
search: func AttestationsDelta(
|
|
spec: |
|
|
<spec fn="get_head_deltas" fork="phase0" hash="3e9b5111">
|
|
def get_head_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
|
"""
|
|
Return attester micro-rewards/penalties for head-vote for each validator.
|
|
"""
|
|
matching_head_attestations = get_matching_head_attestations(state, get_previous_epoch(state))
|
|
return get_attestation_component_deltas(state, matching_head_attestations)
|
|
</spec>
|
|
|
|
- name: get_inactivity_penalty_deltas#phase0
|
|
sources:
|
|
- file: beacon-chain/core/epoch/precompute/reward_penalty.go
|
|
search: func AttestationsDelta(
|
|
spec: |
|
|
<spec fn="get_inactivity_penalty_deltas" fork="phase0" hash="11718f1b">
|
|
def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
|
"""
|
|
Return inactivity reward/penalty deltas for each validator.
|
|
"""
|
|
penalties = [Gwei(0) for _ in range(len(state.validators))]
|
|
if is_in_inactivity_leak(state):
|
|
matching_target_attestations = get_matching_target_attestations(
|
|
state, get_previous_epoch(state)
|
|
)
|
|
matching_target_attesting_indices = get_unslashed_attesting_indices(
|
|
state, matching_target_attestations
|
|
)
|
|
for index in get_eligible_validator_indices(state):
|
|
# If validator is performing optimally this cancels all rewards for a neutral balance
|
|
base_reward = get_base_reward(state, index)
|
|
penalties[index] += Gwei(
|
|
BASE_REWARDS_PER_EPOCH * base_reward - get_proposer_reward(state, index)
|
|
)
|
|
if index not in matching_target_attesting_indices:
|
|
effective_balance = state.validators[index].effective_balance
|
|
penalties[index] += Gwei(
|
|
effective_balance * get_finality_delay(state) // INACTIVITY_PENALTY_QUOTIENT
|
|
)
|
|
|
|
# No rewards associated with inactivity penalties
|
|
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
|
return rewards, penalties
|
|
</spec>
|
|
|
|
- name: get_inactivity_penalty_deltas#altair
|
|
sources:
|
|
- file: beacon-chain/core/altair/epoch_precompute.go
|
|
search: func AttestationsDelta(
|
|
spec: |
|
|
<spec fn="get_inactivity_penalty_deltas" fork="altair" hash="1d27298b">
|
|
def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
|
"""
|
|
Return the inactivity penalty deltas by considering timely target participation flags and inactivity scores.
|
|
"""
|
|
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
|
penalties = [Gwei(0) for _ in range(len(state.validators))]
|
|
previous_epoch = get_previous_epoch(state)
|
|
matching_target_indices = get_unslashed_participating_indices(
|
|
state, TIMELY_TARGET_FLAG_INDEX, previous_epoch
|
|
)
|
|
for index in get_eligible_validator_indices(state):
|
|
if index not in matching_target_indices:
|
|
penalty_numerator = (
|
|
state.validators[index].effective_balance * state.inactivity_scores[index]
|
|
)
|
|
penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR
|
|
penalties[index] += Gwei(penalty_numerator // penalty_denominator)
|
|
return rewards, penalties
|
|
</spec>
|
|
|
|
- name: get_inactivity_penalty_deltas#bellatrix
|
|
sources:
|
|
- file: beacon-chain/core/altair/epoch_precompute.go
|
|
search: func AttestationsDelta(
|
|
spec: |
|
|
<spec fn="get_inactivity_penalty_deltas" fork="bellatrix" hash="b289e0cd">
|
|
def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
|
"""
|
|
Return the inactivity penalty deltas by considering timely target participation flags and inactivity scores.
|
|
"""
|
|
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
|
penalties = [Gwei(0) for _ in range(len(state.validators))]
|
|
previous_epoch = get_previous_epoch(state)
|
|
matching_target_indices = get_unslashed_participating_indices(
|
|
state, TIMELY_TARGET_FLAG_INDEX, previous_epoch
|
|
)
|
|
for index in get_eligible_validator_indices(state):
|
|
if index not in matching_target_indices:
|
|
penalty_numerator = (
|
|
state.validators[index].effective_balance * state.inactivity_scores[index]
|
|
)
|
|
# [Modified in Bellatrix]
|
|
penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_BELLATRIX
|
|
penalties[index] += Gwei(penalty_numerator // penalty_denominator)
|
|
return rewards, penalties
|
|
</spec>
|
|
|
|
- name: get_inclusion_delay_deltas
|
|
sources:
|
|
- file: beacon-chain/core/epoch/precompute/reward_penalty.go
|
|
search: func AttestationsDelta(
|
|
spec: |
|
|
<spec fn="get_inclusion_delay_deltas" fork="phase0" hash="aa690a2f">
|
|
def get_inclusion_delay_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
|
"""
|
|
Return proposer and inclusion delay micro-rewards/penalties for each validator.
|
|
"""
|
|
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
|
matching_source_attestations = get_matching_source_attestations(
|
|
state, get_previous_epoch(state)
|
|
)
|
|
for index in get_unslashed_attesting_indices(state, matching_source_attestations):
|
|
attestation = min(
|
|
[a for a in matching_source_attestations if index in get_attesting_indices(state, a)],
|
|
key=lambda a: a.inclusion_delay,
|
|
)
|
|
rewards[attestation.proposer_index] += get_proposer_reward(state, index)
|
|
max_attester_reward = Gwei(
|
|
get_base_reward(state, index) - get_proposer_reward(state, index)
|
|
)
|
|
rewards[index] += Gwei(max_attester_reward // attestation.inclusion_delay)
|
|
|
|
# No penalties associated with inclusion delay
|
|
penalties = [Gwei(0) for _ in range(len(state.validators))]
|
|
return rewards, penalties
|
|
</spec>
|
|
|
|
- name: get_index_for_new_validator
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_index_for_new_validator" fork="altair" hash="e78e8714">
|
|
def get_index_for_new_validator(state: BeaconState) -> ValidatorIndex:
|
|
return ValidatorIndex(len(state.validators))
|
|
</spec>
|
|
|
|
- name: get_indexed_attestation
|
|
sources:
|
|
- file: proto/prysm/v1alpha1/attestation/attestation_utils.go
|
|
search: func ConvertToIndexed(
|
|
spec: |
|
|
<spec fn="get_indexed_attestation" fork="phase0" hash="424c7a96">
|
|
def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> IndexedAttestation:
|
|
"""
|
|
Return the indexed attestation corresponding to ``attestation``.
|
|
"""
|
|
attesting_indices = get_attesting_indices(state, attestation)
|
|
|
|
return IndexedAttestation(
|
|
attesting_indices=sorted(attesting_indices),
|
|
data=attestation.data,
|
|
signature=attestation.signature,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_lc_execution_root#capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_lc_execution_root" fork="capella" hash="1fe004a9">
|
|
def get_lc_execution_root(header: LightClientHeader) -> Root:
|
|
epoch = compute_epoch_at_slot(header.beacon.slot)
|
|
|
|
if epoch >= CAPELLA_FORK_EPOCH:
|
|
return hash_tree_root(header.execution)
|
|
|
|
return Root()
|
|
</spec>
|
|
|
|
- name: get_lc_execution_root#deneb
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_lc_execution_root" fork="deneb" hash="311e3c71">
|
|
def get_lc_execution_root(header: LightClientHeader) -> Root:
|
|
epoch = compute_epoch_at_slot(header.beacon.slot)
|
|
|
|
# [New in Deneb]
|
|
if epoch >= DENEB_FORK_EPOCH:
|
|
return hash_tree_root(header.execution)
|
|
|
|
# [Modified in Deneb]
|
|
if epoch >= CAPELLA_FORK_EPOCH:
|
|
execution_header = capella.ExecutionPayloadHeader(
|
|
parent_hash=header.execution.parent_hash,
|
|
fee_recipient=header.execution.fee_recipient,
|
|
state_root=header.execution.state_root,
|
|
receipts_root=header.execution.receipts_root,
|
|
logs_bloom=header.execution.logs_bloom,
|
|
prev_randao=header.execution.prev_randao,
|
|
block_number=header.execution.block_number,
|
|
gas_limit=header.execution.gas_limit,
|
|
gas_used=header.execution.gas_used,
|
|
timestamp=header.execution.timestamp,
|
|
extra_data=header.execution.extra_data,
|
|
base_fee_per_gas=header.execution.base_fee_per_gas,
|
|
block_hash=header.execution.block_hash,
|
|
transactions_root=header.execution.transactions_root,
|
|
withdrawals_root=header.execution.withdrawals_root,
|
|
)
|
|
return hash_tree_root(execution_header)
|
|
|
|
return Root()
|
|
</spec>
|
|
|
|
- name: get_lc_execution_root#electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_lc_execution_root" fork="electra" hash="939f89bd">
|
|
def get_lc_execution_root(header: LightClientHeader) -> Root:
|
|
epoch = compute_epoch_at_slot(header.beacon.slot)
|
|
|
|
# [New in Electra]
|
|
if epoch >= ELECTRA_FORK_EPOCH:
|
|
return hash_tree_root(header.execution)
|
|
|
|
# [Modified in Electra]
|
|
if epoch >= DENEB_FORK_EPOCH:
|
|
execution_header = deneb.ExecutionPayloadHeader(
|
|
parent_hash=header.execution.parent_hash,
|
|
fee_recipient=header.execution.fee_recipient,
|
|
state_root=header.execution.state_root,
|
|
receipts_root=header.execution.receipts_root,
|
|
logs_bloom=header.execution.logs_bloom,
|
|
prev_randao=header.execution.prev_randao,
|
|
block_number=header.execution.block_number,
|
|
gas_limit=header.execution.gas_limit,
|
|
gas_used=header.execution.gas_used,
|
|
timestamp=header.execution.timestamp,
|
|
extra_data=header.execution.extra_data,
|
|
base_fee_per_gas=header.execution.base_fee_per_gas,
|
|
block_hash=header.execution.block_hash,
|
|
transactions_root=header.execution.transactions_root,
|
|
withdrawals_root=header.execution.withdrawals_root,
|
|
blob_gas_used=header.execution.blob_gas_used,
|
|
excess_blob_gas=header.execution.excess_blob_gas,
|
|
)
|
|
return hash_tree_root(execution_header)
|
|
|
|
if epoch >= CAPELLA_FORK_EPOCH:
|
|
execution_header = capella.ExecutionPayloadHeader(
|
|
parent_hash=header.execution.parent_hash,
|
|
fee_recipient=header.execution.fee_recipient,
|
|
state_root=header.execution.state_root,
|
|
receipts_root=header.execution.receipts_root,
|
|
logs_bloom=header.execution.logs_bloom,
|
|
prev_randao=header.execution.prev_randao,
|
|
block_number=header.execution.block_number,
|
|
gas_limit=header.execution.gas_limit,
|
|
gas_used=header.execution.gas_used,
|
|
timestamp=header.execution.timestamp,
|
|
extra_data=header.execution.extra_data,
|
|
base_fee_per_gas=header.execution.base_fee_per_gas,
|
|
block_hash=header.execution.block_hash,
|
|
transactions_root=header.execution.transactions_root,
|
|
withdrawals_root=header.execution.withdrawals_root,
|
|
)
|
|
return hash_tree_root(execution_header)
|
|
|
|
return Root()
|
|
</spec>
|
|
|
|
- name: get_matching_head_attestations
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_matching_head_attestations" fork="phase0" hash="9e3ca171">
|
|
def get_matching_head_attestations(
|
|
state: BeaconState, epoch: Epoch
|
|
) -> Sequence[PendingAttestation]:
|
|
return [
|
|
a
|
|
for a in get_matching_target_attestations(state, epoch)
|
|
if a.data.beacon_block_root == get_block_root_at_slot(state, a.data.slot)
|
|
]
|
|
</spec>
|
|
|
|
- name: get_matching_source_attestations
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_matching_source_attestations" fork="phase0" hash="4771b4df">
|
|
def get_matching_source_attestations(
|
|
state: BeaconState, epoch: Epoch
|
|
) -> Sequence[PendingAttestation]:
|
|
assert epoch in (get_previous_epoch(state), get_current_epoch(state))
|
|
return (
|
|
state.current_epoch_attestations
|
|
if epoch == get_current_epoch(state)
|
|
else state.previous_epoch_attestations
|
|
)
|
|
</spec>
|
|
|
|
- name: get_matching_target_attestations
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_matching_target_attestations" fork="phase0" hash="811f3459">
|
|
def get_matching_target_attestations(
|
|
state: BeaconState, epoch: Epoch
|
|
) -> Sequence[PendingAttestation]:
|
|
return [
|
|
a
|
|
for a in get_matching_source_attestations(state, epoch)
|
|
if a.data.target.root == get_block_root(state, epoch)
|
|
]
|
|
</spec>
|
|
|
|
- name: get_max_effective_balance
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func ValidatorMaxEffectiveBalance(
|
|
spec: |
|
|
<spec fn="get_max_effective_balance" fork="electra" hash="77f96872">
|
|
def get_max_effective_balance(validator: Validator) -> Gwei:
|
|
"""
|
|
Get max effective balance for ``validator``.
|
|
"""
|
|
if has_compounding_withdrawal_credential(validator):
|
|
return MAX_EFFECTIVE_BALANCE_ELECTRA
|
|
else:
|
|
return MIN_ACTIVATION_BALANCE
|
|
</spec>
|
|
|
|
- name: get_next_sync_committee
|
|
sources:
|
|
- file: beacon-chain/core/altair/sync_committee.go
|
|
search: func NextSyncCommittee(
|
|
spec: |
|
|
<spec fn="get_next_sync_committee" fork="altair" hash="ba4f5d3e">
|
|
def get_next_sync_committee(state: BeaconState) -> SyncCommittee:
|
|
"""
|
|
Return the next sync committee, with possible pubkey duplicates.
|
|
"""
|
|
indices = get_next_sync_committee_indices(state)
|
|
pubkeys = [state.validators[index].pubkey for index in indices]
|
|
aggregate_pubkey = eth_aggregate_pubkeys(pubkeys)
|
|
return SyncCommittee(pubkeys=pubkeys, aggregate_pubkey=aggregate_pubkey)
|
|
</spec>
|
|
|
|
- name: get_next_sync_committee_indices#altair
|
|
sources:
|
|
- file: beacon-chain/core/altair/sync_committee.go
|
|
search: func NextSyncCommitteeIndices(
|
|
spec: |
|
|
<spec fn="get_next_sync_committee_indices" fork="altair" hash="29891ee8">
|
|
def get_next_sync_committee_indices(state: BeaconState) -> Sequence[ValidatorIndex]:
|
|
"""
|
|
Return the sync committee indices, with possible duplicates, for the next sync committee.
|
|
"""
|
|
epoch = Epoch(get_current_epoch(state) + 1)
|
|
|
|
MAX_RANDOM_BYTE = 2**8 - 1
|
|
active_validator_indices = get_active_validator_indices(state, epoch)
|
|
active_validator_count = uint64(len(active_validator_indices))
|
|
seed = get_seed(state, epoch, DOMAIN_SYNC_COMMITTEE)
|
|
i = 0
|
|
sync_committee_indices: List[ValidatorIndex] = []
|
|
while len(sync_committee_indices) < SYNC_COMMITTEE_SIZE:
|
|
shuffled_index = compute_shuffled_index(
|
|
uint64(i % active_validator_count), active_validator_count, seed
|
|
)
|
|
candidate_index = active_validator_indices[shuffled_index]
|
|
random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32]
|
|
effective_balance = state.validators[candidate_index].effective_balance
|
|
if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
|
|
sync_committee_indices.append(candidate_index)
|
|
i += 1
|
|
return sync_committee_indices
|
|
</spec>
|
|
|
|
- name: get_next_sync_committee_indices#electra
|
|
sources:
|
|
- file: beacon-chain/core/altair/sync_committee.go
|
|
search: func NextSyncCommitteeIndices(
|
|
- file: beacon-chain/core/altair/sync_committee.go
|
|
search: if s.Version() >= version.Electra {
|
|
spec: |
|
|
<spec fn="get_next_sync_committee_indices" fork="electra" hash="2ecaf5da">
|
|
def get_next_sync_committee_indices(state: BeaconState) -> Sequence[ValidatorIndex]:
|
|
"""
|
|
Return the sync committee indices, with possible duplicates, for the next sync committee.
|
|
"""
|
|
epoch = Epoch(get_current_epoch(state) + 1)
|
|
|
|
# [Modified in Electra]
|
|
MAX_RANDOM_VALUE = 2**16 - 1
|
|
active_validator_indices = get_active_validator_indices(state, epoch)
|
|
active_validator_count = uint64(len(active_validator_indices))
|
|
seed = get_seed(state, epoch, DOMAIN_SYNC_COMMITTEE)
|
|
i = uint64(0)
|
|
sync_committee_indices: List[ValidatorIndex] = []
|
|
while len(sync_committee_indices) < SYNC_COMMITTEE_SIZE:
|
|
shuffled_index = compute_shuffled_index(
|
|
uint64(i % active_validator_count), active_validator_count, seed
|
|
)
|
|
candidate_index = active_validator_indices[shuffled_index]
|
|
# [Modified in Electra]
|
|
random_bytes = hash(seed + uint_to_bytes(i // 16))
|
|
offset = i % 16 * 2
|
|
random_value = bytes_to_uint64(random_bytes[offset : offset + 2])
|
|
effective_balance = state.validators[candidate_index].effective_balance
|
|
# [Modified in Electra:EIP7251]
|
|
if effective_balance * MAX_RANDOM_VALUE >= MAX_EFFECTIVE_BALANCE_ELECTRA * random_value:
|
|
sync_committee_indices.append(candidate_index)
|
|
i += 1
|
|
return sync_committee_indices
|
|
</spec>
|
|
|
|
- name: get_pending_balance_to_withdraw
|
|
sources:
|
|
- file: beacon-chain/state/state-native/getters_validator.go
|
|
search: func (b *BeaconState) PendingBalanceToWithdraw(
|
|
spec: |
|
|
<spec fn="get_pending_balance_to_withdraw" fork="electra" hash="3960d28f">
|
|
def get_pending_balance_to_withdraw(state: BeaconState, validator_index: ValidatorIndex) -> Gwei:
|
|
return sum(
|
|
withdrawal.amount
|
|
for withdrawal in state.pending_partial_withdrawals
|
|
if withdrawal.validator_index == validator_index
|
|
)
|
|
</spec>
|
|
|
|
- name: get_pow_block_at_terminal_total_difficulty
|
|
sources:
|
|
- file: beacon-chain/execution/engine_client.go
|
|
search: func (s *Service) GetTerminalBlockHash(
|
|
spec: |
|
|
<spec fn="get_pow_block_at_terminal_total_difficulty" fork="bellatrix" hash="7b6668d2">
|
|
def get_pow_block_at_terminal_total_difficulty(
|
|
pow_chain: Dict[Hash32, PowBlock],
|
|
) -> Optional[PowBlock]:
|
|
# `pow_chain` abstractly represents all blocks in the PoW chain
|
|
for block in pow_chain.values():
|
|
block_reached_ttd = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
|
if block_reached_ttd:
|
|
# If genesis block, no parent exists so reaching TTD alone qualifies as valid terminal block
|
|
if block.parent_hash == Hash32():
|
|
return block
|
|
parent = pow_chain[block.parent_hash]
|
|
parent_reached_ttd = parent.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
|
if not parent_reached_ttd:
|
|
return block
|
|
|
|
return None
|
|
</spec>
|
|
|
|
- name: get_previous_epoch
|
|
sources:
|
|
- file: beacon-chain/core/time/slot_epoch.go
|
|
search: func PrevEpoch(
|
|
spec: |
|
|
<spec fn="get_previous_epoch" fork="phase0" hash="935f3711">
|
|
def get_previous_epoch(state: BeaconState) -> Epoch:
|
|
"""`
|
|
Return the previous epoch (unless the current epoch is ``GENESIS_EPOCH``).
|
|
"""
|
|
current_epoch = get_current_epoch(state)
|
|
return GENESIS_EPOCH if current_epoch == GENESIS_EPOCH else Epoch(current_epoch - 1)
|
|
</spec>
|
|
|
|
- name: get_proposer_head
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_proposer_head" fork="phase0" hash="99e8fc05">
|
|
def get_proposer_head(store: Store, head_root: Root, slot: Slot) -> Root:
|
|
head_block = store.blocks[head_root]
|
|
parent_root = head_block.parent_root
|
|
parent_block = store.blocks[parent_root]
|
|
|
|
# Only re-org the head block if it arrived later than the attestation deadline.
|
|
head_late = is_head_late(store, head_root)
|
|
|
|
# Do not re-org on an epoch boundary where the proposer shuffling could change.
|
|
shuffling_stable = is_shuffling_stable(slot)
|
|
|
|
# Ensure that the FFG information of the new head will be competitive with the current head.
|
|
ffg_competitive = is_ffg_competitive(store, head_root, parent_root)
|
|
|
|
# Do not re-org if the chain is not finalizing with acceptable frequency.
|
|
finalization_ok = is_finalization_ok(store, slot)
|
|
|
|
# Only re-org if we are proposing on-time.
|
|
proposing_on_time = is_proposing_on_time(store)
|
|
|
|
# Only re-org a single slot at most.
|
|
parent_slot_ok = parent_block.slot + 1 == head_block.slot
|
|
current_time_ok = head_block.slot + 1 == slot
|
|
single_slot_reorg = parent_slot_ok and current_time_ok
|
|
|
|
# Check that the head has few enough votes to be overpowered by our proposer boost.
|
|
assert store.proposer_boost_root != head_root # ensure boost has worn off
|
|
head_weak = is_head_weak(store, head_root)
|
|
|
|
# Check that the missing votes are assigned to the parent and not being hoarded.
|
|
parent_strong = is_parent_strong(store, head_root)
|
|
|
|
# Re-org more aggressively if there is a proposer equivocation in the previous slot.
|
|
proposer_equivocation = is_proposer_equivocation(store, head_root)
|
|
|
|
if all(
|
|
[
|
|
head_late,
|
|
shuffling_stable,
|
|
ffg_competitive,
|
|
finalization_ok,
|
|
proposing_on_time,
|
|
single_slot_reorg,
|
|
head_weak,
|
|
parent_strong,
|
|
]
|
|
):
|
|
# We can re-org the current head by building upon its parent block.
|
|
return parent_root
|
|
elif all([head_weak, current_time_ok, proposer_equivocation]):
|
|
return parent_root
|
|
else:
|
|
return head_root
|
|
</spec>
|
|
|
|
- name: get_proposer_reward
|
|
sources:
|
|
- file: beacon-chain/core/epoch/precompute/reward_penalty.go
|
|
search: func ProposersDelta(
|
|
spec: |
|
|
<spec fn="get_proposer_reward" fork="phase0" hash="2ccc4963">
|
|
def get_proposer_reward(state: BeaconState, attesting_index: ValidatorIndex) -> Gwei:
|
|
return Gwei(get_base_reward(state, attesting_index) // PROPOSER_REWARD_QUOTIENT)
|
|
</spec>
|
|
|
|
- name: get_proposer_score
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_proposer_score" fork="phase0" hash="2c8d8a27">
|
|
def get_proposer_score(store: Store) -> Gwei:
|
|
justified_checkpoint_state = store.checkpoint_states[store.justified_checkpoint]
|
|
return compute_proposer_score(justified_checkpoint_state)
|
|
</spec>
|
|
|
|
- name: get_randao_mix
|
|
sources:
|
|
- file: beacon-chain/core/helpers/randao.go
|
|
search: func RandaoMix(
|
|
spec: |
|
|
<spec fn="get_randao_mix" fork="phase0" hash="c79ea866">
|
|
def get_randao_mix(state: BeaconState, epoch: Epoch) -> Bytes32:
|
|
"""
|
|
Return the randao mix at a recent ``epoch``.
|
|
"""
|
|
return state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR]
|
|
</spec>
|
|
|
|
- name: get_safety_threshold
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_safety_threshold" fork="altair" hash="72f3d7ff">
|
|
def get_safety_threshold(store: LightClientStore) -> uint64:
|
|
return (
|
|
max(
|
|
store.previous_max_active_participants,
|
|
store.current_max_active_participants,
|
|
)
|
|
// 2
|
|
)
|
|
</spec>
|
|
|
|
- name: get_seed
|
|
sources:
|
|
- file: beacon-chain/core/helpers/randao.go
|
|
search: func Seed(
|
|
spec: |
|
|
<spec fn="get_seed" fork="phase0" hash="b0921a0a">
|
|
def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Bytes32:
|
|
"""
|
|
Return the seed at ``epoch``.
|
|
"""
|
|
mix = get_randao_mix(
|
|
state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)
|
|
) # Avoid underflow
|
|
return hash(domain_type + uint_to_bytes(epoch) + mix)
|
|
</spec>
|
|
|
|
- name: get_slot_component_duration_ms
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_slot_component_duration_ms" fork="phase0" hash="b81504df">
|
|
def get_slot_component_duration_ms(basis_points: uint64) -> uint64:
|
|
"""
|
|
Calculate the duration of a slot component in milliseconds.
|
|
"""
|
|
return basis_points * SLOT_DURATION_MS // BASIS_POINTS
|
|
</spec>
|
|
|
|
- name: get_slot_signature
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_slot_signature" fork="phase0" hash="b28edd87">
|
|
def get_slot_signature(state: BeaconState, slot: Slot, privkey: int) -> BLSSignature:
|
|
domain = get_domain(state, DOMAIN_SELECTION_PROOF, compute_epoch_at_slot(slot))
|
|
signing_root = compute_signing_root(slot, domain)
|
|
return bls.Sign(privkey, signing_root)
|
|
</spec>
|
|
|
|
- name: get_slots_since_genesis
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func CurrentSlot(
|
|
spec: |
|
|
<spec fn="get_slots_since_genesis" fork="phase0" hash="3c697269">
|
|
def get_slots_since_genesis(store: Store) -> int:
|
|
return (store.time - store.genesis_time) // SECONDS_PER_SLOT
|
|
</spec>
|
|
|
|
- name: get_source_deltas
|
|
sources:
|
|
- file: beacon-chain/core/epoch/precompute/reward_penalty.go
|
|
search: func AttestationsDelta(
|
|
spec: |
|
|
<spec fn="get_source_deltas" fork="phase0" hash="af1e0223">
|
|
def get_source_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
|
"""
|
|
Return attester micro-rewards/penalties for source-vote for each validator.
|
|
"""
|
|
matching_source_attestations = get_matching_source_attestations(
|
|
state, get_previous_epoch(state)
|
|
)
|
|
return get_attestation_component_deltas(state, matching_source_attestations)
|
|
</spec>
|
|
|
|
- name: get_subtree_index
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_subtree_index" fork="altair" hash="74962089">
|
|
def get_subtree_index(generalized_index: GeneralizedIndex) -> uint64:
|
|
return uint64(generalized_index % 2 ** (floorlog2(generalized_index)))
|
|
</spec>
|
|
|
|
- name: get_sync_committee_message
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_sync_committee_message" fork="altair" hash="f5ff7ff2">
|
|
def get_sync_committee_message(
|
|
state: BeaconState, block_root: Root, validator_index: ValidatorIndex, privkey: int
|
|
) -> SyncCommitteeMessage:
|
|
epoch = get_current_epoch(state)
|
|
domain = get_domain(state, DOMAIN_SYNC_COMMITTEE, epoch)
|
|
signing_root = compute_signing_root(block_root, domain)
|
|
signature = bls.Sign(privkey, signing_root)
|
|
|
|
return SyncCommitteeMessage(
|
|
slot=state.slot,
|
|
beacon_block_root=block_root,
|
|
validator_index=validator_index,
|
|
signature=signature,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_sync_committee_selection_proof
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_sync_committee_selection_proof" fork="altair" hash="fa4e7704">
|
|
def get_sync_committee_selection_proof(
|
|
state: BeaconState, slot: Slot, subcommittee_index: uint64, privkey: int
|
|
) -> BLSSignature:
|
|
domain = get_domain(state, DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF, compute_epoch_at_slot(slot))
|
|
signing_data = SyncAggregatorSelectionData(
|
|
slot=slot,
|
|
subcommittee_index=subcommittee_index,
|
|
)
|
|
signing_root = compute_signing_root(signing_data, domain)
|
|
return bls.Sign(privkey, signing_root)
|
|
</spec>
|
|
|
|
- name: get_sync_subcommittee_pubkeys
|
|
sources:
|
|
- file: beacon-chain/core/altair/sync_committee.go
|
|
search: func SyncSubCommitteePubkeys(
|
|
spec: |
|
|
<spec fn="get_sync_subcommittee_pubkeys" fork="altair" hash="772addda">
|
|
def get_sync_subcommittee_pubkeys(
|
|
state: BeaconState, subcommittee_index: uint64
|
|
) -> Sequence[BLSPubkey]:
|
|
# Committees assigned to `slot` sign for `slot - 1`
|
|
# This creates the exceptional logic below when transitioning between sync committee periods
|
|
next_slot_epoch = compute_epoch_at_slot(Slot(state.slot + 1))
|
|
if compute_sync_committee_period(get_current_epoch(state)) == compute_sync_committee_period(
|
|
next_slot_epoch
|
|
):
|
|
sync_committee = state.current_sync_committee
|
|
else:
|
|
sync_committee = state.next_sync_committee
|
|
|
|
# Return pubkeys for the subcommittee index
|
|
sync_subcommittee_size = SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT
|
|
i = subcommittee_index * sync_subcommittee_size
|
|
return sync_committee.pubkeys[i : i + sync_subcommittee_size]
|
|
</spec>
|
|
|
|
- name: get_target_deltas
|
|
sources:
|
|
- file: beacon-chain/core/epoch/precompute/reward_penalty.go
|
|
search: func AttestationsDelta(
|
|
spec: |
|
|
<spec fn="get_target_deltas" fork="phase0" hash="2e16c38d">
|
|
def get_target_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
|
"""
|
|
Return attester micro-rewards/penalties for target-vote for each validator.
|
|
"""
|
|
matching_target_attestations = get_matching_target_attestations(
|
|
state, get_previous_epoch(state)
|
|
)
|
|
return get_attestation_component_deltas(state, matching_target_attestations)
|
|
</spec>
|
|
|
|
- name: get_terminal_pow_block
|
|
sources:
|
|
- file: beacon-chain/rpc/prysm/v1alpha1/validator/proposer_execution_payload.go
|
|
search: func (vs *Server) getTerminalBlockHashIfExists(
|
|
spec: |
|
|
<spec fn="get_terminal_pow_block" fork="bellatrix" hash="90e145ca">
|
|
def get_terminal_pow_block(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlock]:
|
|
if TERMINAL_BLOCK_HASH != Hash32():
|
|
# Terminal block hash override takes precedence over terminal total difficulty
|
|
if TERMINAL_BLOCK_HASH in pow_chain:
|
|
return pow_chain[TERMINAL_BLOCK_HASH]
|
|
else:
|
|
return None
|
|
|
|
return get_pow_block_at_terminal_total_difficulty(pow_chain)
|
|
</spec>
|
|
|
|
- name: get_total_active_balance
|
|
sources:
|
|
- file: beacon-chain/core/helpers/rewards_penalties.go
|
|
search: func TotalActiveBalance(
|
|
spec: |
|
|
<spec fn="get_total_active_balance" fork="phase0" hash="500e8576">
|
|
def get_total_active_balance(state: BeaconState) -> Gwei:
|
|
"""
|
|
Return the combined effective balance of the active validators.
|
|
Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
|
|
"""
|
|
return get_total_balance(
|
|
state, set(get_active_validator_indices(state, get_current_epoch(state)))
|
|
)
|
|
</spec>
|
|
|
|
- name: get_total_balance
|
|
sources:
|
|
- file: beacon-chain/core/helpers/rewards_penalties.go
|
|
search: func TotalBalance(
|
|
spec: |
|
|
<spec fn="get_total_balance" fork="phase0" hash="32fd5ad7">
|
|
def get_total_balance(state: BeaconState, indices: Set[ValidatorIndex]) -> Gwei:
|
|
"""
|
|
Return the combined effective balance of the ``indices``.
|
|
``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
|
|
Math safe up to ~10B ETH, after which this overflows uint64.
|
|
"""
|
|
return Gwei(
|
|
max(
|
|
EFFECTIVE_BALANCE_INCREMENT,
|
|
sum([state.validators[index].effective_balance for index in indices]),
|
|
)
|
|
)
|
|
</spec>
|
|
|
|
- name: get_unslashed_attesting_indices
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_unslashed_attesting_indices" fork="phase0" hash="21c7dd12">
|
|
def get_unslashed_attesting_indices(
|
|
state: BeaconState, attestations: Sequence[PendingAttestation]
|
|
) -> Set[ValidatorIndex]:
|
|
output: Set[ValidatorIndex] = set()
|
|
for a in attestations:
|
|
output = output.union(get_attesting_indices(state, a))
|
|
return set(filter(lambda index: not state.validators[index].slashed, output))
|
|
</spec>
|
|
|
|
- name: get_unslashed_participating_indices
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_unslashed_participating_indices" fork="altair" hash="8a8aae5f">
|
|
def get_unslashed_participating_indices(
|
|
state: BeaconState, flag_index: int, epoch: Epoch
|
|
) -> Set[ValidatorIndex]:
|
|
"""
|
|
Return the set of validator indices that are both active and unslashed for the given ``flag_index`` and ``epoch``.
|
|
"""
|
|
assert epoch in (get_previous_epoch(state), get_current_epoch(state))
|
|
if epoch == get_current_epoch(state):
|
|
epoch_participation = state.current_epoch_participation
|
|
else:
|
|
epoch_participation = state.previous_epoch_participation
|
|
active_validator_indices = get_active_validator_indices(state, epoch)
|
|
participating_indices = [
|
|
i for i in active_validator_indices if has_flag(epoch_participation[i], flag_index)
|
|
]
|
|
return set(filter(lambda index: not state.validators[index].slashed, participating_indices))
|
|
</spec>
|
|
|
|
- name: get_validator_activation_churn_limit
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func ValidatorActivationChurnLimitDeneb(
|
|
spec: |
|
|
<spec fn="get_validator_activation_churn_limit" fork="deneb" hash="cfc9e54a">
|
|
def get_validator_activation_churn_limit(state: BeaconState) -> uint64:
|
|
"""
|
|
Return the validator activation churn limit for the current epoch.
|
|
"""
|
|
return min(MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, get_validator_churn_limit(state))
|
|
</spec>
|
|
|
|
- name: get_validator_churn_limit
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func ValidatorExitChurnLimit(
|
|
spec: |
|
|
<spec fn="get_validator_churn_limit" fork="phase0" hash="76f815b3">
|
|
def get_validator_churn_limit(state: BeaconState) -> uint64:
|
|
"""
|
|
Return the validator churn limit for the current epoch.
|
|
"""
|
|
active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
|
|
return max(
|
|
MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT
|
|
)
|
|
</spec>
|
|
|
|
- name: get_validator_from_deposit#phase0
|
|
sources:
|
|
- file: beacon-chain/core/altair/deposit.go
|
|
search: func GetValidatorFromDeposit(
|
|
spec: |
|
|
<spec fn="get_validator_from_deposit" fork="phase0" hash="d267aa31">
|
|
def get_validator_from_deposit(
|
|
pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64
|
|
) -> Validator:
|
|
effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)
|
|
|
|
return Validator(
|
|
pubkey=pubkey,
|
|
withdrawal_credentials=withdrawal_credentials,
|
|
effective_balance=effective_balance,
|
|
slashed=False,
|
|
activation_eligibility_epoch=FAR_FUTURE_EPOCH,
|
|
activation_epoch=FAR_FUTURE_EPOCH,
|
|
exit_epoch=FAR_FUTURE_EPOCH,
|
|
withdrawable_epoch=FAR_FUTURE_EPOCH,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_validator_from_deposit#electra
|
|
sources:
|
|
- file: beacon-chain/core/electra/deposits.go
|
|
search: func GetValidatorFromDeposit(
|
|
spec: |
|
|
<spec fn="get_validator_from_deposit" fork="electra" hash="b1164a28">
|
|
def get_validator_from_deposit(
|
|
pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64
|
|
) -> Validator:
|
|
validator = Validator(
|
|
pubkey=pubkey,
|
|
withdrawal_credentials=withdrawal_credentials,
|
|
effective_balance=Gwei(0),
|
|
slashed=False,
|
|
activation_eligibility_epoch=FAR_FUTURE_EPOCH,
|
|
activation_epoch=FAR_FUTURE_EPOCH,
|
|
exit_epoch=FAR_FUTURE_EPOCH,
|
|
withdrawable_epoch=FAR_FUTURE_EPOCH,
|
|
)
|
|
|
|
# [Modified in Electra:EIP7251]
|
|
max_effective_balance = get_max_effective_balance(validator)
|
|
validator.effective_balance = min(
|
|
amount - amount % EFFECTIVE_BALANCE_INCREMENT, max_effective_balance
|
|
)
|
|
|
|
return validator
|
|
</spec>
|
|
|
|
- name: get_validators_custody_requirement
|
|
sources:
|
|
- file: beacon-chain/core/peerdas/validator.go
|
|
search: func ValidatorsCustodyRequirement(
|
|
spec: |
|
|
<spec fn="get_validators_custody_requirement" fork="fulu" hash="b2f47d03">
|
|
def get_validators_custody_requirement(
|
|
state: BeaconState, validator_indices: Sequence[ValidatorIndex]
|
|
) -> uint64:
|
|
total_node_balance = sum(
|
|
state.validators[index].effective_balance for index in validator_indices
|
|
)
|
|
count = total_node_balance // BALANCE_PER_ADDITIONAL_CUSTODY_GROUP
|
|
return min(max(count, VALIDATOR_CUSTODY_REQUIREMENT), NUMBER_OF_CUSTODY_GROUPS)
|
|
</spec>
|
|
|
|
- name: get_voting_source
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_voting_source" fork="phase0" hash="0af91bae">
|
|
def get_voting_source(store: Store, block_root: Root) -> Checkpoint:
|
|
"""
|
|
Compute the voting source checkpoint in event that block with root ``block_root`` is the head block
|
|
"""
|
|
block = store.blocks[block_root]
|
|
current_epoch = get_current_store_epoch(store)
|
|
block_epoch = compute_epoch_at_slot(block.slot)
|
|
if current_epoch > block_epoch:
|
|
# The block is from a prior epoch, the voting source will be pulled-up
|
|
return store.unrealized_justifications[block_root]
|
|
else:
|
|
# The block is not from a prior epoch, therefore the voting source is not pulled up
|
|
head_state = store.block_states[block_root]
|
|
return head_state.current_justified_checkpoint
|
|
</spec>
|
|
|
|
- name: get_weight
|
|
sources:
|
|
- file: beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go
|
|
search: func (f *ForkChoice) Weight(
|
|
spec: |
|
|
<spec fn="get_weight" fork="phase0" hash="b18bf25c">
|
|
def get_weight(store: Store, root: Root) -> Gwei:
|
|
state = store.checkpoint_states[store.justified_checkpoint]
|
|
attestation_score = get_attestation_score(store, root, state)
|
|
if store.proposer_boost_root == Root():
|
|
# Return only attestation score if ``proposer_boost_root`` is not set
|
|
return attestation_score
|
|
|
|
# Calculate proposer score if ``proposer_boost_root`` is set
|
|
proposer_score = Gwei(0)
|
|
# Boost is applied if ``root`` is an ancestor of ``proposer_boost_root``
|
|
if get_ancestor(store, store.proposer_boost_root, store.blocks[root].slot) == root:
|
|
proposer_score = get_proposer_score(store)
|
|
return attestation_score + proposer_score
|
|
</spec>
|
|
|
|
- name: has_compounding_withdrawal_credential
|
|
sources:
|
|
- file: beacon-chain/state/state-native/readonly_validator.go
|
|
search: func (v readOnlyValidator) HasCompoundingWithdrawalCredentials(
|
|
spec: |
|
|
<spec fn="has_compounding_withdrawal_credential" fork="electra" hash="a83f0327">
|
|
def has_compounding_withdrawal_credential(validator: Validator) -> bool:
|
|
"""
|
|
Check if ``validator`` has an 0x02 prefixed "compounding" withdrawal credential.
|
|
"""
|
|
return is_compounding_withdrawal_credential(validator.withdrawal_credentials)
|
|
</spec>
|
|
|
|
- name: has_eth1_withdrawal_credential
|
|
sources:
|
|
- file: beacon-chain/state/state-native/readonly_validator.go
|
|
search: func (v readOnlyValidator) HasETH1WithdrawalCredentials(
|
|
spec: |
|
|
<spec fn="has_eth1_withdrawal_credential" fork="capella" hash="184688ed">
|
|
def has_eth1_withdrawal_credential(validator: Validator) -> bool:
|
|
"""
|
|
Check if ``validator`` has an 0x01 prefixed "eth1" withdrawal credential.
|
|
"""
|
|
return validator.withdrawal_credentials[:1] == ETH1_ADDRESS_WITHDRAWAL_PREFIX
|
|
</spec>
|
|
|
|
- name: has_execution_withdrawal_credential
|
|
sources:
|
|
- file: beacon-chain/state/state-native/readonly_validator.go
|
|
search: func (v readOnlyValidator) HasExecutionWithdrawalCredentials(
|
|
spec: |
|
|
<spec fn="has_execution_withdrawal_credential" fork="electra" hash="afc14c81">
|
|
def has_execution_withdrawal_credential(validator: Validator) -> bool:
|
|
"""
|
|
Check if ``validator`` has a 0x01 or 0x02 prefixed withdrawal credential.
|
|
"""
|
|
return (
|
|
has_eth1_withdrawal_credential(validator) # 0x01
|
|
or has_compounding_withdrawal_credential(validator) # 0x02
|
|
)
|
|
</spec>
|
|
|
|
- name: has_flag
|
|
sources:
|
|
- file: beacon-chain/core/altair/attestation.go
|
|
search: func HasValidatorFlag(
|
|
spec: |
|
|
<spec fn="has_flag" fork="altair" hash="313bedd9">
|
|
def has_flag(flags: ParticipationFlags, flag_index: int) -> bool:
|
|
"""
|
|
Return whether ``flags`` has ``flag_index`` set.
|
|
"""
|
|
flag = ParticipationFlags(2**flag_index)
|
|
return flags & flag == flag
|
|
</spec>
|
|
|
|
- name: increase_balance
|
|
sources:
|
|
- file: beacon-chain/core/helpers/rewards_penalties.go
|
|
search: func IncreaseBalance(
|
|
spec: |
|
|
<spec fn="increase_balance" fork="phase0" hash="33ddd025">
|
|
def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
|
|
"""
|
|
Increase the validator balance at index ``index`` by ``delta``.
|
|
"""
|
|
state.balances[index] += delta
|
|
</spec>
|
|
|
|
- name: initialize_beacon_state_from_eth1
|
|
sources:
|
|
- file: beacon-chain/core/transition/state.go
|
|
search: func GenesisBeaconState(
|
|
spec: |
|
|
<spec fn="initialize_beacon_state_from_eth1" fork="phase0" hash="d3a0ddd4">
|
|
def initialize_beacon_state_from_eth1(
|
|
eth1_block_hash: Hash32, eth1_timestamp: uint64, deposits: Sequence[Deposit]
|
|
) -> BeaconState:
|
|
fork = Fork(
|
|
previous_version=GENESIS_FORK_VERSION,
|
|
current_version=GENESIS_FORK_VERSION,
|
|
epoch=GENESIS_EPOCH,
|
|
)
|
|
state = BeaconState(
|
|
genesis_time=eth1_timestamp + GENESIS_DELAY,
|
|
fork=fork,
|
|
eth1_data=Eth1Data(deposit_count=uint64(len(deposits)), block_hash=eth1_block_hash),
|
|
latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
|
|
randao_mixes=[eth1_block_hash]
|
|
* EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy
|
|
)
|
|
|
|
# Process deposits
|
|
leaves = list(map(lambda deposit: deposit.data, deposits))
|
|
for index, deposit in enumerate(deposits):
|
|
deposit_data_list = List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[: index + 1])
|
|
state.eth1_data.deposit_root = hash_tree_root(deposit_data_list)
|
|
process_deposit(state, deposit)
|
|
|
|
# Process activations
|
|
for index, validator in enumerate(state.validators):
|
|
balance = state.balances[index]
|
|
validator.effective_balance = min(
|
|
balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE
|
|
)
|
|
if validator.effective_balance == MAX_EFFECTIVE_BALANCE:
|
|
validator.activation_eligibility_epoch = GENESIS_EPOCH
|
|
validator.activation_epoch = GENESIS_EPOCH
|
|
|
|
# Set genesis validators root for domain separation and chain versioning
|
|
state.genesis_validators_root = hash_tree_root(state.validators)
|
|
|
|
return state
|
|
</spec>
|
|
|
|
- name: initialize_light_client_store
|
|
sources:
|
|
- file: beacon-chain/light-client/store.go
|
|
search: func NewLightClientStore(
|
|
spec: |
|
|
<spec fn="initialize_light_client_store" fork="altair" hash="f3de55b9">
|
|
def initialize_light_client_store(
|
|
trusted_block_root: Root, bootstrap: LightClientBootstrap
|
|
) -> LightClientStore:
|
|
assert is_valid_light_client_header(bootstrap.header)
|
|
assert hash_tree_root(bootstrap.header.beacon) == trusted_block_root
|
|
|
|
assert is_valid_normalized_merkle_branch(
|
|
leaf=hash_tree_root(bootstrap.current_sync_committee),
|
|
branch=bootstrap.current_sync_committee_branch,
|
|
gindex=current_sync_committee_gindex_at_slot(bootstrap.header.beacon.slot),
|
|
root=bootstrap.header.beacon.state_root,
|
|
)
|
|
|
|
return LightClientStore(
|
|
finalized_header=bootstrap.header,
|
|
current_sync_committee=bootstrap.current_sync_committee,
|
|
next_sync_committee=SyncCommittee(),
|
|
best_valid_update=None,
|
|
optimistic_header=bootstrap.header,
|
|
previous_max_active_participants=0,
|
|
current_max_active_participants=0,
|
|
)
|
|
</spec>
|
|
|
|
- name: initialize_proposer_lookahead
|
|
sources:
|
|
- file: beacon-chain/core/helpers/beacon_committee.go
|
|
search: func InitializeProposerLookahead(
|
|
spec: |
|
|
<spec fn="initialize_proposer_lookahead" fork="fulu" hash="58f9a307">
|
|
def initialize_proposer_lookahead(
|
|
state: electra.BeaconState,
|
|
) -> Vector[ValidatorIndex, (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH]:
|
|
"""
|
|
Return the proposer indices for the full available lookahead starting from current epoch.
|
|
Used to initialize the ``proposer_lookahead`` field in the beacon state at genesis and after forks.
|
|
"""
|
|
current_epoch = get_current_epoch(state)
|
|
lookahead = []
|
|
for i in range(MIN_SEED_LOOKAHEAD + 1):
|
|
lookahead.extend(get_beacon_proposer_indices(state, Epoch(current_epoch + i)))
|
|
return lookahead
|
|
</spec>
|
|
|
|
- name: initiate_validator_exit#phase0
|
|
sources:
|
|
- file: beacon-chain/core/validators/validator.go
|
|
search: func InitiateValidatorExit(
|
|
spec: |
|
|
<spec fn="initiate_validator_exit" fork="phase0" hash="0483a058">
|
|
def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
|
|
"""
|
|
Initiate the exit of the validator with index ``index``.
|
|
"""
|
|
# Return if validator already initiated exit
|
|
validator = state.validators[index]
|
|
if validator.exit_epoch != FAR_FUTURE_EPOCH:
|
|
return
|
|
|
|
# Compute exit queue epoch
|
|
exit_epochs = [v.exit_epoch for v in state.validators if v.exit_epoch != FAR_FUTURE_EPOCH]
|
|
exit_queue_epoch = max(exit_epochs + [compute_activation_exit_epoch(get_current_epoch(state))])
|
|
exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch])
|
|
if exit_queue_churn >= get_validator_churn_limit(state):
|
|
exit_queue_epoch += Epoch(1)
|
|
|
|
# Set validator exit epoch and withdrawable epoch
|
|
validator.exit_epoch = exit_queue_epoch
|
|
validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY)
|
|
</spec>
|
|
|
|
- name: initiate_validator_exit#electra
|
|
sources:
|
|
- file: beacon-chain/core/validators/validator.go
|
|
search: func InitiateValidatorExit(
|
|
spec: |
|
|
<spec fn="initiate_validator_exit" fork="electra" hash="0adfd988">
|
|
def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
|
|
"""
|
|
Initiate the exit of the validator with index ``index``.
|
|
"""
|
|
# Return if validator already initiated exit
|
|
validator = state.validators[index]
|
|
if validator.exit_epoch != FAR_FUTURE_EPOCH:
|
|
return
|
|
|
|
# Compute exit queue epoch [Modified in Electra:EIP7251]
|
|
exit_queue_epoch = compute_exit_epoch_and_update_churn(state, validator.effective_balance)
|
|
|
|
# Set validator exit epoch and withdrawable epoch
|
|
validator.exit_epoch = exit_queue_epoch
|
|
validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY)
|
|
</spec>
|
|
|
|
- name: integer_squareroot
|
|
sources:
|
|
- file: math/math_helper.go
|
|
search: func IntegerSquareRoot(
|
|
spec: |
|
|
<spec fn="integer_squareroot" fork="phase0" hash="f99da6e7">
|
|
def integer_squareroot(n: uint64) -> uint64:
|
|
"""
|
|
Return the largest integer ``x`` such that ``x**2 <= n``.
|
|
"""
|
|
if n == UINT64_MAX:
|
|
return UINT64_MAX_SQRT
|
|
x = n
|
|
y = (x + 1) // 2
|
|
while y < x:
|
|
x = y
|
|
y = (x + n // x) // 2
|
|
return x
|
|
</spec>
|
|
|
|
- name: is_active_validator
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func IsActiveValidator(
|
|
spec: |
|
|
<spec fn="is_active_validator" fork="phase0" hash="f8673b09">
|
|
def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
|
|
"""
|
|
Check if ``validator`` is active.
|
|
"""
|
|
return validator.activation_epoch <= epoch < validator.exit_epoch
|
|
</spec>
|
|
|
|
- name: is_aggregator
|
|
sources:
|
|
- file: beacon-chain/core/helpers/attestation.go
|
|
search: func IsAggregator(
|
|
spec: |
|
|
<spec fn="is_aggregator" fork="phase0" hash="20aea893">
|
|
def is_aggregator(
|
|
state: BeaconState, slot: Slot, index: CommitteeIndex, slot_signature: BLSSignature
|
|
) -> bool:
|
|
committee = get_beacon_committee(state, slot, index)
|
|
modulo = max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE)
|
|
return bytes_to_uint64(hash(slot_signature)[0:8]) % modulo == 0
|
|
</spec>
|
|
|
|
- name: is_assigned_to_sync_committee
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_assigned_to_sync_committee" fork="altair" hash="b83187d8">
|
|
def is_assigned_to_sync_committee(
|
|
state: BeaconState, epoch: Epoch, validator_index: ValidatorIndex
|
|
) -> bool:
|
|
sync_committee_period = compute_sync_committee_period(epoch)
|
|
current_epoch = get_current_epoch(state)
|
|
current_sync_committee_period = compute_sync_committee_period(current_epoch)
|
|
next_sync_committee_period = current_sync_committee_period + 1
|
|
assert sync_committee_period in (current_sync_committee_period, next_sync_committee_period)
|
|
|
|
pubkey = state.validators[validator_index].pubkey
|
|
if sync_committee_period == current_sync_committee_period:
|
|
return pubkey in state.current_sync_committee.pubkeys
|
|
else: # sync_committee_period == next_sync_committee_period
|
|
return pubkey in state.next_sync_committee.pubkeys
|
|
</spec>
|
|
|
|
- name: is_better_update
|
|
sources:
|
|
- file: beacon-chain/light-client/lightclient.go
|
|
search: func IsBetterUpdate(
|
|
spec: |
|
|
<spec fn="is_better_update" fork="altair" hash="b99582ff">
|
|
def is_better_update(new_update: LightClientUpdate, old_update: LightClientUpdate) -> bool:
|
|
# Compare supermajority (> 2/3) sync committee participation
|
|
max_active_participants = len(new_update.sync_aggregate.sync_committee_bits)
|
|
new_num_active_participants = sum(new_update.sync_aggregate.sync_committee_bits)
|
|
old_num_active_participants = sum(old_update.sync_aggregate.sync_committee_bits)
|
|
new_has_supermajority = new_num_active_participants * 3 >= max_active_participants * 2
|
|
old_has_supermajority = old_num_active_participants * 3 >= max_active_participants * 2
|
|
if new_has_supermajority != old_has_supermajority:
|
|
return new_has_supermajority
|
|
if not new_has_supermajority and new_num_active_participants != old_num_active_participants:
|
|
return new_num_active_participants > old_num_active_participants
|
|
|
|
# Compare presence of relevant sync committee
|
|
new_has_relevant_sync_committee = is_sync_committee_update(new_update) and (
|
|
compute_sync_committee_period_at_slot(new_update.attested_header.beacon.slot)
|
|
== compute_sync_committee_period_at_slot(new_update.signature_slot)
|
|
)
|
|
old_has_relevant_sync_committee = is_sync_committee_update(old_update) and (
|
|
compute_sync_committee_period_at_slot(old_update.attested_header.beacon.slot)
|
|
== compute_sync_committee_period_at_slot(old_update.signature_slot)
|
|
)
|
|
if new_has_relevant_sync_committee != old_has_relevant_sync_committee:
|
|
return new_has_relevant_sync_committee
|
|
|
|
# Compare indication of any finality
|
|
new_has_finality = is_finality_update(new_update)
|
|
old_has_finality = is_finality_update(old_update)
|
|
if new_has_finality != old_has_finality:
|
|
return new_has_finality
|
|
|
|
# Compare sync committee finality
|
|
if new_has_finality:
|
|
new_has_sync_committee_finality = compute_sync_committee_period_at_slot(
|
|
new_update.finalized_header.beacon.slot
|
|
) == compute_sync_committee_period_at_slot(new_update.attested_header.beacon.slot)
|
|
old_has_sync_committee_finality = compute_sync_committee_period_at_slot(
|
|
old_update.finalized_header.beacon.slot
|
|
) == compute_sync_committee_period_at_slot(old_update.attested_header.beacon.slot)
|
|
if new_has_sync_committee_finality != old_has_sync_committee_finality:
|
|
return new_has_sync_committee_finality
|
|
|
|
# Tiebreaker 1: Sync committee participation beyond supermajority
|
|
if new_num_active_participants != old_num_active_participants:
|
|
return new_num_active_participants > old_num_active_participants
|
|
|
|
# Tiebreaker 2: Prefer older data (fewer changes to best)
|
|
if new_update.attested_header.beacon.slot != old_update.attested_header.beacon.slot:
|
|
return new_update.attested_header.beacon.slot < old_update.attested_header.beacon.slot
|
|
|
|
# Tiebreaker 3: Prefer updates with earlier signature slots
|
|
return new_update.signature_slot < old_update.signature_slot
|
|
</spec>
|
|
|
|
- name: is_candidate_block
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_candidate_block" fork="phase0" hash="c588dc0a">
|
|
def is_candidate_block(block: Eth1Block, period_start: uint64) -> bool:
|
|
return (
|
|
block.timestamp + SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE <= period_start
|
|
and block.timestamp + SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE * 2 >= period_start
|
|
)
|
|
</spec>
|
|
|
|
- name: is_compounding_withdrawal_credential
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_compounding_withdrawal_credential" fork="electra" hash="5f97fbb0">
|
|
def is_compounding_withdrawal_credential(withdrawal_credentials: Bytes32) -> bool:
|
|
return withdrawal_credentials[:1] == COMPOUNDING_WITHDRAWAL_PREFIX
|
|
</spec>
|
|
|
|
- name: is_data_available#deneb
|
|
sources:
|
|
- file: beacon-chain/blockchain/process_block.go
|
|
search: func (s *Service) isDataAvailable(
|
|
- file: beacon-chain/blockchain/process_block.go
|
|
search: func (s *Service) areBlobsAvailable(
|
|
spec: |
|
|
<spec fn="is_data_available" fork="deneb" hash="98ad45cf">
|
|
def is_data_available(
|
|
beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]
|
|
) -> bool:
|
|
# `retrieve_blobs_and_proofs` is implementation and context dependent
|
|
# It returns all the blobs for the given block root, and raises an exception if not available
|
|
# Note: the p2p network does not guarantee sidecar retrieval outside of
|
|
# `MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS`
|
|
blobs, proofs = retrieve_blobs_and_proofs(beacon_block_root)
|
|
|
|
return verify_blob_kzg_proof_batch(blobs, blob_kzg_commitments, proofs)
|
|
</spec>
|
|
|
|
- name: is_data_available#fulu
|
|
sources:
|
|
- file: beacon-chain/blockchain/process_block.go
|
|
search: func (s *Service) isDataAvailable(
|
|
- file: beacon-chain/blockchain/process_block.go
|
|
search: func (s *Service) areDataColumnsAvailable(
|
|
spec: |
|
|
<spec fn="is_data_available" fork="fulu" hash="2062e9c6">
|
|
def is_data_available(beacon_block_root: Root) -> bool:
|
|
# `retrieve_column_sidecars` is implementation and context dependent, replacing
|
|
# `retrieve_blobs_and_proofs`. For the given block root, it returns all column
|
|
# sidecars to sample, or raises an exception if they are not available.
|
|
# The p2p network does not guarantee sidecar retrieval outside of
|
|
# `MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS` epochs.
|
|
column_sidecars = retrieve_column_sidecars(beacon_block_root)
|
|
return all(
|
|
verify_data_column_sidecar(column_sidecar)
|
|
and verify_data_column_sidecar_kzg_proofs(column_sidecar)
|
|
for column_sidecar in column_sidecars
|
|
)
|
|
</spec>
|
|
|
|
- name: is_eligible_for_activation
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func IsEligibleForActivation(
|
|
spec: |
|
|
<spec fn="is_eligible_for_activation" fork="phase0" hash="46b8f01a">
|
|
def is_eligible_for_activation(state: BeaconState, validator: Validator) -> bool:
|
|
"""
|
|
Check if ``validator`` is eligible for activation.
|
|
"""
|
|
return (
|
|
# Placement in queue is finalized
|
|
validator.activation_eligibility_epoch <= state.finalized_checkpoint.epoch
|
|
# Has not yet been activated
|
|
and validator.activation_epoch == FAR_FUTURE_EPOCH
|
|
)
|
|
</spec>
|
|
|
|
- name: is_eligible_for_activation_queue#phase0
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func IsEligibleForActivationQueue(
|
|
spec: |
|
|
<spec fn="is_eligible_for_activation_queue" fork="phase0" hash="513e6ed5">
|
|
def is_eligible_for_activation_queue(validator: Validator) -> bool:
|
|
"""
|
|
Check if ``validator`` is eligible to be placed into the activation queue.
|
|
"""
|
|
return (
|
|
validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
|
|
and validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
|
)
|
|
</spec>
|
|
|
|
- name: is_eligible_for_activation_queue#electra
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func IsEligibleForActivationQueue(
|
|
spec: |
|
|
<spec fn="is_eligible_for_activation_queue" fork="electra" hash="df3ee6a5">
|
|
def is_eligible_for_activation_queue(validator: Validator) -> bool:
|
|
"""
|
|
Check if ``validator`` is eligible to be placed into the activation queue.
|
|
"""
|
|
return (
|
|
validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
|
|
# [Modified in Electra:EIP7251]
|
|
and validator.effective_balance >= MIN_ACTIVATION_BALANCE
|
|
)
|
|
</spec>
|
|
|
|
- name: is_execution_block
|
|
sources:
|
|
- file: beacon-chain/core/blocks/payload.go
|
|
search: func IsExecutionBlock(
|
|
spec: |
|
|
<spec fn="is_execution_block" fork="bellatrix" hash="26be0b84">
|
|
def is_execution_block(block: BeaconBlock) -> bool:
|
|
return block.body.execution_payload != ExecutionPayload()
|
|
</spec>
|
|
|
|
- name: is_execution_enabled
|
|
sources:
|
|
- file: beacon-chain/core/blocks/payload.go
|
|
search: func IsExecutionEnabled(
|
|
spec: |
|
|
<spec fn="is_execution_enabled" fork="bellatrix" hash="3f36d4f4">
|
|
def is_execution_enabled(state: BeaconState, body: BeaconBlockBody) -> bool:
|
|
return is_merge_transition_block(state, body) or is_merge_transition_complete(state)
|
|
</spec>
|
|
|
|
- name: is_ffg_competitive
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_ffg_competitive" fork="phase0" hash="f6a3c0c5">
|
|
def is_ffg_competitive(store: Store, head_root: Root, parent_root: Root) -> bool:
|
|
return (
|
|
store.unrealized_justifications[head_root] == store.unrealized_justifications[parent_root]
|
|
)
|
|
</spec>
|
|
|
|
- name: is_finality_update
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_finality_update" fork="altair" hash="4d0080ac">
|
|
def is_finality_update(update: LightClientUpdate) -> bool:
|
|
return update.finality_branch != FinalityBranch()
|
|
</spec>
|
|
|
|
- name: is_finalization_ok
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_finalization_ok" fork="phase0" hash="760ff77e">
|
|
def is_finalization_ok(store: Store, slot: Slot) -> bool:
|
|
epochs_since_finalization = compute_epoch_at_slot(slot) - store.finalized_checkpoint.epoch
|
|
return epochs_since_finalization <= REORG_MAX_EPOCHS_SINCE_FINALIZATION
|
|
</spec>
|
|
|
|
- name: is_fully_withdrawable_validator#capella
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func IsFullyWithdrawableValidator(
|
|
spec: |
|
|
<spec fn="is_fully_withdrawable_validator" fork="capella" hash="e936da25">
|
|
def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch: Epoch) -> bool:
|
|
"""
|
|
Check if ``validator`` is fully withdrawable.
|
|
"""
|
|
return (
|
|
has_eth1_withdrawal_credential(validator)
|
|
and validator.withdrawable_epoch <= epoch
|
|
and balance > 0
|
|
)
|
|
</spec>
|
|
|
|
- name: is_fully_withdrawable_validator#electra
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func IsFullyWithdrawableValidator(
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: if fork >= version.Electra {
|
|
spec: |
|
|
<spec fn="is_fully_withdrawable_validator" fork="electra" hash="0db6f143">
|
|
def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch: Epoch) -> bool:
|
|
"""
|
|
Check if ``validator`` is fully withdrawable.
|
|
"""
|
|
return (
|
|
# [Modified in Electra:EIP7251]
|
|
has_execution_withdrawal_credential(validator)
|
|
and validator.withdrawable_epoch <= epoch
|
|
and balance > 0
|
|
)
|
|
</spec>
|
|
|
|
- name: is_head_late
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_head_late" fork="phase0" hash="e393dce6">
|
|
def is_head_late(store: Store, head_root: Root) -> bool:
|
|
return not store.block_timeliness[head_root]
|
|
</spec>
|
|
|
|
- name: is_head_weak
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_head_weak" fork="phase0" hash="3a6650d7">
|
|
def is_head_weak(store: Store, head_root: Root) -> bool:
|
|
justified_state = store.checkpoint_states[store.justified_checkpoint]
|
|
reorg_threshold = calculate_committee_fraction(justified_state, REORG_HEAD_WEIGHT_THRESHOLD)
|
|
head_weight = get_weight(store, head_root)
|
|
return head_weight < reorg_threshold
|
|
</spec>
|
|
|
|
- name: is_in_inactivity_leak
|
|
sources:
|
|
- file: beacon-chain/core/helpers/rewards_penalties.go
|
|
search: func IsInInactivityLeak(
|
|
spec: |
|
|
<spec fn="is_in_inactivity_leak" fork="phase0" hash="e95b1146">
|
|
def is_in_inactivity_leak(state: BeaconState) -> bool:
|
|
return get_finality_delay(state) > MIN_EPOCHS_TO_INACTIVITY_PENALTY
|
|
</spec>
|
|
|
|
- name: is_merge_transition_block
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_merge_transition_block" fork="bellatrix" hash="f0b5e2a0">
|
|
def is_merge_transition_block(state: BeaconState, body: BeaconBlockBody) -> bool:
|
|
return not is_merge_transition_complete(state) and body.execution_payload != ExecutionPayload()
|
|
</spec>
|
|
|
|
- name: is_merge_transition_complete
|
|
sources:
|
|
- file: beacon-chain/core/blocks/payload.go
|
|
search: func IsMergeTransitionComplete(
|
|
spec: |
|
|
<spec fn="is_merge_transition_complete" fork="bellatrix" hash="b13c98c6">
|
|
def is_merge_transition_complete(state: BeaconState) -> bool:
|
|
return state.latest_execution_payload_header != ExecutionPayloadHeader()
|
|
</spec>
|
|
|
|
- name: is_next_sync_committee_known
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_next_sync_committee_known" fork="altair" hash="f5d3196e">
|
|
def is_next_sync_committee_known(store: LightClientStore) -> bool:
|
|
return store.next_sync_committee != SyncCommittee()
|
|
</spec>
|
|
|
|
- name: is_optimistic
|
|
sources:
|
|
- file: beacon-chain/blockchain/chain_info.go
|
|
search: func (s *Service) IsOptimistic(
|
|
spec: |
|
|
<spec fn="is_optimistic" fork="bellatrix" hash="d5afd615">
|
|
def is_optimistic(opt_store: OptimisticStore, block: BeaconBlock) -> bool:
|
|
return hash_tree_root(block) in opt_store.optimistic_roots
|
|
</spec>
|
|
|
|
- name: is_optimistic_candidate_block
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_optimistic_candidate_block" fork="bellatrix" hash="fdb54007">
|
|
def is_optimistic_candidate_block(
|
|
opt_store: OptimisticStore, current_slot: Slot, block: BeaconBlock
|
|
) -> bool:
|
|
if is_execution_block(opt_store.blocks[block.parent_root]):
|
|
return True
|
|
|
|
if block.slot + SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY <= current_slot:
|
|
return True
|
|
|
|
return False
|
|
</spec>
|
|
|
|
- name: is_parent_strong
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_parent_strong" fork="phase0" hash="02a3fd0b">
|
|
def is_parent_strong(store: Store, root: Root) -> bool:
|
|
justified_state = store.checkpoint_states[store.justified_checkpoint]
|
|
parent_threshold = calculate_committee_fraction(justified_state, REORG_PARENT_WEIGHT_THRESHOLD)
|
|
parent_root = store.blocks[root].parent_root
|
|
parent_weight = get_weight(store, parent_root)
|
|
return parent_weight > parent_threshold
|
|
</spec>
|
|
|
|
- name: is_partially_withdrawable_validator#capella
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func IsPartiallyWithdrawableValidator(
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func isPartiallyWithdrawableValidatorCapella(
|
|
spec: |
|
|
<spec fn="is_partially_withdrawable_validator" fork="capella" hash="75d433d4">
|
|
def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) -> bool:
|
|
"""
|
|
Check if ``validator`` is partially withdrawable.
|
|
"""
|
|
has_max_effective_balance = validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
|
has_excess_balance = balance > MAX_EFFECTIVE_BALANCE
|
|
return (
|
|
has_eth1_withdrawal_credential(validator)
|
|
and has_max_effective_balance
|
|
and has_excess_balance
|
|
)
|
|
</spec>
|
|
|
|
- name: is_partially_withdrawable_validator#electra
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func IsPartiallyWithdrawableValidator(
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func isPartiallyWithdrawableValidatorElectra(v
|
|
spec: |
|
|
<spec fn="is_partially_withdrawable_validator" fork="electra" hash="82f25b17">
|
|
def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) -> bool:
|
|
"""
|
|
Check if ``validator`` is partially withdrawable.
|
|
"""
|
|
max_effective_balance = get_max_effective_balance(validator)
|
|
# [Modified in Electra:EIP7251]
|
|
has_max_effective_balance = validator.effective_balance == max_effective_balance
|
|
# [Modified in Electra:EIP7251]
|
|
has_excess_balance = balance > max_effective_balance
|
|
return (
|
|
# [Modified in Electra:EIP7251]
|
|
has_execution_withdrawal_credential(validator)
|
|
and has_max_effective_balance
|
|
and has_excess_balance
|
|
)
|
|
</spec>
|
|
|
|
- name: is_proposer
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_proposer" fork="phase0" hash="d9e66d76">
|
|
def is_proposer(state: BeaconState, validator_index: ValidatorIndex) -> bool:
|
|
return get_beacon_proposer_index(state) == validator_index
|
|
</spec>
|
|
|
|
- name: is_proposing_on_time
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_proposing_on_time" fork="phase0" hash="dc5256b4">
|
|
def is_proposing_on_time(store: Store) -> bool:
|
|
seconds_since_genesis = store.time - store.genesis_time
|
|
time_into_slot_ms = seconds_to_milliseconds(seconds_since_genesis) % SLOT_DURATION_MS
|
|
epoch = get_current_store_epoch(store)
|
|
proposer_reorg_cutoff_ms = get_proposer_reorg_cutoff_ms(epoch)
|
|
return time_into_slot_ms <= proposer_reorg_cutoff_ms
|
|
</spec>
|
|
|
|
- name: is_shuffling_stable
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_shuffling_stable" fork="phase0" hash="680f0aa0">
|
|
def is_shuffling_stable(slot: Slot) -> bool:
|
|
return slot % SLOTS_PER_EPOCH != 0
|
|
</spec>
|
|
|
|
- name: is_slashable_attestation_data
|
|
sources:
|
|
- file: beacon-chain/core/blocks/attester_slashing.go
|
|
search: func IsSlashableAttestationData(
|
|
spec: |
|
|
<spec fn="is_slashable_attestation_data" fork="phase0" hash="47bacf1f">
|
|
def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationData) -> bool:
|
|
"""
|
|
Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG rules.
|
|
"""
|
|
return (
|
|
# Double vote
|
|
(data_1 != data_2 and data_1.target.epoch == data_2.target.epoch)
|
|
or
|
|
# Surround vote
|
|
(data_1.source.epoch < data_2.source.epoch and data_2.target.epoch < data_1.target.epoch)
|
|
)
|
|
</spec>
|
|
|
|
- name: is_slashable_validator
|
|
sources:
|
|
- file: beacon-chain/core/helpers/validators.go
|
|
search: func IsSlashableValidator(
|
|
spec: |
|
|
<spec fn="is_slashable_validator" fork="phase0" hash="84f09f6b">
|
|
def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool:
|
|
"""
|
|
Check if ``validator`` is slashable.
|
|
"""
|
|
return (not validator.slashed) and (
|
|
validator.activation_epoch <= epoch < validator.withdrawable_epoch
|
|
)
|
|
</spec>
|
|
|
|
- name: is_sync_committee_aggregator
|
|
sources:
|
|
- file: beacon-chain/core/altair/sync_committee.go
|
|
search: func IsSyncCommitteeAggregator(
|
|
- file: validator/client/validator.go
|
|
search: func (v *validator) isSyncCommitteeAggregator(
|
|
spec: |
|
|
<spec fn="is_sync_committee_aggregator" fork="altair" hash="13d9d470">
|
|
def is_sync_committee_aggregator(signature: BLSSignature) -> bool:
|
|
modulo = max(
|
|
1,
|
|
SYNC_COMMITTEE_SIZE
|
|
// SYNC_COMMITTEE_SUBNET_COUNT
|
|
// TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE,
|
|
)
|
|
return bytes_to_uint64(hash(signature)[0:8]) % modulo == 0
|
|
</spec>
|
|
|
|
- name: is_sync_committee_update
|
|
sources:
|
|
- file: beacon-chain/light-client/lightclient.go
|
|
search: func HasRelevantSyncCommittee(
|
|
spec: |
|
|
<spec fn="is_sync_committee_update" fork="altair" hash="af6f8cb0">
|
|
def is_sync_committee_update(update: LightClientUpdate) -> bool:
|
|
return update.next_sync_committee_branch != NextSyncCommitteeBranch()
|
|
</spec>
|
|
|
|
- name: is_valid_deposit_signature
|
|
sources:
|
|
- file: beacon-chain/core/electra/deposits.go
|
|
search: func IsValidDepositSignature(
|
|
- file: beacon-chain/core/blocks/deposit.go
|
|
search: func IsValidDepositSignature(
|
|
spec: |
|
|
<spec fn="is_valid_deposit_signature" fork="electra" hash="7347b754">
|
|
def is_valid_deposit_signature(
|
|
pubkey: BLSPubkey, withdrawal_credentials: Bytes32, amount: uint64, signature: BLSSignature
|
|
) -> bool:
|
|
deposit_message = DepositMessage(
|
|
pubkey=pubkey,
|
|
withdrawal_credentials=withdrawal_credentials,
|
|
amount=amount,
|
|
)
|
|
# Fork-agnostic domain since deposits are valid across forks
|
|
domain = compute_domain(DOMAIN_DEPOSIT)
|
|
signing_root = compute_signing_root(deposit_message, domain)
|
|
return bls.Verify(pubkey, signing_root, signature)
|
|
</spec>
|
|
|
|
- name: is_valid_genesis_state
|
|
sources:
|
|
- file: beacon-chain/core/transition/state.go
|
|
search: func IsValidGenesisState(
|
|
spec: |
|
|
<spec fn="is_valid_genesis_state" fork="phase0" hash="af8478cd">
|
|
def is_valid_genesis_state(state: BeaconState) -> bool:
|
|
if state.genesis_time < MIN_GENESIS_TIME:
|
|
return False
|
|
if len(get_active_validator_indices(state, GENESIS_EPOCH)) < MIN_GENESIS_ACTIVE_VALIDATOR_COUNT:
|
|
return False
|
|
return True
|
|
</spec>
|
|
|
|
- name: is_valid_indexed_attestation
|
|
sources:
|
|
- file: beacon-chain/core/blocks/attestation.go
|
|
search: func VerifyIndexedAttestation(
|
|
spec: |
|
|
<spec fn="is_valid_indexed_attestation" fork="phase0" hash="31223a33">
|
|
def is_valid_indexed_attestation(
|
|
state: BeaconState, indexed_attestation: IndexedAttestation
|
|
) -> bool:
|
|
"""
|
|
Check if ``indexed_attestation`` is not empty, has sorted and unique indices and has a valid aggregate signature.
|
|
"""
|
|
# Verify indices are sorted and unique
|
|
indices = indexed_attestation.attesting_indices
|
|
if len(indices) == 0 or not indices == sorted(set(indices)):
|
|
return False
|
|
# Verify aggregate signature
|
|
pubkeys = [state.validators[i].pubkey for i in indices]
|
|
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch)
|
|
signing_root = compute_signing_root(indexed_attestation.data, domain)
|
|
return bls.FastAggregateVerify(pubkeys, signing_root, indexed_attestation.signature)
|
|
</spec>
|
|
|
|
- name: is_valid_light_client_header#altair
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_valid_light_client_header" fork="altair" hash="a4c4f0f3">
|
|
def is_valid_light_client_header(_header: LightClientHeader) -> bool:
|
|
return True
|
|
</spec>
|
|
|
|
- name: is_valid_light_client_header#capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_valid_light_client_header" fork="capella" hash="232ae1d9">
|
|
def is_valid_light_client_header(header: LightClientHeader) -> bool:
|
|
epoch = compute_epoch_at_slot(header.beacon.slot)
|
|
|
|
if epoch < CAPELLA_FORK_EPOCH:
|
|
return (
|
|
header.execution == ExecutionPayloadHeader()
|
|
and header.execution_branch == ExecutionBranch()
|
|
)
|
|
|
|
return is_valid_merkle_branch(
|
|
leaf=get_lc_execution_root(header),
|
|
branch=header.execution_branch,
|
|
depth=floorlog2(EXECUTION_PAYLOAD_GINDEX),
|
|
index=get_subtree_index(EXECUTION_PAYLOAD_GINDEX),
|
|
root=header.beacon.body_root,
|
|
)
|
|
</spec>
|
|
|
|
- name: is_valid_light_client_header#deneb
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_valid_light_client_header" fork="deneb" hash="f458ba78">
|
|
def is_valid_light_client_header(header: LightClientHeader) -> bool:
|
|
epoch = compute_epoch_at_slot(header.beacon.slot)
|
|
|
|
# [New in Deneb:EIP4844]
|
|
if epoch < DENEB_FORK_EPOCH:
|
|
if header.execution.blob_gas_used != uint64(0):
|
|
return False
|
|
if header.execution.excess_blob_gas != uint64(0):
|
|
return False
|
|
|
|
if epoch < CAPELLA_FORK_EPOCH:
|
|
return (
|
|
header.execution == ExecutionPayloadHeader()
|
|
and header.execution_branch == ExecutionBranch()
|
|
)
|
|
|
|
return is_valid_merkle_branch(
|
|
leaf=get_lc_execution_root(header),
|
|
branch=header.execution_branch,
|
|
depth=floorlog2(EXECUTION_PAYLOAD_GINDEX),
|
|
index=get_subtree_index(EXECUTION_PAYLOAD_GINDEX),
|
|
root=header.beacon.body_root,
|
|
)
|
|
</spec>
|
|
|
|
- name: is_valid_merkle_branch
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_valid_merkle_branch" fork="phase0" hash="f754117e">
|
|
def is_valid_merkle_branch(
|
|
leaf: Bytes32, branch: Sequence[Bytes32], depth: uint64, index: uint64, root: Root
|
|
) -> bool:
|
|
"""
|
|
Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and ``branch``.
|
|
"""
|
|
value = leaf
|
|
for i in range(depth):
|
|
if index // (2**i) % 2:
|
|
value = hash(branch[i] + value)
|
|
else:
|
|
value = hash(value + branch[i])
|
|
return value == root
|
|
</spec>
|
|
|
|
- name: is_valid_normalized_merkle_branch
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_valid_normalized_merkle_branch" fork="altair" hash="ef32cdea">
|
|
def is_valid_normalized_merkle_branch(
|
|
leaf: Bytes32, branch: Sequence[Bytes32], gindex: GeneralizedIndex, root: Root
|
|
) -> bool:
|
|
depth = floorlog2(gindex)
|
|
index = get_subtree_index(gindex)
|
|
num_extra = len(branch) - depth
|
|
for i in range(num_extra):
|
|
if branch[i] != Bytes32():
|
|
return False
|
|
return is_valid_merkle_branch(leaf, branch[num_extra:], depth, index, root)
|
|
</spec>
|
|
|
|
- name: is_valid_switch_to_compounding_request
|
|
sources:
|
|
- file: beacon-chain/core/electra/consolidations.go
|
|
search: func IsValidSwitchToCompoundingRequest(
|
|
spec: |
|
|
<spec fn="is_valid_switch_to_compounding_request" fork="electra" hash="91aa69bb">
|
|
def is_valid_switch_to_compounding_request(
|
|
state: BeaconState, consolidation_request: ConsolidationRequest
|
|
) -> bool:
|
|
# Switch to compounding requires source and target be equal
|
|
if consolidation_request.source_pubkey != consolidation_request.target_pubkey:
|
|
return False
|
|
|
|
# Verify pubkey exists
|
|
source_pubkey = consolidation_request.source_pubkey
|
|
validator_pubkeys = [v.pubkey for v in state.validators]
|
|
if source_pubkey not in validator_pubkeys:
|
|
return False
|
|
|
|
source_validator = state.validators[ValidatorIndex(validator_pubkeys.index(source_pubkey))]
|
|
|
|
# Verify request has been authorized
|
|
if source_validator.withdrawal_credentials[12:] != consolidation_request.source_address:
|
|
return False
|
|
|
|
# Verify source withdrawal credentials
|
|
if not has_eth1_withdrawal_credential(source_validator):
|
|
return False
|
|
|
|
# Verify the source is active
|
|
current_epoch = get_current_epoch(state)
|
|
if not is_active_validator(source_validator, current_epoch):
|
|
return False
|
|
|
|
# Verify exit for source has not been initiated
|
|
if source_validator.exit_epoch != FAR_FUTURE_EPOCH:
|
|
return False
|
|
|
|
return True
|
|
</spec>
|
|
|
|
- name: is_valid_terminal_pow_block
|
|
sources:
|
|
- file: beacon-chain/blockchain/pow_block.go
|
|
search: func validateTerminalBlockDifficulties(
|
|
spec: |
|
|
<spec fn="is_valid_terminal_pow_block" fork="bellatrix" hash="96a7d581">
|
|
def is_valid_terminal_pow_block(block: PowBlock, parent: PowBlock) -> bool:
|
|
is_total_difficulty_reached = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
|
is_parent_total_difficulty_valid = parent.total_difficulty < TERMINAL_TOTAL_DIFFICULTY
|
|
return is_total_difficulty_reached and is_parent_total_difficulty_valid
|
|
</spec>
|
|
|
|
- name: is_within_weak_subjectivity_period#phase0
|
|
sources:
|
|
- file: beacon-chain/core/helpers/weak_subjectivity.go
|
|
search: func IsWithinWeakSubjectivityPeriod(
|
|
spec: |
|
|
<spec fn="is_within_weak_subjectivity_period" fork="phase0" hash="aef08e82">
|
|
def is_within_weak_subjectivity_period(
|
|
store: Store, ws_state: BeaconState, ws_checkpoint: Checkpoint
|
|
) -> bool:
|
|
# Clients may choose to validate the input state against the input Weak Subjectivity Checkpoint
|
|
assert get_block_root(ws_state, ws_checkpoint.epoch) == ws_checkpoint.root
|
|
assert compute_epoch_at_slot(ws_state.slot) == ws_checkpoint.epoch
|
|
|
|
ws_period = compute_weak_subjectivity_period(ws_state)
|
|
ws_state_epoch = compute_epoch_at_slot(ws_state.slot)
|
|
current_epoch = compute_epoch_at_slot(get_current_slot(store))
|
|
return current_epoch <= ws_state_epoch + ws_period
|
|
</spec>
|
|
|
|
- name: is_within_weak_subjectivity_period#electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_within_weak_subjectivity_period" fork="electra" hash="d05d230d">
|
|
def is_within_weak_subjectivity_period(
|
|
store: Store, ws_state: BeaconState, ws_checkpoint: Checkpoint
|
|
) -> bool:
|
|
# Clients may choose to validate the input state against the input Weak Subjectivity Checkpoint
|
|
assert get_block_root(ws_state, ws_checkpoint.epoch) == ws_checkpoint.root
|
|
assert compute_epoch_at_slot(ws_state.slot) == ws_checkpoint.epoch
|
|
|
|
# [Modified in Electra]
|
|
ws_period = compute_weak_subjectivity_period(ws_state)
|
|
ws_state_epoch = compute_epoch_at_slot(ws_state.slot)
|
|
current_epoch = compute_epoch_at_slot(get_current_slot(store))
|
|
return current_epoch <= ws_state_epoch + ws_period
|
|
</spec>
|
|
|
|
- name: kzg_commitment_to_versioned_hash
|
|
sources:
|
|
- file: consensus-types/primitives/kzg.go
|
|
search: func ConvertKzgCommitmentToVersionedHash(
|
|
spec: |
|
|
<spec fn="kzg_commitment_to_versioned_hash" fork="deneb" hash="fad217c1">
|
|
def kzg_commitment_to_versioned_hash(kzg_commitment: KZGCommitment) -> VersionedHash:
|
|
return VERSIONED_HASH_VERSION_KZG + hash(kzg_commitment)[1:]
|
|
</spec>
|
|
|
|
- name: latest_verified_ancestor
|
|
sources: []
|
|
spec: |
|
|
<spec fn="latest_verified_ancestor" fork="bellatrix" hash="c2ee2c55">
|
|
def latest_verified_ancestor(opt_store: OptimisticStore, block: BeaconBlock) -> BeaconBlock:
|
|
# It is assumed that the `block` parameter is never an INVALIDATED block.
|
|
while True:
|
|
if not is_optimistic(opt_store, block) or block.parent_root == Root():
|
|
return block
|
|
block = opt_store.blocks[block.parent_root]
|
|
</spec>
|
|
|
|
- name: max_compressed_len
|
|
sources:
|
|
- file: beacon-chain/p2p/encoder/ssz.go
|
|
search: func MaxCompressedLen(
|
|
spec: |
|
|
<spec fn="max_compressed_len" fork="phase0" hash="2e531709">
|
|
def max_compressed_len(n: uint64) -> uint64:
|
|
# Worst-case compressed length for a given payload of size n when using snappy:
|
|
# https://github.com/google/snappy/blob/32ded457c0b1fe78ceb8397632c416568d6714a0/snappy.cc#L218C1-L218C47
|
|
return uint64(32 + n + n / 6)
|
|
</spec>
|
|
|
|
- name: max_message_size
|
|
sources:
|
|
- file: beacon-chain/p2p/pubsub.go
|
|
search: func MaxMessageSize(
|
|
spec: |
|
|
<spec fn="max_message_size" fork="phase0" hash="8abedfdb">
|
|
def max_message_size() -> uint64:
|
|
# Allow 1024 bytes for framing and encoding overhead but at least 1MiB in case MAX_PAYLOAD_SIZE is small.
|
|
return max(max_compressed_len(MAX_PAYLOAD_SIZE) + 1024, 1024 * 1024)
|
|
</spec>
|
|
|
|
- name: next_sync_committee_gindex_at_slot#altair
|
|
sources:
|
|
- file: beacon-chain/state/state-native/proofs.go
|
|
search: func (b *BeaconState) NextSyncCommitteeGeneralizedIndex(
|
|
spec: |
|
|
<spec fn="next_sync_committee_gindex_at_slot" fork="altair" hash="129a53c6">
|
|
def next_sync_committee_gindex_at_slot(_slot: Slot) -> GeneralizedIndex:
|
|
return NEXT_SYNC_COMMITTEE_GINDEX
|
|
</spec>
|
|
|
|
- name: next_sync_committee_gindex_at_slot#electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="next_sync_committee_gindex_at_slot" fork="electra" hash="87d7c53a">
|
|
def next_sync_committee_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
|
|
epoch = compute_epoch_at_slot(slot)
|
|
|
|
# [Modified in Electra]
|
|
if epoch >= ELECTRA_FORK_EPOCH:
|
|
return NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA
|
|
return NEXT_SYNC_COMMITTEE_GINDEX
|
|
</spec>
|
|
|
|
- name: normalize_merkle_branch
|
|
sources: []
|
|
spec: |
|
|
<spec fn="normalize_merkle_branch" fork="electra" hash="149d7b0c">
|
|
def normalize_merkle_branch(
|
|
branch: Sequence[Bytes32], gindex: GeneralizedIndex
|
|
) -> Sequence[Bytes32]:
|
|
depth = floorlog2(gindex)
|
|
num_extra = depth - len(branch)
|
|
return [Bytes32()] * num_extra + [*branch]
|
|
</spec>
|
|
|
|
- name: on_attestation
|
|
sources:
|
|
- file: beacon-chain/blockchain/process_attestation.go
|
|
search: func (s *Service) OnAttestation(
|
|
spec: |
|
|
<spec fn="on_attestation" fork="phase0" hash="b7b4d7de">
|
|
def on_attestation(store: Store, attestation: Attestation, is_from_block: bool = False) -> None:
|
|
"""
|
|
Run ``on_attestation`` upon receiving a new ``attestation`` from either within a block or directly on the wire.
|
|
|
|
An ``attestation`` that is asserted as invalid may be valid at a later time,
|
|
consider scheduling it for later processing in such case.
|
|
"""
|
|
validate_on_attestation(store, attestation, is_from_block)
|
|
|
|
store_target_checkpoint_state(store, attestation.data.target)
|
|
|
|
# Get state at the `target` to fully validate attestation
|
|
target_state = store.checkpoint_states[attestation.data.target]
|
|
indexed_attestation = get_indexed_attestation(target_state, attestation)
|
|
assert is_valid_indexed_attestation(target_state, indexed_attestation)
|
|
|
|
# Update latest messages for attesting indices
|
|
update_latest_messages(store, indexed_attestation.attesting_indices, attestation)
|
|
</spec>
|
|
|
|
- name: on_attester_slashing
|
|
sources:
|
|
- file: beacon-chain/core/blocks/attester_slashing.go
|
|
search: func ProcessAttesterSlashing(
|
|
spec: |
|
|
<spec fn="on_attester_slashing" fork="phase0" hash="73957b9b">
|
|
def on_attester_slashing(store: Store, attester_slashing: AttesterSlashing) -> None:
|
|
"""
|
|
Run ``on_attester_slashing`` immediately upon receiving a new ``AttesterSlashing``
|
|
from either within a block or directly on the wire.
|
|
"""
|
|
attestation_1 = attester_slashing.attestation_1
|
|
attestation_2 = attester_slashing.attestation_2
|
|
assert is_slashable_attestation_data(attestation_1.data, attestation_2.data)
|
|
state = store.block_states[store.justified_checkpoint.root]
|
|
assert is_valid_indexed_attestation(state, attestation_1)
|
|
assert is_valid_indexed_attestation(state, attestation_2)
|
|
|
|
indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices)
|
|
for index in indices:
|
|
store.equivocating_indices.add(index)
|
|
</spec>
|
|
|
|
- name: on_block#phase0
|
|
sources:
|
|
- file: beacon-chain/blockchain/receive_block.go
|
|
search: func (s *Service) ReceiveBlock(
|
|
spec: |
|
|
<spec fn="on_block" fork="phase0" hash="5f45947a">
|
|
def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
|
block = signed_block.message
|
|
# Parent block must be known
|
|
assert block.parent_root in store.block_states
|
|
# Make a copy of the state to avoid mutability issues
|
|
pre_state = copy(store.block_states[block.parent_root])
|
|
# Blocks cannot be in the future. If they are, their consideration must be delayed until they are in the past.
|
|
assert get_current_slot(store) >= block.slot
|
|
|
|
# Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor)
|
|
finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
|
|
assert block.slot > finalized_slot
|
|
# Check block is a descendant of the finalized block at the checkpoint finalized slot
|
|
finalized_checkpoint_block = get_checkpoint_block(
|
|
store,
|
|
block.parent_root,
|
|
store.finalized_checkpoint.epoch,
|
|
)
|
|
assert store.finalized_checkpoint.root == finalized_checkpoint_block
|
|
|
|
# Check the block is valid and compute the post-state
|
|
state = pre_state.copy()
|
|
block_root = hash_tree_root(block)
|
|
state_transition(state, signed_block, True)
|
|
# Add new block to the store
|
|
store.blocks[block_root] = block
|
|
# Add new state for this block to the store
|
|
store.block_states[block_root] = state
|
|
|
|
record_block_timeliness(store, block_root)
|
|
update_proposer_boost_root(store, block_root)
|
|
|
|
# Update checkpoints in store if necessary
|
|
update_checkpoints(store, state.current_justified_checkpoint, state.finalized_checkpoint)
|
|
|
|
# Eagerly compute unrealized justification and finality
|
|
compute_pulled_up_tip(store, block_root)
|
|
</spec>
|
|
|
|
- name: on_block#bellatrix
|
|
sources:
|
|
- file: beacon-chain/blockchain/receive_block.go
|
|
search: func (s *Service) ReceiveBlock(
|
|
spec: |
|
|
<spec fn="on_block" fork="bellatrix" hash="e81d01c3">
|
|
def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
|
"""
|
|
Run ``on_block`` upon receiving a new block.
|
|
|
|
A block that is asserted as invalid due to unavailable PoW block may be valid at a later time,
|
|
consider scheduling it for later processing in such case.
|
|
"""
|
|
block = signed_block.message
|
|
# Parent block must be known
|
|
assert block.parent_root in store.block_states
|
|
# Make a copy of the state to avoid mutability issues
|
|
pre_state = copy(store.block_states[block.parent_root])
|
|
# Blocks cannot be in the future. If they are, their consideration must be delayed until they are in the past.
|
|
assert get_current_slot(store) >= block.slot
|
|
|
|
# Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor)
|
|
finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
|
|
assert block.slot > finalized_slot
|
|
# Check block is a descendant of the finalized block at the checkpoint finalized slot
|
|
finalized_checkpoint_block = get_checkpoint_block(
|
|
store,
|
|
block.parent_root,
|
|
store.finalized_checkpoint.epoch,
|
|
)
|
|
assert store.finalized_checkpoint.root == finalized_checkpoint_block
|
|
|
|
# Check the block is valid and compute the post-state
|
|
state = pre_state.copy()
|
|
block_root = hash_tree_root(block)
|
|
state_transition(state, signed_block, True)
|
|
|
|
# [New in Bellatrix]
|
|
if is_merge_transition_block(pre_state, block.body):
|
|
validate_merge_block(block)
|
|
|
|
# Add new block to the store
|
|
store.blocks[block_root] = block
|
|
# Add new state for this block to the store
|
|
store.block_states[block_root] = state
|
|
|
|
record_block_timeliness(store, block_root)
|
|
update_proposer_boost_root(store, block_root)
|
|
|
|
# Update checkpoints in store if necessary
|
|
update_checkpoints(store, state.current_justified_checkpoint, state.finalized_checkpoint)
|
|
|
|
# Eagerly compute unrealized justification and finality.
|
|
compute_pulled_up_tip(store, block_root)
|
|
</spec>
|
|
|
|
- name: on_block#capella
|
|
sources:
|
|
- file: beacon-chain/blockchain/receive_block.go
|
|
search: func (s *Service) ReceiveBlock(
|
|
spec: |
|
|
<spec fn="on_block" fork="capella" hash="7450531c">
|
|
def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
|
"""
|
|
Run ``on_block`` upon receiving a new block.
|
|
"""
|
|
block = signed_block.message
|
|
# Parent block must be known
|
|
assert block.parent_root in store.block_states
|
|
# Blocks cannot be in the future. If they are, their consideration must be delayed until they are in the past.
|
|
assert get_current_slot(store) >= block.slot
|
|
|
|
# Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor)
|
|
finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
|
|
assert block.slot > finalized_slot
|
|
# Check block is a descendant of the finalized block at the checkpoint finalized slot
|
|
finalized_checkpoint_block = get_checkpoint_block(
|
|
store,
|
|
block.parent_root,
|
|
store.finalized_checkpoint.epoch,
|
|
)
|
|
assert store.finalized_checkpoint.root == finalized_checkpoint_block
|
|
|
|
# Check the block is valid and compute the post-state
|
|
# Make a copy of the state to avoid mutability issues
|
|
state = copy(store.block_states[block.parent_root])
|
|
block_root = hash_tree_root(block)
|
|
state_transition(state, signed_block, True)
|
|
|
|
# Add new block to the store
|
|
store.blocks[block_root] = block
|
|
# Add new state for this block to the store
|
|
store.block_states[block_root] = state
|
|
|
|
record_block_timeliness(store, block_root)
|
|
update_proposer_boost_root(store, block_root)
|
|
|
|
# Update checkpoints in store if necessary
|
|
update_checkpoints(store, state.current_justified_checkpoint, state.finalized_checkpoint)
|
|
|
|
# Eagerly compute unrealized justification and finality.
|
|
compute_pulled_up_tip(store, block_root)
|
|
</spec>
|
|
|
|
- name: on_block#deneb
|
|
sources:
|
|
- file: beacon-chain/blockchain/receive_block.go
|
|
search: func (s *Service) ReceiveBlock(
|
|
spec: |
|
|
<spec fn="on_block" fork="deneb" hash="bbad196e">
|
|
def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
|
"""
|
|
Run ``on_block`` upon receiving a new block.
|
|
"""
|
|
block = signed_block.message
|
|
# Parent block must be known
|
|
assert block.parent_root in store.block_states
|
|
# Blocks cannot be in the future. If they are, their consideration must be delayed until they are in the past.
|
|
assert get_current_slot(store) >= block.slot
|
|
|
|
# Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor)
|
|
finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
|
|
assert block.slot > finalized_slot
|
|
# Check block is a descendant of the finalized block at the checkpoint finalized slot
|
|
finalized_checkpoint_block = get_checkpoint_block(
|
|
store,
|
|
block.parent_root,
|
|
store.finalized_checkpoint.epoch,
|
|
)
|
|
assert store.finalized_checkpoint.root == finalized_checkpoint_block
|
|
|
|
# [New in Deneb:EIP4844]
|
|
# Check if blob data is available
|
|
# If not, this payload MAY be queued and subsequently considered when blob data becomes available
|
|
assert is_data_available(hash_tree_root(block), block.body.blob_kzg_commitments)
|
|
|
|
# Check the block is valid and compute the post-state
|
|
# Make a copy of the state to avoid mutability issues
|
|
state = copy(store.block_states[block.parent_root])
|
|
block_root = hash_tree_root(block)
|
|
state_transition(state, signed_block, True)
|
|
|
|
# Add new block to the store
|
|
store.blocks[block_root] = block
|
|
# Add new state for this block to the store
|
|
store.block_states[block_root] = state
|
|
|
|
record_block_timeliness(store, block_root)
|
|
update_proposer_boost_root(store, block_root)
|
|
|
|
# Update checkpoints in store if necessary
|
|
update_checkpoints(store, state.current_justified_checkpoint, state.finalized_checkpoint)
|
|
|
|
# Eagerly compute unrealized justification and finality.
|
|
compute_pulled_up_tip(store, block_root)
|
|
</spec>
|
|
|
|
- name: on_block#fulu
|
|
sources:
|
|
- file: beacon-chain/blockchain/receive_block.go
|
|
search: func (s *Service) ReceiveBlock(
|
|
spec: |
|
|
<spec fn="on_block" fork="fulu" hash="b8f279b9">
|
|
def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
|
"""
|
|
Run ``on_block`` upon receiving a new block.
|
|
"""
|
|
block = signed_block.message
|
|
# Parent block must be known
|
|
assert block.parent_root in store.block_states
|
|
# Make a copy of the state to avoid mutability issues
|
|
state = copy(store.block_states[block.parent_root])
|
|
# Blocks cannot be in the future. If they are, their consideration must be delayed until they are in the past.
|
|
assert get_current_slot(store) >= block.slot
|
|
|
|
# Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor)
|
|
finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
|
|
assert block.slot > finalized_slot
|
|
# Check block is a descendant of the finalized block at the checkpoint finalized slot
|
|
finalized_checkpoint_block = get_checkpoint_block(
|
|
store,
|
|
block.parent_root,
|
|
store.finalized_checkpoint.epoch,
|
|
)
|
|
assert store.finalized_checkpoint.root == finalized_checkpoint_block
|
|
|
|
# [Modified in Fulu:EIP7594]
|
|
# Check if blob data is available
|
|
# If not, this payload MAY be queued and subsequently considered when blob data becomes available
|
|
assert is_data_available(hash_tree_root(block))
|
|
|
|
# Check the block is valid and compute the post-state
|
|
block_root = hash_tree_root(block)
|
|
state_transition(state, signed_block, True)
|
|
|
|
# Add new block to the store
|
|
store.blocks[block_root] = block
|
|
# Add new state for this block to the store
|
|
store.block_states[block_root] = state
|
|
|
|
record_block_timeliness(store, block_root)
|
|
update_proposer_boost_root(store, block_root)
|
|
|
|
# Update checkpoints in store if necessary
|
|
update_checkpoints(store, state.current_justified_checkpoint, state.finalized_checkpoint)
|
|
|
|
# Eagerly compute unrealized justification and finality.
|
|
compute_pulled_up_tip(store, block_root)
|
|
</spec>
|
|
|
|
- name: on_tick
|
|
sources: []
|
|
spec: |
|
|
<spec fn="on_tick" fork="phase0" hash="9cc6e040">
|
|
def on_tick(store: Store, time: uint64) -> None:
|
|
# If the ``store.time`` falls behind, while loop catches up slot by slot
|
|
# to ensure that every previous slot is processed with ``on_tick_per_slot``
|
|
tick_slot = (time - store.genesis_time) // SECONDS_PER_SLOT
|
|
while get_current_slot(store) < tick_slot:
|
|
previous_time = store.genesis_time + (get_current_slot(store) + 1) * SECONDS_PER_SLOT
|
|
on_tick_per_slot(store, previous_time)
|
|
on_tick_per_slot(store, time)
|
|
</spec>
|
|
|
|
- name: on_tick_per_slot
|
|
sources: []
|
|
spec: |
|
|
<spec fn="on_tick_per_slot" fork="phase0" hash="ae9ef172">
|
|
def on_tick_per_slot(store: Store, time: uint64) -> None:
|
|
previous_slot = get_current_slot(store)
|
|
|
|
# Update store time
|
|
store.time = time
|
|
|
|
current_slot = get_current_slot(store)
|
|
|
|
# If this is a new slot, reset store.proposer_boost_root
|
|
if current_slot > previous_slot:
|
|
store.proposer_boost_root = Root()
|
|
|
|
# If a new epoch, pull-up justification and finalization from previous epoch
|
|
if current_slot > previous_slot and compute_slots_since_epoch_start(current_slot) == 0:
|
|
update_checkpoints(
|
|
store, store.unrealized_justified_checkpoint, store.unrealized_finalized_checkpoint
|
|
)
|
|
</spec>
|
|
|
|
- name: prepare_execution_payload#bellatrix
|
|
sources: []
|
|
spec: |
|
|
<spec fn="prepare_execution_payload" fork="bellatrix" hash="6af4de76">
|
|
def prepare_execution_payload(
|
|
state: BeaconState,
|
|
safe_block_hash: Hash32,
|
|
finalized_block_hash: Hash32,
|
|
suggested_fee_recipient: ExecutionAddress,
|
|
execution_engine: ExecutionEngine,
|
|
pow_chain: Optional[Dict[Hash32, PowBlock]] = None,
|
|
) -> Optional[PayloadId]:
|
|
if not is_merge_transition_complete(state):
|
|
assert pow_chain is not None
|
|
is_terminal_block_hash_set = TERMINAL_BLOCK_HASH != Hash32()
|
|
is_activation_epoch_reached = (
|
|
get_current_epoch(state) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
|
|
)
|
|
if is_terminal_block_hash_set and not is_activation_epoch_reached:
|
|
# Terminal block hash is set but activation epoch is not yet reached, no prepare payload call is needed
|
|
return None
|
|
|
|
terminal_pow_block = get_terminal_pow_block(pow_chain)
|
|
if terminal_pow_block is None:
|
|
# Pre-merge, no prepare payload call is needed
|
|
return None
|
|
# Signify merge via producing on top of the terminal PoW block
|
|
parent_hash = terminal_pow_block.block_hash
|
|
else:
|
|
# Post-merge, normal payload
|
|
parent_hash = state.latest_execution_payload_header.block_hash
|
|
|
|
# Set the forkchoice head and initiate the payload build process
|
|
payload_attributes = PayloadAttributes(
|
|
timestamp=compute_time_at_slot(state, state.slot),
|
|
prev_randao=get_randao_mix(state, get_current_epoch(state)),
|
|
suggested_fee_recipient=suggested_fee_recipient,
|
|
)
|
|
return execution_engine.notify_forkchoice_updated(
|
|
head_block_hash=parent_hash,
|
|
safe_block_hash=safe_block_hash,
|
|
finalized_block_hash=finalized_block_hash,
|
|
payload_attributes=payload_attributes,
|
|
)
|
|
</spec>
|
|
|
|
- name: prepare_execution_payload#capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="prepare_execution_payload" fork="capella" hash="c258893e">
|
|
def prepare_execution_payload(
|
|
state: BeaconState,
|
|
safe_block_hash: Hash32,
|
|
finalized_block_hash: Hash32,
|
|
suggested_fee_recipient: ExecutionAddress,
|
|
execution_engine: ExecutionEngine,
|
|
) -> Optional[PayloadId]:
|
|
# [Modified in Capella]
|
|
# Removed `is_merge_transition_complete` check
|
|
parent_hash = state.latest_execution_payload_header.block_hash
|
|
|
|
# Set the forkchoice head and initiate the payload build process
|
|
# [New in Capella]
|
|
withdrawals, _ = get_expected_withdrawals(state)
|
|
|
|
payload_attributes = PayloadAttributes(
|
|
timestamp=compute_time_at_slot(state, state.slot),
|
|
prev_randao=get_randao_mix(state, get_current_epoch(state)),
|
|
suggested_fee_recipient=suggested_fee_recipient,
|
|
# [New in Capella]
|
|
withdrawals=withdrawals,
|
|
)
|
|
return execution_engine.notify_forkchoice_updated(
|
|
head_block_hash=parent_hash,
|
|
safe_block_hash=safe_block_hash,
|
|
finalized_block_hash=finalized_block_hash,
|
|
payload_attributes=payload_attributes,
|
|
)
|
|
</spec>
|
|
|
|
- name: prepare_execution_payload#deneb
|
|
sources: []
|
|
spec: |
|
|
<spec fn="prepare_execution_payload" fork="deneb" hash="59f61f3a">
|
|
def prepare_execution_payload(
|
|
state: BeaconState,
|
|
safe_block_hash: Hash32,
|
|
finalized_block_hash: Hash32,
|
|
suggested_fee_recipient: ExecutionAddress,
|
|
execution_engine: ExecutionEngine,
|
|
) -> Optional[PayloadId]:
|
|
# Verify consistency of the parent hash with respect to the previous execution payload header
|
|
parent_hash = state.latest_execution_payload_header.block_hash
|
|
|
|
# Set the forkchoice head and initiate the payload build process
|
|
withdrawals, _ = get_expected_withdrawals(state)
|
|
|
|
payload_attributes = PayloadAttributes(
|
|
timestamp=compute_time_at_slot(state, state.slot),
|
|
prev_randao=get_randao_mix(state, get_current_epoch(state)),
|
|
suggested_fee_recipient=suggested_fee_recipient,
|
|
withdrawals=withdrawals,
|
|
# [New in Deneb:EIP4788]
|
|
parent_beacon_block_root=hash_tree_root(state.latest_block_header),
|
|
)
|
|
return execution_engine.notify_forkchoice_updated(
|
|
head_block_hash=parent_hash,
|
|
safe_block_hash=safe_block_hash,
|
|
finalized_block_hash=finalized_block_hash,
|
|
payload_attributes=payload_attributes,
|
|
)
|
|
</spec>
|
|
|
|
- name: prepare_execution_payload#electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="prepare_execution_payload" fork="electra" hash="5414b883">
|
|
def prepare_execution_payload(
|
|
state: BeaconState,
|
|
safe_block_hash: Hash32,
|
|
finalized_block_hash: Hash32,
|
|
suggested_fee_recipient: ExecutionAddress,
|
|
execution_engine: ExecutionEngine,
|
|
) -> Optional[PayloadId]:
|
|
# Verify consistency of the parent hash with respect to the previous execution payload header
|
|
parent_hash = state.latest_execution_payload_header.block_hash
|
|
|
|
# [Modified in EIP7251]
|
|
# Set the forkchoice head and initiate the payload build process
|
|
withdrawals, _, _ = get_expected_withdrawals(state)
|
|
|
|
payload_attributes = PayloadAttributes(
|
|
timestamp=compute_time_at_slot(state, state.slot),
|
|
prev_randao=get_randao_mix(state, get_current_epoch(state)),
|
|
suggested_fee_recipient=suggested_fee_recipient,
|
|
withdrawals=withdrawals,
|
|
parent_beacon_block_root=hash_tree_root(state.latest_block_header),
|
|
)
|
|
return execution_engine.notify_forkchoice_updated(
|
|
head_block_hash=parent_hash,
|
|
safe_block_hash=safe_block_hash,
|
|
finalized_block_hash=finalized_block_hash,
|
|
payload_attributes=payload_attributes,
|
|
)
|
|
</spec>
|
|
|
|
- name: process_attestation#phase0
|
|
sources:
|
|
- file: beacon-chain/core/blocks/attestation.go
|
|
search: func ProcessAttestationNoVerifySignature(
|
|
spec: |
|
|
<spec fn="process_attestation" fork="phase0" hash="d8e86aa9">
|
|
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
|
data = attestation.data
|
|
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
|
assert data.target.epoch == compute_epoch_at_slot(data.slot)
|
|
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
|
|
assert data.index < get_committee_count_per_slot(state, data.target.epoch)
|
|
|
|
committee = get_beacon_committee(state, data.slot, data.index)
|
|
assert len(attestation.aggregation_bits) == len(committee)
|
|
|
|
pending_attestation = PendingAttestation(
|
|
aggregation_bits=attestation.aggregation_bits,
|
|
data=data,
|
|
inclusion_delay=state.slot - data.slot,
|
|
proposer_index=get_beacon_proposer_index(state),
|
|
)
|
|
|
|
if data.target.epoch == get_current_epoch(state):
|
|
assert data.source == state.current_justified_checkpoint
|
|
state.current_epoch_attestations.append(pending_attestation)
|
|
else:
|
|
assert data.source == state.previous_justified_checkpoint
|
|
state.previous_epoch_attestations.append(pending_attestation)
|
|
|
|
# Verify signature
|
|
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
|
|
</spec>
|
|
|
|
- name: process_attestation#altair
|
|
sources:
|
|
- file: beacon-chain/core/altair/attestation.go
|
|
search: func ProcessAttestationNoVerifySignature(
|
|
spec: |
|
|
<spec fn="process_attestation" fork="altair" hash="5bec7f7a">
|
|
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
|
data = attestation.data
|
|
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
|
assert data.target.epoch == compute_epoch_at_slot(data.slot)
|
|
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
|
|
assert data.index < get_committee_count_per_slot(state, data.target.epoch)
|
|
|
|
committee = get_beacon_committee(state, data.slot, data.index)
|
|
assert len(attestation.aggregation_bits) == len(committee)
|
|
|
|
# Participation flag indices
|
|
participation_flag_indices = get_attestation_participation_flag_indices(
|
|
state, data, state.slot - data.slot
|
|
)
|
|
|
|
# Verify signature
|
|
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
|
|
|
|
# Update epoch participation flags
|
|
if data.target.epoch == get_current_epoch(state):
|
|
epoch_participation = state.current_epoch_participation
|
|
else:
|
|
epoch_participation = state.previous_epoch_participation
|
|
|
|
proposer_reward_numerator = 0
|
|
for index in get_attesting_indices(state, attestation):
|
|
for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS):
|
|
if flag_index in participation_flag_indices and not has_flag(
|
|
epoch_participation[index], flag_index
|
|
):
|
|
epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
|
|
proposer_reward_numerator += get_base_reward(state, index) * weight
|
|
|
|
# Reward proposer
|
|
proposer_reward_denominator = (
|
|
(WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
|
|
)
|
|
proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
|
|
increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
|
</spec>
|
|
|
|
- name: process_attestation#deneb
|
|
sources:
|
|
- file: beacon-chain/core/altair/attestation.go
|
|
search: func ProcessAttestationNoVerifySignature(
|
|
spec: |
|
|
<spec fn="process_attestation" fork="deneb" hash="80a3331f">
|
|
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
|
data = attestation.data
|
|
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
|
assert data.target.epoch == compute_epoch_at_slot(data.slot)
|
|
# [Modified in Deneb:EIP7045]
|
|
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot
|
|
assert data.index < get_committee_count_per_slot(state, data.target.epoch)
|
|
|
|
committee = get_beacon_committee(state, data.slot, data.index)
|
|
assert len(attestation.aggregation_bits) == len(committee)
|
|
|
|
# Participation flag indices
|
|
participation_flag_indices = get_attestation_participation_flag_indices(
|
|
state, data, state.slot - data.slot
|
|
)
|
|
|
|
# Verify signature
|
|
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
|
|
|
|
# Update epoch participation flags
|
|
if data.target.epoch == get_current_epoch(state):
|
|
epoch_participation = state.current_epoch_participation
|
|
else:
|
|
epoch_participation = state.previous_epoch_participation
|
|
|
|
proposer_reward_numerator = 0
|
|
for index in get_attesting_indices(state, attestation):
|
|
for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS):
|
|
if flag_index in participation_flag_indices and not has_flag(
|
|
epoch_participation[index], flag_index
|
|
):
|
|
epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
|
|
proposer_reward_numerator += get_base_reward(state, index) * weight
|
|
|
|
# Reward proposer
|
|
proposer_reward_denominator = (
|
|
(WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
|
|
)
|
|
proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
|
|
increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
|
</spec>
|
|
|
|
- name: process_attestation#electra
|
|
sources:
|
|
- file: beacon-chain/core/altair/attestation.go
|
|
search: func ProcessAttestationNoVerifySignature(
|
|
spec: |
|
|
<spec fn="process_attestation" fork="electra" hash="52d8bb9a">
|
|
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
|
data = attestation.data
|
|
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
|
assert data.target.epoch == compute_epoch_at_slot(data.slot)
|
|
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot
|
|
|
|
# [Modified in Electra:EIP7549]
|
|
assert data.index == 0
|
|
committee_indices = get_committee_indices(attestation.committee_bits)
|
|
committee_offset = 0
|
|
for committee_index in committee_indices:
|
|
assert committee_index < get_committee_count_per_slot(state, data.target.epoch)
|
|
committee = get_beacon_committee(state, data.slot, committee_index)
|
|
committee_attesters = set(
|
|
attester_index
|
|
for i, attester_index in enumerate(committee)
|
|
if attestation.aggregation_bits[committee_offset + i]
|
|
)
|
|
assert len(committee_attesters) > 0
|
|
committee_offset += len(committee)
|
|
|
|
# Bitfield length matches total number of participants
|
|
assert len(attestation.aggregation_bits) == committee_offset
|
|
|
|
# Participation flag indices
|
|
participation_flag_indices = get_attestation_participation_flag_indices(
|
|
state, data, state.slot - data.slot
|
|
)
|
|
|
|
# Verify signature
|
|
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
|
|
|
|
# Update epoch participation flags
|
|
if data.target.epoch == get_current_epoch(state):
|
|
epoch_participation = state.current_epoch_participation
|
|
else:
|
|
epoch_participation = state.previous_epoch_participation
|
|
|
|
proposer_reward_numerator = 0
|
|
for index in get_attesting_indices(state, attestation):
|
|
for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS):
|
|
if flag_index in participation_flag_indices and not has_flag(
|
|
epoch_participation[index], flag_index
|
|
):
|
|
epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
|
|
proposer_reward_numerator += get_base_reward(state, index) * weight
|
|
|
|
# Reward proposer
|
|
proposer_reward_denominator = (
|
|
(WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
|
|
)
|
|
proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
|
|
increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
|
</spec>
|
|
|
|
- name: process_attester_slashing
|
|
sources:
|
|
- file: beacon-chain/core/blocks/attester_slashing.go
|
|
search: func ProcessAttesterSlashing(
|
|
spec: |
|
|
<spec fn="process_attester_slashing" fork="phase0" hash="e47f6e1e">
|
|
def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None:
|
|
attestation_1 = attester_slashing.attestation_1
|
|
attestation_2 = attester_slashing.attestation_2
|
|
assert is_slashable_attestation_data(attestation_1.data, attestation_2.data)
|
|
assert is_valid_indexed_attestation(state, attestation_1)
|
|
assert is_valid_indexed_attestation(state, attestation_2)
|
|
|
|
slashed_any = False
|
|
indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices)
|
|
for index in sorted(indices):
|
|
if is_slashable_validator(state.validators[index], get_current_epoch(state)):
|
|
slash_validator(state, index)
|
|
slashed_any = True
|
|
assert slashed_any
|
|
</spec>
|
|
|
|
- name: process_block#phase0
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition_no_verify_sig.go
|
|
search: func ProcessBlockNoVerifyAnySig(
|
|
spec: |
|
|
<spec fn="process_block" fork="phase0" hash="188d4c44">
|
|
def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
|
process_block_header(state, block)
|
|
process_randao(state, block.body)
|
|
process_eth1_data(state, block.body)
|
|
process_operations(state, block.body)
|
|
</spec>
|
|
|
|
- name: process_block#altair
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition_no_verify_sig.go
|
|
search: func ProcessBlockNoVerifyAnySig(
|
|
spec: |
|
|
<spec fn="process_block" fork="altair" hash="39d1d830">
|
|
def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
|
process_block_header(state, block)
|
|
process_randao(state, block.body)
|
|
process_eth1_data(state, block.body)
|
|
# [Modified in Altair]
|
|
process_operations(state, block.body)
|
|
# [New in Altair]
|
|
process_sync_aggregate(state, block.body.sync_aggregate)
|
|
</spec>
|
|
|
|
- name: process_block#bellatrix
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition_no_verify_sig.go
|
|
search: func ProcessBlockNoVerifyAnySig(
|
|
spec: |
|
|
<spec fn="process_block" fork="bellatrix" hash="623f360a">
|
|
def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
|
process_block_header(state, block)
|
|
if is_execution_enabled(state, block.body):
|
|
# [New in Bellatrix]
|
|
process_execution_payload(state, block.body, EXECUTION_ENGINE)
|
|
process_randao(state, block.body)
|
|
process_eth1_data(state, block.body)
|
|
process_operations(state, block.body)
|
|
process_sync_aggregate(state, block.body.sync_aggregate)
|
|
</spec>
|
|
|
|
- name: process_block#capella
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition_no_verify_sig.go
|
|
search: func ProcessBlockNoVerifyAnySig(
|
|
spec: |
|
|
<spec fn="process_block" fork="capella" hash="60e90ef7">
|
|
def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
|
process_block_header(state, block)
|
|
# [Modified in Capella]
|
|
# Removed `is_execution_enabled` call
|
|
# [New in Capella]
|
|
process_withdrawals(state, block.body.execution_payload)
|
|
# [Modified in Capella]
|
|
process_execution_payload(state, block.body, EXECUTION_ENGINE)
|
|
process_randao(state, block.body)
|
|
process_eth1_data(state, block.body)
|
|
# [Modified in Capella]
|
|
process_operations(state, block.body)
|
|
process_sync_aggregate(state, block.body.sync_aggregate)
|
|
</spec>
|
|
|
|
- name: process_block#electra
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition_no_verify_sig.go
|
|
search: func ProcessBlockNoVerifyAnySig(
|
|
spec: |
|
|
<spec fn="process_block" fork="electra" hash="26d93faf">
|
|
def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
|
process_block_header(state, block)
|
|
# [Modified in Electra:EIP7251]
|
|
process_withdrawals(state, block.body.execution_payload)
|
|
# [Modified in Electra:EIP6110]
|
|
process_execution_payload(state, block.body, EXECUTION_ENGINE)
|
|
process_randao(state, block.body)
|
|
process_eth1_data(state, block.body)
|
|
# [Modified in Electra:EIP6110:EIP7002:EIP7549:EIP7251]
|
|
process_operations(state, block.body)
|
|
process_sync_aggregate(state, block.body.sync_aggregate)
|
|
</spec>
|
|
|
|
- name: process_block_header
|
|
sources:
|
|
- file: beacon-chain/core/blocks/header.go
|
|
search: func ProcessBlockHeader(
|
|
spec: |
|
|
<spec fn="process_block_header" fork="phase0" hash="e782040b">
|
|
def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
|
|
# Verify that the slots match
|
|
assert block.slot == state.slot
|
|
# Verify that the block is newer than latest block header
|
|
assert block.slot > state.latest_block_header.slot
|
|
# Verify that proposer index is the correct index
|
|
assert block.proposer_index == get_beacon_proposer_index(state)
|
|
# Verify that the parent matches
|
|
assert block.parent_root == hash_tree_root(state.latest_block_header)
|
|
# Cache current block as the new latest block
|
|
state.latest_block_header = BeaconBlockHeader(
|
|
slot=block.slot,
|
|
proposer_index=block.proposer_index,
|
|
parent_root=block.parent_root,
|
|
state_root=Bytes32(), # Overwritten in the next process_slot call
|
|
body_root=hash_tree_root(block.body),
|
|
)
|
|
|
|
# Verify proposer is not slashed
|
|
proposer = state.validators[block.proposer_index]
|
|
assert not proposer.slashed
|
|
</spec>
|
|
|
|
- name: process_bls_to_execution_change
|
|
sources:
|
|
- file: beacon-chain/core/blocks/withdrawals.go
|
|
search: func ProcessBLSToExecutionChanges(
|
|
spec: |
|
|
<spec fn="process_bls_to_execution_change" fork="capella" hash="1fade4f1">
|
|
def process_bls_to_execution_change(
|
|
state: BeaconState, signed_address_change: SignedBLSToExecutionChange
|
|
) -> None:
|
|
address_change = signed_address_change.message
|
|
|
|
assert address_change.validator_index < len(state.validators)
|
|
|
|
validator = state.validators[address_change.validator_index]
|
|
|
|
assert validator.withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX
|
|
assert validator.withdrawal_credentials[1:] == hash(address_change.from_bls_pubkey)[1:]
|
|
|
|
# Fork-agnostic domain since address changes are valid across forks
|
|
domain = compute_domain(
|
|
DOMAIN_BLS_TO_EXECUTION_CHANGE, genesis_validators_root=state.genesis_validators_root
|
|
)
|
|
signing_root = compute_signing_root(address_change, domain)
|
|
assert bls.Verify(address_change.from_bls_pubkey, signing_root, signed_address_change.signature)
|
|
|
|
validator.withdrawal_credentials = (
|
|
ETH1_ADDRESS_WITHDRAWAL_PREFIX + b"\x00" * 11 + address_change.to_execution_address
|
|
)
|
|
</spec>
|
|
|
|
- name: process_consolidation_request
|
|
sources:
|
|
- file: beacon-chain/core/electra/consolidations.go
|
|
search: func ProcessConsolidationRequests(
|
|
spec: |
|
|
<spec fn="process_consolidation_request" fork="electra" hash="4da4b0fb">
|
|
def process_consolidation_request(
|
|
state: BeaconState, consolidation_request: ConsolidationRequest
|
|
) -> None:
|
|
if is_valid_switch_to_compounding_request(state, consolidation_request):
|
|
validator_pubkeys = [v.pubkey for v in state.validators]
|
|
request_source_pubkey = consolidation_request.source_pubkey
|
|
source_index = ValidatorIndex(validator_pubkeys.index(request_source_pubkey))
|
|
switch_to_compounding_validator(state, source_index)
|
|
return
|
|
|
|
# Verify that source != target, so a consolidation cannot be used as an exit
|
|
if consolidation_request.source_pubkey == consolidation_request.target_pubkey:
|
|
return
|
|
# If the pending consolidations queue is full, consolidation requests are ignored
|
|
if len(state.pending_consolidations) == PENDING_CONSOLIDATIONS_LIMIT:
|
|
return
|
|
# If there is too little available consolidation churn limit, consolidation requests are ignored
|
|
if get_consolidation_churn_limit(state) <= MIN_ACTIVATION_BALANCE:
|
|
return
|
|
|
|
validator_pubkeys = [v.pubkey for v in state.validators]
|
|
# Verify pubkeys exists
|
|
request_source_pubkey = consolidation_request.source_pubkey
|
|
request_target_pubkey = consolidation_request.target_pubkey
|
|
if request_source_pubkey not in validator_pubkeys:
|
|
return
|
|
if request_target_pubkey not in validator_pubkeys:
|
|
return
|
|
source_index = ValidatorIndex(validator_pubkeys.index(request_source_pubkey))
|
|
target_index = ValidatorIndex(validator_pubkeys.index(request_target_pubkey))
|
|
source_validator = state.validators[source_index]
|
|
target_validator = state.validators[target_index]
|
|
|
|
# Verify source withdrawal credentials
|
|
has_correct_credential = has_execution_withdrawal_credential(source_validator)
|
|
is_correct_source_address = (
|
|
source_validator.withdrawal_credentials[12:] == consolidation_request.source_address
|
|
)
|
|
if not (has_correct_credential and is_correct_source_address):
|
|
return
|
|
|
|
# Verify that target has compounding withdrawal credentials
|
|
if not has_compounding_withdrawal_credential(target_validator):
|
|
return
|
|
|
|
# Verify the source and the target are active
|
|
current_epoch = get_current_epoch(state)
|
|
if not is_active_validator(source_validator, current_epoch):
|
|
return
|
|
if not is_active_validator(target_validator, current_epoch):
|
|
return
|
|
# Verify exits for source and target have not been initiated
|
|
if source_validator.exit_epoch != FAR_FUTURE_EPOCH:
|
|
return
|
|
if target_validator.exit_epoch != FAR_FUTURE_EPOCH:
|
|
return
|
|
# Verify the source has been active long enough
|
|
if current_epoch < source_validator.activation_epoch + SHARD_COMMITTEE_PERIOD:
|
|
return
|
|
# Verify the source has no pending withdrawals in the queue
|
|
if get_pending_balance_to_withdraw(state, source_index) > 0:
|
|
return
|
|
|
|
# Initiate source validator exit and append pending consolidation
|
|
source_validator.exit_epoch = compute_consolidation_epoch_and_update_churn(
|
|
state, source_validator.effective_balance
|
|
)
|
|
source_validator.withdrawable_epoch = Epoch(
|
|
source_validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
|
)
|
|
state.pending_consolidations.append(
|
|
PendingConsolidation(source_index=source_index, target_index=target_index)
|
|
)
|
|
</spec>
|
|
|
|
- name: process_deposit#phase0
|
|
sources:
|
|
- file: beacon-chain/core/altair/deposit.go
|
|
search: func ProcessDeposit(
|
|
spec: |
|
|
<spec fn="process_deposit" fork="phase0" hash="ee6b3afe">
|
|
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
|
# Verify the Merkle branch
|
|
assert is_valid_merkle_branch(
|
|
leaf=hash_tree_root(deposit.data),
|
|
branch=deposit.proof,
|
|
# Add 1 for the List length mix-in
|
|
depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1,
|
|
index=state.eth1_deposit_index,
|
|
root=state.eth1_data.deposit_root,
|
|
)
|
|
|
|
# Deposits must be processed in order
|
|
state.eth1_deposit_index += 1
|
|
|
|
apply_deposit(
|
|
state=state,
|
|
pubkey=deposit.data.pubkey,
|
|
withdrawal_credentials=deposit.data.withdrawal_credentials,
|
|
amount=deposit.data.amount,
|
|
signature=deposit.data.signature,
|
|
)
|
|
</spec>
|
|
|
|
- name: process_deposit#electra
|
|
sources:
|
|
- file: beacon-chain/core/electra/deposits.go
|
|
search: func ProcessDeposit(
|
|
spec: |
|
|
<spec fn="process_deposit" fork="electra" hash="b75831d0">
|
|
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
|
# Verify the Merkle branch
|
|
assert is_valid_merkle_branch(
|
|
leaf=hash_tree_root(deposit.data),
|
|
branch=deposit.proof,
|
|
# Add 1 for the List length mix-in
|
|
depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1,
|
|
index=state.eth1_deposit_index,
|
|
root=state.eth1_data.deposit_root,
|
|
)
|
|
|
|
# Deposits must be processed in order
|
|
state.eth1_deposit_index += 1
|
|
|
|
# [Modified in Electra:EIP7251]
|
|
apply_deposit(
|
|
state=state,
|
|
pubkey=deposit.data.pubkey,
|
|
withdrawal_credentials=deposit.data.withdrawal_credentials,
|
|
amount=deposit.data.amount,
|
|
signature=deposit.data.signature,
|
|
)
|
|
</spec>
|
|
|
|
- name: process_deposit_request
|
|
sources:
|
|
- file: beacon-chain/core/electra/deposits.go
|
|
search: func processDepositRequest(
|
|
spec: |
|
|
<spec fn="process_deposit_request" fork="electra" hash="547c4a35">
|
|
def process_deposit_request(state: BeaconState, deposit_request: DepositRequest) -> None:
|
|
# Set deposit request start index
|
|
if state.deposit_requests_start_index == UNSET_DEPOSIT_REQUESTS_START_INDEX:
|
|
state.deposit_requests_start_index = deposit_request.index
|
|
|
|
# Create pending deposit
|
|
state.pending_deposits.append(
|
|
PendingDeposit(
|
|
pubkey=deposit_request.pubkey,
|
|
withdrawal_credentials=deposit_request.withdrawal_credentials,
|
|
amount=deposit_request.amount,
|
|
signature=deposit_request.signature,
|
|
slot=state.slot,
|
|
)
|
|
)
|
|
</spec>
|
|
|
|
- name: process_effective_balance_updates#phase0
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessEffectiveBalanceUpdates(
|
|
spec: |
|
|
<spec fn="process_effective_balance_updates" fork="phase0" hash="678d0327">
|
|
def process_effective_balance_updates(state: BeaconState) -> None:
|
|
# Update effective balances with hysteresis
|
|
for index, validator in enumerate(state.validators):
|
|
balance = state.balances[index]
|
|
HYSTERESIS_INCREMENT = uint64(EFFECTIVE_BALANCE_INCREMENT // HYSTERESIS_QUOTIENT)
|
|
DOWNWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER
|
|
UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER
|
|
if (
|
|
balance + DOWNWARD_THRESHOLD < validator.effective_balance
|
|
or validator.effective_balance + UPWARD_THRESHOLD < balance
|
|
):
|
|
validator.effective_balance = min(
|
|
balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE
|
|
)
|
|
</spec>
|
|
|
|
- name: process_effective_balance_updates#electra
|
|
sources:
|
|
- file: beacon-chain/core/electra/effective_balance_updates.go
|
|
search: func ProcessEffectiveBalanceUpdates(
|
|
spec: |
|
|
<spec fn="process_effective_balance_updates" fork="electra" hash="6b447363">
|
|
def process_effective_balance_updates(state: BeaconState) -> None:
|
|
# Update effective balances with hysteresis
|
|
for index, validator in enumerate(state.validators):
|
|
balance = state.balances[index]
|
|
HYSTERESIS_INCREMENT = uint64(EFFECTIVE_BALANCE_INCREMENT // HYSTERESIS_QUOTIENT)
|
|
DOWNWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER
|
|
UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER
|
|
# [Modified in Electra:EIP7251]
|
|
max_effective_balance = get_max_effective_balance(validator)
|
|
|
|
if (
|
|
balance + DOWNWARD_THRESHOLD < validator.effective_balance
|
|
or validator.effective_balance + UPWARD_THRESHOLD < balance
|
|
):
|
|
validator.effective_balance = min(
|
|
balance - balance % EFFECTIVE_BALANCE_INCREMENT, max_effective_balance
|
|
)
|
|
</spec>
|
|
|
|
- name: process_epoch#phase0
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition.go
|
|
search: func ProcessEpoch(
|
|
- file: beacon-chain/core/transition/transition.go
|
|
search: func ProcessEpochPrecompute(
|
|
spec: |
|
|
<spec fn="process_epoch" fork="phase0" hash="9b0ba41d">
|
|
def process_epoch(state: BeaconState) -> None:
|
|
process_justification_and_finalization(state)
|
|
process_rewards_and_penalties(state)
|
|
process_registry_updates(state)
|
|
process_slashings(state)
|
|
process_eth1_data_reset(state)
|
|
process_effective_balance_updates(state)
|
|
process_slashings_reset(state)
|
|
process_randao_mixes_reset(state)
|
|
process_historical_roots_update(state)
|
|
process_participation_record_updates(state)
|
|
</spec>
|
|
|
|
- name: process_epoch#altair
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition.go
|
|
search: func ProcessEpoch(
|
|
- file: beacon-chain/core/altair/transition.go
|
|
search: func ProcessEpoch(
|
|
spec: |
|
|
<spec fn="process_epoch" fork="altair" hash="d84c7ecd">
|
|
def process_epoch(state: BeaconState) -> None:
|
|
# [Modified in Altair]
|
|
process_justification_and_finalization(state)
|
|
# [New in Altair]
|
|
process_inactivity_updates(state)
|
|
# [Modified in Altair]
|
|
process_rewards_and_penalties(state)
|
|
process_registry_updates(state)
|
|
# [Modified in Altair]
|
|
process_slashings(state)
|
|
process_eth1_data_reset(state)
|
|
process_effective_balance_updates(state)
|
|
process_slashings_reset(state)
|
|
process_randao_mixes_reset(state)
|
|
process_historical_roots_update(state)
|
|
# [New in Altair]
|
|
process_participation_flag_updates(state)
|
|
# [New in Altair]
|
|
process_sync_committee_updates(state)
|
|
</spec>
|
|
|
|
- name: process_epoch#electra
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition.go
|
|
search: func ProcessEpoch(
|
|
- file: beacon-chain/core/electra/transition.go
|
|
search: func ProcessEpoch(
|
|
spec: |
|
|
<spec fn="process_epoch" fork="electra" hash="505b801d">
|
|
def process_epoch(state: BeaconState) -> None:
|
|
process_justification_and_finalization(state)
|
|
process_inactivity_updates(state)
|
|
process_rewards_and_penalties(state)
|
|
# [Modified in Electra:EIP7251]
|
|
process_registry_updates(state)
|
|
# [Modified in Electra:EIP7251]
|
|
process_slashings(state)
|
|
process_eth1_data_reset(state)
|
|
# [New in Electra:EIP7251]
|
|
process_pending_deposits(state)
|
|
# [New in Electra:EIP7251]
|
|
process_pending_consolidations(state)
|
|
# [Modified in Electra:EIP7251]
|
|
process_effective_balance_updates(state)
|
|
process_slashings_reset(state)
|
|
process_randao_mixes_reset(state)
|
|
process_historical_summaries_update(state)
|
|
process_participation_flag_updates(state)
|
|
process_sync_committee_updates(state)
|
|
</spec>
|
|
|
|
- name: process_epoch#fulu
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition.go
|
|
search: func ProcessEpoch(
|
|
- file: beacon-chain/core/fulu/transition.go
|
|
search: func ProcessEpoch(
|
|
spec: |
|
|
<spec fn="process_epoch" fork="fulu" hash="420a4a68">
|
|
def process_epoch(state: BeaconState) -> None:
|
|
process_justification_and_finalization(state)
|
|
process_inactivity_updates(state)
|
|
process_rewards_and_penalties(state)
|
|
process_registry_updates(state)
|
|
process_slashings(state)
|
|
process_eth1_data_reset(state)
|
|
process_pending_deposits(state)
|
|
process_pending_consolidations(state)
|
|
process_effective_balance_updates(state)
|
|
process_slashings_reset(state)
|
|
process_randao_mixes_reset(state)
|
|
process_historical_summaries_update(state)
|
|
process_participation_flag_updates(state)
|
|
process_sync_committee_updates(state)
|
|
# [New in Fulu:EIP7917]
|
|
process_proposer_lookahead(state)
|
|
</spec>
|
|
|
|
- name: process_eth1_data
|
|
sources:
|
|
- file: beacon-chain/core/blocks/eth1_data.go
|
|
search: func ProcessEth1DataInBlock(
|
|
spec: |
|
|
<spec fn="process_eth1_data" fork="phase0" hash="a320a108">
|
|
def process_eth1_data(state: BeaconState, body: BeaconBlockBody) -> None:
|
|
state.eth1_data_votes.append(body.eth1_data)
|
|
if (
|
|
state.eth1_data_votes.count(body.eth1_data) * 2
|
|
> EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH
|
|
):
|
|
state.eth1_data = body.eth1_data
|
|
</spec>
|
|
|
|
- name: process_eth1_data_reset
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessEth1DataReset(
|
|
spec: |
|
|
<spec fn="process_eth1_data_reset" fork="phase0" hash="c777739e">
|
|
def process_eth1_data_reset(state: BeaconState) -> None:
|
|
next_epoch = Epoch(get_current_epoch(state) + 1)
|
|
# Reset eth1 data votes
|
|
if next_epoch % EPOCHS_PER_ETH1_VOTING_PERIOD == 0:
|
|
state.eth1_data_votes = []
|
|
</spec>
|
|
|
|
- name: process_execution_payload#bellatrix
|
|
sources:
|
|
- file: beacon-chain/core/blocks/payload.go
|
|
search: func ProcessPayload(
|
|
spec: |
|
|
<spec fn="process_execution_payload" fork="bellatrix" hash="d1d1e466">
|
|
def process_execution_payload(
|
|
state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine
|
|
) -> None:
|
|
payload = body.execution_payload
|
|
|
|
# Verify consistency of the parent hash with respect to the previous execution payload header
|
|
if is_merge_transition_complete(state):
|
|
assert payload.parent_hash == state.latest_execution_payload_header.block_hash
|
|
# Verify prev_randao
|
|
assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state))
|
|
# Verify timestamp
|
|
assert payload.timestamp == compute_time_at_slot(state, state.slot)
|
|
# Verify the execution payload is valid
|
|
assert execution_engine.verify_and_notify_new_payload(
|
|
NewPayloadRequest(execution_payload=payload)
|
|
)
|
|
# Cache execution payload header
|
|
state.latest_execution_payload_header = ExecutionPayloadHeader(
|
|
parent_hash=payload.parent_hash,
|
|
fee_recipient=payload.fee_recipient,
|
|
state_root=payload.state_root,
|
|
receipts_root=payload.receipts_root,
|
|
logs_bloom=payload.logs_bloom,
|
|
prev_randao=payload.prev_randao,
|
|
block_number=payload.block_number,
|
|
gas_limit=payload.gas_limit,
|
|
gas_used=payload.gas_used,
|
|
timestamp=payload.timestamp,
|
|
extra_data=payload.extra_data,
|
|
base_fee_per_gas=payload.base_fee_per_gas,
|
|
block_hash=payload.block_hash,
|
|
transactions_root=hash_tree_root(payload.transactions),
|
|
)
|
|
</spec>
|
|
|
|
- name: process_execution_payload#capella
|
|
sources:
|
|
- file: beacon-chain/core/blocks/payload.go
|
|
search: func ProcessPayload(
|
|
spec: |
|
|
<spec fn="process_execution_payload" fork="capella" hash="8da02dfc">
|
|
def process_execution_payload(
|
|
state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine
|
|
) -> None:
|
|
payload = body.execution_payload
|
|
# [Modified in Capella]
|
|
# Removed `is_merge_transition_complete` check
|
|
# Verify consistency of the parent hash with respect to the previous execution payload header
|
|
assert payload.parent_hash == state.latest_execution_payload_header.block_hash
|
|
# Verify prev_randao
|
|
assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state))
|
|
# Verify timestamp
|
|
assert payload.timestamp == compute_time_at_slot(state, state.slot)
|
|
# Verify the execution payload is valid
|
|
assert execution_engine.verify_and_notify_new_payload(
|
|
NewPayloadRequest(execution_payload=payload)
|
|
)
|
|
# Cache execution payload header
|
|
state.latest_execution_payload_header = ExecutionPayloadHeader(
|
|
parent_hash=payload.parent_hash,
|
|
fee_recipient=payload.fee_recipient,
|
|
state_root=payload.state_root,
|
|
receipts_root=payload.receipts_root,
|
|
logs_bloom=payload.logs_bloom,
|
|
prev_randao=payload.prev_randao,
|
|
block_number=payload.block_number,
|
|
gas_limit=payload.gas_limit,
|
|
gas_used=payload.gas_used,
|
|
timestamp=payload.timestamp,
|
|
extra_data=payload.extra_data,
|
|
base_fee_per_gas=payload.base_fee_per_gas,
|
|
block_hash=payload.block_hash,
|
|
transactions_root=hash_tree_root(payload.transactions),
|
|
# [New in Capella]
|
|
withdrawals_root=hash_tree_root(payload.withdrawals),
|
|
)
|
|
</spec>
|
|
|
|
- name: process_execution_payload#deneb
|
|
sources:
|
|
- file: beacon-chain/core/blocks/payload.go
|
|
search: func ProcessPayload(
|
|
spec: |
|
|
<spec fn="process_execution_payload" fork="deneb" hash="5e93d3cd">
|
|
def process_execution_payload(
|
|
state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine
|
|
) -> None:
|
|
payload = body.execution_payload
|
|
|
|
# Verify consistency of the parent hash with respect to the previous execution payload header
|
|
assert payload.parent_hash == state.latest_execution_payload_header.block_hash
|
|
# Verify prev_randao
|
|
assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state))
|
|
# Verify timestamp
|
|
assert payload.timestamp == compute_time_at_slot(state, state.slot)
|
|
# [New in Deneb:EIP4844]
|
|
# Verify commitments are under limit
|
|
assert len(body.blob_kzg_commitments) <= MAX_BLOBS_PER_BLOCK
|
|
|
|
# [New in Deneb:EIP4844]
|
|
# Compute list of versioned hashes
|
|
versioned_hashes = [
|
|
kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments
|
|
]
|
|
|
|
# Verify the execution payload is valid
|
|
assert execution_engine.verify_and_notify_new_payload(
|
|
NewPayloadRequest(
|
|
execution_payload=payload,
|
|
# [New in Deneb:EIP4844]
|
|
versioned_hashes=versioned_hashes,
|
|
# [New in Deneb:EIP4788]
|
|
parent_beacon_block_root=state.latest_block_header.parent_root,
|
|
)
|
|
)
|
|
|
|
# Cache execution payload header
|
|
state.latest_execution_payload_header = ExecutionPayloadHeader(
|
|
parent_hash=payload.parent_hash,
|
|
fee_recipient=payload.fee_recipient,
|
|
state_root=payload.state_root,
|
|
receipts_root=payload.receipts_root,
|
|
logs_bloom=payload.logs_bloom,
|
|
prev_randao=payload.prev_randao,
|
|
block_number=payload.block_number,
|
|
gas_limit=payload.gas_limit,
|
|
gas_used=payload.gas_used,
|
|
timestamp=payload.timestamp,
|
|
extra_data=payload.extra_data,
|
|
base_fee_per_gas=payload.base_fee_per_gas,
|
|
block_hash=payload.block_hash,
|
|
transactions_root=hash_tree_root(payload.transactions),
|
|
withdrawals_root=hash_tree_root(payload.withdrawals),
|
|
# [New in Deneb:EIP4844]
|
|
blob_gas_used=payload.blob_gas_used,
|
|
# [New in Deneb:EIP4844]
|
|
excess_blob_gas=payload.excess_blob_gas,
|
|
)
|
|
</spec>
|
|
|
|
- name: process_execution_payload#electra
|
|
sources:
|
|
- file: beacon-chain/core/blocks/payload.go
|
|
search: func ProcessPayload(
|
|
spec: |
|
|
<spec fn="process_execution_payload" fork="electra" hash="078e4a6c">
|
|
def process_execution_payload(
|
|
state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine
|
|
) -> None:
|
|
payload = body.execution_payload
|
|
|
|
# Verify consistency of the parent hash with respect to the previous execution payload header
|
|
assert payload.parent_hash == state.latest_execution_payload_header.block_hash
|
|
# Verify prev_randao
|
|
assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state))
|
|
# Verify timestamp
|
|
assert payload.timestamp == compute_time_at_slot(state, state.slot)
|
|
# [Modified in Electra:EIP7691]
|
|
# Verify commitments are under limit
|
|
assert len(body.blob_kzg_commitments) <= MAX_BLOBS_PER_BLOCK_ELECTRA
|
|
|
|
# Compute list of versioned hashes
|
|
versioned_hashes = [
|
|
kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments
|
|
]
|
|
|
|
# Verify the execution payload is valid
|
|
assert execution_engine.verify_and_notify_new_payload(
|
|
NewPayloadRequest(
|
|
execution_payload=payload,
|
|
versioned_hashes=versioned_hashes,
|
|
parent_beacon_block_root=state.latest_block_header.parent_root,
|
|
# [New in Electra]
|
|
execution_requests=body.execution_requests,
|
|
)
|
|
)
|
|
|
|
# Cache execution payload header
|
|
state.latest_execution_payload_header = ExecutionPayloadHeader(
|
|
parent_hash=payload.parent_hash,
|
|
fee_recipient=payload.fee_recipient,
|
|
state_root=payload.state_root,
|
|
receipts_root=payload.receipts_root,
|
|
logs_bloom=payload.logs_bloom,
|
|
prev_randao=payload.prev_randao,
|
|
block_number=payload.block_number,
|
|
gas_limit=payload.gas_limit,
|
|
gas_used=payload.gas_used,
|
|
timestamp=payload.timestamp,
|
|
extra_data=payload.extra_data,
|
|
base_fee_per_gas=payload.base_fee_per_gas,
|
|
block_hash=payload.block_hash,
|
|
transactions_root=hash_tree_root(payload.transactions),
|
|
withdrawals_root=hash_tree_root(payload.withdrawals),
|
|
blob_gas_used=payload.blob_gas_used,
|
|
excess_blob_gas=payload.excess_blob_gas,
|
|
)
|
|
</spec>
|
|
|
|
- name: process_execution_payload#fulu
|
|
sources:
|
|
- file: beacon-chain/core/blocks/payload.go
|
|
search: func ProcessPayload(
|
|
spec: |
|
|
<spec fn="process_execution_payload" fork="fulu" hash="adc1f5c6">
|
|
def process_execution_payload(
|
|
state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine
|
|
) -> None:
|
|
payload = body.execution_payload
|
|
|
|
# Verify consistency of the parent hash with respect to the previous execution payload header
|
|
assert payload.parent_hash == state.latest_execution_payload_header.block_hash
|
|
# Verify prev_randao
|
|
assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state))
|
|
# Verify timestamp
|
|
assert payload.timestamp == compute_time_at_slot(state, state.slot)
|
|
# [Modified in Fulu:EIP7892]
|
|
# Verify commitments are under limit
|
|
assert (
|
|
len(body.blob_kzg_commitments)
|
|
<= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block
|
|
)
|
|
|
|
# Compute list of versioned hashes
|
|
versioned_hashes = [
|
|
kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments
|
|
]
|
|
|
|
# Verify the execution payload is valid
|
|
assert execution_engine.verify_and_notify_new_payload(
|
|
NewPayloadRequest(
|
|
execution_payload=payload,
|
|
versioned_hashes=versioned_hashes,
|
|
parent_beacon_block_root=state.latest_block_header.parent_root,
|
|
execution_requests=body.execution_requests,
|
|
)
|
|
)
|
|
|
|
# Cache execution payload header
|
|
state.latest_execution_payload_header = ExecutionPayloadHeader(
|
|
parent_hash=payload.parent_hash,
|
|
fee_recipient=payload.fee_recipient,
|
|
state_root=payload.state_root,
|
|
receipts_root=payload.receipts_root,
|
|
logs_bloom=payload.logs_bloom,
|
|
prev_randao=payload.prev_randao,
|
|
block_number=payload.block_number,
|
|
gas_limit=payload.gas_limit,
|
|
gas_used=payload.gas_used,
|
|
timestamp=payload.timestamp,
|
|
extra_data=payload.extra_data,
|
|
base_fee_per_gas=payload.base_fee_per_gas,
|
|
block_hash=payload.block_hash,
|
|
transactions_root=hash_tree_root(payload.transactions),
|
|
withdrawals_root=hash_tree_root(payload.withdrawals),
|
|
blob_gas_used=payload.blob_gas_used,
|
|
excess_blob_gas=payload.excess_blob_gas,
|
|
)
|
|
</spec>
|
|
|
|
- name: process_historical_roots_update
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessHistoricalDataUpdate(
|
|
spec: |
|
|
<spec fn="process_historical_roots_update" fork="phase0" hash="4cda7ad5">
|
|
def process_historical_roots_update(state: BeaconState) -> None:
|
|
# Set historical root accumulator
|
|
next_epoch = Epoch(get_current_epoch(state) + 1)
|
|
if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0:
|
|
historical_batch = HistoricalBatch(
|
|
block_roots=state.block_roots, state_roots=state.state_roots
|
|
)
|
|
state.historical_roots.append(hash_tree_root(historical_batch))
|
|
</spec>
|
|
|
|
- name: process_historical_summaries_update
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessHistoricalDataUpdate(
|
|
spec: |
|
|
<spec fn="process_historical_summaries_update" fork="capella" hash="8eb6dcbb">
|
|
def process_historical_summaries_update(state: BeaconState) -> None:
|
|
# Set historical block root accumulator.
|
|
next_epoch = Epoch(get_current_epoch(state) + 1)
|
|
if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0:
|
|
historical_summary = HistoricalSummary(
|
|
block_summary_root=hash_tree_root(state.block_roots),
|
|
state_summary_root=hash_tree_root(state.state_roots),
|
|
)
|
|
state.historical_summaries.append(historical_summary)
|
|
</spec>
|
|
|
|
- name: process_inactivity_updates
|
|
sources:
|
|
- file: beacon-chain/core/altair/epoch_precompute.go
|
|
search: func ProcessInactivityScores(
|
|
spec: |
|
|
<spec fn="process_inactivity_updates" fork="altair" hash="4fe14f5a">
|
|
def process_inactivity_updates(state: BeaconState) -> None:
|
|
# Skip the genesis epoch as score updates are based on the previous epoch participation
|
|
if get_current_epoch(state) == GENESIS_EPOCH:
|
|
return
|
|
|
|
for index in get_eligible_validator_indices(state):
|
|
# Increase the inactivity score of inactive validators
|
|
if index in get_unslashed_participating_indices(
|
|
state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state)
|
|
):
|
|
state.inactivity_scores[index] -= min(1, state.inactivity_scores[index])
|
|
else:
|
|
state.inactivity_scores[index] += INACTIVITY_SCORE_BIAS
|
|
# Decrease the inactivity score of all eligible validators during a leak-free epoch
|
|
if not is_in_inactivity_leak(state):
|
|
state.inactivity_scores[index] -= min(
|
|
INACTIVITY_SCORE_RECOVERY_RATE, state.inactivity_scores[index]
|
|
)
|
|
</spec>
|
|
|
|
- name: process_justification_and_finalization#phase0
|
|
sources:
|
|
- file: beacon-chain/core/epoch/precompute/justification_finalization.go
|
|
search: func ProcessJustificationAndFinalizationPreCompute(
|
|
spec: |
|
|
<spec fn="process_justification_and_finalization" fork="phase0" hash="4a2b2108">
|
|
def process_justification_and_finalization(state: BeaconState) -> None:
|
|
# Initial FFG checkpoint values have a `0x00` stub for `root`.
|
|
# Skip FFG updates in the first two epochs to avoid corner cases that might result in modifying this stub.
|
|
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
|
|
return
|
|
previous_attestations = get_matching_target_attestations(state, get_previous_epoch(state))
|
|
current_attestations = get_matching_target_attestations(state, get_current_epoch(state))
|
|
total_active_balance = get_total_active_balance(state)
|
|
previous_target_balance = get_attesting_balance(state, previous_attestations)
|
|
current_target_balance = get_attesting_balance(state, current_attestations)
|
|
weigh_justification_and_finalization(
|
|
state, total_active_balance, previous_target_balance, current_target_balance
|
|
)
|
|
</spec>
|
|
|
|
- name: process_justification_and_finalization#altair
|
|
sources:
|
|
- file: beacon-chain/core/epoch/precompute/justification_finalization.go
|
|
search: func ProcessJustificationAndFinalizationPreCompute(
|
|
spec: |
|
|
<spec fn="process_justification_and_finalization" fork="altair" hash="863cfca6">
|
|
def process_justification_and_finalization(state: BeaconState) -> None:
|
|
# Initial FFG checkpoint values have a `0x00` stub for `root`.
|
|
# Skip FFG updates in the first two epochs to avoid corner cases that might result in modifying this stub.
|
|
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
|
|
return
|
|
previous_indices = get_unslashed_participating_indices(
|
|
state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state)
|
|
)
|
|
current_indices = get_unslashed_participating_indices(
|
|
state, TIMELY_TARGET_FLAG_INDEX, get_current_epoch(state)
|
|
)
|
|
total_active_balance = get_total_active_balance(state)
|
|
previous_target_balance = get_total_balance(state, previous_indices)
|
|
current_target_balance = get_total_balance(state, current_indices)
|
|
weigh_justification_and_finalization(
|
|
state, total_active_balance, previous_target_balance, current_target_balance
|
|
)
|
|
</spec>
|
|
|
|
- name: process_light_client_finality_update
|
|
sources:
|
|
- file: beacon-chain/light-client/lightclient.go
|
|
search: func NewLightClientFinalityUpdateFromBeaconState(
|
|
spec: |
|
|
<spec fn="process_light_client_finality_update" fork="altair" hash="befaba5b">
|
|
def process_light_client_finality_update(
|
|
store: LightClientStore,
|
|
finality_update: LightClientFinalityUpdate,
|
|
current_slot: Slot,
|
|
genesis_validators_root: Root,
|
|
) -> None:
|
|
update = LightClientUpdate(
|
|
attested_header=finality_update.attested_header,
|
|
next_sync_committee=SyncCommittee(),
|
|
next_sync_committee_branch=NextSyncCommitteeBranch(),
|
|
finalized_header=finality_update.finalized_header,
|
|
finality_branch=finality_update.finality_branch,
|
|
sync_aggregate=finality_update.sync_aggregate,
|
|
signature_slot=finality_update.signature_slot,
|
|
)
|
|
process_light_client_update(store, update, current_slot, genesis_validators_root)
|
|
</spec>
|
|
|
|
- name: process_light_client_optimistic_update
|
|
sources:
|
|
- file: beacon-chain/light-client/lightclient.go
|
|
search: func NewLightClientOptimisticUpdateFromBeaconState(
|
|
spec: |
|
|
<spec fn="process_light_client_optimistic_update" fork="altair" hash="06c0365a">
|
|
def process_light_client_optimistic_update(
|
|
store: LightClientStore,
|
|
optimistic_update: LightClientOptimisticUpdate,
|
|
current_slot: Slot,
|
|
genesis_validators_root: Root,
|
|
) -> None:
|
|
update = LightClientUpdate(
|
|
attested_header=optimistic_update.attested_header,
|
|
next_sync_committee=SyncCommittee(),
|
|
next_sync_committee_branch=NextSyncCommitteeBranch(),
|
|
finalized_header=LightClientHeader(),
|
|
finality_branch=FinalityBranch(),
|
|
sync_aggregate=optimistic_update.sync_aggregate,
|
|
signature_slot=optimistic_update.signature_slot,
|
|
)
|
|
process_light_client_update(store, update, current_slot, genesis_validators_root)
|
|
</spec>
|
|
|
|
- name: process_light_client_store_force_update
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_light_client_store_force_update" fork="altair" hash="fbb77069">
|
|
def process_light_client_store_force_update(store: LightClientStore, current_slot: Slot) -> None:
|
|
if (
|
|
current_slot > store.finalized_header.beacon.slot + UPDATE_TIMEOUT
|
|
and store.best_valid_update is not None
|
|
):
|
|
# Forced best update when the update timeout has elapsed.
|
|
# Because the apply logic waits for `finalized_header.beacon.slot` to indicate sync committee finality,
|
|
# the `attested_header` may be treated as `finalized_header` in extended periods of non-finality
|
|
# to guarantee progression into later sync committee periods according to `is_better_update`.
|
|
if (
|
|
store.best_valid_update.finalized_header.beacon.slot
|
|
<= store.finalized_header.beacon.slot
|
|
):
|
|
store.best_valid_update.finalized_header = store.best_valid_update.attested_header
|
|
apply_light_client_update(store, store.best_valid_update)
|
|
store.best_valid_update = None
|
|
</spec>
|
|
|
|
- name: process_light_client_update
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_light_client_update" fork="altair" hash="b64a67ff">
|
|
def process_light_client_update(
|
|
store: LightClientStore,
|
|
update: LightClientUpdate,
|
|
current_slot: Slot,
|
|
genesis_validators_root: Root,
|
|
) -> None:
|
|
validate_light_client_update(store, update, current_slot, genesis_validators_root)
|
|
|
|
sync_committee_bits = update.sync_aggregate.sync_committee_bits
|
|
|
|
# Update the best update in case we have to force-update to it if the timeout elapses
|
|
if store.best_valid_update is None or is_better_update(update, store.best_valid_update):
|
|
store.best_valid_update = update
|
|
|
|
# Track the maximum number of active participants in the committee signatures
|
|
store.current_max_active_participants = max(
|
|
store.current_max_active_participants,
|
|
sum(sync_committee_bits),
|
|
)
|
|
|
|
# Update the optimistic header
|
|
if (
|
|
sum(sync_committee_bits) > get_safety_threshold(store)
|
|
and update.attested_header.beacon.slot > store.optimistic_header.beacon.slot
|
|
):
|
|
store.optimistic_header = update.attested_header
|
|
|
|
# Update finalized header
|
|
update_has_finalized_next_sync_committee = (
|
|
not is_next_sync_committee_known(store)
|
|
and is_sync_committee_update(update)
|
|
and is_finality_update(update)
|
|
and (
|
|
compute_sync_committee_period_at_slot(update.finalized_header.beacon.slot)
|
|
== compute_sync_committee_period_at_slot(update.attested_header.beacon.slot)
|
|
)
|
|
)
|
|
if sum(sync_committee_bits) * 3 >= len(sync_committee_bits) * 2 and (
|
|
update.finalized_header.beacon.slot > store.finalized_header.beacon.slot
|
|
or update_has_finalized_next_sync_committee
|
|
):
|
|
# Normal update through 2/3 threshold
|
|
apply_light_client_update(store, update)
|
|
store.best_valid_update = None
|
|
</spec>
|
|
|
|
- name: process_operations#phase0
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition_no_verify_sig.go
|
|
search: func ProcessOperationsNoVerifyAttsSigs(
|
|
spec: |
|
|
<spec fn="process_operations" fork="phase0" hash="eb7114df">
|
|
def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
|
# Verify that outstanding deposits are processed up to the maximum number of deposits
|
|
assert len(body.deposits) == min(
|
|
MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index
|
|
)
|
|
|
|
def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None:
|
|
for operation in operations:
|
|
fn(state, operation)
|
|
|
|
for_ops(body.proposer_slashings, process_proposer_slashing)
|
|
for_ops(body.attester_slashings, process_attester_slashing)
|
|
for_ops(body.attestations, process_attestation)
|
|
for_ops(body.deposits, process_deposit)
|
|
for_ops(body.voluntary_exits, process_voluntary_exit)
|
|
</spec>
|
|
|
|
- name: process_operations#capella
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition_no_verify_sig.go
|
|
search: func ProcessOperationsNoVerifyAttsSigs(
|
|
spec: |
|
|
<spec fn="process_operations" fork="capella" hash="1314042b">
|
|
def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
|
# Verify that outstanding deposits are processed up to the maximum number of deposits
|
|
assert len(body.deposits) == min(
|
|
MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index
|
|
)
|
|
|
|
def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None:
|
|
for operation in operations:
|
|
fn(state, operation)
|
|
|
|
for_ops(body.proposer_slashings, process_proposer_slashing)
|
|
for_ops(body.attester_slashings, process_attester_slashing)
|
|
for_ops(body.attestations, process_attestation)
|
|
for_ops(body.deposits, process_deposit)
|
|
for_ops(body.voluntary_exits, process_voluntary_exit)
|
|
# [New in Capella]
|
|
for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
|
|
</spec>
|
|
|
|
- name: process_operations#electra
|
|
sources:
|
|
- file: beacon-chain/core/electra/transition_no_verify_sig.go
|
|
search: func ProcessOperations(
|
|
spec: |
|
|
<spec fn="process_operations" fork="electra" hash="643f18b4">
|
|
def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
|
# [Modified in Electra:EIP6110]
|
|
# Disable former deposit mechanism once all prior deposits are processed
|
|
eth1_deposit_index_limit = min(
|
|
state.eth1_data.deposit_count, state.deposit_requests_start_index
|
|
)
|
|
if state.eth1_deposit_index < eth1_deposit_index_limit:
|
|
assert len(body.deposits) == min(
|
|
MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index
|
|
)
|
|
else:
|
|
assert len(body.deposits) == 0
|
|
|
|
def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None:
|
|
for operation in operations:
|
|
fn(state, operation)
|
|
|
|
for_ops(body.proposer_slashings, process_proposer_slashing)
|
|
for_ops(body.attester_slashings, process_attester_slashing)
|
|
# [Modified in Electra:EIP7549]
|
|
for_ops(body.attestations, process_attestation)
|
|
for_ops(body.deposits, process_deposit)
|
|
# [Modified in Electra:EIP7251]
|
|
for_ops(body.voluntary_exits, process_voluntary_exit)
|
|
for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
|
|
# [New in Electra:EIP6110]
|
|
for_ops(body.execution_requests.deposits, process_deposit_request)
|
|
# [New in Electra:EIP7002:EIP7251]
|
|
for_ops(body.execution_requests.withdrawals, process_withdrawal_request)
|
|
# [New in Electra:EIP7251]
|
|
for_ops(body.execution_requests.consolidations, process_consolidation_request)
|
|
</spec>
|
|
|
|
- name: process_participation_flag_updates
|
|
sources:
|
|
- file: beacon-chain/core/altair/epoch_spec.go
|
|
search: func ProcessParticipationFlagUpdates(
|
|
spec: |
|
|
<spec fn="process_participation_flag_updates" fork="altair" hash="92852f31">
|
|
def process_participation_flag_updates(state: BeaconState) -> None:
|
|
state.previous_epoch_participation = state.current_epoch_participation
|
|
state.current_epoch_participation = [
|
|
ParticipationFlags(0b0000_0000) for _ in range(len(state.validators))
|
|
]
|
|
</spec>
|
|
|
|
- name: process_participation_record_updates
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessParticipationRecordUpdates(
|
|
spec: |
|
|
<spec fn="process_participation_record_updates" fork="phase0" hash="6a203574">
|
|
def process_participation_record_updates(state: BeaconState) -> None:
|
|
# Rotate current/previous epoch attestations
|
|
state.previous_epoch_attestations = state.current_epoch_attestations
|
|
state.current_epoch_attestations = []
|
|
</spec>
|
|
|
|
- name: process_pending_consolidations
|
|
sources:
|
|
- file: beacon-chain/core/electra/consolidations.go
|
|
search: func ProcessPendingConsolidations(
|
|
spec: |
|
|
<spec fn="process_pending_consolidations" fork="electra" hash="75e32e4b">
|
|
def process_pending_consolidations(state: BeaconState) -> None:
|
|
next_epoch = Epoch(get_current_epoch(state) + 1)
|
|
next_pending_consolidation = 0
|
|
for pending_consolidation in state.pending_consolidations:
|
|
source_validator = state.validators[pending_consolidation.source_index]
|
|
if source_validator.slashed:
|
|
next_pending_consolidation += 1
|
|
continue
|
|
if source_validator.withdrawable_epoch > next_epoch:
|
|
break
|
|
|
|
# Calculate the consolidated balance
|
|
source_effective_balance = min(
|
|
state.balances[pending_consolidation.source_index], source_validator.effective_balance
|
|
)
|
|
|
|
# Move active balance to target. Excess balance is withdrawable.
|
|
decrease_balance(state, pending_consolidation.source_index, source_effective_balance)
|
|
increase_balance(state, pending_consolidation.target_index, source_effective_balance)
|
|
next_pending_consolidation += 1
|
|
|
|
state.pending_consolidations = state.pending_consolidations[next_pending_consolidation:]
|
|
</spec>
|
|
|
|
- name: process_pending_deposits
|
|
sources:
|
|
- file: beacon-chain/core/electra/deposits.go
|
|
search: func ProcessPendingDeposits(
|
|
spec: |
|
|
<spec fn="process_pending_deposits" fork="electra" hash="f0e7c738">
|
|
def process_pending_deposits(state: BeaconState) -> None:
|
|
next_epoch = Epoch(get_current_epoch(state) + 1)
|
|
available_for_processing = state.deposit_balance_to_consume + get_activation_exit_churn_limit(
|
|
state
|
|
)
|
|
processed_amount = 0
|
|
next_deposit_index = 0
|
|
deposits_to_postpone = []
|
|
is_churn_limit_reached = False
|
|
finalized_slot = compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)
|
|
|
|
for deposit in state.pending_deposits:
|
|
# Do not process deposit requests if Eth1 bridge deposits are not yet applied.
|
|
if (
|
|
# Is deposit request
|
|
deposit.slot > GENESIS_SLOT
|
|
and
|
|
# There are pending Eth1 bridge deposits
|
|
state.eth1_deposit_index < state.deposit_requests_start_index
|
|
):
|
|
break
|
|
|
|
# Check if deposit has been finalized, otherwise, stop processing.
|
|
if deposit.slot > finalized_slot:
|
|
break
|
|
|
|
# Check if number of processed deposits has not reached the limit, otherwise, stop processing.
|
|
if next_deposit_index >= MAX_PENDING_DEPOSITS_PER_EPOCH:
|
|
break
|
|
|
|
# Read validator state
|
|
is_validator_exited = False
|
|
is_validator_withdrawn = False
|
|
validator_pubkeys = [v.pubkey for v in state.validators]
|
|
if deposit.pubkey in validator_pubkeys:
|
|
validator = state.validators[ValidatorIndex(validator_pubkeys.index(deposit.pubkey))]
|
|
is_validator_exited = validator.exit_epoch < FAR_FUTURE_EPOCH
|
|
is_validator_withdrawn = validator.withdrawable_epoch < next_epoch
|
|
|
|
if is_validator_withdrawn:
|
|
# Deposited balance will never become active. Increase balance but do not consume churn
|
|
apply_pending_deposit(state, deposit)
|
|
elif is_validator_exited:
|
|
# Validator is exiting, postpone the deposit until after withdrawable epoch
|
|
deposits_to_postpone.append(deposit)
|
|
else:
|
|
# Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch.
|
|
is_churn_limit_reached = processed_amount + deposit.amount > available_for_processing
|
|
if is_churn_limit_reached:
|
|
break
|
|
|
|
# Consume churn and apply deposit.
|
|
processed_amount += deposit.amount
|
|
apply_pending_deposit(state, deposit)
|
|
|
|
# Regardless of how the deposit was handled, we move on in the queue.
|
|
next_deposit_index += 1
|
|
|
|
state.pending_deposits = state.pending_deposits[next_deposit_index:] + deposits_to_postpone
|
|
|
|
# Accumulate churn only if the churn limit has been hit.
|
|
if is_churn_limit_reached:
|
|
state.deposit_balance_to_consume = available_for_processing - processed_amount
|
|
else:
|
|
state.deposit_balance_to_consume = Gwei(0)
|
|
</spec>
|
|
|
|
- name: process_proposer_lookahead
|
|
sources:
|
|
- file: beacon-chain/core/fulu/transition.go
|
|
search: func ProcessProposerLookahead(
|
|
spec: |
|
|
<spec fn="process_proposer_lookahead" fork="fulu" hash="3ff130cc">
|
|
def process_proposer_lookahead(state: BeaconState) -> None:
|
|
last_epoch_start = len(state.proposer_lookahead) - SLOTS_PER_EPOCH
|
|
# Shift out proposers in the first epoch
|
|
state.proposer_lookahead[:last_epoch_start] = state.proposer_lookahead[SLOTS_PER_EPOCH:]
|
|
# Fill in the last epoch with new proposer indices
|
|
last_epoch_proposers = get_beacon_proposer_indices(
|
|
state, Epoch(get_current_epoch(state) + MIN_SEED_LOOKAHEAD + 1)
|
|
)
|
|
state.proposer_lookahead[last_epoch_start:] = last_epoch_proposers
|
|
</spec>
|
|
|
|
- name: process_proposer_slashing
|
|
sources:
|
|
- file: beacon-chain/core/blocks/proposer_slashing.go
|
|
search: func ProcessProposerSlashing(
|
|
spec: |
|
|
<spec fn="process_proposer_slashing" fork="phase0" hash="279ec199">
|
|
def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None:
|
|
header_1 = proposer_slashing.signed_header_1.message
|
|
header_2 = proposer_slashing.signed_header_2.message
|
|
|
|
# Verify header slots match
|
|
assert header_1.slot == header_2.slot
|
|
# Verify header proposer indices match
|
|
assert header_1.proposer_index == header_2.proposer_index
|
|
# Verify the headers are different
|
|
assert header_1 != header_2
|
|
# Verify the proposer is slashable
|
|
proposer = state.validators[header_1.proposer_index]
|
|
assert is_slashable_validator(proposer, get_current_epoch(state))
|
|
# Verify signatures
|
|
for signed_header in (proposer_slashing.signed_header_1, proposer_slashing.signed_header_2):
|
|
domain = get_domain(
|
|
state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(signed_header.message.slot)
|
|
)
|
|
signing_root = compute_signing_root(signed_header.message, domain)
|
|
assert bls.Verify(proposer.pubkey, signing_root, signed_header.signature)
|
|
|
|
slash_validator(state, header_1.proposer_index)
|
|
</spec>
|
|
|
|
- name: process_randao
|
|
sources:
|
|
- file: beacon-chain/core/blocks/randao.go
|
|
search: func ProcessRandao(
|
|
spec: |
|
|
<spec fn="process_randao" fork="phase0" hash="607c511e">
|
|
def process_randao(state: BeaconState, body: BeaconBlockBody) -> None:
|
|
epoch = get_current_epoch(state)
|
|
# Verify RANDAO reveal
|
|
proposer = state.validators[get_beacon_proposer_index(state)]
|
|
signing_root = compute_signing_root(epoch, get_domain(state, DOMAIN_RANDAO))
|
|
assert bls.Verify(proposer.pubkey, signing_root, body.randao_reveal)
|
|
# Mix in RANDAO reveal
|
|
mix = xor(get_randao_mix(state, epoch), hash(body.randao_reveal))
|
|
state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR] = mix
|
|
</spec>
|
|
|
|
- name: process_randao_mixes_reset
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessRandaoMixesReset(
|
|
spec: |
|
|
<spec fn="process_randao_mixes_reset" fork="phase0" hash="0faaabf3">
|
|
def process_randao_mixes_reset(state: BeaconState) -> None:
|
|
current_epoch = get_current_epoch(state)
|
|
next_epoch = Epoch(current_epoch + 1)
|
|
# Set randao mix
|
|
state.randao_mixes[next_epoch % EPOCHS_PER_HISTORICAL_VECTOR] = get_randao_mix(
|
|
state, current_epoch
|
|
)
|
|
</spec>
|
|
|
|
- name: process_registry_updates#phase0
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessRegistryUpdates(
|
|
spec: |
|
|
<spec fn="process_registry_updates" fork="phase0" hash="3adacc1e">
|
|
def process_registry_updates(state: BeaconState) -> None:
|
|
# Process activation eligibility and ejections
|
|
for index, validator in enumerate(state.validators):
|
|
if is_eligible_for_activation_queue(validator):
|
|
validator.activation_eligibility_epoch = get_current_epoch(state) + 1
|
|
|
|
if (
|
|
is_active_validator(validator, get_current_epoch(state))
|
|
and validator.effective_balance <= EJECTION_BALANCE
|
|
):
|
|
initiate_validator_exit(state, ValidatorIndex(index))
|
|
|
|
# Queue validators eligible for activation and not yet dequeued for activation
|
|
activation_queue = sorted(
|
|
[
|
|
index
|
|
for index, validator in enumerate(state.validators)
|
|
if is_eligible_for_activation(state, validator)
|
|
],
|
|
# Order by the sequence of activation_eligibility_epoch setting and then index
|
|
key=lambda index: (state.validators[index].activation_eligibility_epoch, index),
|
|
)
|
|
# Dequeued validators for activation up to churn limit
|
|
for index in activation_queue[: get_validator_churn_limit(state)]:
|
|
validator = state.validators[index]
|
|
validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state))
|
|
</spec>
|
|
|
|
- name: process_registry_updates#deneb
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessRegistryUpdates(
|
|
spec: |
|
|
<spec fn="process_registry_updates" fork="deneb" hash="71a6a2d9">
|
|
def process_registry_updates(state: BeaconState) -> None:
|
|
# Process activation eligibility and ejections
|
|
for index, validator in enumerate(state.validators):
|
|
if is_eligible_for_activation_queue(validator):
|
|
validator.activation_eligibility_epoch = get_current_epoch(state) + 1
|
|
|
|
if (
|
|
is_active_validator(validator, get_current_epoch(state))
|
|
and validator.effective_balance <= EJECTION_BALANCE
|
|
):
|
|
initiate_validator_exit(state, ValidatorIndex(index))
|
|
|
|
# Queue validators eligible for activation and not yet dequeued for activation
|
|
activation_queue = sorted(
|
|
[
|
|
index
|
|
for index, validator in enumerate(state.validators)
|
|
if is_eligible_for_activation(state, validator)
|
|
],
|
|
# Order by the sequence of activation_eligibility_epoch setting and then index
|
|
key=lambda index: (state.validators[index].activation_eligibility_epoch, index),
|
|
)
|
|
# Dequeued validators for activation up to activation churn limit
|
|
# [Modified in Deneb:EIP7514]
|
|
for index in activation_queue[: get_validator_activation_churn_limit(state)]:
|
|
validator = state.validators[index]
|
|
validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state))
|
|
</spec>
|
|
|
|
- name: process_registry_updates#electra
|
|
sources:
|
|
- file: beacon-chain/core/electra/registry_updates.go
|
|
search: func ProcessRegistryUpdates(
|
|
spec: |
|
|
<spec fn="process_registry_updates" fork="electra" hash="8a75e443">
|
|
def process_registry_updates(state: BeaconState) -> None:
|
|
current_epoch = get_current_epoch(state)
|
|
activation_epoch = compute_activation_exit_epoch(current_epoch)
|
|
|
|
# Process activation eligibility, ejections, and activations
|
|
for index, validator in enumerate(state.validators):
|
|
# [Modified in Electra:EIP7251]
|
|
if is_eligible_for_activation_queue(validator):
|
|
validator.activation_eligibility_epoch = current_epoch + 1
|
|
elif (
|
|
is_active_validator(validator, current_epoch)
|
|
and validator.effective_balance <= EJECTION_BALANCE
|
|
):
|
|
# [Modified in Electra:EIP7251]
|
|
initiate_validator_exit(state, ValidatorIndex(index))
|
|
elif is_eligible_for_activation(state, validator):
|
|
validator.activation_epoch = activation_epoch
|
|
</spec>
|
|
|
|
- name: process_rewards_and_penalties#phase0
|
|
sources:
|
|
- file: beacon-chain/core/epoch/precompute/reward_penalty.go
|
|
search: func ProcessRewardsAndPenaltiesPrecompute(
|
|
spec: |
|
|
<spec fn="process_rewards_and_penalties" fork="phase0" hash="93779fdb">
|
|
def process_rewards_and_penalties(state: BeaconState) -> None:
|
|
# No rewards are applied at the end of `GENESIS_EPOCH` because rewards are for work done in the previous epoch
|
|
if get_current_epoch(state) == GENESIS_EPOCH:
|
|
return
|
|
|
|
rewards, penalties = get_attestation_deltas(state)
|
|
for index in range(len(state.validators)):
|
|
increase_balance(state, ValidatorIndex(index), rewards[index])
|
|
decrease_balance(state, ValidatorIndex(index), penalties[index])
|
|
</spec>
|
|
|
|
- name: process_rewards_and_penalties#altair
|
|
sources:
|
|
- file: beacon-chain/core/altair/epoch_precompute.go
|
|
search: func ProcessRewardsAndPenaltiesPrecompute(
|
|
spec: |
|
|
<spec fn="process_rewards_and_penalties" fork="altair" hash="373af4c1">
|
|
def process_rewards_and_penalties(state: BeaconState) -> None:
|
|
# No rewards are applied at the end of `GENESIS_EPOCH` because rewards are for work done in the previous epoch
|
|
if get_current_epoch(state) == GENESIS_EPOCH:
|
|
return
|
|
|
|
flag_deltas = [
|
|
get_flag_index_deltas(state, flag_index)
|
|
for flag_index in range(len(PARTICIPATION_FLAG_WEIGHTS))
|
|
]
|
|
deltas = flag_deltas + [get_inactivity_penalty_deltas(state)]
|
|
for rewards, penalties in deltas:
|
|
for index in range(len(state.validators)):
|
|
increase_balance(state, ValidatorIndex(index), rewards[index])
|
|
decrease_balance(state, ValidatorIndex(index), penalties[index])
|
|
</spec>
|
|
|
|
- name: process_slashings#phase0
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessSlashings(
|
|
spec: |
|
|
<spec fn="process_slashings" fork="phase0" hash="a18a9045">
|
|
def process_slashings(state: BeaconState) -> None:
|
|
epoch = get_current_epoch(state)
|
|
total_balance = get_total_active_balance(state)
|
|
adjusted_total_slashing_balance = min(
|
|
sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER, total_balance
|
|
)
|
|
for index, validator in enumerate(state.validators):
|
|
if (
|
|
validator.slashed
|
|
and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch
|
|
):
|
|
increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow
|
|
penalty_numerator = (
|
|
validator.effective_balance // increment * adjusted_total_slashing_balance
|
|
)
|
|
penalty = penalty_numerator // total_balance * increment
|
|
decrease_balance(state, ValidatorIndex(index), penalty)
|
|
</spec>
|
|
|
|
- name: process_slashings#altair
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessSlashings(
|
|
spec: |
|
|
<spec fn="process_slashings" fork="altair" hash="96cce87d">
|
|
def process_slashings(state: BeaconState) -> None:
|
|
epoch = get_current_epoch(state)
|
|
total_balance = get_total_active_balance(state)
|
|
adjusted_total_slashing_balance = min(
|
|
sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR, total_balance
|
|
)
|
|
for index, validator in enumerate(state.validators):
|
|
if (
|
|
validator.slashed
|
|
and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch
|
|
):
|
|
increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow
|
|
penalty_numerator = (
|
|
validator.effective_balance // increment * adjusted_total_slashing_balance
|
|
)
|
|
penalty = penalty_numerator // total_balance * increment
|
|
decrease_balance(state, ValidatorIndex(index), penalty)
|
|
</spec>
|
|
|
|
- name: process_slashings#bellatrix
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessSlashings(
|
|
spec: |
|
|
<spec fn="process_slashings" fork="bellatrix" hash="0c8f6e6e">
|
|
def process_slashings(state: BeaconState) -> None:
|
|
epoch = get_current_epoch(state)
|
|
total_balance = get_total_active_balance(state)
|
|
adjusted_total_slashing_balance = min(
|
|
sum(state.slashings)
|
|
# [Modified in Bellatrix]
|
|
* PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX,
|
|
total_balance,
|
|
)
|
|
for index, validator in enumerate(state.validators):
|
|
if (
|
|
validator.slashed
|
|
and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch
|
|
):
|
|
increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow
|
|
penalty_numerator = (
|
|
validator.effective_balance // increment * adjusted_total_slashing_balance
|
|
)
|
|
penalty = penalty_numerator // total_balance * increment
|
|
decrease_balance(state, ValidatorIndex(index), penalty)
|
|
</spec>
|
|
|
|
- name: process_slashings#electra
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessSlashings(
|
|
spec: |
|
|
<spec fn="process_slashings" fork="electra" hash="a948faff">
|
|
def process_slashings(state: BeaconState) -> None:
|
|
epoch = get_current_epoch(state)
|
|
total_balance = get_total_active_balance(state)
|
|
adjusted_total_slashing_balance = min(
|
|
sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX, total_balance
|
|
)
|
|
increment = (
|
|
EFFECTIVE_BALANCE_INCREMENT # Factored out from total balance to avoid uint64 overflow
|
|
)
|
|
penalty_per_effective_balance_increment = adjusted_total_slashing_balance // (
|
|
total_balance // increment
|
|
)
|
|
for index, validator in enumerate(state.validators):
|
|
if (
|
|
validator.slashed
|
|
and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch
|
|
):
|
|
effective_balance_increments = validator.effective_balance // increment
|
|
# [Modified in Electra:EIP7251]
|
|
penalty = penalty_per_effective_balance_increment * effective_balance_increments
|
|
decrease_balance(state, ValidatorIndex(index), penalty)
|
|
</spec>
|
|
|
|
- name: process_slashings_reset
|
|
sources:
|
|
- file: beacon-chain/core/epoch/epoch_processing.go
|
|
search: func ProcessSlashingsReset(
|
|
spec: |
|
|
<spec fn="process_slashings_reset" fork="phase0" hash="a82fb03c">
|
|
def process_slashings_reset(state: BeaconState) -> None:
|
|
next_epoch = Epoch(get_current_epoch(state) + 1)
|
|
# Reset slashings
|
|
state.slashings[next_epoch % EPOCHS_PER_SLASHINGS_VECTOR] = Gwei(0)
|
|
</spec>
|
|
|
|
- name: process_slot
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition.go
|
|
search: func ProcessSlot(
|
|
spec: |
|
|
<spec fn="process_slot" fork="phase0" hash="1ba7e009">
|
|
def process_slot(state: BeaconState) -> None:
|
|
# Cache state root
|
|
previous_state_root = hash_tree_root(state)
|
|
state.state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root
|
|
# Cache latest block header state root
|
|
if state.latest_block_header.state_root == Bytes32():
|
|
state.latest_block_header.state_root = previous_state_root
|
|
# Cache block root
|
|
previous_block_root = hash_tree_root(state.latest_block_header)
|
|
state.block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root
|
|
</spec>
|
|
|
|
- name: process_slots
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition.go
|
|
search: func ProcessSlots(
|
|
spec: |
|
|
<spec fn="process_slots" fork="phase0" hash="f50107a2">
|
|
def process_slots(state: BeaconState, slot: Slot) -> None:
|
|
assert state.slot < slot
|
|
while state.slot < slot:
|
|
process_slot(state)
|
|
# Process epoch on the start slot of the next epoch
|
|
if (state.slot + 1) % SLOTS_PER_EPOCH == 0:
|
|
process_epoch(state)
|
|
state.slot = Slot(state.slot + 1)
|
|
</spec>
|
|
|
|
- name: process_sync_aggregate
|
|
sources:
|
|
- file: beacon-chain/core/altair/block.go
|
|
search: func ProcessSyncAggregate(
|
|
spec: |
|
|
<spec fn="process_sync_aggregate" fork="altair" hash="5f7c046f">
|
|
def process_sync_aggregate(state: BeaconState, sync_aggregate: SyncAggregate) -> None:
|
|
# Verify sync committee aggregate signature signing over the previous slot block root
|
|
committee_pubkeys = state.current_sync_committee.pubkeys
|
|
committee_bits = sync_aggregate.sync_committee_bits
|
|
if sum(committee_bits) == SYNC_COMMITTEE_SIZE:
|
|
# All members participated - use precomputed aggregate key
|
|
participant_pubkeys = [state.current_sync_committee.aggregate_pubkey]
|
|
elif sum(committee_bits) > SYNC_COMMITTEE_SIZE // 2:
|
|
# More than half participated - subtract non-participant keys.
|
|
# First determine nonparticipating members
|
|
non_participant_pubkeys = [
|
|
pubkey for pubkey, bit in zip(committee_pubkeys, committee_bits) if not bit
|
|
]
|
|
# Compute aggregate of non-participants
|
|
non_participant_aggregate = eth_aggregate_pubkeys(non_participant_pubkeys)
|
|
# Subtract non-participants from the full aggregate
|
|
# This is equivalent to: aggregate_pubkey + (-non_participant_aggregate)
|
|
participant_pubkey = bls.add(
|
|
bls.bytes48_to_G1(state.current_sync_committee.aggregate_pubkey),
|
|
bls.neg(bls.bytes48_to_G1(non_participant_aggregate)),
|
|
)
|
|
participant_pubkeys = [BLSPubkey(bls.G1_to_bytes48(participant_pubkey))]
|
|
else:
|
|
# Less than half participated - aggregate participant keys
|
|
participant_pubkeys = [
|
|
pubkey
|
|
for pubkey, bit in zip(committee_pubkeys, sync_aggregate.sync_committee_bits)
|
|
if bit
|
|
]
|
|
previous_slot = max(state.slot, Slot(1)) - Slot(1)
|
|
domain = get_domain(state, DOMAIN_SYNC_COMMITTEE, compute_epoch_at_slot(previous_slot))
|
|
signing_root = compute_signing_root(get_block_root_at_slot(state, previous_slot), domain)
|
|
# Note: eth_fast_aggregate_verify works with a singleton list containing an aggregated key
|
|
assert eth_fast_aggregate_verify(
|
|
participant_pubkeys, signing_root, sync_aggregate.sync_committee_signature
|
|
)
|
|
|
|
# Compute participant and proposer rewards
|
|
total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
|
|
total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
|
|
max_participant_rewards = Gwei(
|
|
total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH
|
|
)
|
|
participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
|
|
proposer_reward = Gwei(
|
|
participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT)
|
|
)
|
|
|
|
# Apply participant and proposer rewards
|
|
all_pubkeys = [v.pubkey for v in state.validators]
|
|
committee_indices = [
|
|
ValidatorIndex(all_pubkeys.index(pubkey)) for pubkey in state.current_sync_committee.pubkeys
|
|
]
|
|
for participant_index, participation_bit in zip(
|
|
committee_indices, sync_aggregate.sync_committee_bits
|
|
):
|
|
if participation_bit:
|
|
increase_balance(state, participant_index, participant_reward)
|
|
increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
|
else:
|
|
decrease_balance(state, participant_index, participant_reward)
|
|
</spec>
|
|
|
|
- name: process_sync_committee_contributions
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_sync_committee_contributions" fork="altair" hash="34e95c46">
|
|
def process_sync_committee_contributions(
|
|
block: BeaconBlock, contributions: Set[SyncCommitteeContribution]
|
|
) -> None:
|
|
sync_aggregate = SyncAggregate()
|
|
signatures = []
|
|
sync_subcommittee_size = SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT
|
|
|
|
for contribution in contributions:
|
|
subcommittee_index = contribution.subcommittee_index
|
|
for index, participated in enumerate(contribution.aggregation_bits):
|
|
if participated:
|
|
participant_index = sync_subcommittee_size * subcommittee_index + index
|
|
sync_aggregate.sync_committee_bits[participant_index] = True
|
|
signatures.append(contribution.signature)
|
|
|
|
sync_aggregate.sync_committee_signature = bls.Aggregate(signatures)
|
|
|
|
block.body.sync_aggregate = sync_aggregate
|
|
</spec>
|
|
|
|
- name: process_sync_committee_updates
|
|
sources:
|
|
- file: beacon-chain/core/altair/epoch_spec.go
|
|
search: func ProcessSyncCommitteeUpdates(
|
|
spec: |
|
|
<spec fn="process_sync_committee_updates" fork="altair" hash="9e59de37">
|
|
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_next_sync_committee(state)
|
|
</spec>
|
|
|
|
- name: process_voluntary_exit#phase0
|
|
sources:
|
|
- file: beacon-chain/core/blocks/exit.go
|
|
search: func ProcessVoluntaryExits(
|
|
spec: |
|
|
<spec fn="process_voluntary_exit" fork="phase0" hash="d867310e">
|
|
def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
|
|
voluntary_exit = signed_voluntary_exit.message
|
|
validator = state.validators[voluntary_exit.validator_index]
|
|
# Verify the validator is active
|
|
assert is_active_validator(validator, get_current_epoch(state))
|
|
# Verify exit has not been initiated
|
|
assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
|
# Exits must specify an epoch when they become valid; they are not valid before then
|
|
assert get_current_epoch(state) >= voluntary_exit.epoch
|
|
# Verify the validator has been active long enough
|
|
assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
|
|
# Verify signature
|
|
domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
|
|
signing_root = compute_signing_root(voluntary_exit, domain)
|
|
assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
|
|
# Initiate exit
|
|
initiate_validator_exit(state, voluntary_exit.validator_index)
|
|
</spec>
|
|
|
|
- name: process_voluntary_exit#deneb
|
|
sources:
|
|
- file: beacon-chain/core/blocks/exit.go
|
|
search: func ProcessVoluntaryExits(
|
|
spec: |
|
|
<spec fn="process_voluntary_exit" fork="deneb" hash="f97fd167">
|
|
def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
|
|
voluntary_exit = signed_voluntary_exit.message
|
|
validator = state.validators[voluntary_exit.validator_index]
|
|
# Verify the validator is active
|
|
assert is_active_validator(validator, get_current_epoch(state))
|
|
# Verify exit has not been initiated
|
|
assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
|
# Exits must specify an epoch when they become valid; they are not valid before then
|
|
assert get_current_epoch(state) >= voluntary_exit.epoch
|
|
# Verify the validator has been active long enough
|
|
assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
|
|
# Verify signature
|
|
# [Modified in Deneb:EIP7044]
|
|
domain = compute_domain(
|
|
DOMAIN_VOLUNTARY_EXIT, CAPELLA_FORK_VERSION, state.genesis_validators_root
|
|
)
|
|
signing_root = compute_signing_root(voluntary_exit, domain)
|
|
assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
|
|
# Initiate exit
|
|
initiate_validator_exit(state, voluntary_exit.validator_index)
|
|
</spec>
|
|
|
|
- name: process_voluntary_exit#electra
|
|
sources:
|
|
- file: beacon-chain/core/blocks/exit.go
|
|
search: func ProcessVoluntaryExits(
|
|
spec: |
|
|
<spec fn="process_voluntary_exit" fork="electra" hash="51b07d36">
|
|
def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
|
|
voluntary_exit = signed_voluntary_exit.message
|
|
validator = state.validators[voluntary_exit.validator_index]
|
|
# Verify the validator is active
|
|
assert is_active_validator(validator, get_current_epoch(state))
|
|
# Verify exit has not been initiated
|
|
assert validator.exit_epoch == FAR_FUTURE_EPOCH
|
|
# Exits must specify an epoch when they become valid; they are not valid before then
|
|
assert get_current_epoch(state) >= voluntary_exit.epoch
|
|
# Verify the validator has been active long enough
|
|
assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
|
|
# [New in Electra:EIP7251]
|
|
# Only exit validator if it has no pending withdrawals in the queue
|
|
assert get_pending_balance_to_withdraw(state, voluntary_exit.validator_index) == 0
|
|
# Verify signature
|
|
domain = compute_domain(
|
|
DOMAIN_VOLUNTARY_EXIT, CAPELLA_FORK_VERSION, state.genesis_validators_root
|
|
)
|
|
signing_root = compute_signing_root(voluntary_exit, domain)
|
|
assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
|
|
# Initiate exit
|
|
initiate_validator_exit(state, voluntary_exit.validator_index)
|
|
</spec>
|
|
|
|
- name: process_withdrawal_request
|
|
sources:
|
|
- file: beacon-chain/core/electra/withdrawals.go
|
|
search: func ProcessWithdrawalRequests(
|
|
spec: |
|
|
<spec fn="process_withdrawal_request" fork="electra" hash="c21a0a53">
|
|
def process_withdrawal_request(state: BeaconState, withdrawal_request: WithdrawalRequest) -> None:
|
|
amount = withdrawal_request.amount
|
|
is_full_exit_request = amount == FULL_EXIT_REQUEST_AMOUNT
|
|
|
|
# If partial withdrawal queue is full, only full exits are processed
|
|
if (
|
|
len(state.pending_partial_withdrawals) == PENDING_PARTIAL_WITHDRAWALS_LIMIT
|
|
and not is_full_exit_request
|
|
):
|
|
return
|
|
|
|
validator_pubkeys = [v.pubkey for v in state.validators]
|
|
# Verify pubkey exists
|
|
request_pubkey = withdrawal_request.validator_pubkey
|
|
if request_pubkey not in validator_pubkeys:
|
|
return
|
|
index = ValidatorIndex(validator_pubkeys.index(request_pubkey))
|
|
validator = state.validators[index]
|
|
|
|
# Verify withdrawal credentials
|
|
has_correct_credential = has_execution_withdrawal_credential(validator)
|
|
is_correct_source_address = (
|
|
validator.withdrawal_credentials[12:] == withdrawal_request.source_address
|
|
)
|
|
if not (has_correct_credential and is_correct_source_address):
|
|
return
|
|
# Verify the validator is active
|
|
if not is_active_validator(validator, get_current_epoch(state)):
|
|
return
|
|
# Verify exit has not been initiated
|
|
if validator.exit_epoch != FAR_FUTURE_EPOCH:
|
|
return
|
|
# Verify the validator has been active long enough
|
|
if get_current_epoch(state) < validator.activation_epoch + SHARD_COMMITTEE_PERIOD:
|
|
return
|
|
|
|
pending_balance_to_withdraw = get_pending_balance_to_withdraw(state, index)
|
|
|
|
if is_full_exit_request:
|
|
# Only exit validator if it has no pending withdrawals in the queue
|
|
if pending_balance_to_withdraw == 0:
|
|
initiate_validator_exit(state, index)
|
|
return
|
|
|
|
has_sufficient_effective_balance = validator.effective_balance >= MIN_ACTIVATION_BALANCE
|
|
has_excess_balance = (
|
|
state.balances[index] > MIN_ACTIVATION_BALANCE + pending_balance_to_withdraw
|
|
)
|
|
|
|
# Only allow partial withdrawals with compounding withdrawal credentials
|
|
if (
|
|
has_compounding_withdrawal_credential(validator)
|
|
and has_sufficient_effective_balance
|
|
and has_excess_balance
|
|
):
|
|
to_withdraw = min(
|
|
state.balances[index] - MIN_ACTIVATION_BALANCE - pending_balance_to_withdraw, amount
|
|
)
|
|
exit_queue_epoch = compute_exit_epoch_and_update_churn(state, to_withdraw)
|
|
withdrawable_epoch = Epoch(exit_queue_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY)
|
|
state.pending_partial_withdrawals.append(
|
|
PendingPartialWithdrawal(
|
|
validator_index=index,
|
|
amount=to_withdraw,
|
|
withdrawable_epoch=withdrawable_epoch,
|
|
)
|
|
)
|
|
</spec>
|
|
|
|
- name: process_withdrawals#capella
|
|
sources:
|
|
- file: beacon-chain/core/blocks/withdrawals.go
|
|
search: func ProcessWithdrawals(
|
|
spec: |
|
|
<spec fn="process_withdrawals" fork="capella" hash="901f9fc4">
|
|
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
|
# Get expected withdrawals
|
|
withdrawals, processed_validators_sweep_count = get_expected_withdrawals(state)
|
|
assert payload.withdrawals == withdrawals
|
|
|
|
# Apply expected withdrawals
|
|
apply_withdrawals(state, withdrawals)
|
|
|
|
# Update withdrawals fields in the state
|
|
update_next_withdrawal_index(state, withdrawals)
|
|
update_next_withdrawal_validator_index(state, processed_validators_sweep_count)
|
|
</spec>
|
|
|
|
- name: process_withdrawals#electra
|
|
sources:
|
|
- file: beacon-chain/core/blocks/withdrawals.go
|
|
search: func ProcessWithdrawals(
|
|
spec: |
|
|
<spec fn="process_withdrawals" fork="electra" hash="67870972">
|
|
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
|
# [Modified in Electra:EIP7251]
|
|
# Get expected withdrawals
|
|
withdrawals, processed_partial_withdrawals_count, processed_validators_sweep_count = (
|
|
get_expected_withdrawals(state)
|
|
)
|
|
assert payload.withdrawals == withdrawals
|
|
|
|
# Apply expected withdrawals
|
|
apply_withdrawals(state, withdrawals)
|
|
|
|
# Update withdrawals fields in the state
|
|
update_next_withdrawal_index(state, withdrawals)
|
|
# [New in Electra:EIP7251]
|
|
update_pending_partial_withdrawals(state, processed_partial_withdrawals_count)
|
|
update_next_withdrawal_validator_index(state, processed_validators_sweep_count)
|
|
</spec>
|
|
|
|
- name: queue_excess_active_balance
|
|
sources:
|
|
- file: beacon-chain/core/electra/validator.go
|
|
search: func QueueExcessActiveBalance(
|
|
spec: |
|
|
<spec fn="queue_excess_active_balance" fork="electra" hash="2c952be0">
|
|
def queue_excess_active_balance(state: BeaconState, index: ValidatorIndex) -> None:
|
|
balance = state.balances[index]
|
|
if balance > MIN_ACTIVATION_BALANCE:
|
|
excess_balance = balance - MIN_ACTIVATION_BALANCE
|
|
state.balances[index] = MIN_ACTIVATION_BALANCE
|
|
validator = state.validators[index]
|
|
# Use bls.G2_POINT_AT_INFINITY as a signature field placeholder
|
|
# and GENESIS_SLOT to distinguish from a pending deposit request
|
|
state.pending_deposits.append(
|
|
PendingDeposit(
|
|
pubkey=validator.pubkey,
|
|
withdrawal_credentials=validator.withdrawal_credentials,
|
|
amount=excess_balance,
|
|
signature=bls.G2_POINT_AT_INFINITY,
|
|
slot=GENESIS_SLOT,
|
|
)
|
|
)
|
|
</spec>
|
|
|
|
- name: recover_matrix
|
|
sources: []
|
|
spec: |
|
|
<spec fn="recover_matrix" fork="fulu" hash="3db21f50">
|
|
def recover_matrix(
|
|
partial_matrix: Sequence[MatrixEntry], blob_count: uint64
|
|
) -> Sequence[MatrixEntry]:
|
|
"""
|
|
Recover the full, flattened sequence of matrix entries.
|
|
|
|
This helper demonstrates how to apply ``recover_cells_and_kzg_proofs``.
|
|
The data structure for storing cells/proofs is implementation-dependent.
|
|
"""
|
|
matrix = []
|
|
for blob_index in range(blob_count):
|
|
cell_indices = [e.column_index for e in partial_matrix if e.row_index == blob_index]
|
|
cells = [e.cell for e in partial_matrix if e.row_index == blob_index]
|
|
recovered_cells, recovered_proofs = recover_cells_and_kzg_proofs(cell_indices, cells)
|
|
for cell_index, (cell, proof) in enumerate(zip(recovered_cells, recovered_proofs)):
|
|
matrix.append(
|
|
MatrixEntry(
|
|
cell=cell,
|
|
kzg_proof=proof,
|
|
column_index=cell_index,
|
|
row_index=blob_index,
|
|
)
|
|
)
|
|
return matrix
|
|
</spec>
|
|
|
|
- name: saturating_sub
|
|
sources:
|
|
- file: consensus-types/primitives/slot.go
|
|
search: func (s Slot) SafeSub(
|
|
spec: |
|
|
<spec fn="saturating_sub" fork="phase0" hash="ab62a392">
|
|
def saturating_sub(a: int, b: int) -> int:
|
|
"""
|
|
Computes a - b, saturating at numeric bounds.
|
|
"""
|
|
return a - b if a > b else 0
|
|
</spec>
|
|
|
|
- name: seconds_to_milliseconds
|
|
sources: []
|
|
spec: |
|
|
<spec fn="seconds_to_milliseconds" fork="phase0" hash="b2cc9743">
|
|
def seconds_to_milliseconds(seconds: uint64) -> uint64:
|
|
"""
|
|
Convert seconds to milliseconds with overflow protection.
|
|
Returns ``UINT64_MAX`` if the result would overflow.
|
|
"""
|
|
if seconds > UINT64_MAX // 1000:
|
|
return UINT64_MAX
|
|
return seconds * 1000
|
|
</spec>
|
|
|
|
- name: set_or_append_list
|
|
sources: []
|
|
spec: |
|
|
<spec fn="set_or_append_list" fork="altair" hash="a48f6f4c">
|
|
def set_or_append_list(list: List, index: ValidatorIndex, value: Any) -> None:
|
|
if index == len(list):
|
|
list.append(value)
|
|
else:
|
|
list[index] = value
|
|
</spec>
|
|
|
|
- name: should_override_forkchoice_update
|
|
sources:
|
|
- file: beacon-chain/forkchoice/ro.go
|
|
search: func (ro *ROForkChoice) ShouldOverrideFCU(
|
|
spec: |
|
|
<spec fn="should_override_forkchoice_update" fork="bellatrix" hash="c055d92a">
|
|
def should_override_forkchoice_update(store: Store, head_root: Root) -> bool:
|
|
head_block = store.blocks[head_root]
|
|
parent_root = head_block.parent_root
|
|
parent_block = store.blocks[parent_root]
|
|
current_slot = get_current_slot(store)
|
|
proposal_slot = head_block.slot + Slot(1)
|
|
|
|
# Only re-org the head_block block if it arrived later than the attestation deadline.
|
|
head_late = is_head_late(store, head_root)
|
|
|
|
# Shuffling stable.
|
|
shuffling_stable = is_shuffling_stable(proposal_slot)
|
|
|
|
# FFG information of the new head_block will be competitive with the current head.
|
|
ffg_competitive = is_ffg_competitive(store, head_root, parent_root)
|
|
|
|
# Do not re-org if the chain is not finalizing with acceptable frequency.
|
|
finalization_ok = is_finalization_ok(store, proposal_slot)
|
|
|
|
# Only suppress the fork choice update if we are confident that we will propose the next block.
|
|
parent_state_advanced = store.block_states[parent_root].copy()
|
|
process_slots(parent_state_advanced, proposal_slot)
|
|
proposer_index = get_beacon_proposer_index(parent_state_advanced)
|
|
proposing_reorg_slot = validator_is_connected(proposer_index)
|
|
|
|
# Single slot re-org.
|
|
parent_slot_ok = parent_block.slot + 1 == head_block.slot
|
|
proposing_on_time = is_proposing_on_time(store)
|
|
|
|
# Note that this condition is different from `get_proposer_head`
|
|
current_time_ok = head_block.slot == current_slot or (
|
|
proposal_slot == current_slot and proposing_on_time
|
|
)
|
|
single_slot_reorg = parent_slot_ok and current_time_ok
|
|
|
|
# Check the head weight only if the attestations from the head slot have already been applied.
|
|
# Implementations may want to do this in different ways, e.g. by advancing
|
|
# `store.time` early, or by counting queued attestations during the head block's slot.
|
|
if current_slot > head_block.slot:
|
|
head_weak = is_head_weak(store, head_root)
|
|
parent_strong = is_parent_strong(store, head_root)
|
|
else:
|
|
head_weak = True
|
|
parent_strong = True
|
|
|
|
return all(
|
|
[
|
|
head_late,
|
|
shuffling_stable,
|
|
ffg_competitive,
|
|
finalization_ok,
|
|
proposing_reorg_slot,
|
|
single_slot_reorg,
|
|
head_weak,
|
|
parent_strong,
|
|
]
|
|
)
|
|
</spec>
|
|
|
|
- name: slash_validator#phase0
|
|
sources:
|
|
- file: beacon-chain/core/validators/validator.go
|
|
search: func SlashValidator(
|
|
spec: |
|
|
<spec fn="slash_validator" fork="phase0" hash="85d8d7c9">
|
|
def slash_validator(
|
|
state: BeaconState, slashed_index: ValidatorIndex, whistleblower_index: ValidatorIndex = None
|
|
) -> None:
|
|
"""
|
|
Slash the validator with index ``slashed_index``.
|
|
"""
|
|
epoch = get_current_epoch(state)
|
|
initiate_validator_exit(state, slashed_index)
|
|
validator = state.validators[slashed_index]
|
|
validator.slashed = True
|
|
validator.withdrawable_epoch = max(
|
|
validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR)
|
|
)
|
|
state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
|
|
decrease_balance(
|
|
state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT
|
|
)
|
|
|
|
# Apply proposer and whistleblower rewards
|
|
proposer_index = get_beacon_proposer_index(state)
|
|
if whistleblower_index is None:
|
|
whistleblower_index = proposer_index
|
|
whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
|
|
proposer_reward = Gwei(whistleblower_reward // PROPOSER_REWARD_QUOTIENT)
|
|
increase_balance(state, proposer_index, proposer_reward)
|
|
increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
|
|
</spec>
|
|
|
|
- name: slash_validator#altair
|
|
sources:
|
|
- file: beacon-chain/core/validators/validator.go
|
|
search: func SlashValidator(
|
|
spec: |
|
|
<spec fn="slash_validator" fork="altair" hash="88f6c284">
|
|
def slash_validator(
|
|
state: BeaconState, slashed_index: ValidatorIndex, whistleblower_index: ValidatorIndex = None
|
|
) -> None:
|
|
"""
|
|
Slash the validator with index ``slashed_index``.
|
|
"""
|
|
epoch = get_current_epoch(state)
|
|
initiate_validator_exit(state, slashed_index)
|
|
validator = state.validators[slashed_index]
|
|
validator.slashed = True
|
|
validator.withdrawable_epoch = max(
|
|
validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR)
|
|
)
|
|
state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
|
|
decrease_balance(
|
|
state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR
|
|
)
|
|
|
|
# Apply proposer and whistleblower rewards
|
|
proposer_index = get_beacon_proposer_index(state)
|
|
if whistleblower_index is None:
|
|
whistleblower_index = proposer_index
|
|
whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
|
|
proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
|
|
increase_balance(state, proposer_index, proposer_reward)
|
|
increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
|
|
</spec>
|
|
|
|
- name: slash_validator#bellatrix
|
|
sources:
|
|
- file: beacon-chain/core/validators/validator.go
|
|
search: func SlashValidator(
|
|
spec: |
|
|
<spec fn="slash_validator" fork="bellatrix" hash="124f6889">
|
|
def slash_validator(
|
|
state: BeaconState, slashed_index: ValidatorIndex, whistleblower_index: ValidatorIndex = None
|
|
) -> None:
|
|
"""
|
|
Slash the validator with index ``slashed_index``.
|
|
"""
|
|
epoch = get_current_epoch(state)
|
|
initiate_validator_exit(state, slashed_index)
|
|
validator = state.validators[slashed_index]
|
|
validator.slashed = True
|
|
validator.withdrawable_epoch = max(
|
|
validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR)
|
|
)
|
|
state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
|
|
# [Modified in Bellatrix]
|
|
slashing_penalty = validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX
|
|
decrease_balance(state, slashed_index, slashing_penalty)
|
|
|
|
# Apply proposer and whistleblower rewards
|
|
proposer_index = get_beacon_proposer_index(state)
|
|
if whistleblower_index is None:
|
|
whistleblower_index = proposer_index
|
|
whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
|
|
proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
|
|
increase_balance(state, proposer_index, proposer_reward)
|
|
increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
|
|
</spec>
|
|
|
|
- name: slash_validator#electra
|
|
sources:
|
|
- file: beacon-chain/core/validators/validator.go
|
|
search: func SlashValidator(
|
|
spec: |
|
|
<spec fn="slash_validator" fork="electra" hash="54b64d21">
|
|
def slash_validator(
|
|
state: BeaconState, slashed_index: ValidatorIndex, whistleblower_index: ValidatorIndex = None
|
|
) -> None:
|
|
"""
|
|
Slash the validator with index ``slashed_index``.
|
|
"""
|
|
epoch = get_current_epoch(state)
|
|
initiate_validator_exit(state, slashed_index)
|
|
validator = state.validators[slashed_index]
|
|
validator.slashed = True
|
|
validator.withdrawable_epoch = max(
|
|
validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR)
|
|
)
|
|
state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
|
|
# [Modified in Electra:EIP7251]
|
|
slashing_penalty = validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA
|
|
decrease_balance(state, slashed_index, slashing_penalty)
|
|
|
|
# Apply proposer and whistleblower rewards
|
|
proposer_index = get_beacon_proposer_index(state)
|
|
if whistleblower_index is None:
|
|
whistleblower_index = proposer_index
|
|
# [Modified in Electra:EIP7251]
|
|
whistleblower_reward = Gwei(
|
|
validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA
|
|
)
|
|
proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
|
|
increase_balance(state, proposer_index, proposer_reward)
|
|
increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
|
|
</spec>
|
|
|
|
- name: state_transition
|
|
sources:
|
|
- file: beacon-chain/core/transition/transition.go
|
|
search: func ExecuteStateTransition(
|
|
spec: |
|
|
<spec fn="state_transition" fork="phase0" hash="0b7e562f">
|
|
def state_transition(
|
|
state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool = True
|
|
) -> None:
|
|
block = signed_block.message
|
|
# Process slots (including those with no blocks) since block
|
|
process_slots(state, block.slot)
|
|
# Verify signature
|
|
if validate_result:
|
|
assert verify_block_signature(state, signed_block)
|
|
# Process block
|
|
process_block(state, block)
|
|
# Verify state root
|
|
if validate_result:
|
|
assert block.state_root == hash_tree_root(state)
|
|
</spec>
|
|
|
|
- name: store_target_checkpoint_state
|
|
sources: []
|
|
spec: |
|
|
<spec fn="store_target_checkpoint_state" fork="phase0" hash="2e4ff2b7">
|
|
def store_target_checkpoint_state(store: Store, target: Checkpoint) -> None:
|
|
# Store target checkpoint state if not yet seen
|
|
if target not in store.checkpoint_states:
|
|
base_state = copy(store.block_states[target.root])
|
|
if base_state.slot < compute_start_slot_at_epoch(target.epoch):
|
|
process_slots(base_state, compute_start_slot_at_epoch(target.epoch))
|
|
store.checkpoint_states[target] = base_state
|
|
</spec>
|
|
|
|
- name: switch_to_compounding_validator
|
|
sources:
|
|
- file: beacon-chain/core/electra/validator.go
|
|
search: func SwitchToCompoundingValidator(
|
|
spec: |
|
|
<spec fn="switch_to_compounding_validator" fork="electra" hash="a6fd864b">
|
|
def switch_to_compounding_validator(state: BeaconState, index: ValidatorIndex) -> None:
|
|
validator = state.validators[index]
|
|
validator.withdrawal_credentials = (
|
|
COMPOUNDING_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:]
|
|
)
|
|
queue_excess_active_balance(state, index)
|
|
</spec>
|
|
|
|
- name: translate_participation
|
|
sources:
|
|
- file: beacon-chain/core/altair/upgrade.go
|
|
search: func TranslateParticipation(
|
|
spec: |
|
|
<spec fn="translate_participation" fork="altair" hash="3d7c74a1">
|
|
def translate_participation(
|
|
state: BeaconState, pending_attestations: Sequence[phase0.PendingAttestation]
|
|
) -> None:
|
|
for attestation in pending_attestations:
|
|
data = attestation.data
|
|
inclusion_delay = attestation.inclusion_delay
|
|
# Translate attestation inclusion info to flag indices
|
|
participation_flag_indices = get_attestation_participation_flag_indices(
|
|
state, data, inclusion_delay
|
|
)
|
|
|
|
# Apply flags to all attesting validators
|
|
epoch_participation = state.previous_epoch_participation
|
|
for index in get_attesting_indices(state, attestation):
|
|
for flag_index in participation_flag_indices:
|
|
epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
|
|
</spec>
|
|
|
|
- name: update_checkpoints
|
|
sources:
|
|
- file: beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go
|
|
search: func (f *ForkChoice) updateCheckpoints(
|
|
spec: |
|
|
<spec fn="update_checkpoints" fork="phase0" hash="89e18dc4">
|
|
def update_checkpoints(
|
|
store: Store, justified_checkpoint: Checkpoint, finalized_checkpoint: Checkpoint
|
|
) -> None:
|
|
"""
|
|
Update checkpoints in store if necessary
|
|
"""
|
|
# Update justified checkpoint
|
|
if justified_checkpoint.epoch > store.justified_checkpoint.epoch:
|
|
store.justified_checkpoint = justified_checkpoint
|
|
|
|
# Update finalized checkpoint
|
|
if finalized_checkpoint.epoch > store.finalized_checkpoint.epoch:
|
|
store.finalized_checkpoint = finalized_checkpoint
|
|
</spec>
|
|
|
|
- name: update_latest_messages
|
|
sources: []
|
|
spec: |
|
|
<spec fn="update_latest_messages" fork="phase0" hash="3da3b413">
|
|
def update_latest_messages(
|
|
store: Store, attesting_indices: Sequence[ValidatorIndex], attestation: Attestation
|
|
) -> None:
|
|
target = attestation.data.target
|
|
beacon_block_root = attestation.data.beacon_block_root
|
|
non_equivocating_attesting_indices = [
|
|
i for i in attesting_indices if i not in store.equivocating_indices
|
|
]
|
|
for i in non_equivocating_attesting_indices:
|
|
if i not in store.latest_messages or target.epoch > store.latest_messages[i].epoch:
|
|
store.latest_messages[i] = LatestMessage(epoch=target.epoch, root=beacon_block_root)
|
|
</spec>
|
|
|
|
- name: update_unrealized_checkpoints
|
|
sources:
|
|
- file: beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification.go
|
|
search: func (f *ForkChoice) updateUnrealizedCheckpoints(
|
|
spec: |
|
|
<spec fn="update_unrealized_checkpoints" fork="phase0" hash="8cee5d3b">
|
|
def update_unrealized_checkpoints(
|
|
store: Store,
|
|
unrealized_justified_checkpoint: Checkpoint,
|
|
unrealized_finalized_checkpoint: Checkpoint,
|
|
) -> None:
|
|
"""
|
|
Update unrealized checkpoints in store if necessary
|
|
"""
|
|
# Update unrealized justified checkpoint
|
|
if unrealized_justified_checkpoint.epoch > store.unrealized_justified_checkpoint.epoch:
|
|
store.unrealized_justified_checkpoint = unrealized_justified_checkpoint
|
|
|
|
# Update unrealized finalized checkpoint
|
|
if unrealized_finalized_checkpoint.epoch > store.unrealized_finalized_checkpoint.epoch:
|
|
store.unrealized_finalized_checkpoint = unrealized_finalized_checkpoint
|
|
</spec>
|
|
|
|
- name: upgrade_lc_bootstrap_to_capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_bootstrap_to_capella" fork="capella" hash="d5f1203a">
|
|
def upgrade_lc_bootstrap_to_capella(pre: altair.LightClientBootstrap) -> LightClientBootstrap:
|
|
return LightClientBootstrap(
|
|
header=upgrade_lc_header_to_capella(pre.header),
|
|
current_sync_committee=pre.current_sync_committee,
|
|
current_sync_committee_branch=pre.current_sync_committee_branch,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_bootstrap_to_deneb
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_bootstrap_to_deneb" fork="deneb" hash="f91baf71">
|
|
def upgrade_lc_bootstrap_to_deneb(pre: capella.LightClientBootstrap) -> LightClientBootstrap:
|
|
return LightClientBootstrap(
|
|
header=upgrade_lc_header_to_deneb(pre.header),
|
|
current_sync_committee=pre.current_sync_committee,
|
|
current_sync_committee_branch=pre.current_sync_committee_branch,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_bootstrap_to_electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_bootstrap_to_electra" fork="electra" hash="93818ed1">
|
|
def upgrade_lc_bootstrap_to_electra(pre: deneb.LightClientBootstrap) -> LightClientBootstrap:
|
|
return LightClientBootstrap(
|
|
header=upgrade_lc_header_to_electra(pre.header),
|
|
current_sync_committee=pre.current_sync_committee,
|
|
current_sync_committee_branch=normalize_merkle_branch(
|
|
pre.current_sync_committee_branch, CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA
|
|
),
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_finality_update_to_capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_finality_update_to_capella" fork="capella" hash="c314b172">
|
|
def upgrade_lc_finality_update_to_capella(
|
|
pre: altair.LightClientFinalityUpdate,
|
|
) -> LightClientFinalityUpdate:
|
|
return LightClientFinalityUpdate(
|
|
attested_header=upgrade_lc_header_to_capella(pre.attested_header),
|
|
finalized_header=upgrade_lc_header_to_capella(pre.finalized_header),
|
|
finality_branch=pre.finality_branch,
|
|
sync_aggregate=pre.sync_aggregate,
|
|
signature_slot=pre.signature_slot,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_finality_update_to_deneb
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_finality_update_to_deneb" fork="deneb" hash="bfa53da6">
|
|
def upgrade_lc_finality_update_to_deneb(
|
|
pre: capella.LightClientFinalityUpdate,
|
|
) -> LightClientFinalityUpdate:
|
|
return LightClientFinalityUpdate(
|
|
attested_header=upgrade_lc_header_to_deneb(pre.attested_header),
|
|
finalized_header=upgrade_lc_header_to_deneb(pre.finalized_header),
|
|
finality_branch=pre.finality_branch,
|
|
sync_aggregate=pre.sync_aggregate,
|
|
signature_slot=pre.signature_slot,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_finality_update_to_electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_finality_update_to_electra" fork="electra" hash="1bcc6b97">
|
|
def upgrade_lc_finality_update_to_electra(
|
|
pre: deneb.LightClientFinalityUpdate,
|
|
) -> LightClientFinalityUpdate:
|
|
return LightClientFinalityUpdate(
|
|
attested_header=upgrade_lc_header_to_electra(pre.attested_header),
|
|
finalized_header=upgrade_lc_header_to_electra(pre.finalized_header),
|
|
finality_branch=normalize_merkle_branch(pre.finality_branch, FINALIZED_ROOT_GINDEX_ELECTRA),
|
|
sync_aggregate=pre.sync_aggregate,
|
|
signature_slot=pre.signature_slot,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_header_to_capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_header_to_capella" fork="capella" hash="9f7a832d">
|
|
def upgrade_lc_header_to_capella(pre: altair.LightClientHeader) -> LightClientHeader:
|
|
return LightClientHeader(
|
|
beacon=pre.beacon,
|
|
execution=ExecutionPayloadHeader(),
|
|
execution_branch=ExecutionBranch(),
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_header_to_deneb
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_header_to_deneb" fork="deneb" hash="7dc12b61">
|
|
def upgrade_lc_header_to_deneb(pre: capella.LightClientHeader) -> LightClientHeader:
|
|
return LightClientHeader(
|
|
beacon=pre.beacon,
|
|
execution=ExecutionPayloadHeader(
|
|
parent_hash=pre.execution.parent_hash,
|
|
fee_recipient=pre.execution.fee_recipient,
|
|
state_root=pre.execution.state_root,
|
|
receipts_root=pre.execution.receipts_root,
|
|
logs_bloom=pre.execution.logs_bloom,
|
|
prev_randao=pre.execution.prev_randao,
|
|
block_number=pre.execution.block_number,
|
|
gas_limit=pre.execution.gas_limit,
|
|
gas_used=pre.execution.gas_used,
|
|
timestamp=pre.execution.timestamp,
|
|
extra_data=pre.execution.extra_data,
|
|
base_fee_per_gas=pre.execution.base_fee_per_gas,
|
|
block_hash=pre.execution.block_hash,
|
|
transactions_root=pre.execution.transactions_root,
|
|
withdrawals_root=pre.execution.withdrawals_root,
|
|
# [New in Deneb:EIP4844]
|
|
blob_gas_used=uint64(0),
|
|
# [New in Deneb:EIP4844]
|
|
excess_blob_gas=uint64(0),
|
|
),
|
|
execution_branch=pre.execution_branch,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_header_to_electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_header_to_electra" fork="electra" hash="90aa82aa">
|
|
def upgrade_lc_header_to_electra(pre: deneb.LightClientHeader) -> LightClientHeader:
|
|
return LightClientHeader(
|
|
beacon=pre.beacon,
|
|
execution=pre.execution,
|
|
execution_branch=pre.execution_branch,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_optimistic_update_to_capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_optimistic_update_to_capella" fork="capella" hash="c4c29295">
|
|
def upgrade_lc_optimistic_update_to_capella(
|
|
pre: altair.LightClientOptimisticUpdate,
|
|
) -> LightClientOptimisticUpdate:
|
|
return LightClientOptimisticUpdate(
|
|
attested_header=upgrade_lc_header_to_capella(pre.attested_header),
|
|
sync_aggregate=pre.sync_aggregate,
|
|
signature_slot=pre.signature_slot,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_optimistic_update_to_deneb
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_optimistic_update_to_deneb" fork="deneb" hash="66aa73ab">
|
|
def upgrade_lc_optimistic_update_to_deneb(
|
|
pre: capella.LightClientOptimisticUpdate,
|
|
) -> LightClientOptimisticUpdate:
|
|
return LightClientOptimisticUpdate(
|
|
attested_header=upgrade_lc_header_to_deneb(pre.attested_header),
|
|
sync_aggregate=pre.sync_aggregate,
|
|
signature_slot=pre.signature_slot,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_optimistic_update_to_electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_optimistic_update_to_electra" fork="electra" hash="a6059e7d">
|
|
def upgrade_lc_optimistic_update_to_electra(
|
|
pre: deneb.LightClientOptimisticUpdate,
|
|
) -> LightClientOptimisticUpdate:
|
|
return LightClientOptimisticUpdate(
|
|
attested_header=upgrade_lc_header_to_electra(pre.attested_header),
|
|
sync_aggregate=pre.sync_aggregate,
|
|
signature_slot=pre.signature_slot,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_store_to_capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_store_to_capella" fork="capella" hash="d02773ec">
|
|
def upgrade_lc_store_to_capella(pre: altair.LightClientStore) -> LightClientStore:
|
|
if pre.best_valid_update is None:
|
|
best_valid_update = None
|
|
else:
|
|
best_valid_update = upgrade_lc_update_to_capella(pre.best_valid_update)
|
|
return LightClientStore(
|
|
finalized_header=upgrade_lc_header_to_capella(pre.finalized_header),
|
|
current_sync_committee=pre.current_sync_committee,
|
|
next_sync_committee=pre.next_sync_committee,
|
|
best_valid_update=best_valid_update,
|
|
optimistic_header=upgrade_lc_header_to_capella(pre.optimistic_header),
|
|
previous_max_active_participants=pre.previous_max_active_participants,
|
|
current_max_active_participants=pre.current_max_active_participants,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_store_to_deneb
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_store_to_deneb" fork="deneb" hash="29b26f52">
|
|
def upgrade_lc_store_to_deneb(pre: capella.LightClientStore) -> LightClientStore:
|
|
if pre.best_valid_update is None:
|
|
best_valid_update = None
|
|
else:
|
|
best_valid_update = upgrade_lc_update_to_deneb(pre.best_valid_update)
|
|
return LightClientStore(
|
|
finalized_header=upgrade_lc_header_to_deneb(pre.finalized_header),
|
|
current_sync_committee=pre.current_sync_committee,
|
|
next_sync_committee=pre.next_sync_committee,
|
|
best_valid_update=best_valid_update,
|
|
optimistic_header=upgrade_lc_header_to_deneb(pre.optimistic_header),
|
|
previous_max_active_participants=pre.previous_max_active_participants,
|
|
current_max_active_participants=pre.current_max_active_participants,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_store_to_electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_store_to_electra" fork="electra" hash="e9096017">
|
|
def upgrade_lc_store_to_electra(pre: deneb.LightClientStore) -> LightClientStore:
|
|
if pre.best_valid_update is None:
|
|
best_valid_update = None
|
|
else:
|
|
best_valid_update = upgrade_lc_update_to_electra(pre.best_valid_update)
|
|
return LightClientStore(
|
|
finalized_header=upgrade_lc_header_to_electra(pre.finalized_header),
|
|
current_sync_committee=pre.current_sync_committee,
|
|
next_sync_committee=pre.next_sync_committee,
|
|
best_valid_update=best_valid_update,
|
|
optimistic_header=upgrade_lc_header_to_electra(pre.optimistic_header),
|
|
previous_max_active_participants=pre.previous_max_active_participants,
|
|
current_max_active_participants=pre.current_max_active_participants,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_update_to_capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_update_to_capella" fork="capella" hash="ba863d2f">
|
|
def upgrade_lc_update_to_capella(pre: altair.LightClientUpdate) -> LightClientUpdate:
|
|
return LightClientUpdate(
|
|
attested_header=upgrade_lc_header_to_capella(pre.attested_header),
|
|
next_sync_committee=pre.next_sync_committee,
|
|
next_sync_committee_branch=pre.next_sync_committee_branch,
|
|
finalized_header=upgrade_lc_header_to_capella(pre.finalized_header),
|
|
finality_branch=pre.finality_branch,
|
|
sync_aggregate=pre.sync_aggregate,
|
|
signature_slot=pre.signature_slot,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_update_to_deneb
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_update_to_deneb" fork="deneb" hash="64adb1e0">
|
|
def upgrade_lc_update_to_deneb(pre: capella.LightClientUpdate) -> LightClientUpdate:
|
|
return LightClientUpdate(
|
|
attested_header=upgrade_lc_header_to_deneb(pre.attested_header),
|
|
next_sync_committee=pre.next_sync_committee,
|
|
next_sync_committee_branch=pre.next_sync_committee_branch,
|
|
finalized_header=upgrade_lc_header_to_deneb(pre.finalized_header),
|
|
finality_branch=pre.finality_branch,
|
|
sync_aggregate=pre.sync_aggregate,
|
|
signature_slot=pre.signature_slot,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_lc_update_to_electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_lc_update_to_electra" fork="electra" hash="4d42b63e">
|
|
def upgrade_lc_update_to_electra(pre: deneb.LightClientUpdate) -> LightClientUpdate:
|
|
return LightClientUpdate(
|
|
attested_header=upgrade_lc_header_to_electra(pre.attested_header),
|
|
next_sync_committee=pre.next_sync_committee,
|
|
next_sync_committee_branch=normalize_merkle_branch(
|
|
pre.next_sync_committee_branch, NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA
|
|
),
|
|
finalized_header=upgrade_lc_header_to_electra(pre.finalized_header),
|
|
finality_branch=normalize_merkle_branch(pre.finality_branch, FINALIZED_ROOT_GINDEX_ELECTRA),
|
|
sync_aggregate=pre.sync_aggregate,
|
|
signature_slot=pre.signature_slot,
|
|
)
|
|
</spec>
|
|
|
|
- name: upgrade_to_altair
|
|
sources:
|
|
- file: beacon-chain/core/altair/upgrade.go
|
|
search: func UpgradeToAltair(
|
|
spec: |
|
|
<spec fn="upgrade_to_altair" fork="altair" hash="4f858ca3">
|
|
def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState:
|
|
epoch = phase0.get_current_epoch(pre)
|
|
post = BeaconState(
|
|
genesis_time=pre.genesis_time,
|
|
genesis_validators_root=pre.genesis_validators_root,
|
|
slot=pre.slot,
|
|
fork=Fork(
|
|
previous_version=pre.fork.current_version,
|
|
current_version=ALTAIR_FORK_VERSION,
|
|
epoch=epoch,
|
|
),
|
|
latest_block_header=pre.latest_block_header,
|
|
block_roots=pre.block_roots,
|
|
state_roots=pre.state_roots,
|
|
historical_roots=pre.historical_roots,
|
|
eth1_data=pre.eth1_data,
|
|
eth1_data_votes=pre.eth1_data_votes,
|
|
eth1_deposit_index=pre.eth1_deposit_index,
|
|
validators=pre.validators,
|
|
balances=pre.balances,
|
|
randao_mixes=pre.randao_mixes,
|
|
slashings=pre.slashings,
|
|
previous_epoch_participation=[
|
|
ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))
|
|
],
|
|
current_epoch_participation=[
|
|
ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))
|
|
],
|
|
justification_bits=pre.justification_bits,
|
|
previous_justified_checkpoint=pre.previous_justified_checkpoint,
|
|
current_justified_checkpoint=pre.current_justified_checkpoint,
|
|
finalized_checkpoint=pre.finalized_checkpoint,
|
|
inactivity_scores=[uint64(0) for _ in range(len(pre.validators))],
|
|
)
|
|
# Fill in previous epoch participation from the pre state's pending attestations
|
|
translate_participation(post, pre.previous_epoch_attestations)
|
|
|
|
# Fill in sync committees
|
|
# 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
|
|
</spec>
|
|
|
|
- name: upgrade_to_bellatrix
|
|
sources:
|
|
- file: beacon-chain/core/execution/upgrade.go
|
|
search: func UpgradeToBellatrix(
|
|
spec: |
|
|
<spec fn="upgrade_to_bellatrix" fork="bellatrix" hash="73a0a7ba">
|
|
def upgrade_to_bellatrix(pre: altair.BeaconState) -> BeaconState:
|
|
epoch = altair.get_current_epoch(pre)
|
|
post = BeaconState(
|
|
genesis_time=pre.genesis_time,
|
|
genesis_validators_root=pre.genesis_validators_root,
|
|
slot=pre.slot,
|
|
fork=Fork(
|
|
previous_version=pre.fork.current_version,
|
|
# [New in Bellatrix]
|
|
current_version=BELLATRIX_FORK_VERSION,
|
|
epoch=epoch,
|
|
),
|
|
latest_block_header=pre.latest_block_header,
|
|
block_roots=pre.block_roots,
|
|
state_roots=pre.state_roots,
|
|
historical_roots=pre.historical_roots,
|
|
eth1_data=pre.eth1_data,
|
|
eth1_data_votes=pre.eth1_data_votes,
|
|
eth1_deposit_index=pre.eth1_deposit_index,
|
|
validators=pre.validators,
|
|
balances=pre.balances,
|
|
randao_mixes=pre.randao_mixes,
|
|
slashings=pre.slashings,
|
|
previous_epoch_participation=pre.previous_epoch_participation,
|
|
current_epoch_participation=pre.current_epoch_participation,
|
|
justification_bits=pre.justification_bits,
|
|
previous_justified_checkpoint=pre.previous_justified_checkpoint,
|
|
current_justified_checkpoint=pre.current_justified_checkpoint,
|
|
finalized_checkpoint=pre.finalized_checkpoint,
|
|
inactivity_scores=pre.inactivity_scores,
|
|
current_sync_committee=pre.current_sync_committee,
|
|
next_sync_committee=pre.next_sync_committee,
|
|
# [New in Bellatrix]
|
|
latest_execution_payload_header=ExecutionPayloadHeader(),
|
|
)
|
|
|
|
return post
|
|
</spec>
|
|
|
|
- name: upgrade_to_capella
|
|
sources:
|
|
- file: beacon-chain/core/capella/upgrade.go
|
|
search: func UpgradeToCapella(
|
|
spec: |
|
|
<spec fn="upgrade_to_capella" fork="capella" hash="673c74f0">
|
|
def upgrade_to_capella(pre: bellatrix.BeaconState) -> BeaconState:
|
|
epoch = bellatrix.get_current_epoch(pre)
|
|
latest_execution_payload_header = ExecutionPayloadHeader(
|
|
parent_hash=pre.latest_execution_payload_header.parent_hash,
|
|
fee_recipient=pre.latest_execution_payload_header.fee_recipient,
|
|
state_root=pre.latest_execution_payload_header.state_root,
|
|
receipts_root=pre.latest_execution_payload_header.receipts_root,
|
|
logs_bloom=pre.latest_execution_payload_header.logs_bloom,
|
|
prev_randao=pre.latest_execution_payload_header.prev_randao,
|
|
block_number=pre.latest_execution_payload_header.block_number,
|
|
gas_limit=pre.latest_execution_payload_header.gas_limit,
|
|
gas_used=pre.latest_execution_payload_header.gas_used,
|
|
timestamp=pre.latest_execution_payload_header.timestamp,
|
|
extra_data=pre.latest_execution_payload_header.extra_data,
|
|
base_fee_per_gas=pre.latest_execution_payload_header.base_fee_per_gas,
|
|
block_hash=pre.latest_execution_payload_header.block_hash,
|
|
transactions_root=pre.latest_execution_payload_header.transactions_root,
|
|
# [New in Capella]
|
|
withdrawals_root=Root(),
|
|
)
|
|
post = BeaconState(
|
|
genesis_time=pre.genesis_time,
|
|
genesis_validators_root=pre.genesis_validators_root,
|
|
slot=pre.slot,
|
|
fork=Fork(
|
|
previous_version=pre.fork.current_version,
|
|
current_version=CAPELLA_FORK_VERSION,
|
|
epoch=epoch,
|
|
),
|
|
latest_block_header=pre.latest_block_header,
|
|
block_roots=pre.block_roots,
|
|
state_roots=pre.state_roots,
|
|
historical_roots=pre.historical_roots,
|
|
eth1_data=pre.eth1_data,
|
|
eth1_data_votes=pre.eth1_data_votes,
|
|
eth1_deposit_index=pre.eth1_deposit_index,
|
|
validators=pre.validators,
|
|
balances=pre.balances,
|
|
randao_mixes=pre.randao_mixes,
|
|
slashings=pre.slashings,
|
|
previous_epoch_participation=pre.previous_epoch_participation,
|
|
current_epoch_participation=pre.current_epoch_participation,
|
|
justification_bits=pre.justification_bits,
|
|
previous_justified_checkpoint=pre.previous_justified_checkpoint,
|
|
current_justified_checkpoint=pre.current_justified_checkpoint,
|
|
finalized_checkpoint=pre.finalized_checkpoint,
|
|
inactivity_scores=pre.inactivity_scores,
|
|
current_sync_committee=pre.current_sync_committee,
|
|
next_sync_committee=pre.next_sync_committee,
|
|
latest_execution_payload_header=latest_execution_payload_header,
|
|
# [New in Capella]
|
|
next_withdrawal_index=WithdrawalIndex(0),
|
|
# [New in Capella]
|
|
next_withdrawal_validator_index=ValidatorIndex(0),
|
|
# [New in Capella]
|
|
historical_summaries=List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT]([]),
|
|
)
|
|
|
|
return post
|
|
</spec>
|
|
|
|
- name: upgrade_to_deneb
|
|
sources:
|
|
- file: beacon-chain/core/deneb/upgrade.go
|
|
search: func UpgradeToDeneb(
|
|
spec: |
|
|
<spec fn="upgrade_to_deneb" fork="deneb" hash="0c371849">
|
|
def upgrade_to_deneb(pre: capella.BeaconState) -> BeaconState:
|
|
epoch = capella.get_current_epoch(pre)
|
|
latest_execution_payload_header = ExecutionPayloadHeader(
|
|
parent_hash=pre.latest_execution_payload_header.parent_hash,
|
|
fee_recipient=pre.latest_execution_payload_header.fee_recipient,
|
|
state_root=pre.latest_execution_payload_header.state_root,
|
|
receipts_root=pre.latest_execution_payload_header.receipts_root,
|
|
logs_bloom=pre.latest_execution_payload_header.logs_bloom,
|
|
prev_randao=pre.latest_execution_payload_header.prev_randao,
|
|
block_number=pre.latest_execution_payload_header.block_number,
|
|
gas_limit=pre.latest_execution_payload_header.gas_limit,
|
|
gas_used=pre.latest_execution_payload_header.gas_used,
|
|
timestamp=pre.latest_execution_payload_header.timestamp,
|
|
extra_data=pre.latest_execution_payload_header.extra_data,
|
|
base_fee_per_gas=pre.latest_execution_payload_header.base_fee_per_gas,
|
|
block_hash=pre.latest_execution_payload_header.block_hash,
|
|
transactions_root=pre.latest_execution_payload_header.transactions_root,
|
|
withdrawals_root=pre.latest_execution_payload_header.withdrawals_root,
|
|
# [New in Deneb:EIP4844]
|
|
blob_gas_used=uint64(0),
|
|
# [New in Deneb:EIP4844]
|
|
excess_blob_gas=uint64(0),
|
|
)
|
|
post = BeaconState(
|
|
genesis_time=pre.genesis_time,
|
|
genesis_validators_root=pre.genesis_validators_root,
|
|
slot=pre.slot,
|
|
fork=Fork(
|
|
previous_version=pre.fork.current_version,
|
|
# [Modified in Deneb]
|
|
current_version=DENEB_FORK_VERSION,
|
|
epoch=epoch,
|
|
),
|
|
latest_block_header=pre.latest_block_header,
|
|
block_roots=pre.block_roots,
|
|
state_roots=pre.state_roots,
|
|
historical_roots=pre.historical_roots,
|
|
eth1_data=pre.eth1_data,
|
|
eth1_data_votes=pre.eth1_data_votes,
|
|
eth1_deposit_index=pre.eth1_deposit_index,
|
|
validators=pre.validators,
|
|
balances=pre.balances,
|
|
randao_mixes=pre.randao_mixes,
|
|
slashings=pre.slashings,
|
|
previous_epoch_participation=pre.previous_epoch_participation,
|
|
current_epoch_participation=pre.current_epoch_participation,
|
|
justification_bits=pre.justification_bits,
|
|
previous_justified_checkpoint=pre.previous_justified_checkpoint,
|
|
current_justified_checkpoint=pre.current_justified_checkpoint,
|
|
finalized_checkpoint=pre.finalized_checkpoint,
|
|
inactivity_scores=pre.inactivity_scores,
|
|
current_sync_committee=pre.current_sync_committee,
|
|
next_sync_committee=pre.next_sync_committee,
|
|
# [Modified in Deneb:EIP4844]
|
|
latest_execution_payload_header=latest_execution_payload_header,
|
|
next_withdrawal_index=pre.next_withdrawal_index,
|
|
next_withdrawal_validator_index=pre.next_withdrawal_validator_index,
|
|
historical_summaries=pre.historical_summaries,
|
|
)
|
|
|
|
return post
|
|
</spec>
|
|
|
|
- name: upgrade_to_electra
|
|
sources:
|
|
- file: beacon-chain/core/electra/upgrade.go
|
|
search: func UpgradeToElectra(
|
|
spec: |
|
|
<spec fn="upgrade_to_electra" fork="electra" hash="c82f0e68">
|
|
def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
|
|
epoch = deneb.get_current_epoch(pre)
|
|
|
|
earliest_exit_epoch = compute_activation_exit_epoch(get_current_epoch(pre))
|
|
for validator in pre.validators:
|
|
if validator.exit_epoch != FAR_FUTURE_EPOCH:
|
|
if validator.exit_epoch > earliest_exit_epoch:
|
|
earliest_exit_epoch = validator.exit_epoch
|
|
earliest_exit_epoch += Epoch(1)
|
|
|
|
post = BeaconState(
|
|
genesis_time=pre.genesis_time,
|
|
genesis_validators_root=pre.genesis_validators_root,
|
|
slot=pre.slot,
|
|
fork=Fork(
|
|
previous_version=pre.fork.current_version,
|
|
# [Modified in Electra]
|
|
current_version=ELECTRA_FORK_VERSION,
|
|
epoch=epoch,
|
|
),
|
|
latest_block_header=pre.latest_block_header,
|
|
block_roots=pre.block_roots,
|
|
state_roots=pre.state_roots,
|
|
historical_roots=pre.historical_roots,
|
|
eth1_data=pre.eth1_data,
|
|
eth1_data_votes=pre.eth1_data_votes,
|
|
eth1_deposit_index=pre.eth1_deposit_index,
|
|
validators=pre.validators,
|
|
balances=pre.balances,
|
|
randao_mixes=pre.randao_mixes,
|
|
slashings=pre.slashings,
|
|
previous_epoch_participation=pre.previous_epoch_participation,
|
|
current_epoch_participation=pre.current_epoch_participation,
|
|
justification_bits=pre.justification_bits,
|
|
previous_justified_checkpoint=pre.previous_justified_checkpoint,
|
|
current_justified_checkpoint=pre.current_justified_checkpoint,
|
|
finalized_checkpoint=pre.finalized_checkpoint,
|
|
inactivity_scores=pre.inactivity_scores,
|
|
current_sync_committee=pre.current_sync_committee,
|
|
next_sync_committee=pre.next_sync_committee,
|
|
latest_execution_payload_header=pre.latest_execution_payload_header,
|
|
next_withdrawal_index=pre.next_withdrawal_index,
|
|
next_withdrawal_validator_index=pre.next_withdrawal_validator_index,
|
|
historical_summaries=pre.historical_summaries,
|
|
# [New in Electra:EIP6110]
|
|
deposit_requests_start_index=UNSET_DEPOSIT_REQUESTS_START_INDEX,
|
|
# [New in Electra:EIP7251]
|
|
deposit_balance_to_consume=0,
|
|
# [New in Electra:EIP7251]
|
|
exit_balance_to_consume=0,
|
|
# [New in Electra:EIP7251]
|
|
earliest_exit_epoch=earliest_exit_epoch,
|
|
# [New in Electra:EIP7251]
|
|
consolidation_balance_to_consume=0,
|
|
# [New in Electra:EIP7251]
|
|
earliest_consolidation_epoch=compute_activation_exit_epoch(get_current_epoch(pre)),
|
|
# [New in Electra:EIP7251]
|
|
pending_deposits=[],
|
|
# [New in Electra:EIP7251]
|
|
pending_partial_withdrawals=[],
|
|
# [New in Electra:EIP7251]
|
|
pending_consolidations=[],
|
|
)
|
|
|
|
post.exit_balance_to_consume = get_activation_exit_churn_limit(post)
|
|
post.consolidation_balance_to_consume = get_consolidation_churn_limit(post)
|
|
|
|
# [New in Electra:EIP7251]
|
|
# add validators that are not yet active to pending balance deposits
|
|
pre_activation = sorted(
|
|
[
|
|
index
|
|
for index, validator in enumerate(post.validators)
|
|
if validator.activation_epoch == FAR_FUTURE_EPOCH
|
|
],
|
|
key=lambda index: (post.validators[index].activation_eligibility_epoch, index),
|
|
)
|
|
|
|
for index in pre_activation:
|
|
balance = post.balances[index]
|
|
post.balances[index] = 0
|
|
validator = post.validators[index]
|
|
validator.effective_balance = 0
|
|
validator.activation_eligibility_epoch = FAR_FUTURE_EPOCH
|
|
# Use bls.G2_POINT_AT_INFINITY as a signature field placeholder
|
|
# and GENESIS_SLOT to distinguish from a pending deposit request
|
|
post.pending_deposits.append(
|
|
PendingDeposit(
|
|
pubkey=validator.pubkey,
|
|
withdrawal_credentials=validator.withdrawal_credentials,
|
|
amount=balance,
|
|
signature=bls.G2_POINT_AT_INFINITY,
|
|
slot=GENESIS_SLOT,
|
|
)
|
|
)
|
|
|
|
# Ensure early adopters of compounding credentials go through the activation churn
|
|
for index, validator in enumerate(post.validators):
|
|
if has_compounding_withdrawal_credential(validator):
|
|
queue_excess_active_balance(post, ValidatorIndex(index))
|
|
|
|
return post
|
|
</spec>
|
|
|
|
- name: upgrade_to_fulu
|
|
sources:
|
|
- file: beacon-chain/core/fulu/upgrade.go
|
|
search: func UpgradeToFulu(
|
|
spec: |
|
|
<spec fn="upgrade_to_fulu" fork="fulu" hash="7c4bae8d">
|
|
def upgrade_to_fulu(pre: electra.BeaconState) -> BeaconState:
|
|
epoch = electra.get_current_epoch(pre)
|
|
post = BeaconState(
|
|
genesis_time=pre.genesis_time,
|
|
genesis_validators_root=pre.genesis_validators_root,
|
|
slot=pre.slot,
|
|
fork=Fork(
|
|
previous_version=pre.fork.current_version,
|
|
# [Modified in Fulu]
|
|
current_version=FULU_FORK_VERSION,
|
|
epoch=epoch,
|
|
),
|
|
latest_block_header=pre.latest_block_header,
|
|
block_roots=pre.block_roots,
|
|
state_roots=pre.state_roots,
|
|
historical_roots=pre.historical_roots,
|
|
eth1_data=pre.eth1_data,
|
|
eth1_data_votes=pre.eth1_data_votes,
|
|
eth1_deposit_index=pre.eth1_deposit_index,
|
|
validators=pre.validators,
|
|
balances=pre.balances,
|
|
randao_mixes=pre.randao_mixes,
|
|
slashings=pre.slashings,
|
|
previous_epoch_participation=pre.previous_epoch_participation,
|
|
current_epoch_participation=pre.current_epoch_participation,
|
|
justification_bits=pre.justification_bits,
|
|
previous_justified_checkpoint=pre.previous_justified_checkpoint,
|
|
current_justified_checkpoint=pre.current_justified_checkpoint,
|
|
finalized_checkpoint=pre.finalized_checkpoint,
|
|
inactivity_scores=pre.inactivity_scores,
|
|
current_sync_committee=pre.current_sync_committee,
|
|
next_sync_committee=pre.next_sync_committee,
|
|
latest_execution_payload_header=pre.latest_execution_payload_header,
|
|
next_withdrawal_index=pre.next_withdrawal_index,
|
|
next_withdrawal_validator_index=pre.next_withdrawal_validator_index,
|
|
historical_summaries=pre.historical_summaries,
|
|
deposit_requests_start_index=pre.deposit_requests_start_index,
|
|
deposit_balance_to_consume=pre.deposit_balance_to_consume,
|
|
exit_balance_to_consume=pre.exit_balance_to_consume,
|
|
earliest_exit_epoch=pre.earliest_exit_epoch,
|
|
consolidation_balance_to_consume=pre.consolidation_balance_to_consume,
|
|
earliest_consolidation_epoch=pre.earliest_consolidation_epoch,
|
|
pending_deposits=pre.pending_deposits,
|
|
pending_partial_withdrawals=pre.pending_partial_withdrawals,
|
|
pending_consolidations=pre.pending_consolidations,
|
|
# [New in Fulu:EIP7917]
|
|
proposer_lookahead=initialize_proposer_lookahead(pre),
|
|
)
|
|
|
|
return post
|
|
</spec>
|
|
|
|
- name: validate_light_client_update
|
|
sources: []
|
|
spec: |
|
|
<spec fn="validate_light_client_update" fork="altair" hash="eb5d169b">
|
|
def validate_light_client_update(
|
|
store: LightClientStore,
|
|
update: LightClientUpdate,
|
|
current_slot: Slot,
|
|
genesis_validators_root: Root,
|
|
) -> None:
|
|
# Verify sync committee has sufficient participants
|
|
sync_aggregate = update.sync_aggregate
|
|
assert sum(sync_aggregate.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
|
|
|
|
# Verify update does not skip a sync committee period
|
|
assert is_valid_light_client_header(update.attested_header)
|
|
update_attested_slot = update.attested_header.beacon.slot
|
|
update_finalized_slot = update.finalized_header.beacon.slot
|
|
assert current_slot >= update.signature_slot > update_attested_slot >= update_finalized_slot
|
|
store_period = compute_sync_committee_period_at_slot(store.finalized_header.beacon.slot)
|
|
update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot)
|
|
if is_next_sync_committee_known(store):
|
|
assert update_signature_period in (store_period, store_period + 1)
|
|
else:
|
|
assert update_signature_period == store_period
|
|
|
|
# Verify update is relevant
|
|
update_attested_period = compute_sync_committee_period_at_slot(update_attested_slot)
|
|
update_has_next_sync_committee = not is_next_sync_committee_known(store) and (
|
|
is_sync_committee_update(update) and update_attested_period == store_period
|
|
)
|
|
assert (
|
|
update_attested_slot > store.finalized_header.beacon.slot or update_has_next_sync_committee
|
|
)
|
|
|
|
# Verify that the `finality_branch`, if present, confirms `finalized_header`
|
|
# to match the finalized checkpoint root saved in the state of `attested_header`.
|
|
# Note that the genesis finalized checkpoint root is represented as a zero hash.
|
|
if not is_finality_update(update):
|
|
assert update.finalized_header == LightClientHeader()
|
|
else:
|
|
if update_finalized_slot == GENESIS_SLOT:
|
|
assert update.finalized_header == LightClientHeader()
|
|
finalized_root = Bytes32()
|
|
else:
|
|
assert is_valid_light_client_header(update.finalized_header)
|
|
finalized_root = hash_tree_root(update.finalized_header.beacon)
|
|
assert is_valid_normalized_merkle_branch(
|
|
leaf=finalized_root,
|
|
branch=update.finality_branch,
|
|
gindex=finalized_root_gindex_at_slot(update.attested_header.beacon.slot),
|
|
root=update.attested_header.beacon.state_root,
|
|
)
|
|
|
|
# Verify that the `next_sync_committee`, if present, actually is the next sync committee saved in the
|
|
# state of the `attested_header`
|
|
if not is_sync_committee_update(update):
|
|
assert update.next_sync_committee == SyncCommittee()
|
|
else:
|
|
if update_attested_period == store_period and is_next_sync_committee_known(store):
|
|
assert update.next_sync_committee == store.next_sync_committee
|
|
assert is_valid_normalized_merkle_branch(
|
|
leaf=hash_tree_root(update.next_sync_committee),
|
|
branch=update.next_sync_committee_branch,
|
|
gindex=next_sync_committee_gindex_at_slot(update.attested_header.beacon.slot),
|
|
root=update.attested_header.beacon.state_root,
|
|
)
|
|
|
|
# Verify sync committee aggregate signature
|
|
if update_signature_period == store_period:
|
|
sync_committee = store.current_sync_committee
|
|
else:
|
|
sync_committee = store.next_sync_committee
|
|
participant_pubkeys = [
|
|
pubkey
|
|
for (bit, pubkey) in zip(sync_aggregate.sync_committee_bits, sync_committee.pubkeys)
|
|
if bit
|
|
]
|
|
fork_version_slot = max(update.signature_slot, Slot(1)) - Slot(1)
|
|
fork_version = compute_fork_version(compute_epoch_at_slot(fork_version_slot))
|
|
domain = compute_domain(DOMAIN_SYNC_COMMITTEE, fork_version, genesis_validators_root)
|
|
signing_root = compute_signing_root(update.attested_header.beacon, domain)
|
|
assert bls.FastAggregateVerify(
|
|
participant_pubkeys, signing_root, sync_aggregate.sync_committee_signature
|
|
)
|
|
</spec>
|
|
|
|
- name: validate_merge_block
|
|
sources:
|
|
- file: beacon-chain/blockchain/pow_block.go
|
|
search: func (s *Service) validateMergeBlock(
|
|
spec: |
|
|
<spec fn="validate_merge_block" fork="bellatrix" hash="d51209cc">
|
|
def validate_merge_block(block: BeaconBlock) -> None:
|
|
"""
|
|
Check the parent PoW block of execution payload is a valid terminal PoW block.
|
|
|
|
Note: Unavailable PoW block(s) may later become available,
|
|
and a client software MAY delay a call to ``validate_merge_block``
|
|
until the PoW block(s) become available.
|
|
"""
|
|
if TERMINAL_BLOCK_HASH != Hash32():
|
|
# If `TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached.
|
|
assert compute_epoch_at_slot(block.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
|
|
assert block.body.execution_payload.parent_hash == TERMINAL_BLOCK_HASH
|
|
return
|
|
|
|
pow_block = get_pow_block(block.body.execution_payload.parent_hash)
|
|
# Check if `pow_block` is available
|
|
assert pow_block is not None
|
|
pow_parent = get_pow_block(pow_block.parent_hash)
|
|
# Check if `pow_parent` is available
|
|
assert pow_parent is not None
|
|
# Check if `pow_block` is a valid terminal PoW block
|
|
assert is_valid_terminal_pow_block(pow_block, pow_parent)
|
|
</spec>
|
|
|
|
- name: validate_on_attestation
|
|
sources: []
|
|
spec: |
|
|
<spec fn="validate_on_attestation" fork="phase0" hash="4571ccf8">
|
|
def validate_on_attestation(store: Store, attestation: Attestation, is_from_block: bool) -> None:
|
|
target = attestation.data.target
|
|
|
|
# If the given attestation is not from a beacon block message, we have to check the target epoch scope.
|
|
if not is_from_block:
|
|
validate_target_epoch_against_current_time(store, attestation)
|
|
|
|
# Check that the epoch number and slot number are matching
|
|
assert target.epoch == compute_epoch_at_slot(attestation.data.slot)
|
|
|
|
# Attestation target must be for a known block. If target block is unknown, delay consideration until block is found
|
|
assert target.root in store.blocks
|
|
|
|
# Attestations must be for a known block. If block is unknown, delay consideration until the block is found
|
|
assert attestation.data.beacon_block_root in store.blocks
|
|
# Attestations must not be for blocks in the future. If not, the attestation should not be considered
|
|
assert store.blocks[attestation.data.beacon_block_root].slot <= attestation.data.slot
|
|
|
|
# LMD vote must be consistent with FFG vote target
|
|
assert target.root == get_checkpoint_block(
|
|
store, attestation.data.beacon_block_root, target.epoch
|
|
)
|
|
|
|
# Attestations can only affect the fork choice of subsequent slots.
|
|
# Delay consideration in the fork choice until their slot is in the past.
|
|
assert get_current_slot(store) >= attestation.data.slot + 1
|
|
</spec>
|
|
|
|
- name: validate_target_epoch_against_current_time
|
|
sources: []
|
|
spec: |
|
|
<spec fn="validate_target_epoch_against_current_time" fork="phase0" hash="f7352056">
|
|
def validate_target_epoch_against_current_time(store: Store, attestation: Attestation) -> None:
|
|
target = attestation.data.target
|
|
|
|
# Attestations must be from the current or previous epoch
|
|
current_epoch = get_current_store_epoch(store)
|
|
# Use GENESIS_EPOCH for previous when genesis to avoid underflow
|
|
previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else GENESIS_EPOCH
|
|
# If attestation target is from a future epoch, delay consideration until the epoch arrives
|
|
assert target.epoch in [current_epoch, previous_epoch]
|
|
</spec>
|
|
|
|
- name: verify_blob_sidecar_inclusion_proof
|
|
sources:
|
|
- file: consensus-types/blocks/kzg.go
|
|
search: func VerifyKZGInclusionProof(
|
|
spec: |
|
|
<spec fn="verify_blob_sidecar_inclusion_proof" fork="deneb" hash="0ccdc846">
|
|
def verify_blob_sidecar_inclusion_proof(blob_sidecar: BlobSidecar) -> bool:
|
|
gindex = get_subtree_index(
|
|
get_generalized_index(BeaconBlockBody, "blob_kzg_commitments", blob_sidecar.index)
|
|
)
|
|
return is_valid_merkle_branch(
|
|
leaf=blob_sidecar.kzg_commitment.hash_tree_root(),
|
|
branch=blob_sidecar.kzg_commitment_inclusion_proof,
|
|
depth=KZG_COMMITMENT_INCLUSION_PROOF_DEPTH,
|
|
index=gindex,
|
|
root=blob_sidecar.signed_block_header.message.body_root,
|
|
)
|
|
</spec>
|
|
|
|
- name: verify_block_signature
|
|
sources:
|
|
- file: beacon-chain/core/blocks/signature.go
|
|
search: func VerifyBlockSignature(
|
|
spec: |
|
|
<spec fn="verify_block_signature" fork="phase0" hash="3a777e68">
|
|
def verify_block_signature(state: BeaconState, signed_block: SignedBeaconBlock) -> bool:
|
|
proposer = state.validators[signed_block.message.proposer_index]
|
|
signing_root = compute_signing_root(
|
|
signed_block.message, get_domain(state, DOMAIN_BEACON_PROPOSER)
|
|
)
|
|
return bls.Verify(proposer.pubkey, signing_root, signed_block.signature)
|
|
</spec>
|
|
|
|
- name: verify_data_column_sidecar
|
|
sources:
|
|
- file: beacon-chain/core/peerdas/p2p_interface.go
|
|
search: func VerifyDataColumnSidecar(
|
|
spec: |
|
|
<spec fn="verify_data_column_sidecar" fork="fulu" hash="1517491f">
|
|
def verify_data_column_sidecar(sidecar: DataColumnSidecar) -> bool:
|
|
"""
|
|
Verify if the data column sidecar is valid.
|
|
"""
|
|
# The sidecar index must be within the valid range
|
|
if sidecar.index >= NUMBER_OF_COLUMNS:
|
|
return False
|
|
|
|
# A sidecar for zero blobs is invalid
|
|
if len(sidecar.kzg_commitments) == 0:
|
|
return False
|
|
|
|
# Check that the sidecar respects the blob limit
|
|
epoch = compute_epoch_at_slot(sidecar.signed_block_header.message.slot)
|
|
if len(sidecar.kzg_commitments) > get_blob_parameters(epoch).max_blobs_per_block:
|
|
return False
|
|
|
|
# The column length must be equal to the number of commitments/proofs
|
|
if len(sidecar.column) != len(sidecar.kzg_commitments) or len(sidecar.column) != len(
|
|
sidecar.kzg_proofs
|
|
):
|
|
return False
|
|
|
|
return True
|
|
</spec>
|
|
|
|
- name: verify_data_column_sidecar_inclusion_proof
|
|
sources:
|
|
- file: beacon-chain/core/peerdas/p2p_interface.go
|
|
search: func VerifyDataColumnSidecarInclusionProof(
|
|
spec: |
|
|
<spec fn="verify_data_column_sidecar_inclusion_proof" fork="fulu" hash="aaa9b8d8">
|
|
def verify_data_column_sidecar_inclusion_proof(sidecar: DataColumnSidecar) -> bool:
|
|
"""
|
|
Verify if the given KZG commitments included in the given beacon block.
|
|
"""
|
|
return is_valid_merkle_branch(
|
|
leaf=hash_tree_root(sidecar.kzg_commitments),
|
|
branch=sidecar.kzg_commitments_inclusion_proof,
|
|
depth=KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH,
|
|
index=get_subtree_index(get_generalized_index(BeaconBlockBody, "blob_kzg_commitments")),
|
|
root=sidecar.signed_block_header.message.body_root,
|
|
)
|
|
</spec>
|
|
|
|
- name: verify_data_column_sidecar_kzg_proofs
|
|
sources:
|
|
- file: beacon-chain/core/peerdas/p2p_interface.go
|
|
search: func VerifyDataColumnsSidecarKZGProofs(
|
|
spec: |
|
|
<spec fn="verify_data_column_sidecar_kzg_proofs" fork="fulu" hash="1cd21395">
|
|
def verify_data_column_sidecar_kzg_proofs(sidecar: DataColumnSidecar) -> bool:
|
|
"""
|
|
Verify if the KZG proofs are correct.
|
|
"""
|
|
# The column index also represents the cell index
|
|
cell_indices = [CellIndex(sidecar.index)] * len(sidecar.column)
|
|
|
|
# Batch verify that the cells match the corresponding commitments and proofs
|
|
return verify_cell_kzg_proof_batch(
|
|
commitments_bytes=sidecar.kzg_commitments,
|
|
cell_indices=cell_indices,
|
|
cells=sidecar.column,
|
|
proofs_bytes=sidecar.kzg_proofs,
|
|
)
|
|
</spec>
|
|
|
|
- name: voting_period_start_time
|
|
sources:
|
|
- file: time/slots/slottime.go
|
|
search: func VotingPeriodStartTime(
|
|
spec: |
|
|
<spec fn="voting_period_start_time" fork="phase0" hash="4eb8d0ca">
|
|
def voting_period_start_time(state: BeaconState) -> uint64:
|
|
eth1_voting_period_start_slot = Slot(
|
|
state.slot - state.slot % (EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH)
|
|
)
|
|
return compute_time_at_slot(state, eth1_voting_period_start_slot)
|
|
</spec>
|
|
|
|
- name: weigh_justification_and_finalization
|
|
sources:
|
|
- file: beacon-chain/core/epoch/precompute/justification_finalization.go
|
|
search: func weighJustificationAndFinalization(
|
|
spec: |
|
|
<spec fn="weigh_justification_and_finalization" fork="phase0" hash="1087de6f">
|
|
def weigh_justification_and_finalization(
|
|
state: BeaconState,
|
|
total_active_balance: Gwei,
|
|
previous_epoch_target_balance: Gwei,
|
|
current_epoch_target_balance: Gwei,
|
|
) -> None:
|
|
previous_epoch = get_previous_epoch(state)
|
|
current_epoch = get_current_epoch(state)
|
|
old_previous_justified_checkpoint = state.previous_justified_checkpoint
|
|
old_current_justified_checkpoint = state.current_justified_checkpoint
|
|
|
|
# Process justifications
|
|
state.previous_justified_checkpoint = state.current_justified_checkpoint
|
|
state.justification_bits[1:] = state.justification_bits[: JUSTIFICATION_BITS_LENGTH - 1]
|
|
state.justification_bits[0] = 0b0
|
|
if previous_epoch_target_balance * 3 >= total_active_balance * 2:
|
|
state.current_justified_checkpoint = Checkpoint(
|
|
epoch=previous_epoch, root=get_block_root(state, previous_epoch)
|
|
)
|
|
state.justification_bits[1] = 0b1
|
|
if current_epoch_target_balance * 3 >= total_active_balance * 2:
|
|
state.current_justified_checkpoint = Checkpoint(
|
|
epoch=current_epoch, root=get_block_root(state, current_epoch)
|
|
)
|
|
state.justification_bits[0] = 0b1
|
|
|
|
# Process finalizations
|
|
bits = state.justification_bits
|
|
# The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source
|
|
if all(bits[1:4]) and old_previous_justified_checkpoint.epoch + 3 == current_epoch:
|
|
state.finalized_checkpoint = old_previous_justified_checkpoint
|
|
# The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source
|
|
if all(bits[1:3]) and old_previous_justified_checkpoint.epoch + 2 == current_epoch:
|
|
state.finalized_checkpoint = old_previous_justified_checkpoint
|
|
# The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source
|
|
if all(bits[0:3]) and old_current_justified_checkpoint.epoch + 2 == current_epoch:
|
|
state.finalized_checkpoint = old_current_justified_checkpoint
|
|
# The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source
|
|
if all(bits[0:2]) and old_current_justified_checkpoint.epoch + 1 == current_epoch:
|
|
state.finalized_checkpoint = old_current_justified_checkpoint
|
|
</spec>
|
|
|
|
- name: xor
|
|
sources: []
|
|
spec: |
|
|
<spec fn="xor" fork="phase0" hash="3b45f2e1">
|
|
def xor(bytes_1: Bytes32, bytes_2: Bytes32) -> Bytes32:
|
|
"""
|
|
Return the exclusive-or of two 32-byte strings.
|
|
"""
|
|
return Bytes32(a ^ b for a, b in zip(bytes_1, bytes_2))
|
|
</spec>
|