mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-04-19 03:01:06 -04:00
This PR moves kzg commitments to bid. The rationale behind is captured in this [issue](https://github.com/ethereum/consensus-specs/issues/4870) * Moves blob KZG commitments to the earlier point where builder intent is known * Removes duplicated commitments from data column sidecars which saves descent b/w per slot * Enables nodes to start fetching blobs via getBlobs as soon as the bid is received * Slightly increases bid size and may add minor bidding channel latency but the tradeoff favors lower network load and simpler DA handling
11176 lines
463 KiB
YAML
11176 lines
463 KiB
YAML
- name: add_builder_to_registry#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="add_builder_to_registry" fork="gloas" hash="cd0414c9">
|
|
def add_builder_to_registry(
|
|
state: BeaconState,
|
|
pubkey: BLSPubkey,
|
|
withdrawal_credentials: Bytes32,
|
|
amount: uint64,
|
|
slot: Slot,
|
|
) -> None:
|
|
set_or_append_list(
|
|
state.builders,
|
|
get_index_for_new_builder(state),
|
|
Builder(
|
|
pubkey=pubkey,
|
|
version=uint8(withdrawal_credentials[0]),
|
|
execution_address=ExecutionAddress(withdrawal_credentials[12:]),
|
|
balance=amount,
|
|
deposit_epoch=compute_epoch_at_slot(slot),
|
|
withdrawable_epoch=FAR_FUTURE_EPOCH,
|
|
),
|
|
)
|
|
</spec>
|
|
|
|
- name: add_flag#altair
|
|
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_deposit_for_builder#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="apply_deposit_for_builder" fork="gloas" hash="e4bc98c7">
|
|
def apply_deposit_for_builder(
|
|
state: BeaconState,
|
|
pubkey: BLSPubkey,
|
|
withdrawal_credentials: Bytes32,
|
|
amount: uint64,
|
|
signature: BLSSignature,
|
|
slot: Slot,
|
|
) -> None:
|
|
builder_pubkeys = [b.pubkey for b in state.builders]
|
|
if pubkey not in builder_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):
|
|
add_builder_to_registry(state, pubkey, withdrawal_credentials, amount, slot)
|
|
else:
|
|
# Increase balance by deposit amount
|
|
builder_index = builder_pubkeys.index(pubkey)
|
|
state.builders[builder_index].balance += amount
|
|
</spec>
|
|
|
|
- name: apply_light_client_update#altair
|
|
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#electra
|
|
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: apply_withdrawals#capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="apply_withdrawals" fork="capella" hash="fa29f504">
|
|
def apply_withdrawals(state: BeaconState, withdrawals: Sequence[Withdrawal]) -> None:
|
|
for withdrawal in withdrawals:
|
|
decrease_balance(state, withdrawal.validator_index, withdrawal.amount)
|
|
</spec>
|
|
|
|
- name: apply_withdrawals#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="apply_withdrawals" fork="gloas" hash="4231861d">
|
|
def apply_withdrawals(state: BeaconState, withdrawals: Sequence[Withdrawal]) -> None:
|
|
for withdrawal in withdrawals:
|
|
# [Modified in Gloas:EIP7732]
|
|
if is_builder_index(withdrawal.validator_index):
|
|
builder_index = convert_validator_index_to_builder_index(withdrawal.validator_index)
|
|
builder_balance = state.builders[builder_index].balance
|
|
state.builders[builder_index].balance -= min(withdrawal.amount, builder_balance)
|
|
else:
|
|
decrease_balance(state, withdrawal.validator_index, withdrawal.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#phase0
|
|
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#phase0
|
|
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: can_builder_cover_bid#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="can_builder_cover_bid" fork="gloas" hash="9e3f2d7c">
|
|
def can_builder_cover_bid(
|
|
state: BeaconState, builder_index: BuilderIndex, bid_amount: Gwei
|
|
) -> bool:
|
|
builder_balance = state.builders[builder_index].balance
|
|
pending_withdrawals_amount = get_pending_balance_to_withdraw_for_builder(state, builder_index)
|
|
min_balance = MIN_DEPOSIT_AMOUNT + pending_withdrawals_amount
|
|
if builder_balance < min_balance:
|
|
return False
|
|
return builder_balance - min_balance >= bid_amount
|
|
</spec>
|
|
|
|
- name: check_if_validator_active#phase0
|
|
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#phase0
|
|
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_balance_weighted_acceptance#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="compute_balance_weighted_acceptance" fork="gloas" hash="9954dcd0">
|
|
def compute_balance_weighted_acceptance(
|
|
state: BeaconState, index: ValidatorIndex, seed: Bytes32, i: uint64
|
|
) -> bool:
|
|
"""
|
|
Return whether to accept the selection of the validator ``index``, with probability
|
|
proportional to its ``effective_balance``, and randomness given by ``seed`` and ``i``.
|
|
"""
|
|
MAX_RANDOM_VALUE = 2**16 - 1
|
|
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[index].effective_balance
|
|
return effective_balance * MAX_RANDOM_VALUE >= MAX_EFFECTIVE_BALANCE_ELECTRA * random_value
|
|
</spec>
|
|
|
|
- name: compute_balance_weighted_selection#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="compute_balance_weighted_selection" fork="gloas" hash="2c9f1c23">
|
|
def compute_balance_weighted_selection(
|
|
state: BeaconState,
|
|
indices: Sequence[ValidatorIndex],
|
|
seed: Bytes32,
|
|
size: uint64,
|
|
shuffle_indices: bool,
|
|
) -> Sequence[ValidatorIndex]:
|
|
"""
|
|
Return ``size`` indices sampled by effective balance, using ``indices``
|
|
as candidates. If ``shuffle_indices`` is ``True``, candidate indices
|
|
are themselves sampled from ``indices`` by shuffling it, otherwise
|
|
``indices`` is traversed in order.
|
|
"""
|
|
total = uint64(len(indices))
|
|
assert total > 0
|
|
selected: List[ValidatorIndex] = []
|
|
i = uint64(0)
|
|
while len(selected) < size:
|
|
next_index = i % total
|
|
if shuffle_indices:
|
|
next_index = compute_shuffled_index(next_index, total, seed)
|
|
candidate_index = indices[next_index]
|
|
if compute_balance_weighted_acceptance(state, candidate_index, seed, i):
|
|
selected.append(candidate_index)
|
|
i += 1
|
|
return selected
|
|
</spec>
|
|
|
|
- name: compute_columns_for_custody_group#fulu
|
|
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#phase0
|
|
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#electra
|
|
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#phase0
|
|
sources:
|
|
- file: beacon-chain/core/signing/signing_root.go
|
|
search: func ComputeDomain(
|
|
spec: |
|
|
<spec fn="compute_domain" fork="phase0" hash="a78b32e4">
|
|
def compute_domain(
|
|
domain_type: DomainType,
|
|
fork_version: Optional[Version] = None,
|
|
genesis_validators_root: Optional[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#phase0
|
|
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#electra
|
|
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#phase0
|
|
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#phase0
|
|
sources: []
|
|
spec: |
|
|
<spec fn="compute_fork_version" fork="phase0" hash="69513274">
|
|
def compute_fork_version(epoch: Epoch) -> Version:
|
|
"""
|
|
Return the fork version at the given ``epoch``.
|
|
"""
|
|
return GENESIS_FORK_VERSION
|
|
</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_fork_version#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="compute_fork_version" fork="gloas" hash="b6fccd63">
|
|
def compute_fork_version(epoch: Epoch) -> Version:
|
|
"""
|
|
Return the fork version at the given ``epoch``.
|
|
"""
|
|
if epoch >= GLOAS_FORK_EPOCH:
|
|
return GLOAS_FORK_VERSION
|
|
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#fulu
|
|
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_merkle_proof#altair
|
|
sources: []
|
|
spec: |
|
|
<spec fn="compute_merkle_proof" fork="altair" hash="073d848c">
|
|
def compute_merkle_proof(object: SSZObject, index: GeneralizedIndex) -> Sequence[Bytes32]: ...
|
|
</spec>
|
|
|
|
- name: compute_new_state_root#phase0
|
|
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#electra
|
|
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#fulu
|
|
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_proposer_indices#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="compute_proposer_indices" fork="gloas" hash="8aed10df">
|
|
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)]
|
|
# [Modified in Gloas:EIP7732]
|
|
return [
|
|
compute_balance_weighted_selection(state, indices, seed, size=1, shuffle_indices=True)[0]
|
|
for seed in seeds
|
|
]
|
|
</spec>
|
|
|
|
- name: compute_proposer_score#phase0
|
|
sources: []
|
|
spec: |
|
|
<spec fn="compute_proposer_score" fork="phase0" hash="43ff8b01">
|
|
def compute_proposer_score(state: BeaconState) -> Gwei:
|
|
committee_weight = get_total_active_balance(state) // SLOTS_PER_EPOCH
|
|
return (committee_weight * PROPOSER_SCORE_BOOST) // 100
|
|
</spec>
|
|
|
|
- name: compute_pulled_up_tip#phase0
|
|
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#phase0
|
|
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#deneb
|
|
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#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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#fulu
|
|
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#altair
|
|
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#phase0
|
|
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#phase0
|
|
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#altair
|
|
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#altair
|
|
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#phase0
|
|
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: convert_builder_index_to_validator_index#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="convert_builder_index_to_validator_index" fork="gloas" hash="c416de6c">
|
|
def convert_builder_index_to_validator_index(builder_index: BuilderIndex) -> ValidatorIndex:
|
|
return ValidatorIndex(builder_index | BUILDER_INDEX_FLAG)
|
|
</spec>
|
|
|
|
- name: convert_validator_index_to_builder_index#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="convert_validator_index_to_builder_index" fork="gloas" hash="2fea5b47">
|
|
def convert_validator_index_to_builder_index(validator_index: ValidatorIndex) -> BuilderIndex:
|
|
return BuilderIndex(validator_index & ~BUILDER_INDEX_FLAG)
|
|
</spec>
|
|
|
|
- name: create_light_client_bootstrap#altair
|
|
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#altair
|
|
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#altair
|
|
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#altair
|
|
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#phase0
|
|
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#altair
|
|
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#altair
|
|
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#phase0
|
|
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#electra
|
|
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#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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_due_ms#phase0
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_aggregate_due_ms" fork="phase0" hash="0e8d370d">
|
|
def get_aggregate_due_ms(epoch: Epoch) -> uint64:
|
|
return get_slot_component_duration_ms(AGGREGATE_DUE_BPS)
|
|
</spec>
|
|
|
|
- name: get_aggregate_due_ms#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_aggregate_due_ms" fork="gloas" hash="33a26a53">
|
|
def get_aggregate_due_ms(epoch: Epoch) -> uint64:
|
|
# [New in Gloas]
|
|
if epoch >= GLOAS_FORK_EPOCH:
|
|
return get_slot_component_duration_ms(AGGREGATE_DUE_BPS_GLOAS)
|
|
return get_slot_component_duration_ms(AGGREGATE_DUE_BPS)
|
|
</spec>
|
|
|
|
- name: get_aggregate_signature#phase0
|
|
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#phase0
|
|
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_ancestor#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_ancestor" fork="gloas" hash="95b7e8fe">
|
|
def get_ancestor(store: Store, root: Root, slot: Slot) -> ForkChoiceNode:
|
|
"""
|
|
Returns the beacon block root and the payload status of the ancestor of the beacon block
|
|
with ``root`` at ``slot``. If the beacon block with ``root`` is already at ``slot`` or we are
|
|
requesting an ancestor "in the future", it returns ``PAYLOAD_STATUS_PENDING``.
|
|
"""
|
|
block = store.blocks[root]
|
|
if block.slot <= slot:
|
|
return ForkChoiceNode(root=root, payload_status=PAYLOAD_STATUS_PENDING)
|
|
|
|
parent = store.blocks[block.parent_root]
|
|
while parent.slot > slot:
|
|
block = parent
|
|
parent = store.blocks[block.parent_root]
|
|
|
|
return ForkChoiceNode(
|
|
root=block.parent_root,
|
|
payload_status=get_parent_payload_status(store, block),
|
|
)
|
|
</spec>
|
|
|
|
- name: get_attestation_component_deltas#phase0
|
|
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#phase0
|
|
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_due_ms#phase0
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_attestation_due_ms" fork="phase0" hash="d3b17e38">
|
|
def get_attestation_due_ms(epoch: Epoch) -> uint64:
|
|
return get_slot_component_duration_ms(ATTESTATION_DUE_BPS)
|
|
</spec>
|
|
|
|
- name: get_attestation_due_ms#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_attestation_due_ms" fork="gloas" hash="b99d28c7">
|
|
def get_attestation_due_ms(epoch: Epoch) -> uint64:
|
|
# [New in Gloas]
|
|
if epoch >= GLOAS_FORK_EPOCH:
|
|
return get_slot_component_duration_ms(ATTESTATION_DUE_BPS_GLOAS)
|
|
return get_slot_component_duration_ms(ATTESTATION_DUE_BPS)
|
|
</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_participation_flag_indices#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_attestation_participation_flag_indices" fork="gloas" hash="aeb30d51">
|
|
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
|
|
|
|
# [New in Gloas:EIP7732]
|
|
if is_attestation_same_slot(state, data):
|
|
assert data.index == 0
|
|
payload_matches = True
|
|
else:
|
|
slot_index = data.slot % SLOTS_PER_HISTORICAL_ROOT
|
|
payload_index = state.execution_payload_availability[slot_index]
|
|
payload_matches = data.index == payload_index
|
|
|
|
# Matching head
|
|
head_root = get_block_root_at_slot(state, data.slot)
|
|
head_root_matches = data.beacon_block_root == head_root
|
|
# [Modified in Gloas:EIP7732]
|
|
is_matching_head = is_matching_target and head_root_matches and payload_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:
|
|
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_score#phase0
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_attestation_score" fork="phase0" hash="da969f50">
|
|
def get_attestation_score(store: Store, root: Root, state: BeaconState) -> Gwei:
|
|
unslashed_and_active_indices = [
|
|
i
|
|
for i in get_active_validator_indices(state, get_current_epoch(state))
|
|
if not state.validators[i].slashed
|
|
]
|
|
return Gwei(
|
|
sum(
|
|
state.validators[i].effective_balance
|
|
for i in unslashed_and_active_indices
|
|
if (
|
|
i in store.latest_messages
|
|
and i not in store.equivocating_indices
|
|
and get_ancestor(store, store.latest_messages[i].root, store.blocks[root].slot)
|
|
== root
|
|
)
|
|
)
|
|
)
|
|
</spec>
|
|
|
|
- name: get_attestation_score#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_attestation_score" fork="gloas" hash="8a2e4701">
|
|
def get_attestation_score(
|
|
store: Store,
|
|
# [Modified in Gloas:EIP7732]
|
|
# Removed `root`
|
|
# [New in Gloas:EIP7732]
|
|
node: ForkChoiceNode,
|
|
state: BeaconState,
|
|
) -> Gwei:
|
|
unslashed_and_active_indices = [
|
|
i
|
|
for i in get_active_validator_indices(state, get_current_epoch(state))
|
|
if not state.validators[i].slashed
|
|
]
|
|
return Gwei(
|
|
sum(
|
|
state.validators[i].effective_balance
|
|
for i in unslashed_and_active_indices
|
|
if (
|
|
i in store.latest_messages
|
|
and i not in store.equivocating_indices
|
|
# [Modified in Gloas:EIP7732]
|
|
and is_supporting_vote(store, node, store.latest_messages[i])
|
|
)
|
|
)
|
|
)
|
|
</spec>
|
|
|
|
- name: get_attestation_signature#phase0
|
|
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#phase0
|
|
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_after_withdrawals#capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_balance_after_withdrawals" fork="capella" hash="d01eedbb">
|
|
def get_balance_after_withdrawals(
|
|
state: BeaconState,
|
|
validator_index: ValidatorIndex,
|
|
withdrawals: Sequence[Withdrawal],
|
|
) -> Gwei:
|
|
withdrawn = sum(
|
|
withdrawal.amount
|
|
for withdrawal in withdrawals
|
|
if withdrawal.validator_index == validator_index
|
|
)
|
|
return state.balances[validator_index] - withdrawn
|
|
</spec>
|
|
|
|
- name: get_balance_churn_limit#electra
|
|
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#altair
|
|
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#phase0
|
|
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#fulu
|
|
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#fulu
|
|
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#deneb
|
|
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#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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_builder_payment_quorum_threshold#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_builder_payment_quorum_threshold" fork="gloas" hash="a64b7ffb">
|
|
def get_builder_payment_quorum_threshold(state: BeaconState) -> uint64:
|
|
"""
|
|
Calculate the quorum threshold for builder payments.
|
|
"""
|
|
per_slot_balance = get_total_active_balance(state) // SLOTS_PER_EPOCH
|
|
quorum = per_slot_balance * BUILDER_PAYMENT_THRESHOLD_NUMERATOR
|
|
return uint64(quorum // BUILDER_PAYMENT_THRESHOLD_DENOMINATOR)
|
|
</spec>
|
|
|
|
- name: get_builder_withdrawals#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_builder_withdrawals" fork="gloas" hash="d54dd146">
|
|
def get_builder_withdrawals(
|
|
state: BeaconState,
|
|
withdrawal_index: WithdrawalIndex,
|
|
prior_withdrawals: Sequence[Withdrawal],
|
|
) -> Tuple[Sequence[Withdrawal], WithdrawalIndex, uint64]:
|
|
withdrawals_limit = MAX_WITHDRAWALS_PER_PAYLOAD - 1
|
|
assert len(prior_withdrawals) <= withdrawals_limit
|
|
|
|
processed_count: uint64 = 0
|
|
withdrawals: List[Withdrawal] = []
|
|
for withdrawal in state.builder_pending_withdrawals:
|
|
all_withdrawals = prior_withdrawals + withdrawals
|
|
has_reached_limit = len(all_withdrawals) >= withdrawals_limit
|
|
if has_reached_limit:
|
|
break
|
|
|
|
builder_index = withdrawal.builder_index
|
|
withdrawals.append(
|
|
Withdrawal(
|
|
index=withdrawal_index,
|
|
validator_index=convert_builder_index_to_validator_index(builder_index),
|
|
address=withdrawal.fee_recipient,
|
|
amount=withdrawal.amount,
|
|
)
|
|
)
|
|
withdrawal_index += WithdrawalIndex(1)
|
|
processed_count += 1
|
|
|
|
return withdrawals, withdrawal_index, processed_count
|
|
</spec>
|
|
|
|
- name: get_builders_sweep_withdrawals#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_builders_sweep_withdrawals" fork="gloas" hash="04c1cb10">
|
|
def get_builders_sweep_withdrawals(
|
|
state: BeaconState,
|
|
withdrawal_index: WithdrawalIndex,
|
|
prior_withdrawals: Sequence[Withdrawal],
|
|
) -> Tuple[Sequence[Withdrawal], WithdrawalIndex, uint64]:
|
|
epoch = get_current_epoch(state)
|
|
builders_limit = min(len(state.builders), MAX_BUILDERS_PER_WITHDRAWALS_SWEEP)
|
|
withdrawals_limit = MAX_WITHDRAWALS_PER_PAYLOAD - 1
|
|
assert len(prior_withdrawals) <= withdrawals_limit
|
|
|
|
processed_count: uint64 = 0
|
|
withdrawals: List[Withdrawal] = []
|
|
builder_index = state.next_withdrawal_builder_index
|
|
for _ in range(builders_limit):
|
|
all_withdrawals = prior_withdrawals + withdrawals
|
|
has_reached_limit = len(all_withdrawals) >= withdrawals_limit
|
|
if has_reached_limit:
|
|
break
|
|
|
|
builder = state.builders[builder_index]
|
|
if builder.withdrawable_epoch <= epoch and builder.balance > 0:
|
|
withdrawals.append(
|
|
Withdrawal(
|
|
index=withdrawal_index,
|
|
validator_index=convert_builder_index_to_validator_index(builder_index),
|
|
address=builder.execution_address,
|
|
amount=builder.balance,
|
|
)
|
|
)
|
|
withdrawal_index += WithdrawalIndex(1)
|
|
|
|
builder_index = BuilderIndex((builder_index + 1) % len(state.builders))
|
|
processed_count += 1
|
|
|
|
return withdrawals, withdrawal_index, processed_count
|
|
</spec>
|
|
|
|
- name: get_checkpoint_block#phase0
|
|
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_checkpoint_block#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_checkpoint_block" fork="gloas" hash="e238b732">
|
|
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).root
|
|
</spec>
|
|
|
|
- name: get_committee_assignment#phase0
|
|
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#phase0
|
|
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#electra
|
|
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#electra
|
|
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#altair
|
|
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#altair
|
|
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_contribution_due_ms#altair
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_contribution_due_ms" fork="altair" hash="e647c2b1">
|
|
def get_contribution_due_ms(epoch: Epoch) -> uint64:
|
|
return get_slot_component_duration_ms(CONTRIBUTION_DUE_BPS)
|
|
</spec>
|
|
|
|
- name: get_contribution_due_ms#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_contribution_due_ms" fork="gloas" hash="69183c6c">
|
|
def get_contribution_due_ms(epoch: Epoch) -> uint64:
|
|
# [New in Gloas]
|
|
if epoch >= GLOAS_FORK_EPOCH:
|
|
return get_slot_component_duration_ms(CONTRIBUTION_DUE_BPS_GLOAS)
|
|
return get_slot_component_duration_ms(CONTRIBUTION_DUE_BPS)
|
|
</spec>
|
|
|
|
- name: get_current_epoch#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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#fulu
|
|
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#fulu
|
|
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_data_column_sidecars#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_data_column_sidecars" fork="gloas" hash="abaf4385">
|
|
def get_data_column_sidecars(
|
|
# [Modified in Gloas:EIP7732]
|
|
# Removed `signed_block_header`
|
|
# [New in Gloas:EIP7732]
|
|
beacon_block_root: Root,
|
|
# [New in Gloas:EIP7732]
|
|
slot: Slot,
|
|
# [Modified in Gloas:EIP7732]
|
|
# Removed `kzg_commitments`
|
|
# [Modified in Gloas:EIP7732]
|
|
# Removed `kzg_commitments_inclusion_proof`
|
|
cells_and_kzg_proofs: Sequence[
|
|
Tuple[Vector[Cell, CELLS_PER_EXT_BLOB], Vector[KZGProof, CELLS_PER_EXT_BLOB]]
|
|
],
|
|
) -> Sequence[DataColumnSidecar]:
|
|
"""
|
|
Given a beacon block root and the cells/proofs associated with each blob
|
|
in the corresponding payload, assemble the sidecars which can be
|
|
distributed to peers.
|
|
"""
|
|
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(
|
|
# [Modified in Gloas:EIP7732]
|
|
DataColumnSidecar(
|
|
index=column_index,
|
|
column=column_cells,
|
|
kzg_proofs=column_proofs,
|
|
slot=slot,
|
|
beacon_block_root=beacon_block_root,
|
|
)
|
|
)
|
|
return sidecars
|
|
</spec>
|
|
|
|
- name: get_data_column_sidecars_from_block#fulu
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_data_column_sidecars_from_block" fork="fulu" hash="02ffae23">
|
|
def get_data_column_sidecars_from_block(
|
|
signed_block: SignedBeaconBlock,
|
|
cells_and_kzg_proofs: Sequence[
|
|
Tuple[Vector[Cell, CELLS_PER_EXT_BLOB], Vector[KZGProof, CELLS_PER_EXT_BLOB]]
|
|
],
|
|
) -> Sequence[DataColumnSidecar]:
|
|
"""
|
|
Given a signed block and the cells/proofs associated with each blob in the
|
|
block, assemble the sidecars which can be distributed to peers.
|
|
"""
|
|
blob_kzg_commitments = signed_block.message.body.blob_kzg_commitments
|
|
signed_block_header = compute_signed_block_header(signed_block)
|
|
kzg_commitments_inclusion_proof = compute_merkle_proof(
|
|
signed_block.message.body,
|
|
get_generalized_index(BeaconBlockBody, "blob_kzg_commitments"),
|
|
)
|
|
return get_data_column_sidecars(
|
|
signed_block_header,
|
|
blob_kzg_commitments,
|
|
kzg_commitments_inclusion_proof,
|
|
cells_and_kzg_proofs,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_data_column_sidecars_from_block#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_data_column_sidecars_from_block" fork="gloas" hash="302616d2">
|
|
def get_data_column_sidecars_from_block(
|
|
signed_block: SignedBeaconBlock,
|
|
cells_and_kzg_proofs: Sequence[
|
|
Tuple[Vector[Cell, CELLS_PER_EXT_BLOB], Vector[KZGProof, CELLS_PER_EXT_BLOB]]
|
|
],
|
|
) -> Sequence[DataColumnSidecar]:
|
|
"""
|
|
Given a signed block and the cells/proofs associated with each blob in the
|
|
block, assemble the sidecars which can be distributed to peers.
|
|
"""
|
|
beacon_block_root = hash_tree_root(signed_block.message)
|
|
return get_data_column_sidecars(
|
|
beacon_block_root,
|
|
signed_block.message.slot,
|
|
cells_and_kzg_proofs,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_data_column_sidecars_from_column_sidecar#fulu
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_data_column_sidecars_from_column_sidecar" fork="fulu" hash="4877148a">
|
|
def get_data_column_sidecars_from_column_sidecar(
|
|
sidecar: DataColumnSidecar,
|
|
cells_and_kzg_proofs: Sequence[
|
|
Tuple[Vector[Cell, CELLS_PER_EXT_BLOB], Vector[KZGProof, CELLS_PER_EXT_BLOB]]
|
|
],
|
|
) -> Sequence[DataColumnSidecar]:
|
|
"""
|
|
Given a data column sidecar and the cells/proofs associated with each blob corresponding
|
|
to the commitments it contains, assemble all sidecars for distribution to peers.
|
|
"""
|
|
assert len(cells_and_kzg_proofs) == len(sidecar.kzg_commitments)
|
|
|
|
return get_data_column_sidecars(
|
|
sidecar.signed_block_header,
|
|
sidecar.kzg_commitments,
|
|
sidecar.kzg_commitments_inclusion_proof,
|
|
cells_and_kzg_proofs,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_data_column_sidecars_from_column_sidecar#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_data_column_sidecars_from_column_sidecar" fork="gloas" hash="beb1f94f">
|
|
def get_data_column_sidecars_from_column_sidecar(
|
|
sidecar: DataColumnSidecar,
|
|
cells_and_kzg_proofs: Sequence[
|
|
Tuple[Vector[Cell, CELLS_PER_EXT_BLOB], Vector[KZGProof, CELLS_PER_EXT_BLOB]]
|
|
],
|
|
) -> Sequence[DataColumnSidecar]:
|
|
"""
|
|
Given a data column sidecar and the cells/proofs associated with each blob
|
|
in the corresponding payload, assemble the sidecars which can be
|
|
distributed to peers.
|
|
"""
|
|
# [Modified in Gloas:EIP7732]
|
|
return get_data_column_sidecars(
|
|
sidecar.beacon_block_root,
|
|
sidecar.slot,
|
|
cells_and_kzg_proofs,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_domain#phase0
|
|
sources:
|
|
- file: beacon-chain/core/signing/domain.go
|
|
search: func Domain(
|
|
spec: |
|
|
<spec fn="get_domain" fork="phase0" hash="e60c5fbc">
|
|
def get_domain(
|
|
state: BeaconState, domain_type: DomainType, epoch: Optional[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#phase0
|
|
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#phase0
|
|
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#electra
|
|
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#bellatrix
|
|
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_payload_bid_signature#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_execution_payload_bid_signature" fork="gloas" hash="c09cbb78">
|
|
def get_execution_payload_bid_signature(
|
|
state: BeaconState, bid: ExecutionPayloadBid, privkey: int
|
|
) -> BLSSignature:
|
|
domain = get_domain(state, DOMAIN_BEACON_BUILDER, compute_epoch_at_slot(bid.slot))
|
|
signing_root = compute_signing_root(bid, domain)
|
|
return bls.Sign(privkey, signing_root)
|
|
</spec>
|
|
|
|
- name: get_execution_payload_envelope_signature#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_execution_payload_envelope_signature" fork="gloas" hash="7291faa5">
|
|
def get_execution_payload_envelope_signature(
|
|
state: BeaconState, envelope: ExecutionPayloadEnvelope, privkey: int
|
|
) -> BLSSignature:
|
|
domain = get_domain(state, DOMAIN_BEACON_BUILDER, compute_epoch_at_slot(state.slot))
|
|
signing_root = compute_signing_root(envelope, domain)
|
|
return bls.Sign(privkey, signing_root)
|
|
</spec>
|
|
|
|
- name: get_execution_requests#electra
|
|
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#electra
|
|
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="12088347">
|
|
def get_expected_withdrawals(state: BeaconState) -> ExpectedWithdrawals:
|
|
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 ExpectedWithdrawals(
|
|
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="254541e3">
|
|
def get_expected_withdrawals(state: BeaconState) -> ExpectedWithdrawals:
|
|
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)
|
|
|
|
return ExpectedWithdrawals(
|
|
withdrawals,
|
|
# [New in Electra:EIP7251]
|
|
processed_partial_withdrawals_count,
|
|
processed_validators_sweep_count,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_expected_withdrawals#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_expected_withdrawals" fork="gloas" hash="8d0675cb">
|
|
def get_expected_withdrawals(state: BeaconState) -> ExpectedWithdrawals:
|
|
withdrawal_index = state.next_withdrawal_index
|
|
withdrawals: List[Withdrawal] = []
|
|
|
|
# [New in Gloas:EIP7732]
|
|
# Get builder withdrawals
|
|
builder_withdrawals, withdrawal_index, processed_builder_withdrawals_count = (
|
|
get_builder_withdrawals(state, withdrawal_index, withdrawals)
|
|
)
|
|
withdrawals.extend(builder_withdrawals)
|
|
|
|
# Get partial withdrawals
|
|
partial_withdrawals, withdrawal_index, processed_partial_withdrawals_count = (
|
|
get_pending_partial_withdrawals(state, withdrawal_index, withdrawals)
|
|
)
|
|
withdrawals.extend(partial_withdrawals)
|
|
|
|
# [New in Gloas:EIP7732]
|
|
# Get builders sweep withdrawals
|
|
builders_sweep_withdrawals, withdrawal_index, processed_builders_sweep_count = (
|
|
get_builders_sweep_withdrawals(state, withdrawal_index, withdrawals)
|
|
)
|
|
withdrawals.extend(builders_sweep_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)
|
|
|
|
return ExpectedWithdrawals(
|
|
withdrawals,
|
|
# [New in Gloas:EIP7732]
|
|
processed_builder_withdrawals_count,
|
|
processed_partial_withdrawals_count,
|
|
# [New in Gloas:EIP7732]
|
|
processed_builders_sweep_count,
|
|
processed_validators_sweep_count,
|
|
)
|
|
</spec>
|
|
|
|
- name: get_filtered_block_tree#phase0
|
|
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#phase0
|
|
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#altair
|
|
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#phase0
|
|
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_forkchoice_store#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_forkchoice_store" fork="gloas" hash="40cb6fd0">
|
|
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},
|
|
# [New in Gloas:EIP7732]
|
|
execution_payload_states={anchor_root: copy(anchor_state)},
|
|
ptc_vote={anchor_root: Vector[boolean, PTC_SIZE]()},
|
|
block_timeliness={anchor_root: [True, True]},
|
|
)
|
|
</spec>
|
|
|
|
- name: get_head#phase0
|
|
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#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_head" fork="gloas" hash="d03052ed">
|
|
def get_head(store: Store) -> ForkChoiceNode:
|
|
# Get filtered block tree that only includes viable branches
|
|
blocks = get_filtered_block_tree(store)
|
|
# Execute the LMD-GHOST fork-choice
|
|
head = ForkChoiceNode(
|
|
root=store.justified_checkpoint.root,
|
|
payload_status=PAYLOAD_STATUS_PENDING,
|
|
)
|
|
|
|
while True:
|
|
children = get_node_children(store, blocks, head)
|
|
if len(children) == 0:
|
|
return head
|
|
# Sort by latest attesting balance with ties broken lexicographically
|
|
head = max(
|
|
children,
|
|
key=lambda child: (
|
|
get_weight(store, child),
|
|
child.root,
|
|
get_payload_status_tiebreaker(store, child),
|
|
),
|
|
)
|
|
</spec>
|
|
|
|
- name: get_head_deltas#phase0
|
|
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#phase0
|
|
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_builder#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_index_for_new_builder" fork="gloas" hash="82dfbdb5">
|
|
def get_index_for_new_builder(state: BeaconState) -> BuilderIndex:
|
|
for index, builder in enumerate(state.builders):
|
|
if builder.withdrawable_epoch <= get_current_epoch(state) and builder.balance == 0:
|
|
return BuilderIndex(index)
|
|
return BuilderIndex(len(state.builders))
|
|
</spec>
|
|
|
|
- name: get_index_for_new_validator#altair
|
|
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#phase0
|
|
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_indexed_payload_attestation#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_indexed_payload_attestation" fork="gloas" hash="0e516ffb">
|
|
def get_indexed_payload_attestation(
|
|
state: BeaconState, payload_attestation: PayloadAttestation
|
|
) -> IndexedPayloadAttestation:
|
|
"""
|
|
Return the indexed payload attestation corresponding to ``payload_attestation``.
|
|
"""
|
|
slot = payload_attestation.data.slot
|
|
ptc = get_ptc(state, slot)
|
|
bits = payload_attestation.aggregation_bits
|
|
attesting_indices = [index for i, index in enumerate(ptc) if bits[i]]
|
|
|
|
return IndexedPayloadAttestation(
|
|
attesting_indices=sorted(attesting_indices),
|
|
data=payload_attestation.data,
|
|
signature=payload_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#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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#electra
|
|
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#altair
|
|
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_next_sync_committee_indices#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_next_sync_committee_indices" fork="gloas" hash="4078cd19">
|
|
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)
|
|
seed = get_seed(state, epoch, DOMAIN_SYNC_COMMITTEE)
|
|
indices = get_active_validator_indices(state, epoch)
|
|
return compute_balance_weighted_selection(
|
|
state, indices, seed, size=SYNC_COMMITTEE_SIZE, shuffle_indices=True
|
|
)
|
|
</spec>
|
|
|
|
- name: get_node_children#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_node_children" fork="gloas" hash="b6012515">
|
|
def get_node_children(
|
|
store: Store, blocks: Dict[Root, BeaconBlock], node: ForkChoiceNode
|
|
) -> Sequence[ForkChoiceNode]:
|
|
if node.payload_status == PAYLOAD_STATUS_PENDING:
|
|
children = [ForkChoiceNode(root=node.root, payload_status=PAYLOAD_STATUS_EMPTY)]
|
|
if node.root in store.execution_payload_states:
|
|
children.append(ForkChoiceNode(root=node.root, payload_status=PAYLOAD_STATUS_FULL))
|
|
return children
|
|
else:
|
|
return [
|
|
ForkChoiceNode(root=root, payload_status=PAYLOAD_STATUS_PENDING)
|
|
for root in blocks.keys()
|
|
if (
|
|
blocks[root].parent_root == node.root
|
|
and node.payload_status == get_parent_payload_status(store, blocks[root])
|
|
)
|
|
]
|
|
</spec>
|
|
|
|
- name: get_parent_payload_status#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_parent_payload_status" fork="gloas" hash="4ab82d16">
|
|
def get_parent_payload_status(store: Store, block: BeaconBlock) -> PayloadStatus:
|
|
parent = store.blocks[block.parent_root]
|
|
parent_block_hash = block.body.signed_execution_payload_bid.message.parent_block_hash
|
|
message_block_hash = parent.body.signed_execution_payload_bid.message.block_hash
|
|
return PAYLOAD_STATUS_FULL if parent_block_hash == message_block_hash else PAYLOAD_STATUS_EMPTY
|
|
</spec>
|
|
|
|
- name: get_payload_attestation_due_ms#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_payload_attestation_due_ms" fork="gloas" hash="16f3b9cb">
|
|
def get_payload_attestation_due_ms(epoch: Epoch) -> uint64:
|
|
return get_slot_component_duration_ms(PAYLOAD_ATTESTATION_DUE_BPS)
|
|
</spec>
|
|
|
|
- name: get_payload_attestation_message_signature#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_payload_attestation_message_signature" fork="gloas" hash="e2a06b4b">
|
|
def get_payload_attestation_message_signature(
|
|
state: BeaconState, attestation: PayloadAttestationMessage, privkey: int
|
|
) -> BLSSignature:
|
|
domain = get_domain(state, DOMAIN_PTC_ATTESTER, compute_epoch_at_slot(attestation.data.slot))
|
|
signing_root = compute_signing_root(attestation.data, domain)
|
|
return bls.Sign(privkey, signing_root)
|
|
</spec>
|
|
|
|
- name: get_payload_status_tiebreaker#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_payload_status_tiebreaker" fork="gloas" hash="5ca841c3">
|
|
def get_payload_status_tiebreaker(store: Store, node: ForkChoiceNode) -> uint8:
|
|
if node.payload_status == PAYLOAD_STATUS_PENDING or store.blocks[
|
|
node.root
|
|
].slot + 1 != get_current_slot(store):
|
|
return node.payload_status
|
|
else:
|
|
# To decide on a payload from the previous slot, choose
|
|
# between FULL and EMPTY based on `should_extend_payload`
|
|
if node.payload_status == PAYLOAD_STATUS_EMPTY:
|
|
return 1
|
|
else:
|
|
return 2 if should_extend_payload(store, node.root) else 0
|
|
</spec>
|
|
|
|
- name: get_pending_balance_to_withdraw#electra
|
|
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_pending_balance_to_withdraw_for_builder#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_pending_balance_to_withdraw_for_builder" fork="gloas" hash="a5d10dc1">
|
|
def get_pending_balance_to_withdraw_for_builder(
|
|
state: BeaconState, builder_index: BuilderIndex
|
|
) -> Gwei:
|
|
return sum(
|
|
withdrawal.amount
|
|
for withdrawal in state.builder_pending_withdrawals
|
|
if withdrawal.builder_index == builder_index
|
|
) + sum(
|
|
payment.withdrawal.amount
|
|
for payment in state.builder_pending_payments
|
|
if payment.withdrawal.builder_index == builder_index
|
|
)
|
|
</spec>
|
|
|
|
- name: get_pending_partial_withdrawals#electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_pending_partial_withdrawals" fork="electra" hash="306047e9">
|
|
def get_pending_partial_withdrawals(
|
|
state: BeaconState,
|
|
withdrawal_index: WithdrawalIndex,
|
|
prior_withdrawals: Sequence[Withdrawal],
|
|
) -> Tuple[Sequence[Withdrawal], WithdrawalIndex, uint64]:
|
|
epoch = get_current_epoch(state)
|
|
withdrawals_limit = min(
|
|
len(prior_withdrawals) + MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP,
|
|
MAX_WITHDRAWALS_PER_PAYLOAD - 1,
|
|
)
|
|
assert len(prior_withdrawals) <= withdrawals_limit
|
|
|
|
processed_count: uint64 = 0
|
|
withdrawals: List[Withdrawal] = []
|
|
for withdrawal in state.pending_partial_withdrawals:
|
|
all_withdrawals = prior_withdrawals + withdrawals
|
|
is_withdrawable = withdrawal.withdrawable_epoch <= epoch
|
|
has_reached_limit = len(all_withdrawals) >= withdrawals_limit
|
|
if not is_withdrawable or has_reached_limit:
|
|
break
|
|
|
|
validator_index = withdrawal.validator_index
|
|
validator = state.validators[validator_index]
|
|
balance = get_balance_after_withdrawals(state, validator_index, all_withdrawals)
|
|
if is_eligible_for_partial_withdrawals(validator, balance):
|
|
withdrawal_amount = min(balance - MIN_ACTIVATION_BALANCE, withdrawal.amount)
|
|
withdrawals.append(
|
|
Withdrawal(
|
|
index=withdrawal_index,
|
|
validator_index=validator_index,
|
|
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
|
|
amount=withdrawal_amount,
|
|
)
|
|
)
|
|
withdrawal_index += WithdrawalIndex(1)
|
|
|
|
processed_count += 1
|
|
|
|
return withdrawals, withdrawal_index, processed_count
|
|
</spec>
|
|
|
|
- name: get_pow_block_at_terminal_total_difficulty#bellatrix
|
|
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#phase0
|
|
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#phase0
|
|
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_preferences_signature#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_proposer_preferences_signature" fork="gloas" hash="6a34cb73">
|
|
def get_proposer_preferences_signature(
|
|
state: BeaconState, preferences: ProposerPreferences, privkey: int
|
|
) -> BLSSignature:
|
|
domain = get_domain(
|
|
state, DOMAIN_PROPOSER_PREFERENCES, compute_epoch_at_slot(preferences.proposal_slot)
|
|
)
|
|
signing_root = compute_signing_root(preferences, domain)
|
|
return bls.Sign(privkey, signing_root)
|
|
</spec>
|
|
|
|
- name: get_proposer_reorg_cutoff_ms#phase0
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_proposer_reorg_cutoff_ms" fork="phase0" hash="20263733">
|
|
def get_proposer_reorg_cutoff_ms(epoch: Epoch) -> uint64:
|
|
return get_slot_component_duration_ms(PROPOSER_REORG_CUTOFF_BPS)
|
|
</spec>
|
|
|
|
- name: get_proposer_reward#phase0
|
|
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#phase0
|
|
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_ptc#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_ptc" fork="gloas" hash="ae15f761">
|
|
def get_ptc(state: BeaconState, slot: Slot) -> Vector[ValidatorIndex, PTC_SIZE]:
|
|
"""
|
|
Get the payload timeliness committee for the given ``slot``.
|
|
"""
|
|
epoch = compute_epoch_at_slot(slot)
|
|
seed = hash(get_seed(state, epoch, DOMAIN_PTC_ATTESTER) + uint_to_bytes(slot))
|
|
indices: List[ValidatorIndex] = []
|
|
# Concatenate all committees for this slot in order
|
|
committees_per_slot = get_committee_count_per_slot(state, epoch)
|
|
for i in range(committees_per_slot):
|
|
committee = get_beacon_committee(state, slot, CommitteeIndex(i))
|
|
indices.extend(committee)
|
|
return compute_balance_weighted_selection(
|
|
state, indices, seed, size=PTC_SIZE, shuffle_indices=False
|
|
)
|
|
</spec>
|
|
|
|
- name: get_ptc_assignment#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_ptc_assignment" fork="gloas" hash="7fd50097">
|
|
def get_ptc_assignment(
|
|
state: BeaconState, epoch: Epoch, validator_index: ValidatorIndex
|
|
) -> Optional[Slot]:
|
|
"""
|
|
Returns the slot during the requested epoch in which the validator with
|
|
index ``validator_index`` is a member of the PTC. Returns None if no
|
|
assignment is found.
|
|
"""
|
|
next_epoch = Epoch(get_current_epoch(state) + 1)
|
|
assert epoch <= next_epoch
|
|
|
|
start_slot = compute_start_slot_at_epoch(epoch)
|
|
for slot in range(start_slot, start_slot + SLOTS_PER_EPOCH):
|
|
if validator_index in get_ptc(state, Slot(slot)):
|
|
return Slot(slot)
|
|
return None
|
|
</spec>
|
|
|
|
- name: get_randao_mix#phase0
|
|
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#altair
|
|
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#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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#altair
|
|
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#altair
|
|
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#altair
|
|
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_message_due_ms#altair
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_sync_message_due_ms" fork="altair" hash="4d86dced">
|
|
def get_sync_message_due_ms(epoch: Epoch) -> uint64:
|
|
return get_slot_component_duration_ms(SYNC_MESSAGE_DUE_BPS)
|
|
</spec>
|
|
|
|
- name: get_sync_message_due_ms#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_sync_message_due_ms" fork="gloas" hash="6af451b5">
|
|
def get_sync_message_due_ms(epoch: Epoch) -> uint64:
|
|
# [New in Gloas]
|
|
if epoch >= GLOAS_FORK_EPOCH:
|
|
return get_slot_component_duration_ms(SYNC_MESSAGE_DUE_BPS_GLOAS)
|
|
return get_slot_component_duration_ms(SYNC_MESSAGE_DUE_BPS)
|
|
</spec>
|
|
|
|
- name: get_sync_subcommittee_pubkeys#altair
|
|
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#phase0
|
|
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#bellatrix
|
|
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#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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#altair
|
|
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_upcoming_proposal_slots#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_upcoming_proposal_slots" fork="gloas" hash="802ba480">
|
|
def get_upcoming_proposal_slots(
|
|
state: BeaconState, validator_index: ValidatorIndex
|
|
) -> Sequence[Slot]:
|
|
"""
|
|
Get the slots in the next epoch for which ``validator_index`` is proposing.
|
|
"""
|
|
return [
|
|
Slot(compute_start_slot_at_epoch(get_current_epoch(state) + Epoch(1)) + offset)
|
|
for offset, proposer_index in enumerate(state.proposer_lookahead[SLOTS_PER_EPOCH:])
|
|
if validator_index == proposer_index
|
|
]
|
|
</spec>
|
|
|
|
- name: get_validator_activation_churn_limit#deneb
|
|
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#phase0
|
|
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#fulu
|
|
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_validators_sweep_withdrawals#capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_validators_sweep_withdrawals" fork="capella" hash="59563c2a">
|
|
def get_validators_sweep_withdrawals(
|
|
state: BeaconState,
|
|
withdrawal_index: WithdrawalIndex,
|
|
prior_withdrawals: Sequence[Withdrawal],
|
|
) -> Tuple[Sequence[Withdrawal], WithdrawalIndex, uint64]:
|
|
epoch = get_current_epoch(state)
|
|
validators_limit = min(len(state.validators), MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP)
|
|
withdrawals_limit = MAX_WITHDRAWALS_PER_PAYLOAD
|
|
# There must be at least one space reserved for validator sweep withdrawals
|
|
assert len(prior_withdrawals) < withdrawals_limit
|
|
|
|
processed_count: uint64 = 0
|
|
withdrawals: List[Withdrawal] = []
|
|
validator_index = state.next_withdrawal_validator_index
|
|
for _ in range(validators_limit):
|
|
all_withdrawals = prior_withdrawals + withdrawals
|
|
has_reached_limit = len(all_withdrawals) >= withdrawals_limit
|
|
if has_reached_limit:
|
|
break
|
|
|
|
validator = state.validators[validator_index]
|
|
balance = get_balance_after_withdrawals(state, validator_index, all_withdrawals)
|
|
if is_fully_withdrawable_validator(validator, balance, epoch):
|
|
withdrawals.append(
|
|
Withdrawal(
|
|
index=withdrawal_index,
|
|
validator_index=validator_index,
|
|
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
|
|
amount=balance,
|
|
)
|
|
)
|
|
withdrawal_index += WithdrawalIndex(1)
|
|
elif is_partially_withdrawable_validator(validator, balance):
|
|
withdrawals.append(
|
|
Withdrawal(
|
|
index=withdrawal_index,
|
|
validator_index=validator_index,
|
|
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
|
|
amount=balance - MAX_EFFECTIVE_BALANCE,
|
|
)
|
|
)
|
|
withdrawal_index += WithdrawalIndex(1)
|
|
|
|
validator_index = ValidatorIndex((validator_index + 1) % len(state.validators))
|
|
processed_count += 1
|
|
|
|
return withdrawals, withdrawal_index, processed_count
|
|
</spec>
|
|
|
|
- name: get_validators_sweep_withdrawals#electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_validators_sweep_withdrawals" fork="electra" hash="034093ad">
|
|
def get_validators_sweep_withdrawals(
|
|
state: BeaconState,
|
|
withdrawal_index: WithdrawalIndex,
|
|
prior_withdrawals: Sequence[Withdrawal],
|
|
) -> Tuple[Sequence[Withdrawal], WithdrawalIndex, uint64]:
|
|
epoch = get_current_epoch(state)
|
|
validators_limit = min(len(state.validators), MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP)
|
|
withdrawals_limit = MAX_WITHDRAWALS_PER_PAYLOAD
|
|
# There must be at least one space reserved for validator sweep withdrawals
|
|
assert len(prior_withdrawals) < withdrawals_limit
|
|
|
|
processed_count: uint64 = 0
|
|
withdrawals: List[Withdrawal] = []
|
|
validator_index = state.next_withdrawal_validator_index
|
|
for _ in range(validators_limit):
|
|
all_withdrawals = prior_withdrawals + withdrawals
|
|
has_reached_limit = len(all_withdrawals) >= withdrawals_limit
|
|
if has_reached_limit:
|
|
break
|
|
|
|
validator = state.validators[validator_index]
|
|
balance = get_balance_after_withdrawals(state, validator_index, all_withdrawals)
|
|
if is_fully_withdrawable_validator(validator, balance, epoch):
|
|
withdrawals.append(
|
|
Withdrawal(
|
|
index=withdrawal_index,
|
|
validator_index=validator_index,
|
|
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
|
|
amount=balance,
|
|
)
|
|
)
|
|
withdrawal_index += WithdrawalIndex(1)
|
|
elif is_partially_withdrawable_validator(validator, balance):
|
|
withdrawals.append(
|
|
Withdrawal(
|
|
index=withdrawal_index,
|
|
validator_index=validator_index,
|
|
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
|
|
# [Modified in Electra:EIP7251]
|
|
amount=balance - get_max_effective_balance(validator),
|
|
)
|
|
)
|
|
withdrawal_index += WithdrawalIndex(1)
|
|
|
|
validator_index = ValidatorIndex((validator_index + 1) % len(state.validators))
|
|
processed_count += 1
|
|
|
|
return withdrawals, withdrawal_index, processed_count
|
|
</spec>
|
|
|
|
- name: get_voting_source#phase0
|
|
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#phase0
|
|
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: get_weight#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="get_weight" fork="gloas" hash="10430b82">
|
|
def get_weight(
|
|
store: Store,
|
|
# [Modified in Gloas:EIP7732]
|
|
node: ForkChoiceNode,
|
|
) -> Gwei:
|
|
if node.payload_status == PAYLOAD_STATUS_PENDING or store.blocks[
|
|
node.root
|
|
].slot + 1 != get_current_slot(store):
|
|
state = store.checkpoint_states[store.justified_checkpoint]
|
|
attestation_score = get_attestation_score(store, node, state)
|
|
if not should_apply_proposer_boost(store):
|
|
# Return only attestation score if
|
|
# proposer boost should not apply
|
|
return attestation_score
|
|
|
|
# Calculate proposer score if `proposer_boost_root` is set
|
|
proposer_score = Gwei(0)
|
|
|
|
# `proposer_boost_root` is treated as a vote for the
|
|
# proposer's block in the current slot. Proposer boost
|
|
# is applied accordingly to all ancestors
|
|
message = LatestMessage(
|
|
slot=get_current_slot(store),
|
|
root=store.proposer_boost_root,
|
|
payload_present=False,
|
|
)
|
|
if is_supporting_vote(store, node, message):
|
|
proposer_score = get_proposer_score(store)
|
|
|
|
return attestation_score + proposer_score
|
|
else:
|
|
return Gwei(0)
|
|
</spec>
|
|
|
|
- name: has_compounding_withdrawal_credential#electra
|
|
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#capella
|
|
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#electra
|
|
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#altair
|
|
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#phase0
|
|
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#phase0
|
|
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#altair
|
|
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#fulu
|
|
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_builder_exit#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="initiate_builder_exit" fork="gloas" hash="3da938d5">
|
|
def initiate_builder_exit(state: BeaconState, builder_index: BuilderIndex) -> None:
|
|
"""
|
|
Initiate the exit of the builder with index ``index``.
|
|
"""
|
|
# Return if builder already initiated exit
|
|
builder = state.builders[builder_index]
|
|
if builder.withdrawable_epoch != FAR_FUTURE_EPOCH:
|
|
return
|
|
|
|
# Set builder exit epoch
|
|
builder.withdrawable_epoch = get_current_epoch(state) + MIN_BUILDER_WITHDRAWABILITY_DELAY
|
|
</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#phase0
|
|
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_builder#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_active_builder" fork="gloas" hash="1a599fb2">
|
|
def is_active_builder(state: BeaconState, builder_index: BuilderIndex) -> bool:
|
|
"""
|
|
Check if the builder at ``builder_index`` is active for the given ``state``.
|
|
"""
|
|
builder = state.builders[builder_index]
|
|
return (
|
|
# Placement in builder list is finalized
|
|
builder.deposit_epoch < state.finalized_checkpoint.epoch
|
|
# Has not initiated exit
|
|
and builder.withdrawable_epoch == FAR_FUTURE_EPOCH
|
|
)
|
|
</spec>
|
|
|
|
- name: is_active_validator#phase0
|
|
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#phase0
|
|
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#altair
|
|
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_attestation_same_slot#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_attestation_same_slot" fork="gloas" hash="0a194ce7">
|
|
def is_attestation_same_slot(state: BeaconState, data: AttestationData) -> bool:
|
|
"""
|
|
Check if the attestation is for the block proposed at the attestation slot.
|
|
"""
|
|
if data.slot == 0:
|
|
return True
|
|
|
|
blockroot = data.beacon_block_root
|
|
slot_blockroot = get_block_root_at_slot(state, data.slot)
|
|
prev_blockroot = get_block_root_at_slot(state, Slot(data.slot - 1))
|
|
|
|
return blockroot == slot_blockroot and blockroot != prev_blockroot
|
|
</spec>
|
|
|
|
- name: is_better_update#altair
|
|
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_builder_index#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_builder_index" fork="gloas" hash="2fbd46e9">
|
|
def is_builder_index(validator_index: ValidatorIndex) -> bool:
|
|
return (validator_index & BUILDER_INDEX_FLAG) != 0
|
|
</spec>
|
|
|
|
- name: is_builder_withdrawal_credential#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_builder_withdrawal_credential" fork="gloas" hash="2ae5294b">
|
|
def is_builder_withdrawal_credential(withdrawal_credentials: Bytes32) -> bool:
|
|
return withdrawal_credentials[:1] == BUILDER_WITHDRAWAL_PREFIX
|
|
</spec>
|
|
|
|
- name: is_candidate_block#phase0
|
|
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#electra
|
|
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#phase0
|
|
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_eligible_for_partial_withdrawals#electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_eligible_for_partial_withdrawals" fork="electra" hash="c8d29720">
|
|
def is_eligible_for_partial_withdrawals(validator: Validator, balance: Gwei) -> bool:
|
|
"""
|
|
Check if ``validator`` can process a pending partial withdrawal.
|
|
"""
|
|
has_sufficient_effective_balance = validator.effective_balance >= MIN_ACTIVATION_BALANCE
|
|
has_excess_balance = balance > MIN_ACTIVATION_BALANCE
|
|
return (
|
|
validator.exit_epoch == FAR_FUTURE_EPOCH
|
|
and has_sufficient_effective_balance
|
|
and has_excess_balance
|
|
)
|
|
</spec>
|
|
|
|
- name: is_execution_block#bellatrix
|
|
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#bellatrix
|
|
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#phase0
|
|
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#altair
|
|
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#phase0
|
|
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#phase0
|
|
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_late#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_head_late" fork="gloas" hash="dd4c9308">
|
|
def is_head_late(store: Store, head_root: Root) -> bool:
|
|
return not store.block_timeliness[head_root][ATTESTATION_TIMELINESS_INDEX]
|
|
</spec>
|
|
|
|
- name: is_head_weak#phase0
|
|
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_head_weak#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_head_weak" fork="gloas" hash="a763a3db">
|
|
def is_head_weak(store: Store, head_root: Root) -> bool:
|
|
# Calculate weight threshold for weak head
|
|
justified_state = store.checkpoint_states[store.justified_checkpoint]
|
|
reorg_threshold = calculate_committee_fraction(justified_state, REORG_HEAD_WEIGHT_THRESHOLD)
|
|
|
|
# Compute head weight including equivocations
|
|
head_state = store.block_states[head_root]
|
|
head_block = store.blocks[head_root]
|
|
epoch = compute_epoch_at_slot(head_block.slot)
|
|
head_node = ForkChoiceNode(root=head_root, payload_status=PAYLOAD_STATUS_PENDING)
|
|
head_weight = get_attestation_score(store, head_node, justified_state)
|
|
for index in range(get_committee_count_per_slot(head_state, epoch)):
|
|
committee = get_beacon_committee(head_state, head_block.slot, CommitteeIndex(index))
|
|
head_weight += Gwei(
|
|
sum(
|
|
justified_state.validators[i].effective_balance
|
|
for i in committee
|
|
if i in store.equivocating_indices
|
|
)
|
|
)
|
|
|
|
return head_weight < reorg_threshold
|
|
</spec>
|
|
|
|
- name: is_in_inactivity_leak#phase0
|
|
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#bellatrix
|
|
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#bellatrix
|
|
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#altair
|
|
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#bellatrix
|
|
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#bellatrix
|
|
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_block_full#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_parent_block_full" fork="gloas" hash="b59640c9">
|
|
def is_parent_block_full(state: BeaconState) -> bool:
|
|
return state.latest_execution_payload_bid.block_hash == state.latest_block_hash
|
|
</spec>
|
|
|
|
- name: is_parent_node_full#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_parent_node_full" fork="gloas" hash="4fc2d12c">
|
|
def is_parent_node_full(store: Store, block: BeaconBlock) -> bool:
|
|
return get_parent_payload_status(store, block) == PAYLOAD_STATUS_FULL
|
|
</spec>
|
|
|
|
- name: is_parent_strong#phase0
|
|
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_parent_strong#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_parent_strong" fork="gloas" hash="37089462">
|
|
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)
|
|
block = store.blocks[root]
|
|
parent_payload_status = get_parent_payload_status(store, block)
|
|
parent_node = ForkChoiceNode(root=block.parent_root, payload_status=parent_payload_status)
|
|
parent_weight = get_attestation_score(store, parent_node, justified_state)
|
|
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_payload_timely#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_payload_timely" fork="gloas" hash="ae36e125">
|
|
def is_payload_timely(store: Store, root: Root) -> bool:
|
|
"""
|
|
Return whether the execution payload for the beacon block with root ``root``
|
|
was voted as present by the PTC, and was locally determined to be available.
|
|
"""
|
|
# The beacon block root must be known
|
|
assert root in store.ptc_vote
|
|
|
|
# If the payload is not locally available, the payload
|
|
# is not considered available regardless of the PTC vote
|
|
if root not in store.execution_payload_states:
|
|
return False
|
|
|
|
return sum(store.ptc_vote[root]) > PAYLOAD_TIMELY_THRESHOLD
|
|
</spec>
|
|
|
|
- name: is_proposer#phase0
|
|
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_proposer_equivocation#phase0
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_proposer_equivocation" fork="phase0" hash="8e90bb33">
|
|
def is_proposer_equivocation(store: Store, root: Root) -> bool:
|
|
block = store.blocks[root]
|
|
proposer_index = block.proposer_index
|
|
slot = block.slot
|
|
# roots from the same slot and proposer
|
|
matching_roots = [
|
|
root
|
|
for root, block in store.blocks.items()
|
|
if (block.proposer_index == proposer_index and block.slot == slot)
|
|
]
|
|
return len(matching_roots) > 1
|
|
</spec>
|
|
|
|
- name: is_proposing_on_time#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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_supporting_vote#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_supporting_vote" fork="gloas" hash="87dd2faf">
|
|
def is_supporting_vote(store: Store, node: ForkChoiceNode, message: LatestMessage) -> bool:
|
|
"""
|
|
Returns whether a vote for ``message.root`` supports the chain containing the beacon block ``node.root`` with the
|
|
payload contents indicated by ``node.payload_status`` as head during slot ``node.slot``.
|
|
"""
|
|
block = store.blocks[node.root]
|
|
if node.root == message.root:
|
|
if node.payload_status == PAYLOAD_STATUS_PENDING:
|
|
return True
|
|
if message.slot <= block.slot:
|
|
return False
|
|
if message.payload_present:
|
|
return node.payload_status == PAYLOAD_STATUS_FULL
|
|
else:
|
|
return node.payload_status == PAYLOAD_STATUS_EMPTY
|
|
else:
|
|
ancestor = get_ancestor(store, message.root, block.slot)
|
|
return node.root == ancestor.root and (
|
|
node.payload_status == PAYLOAD_STATUS_PENDING
|
|
or node.payload_status == ancestor.payload_status
|
|
)
|
|
</spec>
|
|
|
|
- name: is_sync_committee_aggregator#altair
|
|
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#altair
|
|
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#electra
|
|
sources:
|
|
- file: beacon-chain/core/electra/deposits.go
|
|
search: func IsValidDepositSignature(
|
|
- file: beacon-chain/core/helpers/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#phase0
|
|
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#phase0
|
|
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_indexed_payload_attestation#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_valid_indexed_payload_attestation" fork="gloas" hash="d76e0f89">
|
|
def is_valid_indexed_payload_attestation(
|
|
state: BeaconState, attestation: IndexedPayloadAttestation
|
|
) -> bool:
|
|
"""
|
|
Check if ``attestation`` is non-empty, has sorted indices, and has
|
|
a valid aggregate signature.
|
|
"""
|
|
# Verify indices are non-empty and sorted
|
|
indices = attestation.attesting_indices
|
|
if len(indices) == 0 or not indices == sorted(indices):
|
|
return False
|
|
|
|
# Verify aggregate signature
|
|
pubkeys = [state.validators[i].pubkey for i in indices]
|
|
domain = get_domain(state, DOMAIN_PTC_ATTESTER, compute_epoch_at_slot(attestation.data.slot))
|
|
signing_root = compute_signing_root(attestation.data, domain)
|
|
return bls.FastAggregateVerify(pubkeys, signing_root, 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#phase0
|
|
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#altair
|
|
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_proposal_slot#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="is_valid_proposal_slot" fork="gloas" hash="55b8762f">
|
|
def is_valid_proposal_slot(state: BeaconState, preferences: ProposerPreferences) -> bool:
|
|
"""
|
|
Check if the validator is the proposer for the given slot in the next epoch.
|
|
"""
|
|
index = SLOTS_PER_EPOCH + preferences.proposal_slot % SLOTS_PER_EPOCH
|
|
return state.proposer_lookahead[index] == preferences.validator_index
|
|
</spec>
|
|
|
|
- name: is_valid_switch_to_compounding_request#electra
|
|
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#bellatrix
|
|
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#deneb
|
|
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#bellatrix
|
|
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#phase0
|
|
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#phase0
|
|
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#electra
|
|
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: notify_ptc_messages#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="notify_ptc_messages" fork="gloas" hash="f28f190f">
|
|
def notify_ptc_messages(
|
|
store: Store, state: BeaconState, payload_attestations: Sequence[PayloadAttestation]
|
|
) -> None:
|
|
"""
|
|
Extracts a list of ``PayloadAttestationMessage`` from ``payload_attestations`` and updates the store with them
|
|
These Payload attestations are assumed to be in the beacon block hence signature verification is not needed
|
|
"""
|
|
if state.slot == 0:
|
|
return
|
|
for payload_attestation in payload_attestations:
|
|
indexed_payload_attestation = get_indexed_payload_attestation(state, payload_attestation)
|
|
for idx in indexed_payload_attestation.attesting_indices:
|
|
on_payload_attestation_message(
|
|
store,
|
|
PayloadAttestationMessage(
|
|
validator_index=idx,
|
|
data=payload_attestation.data,
|
|
signature=BLSSignature(),
|
|
),
|
|
is_from_block=True,
|
|
)
|
|
</spec>
|
|
|
|
- name: on_attestation#phase0
|
|
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#phase0
|
|
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_block#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="on_block" fork="gloas" hash="0a135554">
|
|
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
|
|
|
|
# Check if this blocks builds on empty or full parent block
|
|
parent_block = store.blocks[block.parent_root]
|
|
bid = block.body.signed_execution_payload_bid.message
|
|
parent_bid = parent_block.body.signed_execution_payload_bid.message
|
|
# Make a copy of the state to avoid mutability issues
|
|
if is_parent_node_full(store, block):
|
|
assert block.parent_root in store.execution_payload_states
|
|
state = copy(store.execution_payload_states[block.parent_root])
|
|
else:
|
|
assert bid.parent_block_hash == parent_bid.parent_block_hash
|
|
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.
|
|
current_slot = get_current_slot(store)
|
|
assert current_slot >= 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
|
|
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
|
|
# Add a new PTC voting for this block to the store
|
|
store.ptc_vote[block_root] = [False] * PTC_SIZE
|
|
|
|
# Notify the store about the payload_attestations in the block
|
|
notify_ptc_messages(store, state, block.body.payload_attestations)
|
|
|
|
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_execution_payload#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="on_execution_payload" fork="gloas" hash="49b534de">
|
|
def on_execution_payload(store: Store, signed_envelope: SignedExecutionPayloadEnvelope) -> None:
|
|
"""
|
|
Run ``on_execution_payload`` upon receiving a new execution payload.
|
|
"""
|
|
envelope = signed_envelope.message
|
|
# The corresponding beacon block root needs to be known
|
|
assert envelope.beacon_block_root in store.block_states
|
|
|
|
# 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(envelope.beacon_block_root)
|
|
|
|
# Make a copy of the state to avoid mutability issues
|
|
state = copy(store.block_states[envelope.beacon_block_root])
|
|
|
|
# Process the execution payload
|
|
process_execution_payload(state, signed_envelope, EXECUTION_ENGINE)
|
|
|
|
# Add new state for this payload to the store
|
|
store.execution_payload_states[envelope.beacon_block_root] = state
|
|
</spec>
|
|
|
|
- name: on_payload_attestation_message#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="on_payload_attestation_message" fork="gloas" hash="e801d849">
|
|
def on_payload_attestation_message(
|
|
store: Store, ptc_message: PayloadAttestationMessage, is_from_block: bool = False
|
|
) -> None:
|
|
"""
|
|
Run ``on_payload_attestation_message`` upon receiving a new ``ptc_message`` from
|
|
either within a block or directly on the wire.
|
|
"""
|
|
# The beacon block root must be known
|
|
data = ptc_message.data
|
|
# PTC attestation must be for a known block. If block is unknown, delay consideration until the block is found
|
|
state = store.block_states[data.beacon_block_root]
|
|
ptc = get_ptc(state, data.slot)
|
|
# PTC votes can only change the vote for their assigned beacon block, return early otherwise
|
|
if data.slot != state.slot:
|
|
return
|
|
# Check that the attester is from the PTC
|
|
assert ptc_message.validator_index in ptc
|
|
|
|
# Verify the signature and check that its for the current slot if it is coming from the wire
|
|
if not is_from_block:
|
|
# Check that the attestation is for the current slot
|
|
assert data.slot == get_current_slot(store)
|
|
# Verify the signature
|
|
assert is_valid_indexed_payload_attestation(
|
|
state,
|
|
IndexedPayloadAttestation(
|
|
attesting_indices=[ptc_message.validator_index],
|
|
data=data,
|
|
signature=ptc_message.signature,
|
|
),
|
|
)
|
|
# Update the ptc vote for the block
|
|
ptc_index = ptc.index(ptc_message.validator_index)
|
|
ptc_vote = store.ptc_vote[data.beacon_block_root]
|
|
ptc_vote[ptc_index] = data.payload_present
|
|
</spec>
|
|
|
|
- name: on_tick#phase0
|
|
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#phase0
|
|
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="bdb15c3f">
|
|
def prepare_execution_payload(
|
|
state: BeaconState,
|
|
safe_block_hash: Hash32,
|
|
finalized_block_hash: Hash32,
|
|
suggested_fee_recipient: ExecutionAddress,
|
|
execution_engine: ExecutionEngine,
|
|
# [Modified in Capella]
|
|
# Removed `pow_chain`
|
|
) -> 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
|
|
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=get_expected_withdrawals(state).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="c617aa1b">
|
|
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
|
|
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=get_expected_withdrawals(state).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="c617aa1b">
|
|
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
|
|
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=get_expected_withdrawals(state).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: 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_attestation#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_attestation" fork="gloas" hash="2258b5bb">
|
|
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 Gloas:EIP7732]
|
|
assert data.index < 2
|
|
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))
|
|
|
|
# [Modified in Gloas:EIP7732]
|
|
if data.target.epoch == get_current_epoch(state):
|
|
current_epoch_target = True
|
|
epoch_participation = state.current_epoch_participation
|
|
payment = state.builder_pending_payments[SLOTS_PER_EPOCH + data.slot % SLOTS_PER_EPOCH]
|
|
else:
|
|
current_epoch_target = False
|
|
epoch_participation = state.previous_epoch_participation
|
|
payment = state.builder_pending_payments[data.slot % SLOTS_PER_EPOCH]
|
|
|
|
proposer_reward_numerator = 0
|
|
for index in get_attesting_indices(state, attestation):
|
|
# [New in Gloas:EIP7732]
|
|
# For same-slot attestations, check if we are setting any new flags.
|
|
# If we are, this validator has not contributed to this slot's quorum yet.
|
|
will_set_new_flag = False
|
|
|
|
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
|
|
# [New in Gloas:EIP7732]
|
|
will_set_new_flag = True
|
|
|
|
# [New in Gloas:EIP7732]
|
|
# Add weight for same-slot attestations when any new flag is set.
|
|
# This ensures each validator contributes exactly once per slot.
|
|
if (
|
|
will_set_new_flag
|
|
and is_attestation_same_slot(state, data)
|
|
and payment.withdrawal.amount > 0
|
|
):
|
|
payment.weight += state.validators[index].effective_balance
|
|
|
|
# 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)
|
|
|
|
# [New in Gloas:EIP7732]
|
|
# Update builder payment weight
|
|
if current_epoch_target:
|
|
state.builder_pending_payments[SLOTS_PER_EPOCH + data.slot % SLOTS_PER_EPOCH] = payment
|
|
else:
|
|
state.builder_pending_payments[data.slot % SLOTS_PER_EPOCH] = payment
|
|
</spec>
|
|
|
|
- name: process_attester_slashing#phase0
|
|
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#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_block" fork="gloas" hash="cc0f05ee">
|
|
def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
|
process_block_header(state, block)
|
|
# [Modified in Gloas:EIP7732]
|
|
process_withdrawals(state)
|
|
# [Modified in Gloas:EIP7732]
|
|
# Removed `process_execution_payload`
|
|
# [New in Gloas:EIP7732]
|
|
process_execution_payload_bid(state, block)
|
|
process_randao(state, block.body)
|
|
process_eth1_data(state, block.body)
|
|
# [Modified in Gloas:EIP7732]
|
|
process_operations(state, block.body)
|
|
process_sync_aggregate(state, block.body.sync_aggregate)
|
|
</spec>
|
|
|
|
- name: process_block_header#phase0
|
|
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#capella
|
|
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_builder_pending_payments#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_builder_pending_payments" fork="gloas" hash="10da48dd">
|
|
def process_builder_pending_payments(state: BeaconState) -> None:
|
|
"""
|
|
Processes the builder pending payments from the previous epoch.
|
|
"""
|
|
quorum = get_builder_payment_quorum_threshold(state)
|
|
for payment in state.builder_pending_payments[:SLOTS_PER_EPOCH]:
|
|
if payment.weight >= quorum:
|
|
state.builder_pending_withdrawals.append(payment.withdrawal)
|
|
|
|
old_payments = state.builder_pending_payments[SLOTS_PER_EPOCH:]
|
|
new_payments = [BuilderPendingPayment() for _ in range(SLOTS_PER_EPOCH)]
|
|
state.builder_pending_payments = old_payments + new_payments
|
|
</spec>
|
|
|
|
- name: process_consolidation_request#electra
|
|
sources:
|
|
- file: beacon-chain/core/requests/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#electra
|
|
sources:
|
|
- file: beacon-chain/core/requests/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_deposit_request#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_deposit_request" fork="gloas" hash="3c6b0310">
|
|
def process_deposit_request(state: BeaconState, deposit_request: DepositRequest) -> None:
|
|
# [New in Gloas:EIP7732]
|
|
builder_pubkeys = [b.pubkey for b in state.builders]
|
|
validator_pubkeys = [v.pubkey for v in state.validators]
|
|
|
|
# [New in Gloas:EIP7732]
|
|
# Regardless of the withdrawal credentials prefix, if a builder/validator
|
|
# already exists with this pubkey, apply the deposit to their balance
|
|
is_builder = deposit_request.pubkey in builder_pubkeys
|
|
is_validator = deposit_request.pubkey in validator_pubkeys
|
|
is_builder_prefix = is_builder_withdrawal_credential(deposit_request.withdrawal_credentials)
|
|
if is_builder or (is_builder_prefix and not is_validator):
|
|
# Apply builder deposits immediately
|
|
apply_deposit_for_builder(
|
|
state,
|
|
deposit_request.pubkey,
|
|
deposit_request.withdrawal_credentials,
|
|
deposit_request.amount,
|
|
deposit_request.signature,
|
|
state.slot,
|
|
)
|
|
return
|
|
|
|
# Add validator deposits to the queue
|
|
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#capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_epoch" fork="capella" hash="c1257690">
|
|
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_effective_balance_updates(state)
|
|
process_slashings_reset(state)
|
|
process_randao_mixes_reset(state)
|
|
# [Modified in Capella]
|
|
process_historical_summaries_update(state)
|
|
process_participation_flag_updates(state)
|
|
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_epoch#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_epoch" fork="gloas" hash="393b69ef">
|
|
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)
|
|
# [New in Gloas:EIP7732]
|
|
process_builder_pending_payments(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)
|
|
process_proposer_lookahead(state)
|
|
</spec>
|
|
|
|
- name: process_eth1_data#phase0
|
|
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#phase0
|
|
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_execution_payload#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_execution_payload" fork="gloas" hash="36bd3af3">
|
|
def process_execution_payload(
|
|
state: BeaconState,
|
|
# [Modified in Gloas:EIP7732]
|
|
# Removed `body`
|
|
# [New in Gloas:EIP7732]
|
|
signed_envelope: SignedExecutionPayloadEnvelope,
|
|
execution_engine: ExecutionEngine,
|
|
# [New in Gloas:EIP7732]
|
|
verify: bool = True,
|
|
) -> None:
|
|
envelope = signed_envelope.message
|
|
payload = envelope.payload
|
|
|
|
# Verify signature
|
|
if verify:
|
|
assert verify_execution_payload_envelope_signature(state, signed_envelope)
|
|
|
|
# Cache latest block header state root
|
|
previous_state_root = hash_tree_root(state)
|
|
if state.latest_block_header.state_root == Root():
|
|
state.latest_block_header.state_root = previous_state_root
|
|
|
|
# Verify consistency with the beacon block
|
|
assert envelope.beacon_block_root == hash_tree_root(state.latest_block_header)
|
|
assert envelope.slot == state.slot
|
|
|
|
# Verify consistency with the committed bid
|
|
committed_bid = state.latest_execution_payload_bid
|
|
assert envelope.builder_index == committed_bid.builder_index
|
|
assert committed_bid.prev_randao == payload.prev_randao
|
|
|
|
# Verify consistency with expected withdrawals
|
|
assert hash_tree_root(payload.withdrawals) == hash_tree_root(state.payload_expected_withdrawals)
|
|
|
|
# Verify the gas_limit
|
|
assert committed_bid.gas_limit == payload.gas_limit
|
|
# Verify the block hash
|
|
assert committed_bid.block_hash == payload.block_hash
|
|
# Verify consistency of the parent hash with respect to the previous execution payload
|
|
assert payload.parent_hash == state.latest_block_hash
|
|
# Verify timestamp
|
|
assert payload.timestamp == compute_time_at_slot(state, state.slot)
|
|
# Verify the execution payload is valid
|
|
versioned_hashes = [
|
|
kzg_commitment_to_versioned_hash(commitment)
|
|
# [Modified in Gloas:EIP7732]
|
|
for commitment in committed_bid.blob_kzg_commitments
|
|
]
|
|
requests = envelope.execution_requests
|
|
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=requests,
|
|
)
|
|
)
|
|
|
|
def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None:
|
|
for operation in operations:
|
|
fn(state, operation)
|
|
|
|
for_ops(requests.deposits, process_deposit_request)
|
|
for_ops(requests.withdrawals, process_withdrawal_request)
|
|
for_ops(requests.consolidations, process_consolidation_request)
|
|
|
|
# Queue the builder payment
|
|
payment = state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH]
|
|
amount = payment.withdrawal.amount
|
|
if amount > 0:
|
|
state.builder_pending_withdrawals.append(payment.withdrawal)
|
|
state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] = (
|
|
BuilderPendingPayment()
|
|
)
|
|
|
|
# Cache the execution payload hash
|
|
state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1
|
|
state.latest_block_hash = payload.block_hash
|
|
|
|
# Verify the state root
|
|
if verify:
|
|
assert envelope.state_root == hash_tree_root(state)
|
|
</spec>
|
|
|
|
- name: process_execution_payload_bid#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_execution_payload_bid" fork="gloas" hash="823c9f3a">
|
|
def process_execution_payload_bid(state: BeaconState, block: BeaconBlock) -> None:
|
|
signed_bid = block.body.signed_execution_payload_bid
|
|
bid = signed_bid.message
|
|
builder_index = bid.builder_index
|
|
amount = bid.value
|
|
|
|
# For self-builds, amount must be zero regardless of withdrawal credential prefix
|
|
if builder_index == BUILDER_INDEX_SELF_BUILD:
|
|
assert amount == 0
|
|
assert signed_bid.signature == bls.G2_POINT_AT_INFINITY
|
|
else:
|
|
# Verify that the builder is active
|
|
assert is_active_builder(state, builder_index)
|
|
# Verify that the builder has funds to cover the bid
|
|
assert can_builder_cover_bid(state, builder_index, amount)
|
|
# Verify that the bid signature is valid
|
|
assert verify_execution_payload_bid_signature(state, signed_bid)
|
|
|
|
# Verify commitments are under limit
|
|
assert (
|
|
len(bid.blob_kzg_commitments)
|
|
<= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block
|
|
)
|
|
|
|
# Verify that the bid is for the current slot
|
|
assert bid.slot == block.slot
|
|
# Verify that the bid is for the right parent block
|
|
assert bid.parent_block_hash == state.latest_block_hash
|
|
assert bid.parent_block_root == block.parent_root
|
|
assert bid.prev_randao == get_randao_mix(state, get_current_epoch(state))
|
|
|
|
# Record the pending payment if there is some payment
|
|
if amount > 0:
|
|
pending_payment = BuilderPendingPayment(
|
|
weight=0,
|
|
withdrawal=BuilderPendingWithdrawal(
|
|
fee_recipient=bid.fee_recipient,
|
|
amount=amount,
|
|
builder_index=builder_index,
|
|
),
|
|
)
|
|
state.builder_pending_payments[SLOTS_PER_EPOCH + bid.slot % SLOTS_PER_EPOCH] = (
|
|
pending_payment
|
|
)
|
|
|
|
# Cache the signed execution payload bid
|
|
state.latest_execution_payload_bid = bid
|
|
</spec>
|
|
|
|
- name: process_historical_roots_update#phase0
|
|
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#capella
|
|
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#altair
|
|
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#altair
|
|
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#altair
|
|
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#altair
|
|
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#altair
|
|
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/transition/electra.go
|
|
search: func electraOperations(
|
|
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_operations#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_operations" fork="gloas" hash="05a7a4ea">
|
|
def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
|
# 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)
|
|
|
|
# [Modified in Gloas:EIP7732]
|
|
for_ops(body.proposer_slashings, process_proposer_slashing)
|
|
for_ops(body.attester_slashings, process_attester_slashing)
|
|
# [Modified in Gloas:EIP7732]
|
|
for_ops(body.attestations, process_attestation)
|
|
for_ops(body.deposits, process_deposit)
|
|
# [Modified in Gloas:EIP7732]
|
|
for_ops(body.voluntary_exits, process_voluntary_exit)
|
|
for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
|
|
# [Modified in Gloas:EIP7732]
|
|
# Removed `process_deposit_request`
|
|
# [Modified in Gloas:EIP7732]
|
|
# Removed `process_withdrawal_request`
|
|
# [Modified in Gloas:EIP7732]
|
|
# Removed `process_consolidation_request`
|
|
# [New in Gloas:EIP7732]
|
|
for_ops(body.payload_attestations, process_payload_attestation)
|
|
</spec>
|
|
|
|
- name: process_participation_flag_updates#altair
|
|
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#phase0
|
|
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_payload_attestation#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_payload_attestation" fork="gloas" hash="f46bf0b0">
|
|
def process_payload_attestation(
|
|
state: BeaconState, payload_attestation: PayloadAttestation
|
|
) -> None:
|
|
data = payload_attestation.data
|
|
|
|
# Check that the attestation is for the parent beacon block
|
|
assert data.beacon_block_root == state.latest_block_header.parent_root
|
|
# Check that the attestation is for the previous slot
|
|
assert data.slot + 1 == state.slot
|
|
# Verify signature
|
|
indexed_payload_attestation = get_indexed_payload_attestation(state, payload_attestation)
|
|
assert is_valid_indexed_payload_attestation(state, indexed_payload_attestation)
|
|
</spec>
|
|
|
|
- name: process_pending_consolidations#electra
|
|
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#electra
|
|
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#fulu
|
|
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#phase0
|
|
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_proposer_slashing#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_proposer_slashing" fork="gloas" hash="10bda1c5">
|
|
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)
|
|
|
|
# [New in Gloas:EIP7732]
|
|
# Remove the BuilderPendingPayment corresponding to
|
|
# this proposal if it is still in the 2-epoch window.
|
|
slot = header_1.slot
|
|
proposal_epoch = compute_epoch_at_slot(slot)
|
|
if proposal_epoch == get_current_epoch(state):
|
|
payment_index = SLOTS_PER_EPOCH + slot % SLOTS_PER_EPOCH
|
|
state.builder_pending_payments[payment_index] = BuilderPendingPayment()
|
|
elif proposal_epoch == get_previous_epoch(state):
|
|
payment_index = slot % SLOTS_PER_EPOCH
|
|
state.builder_pending_payments[payment_index] = BuilderPendingPayment()
|
|
|
|
slash_validator(state, header_1.proposer_index)
|
|
</spec>
|
|
|
|
- name: process_randao#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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_slot#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_slot" fork="gloas" hash="976fd16f">
|
|
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
|
|
# [New in Gloas:EIP7732]
|
|
# Unset the next payload availability
|
|
state.execution_payload_availability[(state.slot + 1) % SLOTS_PER_HISTORICAL_ROOT] = 0b0
|
|
</spec>
|
|
|
|
- name: process_slots#phase0
|
|
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#altair
|
|
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#altair
|
|
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#altair
|
|
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_voluntary_exit#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_voluntary_exit" fork="gloas" hash="2a22a698">
|
|
def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
|
|
voluntary_exit = signed_voluntary_exit.message
|
|
domain = compute_domain(
|
|
DOMAIN_VOLUNTARY_EXIT, CAPELLA_FORK_VERSION, state.genesis_validators_root
|
|
)
|
|
signing_root = compute_signing_root(voluntary_exit, domain)
|
|
|
|
# Exits must specify an epoch when they become valid; they are not valid before then
|
|
assert get_current_epoch(state) >= voluntary_exit.epoch
|
|
|
|
# [New in Gloas:EIP7732]
|
|
if is_builder_index(voluntary_exit.validator_index):
|
|
builder_index = convert_validator_index_to_builder_index(voluntary_exit.validator_index)
|
|
# Verify the builder is active
|
|
assert is_active_builder(state, builder_index)
|
|
# Only exit builder if it has no pending withdrawals in the queue
|
|
assert get_pending_balance_to_withdraw_for_builder(state, builder_index) == 0
|
|
# Verify signature
|
|
pubkey = state.builders[builder_index].pubkey
|
|
assert bls.Verify(pubkey, signing_root, signed_voluntary_exit.signature)
|
|
# Initiate exit
|
|
initiate_builder_exit(state, builder_index)
|
|
return
|
|
|
|
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
|
|
# Verify the validator has been active long enough
|
|
assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
|
|
# 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
|
|
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#electra
|
|
sources:
|
|
- file: beacon-chain/core/requests/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="47327ef6">
|
|
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
|
# Get expected withdrawals
|
|
expected = get_expected_withdrawals(state)
|
|
assert payload.withdrawals == expected.withdrawals
|
|
|
|
# Apply expected withdrawals
|
|
apply_withdrawals(state, expected.withdrawals)
|
|
|
|
# Update withdrawals fields in the state
|
|
update_next_withdrawal_index(state, expected.withdrawals)
|
|
update_next_withdrawal_validator_index(state, expected.withdrawals)
|
|
</spec>
|
|
|
|
- name: process_withdrawals#electra
|
|
sources:
|
|
- file: beacon-chain/core/blocks/withdrawals.go
|
|
search: func ProcessWithdrawals(
|
|
spec: |
|
|
<spec fn="process_withdrawals" fork="electra" hash="24a50daa">
|
|
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
|
# Get expected withdrawals
|
|
expected = get_expected_withdrawals(state)
|
|
assert payload.withdrawals == expected.withdrawals
|
|
|
|
# Apply expected withdrawals
|
|
apply_withdrawals(state, expected.withdrawals)
|
|
|
|
# Update withdrawals fields in the state
|
|
update_next_withdrawal_index(state, expected.withdrawals)
|
|
# [New in Electra:EIP7251]
|
|
update_pending_partial_withdrawals(state, expected.processed_partial_withdrawals_count)
|
|
update_next_withdrawal_validator_index(state, expected.withdrawals)
|
|
</spec>
|
|
|
|
- name: process_withdrawals#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="process_withdrawals" fork="gloas" hash="16d9ad2a">
|
|
def process_withdrawals(
|
|
state: BeaconState,
|
|
# [Modified in Gloas:EIP7732]
|
|
# Removed `payload`
|
|
) -> None:
|
|
# [New in Gloas:EIP7732]
|
|
# Return early if the parent block is empty
|
|
if not is_parent_block_full(state):
|
|
return
|
|
|
|
# Get expected withdrawals
|
|
expected = get_expected_withdrawals(state)
|
|
|
|
# Apply expected withdrawals
|
|
apply_withdrawals(state, expected.withdrawals)
|
|
|
|
# Update withdrawals fields in the state
|
|
update_next_withdrawal_index(state, expected.withdrawals)
|
|
# [New in Gloas:EIP7732]
|
|
update_payload_expected_withdrawals(state, expected.withdrawals)
|
|
# [New in Gloas:EIP7732]
|
|
update_builder_pending_withdrawals(state, expected.processed_builder_withdrawals_count)
|
|
update_pending_partial_withdrawals(state, expected.processed_partial_withdrawals_count)
|
|
# [New in Gloas:EIP7732]
|
|
update_next_withdrawal_builder_index(state, expected.processed_builders_sweep_count)
|
|
update_next_withdrawal_validator_index(state, expected.withdrawals)
|
|
</spec>
|
|
|
|
- name: queue_excess_active_balance#electra
|
|
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: record_block_timeliness#phase0
|
|
sources: []
|
|
spec: |
|
|
<spec fn="record_block_timeliness" fork="phase0" hash="5c584f56">
|
|
def record_block_timeliness(store: Store, root: Root) -> None:
|
|
block = store.blocks[root]
|
|
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)
|
|
attestation_threshold_ms = get_attestation_due_ms(epoch)
|
|
is_before_attesting_interval = time_into_slot_ms < attestation_threshold_ms
|
|
is_timely = get_current_slot(store) == block.slot and is_before_attesting_interval
|
|
store.block_timeliness[root] = is_timely
|
|
</spec>
|
|
|
|
- name: record_block_timeliness#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="record_block_timeliness" fork="gloas" hash="f4bc2c5a">
|
|
def record_block_timeliness(store: Store, root: Root) -> None:
|
|
block = store.blocks[root]
|
|
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)
|
|
attestation_threshold_ms = get_attestation_due_ms(epoch)
|
|
# [New in Gloas:EIP7732]
|
|
is_current_slot = get_current_slot(store) == block.slot
|
|
ptc_threshold_ms = get_payload_attestation_due_ms(epoch)
|
|
# [Modified in Gloas:EIP7732]
|
|
store.block_timeliness[root] = [
|
|
is_current_slot and time_into_slot_ms < threshold
|
|
for threshold in [attestation_threshold_ms, ptc_threshold_ms]
|
|
]
|
|
</spec>
|
|
|
|
- name: recover_matrix#fulu
|
|
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#phase0
|
|
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#phase0
|
|
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#altair
|
|
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_apply_proposer_boost#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="should_apply_proposer_boost" fork="gloas" hash="a18e5701">
|
|
def should_apply_proposer_boost(store: Store) -> bool:
|
|
if store.proposer_boost_root == Root():
|
|
return False
|
|
|
|
block = store.blocks[store.proposer_boost_root]
|
|
parent_root = block.parent_root
|
|
parent = store.blocks[parent_root]
|
|
slot = block.slot
|
|
|
|
# Apply proposer boost if `parent` is not from the previous slot
|
|
if parent.slot + 1 < slot:
|
|
return True
|
|
|
|
# Apply proposer boost if `parent` is not weak
|
|
if not is_head_weak(store, parent_root):
|
|
return True
|
|
|
|
# If `parent` is weak and from the previous slot, apply
|
|
# proposer boost if there are no early equivocations
|
|
equivocations = [
|
|
root
|
|
for root, block in store.blocks.items()
|
|
if (
|
|
store.block_timeliness[root][PTC_TIMELINESS_INDEX]
|
|
and block.proposer_index == parent.proposer_index
|
|
and block.slot + 1 == slot
|
|
and root != parent_root
|
|
)
|
|
]
|
|
|
|
return len(equivocations) == 0
|
|
</spec>
|
|
|
|
- name: should_extend_payload#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="should_extend_payload" fork="gloas" hash="cbdd2370">
|
|
def should_extend_payload(store: Store, root: Root) -> bool:
|
|
proposer_root = store.proposer_boost_root
|
|
return (
|
|
is_payload_timely(store, root)
|
|
or proposer_root == Root()
|
|
or store.blocks[proposer_root].parent_root != root
|
|
or is_parent_node_full(store, store.blocks[proposer_root])
|
|
)
|
|
</spec>
|
|
|
|
- name: should_override_forkchoice_update#bellatrix
|
|
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="d2b5fafa">
|
|
def slash_validator(
|
|
state: BeaconState,
|
|
slashed_index: ValidatorIndex,
|
|
whistleblower_index: Optional[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="179ea102">
|
|
def slash_validator(
|
|
state: BeaconState,
|
|
slashed_index: ValidatorIndex,
|
|
whistleblower_index: Optional[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="5964268e">
|
|
def slash_validator(
|
|
state: BeaconState,
|
|
slashed_index: ValidatorIndex,
|
|
whistleblower_index: Optional[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="07e584e2">
|
|
def slash_validator(
|
|
state: BeaconState,
|
|
slashed_index: ValidatorIndex,
|
|
whistleblower_index: Optional[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#phase0
|
|
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#phase0
|
|
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#electra
|
|
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#altair
|
|
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_builder_pending_withdrawals#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="update_builder_pending_withdrawals" fork="gloas" hash="0cbba9bd">
|
|
def update_builder_pending_withdrawals(
|
|
state: BeaconState, processed_builder_withdrawals_count: uint64
|
|
) -> None:
|
|
state.builder_pending_withdrawals = state.builder_pending_withdrawals[
|
|
processed_builder_withdrawals_count:
|
|
]
|
|
</spec>
|
|
|
|
- name: update_checkpoints#phase0
|
|
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#phase0
|
|
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_latest_messages#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="update_latest_messages" fork="gloas" hash="f75dff96">
|
|
def update_latest_messages(
|
|
store: Store, attesting_indices: Sequence[ValidatorIndex], attestation: Attestation
|
|
) -> None:
|
|
slot = attestation.data.slot
|
|
beacon_block_root = attestation.data.beacon_block_root
|
|
payload_present = attestation.data.index == 1
|
|
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 slot > store.latest_messages[i].slot:
|
|
store.latest_messages[i] = LatestMessage(
|
|
slot=slot, root=beacon_block_root, payload_present=payload_present
|
|
)
|
|
</spec>
|
|
|
|
- name: update_next_withdrawal_builder_index#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="update_next_withdrawal_builder_index" fork="gloas" hash="652c72b5">
|
|
def update_next_withdrawal_builder_index(
|
|
state: BeaconState, processed_builders_sweep_count: uint64
|
|
) -> None:
|
|
if len(state.builders) > 0:
|
|
# Update the next builder index to start the next withdrawal sweep
|
|
next_index = state.next_withdrawal_builder_index + processed_builders_sweep_count
|
|
next_builder_index = BuilderIndex(next_index % len(state.builders))
|
|
state.next_withdrawal_builder_index = next_builder_index
|
|
</spec>
|
|
|
|
- name: update_next_withdrawal_index#capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="update_next_withdrawal_index" fork="capella" hash="a9e9baa5">
|
|
def update_next_withdrawal_index(state: BeaconState, withdrawals: Sequence[Withdrawal]) -> None:
|
|
# Update the next withdrawal index if this block contained withdrawals
|
|
if len(withdrawals) != 0:
|
|
latest_withdrawal = withdrawals[-1]
|
|
state.next_withdrawal_index = WithdrawalIndex(latest_withdrawal.index + 1)
|
|
</spec>
|
|
|
|
- name: update_next_withdrawal_validator_index#capella
|
|
sources: []
|
|
spec: |
|
|
<spec fn="update_next_withdrawal_validator_index" fork="capella" hash="4a3ee635">
|
|
def update_next_withdrawal_validator_index(
|
|
state: BeaconState, withdrawals: Sequence[Withdrawal]
|
|
) -> None:
|
|
# Update the next validator index to start the next withdrawal sweep
|
|
if len(withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
|
|
# Next sweep starts after the latest withdrawal's validator index
|
|
next_validator_index = ValidatorIndex(
|
|
(withdrawals[-1].validator_index + 1) % len(state.validators)
|
|
)
|
|
state.next_withdrawal_validator_index = next_validator_index
|
|
else:
|
|
# Advance sweep by the max length of the sweep if there was not a full set of withdrawals
|
|
next_index = state.next_withdrawal_validator_index + MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP
|
|
next_validator_index = ValidatorIndex(next_index % len(state.validators))
|
|
state.next_withdrawal_validator_index = next_validator_index
|
|
</spec>
|
|
|
|
- name: update_payload_expected_withdrawals#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="update_payload_expected_withdrawals" fork="gloas" hash="6a3810b9">
|
|
def update_payload_expected_withdrawals(
|
|
state: BeaconState, withdrawals: Sequence[Withdrawal]
|
|
) -> None:
|
|
state.payload_expected_withdrawals = List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD](withdrawals)
|
|
</spec>
|
|
|
|
- name: update_pending_partial_withdrawals#electra
|
|
sources: []
|
|
spec: |
|
|
<spec fn="update_pending_partial_withdrawals" fork="electra" hash="dcdd6c8f">
|
|
def update_pending_partial_withdrawals(
|
|
state: BeaconState, processed_partial_withdrawals_count: uint64
|
|
) -> None:
|
|
state.pending_partial_withdrawals = state.pending_partial_withdrawals[
|
|
processed_partial_withdrawals_count:
|
|
]
|
|
</spec>
|
|
|
|
- name: update_proposer_boost_root#phase0
|
|
sources: []
|
|
spec: |
|
|
<spec fn="update_proposer_boost_root" fork="phase0" hash="f037a32d">
|
|
def update_proposer_boost_root(store: Store, root: Root) -> None:
|
|
is_first_block = store.proposer_boost_root == Root()
|
|
is_timely = store.block_timeliness[root]
|
|
|
|
# Add proposer score boost if the block is timely, not conflicting with an
|
|
# existing block, with the same the proposer as the canonical chain.
|
|
if is_timely and is_first_block:
|
|
head_state = copy(store.block_states[get_head(store)])
|
|
slot = get_current_slot(store)
|
|
if head_state.slot < slot:
|
|
process_slots(head_state, slot)
|
|
block = store.blocks[root]
|
|
# Only update if the proposer is the same as on the canonical chain
|
|
if block.proposer_index == get_beacon_proposer_index(head_state):
|
|
store.proposer_boost_root = root
|
|
</spec>
|
|
|
|
- name: update_proposer_boost_root#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="update_proposer_boost_root" fork="gloas" hash="d813a162">
|
|
def update_proposer_boost_root(store: Store, root: Root) -> None:
|
|
is_first_block = store.proposer_boost_root == Root()
|
|
# [Modified in Gloas:EIP7732]
|
|
is_timely = store.block_timeliness[root][ATTESTATION_TIMELINESS_INDEX]
|
|
|
|
# Add proposer score boost if the block is the first timely block
|
|
# for this slot, with the same proposer as the canonical chain.
|
|
if is_timely and is_first_block:
|
|
head_state = copy(store.block_states[get_head(store).root])
|
|
slot = get_current_slot(store)
|
|
if head_state.slot < slot:
|
|
process_slots(head_state, slot)
|
|
block = store.blocks[root]
|
|
# Only update if the proposer is the same as on the canonical chain
|
|
if block.proposer_index == get_beacon_proposer_index(head_state):
|
|
store.proposer_boost_root = root
|
|
</spec>
|
|
|
|
- name: update_unrealized_checkpoints#phase0
|
|
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#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#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#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#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#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#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#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#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#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#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#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#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#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#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#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#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#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#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#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#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#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#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#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#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: upgrade_to_gloas#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="upgrade_to_gloas" fork="gloas" hash="6e66df25">
|
|
def upgrade_to_gloas(pre: fulu.BeaconState) -> BeaconState:
|
|
epoch = fulu.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 Gloas:EIP7732]
|
|
current_version=GLOAS_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 Gloas:EIP7732]
|
|
# Removed `latest_execution_payload_header`
|
|
# [New in Gloas:EIP7732]
|
|
latest_execution_payload_bid=ExecutionPayloadBid(
|
|
block_hash=pre.latest_execution_payload_header.block_hash,
|
|
),
|
|
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,
|
|
proposer_lookahead=pre.proposer_lookahead,
|
|
# [New in Gloas:EIP7732]
|
|
builders=[],
|
|
# [New in Gloas:EIP7732]
|
|
next_withdrawal_builder_index=BuilderIndex(0),
|
|
# [New in Gloas:EIP7732]
|
|
execution_payload_availability=[0b1 for _ in range(SLOTS_PER_HISTORICAL_ROOT)],
|
|
# [New in Gloas:EIP7732]
|
|
builder_pending_payments=[BuilderPendingPayment() for _ in range(2 * SLOTS_PER_EPOCH)],
|
|
# [New in Gloas:EIP7732]
|
|
builder_pending_withdrawals=[],
|
|
# [New in Gloas:EIP7732]
|
|
latest_block_hash=pre.latest_execution_payload_header.block_hash,
|
|
# [New in Gloas:EIP7732]
|
|
payload_expected_withdrawals=[],
|
|
)
|
|
|
|
# [New in Gloas:EIP7732]
|
|
onboard_builders_from_pending_deposits(post)
|
|
|
|
return post
|
|
</spec>
|
|
|
|
- name: validate_light_client_update#altair
|
|
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#bellatrix
|
|
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#phase0
|
|
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_on_attestation#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="validate_on_attestation" fork="gloas" hash="4a56bc6c">
|
|
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.
|
|
block_slot = store.blocks[attestation.data.beacon_block_root].slot
|
|
assert block_slot <= attestation.data.slot
|
|
|
|
# [New in Gloas:EIP7732]
|
|
assert attestation.data.index in [0, 1]
|
|
if block_slot == attestation.data.slot:
|
|
assert attestation.data.index == 0
|
|
|
|
# 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#phase0
|
|
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#deneb
|
|
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#phase0
|
|
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#fulu
|
|
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#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="verify_data_column_sidecar" fork="gloas" hash="71548b68">
|
|
def verify_data_column_sidecar(
|
|
sidecar: DataColumnSidecar,
|
|
# [New in Gloas:EIP7732]
|
|
kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK],
|
|
) -> 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
|
|
|
|
# [Modified in Gloas:EIP7732]
|
|
# A sidecar for zero blobs is invalid
|
|
if len(sidecar.column) == 0:
|
|
return False
|
|
|
|
# [Modified in Gloas:EIP7732]
|
|
# The column length must be equal to the number of commitments/proofs
|
|
if len(sidecar.column) != len(kzg_commitments) or len(sidecar.column) != len(
|
|
sidecar.kzg_proofs
|
|
):
|
|
return False
|
|
|
|
return True
|
|
</spec>
|
|
|
|
- name: verify_data_column_sidecar_inclusion_proof#fulu
|
|
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#fulu
|
|
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: verify_execution_payload_bid_signature#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="verify_execution_payload_bid_signature" fork="gloas" hash="8a3e3a8d">
|
|
def verify_execution_payload_bid_signature(
|
|
state: BeaconState, signed_bid: SignedExecutionPayloadBid
|
|
) -> bool:
|
|
builder = state.builders[signed_bid.message.builder_index]
|
|
signing_root = compute_signing_root(
|
|
signed_bid.message, get_domain(state, DOMAIN_BEACON_BUILDER)
|
|
)
|
|
return bls.Verify(builder.pubkey, signing_root, signed_bid.signature)
|
|
</spec>
|
|
|
|
- name: verify_execution_payload_envelope_signature#gloas
|
|
sources: []
|
|
spec: |
|
|
<spec fn="verify_execution_payload_envelope_signature" fork="gloas" hash="49483ae2">
|
|
def verify_execution_payload_envelope_signature(
|
|
state: BeaconState, signed_envelope: SignedExecutionPayloadEnvelope
|
|
) -> bool:
|
|
builder_index = signed_envelope.message.builder_index
|
|
if builder_index == BUILDER_INDEX_SELF_BUILD:
|
|
validator_index = state.latest_block_header.proposer_index
|
|
pubkey = state.validators[validator_index].pubkey
|
|
else:
|
|
pubkey = state.builders[builder_index].pubkey
|
|
|
|
signing_root = compute_signing_root(
|
|
signed_envelope.message, get_domain(state, DOMAIN_BEACON_BUILDER)
|
|
)
|
|
return bls.Verify(pubkey, signing_root, signed_envelope.signature)
|
|
</spec>
|
|
|
|
- name: voting_period_start_time#phase0
|
|
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#phase0
|
|
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#phase0
|
|
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>
|