From b7308e0953287956908e667f29a150d7493a0a86 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 1 Dec 2021 11:43:32 -0700 Subject: [PATCH] remove capella built files --- .gitignore | 1 + tests/core/pyspec/eth2spec/capella/mainnet.py | 3072 ---------------- tests/core/pyspec/eth2spec/capella/minimal.py | 3074 ----------------- .../pyspec/eth2spec/withdrawals/mainnet.py | 3018 ---------------- .../pyspec/eth2spec/withdrawals/minimal.py | 3018 ---------------- 5 files changed, 1 insertion(+), 12182 deletions(-) delete mode 100644 tests/core/pyspec/eth2spec/capella/mainnet.py delete mode 100644 tests/core/pyspec/eth2spec/capella/minimal.py delete mode 100644 tests/core/pyspec/eth2spec/withdrawals/mainnet.py delete mode 100644 tests/core/pyspec/eth2spec/withdrawals/minimal.py diff --git a/.gitignore b/.gitignore index 76fe21ddd..56d7dfafc 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ consensus-spec-tests/ tests/core/pyspec/eth2spec/phase0/ tests/core/pyspec/eth2spec/altair/ tests/core/pyspec/eth2spec/merge/ +tests/core/pyspec/eth2spec/capella/ # coverage reports .htmlcov diff --git a/tests/core/pyspec/eth2spec/capella/mainnet.py b/tests/core/pyspec/eth2spec/capella/mainnet.py deleted file mode 100644 index e7ec0b4bd..000000000 --- a/tests/core/pyspec/eth2spec/capella/mainnet.py +++ /dev/null @@ -1,3072 +0,0 @@ -from lru import LRU -from dataclasses import ( - dataclass, - field, -) -from typing import ( - Any, Callable, Dict, Set, Sequence, Tuple, Optional, TypeVar, NamedTuple -) - -from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy, uint_to_bytes -from eth2spec.utils.ssz.ssz_typing import ( - View, boolean, Container, List, Vector, uint8, uint32, uint64, - Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist) -from eth2spec.utils.ssz.ssz_typing import Bitvector # noqa: F401 -from eth2spec.utils import bls -from eth2spec.utils.hash_function import hash - - -from typing import NewType, Union as PyUnion - -from eth2spec.phase0 import mainnet as phase0 -from eth2spec.utils.ssz.ssz_typing import Path - -from typing import Protocol -from eth2spec.altair import mainnet as altair -from eth2spec.utils.ssz.ssz_typing import Bytes8, Bytes20, ByteList, ByteVector, uint256 - -from eth2spec.merge import mainnet as merge - -SSZObject = TypeVar('SSZObject', bound=View) - - -SSZVariableName = str -GeneralizedIndex = NewType('GeneralizedIndex', int) - - -fork = 'capella' - - -MAX_BYTES_PER_TRANSACTION = uint64(2**30) - - -class Slot(uint64): - pass - - -class Epoch(uint64): - pass - - -class CommitteeIndex(uint64): - pass - - -class ValidatorIndex(uint64): - pass - - -class Gwei(uint64): - pass - - -class Root(Bytes32): - pass - - -class Hash32(Bytes32): - pass - - -class Version(Bytes4): - pass - - -class DomainType(Bytes4): - pass - - -class ForkDigest(Bytes4): - pass - - -class Domain(Bytes32): - pass - - -class BLSPubkey(Bytes48): - pass - - -class BLSSignature(Bytes96): - pass - - -class Ether(uint64): - pass - - -class ParticipationFlags(uint8): - pass - - -class ExecutionAddress(Bytes20): - pass - - -class PayloadId(Bytes8): - pass - - -class WithdrawalReceiptIndex(uint64): - pass - - -Transaction = ByteList[MAX_BYTES_PER_TRANSACTION] - - -def ceillog2(x: int) -> uint64: - if x < 1: - raise ValueError(f"ceillog2 accepts only positive values, x={x}") - return uint64((x - 1).bit_length()) - - -def floorlog2(x: int) -> uint64: - if x < 1: - raise ValueError(f"floorlog2 accepts only positive values, x={x}") - return uint64(x.bit_length() - 1) - - -FINALIZED_ROOT_INDEX = GeneralizedIndex(105) -NEXT_SYNC_COMMITTEE_INDEX = GeneralizedIndex(55) - -# Constant vars -GENESIS_SLOT = Slot(0) -GENESIS_EPOCH = Epoch(0) -FAR_FUTURE_EPOCH = Epoch(2**64 - 1) -BASE_REWARDS_PER_EPOCH = uint64(4) -DEPOSIT_CONTRACT_TREE_DEPTH = uint64(2**5) -JUSTIFICATION_BITS_LENGTH = uint64(4) -ENDIANNESS = 'little' -BLS_WITHDRAWAL_PREFIX = Bytes1('0x00') -ETH1_ADDRESS_WITHDRAWAL_PREFIX = Bytes1('0x01') -DOMAIN_BEACON_PROPOSER = DomainType('0x00000000') -DOMAIN_BEACON_ATTESTER = DomainType('0x01000000') -DOMAIN_RANDAO = DomainType('0x02000000') -DOMAIN_DEPOSIT = DomainType('0x03000000') -DOMAIN_VOLUNTARY_EXIT = DomainType('0x04000000') -DOMAIN_SELECTION_PROOF = DomainType('0x05000000') -DOMAIN_AGGREGATE_AND_PROOF = DomainType('0x06000000') -INTERVALS_PER_SLOT = uint64(3) -TARGET_AGGREGATORS_PER_COMMITTEE = 2**4 -RANDOM_SUBNETS_PER_VALIDATOR = 2**0 -EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION = 2**8 -ATTESTATION_SUBNET_COUNT = 64 -ETH_TO_GWEI = uint64(10**9) -SAFETY_DECAY = uint64(10) -TIMELY_SOURCE_FLAG_INDEX = 0 -TIMELY_TARGET_FLAG_INDEX = 1 -TIMELY_HEAD_FLAG_INDEX = 2 -TIMELY_SOURCE_WEIGHT = uint64(14) -TIMELY_TARGET_WEIGHT = uint64(26) -TIMELY_HEAD_WEIGHT = uint64(14) -SYNC_REWARD_WEIGHT = uint64(2) -PROPOSER_WEIGHT = uint64(8) -WEIGHT_DENOMINATOR = uint64(64) -DOMAIN_SYNC_COMMITTEE = DomainType('0x07000000') -DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF = DomainType('0x08000000') -DOMAIN_CONTRIBUTION_AND_PROOF = DomainType('0x09000000') -PARTICIPATION_FLAG_WEIGHTS = [TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT, TIMELY_HEAD_WEIGHT] -G2_POINT_AT_INFINITY = BLSSignature(b'\xc0' + b'\x00' * 95) -TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE = 2**4 -SYNC_COMMITTEE_SUBNET_COUNT = 4 -WITHDRAWAL_RECEIPT_LIMIT = uint64(2**40) -CAPELLA_FORK_VERSION = Version('0x03000000') -CAPELLA_FORK_EPOCH = Epoch(18446744073709551615) - -# Preset vars -MAX_COMMITTEES_PER_SLOT = uint64(64) -TARGET_COMMITTEE_SIZE = uint64(128) -MAX_VALIDATORS_PER_COMMITTEE = uint64(2048) -SHUFFLE_ROUND_COUNT = uint64(90) -HYSTERESIS_QUOTIENT = uint64(4) -HYSTERESIS_DOWNWARD_MULTIPLIER = uint64(1) -HYSTERESIS_UPWARD_MULTIPLIER = uint64(5) -MIN_DEPOSIT_AMOUNT = Gwei(1000000000) -MAX_EFFECTIVE_BALANCE = Gwei(32000000000) -EFFECTIVE_BALANCE_INCREMENT = Gwei(1000000000) -MIN_ATTESTATION_INCLUSION_DELAY = uint64(1) -SLOTS_PER_EPOCH = uint64(32) -MIN_SEED_LOOKAHEAD = uint64(1) -MAX_SEED_LOOKAHEAD = uint64(4) -MIN_EPOCHS_TO_INACTIVITY_PENALTY = uint64(4) -EPOCHS_PER_ETH1_VOTING_PERIOD = uint64(64) -SLOTS_PER_HISTORICAL_ROOT = uint64(8192) -EPOCHS_PER_HISTORICAL_VECTOR = uint64(65536) -EPOCHS_PER_SLASHINGS_VECTOR = uint64(8192) -HISTORICAL_ROOTS_LIMIT = uint64(16777216) -VALIDATOR_REGISTRY_LIMIT = uint64(1099511627776) -BASE_REWARD_FACTOR = uint64(64) -WHISTLEBLOWER_REWARD_QUOTIENT = uint64(512) -PROPOSER_REWARD_QUOTIENT = uint64(8) -INACTIVITY_PENALTY_QUOTIENT = uint64(67108864) -MIN_SLASHING_PENALTY_QUOTIENT = uint64(128) -PROPORTIONAL_SLASHING_MULTIPLIER = uint64(1) -MAX_PROPOSER_SLASHINGS = 16 -MAX_ATTESTER_SLASHINGS = 2 -MAX_ATTESTATIONS = 128 -MAX_DEPOSITS = 16 -MAX_VOLUNTARY_EXITS = 16 -SAFE_SLOTS_TO_UPDATE_JUSTIFIED = 8 -INACTIVITY_PENALTY_QUOTIENT_ALTAIR = uint64(50331648) -MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR = uint64(64) -PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR = uint64(2) -SYNC_COMMITTEE_SIZE = uint64(512) -EPOCHS_PER_SYNC_COMMITTEE_PERIOD = uint64(256) -MIN_SYNC_COMMITTEE_PARTICIPANTS = 1 -MAX_BYTES_PER_TRANSACTION = uint64(1073741824) -MAX_TRANSACTIONS_PER_PAYLOAD = uint64(1048576) -BYTES_PER_LOGS_BLOOM = uint64(256) -MAX_EXTRA_DATA_BYTES = 32 -INACTIVITY_PENALTY_QUOTIENT_MERGE = uint64(16777216) -MIN_SLASHING_PENALTY_QUOTIENT_MERGE = uint64(32) -PROPORTIONAL_SLASHING_MULTIPLIER_MERGE = uint64(3) - - -class Configuration(NamedTuple): - PRESET_BASE: str - MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: uint64 - MIN_GENESIS_TIME: uint64 - GENESIS_FORK_VERSION: Version - GENESIS_DELAY: uint64 - SECONDS_PER_SLOT: uint64 - SECONDS_PER_ETH1_BLOCK: uint64 - MIN_VALIDATOR_WITHDRAWABILITY_DELAY: uint64 - SHARD_COMMITTEE_PERIOD: uint64 - ETH1_FOLLOW_DISTANCE: uint64 - EJECTION_BALANCE: Gwei - MIN_PER_EPOCH_CHURN_LIMIT: uint64 - CHURN_LIMIT_QUOTIENT: uint64 - PROPOSER_SCORE_BOOST: uint64 - INACTIVITY_SCORE_BIAS: uint64 - INACTIVITY_SCORE_RECOVERY_RATE: uint64 - ALTAIR_FORK_VERSION: Version - ALTAIR_FORK_EPOCH: Epoch - TERMINAL_TOTAL_DIFFICULTY: int - TERMINAL_BLOCK_HASH: Hash32 - TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: int - MERGE_FORK_VERSION: Version - MERGE_FORK_EPOCH: Epoch - - -config = Configuration( - PRESET_BASE="mainnet", - MIN_GENESIS_ACTIVE_VALIDATOR_COUNT=uint64(16384), - MIN_GENESIS_TIME=uint64(1606824000), - GENESIS_FORK_VERSION=Version('0x00000000'), - GENESIS_DELAY=uint64(604800), - SECONDS_PER_SLOT=uint64(12), - SECONDS_PER_ETH1_BLOCK=uint64(14), - MIN_VALIDATOR_WITHDRAWABILITY_DELAY=uint64(256), - SHARD_COMMITTEE_PERIOD=uint64(256), - ETH1_FOLLOW_DISTANCE=uint64(2048), - EJECTION_BALANCE=Gwei(16000000000), - MIN_PER_EPOCH_CHURN_LIMIT=uint64(4), - CHURN_LIMIT_QUOTIENT=uint64(65536), - PROPOSER_SCORE_BOOST=uint64(70), - INACTIVITY_SCORE_BIAS=uint64(4), - INACTIVITY_SCORE_RECOVERY_RATE=uint64(16), - ALTAIR_FORK_VERSION=Version('0x01000000'), - ALTAIR_FORK_EPOCH=Epoch(74240), - TERMINAL_TOTAL_DIFFICULTY=115792089237316195423570985008687907853269984665640564039457584007913129638912, - TERMINAL_BLOCK_HASH=Hash32('0x0000000000000000000000000000000000000000000000000000000000000000'), - TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH=18446744073709551615, - MERGE_FORK_VERSION=Version('0x02000000'), - MERGE_FORK_EPOCH=Epoch(18446744073709551615), -) - - -class Fork(Container): - previous_version: Version - current_version: Version - epoch: Epoch # Epoch of latest fork - - -class ForkData(Container): - current_version: Version - genesis_validators_root: Root - - -class Checkpoint(Container): - epoch: Epoch - root: Root - - -class Validator(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 # Commitment to pubkey for withdrawals - effective_balance: Gwei # Balance at stake - slashed: boolean - # Status epochs - activation_eligibility_epoch: Epoch # When criteria for activation were met - activation_epoch: Epoch - exit_epoch: Epoch - withdrawable_epoch: Epoch # When validator can withdraw funds - - -class AttestationData(Container): - slot: Slot - index: CommitteeIndex - # LMD GHOST vote - beacon_block_root: Root - # FFG vote - source: Checkpoint - target: Checkpoint - - -class IndexedAttestation(Container): - attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE] - data: AttestationData - signature: BLSSignature - - -class PendingAttestation(Container): - aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] - data: AttestationData - inclusion_delay: Slot - proposer_index: ValidatorIndex - - -class Eth1Data(Container): - deposit_root: Root - deposit_count: uint64 - block_hash: Hash32 - - -class HistoricalBatch(Container): - block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - - -class DepositMessage(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 - amount: Gwei - - -class DepositData(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 - amount: Gwei - signature: BLSSignature # Signing over DepositMessage - - -class BeaconBlockHeader(Container): - slot: Slot - proposer_index: ValidatorIndex - parent_root: Root - state_root: Root - body_root: Root - - -class SigningData(Container): - object_root: Root - domain: Domain - - -class AttesterSlashing(Container): - attestation_1: IndexedAttestation - attestation_2: IndexedAttestation - - -class Attestation(Container): - aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] - data: AttestationData - signature: BLSSignature - - -class Deposit(Container): - proof: Vector[Bytes32, DEPOSIT_CONTRACT_TREE_DEPTH + 1] # Merkle path to deposit root - data: DepositData - - -class VoluntaryExit(Container): - epoch: Epoch # Earliest epoch when voluntary exit can be processed - validator_index: ValidatorIndex - - -class SignedVoluntaryExit(Container): - message: VoluntaryExit - signature: BLSSignature - - -class SignedBeaconBlockHeader(Container): - message: BeaconBlockHeader - signature: BLSSignature - - -class ProposerSlashing(Container): - signed_header_1: SignedBeaconBlockHeader - signed_header_2: SignedBeaconBlockHeader - - -class Eth1Block(Container): - timestamp: uint64 - deposit_root: Root - deposit_count: uint64 - # All other eth1 block fields - - -class AggregateAndProof(Container): - aggregator_index: ValidatorIndex - aggregate: Attestation - selection_proof: BLSSignature - - -class SignedAggregateAndProof(Container): - message: AggregateAndProof - signature: BLSSignature - - -class SyncAggregate(Container): - sync_committee_bits: Bitvector[SYNC_COMMITTEE_SIZE] - sync_committee_signature: BLSSignature - - -class SyncCommittee(Container): - pubkeys: Vector[BLSPubkey, SYNC_COMMITTEE_SIZE] - aggregate_pubkey: BLSPubkey - - -class SyncCommitteeMessage(Container): - # Slot to which this contribution pertains - slot: Slot - # Block root for this signature - beacon_block_root: Root - # Index of the validator that produced this signature - validator_index: ValidatorIndex - # Signature by the validator over the block root of `slot` - signature: BLSSignature - - -class SyncCommitteeContribution(Container): - # Slot to which this contribution pertains - slot: Slot - # Block root for this contribution - beacon_block_root: Root - # The subcommittee this contribution pertains to out of the broader sync committee - subcommittee_index: uint64 - # A bit is set if a signature from the validator at the corresponding - # index in the subcommittee is present in the aggregate `signature`. - aggregation_bits: Bitvector[SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT] - # Signature by the validator(s) over the block root of `slot` - signature: BLSSignature - - -class ContributionAndProof(Container): - aggregator_index: ValidatorIndex - contribution: SyncCommitteeContribution - selection_proof: BLSSignature - - -class SignedContributionAndProof(Container): - message: ContributionAndProof - signature: BLSSignature - - -class SyncAggregatorSelectionData(Container): - slot: Slot - subcommittee_index: uint64 - - -class LightClientSnapshot(Container): - # Beacon block header - header: BeaconBlockHeader - # Sync committees corresponding to the header - current_sync_committee: SyncCommittee - next_sync_committee: SyncCommittee - - -class LightClientUpdate(Container): - # Update beacon block header - header: BeaconBlockHeader - # Next sync committee corresponding to the header - next_sync_committee: SyncCommittee - next_sync_committee_branch: Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_INDEX)] - # Finality proof for the update header - finality_header: BeaconBlockHeader - finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)] - # Sync committee aggregate signature - sync_committee_bits: Bitvector[SYNC_COMMITTEE_SIZE] - sync_committee_signature: BLSSignature - # Fork version for the aggregate signature - fork_version: Version - - -class ExecutionPayload(Container): - # Execution block header fields - parent_hash: Hash32 - fee_recipient: ExecutionAddress # 'beneficiary' in the yellow paper - state_root: Bytes32 - receipt_root: Bytes32 # 'receipts root' in the yellow paper - logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] - random: Bytes32 # 'difficulty' in the yellow paper - block_number: uint64 # 'number' in the yellow paper - gas_limit: uint64 - gas_used: uint64 - timestamp: uint64 - extra_data: ByteList[MAX_EXTRA_DATA_BYTES] - base_fee_per_gas: uint256 - # Extra payload fields - block_hash: Hash32 # Hash of execution block - transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD] - - -class BeaconBlockBody(Container): - randao_reveal: BLSSignature - eth1_data: Eth1Data # Eth1 data vote - graffiti: Bytes32 # Arbitrary data - # Operations - proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] - attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS] - attestations: List[Attestation, MAX_ATTESTATIONS] - deposits: List[Deposit, MAX_DEPOSITS] - voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS] - sync_aggregate: SyncAggregate - # Execution - execution_payload: ExecutionPayload # [New in Merge] - - -class BeaconBlock(Container): - slot: Slot - proposer_index: ValidatorIndex - parent_root: Root - state_root: Root - body: BeaconBlockBody - - -class SignedBeaconBlock(Container): - message: BeaconBlock - signature: BLSSignature - - -class ExecutionPayloadHeader(Container): - # Execution block header fields - parent_hash: Hash32 - fee_recipient: ExecutionAddress - state_root: Bytes32 - receipt_root: Bytes32 - logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] - random: Bytes32 - block_number: uint64 - gas_limit: uint64 - gas_used: uint64 - timestamp: uint64 - extra_data: ByteList[MAX_EXTRA_DATA_BYTES] - base_fee_per_gas: uint256 - # Extra payload fields - block_hash: Hash32 # Hash of execution block - transactions_root: Root - - -class PowBlock(Container): - block_hash: Hash32 - parent_hash: Hash32 - total_difficulty: uint256 - - -class WithdrawalReceipt(Container): - index: WithdrawalReceiptIndex - address: ExecutionAddress - amount: Gwei - - -class BeaconState(Container): - # Versioning - genesis_time: uint64 - genesis_validators_root: Root - slot: Slot - fork: Fork - # History - latest_block_header: BeaconBlockHeader - block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] - # Eth1 - eth1_data: Eth1Data - eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] - eth1_deposit_index: uint64 - # Registry - validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] - balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] - # Randomness - randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] - # Slashings - slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances - # Participation - previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - # Finality - justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch - previous_justified_checkpoint: Checkpoint - current_justified_checkpoint: Checkpoint - finalized_checkpoint: Checkpoint - # Inactivity - inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] - # Sync - current_sync_committee: SyncCommittee - next_sync_committee: SyncCommittee - # Execution - latest_execution_payload_header: ExecutionPayloadHeader - # Withdrawals - withdrawal_receipts: List[WithdrawalReceipt, WITHDRAWAL_RECEIPT_LIMIT] # [New in Cappela] - - -@dataclass(eq=True, frozen=True) -class LatestMessage(object): - epoch: Epoch - root: Root - - -@dataclass -class Store(object): - time: uint64 - genesis_time: uint64 - justified_checkpoint: Checkpoint - finalized_checkpoint: Checkpoint - best_justified_checkpoint: Checkpoint - proposer_boost_root: Root - blocks: Dict[Root, BeaconBlock] = field(default_factory=dict) - block_states: Dict[Root, BeaconState] = field(default_factory=dict) - checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict) - latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict) - - -@dataclass -class LightClientStore(object): - snapshot: LightClientSnapshot - valid_updates: Set[LightClientUpdate] - - -@dataclass -class PayloadAttributes(object): - timestamp: uint64 - random: Bytes32 - suggested_fee_recipient: ExecutionAddress - - -class ExecutionEngine(Protocol): - - def execute_payload(self, execution_payload: ExecutionPayload) -> bool: - """ - Return ``True`` if and only if ``execution_payload`` is valid with respect to ``self.execution_state``. - """ - ... - - def notify_forkchoice_updated(self, - head_block_hash: Hash32, - finalized_block_hash: Hash32, - payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]: - ... - - def get_payload(self, payload_id: PayloadId) -> ExecutionPayload: - """ - Return ``execution_payload`` object. - """ - ... - - -def integer_squareroot(n: uint64) -> uint64: - """ - Return the largest integer ``x`` such that ``x**2 <= n``. - """ - x = n - y = (x + 1) // 2 - while y < x: - x = y - y = (x + n // x) // 2 - return x - - -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)) - - -def bytes_to_uint64(data: bytes) -> uint64: - """ - Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian. - """ - return uint64(int.from_bytes(data, ENDIANNESS)) - - -def is_active_validator(validator: Validator, epoch: Epoch) -> bool: - """ - Check if ``validator`` is active. - """ - return validator.activation_epoch <= epoch < validator.exit_epoch - - -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 - ) - - -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 - ) - - -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) - - -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) - ) - - -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) - - -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 - - -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 - - -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 - - -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)] - - -def compute_epoch_at_slot(slot: Slot) -> Epoch: - """ - Return the epoch number at ``slot``. - """ - return Epoch(slot // SLOTS_PER_EPOCH) - - -def compute_start_slot_at_epoch(epoch: Epoch) -> Slot: - """ - Return the start slot of ``epoch``. - """ - return Slot(epoch * SLOTS_PER_EPOCH) - - -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) - - -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, - )) - - -def compute_fork_digest(current_version: Version, genesis_validators_root: Root) -> ForkDigest: - """ - Return the 4-byte fork digest for the ``current_version`` and ``genesis_validators_root``. - This is a digest primarily used for domain separation on the p2p layer. - 4-bytes suffices for practical separation of forks/chains. - """ - return ForkDigest(compute_fork_data_root(current_version, genesis_validators_root)[:4]) - - -def compute_domain(domain_type: DomainType, fork_version: Version=None, genesis_validators_root: Root=None) -> Domain: - """ - Return the domain for the ``domain_type`` and ``fork_version``. - """ - if fork_version is None: - fork_version = config.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]) - - -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, - )) - - -def get_current_epoch(state: BeaconState) -> Epoch: - """ - Return the current epoch. - """ - return compute_epoch_at_slot(state.slot) - - -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) - - -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)) - - -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] - - -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] - - -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)] - - -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(config.MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // config.CHURN_LIMIT_QUOTIENT) - - -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) - - -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, - )) - - -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, - ) - - -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) - - -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, afterwhich this overflows uint64. - """ - return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, sum([state.validators[index].effective_balance for index in indices]))) - - -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)))) - - -def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch=None) -> Domain: - """ - Return the signature domain (fork version concatenated with domain type) of a message. - """ - epoch = get_current_epoch(state) if epoch is None else epoch - fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version - return compute_domain(domain_type, fork_version, state.genesis_validators_root) - - -def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> IndexedAttestation: - """ - Return the indexed attestation corresponding to ``attestation``. - """ - attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) - - return IndexedAttestation( - attesting_indices=sorted(attesting_indices), - data=attestation.data, - signature=attestation.signature, - ) - - -def get_attesting_indices(state: BeaconState, - data: AttestationData, - bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]: - """ - Return the set of attesting indices corresponding to ``data`` and ``bits``. - """ - committee = get_beacon_committee(state, data.slot, data.index) - return set(index for i, index in enumerate(committee) if bits[i]) - - -def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: - """ - Increase the validator balance at index ``index`` by ``delta``. - """ - state.balances[index] += delta - - -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 - - -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 + config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY) - - -def slash_validator(state: BeaconState, - slashed_index: ValidatorIndex, - whistleblower_index: ValidatorIndex=None) -> None: - """ - Slash the validator with index ``slashed_index``. - """ - epoch = get_current_epoch(state) - initiate_validator_exit(state, slashed_index) - validator = state.validators[slashed_index] - validator.slashed = True - validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR)) - state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance - slashing_penalty = validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_MERGE # [Modified in Merge] - 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)) - - -def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, - eth1_timestamp: uint64, - deposits: Sequence[Deposit], - execution_payload_header: ExecutionPayloadHeader=ExecutionPayloadHeader() - ) -> BeaconState: - fork = Fork( - previous_version=config.MERGE_FORK_VERSION, # [Modified in Merge] for testing only - current_version=config.MERGE_FORK_VERSION, # [Modified in Merge] - epoch=GENESIS_EPOCH, - ) - state = BeaconState( - genesis_time=eth1_timestamp + config.GENESIS_DELAY, - fork=fork, - eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), - 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) - - # Fill in sync committees - # Note: A duplicate committee is assigned for the current and next committee at genesis - state.current_sync_committee = get_next_sync_committee(state) - state.next_sync_committee = get_next_sync_committee(state) - - # [New in Merge] Initialize the execution payload header - # If empty, will initialize a chain that has not yet gone through the Merge transition - state.latest_execution_payload_header = execution_payload_header - - return state - - -def is_valid_genesis_state(state: BeaconState) -> bool: - if state.genesis_time < config.MIN_GENESIS_TIME: - return False - if len(get_active_validator_indices(state, GENESIS_EPOCH)) < config.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: - return False - return True - - -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) - - -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) - - -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) - - -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 - - -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) - process_historical_roots_update(state) - process_participation_flag_updates(state) - process_sync_committee_updates(state) - process_withdrawals(state) # [New in Cappela] - - -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 - - -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) - ] - - -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) - ] - - -def get_unslashed_attesting_indices(state: BeaconState, - attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]: - output = set() # type: Set[ValidatorIndex] - for a in attestations: - output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits)) - return set(filter(lambda index: not state.validators[index].slashed, output)) - - -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)) - - -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) - - -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 - - -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)) - - -def get_proposer_reward(state: BeaconState, attesting_index: ValidatorIndex) -> Gwei: - return Gwei(get_base_reward(state, attesting_index) // PROPOSER_REWARD_QUOTIENT) - - -def get_finality_delay(state: BeaconState) -> uint64: - return get_previous_epoch(state) - state.finalized_checkpoint.epoch - - -def is_in_inactivity_leak(state: BeaconState) -> bool: - return get_finality_delay(state) > MIN_EPOCHS_TO_INACTIVITY_PENALTY - - -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) - ] - - -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 - - -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) - - -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) - - -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) - - -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.data, a.aggregation_bits) - ], 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 - - -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 Merge] - penalty_denominator = config.INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_MERGE - penalties[index] += Gwei(penalty_numerator // penalty_denominator) - return rewards, penalties - - -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 - - -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]) - - -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 <= config.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)) - - -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_MERGE, # [Modified in Merge] - 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) - - -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 = [] - - -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) - - -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) - - -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) - - -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)) - - -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 = [] - - -def process_block(state: BeaconState, block: BeaconBlock) -> None: - process_block_header(state, block) - if is_execution_enabled(state, block.body): - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Merge] - process_randao(state, block.body) - process_eth1_data(state, block.body) - process_operations(state, block.body) - process_sync_aggregate(state, block.body.sync_aggregate) - - -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 - - -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 - - -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 - - -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) - - -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) - - -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 - - -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, data, attestation.aggregation_bits): - 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) - - -def get_validator_from_deposit(state: BeaconState, deposit: Deposit) -> Validator: - amount = deposit.data.amount - effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) - - return Validator( - pubkey=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - activation_eligibility_epoch=FAR_FUTURE_EPOCH, - activation_epoch=FAR_FUTURE_EPOCH, - exit_epoch=FAR_FUTURE_EPOCH, - withdrawable_epoch=FAR_FUTURE_EPOCH, - effective_balance=effective_balance, - ) - - -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, - depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in - index=state.eth1_deposit_index, - root=state.eth1_data.deposit_root, - ) - - # Deposits must be processed in order - state.eth1_deposit_index += 1 - - pubkey = deposit.data.pubkey - amount = deposit.data.amount - validator_pubkeys = [validator.pubkey for validator 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=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - amount=deposit.data.amount, - ) - domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks - signing_root = compute_signing_root(deposit_message, domain) - # Initialize validator if the deposit signature is valid - if bls.Verify(pubkey, signing_root, deposit.data.signature): - state.validators.append(get_validator_from_deposit(state, deposit)) - state.balances.append(amount) - state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) - state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) - state.inactivity_scores.append(uint64(0)) - else: - # Increase balance by deposit amount - index = ValidatorIndex(validator_pubkeys.index(pubkey)) - increase_balance(state, index, amount) - - -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 + config.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) - - -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 + config.SECONDS_PER_SLOT * anchor_state.slot), - genesis_time=anchor_state.genesis_time, - justified_checkpoint=justified_checkpoint, - finalized_checkpoint=finalized_checkpoint, - best_justified_checkpoint=justified_checkpoint, - proposer_boost_root=proposer_boost_root, - blocks={anchor_root: copy(anchor_block)}, - block_states={anchor_root: copy(anchor_state)}, - checkpoint_states={justified_checkpoint: copy(anchor_state)}, - ) - - -def get_slots_since_genesis(store: Store) -> int: - return (store.time - store.genesis_time) // config.SECONDS_PER_SLOT - - -def get_current_slot(store: Store) -> Slot: - return Slot(GENESIS_SLOT + get_slots_since_genesis(store)) - - -def compute_slots_since_epoch_start(slot: Slot) -> int: - return slot - compute_start_slot_at_epoch(compute_epoch_at_slot(slot)) - - -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) - elif block.slot == slot: - return root - else: - # root is older than queried slot, thus a skip slot. Return most recent root prior to slot - return root - - -def get_latest_attesting_balance(store: Store, root: Root) -> Gwei: - state = store.checkpoint_states[store.justified_checkpoint] - active_indices = get_active_validator_indices(state, get_current_epoch(state)) - attestation_score = Gwei(sum( - state.validators[i].effective_balance for i in active_indices - if (i in store.latest_messages - and get_ancestor(store, store.latest_messages[i].root, store.blocks[root].slot) == root) - )) - proposer_score = Gwei(0) - if store.proposer_boost_root != Root(): - block = store.blocks[root] - if get_ancestor(store, root, block.slot) == store.proposer_boost_root: - num_validators = len(get_active_validator_indices(state, get_current_epoch(state))) - avg_balance = get_total_active_balance(state) // num_validators - committee_size = num_validators // SLOTS_PER_EPOCH - committee_weight = committee_size * avg_balance - proposer_score = (committee_weight * config.PROPOSER_SCORE_BOOST) // 100 - return attestation_score + proposer_score - - -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 - - # If leaf block, check finalized/justified checkpoints as matching latest. - head_state = store.block_states[block_root] - - correct_justified = ( - store.justified_checkpoint.epoch == GENESIS_EPOCH - or head_state.current_justified_checkpoint == store.justified_checkpoint - ) - correct_finalized = ( - store.finalized_checkpoint.epoch == GENESIS_EPOCH - or head_state.finalized_checkpoint == store.finalized_checkpoint - ) - # 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 - - -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 - - -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 - head = max(children, key=lambda root: (get_latest_attesting_balance(store, root), root)) - - -def should_update_justified_checkpoint(store: Store, new_justified_checkpoint: Checkpoint) -> bool: - """ - To address the bouncing attack, only update conflicting justified - checkpoints in the fork choice if in the early slots of the epoch. - Otherwise, delay incorporation of new justified checkpoint until next epoch boundary. - - See https://ethresear.ch/t/prevention-of-bouncing-attack-on-ffg/6114 for more detailed analysis and discussion. - """ - if compute_slots_since_epoch_start(get_current_slot(store)) < SAFE_SLOTS_TO_UPDATE_JUSTIFIED: - return True - - justified_slot = compute_start_slot_at_epoch(store.justified_checkpoint.epoch) - if not get_ancestor(store, new_justified_checkpoint.root, justified_slot) == store.justified_checkpoint.root: - return False - - return True - - -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 = compute_epoch_at_slot(get_current_slot(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] - - -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) - - # Attestations target be for a known block. If target block is unknown, delay consideration until the 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 - target_slot = compute_start_slot_at_epoch(target.epoch) - assert target.root == get_ancestor(store, attestation.data.beacon_block_root, target_slot) - - # 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 - - -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 - - -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 - for i in 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) - - -def on_tick(store: Store, time: uint64) -> None: - previous_slot = get_current_slot(store) - - # update store time - store.time = time - - current_slot = get_current_slot(store) - - # Reset store.proposer_boost_root if this is a new slot - if current_slot > previous_slot: - store.proposer_boost_root = Root() - - # Not a new epoch, return - if not (current_slot > previous_slot and compute_slots_since_epoch_start(current_slot) == 0): - return - - # Update store.justified_checkpoint if a better checkpoint on the store.finalized_checkpoint chain - if store.best_justified_checkpoint.epoch > store.justified_checkpoint.epoch: - finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) - ancestor_at_finalized_slot = get_ancestor(store, store.best_justified_checkpoint.root, finalized_slot) - if ancestor_at_finalized_slot == store.finalized_checkpoint.root: - store.justified_checkpoint = store.best_justified_checkpoint - - -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 the 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 - assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root - - # Check the block is valid and compute the post-state - state = pre_state.copy() - state_transition(state, signed_block, True) - - # [New in Merge] - if is_merge_transition_block(pre_state, block.body): - validate_merge_block(block) - - # Add new block to the store - store.blocks[hash_tree_root(block)] = block - # Add new state for this block to the store - store.block_states[hash_tree_root(block)] = state - - # Add proposer score boost if the block is timely - time_into_slot = (store.time - store.genesis_time) % config.SECONDS_PER_SLOT - is_before_attesting_interval = time_into_slot < config.SECONDS_PER_SLOT // INTERVALS_PER_SLOT - if get_current_slot(store) == block.slot and is_before_attesting_interval: - store.proposer_boost_root = hash_tree_root(block) - - # Update justified checkpoint - if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch: - if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch: - store.best_justified_checkpoint = state.current_justified_checkpoint - if should_update_justified_checkpoint(store, state.current_justified_checkpoint): - store.justified_checkpoint = state.current_justified_checkpoint - - # Update finalized checkpoint - if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch: - store.finalized_checkpoint = state.finalized_checkpoint - store.justified_checkpoint = state.current_justified_checkpoint - - -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) - - -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)) - - -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 - - -def is_proposer(state: BeaconState, validator_index: ValidatorIndex) -> bool: - return get_beacon_proposer_index(state) == validator_index - - -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) - - -def compute_time_at_slot(state: BeaconState, slot: Slot) -> uint64: - return uint64(state.genesis_time + slot * config.SECONDS_PER_SLOT) - - -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) - - -def is_candidate_block(block: Eth1Block, period_start: uint64) -> bool: - return ( - block.timestamp + config.SECONDS_PER_ETH1_BLOCK * config.ETH1_FOLLOW_DISTANCE <= period_start - and block.timestamp + config.SECONDS_PER_ETH1_BLOCK * config.ETH1_FOLLOW_DISTANCE * 2 >= period_start - ) - - -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, - key=lambda v: (valid_votes.count(v), -valid_votes.index(v)), # Tiebreak by smallest distance - default=default_vote - ) - - -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) - - -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) - - -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) - - -def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, committee_index: CommitteeIndex) -> uint64: - """ - 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 uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT) - - -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) - - -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 - - -def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature: - signatures = [attestation.signature for attestation in attestations] - return bls.Aggregate(signatures) - - -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), - ) - - -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) - - -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 = config.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 - - -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 ws_state.latest_block_header.state_root == 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 - - -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 - - -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 - - -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 - - -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) - - -def get_base_reward_per_increment(state: BeaconState) -> Gwei: - return Gwei(EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR // integer_squareroot(get_total_active_balance(state))) - - -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)) - - -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. - """ - if data.target.epoch == get_current_epoch(state): - justified_checkpoint = state.current_justified_checkpoint - else: - justified_checkpoint = state.previous_justified_checkpoint - - # Matching roots - is_matching_source = data.source == justified_checkpoint - is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch) - is_matching_head = is_matching_target and data.beacon_block_root == get_block_root_at_slot(state, data.slot) - 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 - - -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 - - -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 - 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) - 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) - - -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] += config.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(config.INACTIVITY_SCORE_RECOVERY_RATE, state.inactivity_scores[index]) - - -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))] - - -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) - - -def eth_aggregate_pubkeys(pubkeys: Sequence[BLSPubkey]) -> BLSPubkey: - return bls.AggregatePKs(pubkeys) - - -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) - - -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, data, attestation.aggregation_bits): - for flag_index in participation_flag_indices: - epoch_participation[index] = add_flag(epoch_participation[index], flag_index) - - -def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState: - epoch = phase0.get_current_epoch(pre) - post = BeaconState( - # Versioning - 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=config.ALTAIR_FORK_VERSION, - epoch=epoch, - ), - # History - latest_block_header=pre.latest_block_header, - block_roots=pre.block_roots, - state_roots=pre.state_roots, - historical_roots=pre.historical_roots, - # Eth1 - eth1_data=pre.eth1_data, - eth1_data_votes=pre.eth1_data_votes, - eth1_deposit_index=pre.eth1_deposit_index, - # Registry - validators=pre.validators, - balances=pre.balances, - # Randomness - randao_mixes=pre.randao_mixes, - # Slashings - slashings=pre.slashings, - # Participation - previous_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))], - current_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))], - # Finality - 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 - 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 - - -def compute_sync_committee_period(epoch: Epoch) -> uint64: - return epoch // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - - -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 - - -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 - - -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, - ) - - -def compute_subnets_for_sync_committee(state: BeaconState, validator_index: ValidatorIndex) -> Set[uint64]: - 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([ - uint64(index // (SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT)) - for index in sync_committee_indices - ]) - - -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) - - -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 - - -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, - ) - - -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) - - -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] - - -def get_subtree_index(generalized_index: GeneralizedIndex) -> uint64: - return uint64(generalized_index % 2**(floorlog2(generalized_index))) - - -def validate_light_client_update(snapshot: LightClientSnapshot, - update: LightClientUpdate, - genesis_validators_root: Root) -> None: - # Verify update slot is larger than snapshot slot - assert update.header.slot > snapshot.header.slot - - # Verify update does not skip a sync committee period - snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - assert update_period in (snapshot_period, snapshot_period + 1) - - # Verify update header root is the finalized root of the finality header, if specified - if update.finality_header == BeaconBlockHeader(): - signed_header = update.header - assert update.finality_branch == [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))] - else: - signed_header = update.finality_header - assert is_valid_merkle_branch( - leaf=hash_tree_root(update.header), - branch=update.finality_branch, - depth=floorlog2(FINALIZED_ROOT_INDEX), - index=get_subtree_index(FINALIZED_ROOT_INDEX), - root=update.finality_header.state_root, - ) - - # Verify update next sync committee if the update period incremented - if update_period == snapshot_period: - sync_committee = snapshot.current_sync_committee - assert update.next_sync_committee_branch == [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))] - else: - sync_committee = snapshot.next_sync_committee - assert is_valid_merkle_branch( - leaf=hash_tree_root(update.next_sync_committee), - branch=update.next_sync_committee_branch, - depth=floorlog2(NEXT_SYNC_COMMITTEE_INDEX), - index=get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX), - root=update.header.state_root, - ) - - # Verify sync committee has sufficient participants - assert sum(update.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS - - # Verify sync committee aggregate signature - participant_pubkeys = [pubkey for (bit, pubkey) in zip(update.sync_committee_bits, sync_committee.pubkeys) if bit] - domain = compute_domain(DOMAIN_SYNC_COMMITTEE, update.fork_version, genesis_validators_root) - signing_root = compute_signing_root(signed_header, domain) - assert bls.FastAggregateVerify(participant_pubkeys, signing_root, update.sync_committee_signature) - - -def apply_light_client_update(snapshot: LightClientSnapshot, update: LightClientUpdate) -> None: - snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - if update_period == snapshot_period + 1: - snapshot.current_sync_committee = snapshot.next_sync_committee - snapshot.next_sync_committee = update.next_sync_committee - snapshot.header = update.header - - -def process_light_client_update(store: LightClientStore, update: LightClientUpdate, current_slot: Slot, - genesis_validators_root: Root) -> None: - validate_light_client_update(store.snapshot, update, genesis_validators_root) - store.valid_updates.add(update) - - update_timeout = SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD - if ( - sum(update.sync_committee_bits) * 3 >= len(update.sync_committee_bits) * 2 - and update.finality_header != BeaconBlockHeader() - ): - # Apply update if (1) 2/3 quorum is reached and (2) we have a finality proof. - # Note that (2) means that the current light client design needs finality. - # It may be changed to re-organizable light client design. See the on-going issue consensus-specs#2182. - apply_light_client_update(store.snapshot, update) - store.valid_updates = set() - elif current_slot > store.snapshot.header.slot + update_timeout: - # Forced best update when the update timeout has elapsed - apply_light_client_update(store.snapshot, - max(store.valid_updates, key=lambda update: sum(update.sync_committee_bits))) - store.valid_updates = set() - - -def is_merge_transition_complete(state: BeaconState) -> bool: - return state.latest_execution_payload_header != ExecutionPayloadHeader() - - -def is_merge_transition_block(state: BeaconState, body: BeaconBlockBody) -> bool: - return not is_merge_transition_complete(state) and body.execution_payload != ExecutionPayload() - - -def is_execution_enabled(state: BeaconState, body: BeaconBlockBody) -> bool: - return is_merge_transition_block(state, body) or is_merge_transition_complete(state) - - -def compute_timestamp_at_slot(state: BeaconState, slot: Slot) -> uint64: - slots_since_genesis = slot - GENESIS_SLOT - return uint64(state.genesis_time + slots_since_genesis * config.SECONDS_PER_SLOT) - - -def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: - # 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 random - assert payload.random == get_randao_mix(state, get_current_epoch(state)) - # Verify timestamp - assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) - # Verify the execution payload is valid - assert execution_engine.execute_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, - receipt_root=payload.receipt_root, - logs_bloom=payload.logs_bloom, - random=payload.random, - 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), - ) - - -def upgrade_to_merge(pre: altair.BeaconState) -> BeaconState: - epoch = altair.get_current_epoch(pre) - post = BeaconState( - # Versioning - 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=config.MERGE_FORK_VERSION, - epoch=epoch, - ), - # History - latest_block_header=pre.latest_block_header, - block_roots=pre.block_roots, - state_roots=pre.state_roots, - historical_roots=pre.historical_roots, - # Eth1 - eth1_data=pre.eth1_data, - eth1_data_votes=pre.eth1_data_votes, - eth1_deposit_index=pre.eth1_deposit_index, - # Registry - validators=pre.validators, - balances=pre.balances, - # Randomness - randao_mixes=pre.randao_mixes, - # Slashings - slashings=pre.slashings, - # Participation - previous_epoch_participation=pre.previous_epoch_participation, - current_epoch_participation=pre.current_epoch_participation, - # Finality - 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 - inactivity_scores=pre.inactivity_scores, - # Sync - current_sync_committee=pre.current_sync_committee, - next_sync_committee=pre.next_sync_committee, - # Execution-layer - latest_execution_payload_header=ExecutionPayloadHeader(), - ) - - return post - - -def is_valid_terminal_pow_block(block: PowBlock, parent: PowBlock) -> bool: - is_total_difficulty_reached = block.total_difficulty >= config.TERMINAL_TOTAL_DIFFICULTY - is_parent_total_difficulty_valid = parent.total_difficulty < config.TERMINAL_TOTAL_DIFFICULTY - return is_total_difficulty_reached and is_parent_total_difficulty_valid - - -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 config.TERMINAL_BLOCK_HASH != Hash32(): - # If `config.TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached. - assert compute_epoch_at_slot(block.slot) >= config.TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH - assert block.body.execution_payload.parent_hash == config.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) - - -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 >= config.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 >= config.TERMINAL_TOTAL_DIFFICULTY - if not parent_reached_ttd: - return block - - return None - - -def get_terminal_pow_block(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlock]: - if config.TERMINAL_BLOCK_HASH != Hash32(): - # Terminal block hash override takes precedence over terminal total difficulty - if config.TERMINAL_BLOCK_HASH in pow_chain: - return pow_chain[config.TERMINAL_BLOCK_HASH] - else: - return None - - return get_pow_block_at_terminal_total_difficulty(pow_chain) - - -def prepare_execution_payload(state: BeaconState, - pow_chain: Dict[Hash32, PowBlock], - finalized_block_hash: Hash32, - suggested_fee_recipient: ExecutionAddress, - execution_engine: ExecutionEngine) -> Optional[PayloadId]: - if not is_merge_transition_complete(state): - is_terminal_block_hash_set = config.TERMINAL_BLOCK_HASH != Hash32() - is_activation_epoch_reached = get_current_epoch(state) >= config.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_timestamp_at_slot(state, state.slot), - random=get_randao_mix(state, get_current_epoch(state)), - suggested_fee_recipient=suggested_fee_recipient, - ) - return execution_engine.notify_forkchoice_updated(parent_hash, finalized_block_hash, payload_attributes) - - -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) - - -def withdraw(state: BeaconState, index: ValidatorIndex, amount: Gwei) -> None: - # Decrease the validator's balance - decrease_balance(state, index, amount) - # Create a corresponding withdrawal receipt - receipt = WithdrawalReceipt( - index=WithdrawalReceiptIndex(len(state.withdrawal_receipts)), - address=state.validators[index].withdrawal_credentials[12:], - amount=amount, - ) - state.withdrawal_receipts.append(receipt) - - -def is_withdrawable_validator(validator: Validator, epoch: Epoch) -> bool: - """ - Check if ``validator`` is withdrawable. - """ - return validator.withdrawable_epoch <= epoch - - -def process_withdrawals(state: BeaconState) -> None: - current_epoch = get_current_epoch(state) - for index, validator in enumerate(state.validators): - balance = state.balances[index] - is_balance_nonzero = state.balances[index] == 0 - is_eth1_withdrawal_prefix = validator.withdrawal_credentials[0] != ETH1_ADDRESS_WITHDRAWAL_PREFIX - if is_balance_nonzero and is_eth1_withdrawal_prefix and is_withdrawable_validator(validator, current_epoch): - withdraw(state, ValidatorIndex(index), balance) - - -def upgrade_to_capella(pre: merge.BeaconState) -> BeaconState: - epoch = merge.get_current_epoch(pre) - post = BeaconState( - # Versioning - 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, - ), - # History - latest_block_header=pre.latest_block_header, - block_roots=pre.block_roots, - state_roots=pre.state_roots, - historical_roots=pre.historical_roots, - # Eth1 - eth1_data=pre.eth1_data, - eth1_data_votes=pre.eth1_data_votes, - eth1_deposit_index=pre.eth1_deposit_index, - # Registry - validators=pre.validators, - balances=pre.balances, - # Randomness - randao_mixes=pre.randao_mixes, - # Slashings - slashings=pre.slashings, - # Participation - previous_epoch_participation=pre.previous_epoch_participation, - current_epoch_participation=pre.current_epoch_participation, - # Finality - 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 - inactivity_scores=pre.inactivity_scores, - # Sync - current_sync_committee=pre.current_sync_committee, - next_sync_committee=pre.next_sync_committee, - # Execution-layer - latest_execution_payload_header=pre.latest_execution_payload_header, - # Withdrawals - withdrawal_receipts=[], - ) - - return post - - -def get_eth1_data(block: Eth1Block) -> Eth1Data: - """ - A stub function return mocking Eth1Data. - """ - return Eth1Data( - deposit_root=block.deposit_root, - deposit_count=block.deposit_count, - block_hash=hash_tree_root(block)) - - -def cache_this(key_fn, value_fn, lru_size): # type: ignore - cache_dict = LRU(size=lru_size) - - def wrapper(*args, **kw): # type: ignore - key = key_fn(*args, **kw) - nonlocal cache_dict - if key not in cache_dict: - cache_dict[key] = value_fn(*args, **kw) - return cache_dict[key] - return wrapper - - -_compute_shuffled_index = compute_shuffled_index -compute_shuffled_index = cache_this( - lambda index, index_count, seed: (index, index_count, seed), - _compute_shuffled_index, lru_size=SLOTS_PER_EPOCH * 3) - -_get_total_active_balance = get_total_active_balance -get_total_active_balance = cache_this( - lambda state: (state.validators.hash_tree_root(), compute_epoch_at_slot(state.slot)), - _get_total_active_balance, lru_size=10) - -_get_base_reward = get_base_reward -get_base_reward = cache_this( - lambda state, index: (state.validators.hash_tree_root(), state.slot, index), - _get_base_reward, lru_size=2048) - -_get_committee_count_per_slot = get_committee_count_per_slot -get_committee_count_per_slot = cache_this( - lambda state, epoch: (state.validators.hash_tree_root(), epoch), - _get_committee_count_per_slot, lru_size=SLOTS_PER_EPOCH * 3) - -_get_active_validator_indices = get_active_validator_indices -get_active_validator_indices = cache_this( - lambda state, epoch: (state.validators.hash_tree_root(), epoch), - _get_active_validator_indices, lru_size=3) - -_get_beacon_committee = get_beacon_committee -get_beacon_committee = cache_this( - lambda state, slot, index: (state.validators.hash_tree_root(), state.randao_mixes.hash_tree_root(), slot, index), - _get_beacon_committee, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3) - -_get_matching_target_attestations = get_matching_target_attestations -get_matching_target_attestations = cache_this( - lambda state, epoch: (state.hash_tree_root(), epoch), - _get_matching_target_attestations, lru_size=10) - -_get_matching_head_attestations = get_matching_head_attestations -get_matching_head_attestations = cache_this( - lambda state, epoch: (state.hash_tree_root(), epoch), - _get_matching_head_attestations, lru_size=10) - -_get_attesting_indices = get_attesting_indices -get_attesting_indices = cache_this( - lambda state, data, bits: ( - state.randao_mixes.hash_tree_root(), - state.validators.hash_tree_root(), data.hash_tree_root(), bits.hash_tree_root() - ), - _get_attesting_indices, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3) - - -def get_generalized_index(ssz_class: Any, *path: Sequence[PyUnion[int, SSZVariableName]]) -> GeneralizedIndex: - ssz_path = Path(ssz_class) - for item in path: - ssz_path = ssz_path / item - return GeneralizedIndex(ssz_path.gindex()) - - -ExecutionState = Any - - -def get_pow_block(hash: Bytes32) -> Optional[PowBlock]: - return PowBlock(block_hash=hash, parent_hash=Bytes32(), total_difficulty=uint256(0)) - - -def get_execution_state(execution_state_root: Bytes32) -> ExecutionState: - pass - - -def get_pow_chain_head() -> PowBlock: - pass - - -class NoopExecutionEngine(ExecutionEngine): - - def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: - return True - - def notify_forkchoice_updated(self: ExecutionEngine, - head_block_hash: Hash32, - finalized_block_hash: Hash32, - payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]: - pass - - def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload: - raise NotImplementedError("no default block production") - - -EXECUTION_ENGINE = NoopExecutionEngine() - - -assert FINALIZED_ROOT_INDEX == get_generalized_index(BeaconState, 'finalized_checkpoint', 'root') -assert NEXT_SYNC_COMMITTEE_INDEX == get_generalized_index(BeaconState, 'next_sync_committee') diff --git a/tests/core/pyspec/eth2spec/capella/minimal.py b/tests/core/pyspec/eth2spec/capella/minimal.py deleted file mode 100644 index 8f0a5f67a..000000000 --- a/tests/core/pyspec/eth2spec/capella/minimal.py +++ /dev/null @@ -1,3074 +0,0 @@ -from lru import LRU -from dataclasses import ( - dataclass, - field, -) -from typing import ( - Any, Callable, Dict, Set, Sequence, Tuple, Optional, TypeVar, NamedTuple -) - -from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy, uint_to_bytes -from eth2spec.utils.ssz.ssz_typing import ( - View, boolean, Container, List, Vector, uint8, uint32, uint64, - Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist) -from eth2spec.utils.ssz.ssz_typing import Bitvector # noqa: F401 -from eth2spec.utils import bls -from eth2spec.utils.hash_function import hash - - -from typing import NewType, Union as PyUnion - -from eth2spec.phase0 import minimal as phase0 -from eth2spec.utils.ssz.ssz_typing import Path - -from typing import Protocol -from eth2spec.altair import minimal as altair -from eth2spec.utils.ssz.ssz_typing import Bytes8, Bytes20, ByteList, ByteVector, uint256 - -from eth2spec.merge import minimal as merge - -SSZObject = TypeVar('SSZObject', bound=View) - - -SSZVariableName = str -GeneralizedIndex = NewType('GeneralizedIndex', int) - - -fork = 'capella' - - -MAX_BYTES_PER_TRANSACTION = uint64(2**30) - - -class Slot(uint64): - pass - - -class Epoch(uint64): - pass - - -class CommitteeIndex(uint64): - pass - - -class ValidatorIndex(uint64): - pass - - -class Gwei(uint64): - pass - - -class Root(Bytes32): - pass - - -class Hash32(Bytes32): - pass - - -class Version(Bytes4): - pass - - -class DomainType(Bytes4): - pass - - -class ForkDigest(Bytes4): - pass - - -class Domain(Bytes32): - pass - - -class BLSPubkey(Bytes48): - pass - - -class BLSSignature(Bytes96): - pass - - -class Ether(uint64): - pass - - -class ParticipationFlags(uint8): - pass - - -class ExecutionAddress(Bytes20): - pass - - -class PayloadId(Bytes8): - pass - - -class WithdrawalReceiptIndex(uint64): - pass - - -Transaction = ByteList[MAX_BYTES_PER_TRANSACTION] - - -def ceillog2(x: int) -> uint64: - if x < 1: - raise ValueError(f"ceillog2 accepts only positive values, x={x}") - return uint64((x - 1).bit_length()) - - -def floorlog2(x: int) -> uint64: - if x < 1: - raise ValueError(f"floorlog2 accepts only positive values, x={x}") - return uint64(x.bit_length() - 1) - - -FINALIZED_ROOT_INDEX = GeneralizedIndex(105) -NEXT_SYNC_COMMITTEE_INDEX = GeneralizedIndex(55) - -# Constant vars -GENESIS_SLOT = Slot(0) -GENESIS_EPOCH = Epoch(0) -FAR_FUTURE_EPOCH = Epoch(2**64 - 1) -BASE_REWARDS_PER_EPOCH = uint64(4) -DEPOSIT_CONTRACT_TREE_DEPTH = uint64(2**5) -JUSTIFICATION_BITS_LENGTH = uint64(4) -ENDIANNESS = 'little' -BLS_WITHDRAWAL_PREFIX = Bytes1('0x00') -ETH1_ADDRESS_WITHDRAWAL_PREFIX = Bytes1('0x01') -DOMAIN_BEACON_PROPOSER = DomainType('0x00000000') -DOMAIN_BEACON_ATTESTER = DomainType('0x01000000') -DOMAIN_RANDAO = DomainType('0x02000000') -DOMAIN_DEPOSIT = DomainType('0x03000000') -DOMAIN_VOLUNTARY_EXIT = DomainType('0x04000000') -DOMAIN_SELECTION_PROOF = DomainType('0x05000000') -DOMAIN_AGGREGATE_AND_PROOF = DomainType('0x06000000') -INTERVALS_PER_SLOT = uint64(3) -TARGET_AGGREGATORS_PER_COMMITTEE = 2**4 -RANDOM_SUBNETS_PER_VALIDATOR = 2**0 -EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION = 2**8 -ATTESTATION_SUBNET_COUNT = 64 -ETH_TO_GWEI = uint64(10**9) -SAFETY_DECAY = uint64(10) -TIMELY_SOURCE_FLAG_INDEX = 0 -TIMELY_TARGET_FLAG_INDEX = 1 -TIMELY_HEAD_FLAG_INDEX = 2 -TIMELY_SOURCE_WEIGHT = uint64(14) -TIMELY_TARGET_WEIGHT = uint64(26) -TIMELY_HEAD_WEIGHT = uint64(14) -SYNC_REWARD_WEIGHT = uint64(2) -PROPOSER_WEIGHT = uint64(8) -WEIGHT_DENOMINATOR = uint64(64) -DOMAIN_SYNC_COMMITTEE = DomainType('0x07000000') -DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF = DomainType('0x08000000') -DOMAIN_CONTRIBUTION_AND_PROOF = DomainType('0x09000000') -PARTICIPATION_FLAG_WEIGHTS = [TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT, TIMELY_HEAD_WEIGHT] -G2_POINT_AT_INFINITY = BLSSignature(b'\xc0' + b'\x00' * 95) -TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE = 2**4 -SYNC_COMMITTEE_SUBNET_COUNT = 4 -WITHDRAWAL_RECEIPT_LIMIT = uint64(2**40) - -# Preset vars -MAX_COMMITTEES_PER_SLOT = uint64(4) -TARGET_COMMITTEE_SIZE = uint64(4) -MAX_VALIDATORS_PER_COMMITTEE = uint64(2048) -SHUFFLE_ROUND_COUNT = uint64(10) -HYSTERESIS_QUOTIENT = uint64(4) -HYSTERESIS_DOWNWARD_MULTIPLIER = uint64(1) -HYSTERESIS_UPWARD_MULTIPLIER = uint64(5) -MIN_DEPOSIT_AMOUNT = Gwei(1000000000) -MAX_EFFECTIVE_BALANCE = Gwei(32000000000) -EFFECTIVE_BALANCE_INCREMENT = Gwei(1000000000) -MIN_ATTESTATION_INCLUSION_DELAY = uint64(1) -SLOTS_PER_EPOCH = uint64(8) -MIN_SEED_LOOKAHEAD = uint64(1) -MAX_SEED_LOOKAHEAD = uint64(4) -MIN_EPOCHS_TO_INACTIVITY_PENALTY = uint64(4) -EPOCHS_PER_ETH1_VOTING_PERIOD = uint64(4) -SLOTS_PER_HISTORICAL_ROOT = uint64(64) -EPOCHS_PER_HISTORICAL_VECTOR = uint64(64) -EPOCHS_PER_SLASHINGS_VECTOR = uint64(64) -HISTORICAL_ROOTS_LIMIT = uint64(16777216) -VALIDATOR_REGISTRY_LIMIT = uint64(1099511627776) -BASE_REWARD_FACTOR = uint64(64) -WHISTLEBLOWER_REWARD_QUOTIENT = uint64(512) -PROPOSER_REWARD_QUOTIENT = uint64(8) -INACTIVITY_PENALTY_QUOTIENT = uint64(33554432) -MIN_SLASHING_PENALTY_QUOTIENT = uint64(64) -PROPORTIONAL_SLASHING_MULTIPLIER = uint64(2) -MAX_PROPOSER_SLASHINGS = 16 -MAX_ATTESTER_SLASHINGS = 2 -MAX_ATTESTATIONS = 128 -MAX_DEPOSITS = 16 -MAX_VOLUNTARY_EXITS = 16 -SAFE_SLOTS_TO_UPDATE_JUSTIFIED = 2 -INACTIVITY_PENALTY_QUOTIENT_ALTAIR = uint64(50331648) -MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR = uint64(64) -PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR = uint64(2) -SYNC_COMMITTEE_SIZE = uint64(32) -EPOCHS_PER_SYNC_COMMITTEE_PERIOD = uint64(8) -MIN_SYNC_COMMITTEE_PARTICIPANTS = 1 -MAX_BYTES_PER_TRANSACTION = uint64(1073741824) -MAX_TRANSACTIONS_PER_PAYLOAD = uint64(1048576) -BYTES_PER_LOGS_BLOOM = uint64(256) -MAX_EXTRA_DATA_BYTES = 32 -INACTIVITY_PENALTY_QUOTIENT_MERGE = uint64(16777216) -MIN_SLASHING_PENALTY_QUOTIENT_MERGE = uint64(32) -PROPORTIONAL_SLASHING_MULTIPLIER_MERGE = uint64(3) - - -class Configuration(NamedTuple): - PRESET_BASE: str - MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: uint64 - MIN_GENESIS_TIME: uint64 - GENESIS_FORK_VERSION: Version - GENESIS_DELAY: uint64 - SECONDS_PER_SLOT: uint64 - SECONDS_PER_ETH1_BLOCK: uint64 - MIN_VALIDATOR_WITHDRAWABILITY_DELAY: uint64 - SHARD_COMMITTEE_PERIOD: uint64 - ETH1_FOLLOW_DISTANCE: uint64 - EJECTION_BALANCE: Gwei - MIN_PER_EPOCH_CHURN_LIMIT: uint64 - CHURN_LIMIT_QUOTIENT: uint64 - PROPOSER_SCORE_BOOST: uint64 - INACTIVITY_SCORE_BIAS: uint64 - INACTIVITY_SCORE_RECOVERY_RATE: uint64 - ALTAIR_FORK_VERSION: Version - ALTAIR_FORK_EPOCH: Epoch - TERMINAL_TOTAL_DIFFICULTY: int - TERMINAL_BLOCK_HASH: Hash32 - TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: int - MERGE_FORK_VERSION: Version - MERGE_FORK_EPOCH: Epoch - CAPELLA_FORK_VERSION: Version - CAPELLA_FORK_EPOCH: Epoch - - -config = Configuration( - PRESET_BASE="minimal", - MIN_GENESIS_ACTIVE_VALIDATOR_COUNT=uint64(64), - MIN_GENESIS_TIME=uint64(1578009600), - GENESIS_FORK_VERSION=Version('0x00000001'), - GENESIS_DELAY=uint64(300), - SECONDS_PER_SLOT=uint64(6), - SECONDS_PER_ETH1_BLOCK=uint64(14), - MIN_VALIDATOR_WITHDRAWABILITY_DELAY=uint64(256), - SHARD_COMMITTEE_PERIOD=uint64(64), - ETH1_FOLLOW_DISTANCE=uint64(16), - EJECTION_BALANCE=Gwei(16000000000), - MIN_PER_EPOCH_CHURN_LIMIT=uint64(4), - CHURN_LIMIT_QUOTIENT=uint64(32), - PROPOSER_SCORE_BOOST=uint64(70), - INACTIVITY_SCORE_BIAS=uint64(4), - INACTIVITY_SCORE_RECOVERY_RATE=uint64(16), - ALTAIR_FORK_VERSION=Version('0x01000001'), - ALTAIR_FORK_EPOCH=Epoch(18446744073709551615), - TERMINAL_TOTAL_DIFFICULTY=115792089237316195423570985008687907853269984665640564039457584007913129638912, - TERMINAL_BLOCK_HASH=Hash32('0x0000000000000000000000000000000000000000000000000000000000000000'), - TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH=18446744073709551615, - MERGE_FORK_VERSION=Version('0x02000001'), - MERGE_FORK_EPOCH=Epoch(18446744073709551615), - CAPELLA_FORK_VERSION=Version('0x03000001'), - CAPELLA_FORK_EPOCH=Epoch(18446744073709551615), -) - - -class Fork(Container): - previous_version: Version - current_version: Version - epoch: Epoch # Epoch of latest fork - - -class ForkData(Container): - current_version: Version - genesis_validators_root: Root - - -class Checkpoint(Container): - epoch: Epoch - root: Root - - -class Validator(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 # Commitment to pubkey for withdrawals - effective_balance: Gwei # Balance at stake - slashed: boolean - # Status epochs - activation_eligibility_epoch: Epoch # When criteria for activation were met - activation_epoch: Epoch - exit_epoch: Epoch - withdrawable_epoch: Epoch # When validator can withdraw funds - - -class AttestationData(Container): - slot: Slot - index: CommitteeIndex - # LMD GHOST vote - beacon_block_root: Root - # FFG vote - source: Checkpoint - target: Checkpoint - - -class IndexedAttestation(Container): - attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE] - data: AttestationData - signature: BLSSignature - - -class PendingAttestation(Container): - aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] - data: AttestationData - inclusion_delay: Slot - proposer_index: ValidatorIndex - - -class Eth1Data(Container): - deposit_root: Root - deposit_count: uint64 - block_hash: Hash32 - - -class HistoricalBatch(Container): - block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - - -class DepositMessage(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 - amount: Gwei - - -class DepositData(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 - amount: Gwei - signature: BLSSignature # Signing over DepositMessage - - -class BeaconBlockHeader(Container): - slot: Slot - proposer_index: ValidatorIndex - parent_root: Root - state_root: Root - body_root: Root - - -class SigningData(Container): - object_root: Root - domain: Domain - - -class AttesterSlashing(Container): - attestation_1: IndexedAttestation - attestation_2: IndexedAttestation - - -class Attestation(Container): - aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] - data: AttestationData - signature: BLSSignature - - -class Deposit(Container): - proof: Vector[Bytes32, DEPOSIT_CONTRACT_TREE_DEPTH + 1] # Merkle path to deposit root - data: DepositData - - -class VoluntaryExit(Container): - epoch: Epoch # Earliest epoch when voluntary exit can be processed - validator_index: ValidatorIndex - - -class SignedVoluntaryExit(Container): - message: VoluntaryExit - signature: BLSSignature - - -class SignedBeaconBlockHeader(Container): - message: BeaconBlockHeader - signature: BLSSignature - - -class ProposerSlashing(Container): - signed_header_1: SignedBeaconBlockHeader - signed_header_2: SignedBeaconBlockHeader - - -class Eth1Block(Container): - timestamp: uint64 - deposit_root: Root - deposit_count: uint64 - # All other eth1 block fields - - -class AggregateAndProof(Container): - aggregator_index: ValidatorIndex - aggregate: Attestation - selection_proof: BLSSignature - - -class SignedAggregateAndProof(Container): - message: AggregateAndProof - signature: BLSSignature - - -class SyncAggregate(Container): - sync_committee_bits: Bitvector[SYNC_COMMITTEE_SIZE] - sync_committee_signature: BLSSignature - - -class SyncCommittee(Container): - pubkeys: Vector[BLSPubkey, SYNC_COMMITTEE_SIZE] - aggregate_pubkey: BLSPubkey - - -class SyncCommitteeMessage(Container): - # Slot to which this contribution pertains - slot: Slot - # Block root for this signature - beacon_block_root: Root - # Index of the validator that produced this signature - validator_index: ValidatorIndex - # Signature by the validator over the block root of `slot` - signature: BLSSignature - - -class SyncCommitteeContribution(Container): - # Slot to which this contribution pertains - slot: Slot - # Block root for this contribution - beacon_block_root: Root - # The subcommittee this contribution pertains to out of the broader sync committee - subcommittee_index: uint64 - # A bit is set if a signature from the validator at the corresponding - # index in the subcommittee is present in the aggregate `signature`. - aggregation_bits: Bitvector[SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT] - # Signature by the validator(s) over the block root of `slot` - signature: BLSSignature - - -class ContributionAndProof(Container): - aggregator_index: ValidatorIndex - contribution: SyncCommitteeContribution - selection_proof: BLSSignature - - -class SignedContributionAndProof(Container): - message: ContributionAndProof - signature: BLSSignature - - -class SyncAggregatorSelectionData(Container): - slot: Slot - subcommittee_index: uint64 - - -class LightClientSnapshot(Container): - # Beacon block header - header: BeaconBlockHeader - # Sync committees corresponding to the header - current_sync_committee: SyncCommittee - next_sync_committee: SyncCommittee - - -class LightClientUpdate(Container): - # Update beacon block header - header: BeaconBlockHeader - # Next sync committee corresponding to the header - next_sync_committee: SyncCommittee - next_sync_committee_branch: Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_INDEX)] - # Finality proof for the update header - finality_header: BeaconBlockHeader - finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)] - # Sync committee aggregate signature - sync_committee_bits: Bitvector[SYNC_COMMITTEE_SIZE] - sync_committee_signature: BLSSignature - # Fork version for the aggregate signature - fork_version: Version - - -class ExecutionPayload(Container): - # Execution block header fields - parent_hash: Hash32 - fee_recipient: ExecutionAddress # 'beneficiary' in the yellow paper - state_root: Bytes32 - receipt_root: Bytes32 # 'receipts root' in the yellow paper - logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] - random: Bytes32 # 'difficulty' in the yellow paper - block_number: uint64 # 'number' in the yellow paper - gas_limit: uint64 - gas_used: uint64 - timestamp: uint64 - extra_data: ByteList[MAX_EXTRA_DATA_BYTES] - base_fee_per_gas: uint256 - # Extra payload fields - block_hash: Hash32 # Hash of execution block - transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD] - - -class BeaconBlockBody(Container): - randao_reveal: BLSSignature - eth1_data: Eth1Data # Eth1 data vote - graffiti: Bytes32 # Arbitrary data - # Operations - proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] - attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS] - attestations: List[Attestation, MAX_ATTESTATIONS] - deposits: List[Deposit, MAX_DEPOSITS] - voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS] - sync_aggregate: SyncAggregate - # Execution - execution_payload: ExecutionPayload # [New in Merge] - - -class BeaconBlock(Container): - slot: Slot - proposer_index: ValidatorIndex - parent_root: Root - state_root: Root - body: BeaconBlockBody - - -class SignedBeaconBlock(Container): - message: BeaconBlock - signature: BLSSignature - - -class ExecutionPayloadHeader(Container): - # Execution block header fields - parent_hash: Hash32 - fee_recipient: ExecutionAddress - state_root: Bytes32 - receipt_root: Bytes32 - logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] - random: Bytes32 - block_number: uint64 - gas_limit: uint64 - gas_used: uint64 - timestamp: uint64 - extra_data: ByteList[MAX_EXTRA_DATA_BYTES] - base_fee_per_gas: uint256 - # Extra payload fields - block_hash: Hash32 # Hash of execution block - transactions_root: Root - - -class PowBlock(Container): - block_hash: Hash32 - parent_hash: Hash32 - total_difficulty: uint256 - - -class WithdrawalReceipt(Container): - index: WithdrawalReceiptIndex - address: ExecutionAddress - amount: Gwei - - -class BeaconState(Container): - # Versioning - genesis_time: uint64 - genesis_validators_root: Root - slot: Slot - fork: Fork - # History - latest_block_header: BeaconBlockHeader - block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] - # Eth1 - eth1_data: Eth1Data - eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] - eth1_deposit_index: uint64 - # Registry - validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] - balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] - # Randomness - randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] - # Slashings - slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances - # Participation - previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - # Finality - justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch - previous_justified_checkpoint: Checkpoint - current_justified_checkpoint: Checkpoint - finalized_checkpoint: Checkpoint - # Inactivity - inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] - # Sync - current_sync_committee: SyncCommittee - next_sync_committee: SyncCommittee - # Execution - latest_execution_payload_header: ExecutionPayloadHeader - # Withdrawals - withdrawal_receipts: List[WithdrawalReceipt, WITHDRAWAL_RECEIPT_LIMIT] # [New in Cappela] - - -@dataclass(eq=True, frozen=True) -class LatestMessage(object): - epoch: Epoch - root: Root - - -@dataclass -class Store(object): - time: uint64 - genesis_time: uint64 - justified_checkpoint: Checkpoint - finalized_checkpoint: Checkpoint - best_justified_checkpoint: Checkpoint - proposer_boost_root: Root - blocks: Dict[Root, BeaconBlock] = field(default_factory=dict) - block_states: Dict[Root, BeaconState] = field(default_factory=dict) - checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict) - latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict) - - -@dataclass -class LightClientStore(object): - snapshot: LightClientSnapshot - valid_updates: Set[LightClientUpdate] - - -@dataclass -class PayloadAttributes(object): - timestamp: uint64 - random: Bytes32 - suggested_fee_recipient: ExecutionAddress - - -class ExecutionEngine(Protocol): - - def execute_payload(self, execution_payload: ExecutionPayload) -> bool: - """ - Return ``True`` if and only if ``execution_payload`` is valid with respect to ``self.execution_state``. - """ - ... - - def notify_forkchoice_updated(self, - head_block_hash: Hash32, - finalized_block_hash: Hash32, - payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]: - ... - - def get_payload(self, payload_id: PayloadId) -> ExecutionPayload: - """ - Return ``execution_payload`` object. - """ - ... - - -def integer_squareroot(n: uint64) -> uint64: - """ - Return the largest integer ``x`` such that ``x**2 <= n``. - """ - x = n - y = (x + 1) // 2 - while y < x: - x = y - y = (x + n // x) // 2 - return x - - -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)) - - -def bytes_to_uint64(data: bytes) -> uint64: - """ - Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian. - """ - return uint64(int.from_bytes(data, ENDIANNESS)) - - -def is_active_validator(validator: Validator, epoch: Epoch) -> bool: - """ - Check if ``validator`` is active. - """ - return validator.activation_epoch <= epoch < validator.exit_epoch - - -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 - ) - - -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 - ) - - -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) - - -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) - ) - - -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) - - -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 - - -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 - - -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 - - -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)] - - -def compute_epoch_at_slot(slot: Slot) -> Epoch: - """ - Return the epoch number at ``slot``. - """ - return Epoch(slot // SLOTS_PER_EPOCH) - - -def compute_start_slot_at_epoch(epoch: Epoch) -> Slot: - """ - Return the start slot of ``epoch``. - """ - return Slot(epoch * SLOTS_PER_EPOCH) - - -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) - - -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, - )) - - -def compute_fork_digest(current_version: Version, genesis_validators_root: Root) -> ForkDigest: - """ - Return the 4-byte fork digest for the ``current_version`` and ``genesis_validators_root``. - This is a digest primarily used for domain separation on the p2p layer. - 4-bytes suffices for practical separation of forks/chains. - """ - return ForkDigest(compute_fork_data_root(current_version, genesis_validators_root)[:4]) - - -def compute_domain(domain_type: DomainType, fork_version: Version=None, genesis_validators_root: Root=None) -> Domain: - """ - Return the domain for the ``domain_type`` and ``fork_version``. - """ - if fork_version is None: - fork_version = config.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]) - - -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, - )) - - -def get_current_epoch(state: BeaconState) -> Epoch: - """ - Return the current epoch. - """ - return compute_epoch_at_slot(state.slot) - - -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) - - -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)) - - -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] - - -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] - - -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)] - - -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(config.MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // config.CHURN_LIMIT_QUOTIENT) - - -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) - - -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, - )) - - -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, - ) - - -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) - - -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, afterwhich this overflows uint64. - """ - return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, sum([state.validators[index].effective_balance for index in indices]))) - - -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)))) - - -def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch=None) -> Domain: - """ - Return the signature domain (fork version concatenated with domain type) of a message. - """ - epoch = get_current_epoch(state) if epoch is None else epoch - fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version - return compute_domain(domain_type, fork_version, state.genesis_validators_root) - - -def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> IndexedAttestation: - """ - Return the indexed attestation corresponding to ``attestation``. - """ - attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) - - return IndexedAttestation( - attesting_indices=sorted(attesting_indices), - data=attestation.data, - signature=attestation.signature, - ) - - -def get_attesting_indices(state: BeaconState, - data: AttestationData, - bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]: - """ - Return the set of attesting indices corresponding to ``data`` and ``bits``. - """ - committee = get_beacon_committee(state, data.slot, data.index) - return set(index for i, index in enumerate(committee) if bits[i]) - - -def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: - """ - Increase the validator balance at index ``index`` by ``delta``. - """ - state.balances[index] += delta - - -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 - - -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 + config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY) - - -def slash_validator(state: BeaconState, - slashed_index: ValidatorIndex, - whistleblower_index: ValidatorIndex=None) -> None: - """ - Slash the validator with index ``slashed_index``. - """ - epoch = get_current_epoch(state) - initiate_validator_exit(state, slashed_index) - validator = state.validators[slashed_index] - validator.slashed = True - validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR)) - state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance - slashing_penalty = validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_MERGE # [Modified in Merge] - 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)) - - -def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, - eth1_timestamp: uint64, - deposits: Sequence[Deposit], - execution_payload_header: ExecutionPayloadHeader=ExecutionPayloadHeader() - ) -> BeaconState: - fork = Fork( - previous_version=config.MERGE_FORK_VERSION, # [Modified in Merge] for testing only - current_version=config.MERGE_FORK_VERSION, # [Modified in Merge] - epoch=GENESIS_EPOCH, - ) - state = BeaconState( - genesis_time=eth1_timestamp + config.GENESIS_DELAY, - fork=fork, - eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), - 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) - - # Fill in sync committees - # Note: A duplicate committee is assigned for the current and next committee at genesis - state.current_sync_committee = get_next_sync_committee(state) - state.next_sync_committee = get_next_sync_committee(state) - - # [New in Merge] Initialize the execution payload header - # If empty, will initialize a chain that has not yet gone through the Merge transition - state.latest_execution_payload_header = execution_payload_header - - return state - - -def is_valid_genesis_state(state: BeaconState) -> bool: - if state.genesis_time < config.MIN_GENESIS_TIME: - return False - if len(get_active_validator_indices(state, GENESIS_EPOCH)) < config.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: - return False - return True - - -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) - - -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) - - -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) - - -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 - - -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) - process_historical_roots_update(state) - process_participation_flag_updates(state) - process_sync_committee_updates(state) - process_withdrawals(state) # [New in Cappela] - - -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 - - -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) - ] - - -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) - ] - - -def get_unslashed_attesting_indices(state: BeaconState, - attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]: - output = set() # type: Set[ValidatorIndex] - for a in attestations: - output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits)) - return set(filter(lambda index: not state.validators[index].slashed, output)) - - -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)) - - -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) - - -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 - - -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)) - - -def get_proposer_reward(state: BeaconState, attesting_index: ValidatorIndex) -> Gwei: - return Gwei(get_base_reward(state, attesting_index) // PROPOSER_REWARD_QUOTIENT) - - -def get_finality_delay(state: BeaconState) -> uint64: - return get_previous_epoch(state) - state.finalized_checkpoint.epoch - - -def is_in_inactivity_leak(state: BeaconState) -> bool: - return get_finality_delay(state) > MIN_EPOCHS_TO_INACTIVITY_PENALTY - - -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) - ] - - -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 - - -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) - - -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) - - -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) - - -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.data, a.aggregation_bits) - ], 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 - - -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 Merge] - penalty_denominator = config.INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_MERGE - penalties[index] += Gwei(penalty_numerator // penalty_denominator) - return rewards, penalties - - -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 - - -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]) - - -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 <= config.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)) - - -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_MERGE, # [Modified in Merge] - 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) - - -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 = [] - - -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) - - -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) - - -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) - - -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)) - - -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 = [] - - -def process_block(state: BeaconState, block: BeaconBlock) -> None: - process_block_header(state, block) - if is_execution_enabled(state, block.body): - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Merge] - process_randao(state, block.body) - process_eth1_data(state, block.body) - process_operations(state, block.body) - process_sync_aggregate(state, block.body.sync_aggregate) - - -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 - - -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 - - -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 - - -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) - - -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) - - -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 - - -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, data, attestation.aggregation_bits): - 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) - - -def get_validator_from_deposit(state: BeaconState, deposit: Deposit) -> Validator: - amount = deposit.data.amount - effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) - - return Validator( - pubkey=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - activation_eligibility_epoch=FAR_FUTURE_EPOCH, - activation_epoch=FAR_FUTURE_EPOCH, - exit_epoch=FAR_FUTURE_EPOCH, - withdrawable_epoch=FAR_FUTURE_EPOCH, - effective_balance=effective_balance, - ) - - -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, - depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in - index=state.eth1_deposit_index, - root=state.eth1_data.deposit_root, - ) - - # Deposits must be processed in order - state.eth1_deposit_index += 1 - - pubkey = deposit.data.pubkey - amount = deposit.data.amount - validator_pubkeys = [validator.pubkey for validator 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=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - amount=deposit.data.amount, - ) - domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks - signing_root = compute_signing_root(deposit_message, domain) - # Initialize validator if the deposit signature is valid - if bls.Verify(pubkey, signing_root, deposit.data.signature): - state.validators.append(get_validator_from_deposit(state, deposit)) - state.balances.append(amount) - state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) - state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) - state.inactivity_scores.append(uint64(0)) - else: - # Increase balance by deposit amount - index = ValidatorIndex(validator_pubkeys.index(pubkey)) - increase_balance(state, index, amount) - - -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 + config.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) - - -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 + config.SECONDS_PER_SLOT * anchor_state.slot), - genesis_time=anchor_state.genesis_time, - justified_checkpoint=justified_checkpoint, - finalized_checkpoint=finalized_checkpoint, - best_justified_checkpoint=justified_checkpoint, - proposer_boost_root=proposer_boost_root, - blocks={anchor_root: copy(anchor_block)}, - block_states={anchor_root: copy(anchor_state)}, - checkpoint_states={justified_checkpoint: copy(anchor_state)}, - ) - - -def get_slots_since_genesis(store: Store) -> int: - return (store.time - store.genesis_time) // config.SECONDS_PER_SLOT - - -def get_current_slot(store: Store) -> Slot: - return Slot(GENESIS_SLOT + get_slots_since_genesis(store)) - - -def compute_slots_since_epoch_start(slot: Slot) -> int: - return slot - compute_start_slot_at_epoch(compute_epoch_at_slot(slot)) - - -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) - elif block.slot == slot: - return root - else: - # root is older than queried slot, thus a skip slot. Return most recent root prior to slot - return root - - -def get_latest_attesting_balance(store: Store, root: Root) -> Gwei: - state = store.checkpoint_states[store.justified_checkpoint] - active_indices = get_active_validator_indices(state, get_current_epoch(state)) - attestation_score = Gwei(sum( - state.validators[i].effective_balance for i in active_indices - if (i in store.latest_messages - and get_ancestor(store, store.latest_messages[i].root, store.blocks[root].slot) == root) - )) - proposer_score = Gwei(0) - if store.proposer_boost_root != Root(): - block = store.blocks[root] - if get_ancestor(store, root, block.slot) == store.proposer_boost_root: - num_validators = len(get_active_validator_indices(state, get_current_epoch(state))) - avg_balance = get_total_active_balance(state) // num_validators - committee_size = num_validators // SLOTS_PER_EPOCH - committee_weight = committee_size * avg_balance - proposer_score = (committee_weight * config.PROPOSER_SCORE_BOOST) // 100 - return attestation_score + proposer_score - - -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 - - # If leaf block, check finalized/justified checkpoints as matching latest. - head_state = store.block_states[block_root] - - correct_justified = ( - store.justified_checkpoint.epoch == GENESIS_EPOCH - or head_state.current_justified_checkpoint == store.justified_checkpoint - ) - correct_finalized = ( - store.finalized_checkpoint.epoch == GENESIS_EPOCH - or head_state.finalized_checkpoint == store.finalized_checkpoint - ) - # 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 - - -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 - - -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 - head = max(children, key=lambda root: (get_latest_attesting_balance(store, root), root)) - - -def should_update_justified_checkpoint(store: Store, new_justified_checkpoint: Checkpoint) -> bool: - """ - To address the bouncing attack, only update conflicting justified - checkpoints in the fork choice if in the early slots of the epoch. - Otherwise, delay incorporation of new justified checkpoint until next epoch boundary. - - See https://ethresear.ch/t/prevention-of-bouncing-attack-on-ffg/6114 for more detailed analysis and discussion. - """ - if compute_slots_since_epoch_start(get_current_slot(store)) < SAFE_SLOTS_TO_UPDATE_JUSTIFIED: - return True - - justified_slot = compute_start_slot_at_epoch(store.justified_checkpoint.epoch) - if not get_ancestor(store, new_justified_checkpoint.root, justified_slot) == store.justified_checkpoint.root: - return False - - return True - - -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 = compute_epoch_at_slot(get_current_slot(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] - - -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) - - # Attestations target be for a known block. If target block is unknown, delay consideration until the 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 - target_slot = compute_start_slot_at_epoch(target.epoch) - assert target.root == get_ancestor(store, attestation.data.beacon_block_root, target_slot) - - # 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 - - -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 - - -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 - for i in 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) - - -def on_tick(store: Store, time: uint64) -> None: - previous_slot = get_current_slot(store) - - # update store time - store.time = time - - current_slot = get_current_slot(store) - - # Reset store.proposer_boost_root if this is a new slot - if current_slot > previous_slot: - store.proposer_boost_root = Root() - - # Not a new epoch, return - if not (current_slot > previous_slot and compute_slots_since_epoch_start(current_slot) == 0): - return - - # Update store.justified_checkpoint if a better checkpoint on the store.finalized_checkpoint chain - if store.best_justified_checkpoint.epoch > store.justified_checkpoint.epoch: - finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) - ancestor_at_finalized_slot = get_ancestor(store, store.best_justified_checkpoint.root, finalized_slot) - if ancestor_at_finalized_slot == store.finalized_checkpoint.root: - store.justified_checkpoint = store.best_justified_checkpoint - - -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 the 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 - assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root - - # Check the block is valid and compute the post-state - state = pre_state.copy() - state_transition(state, signed_block, True) - - # [New in Merge] - if is_merge_transition_block(pre_state, block.body): - validate_merge_block(block) - - # Add new block to the store - store.blocks[hash_tree_root(block)] = block - # Add new state for this block to the store - store.block_states[hash_tree_root(block)] = state - - # Add proposer score boost if the block is timely - time_into_slot = (store.time - store.genesis_time) % config.SECONDS_PER_SLOT - is_before_attesting_interval = time_into_slot < config.SECONDS_PER_SLOT // INTERVALS_PER_SLOT - if get_current_slot(store) == block.slot and is_before_attesting_interval: - store.proposer_boost_root = hash_tree_root(block) - - # Update justified checkpoint - if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch: - if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch: - store.best_justified_checkpoint = state.current_justified_checkpoint - if should_update_justified_checkpoint(store, state.current_justified_checkpoint): - store.justified_checkpoint = state.current_justified_checkpoint - - # Update finalized checkpoint - if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch: - store.finalized_checkpoint = state.finalized_checkpoint - store.justified_checkpoint = state.current_justified_checkpoint - - -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) - - -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)) - - -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 - - -def is_proposer(state: BeaconState, validator_index: ValidatorIndex) -> bool: - return get_beacon_proposer_index(state) == validator_index - - -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) - - -def compute_time_at_slot(state: BeaconState, slot: Slot) -> uint64: - return uint64(state.genesis_time + slot * config.SECONDS_PER_SLOT) - - -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) - - -def is_candidate_block(block: Eth1Block, period_start: uint64) -> bool: - return ( - block.timestamp + config.SECONDS_PER_ETH1_BLOCK * config.ETH1_FOLLOW_DISTANCE <= period_start - and block.timestamp + config.SECONDS_PER_ETH1_BLOCK * config.ETH1_FOLLOW_DISTANCE * 2 >= period_start - ) - - -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, - key=lambda v: (valid_votes.count(v), -valid_votes.index(v)), # Tiebreak by smallest distance - default=default_vote - ) - - -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) - - -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) - - -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) - - -def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, committee_index: CommitteeIndex) -> uint64: - """ - 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 uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT) - - -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) - - -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 - - -def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature: - signatures = [attestation.signature for attestation in attestations] - return bls.Aggregate(signatures) - - -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), - ) - - -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) - - -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 = config.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 - - -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 ws_state.latest_block_header.state_root == 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 - - -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 - - -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 - - -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 - - -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) - - -def get_base_reward_per_increment(state: BeaconState) -> Gwei: - return Gwei(EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR // integer_squareroot(get_total_active_balance(state))) - - -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)) - - -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. - """ - if data.target.epoch == get_current_epoch(state): - justified_checkpoint = state.current_justified_checkpoint - else: - justified_checkpoint = state.previous_justified_checkpoint - - # Matching roots - is_matching_source = data.source == justified_checkpoint - is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch) - is_matching_head = is_matching_target and data.beacon_block_root == get_block_root_at_slot(state, data.slot) - 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 - - -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 - - -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 - 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) - 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) - - -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] += config.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(config.INACTIVITY_SCORE_RECOVERY_RATE, state.inactivity_scores[index]) - - -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))] - - -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) - - -def eth_aggregate_pubkeys(pubkeys: Sequence[BLSPubkey]) -> BLSPubkey: - return bls.AggregatePKs(pubkeys) - - -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) - - -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, data, attestation.aggregation_bits): - for flag_index in participation_flag_indices: - epoch_participation[index] = add_flag(epoch_participation[index], flag_index) - - -def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState: - epoch = phase0.get_current_epoch(pre) - post = BeaconState( - # Versioning - 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=config.ALTAIR_FORK_VERSION, - epoch=epoch, - ), - # History - latest_block_header=pre.latest_block_header, - block_roots=pre.block_roots, - state_roots=pre.state_roots, - historical_roots=pre.historical_roots, - # Eth1 - eth1_data=pre.eth1_data, - eth1_data_votes=pre.eth1_data_votes, - eth1_deposit_index=pre.eth1_deposit_index, - # Registry - validators=pre.validators, - balances=pre.balances, - # Randomness - randao_mixes=pre.randao_mixes, - # Slashings - slashings=pre.slashings, - # Participation - previous_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))], - current_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))], - # Finality - 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 - 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 - - -def compute_sync_committee_period(epoch: Epoch) -> uint64: - return epoch // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - - -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 - - -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 - - -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, - ) - - -def compute_subnets_for_sync_committee(state: BeaconState, validator_index: ValidatorIndex) -> Set[uint64]: - 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([ - uint64(index // (SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT)) - for index in sync_committee_indices - ]) - - -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) - - -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 - - -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, - ) - - -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) - - -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] - - -def get_subtree_index(generalized_index: GeneralizedIndex) -> uint64: - return uint64(generalized_index % 2**(floorlog2(generalized_index))) - - -def validate_light_client_update(snapshot: LightClientSnapshot, - update: LightClientUpdate, - genesis_validators_root: Root) -> None: - # Verify update slot is larger than snapshot slot - assert update.header.slot > snapshot.header.slot - - # Verify update does not skip a sync committee period - snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - assert update_period in (snapshot_period, snapshot_period + 1) - - # Verify update header root is the finalized root of the finality header, if specified - if update.finality_header == BeaconBlockHeader(): - signed_header = update.header - assert update.finality_branch == [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))] - else: - signed_header = update.finality_header - assert is_valid_merkle_branch( - leaf=hash_tree_root(update.header), - branch=update.finality_branch, - depth=floorlog2(FINALIZED_ROOT_INDEX), - index=get_subtree_index(FINALIZED_ROOT_INDEX), - root=update.finality_header.state_root, - ) - - # Verify update next sync committee if the update period incremented - if update_period == snapshot_period: - sync_committee = snapshot.current_sync_committee - assert update.next_sync_committee_branch == [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))] - else: - sync_committee = snapshot.next_sync_committee - assert is_valid_merkle_branch( - leaf=hash_tree_root(update.next_sync_committee), - branch=update.next_sync_committee_branch, - depth=floorlog2(NEXT_SYNC_COMMITTEE_INDEX), - index=get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX), - root=update.header.state_root, - ) - - # Verify sync committee has sufficient participants - assert sum(update.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS - - # Verify sync committee aggregate signature - participant_pubkeys = [pubkey for (bit, pubkey) in zip(update.sync_committee_bits, sync_committee.pubkeys) if bit] - domain = compute_domain(DOMAIN_SYNC_COMMITTEE, update.fork_version, genesis_validators_root) - signing_root = compute_signing_root(signed_header, domain) - assert bls.FastAggregateVerify(participant_pubkeys, signing_root, update.sync_committee_signature) - - -def apply_light_client_update(snapshot: LightClientSnapshot, update: LightClientUpdate) -> None: - snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - if update_period == snapshot_period + 1: - snapshot.current_sync_committee = snapshot.next_sync_committee - snapshot.next_sync_committee = update.next_sync_committee - snapshot.header = update.header - - -def process_light_client_update(store: LightClientStore, update: LightClientUpdate, current_slot: Slot, - genesis_validators_root: Root) -> None: - validate_light_client_update(store.snapshot, update, genesis_validators_root) - store.valid_updates.add(update) - - update_timeout = SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD - if ( - sum(update.sync_committee_bits) * 3 >= len(update.sync_committee_bits) * 2 - and update.finality_header != BeaconBlockHeader() - ): - # Apply update if (1) 2/3 quorum is reached and (2) we have a finality proof. - # Note that (2) means that the current light client design needs finality. - # It may be changed to re-organizable light client design. See the on-going issue consensus-specs#2182. - apply_light_client_update(store.snapshot, update) - store.valid_updates = set() - elif current_slot > store.snapshot.header.slot + update_timeout: - # Forced best update when the update timeout has elapsed - apply_light_client_update(store.snapshot, - max(store.valid_updates, key=lambda update: sum(update.sync_committee_bits))) - store.valid_updates = set() - - -def is_merge_transition_complete(state: BeaconState) -> bool: - return state.latest_execution_payload_header != ExecutionPayloadHeader() - - -def is_merge_transition_block(state: BeaconState, body: BeaconBlockBody) -> bool: - return not is_merge_transition_complete(state) and body.execution_payload != ExecutionPayload() - - -def is_execution_enabled(state: BeaconState, body: BeaconBlockBody) -> bool: - return is_merge_transition_block(state, body) or is_merge_transition_complete(state) - - -def compute_timestamp_at_slot(state: BeaconState, slot: Slot) -> uint64: - slots_since_genesis = slot - GENESIS_SLOT - return uint64(state.genesis_time + slots_since_genesis * config.SECONDS_PER_SLOT) - - -def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: - # 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 random - assert payload.random == get_randao_mix(state, get_current_epoch(state)) - # Verify timestamp - assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) - # Verify the execution payload is valid - assert execution_engine.execute_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, - receipt_root=payload.receipt_root, - logs_bloom=payload.logs_bloom, - random=payload.random, - 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), - ) - - -def upgrade_to_merge(pre: altair.BeaconState) -> BeaconState: - epoch = altair.get_current_epoch(pre) - post = BeaconState( - # Versioning - 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=config.MERGE_FORK_VERSION, - epoch=epoch, - ), - # History - latest_block_header=pre.latest_block_header, - block_roots=pre.block_roots, - state_roots=pre.state_roots, - historical_roots=pre.historical_roots, - # Eth1 - eth1_data=pre.eth1_data, - eth1_data_votes=pre.eth1_data_votes, - eth1_deposit_index=pre.eth1_deposit_index, - # Registry - validators=pre.validators, - balances=pre.balances, - # Randomness - randao_mixes=pre.randao_mixes, - # Slashings - slashings=pre.slashings, - # Participation - previous_epoch_participation=pre.previous_epoch_participation, - current_epoch_participation=pre.current_epoch_participation, - # Finality - 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 - inactivity_scores=pre.inactivity_scores, - # Sync - current_sync_committee=pre.current_sync_committee, - next_sync_committee=pre.next_sync_committee, - # Execution-layer - latest_execution_payload_header=ExecutionPayloadHeader(), - ) - - return post - - -def is_valid_terminal_pow_block(block: PowBlock, parent: PowBlock) -> bool: - is_total_difficulty_reached = block.total_difficulty >= config.TERMINAL_TOTAL_DIFFICULTY - is_parent_total_difficulty_valid = parent.total_difficulty < config.TERMINAL_TOTAL_DIFFICULTY - return is_total_difficulty_reached and is_parent_total_difficulty_valid - - -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 config.TERMINAL_BLOCK_HASH != Hash32(): - # If `config.TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached. - assert compute_epoch_at_slot(block.slot) >= config.TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH - assert block.body.execution_payload.parent_hash == config.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) - - -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 >= config.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 >= config.TERMINAL_TOTAL_DIFFICULTY - if not parent_reached_ttd: - return block - - return None - - -def get_terminal_pow_block(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlock]: - if config.TERMINAL_BLOCK_HASH != Hash32(): - # Terminal block hash override takes precedence over terminal total difficulty - if config.TERMINAL_BLOCK_HASH in pow_chain: - return pow_chain[config.TERMINAL_BLOCK_HASH] - else: - return None - - return get_pow_block_at_terminal_total_difficulty(pow_chain) - - -def prepare_execution_payload(state: BeaconState, - pow_chain: Dict[Hash32, PowBlock], - finalized_block_hash: Hash32, - suggested_fee_recipient: ExecutionAddress, - execution_engine: ExecutionEngine) -> Optional[PayloadId]: - if not is_merge_transition_complete(state): - is_terminal_block_hash_set = config.TERMINAL_BLOCK_HASH != Hash32() - is_activation_epoch_reached = get_current_epoch(state) >= config.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_timestamp_at_slot(state, state.slot), - random=get_randao_mix(state, get_current_epoch(state)), - suggested_fee_recipient=suggested_fee_recipient, - ) - return execution_engine.notify_forkchoice_updated(parent_hash, finalized_block_hash, payload_attributes) - - -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) - - -def withdraw(state: BeaconState, index: ValidatorIndex, amount: Gwei) -> None: - # Decrease the validator's balance - decrease_balance(state, index, amount) - # Create a corresponding withdrawal receipt - receipt = WithdrawalReceipt( - index=WithdrawalReceiptIndex(len(state.withdrawal_receipts)), - address=state.validators[index].withdrawal_credentials[12:], - amount=amount, - ) - state.withdrawal_receipts.append(receipt) - - -def is_withdrawable_validator(validator: Validator, epoch: Epoch) -> bool: - """ - Check if ``validator`` is withdrawable. - """ - return validator.withdrawable_epoch <= epoch - - -def process_withdrawals(state: BeaconState) -> None: - current_epoch = get_current_epoch(state) - for index, validator in enumerate(state.validators): - balance = state.balances[index] - is_balance_nonzero = state.balances[index] == 0 - is_eth1_withdrawal_prefix = validator.withdrawal_credentials[0] != ETH1_ADDRESS_WITHDRAWAL_PREFIX - if is_balance_nonzero and is_eth1_withdrawal_prefix and is_withdrawable_validator(validator, current_epoch): - withdraw(state, ValidatorIndex(index), balance) - - -def upgrade_to_capella(pre: merge.BeaconState) -> BeaconState: - epoch = merge.get_current_epoch(pre) - post = BeaconState( - # Versioning - 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=config.CAPELLA_FORK_VERSION, - epoch=epoch, - ), - # History - latest_block_header=pre.latest_block_header, - block_roots=pre.block_roots, - state_roots=pre.state_roots, - historical_roots=pre.historical_roots, - # Eth1 - eth1_data=pre.eth1_data, - eth1_data_votes=pre.eth1_data_votes, - eth1_deposit_index=pre.eth1_deposit_index, - # Registry - validators=pre.validators, - balances=pre.balances, - # Randomness - randao_mixes=pre.randao_mixes, - # Slashings - slashings=pre.slashings, - # Participation - previous_epoch_participation=pre.previous_epoch_participation, - current_epoch_participation=pre.current_epoch_participation, - # Finality - 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 - inactivity_scores=pre.inactivity_scores, - # Sync - current_sync_committee=pre.current_sync_committee, - next_sync_committee=pre.next_sync_committee, - # Execution-layer - latest_execution_payload_header=pre.latest_execution_payload_header, - # Withdrawals - withdrawal_receipts=[], - ) - - return post - - -def get_eth1_data(block: Eth1Block) -> Eth1Data: - """ - A stub function return mocking Eth1Data. - """ - return Eth1Data( - deposit_root=block.deposit_root, - deposit_count=block.deposit_count, - block_hash=hash_tree_root(block)) - - -def cache_this(key_fn, value_fn, lru_size): # type: ignore - cache_dict = LRU(size=lru_size) - - def wrapper(*args, **kw): # type: ignore - key = key_fn(*args, **kw) - nonlocal cache_dict - if key not in cache_dict: - cache_dict[key] = value_fn(*args, **kw) - return cache_dict[key] - return wrapper - - -_compute_shuffled_index = compute_shuffled_index -compute_shuffled_index = cache_this( - lambda index, index_count, seed: (index, index_count, seed), - _compute_shuffled_index, lru_size=SLOTS_PER_EPOCH * 3) - -_get_total_active_balance = get_total_active_balance -get_total_active_balance = cache_this( - lambda state: (state.validators.hash_tree_root(), compute_epoch_at_slot(state.slot)), - _get_total_active_balance, lru_size=10) - -_get_base_reward = get_base_reward -get_base_reward = cache_this( - lambda state, index: (state.validators.hash_tree_root(), state.slot, index), - _get_base_reward, lru_size=2048) - -_get_committee_count_per_slot = get_committee_count_per_slot -get_committee_count_per_slot = cache_this( - lambda state, epoch: (state.validators.hash_tree_root(), epoch), - _get_committee_count_per_slot, lru_size=SLOTS_PER_EPOCH * 3) - -_get_active_validator_indices = get_active_validator_indices -get_active_validator_indices = cache_this( - lambda state, epoch: (state.validators.hash_tree_root(), epoch), - _get_active_validator_indices, lru_size=3) - -_get_beacon_committee = get_beacon_committee -get_beacon_committee = cache_this( - lambda state, slot, index: (state.validators.hash_tree_root(), state.randao_mixes.hash_tree_root(), slot, index), - _get_beacon_committee, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3) - -_get_matching_target_attestations = get_matching_target_attestations -get_matching_target_attestations = cache_this( - lambda state, epoch: (state.hash_tree_root(), epoch), - _get_matching_target_attestations, lru_size=10) - -_get_matching_head_attestations = get_matching_head_attestations -get_matching_head_attestations = cache_this( - lambda state, epoch: (state.hash_tree_root(), epoch), - _get_matching_head_attestations, lru_size=10) - -_get_attesting_indices = get_attesting_indices -get_attesting_indices = cache_this( - lambda state, data, bits: ( - state.randao_mixes.hash_tree_root(), - state.validators.hash_tree_root(), data.hash_tree_root(), bits.hash_tree_root() - ), - _get_attesting_indices, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3) - - -def get_generalized_index(ssz_class: Any, *path: Sequence[PyUnion[int, SSZVariableName]]) -> GeneralizedIndex: - ssz_path = Path(ssz_class) - for item in path: - ssz_path = ssz_path / item - return GeneralizedIndex(ssz_path.gindex()) - - -ExecutionState = Any - - -def get_pow_block(hash: Bytes32) -> Optional[PowBlock]: - return PowBlock(block_hash=hash, parent_hash=Bytes32(), total_difficulty=uint256(0)) - - -def get_execution_state(execution_state_root: Bytes32) -> ExecutionState: - pass - - -def get_pow_chain_head() -> PowBlock: - pass - - -class NoopExecutionEngine(ExecutionEngine): - - def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: - return True - - def notify_forkchoice_updated(self: ExecutionEngine, - head_block_hash: Hash32, - finalized_block_hash: Hash32, - payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]: - pass - - def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload: - raise NotImplementedError("no default block production") - - -EXECUTION_ENGINE = NoopExecutionEngine() - - -assert FINALIZED_ROOT_INDEX == get_generalized_index(BeaconState, 'finalized_checkpoint', 'root') -assert NEXT_SYNC_COMMITTEE_INDEX == get_generalized_index(BeaconState, 'next_sync_committee') diff --git a/tests/core/pyspec/eth2spec/withdrawals/mainnet.py b/tests/core/pyspec/eth2spec/withdrawals/mainnet.py deleted file mode 100644 index d7c9ce43f..000000000 --- a/tests/core/pyspec/eth2spec/withdrawals/mainnet.py +++ /dev/null @@ -1,3018 +0,0 @@ -from lru import LRU -from dataclasses import ( - dataclass, - field, -) -from typing import ( - Any, Callable, Dict, Set, Sequence, Tuple, Optional, TypeVar, NamedTuple -) - -from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy, uint_to_bytes -from eth2spec.utils.ssz.ssz_typing import ( - View, boolean, Container, List, Vector, uint8, uint32, uint64, - Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist) -from eth2spec.utils.ssz.ssz_typing import Bitvector # noqa: F401 -from eth2spec.utils import bls -from eth2spec.utils.hash_function import hash - - -from typing import NewType, Union as PyUnion - -from eth2spec.phase0 import mainnet as phase0 -from eth2spec.utils.ssz.ssz_typing import Path - -from typing import Protocol -from eth2spec.altair import mainnet as altair -from eth2spec.utils.ssz.ssz_typing import Bytes8, Bytes20, ByteList, ByteVector, uint256 - -SSZObject = TypeVar('SSZObject', bound=View) - - -SSZVariableName = str -GeneralizedIndex = NewType('GeneralizedIndex', int) - - -fork = 'withdrawals' - - -MAX_BYTES_PER_TRANSACTION = uint64(2**30) - - -class Slot(uint64): - pass - - -class Epoch(uint64): - pass - - -class CommitteeIndex(uint64): - pass - - -class ValidatorIndex(uint64): - pass - - -class Gwei(uint64): - pass - - -class Root(Bytes32): - pass - - -class Hash32(Bytes32): - pass - - -class Version(Bytes4): - pass - - -class DomainType(Bytes4): - pass - - -class ForkDigest(Bytes4): - pass - - -class Domain(Bytes32): - pass - - -class BLSPubkey(Bytes48): - pass - - -class BLSSignature(Bytes96): - pass - - -class Ether(uint64): - pass - - -class ParticipationFlags(uint8): - pass - - -class ExecutionAddress(Bytes20): - pass - - -class PayloadId(Bytes8): - pass - - -class WithdrawalReceiptIndex(uint64): - pass - - -Transaction = ByteList[MAX_BYTES_PER_TRANSACTION] - - -def ceillog2(x: int) -> uint64: - if x < 1: - raise ValueError(f"ceillog2 accepts only positive values, x={x}") - return uint64((x - 1).bit_length()) - - -def floorlog2(x: int) -> uint64: - if x < 1: - raise ValueError(f"floorlog2 accepts only positive values, x={x}") - return uint64(x.bit_length() - 1) - - -FINALIZED_ROOT_INDEX = GeneralizedIndex(105) -NEXT_SYNC_COMMITTEE_INDEX = GeneralizedIndex(55) - -# Constant vars -GENESIS_SLOT = Slot(0) -GENESIS_EPOCH = Epoch(0) -FAR_FUTURE_EPOCH = Epoch(2**64 - 1) -BASE_REWARDS_PER_EPOCH = uint64(4) -DEPOSIT_CONTRACT_TREE_DEPTH = uint64(2**5) -JUSTIFICATION_BITS_LENGTH = uint64(4) -ENDIANNESS = 'little' -BLS_WITHDRAWAL_PREFIX = Bytes1('0x00') -ETH1_ADDRESS_WITHDRAWAL_PREFIX = Bytes1('0x01') -DOMAIN_BEACON_PROPOSER = DomainType('0x00000000') -DOMAIN_BEACON_ATTESTER = DomainType('0x01000000') -DOMAIN_RANDAO = DomainType('0x02000000') -DOMAIN_DEPOSIT = DomainType('0x03000000') -DOMAIN_VOLUNTARY_EXIT = DomainType('0x04000000') -DOMAIN_SELECTION_PROOF = DomainType('0x05000000') -DOMAIN_AGGREGATE_AND_PROOF = DomainType('0x06000000') -INTERVALS_PER_SLOT = uint64(3) -TARGET_AGGREGATORS_PER_COMMITTEE = 2**4 -RANDOM_SUBNETS_PER_VALIDATOR = 2**0 -EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION = 2**8 -ATTESTATION_SUBNET_COUNT = 64 -ETH_TO_GWEI = uint64(10**9) -SAFETY_DECAY = uint64(10) -TIMELY_SOURCE_FLAG_INDEX = 0 -TIMELY_TARGET_FLAG_INDEX = 1 -TIMELY_HEAD_FLAG_INDEX = 2 -TIMELY_SOURCE_WEIGHT = uint64(14) -TIMELY_TARGET_WEIGHT = uint64(26) -TIMELY_HEAD_WEIGHT = uint64(14) -SYNC_REWARD_WEIGHT = uint64(2) -PROPOSER_WEIGHT = uint64(8) -WEIGHT_DENOMINATOR = uint64(64) -DOMAIN_SYNC_COMMITTEE = DomainType('0x07000000') -DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF = DomainType('0x08000000') -DOMAIN_CONTRIBUTION_AND_PROOF = DomainType('0x09000000') -PARTICIPATION_FLAG_WEIGHTS = [TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT, TIMELY_HEAD_WEIGHT] -G2_POINT_AT_INFINITY = BLSSignature(b'\xc0' + b'\x00' * 95) -TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE = 2**4 -SYNC_COMMITTEE_SUBNET_COUNT = 4 -WITHDRAWAL_RECEIPT_LIMIT = uint64(2**40) - -# Preset vars -MAX_COMMITTEES_PER_SLOT = uint64(64) -TARGET_COMMITTEE_SIZE = uint64(128) -MAX_VALIDATORS_PER_COMMITTEE = uint64(2048) -SHUFFLE_ROUND_COUNT = uint64(90) -HYSTERESIS_QUOTIENT = uint64(4) -HYSTERESIS_DOWNWARD_MULTIPLIER = uint64(1) -HYSTERESIS_UPWARD_MULTIPLIER = uint64(5) -MIN_DEPOSIT_AMOUNT = Gwei(1000000000) -MAX_EFFECTIVE_BALANCE = Gwei(32000000000) -EFFECTIVE_BALANCE_INCREMENT = Gwei(1000000000) -MIN_ATTESTATION_INCLUSION_DELAY = uint64(1) -SLOTS_PER_EPOCH = uint64(32) -MIN_SEED_LOOKAHEAD = uint64(1) -MAX_SEED_LOOKAHEAD = uint64(4) -MIN_EPOCHS_TO_INACTIVITY_PENALTY = uint64(4) -EPOCHS_PER_ETH1_VOTING_PERIOD = uint64(64) -SLOTS_PER_HISTORICAL_ROOT = uint64(8192) -EPOCHS_PER_HISTORICAL_VECTOR = uint64(65536) -EPOCHS_PER_SLASHINGS_VECTOR = uint64(8192) -HISTORICAL_ROOTS_LIMIT = uint64(16777216) -VALIDATOR_REGISTRY_LIMIT = uint64(1099511627776) -BASE_REWARD_FACTOR = uint64(64) -WHISTLEBLOWER_REWARD_QUOTIENT = uint64(512) -PROPOSER_REWARD_QUOTIENT = uint64(8) -INACTIVITY_PENALTY_QUOTIENT = uint64(67108864) -MIN_SLASHING_PENALTY_QUOTIENT = uint64(128) -PROPORTIONAL_SLASHING_MULTIPLIER = uint64(1) -MAX_PROPOSER_SLASHINGS = 16 -MAX_ATTESTER_SLASHINGS = 2 -MAX_ATTESTATIONS = 128 -MAX_DEPOSITS = 16 -MAX_VOLUNTARY_EXITS = 16 -SAFE_SLOTS_TO_UPDATE_JUSTIFIED = 8 -INACTIVITY_PENALTY_QUOTIENT_ALTAIR = uint64(50331648) -MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR = uint64(64) -PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR = uint64(2) -SYNC_COMMITTEE_SIZE = uint64(512) -EPOCHS_PER_SYNC_COMMITTEE_PERIOD = uint64(256) -MIN_SYNC_COMMITTEE_PARTICIPANTS = 1 -MAX_BYTES_PER_TRANSACTION = uint64(1073741824) -MAX_TRANSACTIONS_PER_PAYLOAD = uint64(1048576) -BYTES_PER_LOGS_BLOOM = uint64(256) -MAX_EXTRA_DATA_BYTES = 32 -INACTIVITY_PENALTY_QUOTIENT_MERGE = uint64(16777216) -MIN_SLASHING_PENALTY_QUOTIENT_MERGE = uint64(32) -PROPORTIONAL_SLASHING_MULTIPLIER_MERGE = uint64(3) - - -class Configuration(NamedTuple): - PRESET_BASE: str - MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: uint64 - MIN_GENESIS_TIME: uint64 - GENESIS_FORK_VERSION: Version - GENESIS_DELAY: uint64 - SECONDS_PER_SLOT: uint64 - SECONDS_PER_ETH1_BLOCK: uint64 - MIN_VALIDATOR_WITHDRAWABILITY_DELAY: uint64 - SHARD_COMMITTEE_PERIOD: uint64 - ETH1_FOLLOW_DISTANCE: uint64 - EJECTION_BALANCE: Gwei - MIN_PER_EPOCH_CHURN_LIMIT: uint64 - CHURN_LIMIT_QUOTIENT: uint64 - PROPOSER_SCORE_BOOST: uint64 - INACTIVITY_SCORE_BIAS: uint64 - INACTIVITY_SCORE_RECOVERY_RATE: uint64 - ALTAIR_FORK_VERSION: Version - ALTAIR_FORK_EPOCH: Epoch - TERMINAL_TOTAL_DIFFICULTY: int - TERMINAL_BLOCK_HASH: Hash32 - TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: int - MERGE_FORK_VERSION: Version - MERGE_FORK_EPOCH: Epoch - - -config = Configuration( - PRESET_BASE="mainnet", - MIN_GENESIS_ACTIVE_VALIDATOR_COUNT=uint64(16384), - MIN_GENESIS_TIME=uint64(1606824000), - GENESIS_FORK_VERSION=Version('0x00000000'), - GENESIS_DELAY=uint64(604800), - SECONDS_PER_SLOT=uint64(12), - SECONDS_PER_ETH1_BLOCK=uint64(14), - MIN_VALIDATOR_WITHDRAWABILITY_DELAY=uint64(256), - SHARD_COMMITTEE_PERIOD=uint64(256), - ETH1_FOLLOW_DISTANCE=uint64(2048), - EJECTION_BALANCE=Gwei(16000000000), - MIN_PER_EPOCH_CHURN_LIMIT=uint64(4), - CHURN_LIMIT_QUOTIENT=uint64(65536), - PROPOSER_SCORE_BOOST=uint64(70), - INACTIVITY_SCORE_BIAS=uint64(4), - INACTIVITY_SCORE_RECOVERY_RATE=uint64(16), - ALTAIR_FORK_VERSION=Version('0x01000000'), - ALTAIR_FORK_EPOCH=Epoch(74240), - TERMINAL_TOTAL_DIFFICULTY=115792089237316195423570985008687907853269984665640564039457584007913129638912, - TERMINAL_BLOCK_HASH=Hash32('0x0000000000000000000000000000000000000000000000000000000000000000'), - TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH=18446744073709551615, - MERGE_FORK_VERSION=Version('0x02000000'), - MERGE_FORK_EPOCH=Epoch(18446744073709551615), -) - - -class Fork(Container): - previous_version: Version - current_version: Version - epoch: Epoch # Epoch of latest fork - - -class ForkData(Container): - current_version: Version - genesis_validators_root: Root - - -class Checkpoint(Container): - epoch: Epoch - root: Root - - -class Validator(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 # Commitment to pubkey for withdrawals - effective_balance: Gwei # Balance at stake - slashed: boolean - # Status epochs - activation_eligibility_epoch: Epoch # When criteria for activation were met - activation_epoch: Epoch - exit_epoch: Epoch - withdrawable_epoch: Epoch # When validator can withdraw funds - - -class AttestationData(Container): - slot: Slot - index: CommitteeIndex - # LMD GHOST vote - beacon_block_root: Root - # FFG vote - source: Checkpoint - target: Checkpoint - - -class IndexedAttestation(Container): - attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE] - data: AttestationData - signature: BLSSignature - - -class PendingAttestation(Container): - aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] - data: AttestationData - inclusion_delay: Slot - proposer_index: ValidatorIndex - - -class Eth1Data(Container): - deposit_root: Root - deposit_count: uint64 - block_hash: Hash32 - - -class HistoricalBatch(Container): - block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - - -class DepositMessage(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 - amount: Gwei - - -class DepositData(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 - amount: Gwei - signature: BLSSignature # Signing over DepositMessage - - -class BeaconBlockHeader(Container): - slot: Slot - proposer_index: ValidatorIndex - parent_root: Root - state_root: Root - body_root: Root - - -class SigningData(Container): - object_root: Root - domain: Domain - - -class AttesterSlashing(Container): - attestation_1: IndexedAttestation - attestation_2: IndexedAttestation - - -class Attestation(Container): - aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] - data: AttestationData - signature: BLSSignature - - -class Deposit(Container): - proof: Vector[Bytes32, DEPOSIT_CONTRACT_TREE_DEPTH + 1] # Merkle path to deposit root - data: DepositData - - -class VoluntaryExit(Container): - epoch: Epoch # Earliest epoch when voluntary exit can be processed - validator_index: ValidatorIndex - - -class SignedVoluntaryExit(Container): - message: VoluntaryExit - signature: BLSSignature - - -class SignedBeaconBlockHeader(Container): - message: BeaconBlockHeader - signature: BLSSignature - - -class ProposerSlashing(Container): - signed_header_1: SignedBeaconBlockHeader - signed_header_2: SignedBeaconBlockHeader - - -class Eth1Block(Container): - timestamp: uint64 - deposit_root: Root - deposit_count: uint64 - # All other eth1 block fields - - -class AggregateAndProof(Container): - aggregator_index: ValidatorIndex - aggregate: Attestation - selection_proof: BLSSignature - - -class SignedAggregateAndProof(Container): - message: AggregateAndProof - signature: BLSSignature - - -class SyncAggregate(Container): - sync_committee_bits: Bitvector[SYNC_COMMITTEE_SIZE] - sync_committee_signature: BLSSignature - - -class SyncCommittee(Container): - pubkeys: Vector[BLSPubkey, SYNC_COMMITTEE_SIZE] - aggregate_pubkey: BLSPubkey - - -class SyncCommitteeMessage(Container): - # Slot to which this contribution pertains - slot: Slot - # Block root for this signature - beacon_block_root: Root - # Index of the validator that produced this signature - validator_index: ValidatorIndex - # Signature by the validator over the block root of `slot` - signature: BLSSignature - - -class SyncCommitteeContribution(Container): - # Slot to which this contribution pertains - slot: Slot - # Block root for this contribution - beacon_block_root: Root - # The subcommittee this contribution pertains to out of the broader sync committee - subcommittee_index: uint64 - # A bit is set if a signature from the validator at the corresponding - # index in the subcommittee is present in the aggregate `signature`. - aggregation_bits: Bitvector[SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT] - # Signature by the validator(s) over the block root of `slot` - signature: BLSSignature - - -class ContributionAndProof(Container): - aggregator_index: ValidatorIndex - contribution: SyncCommitteeContribution - selection_proof: BLSSignature - - -class SignedContributionAndProof(Container): - message: ContributionAndProof - signature: BLSSignature - - -class SyncAggregatorSelectionData(Container): - slot: Slot - subcommittee_index: uint64 - - -class LightClientSnapshot(Container): - # Beacon block header - header: BeaconBlockHeader - # Sync committees corresponding to the header - current_sync_committee: SyncCommittee - next_sync_committee: SyncCommittee - - -class LightClientUpdate(Container): - # Update beacon block header - header: BeaconBlockHeader - # Next sync committee corresponding to the header - next_sync_committee: SyncCommittee - next_sync_committee_branch: Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_INDEX)] - # Finality proof for the update header - finality_header: BeaconBlockHeader - finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)] - # Sync committee aggregate signature - sync_committee_bits: Bitvector[SYNC_COMMITTEE_SIZE] - sync_committee_signature: BLSSignature - # Fork version for the aggregate signature - fork_version: Version - - -class ExecutionPayload(Container): - # Execution block header fields - parent_hash: Hash32 - fee_recipient: ExecutionAddress # 'beneficiary' in the yellow paper - state_root: Bytes32 - receipt_root: Bytes32 # 'receipts root' in the yellow paper - logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] - random: Bytes32 # 'difficulty' in the yellow paper - block_number: uint64 # 'number' in the yellow paper - gas_limit: uint64 - gas_used: uint64 - timestamp: uint64 - extra_data: ByteList[MAX_EXTRA_DATA_BYTES] - base_fee_per_gas: uint256 - # Extra payload fields - block_hash: Hash32 # Hash of execution block - transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD] - - -class BeaconBlockBody(Container): - randao_reveal: BLSSignature - eth1_data: Eth1Data # Eth1 data vote - graffiti: Bytes32 # Arbitrary data - # Operations - proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] - attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS] - attestations: List[Attestation, MAX_ATTESTATIONS] - deposits: List[Deposit, MAX_DEPOSITS] - voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS] - sync_aggregate: SyncAggregate - # Execution - execution_payload: ExecutionPayload # [New in Merge] - - -class BeaconBlock(Container): - slot: Slot - proposer_index: ValidatorIndex - parent_root: Root - state_root: Root - body: BeaconBlockBody - - -class SignedBeaconBlock(Container): - message: BeaconBlock - signature: BLSSignature - - -class ExecutionPayloadHeader(Container): - # Execution block header fields - parent_hash: Hash32 - fee_recipient: ExecutionAddress - state_root: Bytes32 - receipt_root: Bytes32 - logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] - random: Bytes32 - block_number: uint64 - gas_limit: uint64 - gas_used: uint64 - timestamp: uint64 - extra_data: ByteList[MAX_EXTRA_DATA_BYTES] - base_fee_per_gas: uint256 - # Extra payload fields - block_hash: Hash32 # Hash of execution block - transactions_root: Root - - -class PowBlock(Container): - block_hash: Hash32 - parent_hash: Hash32 - total_difficulty: uint256 - - -class WithdrawalReceipt(Container): - index: WithdrawalReceiptIndex - address: ExecutionAddress - amount: Gwei - - -class BeaconState(Container): - # Versioning - genesis_time: uint64 - genesis_validators_root: Root - slot: Slot - fork: Fork - # History - latest_block_header: BeaconBlockHeader - block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] - # Eth1 - eth1_data: Eth1Data - eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] - eth1_deposit_index: uint64 - # Registry - validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] - balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] - # Randomness - randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] - # Slashings - slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances - # Participation - previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - # Finality - justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch - previous_justified_checkpoint: Checkpoint - current_justified_checkpoint: Checkpoint - finalized_checkpoint: Checkpoint - # Inactivity - inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] - # Sync - current_sync_committee: SyncCommittee - next_sync_committee: SyncCommittee - # Execution - latest_execution_payload_header: ExecutionPayloadHeader - # Withdrawals - withdrawal_receipts: List[WithdrawalReceipt, WITHDRAWAL_RECEIPT_LIMIT] # [New in Withdrawals] - - -@dataclass(eq=True, frozen=True) -class LatestMessage(object): - epoch: Epoch - root: Root - - -@dataclass -class Store(object): - time: uint64 - genesis_time: uint64 - justified_checkpoint: Checkpoint - finalized_checkpoint: Checkpoint - best_justified_checkpoint: Checkpoint - proposer_boost_root: Root - blocks: Dict[Root, BeaconBlock] = field(default_factory=dict) - block_states: Dict[Root, BeaconState] = field(default_factory=dict) - checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict) - latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict) - - -@dataclass -class LightClientStore(object): - snapshot: LightClientSnapshot - valid_updates: Set[LightClientUpdate] - - -@dataclass -class PayloadAttributes(object): - timestamp: uint64 - random: Bytes32 - suggested_fee_recipient: ExecutionAddress - - -class ExecutionEngine(Protocol): - - def execute_payload(self, execution_payload: ExecutionPayload) -> bool: - """ - Return ``True`` if and only if ``execution_payload`` is valid with respect to ``self.execution_state``. - """ - ... - - def notify_forkchoice_updated(self, - head_block_hash: Hash32, - finalized_block_hash: Hash32, - payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]: - ... - - def get_payload(self, payload_id: PayloadId) -> ExecutionPayload: - """ - Return ``execution_payload`` object. - """ - ... - - -def integer_squareroot(n: uint64) -> uint64: - """ - Return the largest integer ``x`` such that ``x**2 <= n``. - """ - x = n - y = (x + 1) // 2 - while y < x: - x = y - y = (x + n // x) // 2 - return x - - -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)) - - -def bytes_to_uint64(data: bytes) -> uint64: - """ - Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian. - """ - return uint64(int.from_bytes(data, ENDIANNESS)) - - -def is_active_validator(validator: Validator, epoch: Epoch) -> bool: - """ - Check if ``validator`` is active. - """ - return validator.activation_epoch <= epoch < validator.exit_epoch - - -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 - ) - - -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 - ) - - -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) - - -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) - ) - - -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) - - -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 - - -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 - - -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 - - -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)] - - -def compute_epoch_at_slot(slot: Slot) -> Epoch: - """ - Return the epoch number at ``slot``. - """ - return Epoch(slot // SLOTS_PER_EPOCH) - - -def compute_start_slot_at_epoch(epoch: Epoch) -> Slot: - """ - Return the start slot of ``epoch``. - """ - return Slot(epoch * SLOTS_PER_EPOCH) - - -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) - - -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, - )) - - -def compute_fork_digest(current_version: Version, genesis_validators_root: Root) -> ForkDigest: - """ - Return the 4-byte fork digest for the ``current_version`` and ``genesis_validators_root``. - This is a digest primarily used for domain separation on the p2p layer. - 4-bytes suffices for practical separation of forks/chains. - """ - return ForkDigest(compute_fork_data_root(current_version, genesis_validators_root)[:4]) - - -def compute_domain(domain_type: DomainType, fork_version: Version=None, genesis_validators_root: Root=None) -> Domain: - """ - Return the domain for the ``domain_type`` and ``fork_version``. - """ - if fork_version is None: - fork_version = config.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]) - - -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, - )) - - -def get_current_epoch(state: BeaconState) -> Epoch: - """ - Return the current epoch. - """ - return compute_epoch_at_slot(state.slot) - - -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) - - -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)) - - -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] - - -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] - - -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)] - - -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(config.MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // config.CHURN_LIMIT_QUOTIENT) - - -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) - - -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, - )) - - -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, - ) - - -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) - - -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, afterwhich this overflows uint64. - """ - return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, sum([state.validators[index].effective_balance for index in indices]))) - - -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)))) - - -def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch=None) -> Domain: - """ - Return the signature domain (fork version concatenated with domain type) of a message. - """ - epoch = get_current_epoch(state) if epoch is None else epoch - fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version - return compute_domain(domain_type, fork_version, state.genesis_validators_root) - - -def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> IndexedAttestation: - """ - Return the indexed attestation corresponding to ``attestation``. - """ - attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) - - return IndexedAttestation( - attesting_indices=sorted(attesting_indices), - data=attestation.data, - signature=attestation.signature, - ) - - -def get_attesting_indices(state: BeaconState, - data: AttestationData, - bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]: - """ - Return the set of attesting indices corresponding to ``data`` and ``bits``. - """ - committee = get_beacon_committee(state, data.slot, data.index) - return set(index for i, index in enumerate(committee) if bits[i]) - - -def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: - """ - Increase the validator balance at index ``index`` by ``delta``. - """ - state.balances[index] += delta - - -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 - - -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 + config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY) - - -def slash_validator(state: BeaconState, - slashed_index: ValidatorIndex, - whistleblower_index: ValidatorIndex=None) -> None: - """ - Slash the validator with index ``slashed_index``. - """ - epoch = get_current_epoch(state) - initiate_validator_exit(state, slashed_index) - validator = state.validators[slashed_index] - validator.slashed = True - validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR)) - state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance - slashing_penalty = validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_MERGE # [Modified in Merge] - 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)) - - -def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, - eth1_timestamp: uint64, - deposits: Sequence[Deposit], - execution_payload_header: ExecutionPayloadHeader=ExecutionPayloadHeader() - ) -> BeaconState: - fork = Fork( - previous_version=config.MERGE_FORK_VERSION, # [Modified in Merge] for testing only - current_version=config.MERGE_FORK_VERSION, # [Modified in Merge] - epoch=GENESIS_EPOCH, - ) - state = BeaconState( - genesis_time=eth1_timestamp + config.GENESIS_DELAY, - fork=fork, - eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), - 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) - - # Fill in sync committees - # Note: A duplicate committee is assigned for the current and next committee at genesis - state.current_sync_committee = get_next_sync_committee(state) - state.next_sync_committee = get_next_sync_committee(state) - - # [New in Merge] Initialize the execution payload header - # If empty, will initialize a chain that has not yet gone through the Merge transition - state.latest_execution_payload_header = execution_payload_header - - return state - - -def is_valid_genesis_state(state: BeaconState) -> bool: - if state.genesis_time < config.MIN_GENESIS_TIME: - return False - if len(get_active_validator_indices(state, GENESIS_EPOCH)) < config.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: - return False - return True - - -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) - - -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) - - -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) - - -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 - - -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) - process_historical_roots_update(state) - process_participation_flag_updates(state) - process_sync_committee_updates(state) - process_withdrawals(state) # [New in Withdrawals] - - -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 - - -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) - ] - - -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) - ] - - -def get_unslashed_attesting_indices(state: BeaconState, - attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]: - output = set() # type: Set[ValidatorIndex] - for a in attestations: - output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits)) - return set(filter(lambda index: not state.validators[index].slashed, output)) - - -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)) - - -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) - - -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 - - -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)) - - -def get_proposer_reward(state: BeaconState, attesting_index: ValidatorIndex) -> Gwei: - return Gwei(get_base_reward(state, attesting_index) // PROPOSER_REWARD_QUOTIENT) - - -def get_finality_delay(state: BeaconState) -> uint64: - return get_previous_epoch(state) - state.finalized_checkpoint.epoch - - -def is_in_inactivity_leak(state: BeaconState) -> bool: - return get_finality_delay(state) > MIN_EPOCHS_TO_INACTIVITY_PENALTY - - -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) - ] - - -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 - - -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) - - -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) - - -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) - - -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.data, a.aggregation_bits) - ], 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 - - -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 Merge] - penalty_denominator = config.INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_MERGE - penalties[index] += Gwei(penalty_numerator // penalty_denominator) - return rewards, penalties - - -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 - - -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]) - - -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 <= config.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)) - - -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_MERGE, # [Modified in Merge] - 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) - - -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 = [] - - -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) - - -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) - - -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) - - -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)) - - -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 = [] - - -def process_block(state: BeaconState, block: BeaconBlock) -> None: - process_block_header(state, block) - if is_execution_enabled(state, block.body): - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Merge] - process_randao(state, block.body) - process_eth1_data(state, block.body) - process_operations(state, block.body) - process_sync_aggregate(state, block.body.sync_aggregate) - - -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 - - -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 - - -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 - - -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) - - -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) - - -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 - - -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, data, attestation.aggregation_bits): - 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) - - -def get_validator_from_deposit(state: BeaconState, deposit: Deposit) -> Validator: - amount = deposit.data.amount - effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) - - return Validator( - pubkey=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - activation_eligibility_epoch=FAR_FUTURE_EPOCH, - activation_epoch=FAR_FUTURE_EPOCH, - exit_epoch=FAR_FUTURE_EPOCH, - withdrawable_epoch=FAR_FUTURE_EPOCH, - effective_balance=effective_balance, - ) - - -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, - depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in - index=state.eth1_deposit_index, - root=state.eth1_data.deposit_root, - ) - - # Deposits must be processed in order - state.eth1_deposit_index += 1 - - pubkey = deposit.data.pubkey - amount = deposit.data.amount - validator_pubkeys = [validator.pubkey for validator 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=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - amount=deposit.data.amount, - ) - domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks - signing_root = compute_signing_root(deposit_message, domain) - # Initialize validator if the deposit signature is valid - if bls.Verify(pubkey, signing_root, deposit.data.signature): - state.validators.append(get_validator_from_deposit(state, deposit)) - state.balances.append(amount) - state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) - state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) - state.inactivity_scores.append(uint64(0)) - else: - # Increase balance by deposit amount - index = ValidatorIndex(validator_pubkeys.index(pubkey)) - increase_balance(state, index, amount) - - -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 + config.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) - - -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 + config.SECONDS_PER_SLOT * anchor_state.slot), - genesis_time=anchor_state.genesis_time, - justified_checkpoint=justified_checkpoint, - finalized_checkpoint=finalized_checkpoint, - best_justified_checkpoint=justified_checkpoint, - proposer_boost_root=proposer_boost_root, - blocks={anchor_root: copy(anchor_block)}, - block_states={anchor_root: copy(anchor_state)}, - checkpoint_states={justified_checkpoint: copy(anchor_state)}, - ) - - -def get_slots_since_genesis(store: Store) -> int: - return (store.time - store.genesis_time) // config.SECONDS_PER_SLOT - - -def get_current_slot(store: Store) -> Slot: - return Slot(GENESIS_SLOT + get_slots_since_genesis(store)) - - -def compute_slots_since_epoch_start(slot: Slot) -> int: - return slot - compute_start_slot_at_epoch(compute_epoch_at_slot(slot)) - - -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) - elif block.slot == slot: - return root - else: - # root is older than queried slot, thus a skip slot. Return most recent root prior to slot - return root - - -def get_latest_attesting_balance(store: Store, root: Root) -> Gwei: - state = store.checkpoint_states[store.justified_checkpoint] - active_indices = get_active_validator_indices(state, get_current_epoch(state)) - attestation_score = Gwei(sum( - state.validators[i].effective_balance for i in active_indices - if (i in store.latest_messages - and get_ancestor(store, store.latest_messages[i].root, store.blocks[root].slot) == root) - )) - proposer_score = Gwei(0) - if store.proposer_boost_root != Root(): - block = store.blocks[root] - if get_ancestor(store, root, block.slot) == store.proposer_boost_root: - num_validators = len(get_active_validator_indices(state, get_current_epoch(state))) - avg_balance = get_total_active_balance(state) // num_validators - committee_size = num_validators // SLOTS_PER_EPOCH - committee_weight = committee_size * avg_balance - proposer_score = (committee_weight * config.PROPOSER_SCORE_BOOST) // 100 - return attestation_score + proposer_score - - -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 - - # If leaf block, check finalized/justified checkpoints as matching latest. - head_state = store.block_states[block_root] - - correct_justified = ( - store.justified_checkpoint.epoch == GENESIS_EPOCH - or head_state.current_justified_checkpoint == store.justified_checkpoint - ) - correct_finalized = ( - store.finalized_checkpoint.epoch == GENESIS_EPOCH - or head_state.finalized_checkpoint == store.finalized_checkpoint - ) - # 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 - - -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 - - -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 - head = max(children, key=lambda root: (get_latest_attesting_balance(store, root), root)) - - -def should_update_justified_checkpoint(store: Store, new_justified_checkpoint: Checkpoint) -> bool: - """ - To address the bouncing attack, only update conflicting justified - checkpoints in the fork choice if in the early slots of the epoch. - Otherwise, delay incorporation of new justified checkpoint until next epoch boundary. - - See https://ethresear.ch/t/prevention-of-bouncing-attack-on-ffg/6114 for more detailed analysis and discussion. - """ - if compute_slots_since_epoch_start(get_current_slot(store)) < SAFE_SLOTS_TO_UPDATE_JUSTIFIED: - return True - - justified_slot = compute_start_slot_at_epoch(store.justified_checkpoint.epoch) - if not get_ancestor(store, new_justified_checkpoint.root, justified_slot) == store.justified_checkpoint.root: - return False - - return True - - -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 = compute_epoch_at_slot(get_current_slot(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] - - -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) - - # Attestations target be for a known block. If target block is unknown, delay consideration until the 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 - target_slot = compute_start_slot_at_epoch(target.epoch) - assert target.root == get_ancestor(store, attestation.data.beacon_block_root, target_slot) - - # 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 - - -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 - - -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 - for i in 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) - - -def on_tick(store: Store, time: uint64) -> None: - previous_slot = get_current_slot(store) - - # update store time - store.time = time - - current_slot = get_current_slot(store) - - # Reset store.proposer_boost_root if this is a new slot - if current_slot > previous_slot: - store.proposer_boost_root = Root() - - # Not a new epoch, return - if not (current_slot > previous_slot and compute_slots_since_epoch_start(current_slot) == 0): - return - - # Update store.justified_checkpoint if a better checkpoint on the store.finalized_checkpoint chain - if store.best_justified_checkpoint.epoch > store.justified_checkpoint.epoch: - finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) - ancestor_at_finalized_slot = get_ancestor(store, store.best_justified_checkpoint.root, finalized_slot) - if ancestor_at_finalized_slot == store.finalized_checkpoint.root: - store.justified_checkpoint = store.best_justified_checkpoint - - -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 the 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 - assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root - - # Check the block is valid and compute the post-state - state = pre_state.copy() - state_transition(state, signed_block, True) - - # [New in Merge] - if is_merge_transition_block(pre_state, block.body): - validate_merge_block(block) - - # Add new block to the store - store.blocks[hash_tree_root(block)] = block - # Add new state for this block to the store - store.block_states[hash_tree_root(block)] = state - - # Add proposer score boost if the block is timely - time_into_slot = (store.time - store.genesis_time) % config.SECONDS_PER_SLOT - is_before_attesting_interval = time_into_slot < config.SECONDS_PER_SLOT // INTERVALS_PER_SLOT - if get_current_slot(store) == block.slot and is_before_attesting_interval: - store.proposer_boost_root = hash_tree_root(block) - - # Update justified checkpoint - if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch: - if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch: - store.best_justified_checkpoint = state.current_justified_checkpoint - if should_update_justified_checkpoint(store, state.current_justified_checkpoint): - store.justified_checkpoint = state.current_justified_checkpoint - - # Update finalized checkpoint - if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch: - store.finalized_checkpoint = state.finalized_checkpoint - store.justified_checkpoint = state.current_justified_checkpoint - - -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) - - -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)) - - -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 - - -def is_proposer(state: BeaconState, validator_index: ValidatorIndex) -> bool: - return get_beacon_proposer_index(state) == validator_index - - -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) - - -def compute_time_at_slot(state: BeaconState, slot: Slot) -> uint64: - return uint64(state.genesis_time + slot * config.SECONDS_PER_SLOT) - - -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) - - -def is_candidate_block(block: Eth1Block, period_start: uint64) -> bool: - return ( - block.timestamp + config.SECONDS_PER_ETH1_BLOCK * config.ETH1_FOLLOW_DISTANCE <= period_start - and block.timestamp + config.SECONDS_PER_ETH1_BLOCK * config.ETH1_FOLLOW_DISTANCE * 2 >= period_start - ) - - -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, - key=lambda v: (valid_votes.count(v), -valid_votes.index(v)), # Tiebreak by smallest distance - default=default_vote - ) - - -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) - - -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) - - -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) - - -def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, committee_index: CommitteeIndex) -> uint64: - """ - 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 uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT) - - -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) - - -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 - - -def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature: - signatures = [attestation.signature for attestation in attestations] - return bls.Aggregate(signatures) - - -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), - ) - - -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) - - -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 = config.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 - - -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 ws_state.latest_block_header.state_root == 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 - - -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 - - -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 - - -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 - - -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) - - -def get_base_reward_per_increment(state: BeaconState) -> Gwei: - return Gwei(EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR // integer_squareroot(get_total_active_balance(state))) - - -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)) - - -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. - """ - if data.target.epoch == get_current_epoch(state): - justified_checkpoint = state.current_justified_checkpoint - else: - justified_checkpoint = state.previous_justified_checkpoint - - # Matching roots - is_matching_source = data.source == justified_checkpoint - is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch) - is_matching_head = is_matching_target and data.beacon_block_root == get_block_root_at_slot(state, data.slot) - 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 - - -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 - - -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 - 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) - 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) - - -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] += config.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(config.INACTIVITY_SCORE_RECOVERY_RATE, state.inactivity_scores[index]) - - -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))] - - -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) - - -def eth_aggregate_pubkeys(pubkeys: Sequence[BLSPubkey]) -> BLSPubkey: - return bls.AggregatePKs(pubkeys) - - -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) - - -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, data, attestation.aggregation_bits): - for flag_index in participation_flag_indices: - epoch_participation[index] = add_flag(epoch_participation[index], flag_index) - - -def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState: - epoch = phase0.get_current_epoch(pre) - post = BeaconState( - # Versioning - 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=config.ALTAIR_FORK_VERSION, - epoch=epoch, - ), - # History - latest_block_header=pre.latest_block_header, - block_roots=pre.block_roots, - state_roots=pre.state_roots, - historical_roots=pre.historical_roots, - # Eth1 - eth1_data=pre.eth1_data, - eth1_data_votes=pre.eth1_data_votes, - eth1_deposit_index=pre.eth1_deposit_index, - # Registry - validators=pre.validators, - balances=pre.balances, - # Randomness - randao_mixes=pre.randao_mixes, - # Slashings - slashings=pre.slashings, - # Participation - previous_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))], - current_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))], - # Finality - 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 - 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 - - -def compute_sync_committee_period(epoch: Epoch) -> uint64: - return epoch // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - - -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 - - -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 - - -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, - ) - - -def compute_subnets_for_sync_committee(state: BeaconState, validator_index: ValidatorIndex) -> Set[uint64]: - 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([ - uint64(index // (SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT)) - for index in sync_committee_indices - ]) - - -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) - - -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 - - -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, - ) - - -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) - - -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] - - -def get_subtree_index(generalized_index: GeneralizedIndex) -> uint64: - return uint64(generalized_index % 2**(floorlog2(generalized_index))) - - -def validate_light_client_update(snapshot: LightClientSnapshot, - update: LightClientUpdate, - genesis_validators_root: Root) -> None: - # Verify update slot is larger than snapshot slot - assert update.header.slot > snapshot.header.slot - - # Verify update does not skip a sync committee period - snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - assert update_period in (snapshot_period, snapshot_period + 1) - - # Verify update header root is the finalized root of the finality header, if specified - if update.finality_header == BeaconBlockHeader(): - signed_header = update.header - assert update.finality_branch == [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))] - else: - signed_header = update.finality_header - assert is_valid_merkle_branch( - leaf=hash_tree_root(update.header), - branch=update.finality_branch, - depth=floorlog2(FINALIZED_ROOT_INDEX), - index=get_subtree_index(FINALIZED_ROOT_INDEX), - root=update.finality_header.state_root, - ) - - # Verify update next sync committee if the update period incremented - if update_period == snapshot_period: - sync_committee = snapshot.current_sync_committee - assert update.next_sync_committee_branch == [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))] - else: - sync_committee = snapshot.next_sync_committee - assert is_valid_merkle_branch( - leaf=hash_tree_root(update.next_sync_committee), - branch=update.next_sync_committee_branch, - depth=floorlog2(NEXT_SYNC_COMMITTEE_INDEX), - index=get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX), - root=update.header.state_root, - ) - - # Verify sync committee has sufficient participants - assert sum(update.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS - - # Verify sync committee aggregate signature - participant_pubkeys = [pubkey for (bit, pubkey) in zip(update.sync_committee_bits, sync_committee.pubkeys) if bit] - domain = compute_domain(DOMAIN_SYNC_COMMITTEE, update.fork_version, genesis_validators_root) - signing_root = compute_signing_root(signed_header, domain) - assert bls.FastAggregateVerify(participant_pubkeys, signing_root, update.sync_committee_signature) - - -def apply_light_client_update(snapshot: LightClientSnapshot, update: LightClientUpdate) -> None: - snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - if update_period == snapshot_period + 1: - snapshot.current_sync_committee = snapshot.next_sync_committee - snapshot.next_sync_committee = update.next_sync_committee - snapshot.header = update.header - - -def process_light_client_update(store: LightClientStore, update: LightClientUpdate, current_slot: Slot, - genesis_validators_root: Root) -> None: - validate_light_client_update(store.snapshot, update, genesis_validators_root) - store.valid_updates.add(update) - - update_timeout = SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD - if ( - sum(update.sync_committee_bits) * 3 >= len(update.sync_committee_bits) * 2 - and update.finality_header != BeaconBlockHeader() - ): - # Apply update if (1) 2/3 quorum is reached and (2) we have a finality proof. - # Note that (2) means that the current light client design needs finality. - # It may be changed to re-organizable light client design. See the on-going issue consensus-specs#2182. - apply_light_client_update(store.snapshot, update) - store.valid_updates = set() - elif current_slot > store.snapshot.header.slot + update_timeout: - # Forced best update when the update timeout has elapsed - apply_light_client_update(store.snapshot, - max(store.valid_updates, key=lambda update: sum(update.sync_committee_bits))) - store.valid_updates = set() - - -def is_merge_transition_complete(state: BeaconState) -> bool: - return state.latest_execution_payload_header != ExecutionPayloadHeader() - - -def is_merge_transition_block(state: BeaconState, body: BeaconBlockBody) -> bool: - return not is_merge_transition_complete(state) and body.execution_payload != ExecutionPayload() - - -def is_execution_enabled(state: BeaconState, body: BeaconBlockBody) -> bool: - return is_merge_transition_block(state, body) or is_merge_transition_complete(state) - - -def compute_timestamp_at_slot(state: BeaconState, slot: Slot) -> uint64: - slots_since_genesis = slot - GENESIS_SLOT - return uint64(state.genesis_time + slots_since_genesis * config.SECONDS_PER_SLOT) - - -def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: - # 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 random - assert payload.random == get_randao_mix(state, get_current_epoch(state)) - # Verify timestamp - assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) - # Verify the execution payload is valid - assert execution_engine.execute_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, - receipt_root=payload.receipt_root, - logs_bloom=payload.logs_bloom, - random=payload.random, - 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), - ) - - -def upgrade_to_merge(pre: altair.BeaconState) -> BeaconState: - epoch = altair.get_current_epoch(pre) - post = BeaconState( - # Versioning - 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=config.MERGE_FORK_VERSION, - epoch=epoch, - ), - # History - latest_block_header=pre.latest_block_header, - block_roots=pre.block_roots, - state_roots=pre.state_roots, - historical_roots=pre.historical_roots, - # Eth1 - eth1_data=pre.eth1_data, - eth1_data_votes=pre.eth1_data_votes, - eth1_deposit_index=pre.eth1_deposit_index, - # Registry - validators=pre.validators, - balances=pre.balances, - # Randomness - randao_mixes=pre.randao_mixes, - # Slashings - slashings=pre.slashings, - # Participation - previous_epoch_participation=pre.previous_epoch_participation, - current_epoch_participation=pre.current_epoch_participation, - # Finality - 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 - inactivity_scores=pre.inactivity_scores, - # Sync - current_sync_committee=pre.current_sync_committee, - next_sync_committee=pre.next_sync_committee, - # Execution-layer - latest_execution_payload_header=ExecutionPayloadHeader(), - ) - - return post - - -def is_valid_terminal_pow_block(block: PowBlock, parent: PowBlock) -> bool: - is_total_difficulty_reached = block.total_difficulty >= config.TERMINAL_TOTAL_DIFFICULTY - is_parent_total_difficulty_valid = parent.total_difficulty < config.TERMINAL_TOTAL_DIFFICULTY - return is_total_difficulty_reached and is_parent_total_difficulty_valid - - -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 config.TERMINAL_BLOCK_HASH != Hash32(): - # If `config.TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached. - assert compute_epoch_at_slot(block.slot) >= config.TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH - assert block.body.execution_payload.parent_hash == config.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) - - -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 >= config.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 >= config.TERMINAL_TOTAL_DIFFICULTY - if not parent_reached_ttd: - return block - - return None - - -def get_terminal_pow_block(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlock]: - if config.TERMINAL_BLOCK_HASH != Hash32(): - # Terminal block hash override takes precedence over terminal total difficulty - if config.TERMINAL_BLOCK_HASH in pow_chain: - return pow_chain[config.TERMINAL_BLOCK_HASH] - else: - return None - - return get_pow_block_at_terminal_total_difficulty(pow_chain) - - -def prepare_execution_payload(state: BeaconState, - pow_chain: Dict[Hash32, PowBlock], - finalized_block_hash: Hash32, - suggested_fee_recipient: ExecutionAddress, - execution_engine: ExecutionEngine) -> Optional[PayloadId]: - if not is_merge_transition_complete(state): - is_terminal_block_hash_set = config.TERMINAL_BLOCK_HASH != Hash32() - is_activation_epoch_reached = get_current_epoch(state) >= config.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_timestamp_at_slot(state, state.slot), - random=get_randao_mix(state, get_current_epoch(state)), - suggested_fee_recipient=suggested_fee_recipient, - ) - return execution_engine.notify_forkchoice_updated(parent_hash, finalized_block_hash, payload_attributes) - - -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) - - -def withdraw(state: BeaconState, index: ValidatorIndex, amount: Gwei) -> None: - # Decrease the validator's balance - decrease_balance(state, index, amount) - # Create a corresponding withdrawal receipt - receipt = WithdrawalReceipt( - index=WithdrawalReceiptIndex(len(state.withdrawal_receipts)), - address=state.validators[index].withdrawal_credentials[12:], - amount=amount, - ) - state.withdrawal_receipts.append(receipt) - - -def is_withdrawable_validator(validator: Validator, epoch: Epoch) -> bool: - """ - Check if ``validator`` is withdrawable. - """ - return validator.withdrawable_epoch <= epoch - - -def process_withdrawals(state: BeaconState) -> None: - current_epoch = get_current_epoch(state) - for index, validator in enumerate(state.validators): - balance = state.balances[index] - is_balance_nonzero = state.balances[index] == 0 - is_eth1_withdrawal_prefix = validator.withdrawal_credentials[0] != ETH1_ADDRESS_WITHDRAWAL_PREFIX - if is_balance_nonzero and is_eth1_withdrawal_prefix and is_withdrawable_validator(validator, current_epoch): - withdraw(state, index, balance) - - -def get_eth1_data(block: Eth1Block) -> Eth1Data: - """ - A stub function return mocking Eth1Data. - """ - return Eth1Data( - deposit_root=block.deposit_root, - deposit_count=block.deposit_count, - block_hash=hash_tree_root(block)) - - -def cache_this(key_fn, value_fn, lru_size): # type: ignore - cache_dict = LRU(size=lru_size) - - def wrapper(*args, **kw): # type: ignore - key = key_fn(*args, **kw) - nonlocal cache_dict - if key not in cache_dict: - cache_dict[key] = value_fn(*args, **kw) - return cache_dict[key] - return wrapper - - -_compute_shuffled_index = compute_shuffled_index -compute_shuffled_index = cache_this( - lambda index, index_count, seed: (index, index_count, seed), - _compute_shuffled_index, lru_size=SLOTS_PER_EPOCH * 3) - -_get_total_active_balance = get_total_active_balance -get_total_active_balance = cache_this( - lambda state: (state.validators.hash_tree_root(), compute_epoch_at_slot(state.slot)), - _get_total_active_balance, lru_size=10) - -_get_base_reward = get_base_reward -get_base_reward = cache_this( - lambda state, index: (state.validators.hash_tree_root(), state.slot, index), - _get_base_reward, lru_size=2048) - -_get_committee_count_per_slot = get_committee_count_per_slot -get_committee_count_per_slot = cache_this( - lambda state, epoch: (state.validators.hash_tree_root(), epoch), - _get_committee_count_per_slot, lru_size=SLOTS_PER_EPOCH * 3) - -_get_active_validator_indices = get_active_validator_indices -get_active_validator_indices = cache_this( - lambda state, epoch: (state.validators.hash_tree_root(), epoch), - _get_active_validator_indices, lru_size=3) - -_get_beacon_committee = get_beacon_committee -get_beacon_committee = cache_this( - lambda state, slot, index: (state.validators.hash_tree_root(), state.randao_mixes.hash_tree_root(), slot, index), - _get_beacon_committee, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3) - -_get_matching_target_attestations = get_matching_target_attestations -get_matching_target_attestations = cache_this( - lambda state, epoch: (state.hash_tree_root(), epoch), - _get_matching_target_attestations, lru_size=10) - -_get_matching_head_attestations = get_matching_head_attestations -get_matching_head_attestations = cache_this( - lambda state, epoch: (state.hash_tree_root(), epoch), - _get_matching_head_attestations, lru_size=10) - -_get_attesting_indices = get_attesting_indices -get_attesting_indices = cache_this( - lambda state, data, bits: ( - state.randao_mixes.hash_tree_root(), - state.validators.hash_tree_root(), data.hash_tree_root(), bits.hash_tree_root() - ), - _get_attesting_indices, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3) - - -def get_generalized_index(ssz_class: Any, *path: Sequence[PyUnion[int, SSZVariableName]]) -> GeneralizedIndex: - ssz_path = Path(ssz_class) - for item in path: - ssz_path = ssz_path / item - return GeneralizedIndex(ssz_path.gindex()) - - -ExecutionState = Any - - -def get_pow_block(hash: Bytes32) -> Optional[PowBlock]: - return PowBlock(block_hash=hash, parent_hash=Bytes32(), total_difficulty=uint256(0)) - - -def get_execution_state(execution_state_root: Bytes32) -> ExecutionState: - pass - - -def get_pow_chain_head() -> PowBlock: - pass - - -class NoopExecutionEngine(ExecutionEngine): - - def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: - return True - - def notify_forkchoice_updated(self: ExecutionEngine, - head_block_hash: Hash32, - finalized_block_hash: Hash32, - payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]: - pass - - def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload: - raise NotImplementedError("no default block production") - - -EXECUTION_ENGINE = NoopExecutionEngine() - - -assert FINALIZED_ROOT_INDEX == get_generalized_index(BeaconState, 'finalized_checkpoint', 'root') -assert NEXT_SYNC_COMMITTEE_INDEX == get_generalized_index(BeaconState, 'next_sync_committee') diff --git a/tests/core/pyspec/eth2spec/withdrawals/minimal.py b/tests/core/pyspec/eth2spec/withdrawals/minimal.py deleted file mode 100644 index 820d3703c..000000000 --- a/tests/core/pyspec/eth2spec/withdrawals/minimal.py +++ /dev/null @@ -1,3018 +0,0 @@ -from lru import LRU -from dataclasses import ( - dataclass, - field, -) -from typing import ( - Any, Callable, Dict, Set, Sequence, Tuple, Optional, TypeVar, NamedTuple -) - -from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy, uint_to_bytes -from eth2spec.utils.ssz.ssz_typing import ( - View, boolean, Container, List, Vector, uint8, uint32, uint64, - Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist) -from eth2spec.utils.ssz.ssz_typing import Bitvector # noqa: F401 -from eth2spec.utils import bls -from eth2spec.utils.hash_function import hash - - -from typing import NewType, Union as PyUnion - -from eth2spec.phase0 import minimal as phase0 -from eth2spec.utils.ssz.ssz_typing import Path - -from typing import Protocol -from eth2spec.altair import minimal as altair -from eth2spec.utils.ssz.ssz_typing import Bytes8, Bytes20, ByteList, ByteVector, uint256 - -SSZObject = TypeVar('SSZObject', bound=View) - - -SSZVariableName = str -GeneralizedIndex = NewType('GeneralizedIndex', int) - - -fork = 'withdrawals' - - -MAX_BYTES_PER_TRANSACTION = uint64(2**30) - - -class Slot(uint64): - pass - - -class Epoch(uint64): - pass - - -class CommitteeIndex(uint64): - pass - - -class ValidatorIndex(uint64): - pass - - -class Gwei(uint64): - pass - - -class Root(Bytes32): - pass - - -class Hash32(Bytes32): - pass - - -class Version(Bytes4): - pass - - -class DomainType(Bytes4): - pass - - -class ForkDigest(Bytes4): - pass - - -class Domain(Bytes32): - pass - - -class BLSPubkey(Bytes48): - pass - - -class BLSSignature(Bytes96): - pass - - -class Ether(uint64): - pass - - -class ParticipationFlags(uint8): - pass - - -class ExecutionAddress(Bytes20): - pass - - -class PayloadId(Bytes8): - pass - - -class WithdrawalReceiptIndex(uint64): - pass - - -Transaction = ByteList[MAX_BYTES_PER_TRANSACTION] - - -def ceillog2(x: int) -> uint64: - if x < 1: - raise ValueError(f"ceillog2 accepts only positive values, x={x}") - return uint64((x - 1).bit_length()) - - -def floorlog2(x: int) -> uint64: - if x < 1: - raise ValueError(f"floorlog2 accepts only positive values, x={x}") - return uint64(x.bit_length() - 1) - - -FINALIZED_ROOT_INDEX = GeneralizedIndex(105) -NEXT_SYNC_COMMITTEE_INDEX = GeneralizedIndex(55) - -# Constant vars -GENESIS_SLOT = Slot(0) -GENESIS_EPOCH = Epoch(0) -FAR_FUTURE_EPOCH = Epoch(2**64 - 1) -BASE_REWARDS_PER_EPOCH = uint64(4) -DEPOSIT_CONTRACT_TREE_DEPTH = uint64(2**5) -JUSTIFICATION_BITS_LENGTH = uint64(4) -ENDIANNESS = 'little' -BLS_WITHDRAWAL_PREFIX = Bytes1('0x00') -ETH1_ADDRESS_WITHDRAWAL_PREFIX = Bytes1('0x01') -DOMAIN_BEACON_PROPOSER = DomainType('0x00000000') -DOMAIN_BEACON_ATTESTER = DomainType('0x01000000') -DOMAIN_RANDAO = DomainType('0x02000000') -DOMAIN_DEPOSIT = DomainType('0x03000000') -DOMAIN_VOLUNTARY_EXIT = DomainType('0x04000000') -DOMAIN_SELECTION_PROOF = DomainType('0x05000000') -DOMAIN_AGGREGATE_AND_PROOF = DomainType('0x06000000') -INTERVALS_PER_SLOT = uint64(3) -TARGET_AGGREGATORS_PER_COMMITTEE = 2**4 -RANDOM_SUBNETS_PER_VALIDATOR = 2**0 -EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION = 2**8 -ATTESTATION_SUBNET_COUNT = 64 -ETH_TO_GWEI = uint64(10**9) -SAFETY_DECAY = uint64(10) -TIMELY_SOURCE_FLAG_INDEX = 0 -TIMELY_TARGET_FLAG_INDEX = 1 -TIMELY_HEAD_FLAG_INDEX = 2 -TIMELY_SOURCE_WEIGHT = uint64(14) -TIMELY_TARGET_WEIGHT = uint64(26) -TIMELY_HEAD_WEIGHT = uint64(14) -SYNC_REWARD_WEIGHT = uint64(2) -PROPOSER_WEIGHT = uint64(8) -WEIGHT_DENOMINATOR = uint64(64) -DOMAIN_SYNC_COMMITTEE = DomainType('0x07000000') -DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF = DomainType('0x08000000') -DOMAIN_CONTRIBUTION_AND_PROOF = DomainType('0x09000000') -PARTICIPATION_FLAG_WEIGHTS = [TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT, TIMELY_HEAD_WEIGHT] -G2_POINT_AT_INFINITY = BLSSignature(b'\xc0' + b'\x00' * 95) -TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE = 2**4 -SYNC_COMMITTEE_SUBNET_COUNT = 4 -WITHDRAWAL_RECEIPT_LIMIT = uint64(2**40) - -# Preset vars -MAX_COMMITTEES_PER_SLOT = uint64(4) -TARGET_COMMITTEE_SIZE = uint64(4) -MAX_VALIDATORS_PER_COMMITTEE = uint64(2048) -SHUFFLE_ROUND_COUNT = uint64(10) -HYSTERESIS_QUOTIENT = uint64(4) -HYSTERESIS_DOWNWARD_MULTIPLIER = uint64(1) -HYSTERESIS_UPWARD_MULTIPLIER = uint64(5) -MIN_DEPOSIT_AMOUNT = Gwei(1000000000) -MAX_EFFECTIVE_BALANCE = Gwei(32000000000) -EFFECTIVE_BALANCE_INCREMENT = Gwei(1000000000) -MIN_ATTESTATION_INCLUSION_DELAY = uint64(1) -SLOTS_PER_EPOCH = uint64(8) -MIN_SEED_LOOKAHEAD = uint64(1) -MAX_SEED_LOOKAHEAD = uint64(4) -MIN_EPOCHS_TO_INACTIVITY_PENALTY = uint64(4) -EPOCHS_PER_ETH1_VOTING_PERIOD = uint64(4) -SLOTS_PER_HISTORICAL_ROOT = uint64(64) -EPOCHS_PER_HISTORICAL_VECTOR = uint64(64) -EPOCHS_PER_SLASHINGS_VECTOR = uint64(64) -HISTORICAL_ROOTS_LIMIT = uint64(16777216) -VALIDATOR_REGISTRY_LIMIT = uint64(1099511627776) -BASE_REWARD_FACTOR = uint64(64) -WHISTLEBLOWER_REWARD_QUOTIENT = uint64(512) -PROPOSER_REWARD_QUOTIENT = uint64(8) -INACTIVITY_PENALTY_QUOTIENT = uint64(33554432) -MIN_SLASHING_PENALTY_QUOTIENT = uint64(64) -PROPORTIONAL_SLASHING_MULTIPLIER = uint64(2) -MAX_PROPOSER_SLASHINGS = 16 -MAX_ATTESTER_SLASHINGS = 2 -MAX_ATTESTATIONS = 128 -MAX_DEPOSITS = 16 -MAX_VOLUNTARY_EXITS = 16 -SAFE_SLOTS_TO_UPDATE_JUSTIFIED = 2 -INACTIVITY_PENALTY_QUOTIENT_ALTAIR = uint64(50331648) -MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR = uint64(64) -PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR = uint64(2) -SYNC_COMMITTEE_SIZE = uint64(32) -EPOCHS_PER_SYNC_COMMITTEE_PERIOD = uint64(8) -MIN_SYNC_COMMITTEE_PARTICIPANTS = 1 -MAX_BYTES_PER_TRANSACTION = uint64(1073741824) -MAX_TRANSACTIONS_PER_PAYLOAD = uint64(1048576) -BYTES_PER_LOGS_BLOOM = uint64(256) -MAX_EXTRA_DATA_BYTES = 32 -INACTIVITY_PENALTY_QUOTIENT_MERGE = uint64(16777216) -MIN_SLASHING_PENALTY_QUOTIENT_MERGE = uint64(32) -PROPORTIONAL_SLASHING_MULTIPLIER_MERGE = uint64(3) - - -class Configuration(NamedTuple): - PRESET_BASE: str - MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: uint64 - MIN_GENESIS_TIME: uint64 - GENESIS_FORK_VERSION: Version - GENESIS_DELAY: uint64 - SECONDS_PER_SLOT: uint64 - SECONDS_PER_ETH1_BLOCK: uint64 - MIN_VALIDATOR_WITHDRAWABILITY_DELAY: uint64 - SHARD_COMMITTEE_PERIOD: uint64 - ETH1_FOLLOW_DISTANCE: uint64 - EJECTION_BALANCE: Gwei - MIN_PER_EPOCH_CHURN_LIMIT: uint64 - CHURN_LIMIT_QUOTIENT: uint64 - PROPOSER_SCORE_BOOST: uint64 - INACTIVITY_SCORE_BIAS: uint64 - INACTIVITY_SCORE_RECOVERY_RATE: uint64 - ALTAIR_FORK_VERSION: Version - ALTAIR_FORK_EPOCH: Epoch - TERMINAL_TOTAL_DIFFICULTY: int - TERMINAL_BLOCK_HASH: Hash32 - TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: int - MERGE_FORK_VERSION: Version - MERGE_FORK_EPOCH: Epoch - - -config = Configuration( - PRESET_BASE="minimal", - MIN_GENESIS_ACTIVE_VALIDATOR_COUNT=uint64(64), - MIN_GENESIS_TIME=uint64(1578009600), - GENESIS_FORK_VERSION=Version('0x00000001'), - GENESIS_DELAY=uint64(300), - SECONDS_PER_SLOT=uint64(6), - SECONDS_PER_ETH1_BLOCK=uint64(14), - MIN_VALIDATOR_WITHDRAWABILITY_DELAY=uint64(256), - SHARD_COMMITTEE_PERIOD=uint64(64), - ETH1_FOLLOW_DISTANCE=uint64(16), - EJECTION_BALANCE=Gwei(16000000000), - MIN_PER_EPOCH_CHURN_LIMIT=uint64(4), - CHURN_LIMIT_QUOTIENT=uint64(32), - PROPOSER_SCORE_BOOST=uint64(70), - INACTIVITY_SCORE_BIAS=uint64(4), - INACTIVITY_SCORE_RECOVERY_RATE=uint64(16), - ALTAIR_FORK_VERSION=Version('0x01000001'), - ALTAIR_FORK_EPOCH=Epoch(18446744073709551615), - TERMINAL_TOTAL_DIFFICULTY=115792089237316195423570985008687907853269984665640564039457584007913129638912, - TERMINAL_BLOCK_HASH=Hash32('0x0000000000000000000000000000000000000000000000000000000000000000'), - TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH=18446744073709551615, - MERGE_FORK_VERSION=Version('0x02000001'), - MERGE_FORK_EPOCH=Epoch(18446744073709551615), -) - - -class Fork(Container): - previous_version: Version - current_version: Version - epoch: Epoch # Epoch of latest fork - - -class ForkData(Container): - current_version: Version - genesis_validators_root: Root - - -class Checkpoint(Container): - epoch: Epoch - root: Root - - -class Validator(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 # Commitment to pubkey for withdrawals - effective_balance: Gwei # Balance at stake - slashed: boolean - # Status epochs - activation_eligibility_epoch: Epoch # When criteria for activation were met - activation_epoch: Epoch - exit_epoch: Epoch - withdrawable_epoch: Epoch # When validator can withdraw funds - - -class AttestationData(Container): - slot: Slot - index: CommitteeIndex - # LMD GHOST vote - beacon_block_root: Root - # FFG vote - source: Checkpoint - target: Checkpoint - - -class IndexedAttestation(Container): - attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE] - data: AttestationData - signature: BLSSignature - - -class PendingAttestation(Container): - aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] - data: AttestationData - inclusion_delay: Slot - proposer_index: ValidatorIndex - - -class Eth1Data(Container): - deposit_root: Root - deposit_count: uint64 - block_hash: Hash32 - - -class HistoricalBatch(Container): - block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - - -class DepositMessage(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 - amount: Gwei - - -class DepositData(Container): - pubkey: BLSPubkey - withdrawal_credentials: Bytes32 - amount: Gwei - signature: BLSSignature # Signing over DepositMessage - - -class BeaconBlockHeader(Container): - slot: Slot - proposer_index: ValidatorIndex - parent_root: Root - state_root: Root - body_root: Root - - -class SigningData(Container): - object_root: Root - domain: Domain - - -class AttesterSlashing(Container): - attestation_1: IndexedAttestation - attestation_2: IndexedAttestation - - -class Attestation(Container): - aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] - data: AttestationData - signature: BLSSignature - - -class Deposit(Container): - proof: Vector[Bytes32, DEPOSIT_CONTRACT_TREE_DEPTH + 1] # Merkle path to deposit root - data: DepositData - - -class VoluntaryExit(Container): - epoch: Epoch # Earliest epoch when voluntary exit can be processed - validator_index: ValidatorIndex - - -class SignedVoluntaryExit(Container): - message: VoluntaryExit - signature: BLSSignature - - -class SignedBeaconBlockHeader(Container): - message: BeaconBlockHeader - signature: BLSSignature - - -class ProposerSlashing(Container): - signed_header_1: SignedBeaconBlockHeader - signed_header_2: SignedBeaconBlockHeader - - -class Eth1Block(Container): - timestamp: uint64 - deposit_root: Root - deposit_count: uint64 - # All other eth1 block fields - - -class AggregateAndProof(Container): - aggregator_index: ValidatorIndex - aggregate: Attestation - selection_proof: BLSSignature - - -class SignedAggregateAndProof(Container): - message: AggregateAndProof - signature: BLSSignature - - -class SyncAggregate(Container): - sync_committee_bits: Bitvector[SYNC_COMMITTEE_SIZE] - sync_committee_signature: BLSSignature - - -class SyncCommittee(Container): - pubkeys: Vector[BLSPubkey, SYNC_COMMITTEE_SIZE] - aggregate_pubkey: BLSPubkey - - -class SyncCommitteeMessage(Container): - # Slot to which this contribution pertains - slot: Slot - # Block root for this signature - beacon_block_root: Root - # Index of the validator that produced this signature - validator_index: ValidatorIndex - # Signature by the validator over the block root of `slot` - signature: BLSSignature - - -class SyncCommitteeContribution(Container): - # Slot to which this contribution pertains - slot: Slot - # Block root for this contribution - beacon_block_root: Root - # The subcommittee this contribution pertains to out of the broader sync committee - subcommittee_index: uint64 - # A bit is set if a signature from the validator at the corresponding - # index in the subcommittee is present in the aggregate `signature`. - aggregation_bits: Bitvector[SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT] - # Signature by the validator(s) over the block root of `slot` - signature: BLSSignature - - -class ContributionAndProof(Container): - aggregator_index: ValidatorIndex - contribution: SyncCommitteeContribution - selection_proof: BLSSignature - - -class SignedContributionAndProof(Container): - message: ContributionAndProof - signature: BLSSignature - - -class SyncAggregatorSelectionData(Container): - slot: Slot - subcommittee_index: uint64 - - -class LightClientSnapshot(Container): - # Beacon block header - header: BeaconBlockHeader - # Sync committees corresponding to the header - current_sync_committee: SyncCommittee - next_sync_committee: SyncCommittee - - -class LightClientUpdate(Container): - # Update beacon block header - header: BeaconBlockHeader - # Next sync committee corresponding to the header - next_sync_committee: SyncCommittee - next_sync_committee_branch: Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_INDEX)] - # Finality proof for the update header - finality_header: BeaconBlockHeader - finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)] - # Sync committee aggregate signature - sync_committee_bits: Bitvector[SYNC_COMMITTEE_SIZE] - sync_committee_signature: BLSSignature - # Fork version for the aggregate signature - fork_version: Version - - -class ExecutionPayload(Container): - # Execution block header fields - parent_hash: Hash32 - fee_recipient: ExecutionAddress # 'beneficiary' in the yellow paper - state_root: Bytes32 - receipt_root: Bytes32 # 'receipts root' in the yellow paper - logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] - random: Bytes32 # 'difficulty' in the yellow paper - block_number: uint64 # 'number' in the yellow paper - gas_limit: uint64 - gas_used: uint64 - timestamp: uint64 - extra_data: ByteList[MAX_EXTRA_DATA_BYTES] - base_fee_per_gas: uint256 - # Extra payload fields - block_hash: Hash32 # Hash of execution block - transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD] - - -class BeaconBlockBody(Container): - randao_reveal: BLSSignature - eth1_data: Eth1Data # Eth1 data vote - graffiti: Bytes32 # Arbitrary data - # Operations - proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] - attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS] - attestations: List[Attestation, MAX_ATTESTATIONS] - deposits: List[Deposit, MAX_DEPOSITS] - voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS] - sync_aggregate: SyncAggregate - # Execution - execution_payload: ExecutionPayload # [New in Merge] - - -class BeaconBlock(Container): - slot: Slot - proposer_index: ValidatorIndex - parent_root: Root - state_root: Root - body: BeaconBlockBody - - -class SignedBeaconBlock(Container): - message: BeaconBlock - signature: BLSSignature - - -class ExecutionPayloadHeader(Container): - # Execution block header fields - parent_hash: Hash32 - fee_recipient: ExecutionAddress - state_root: Bytes32 - receipt_root: Bytes32 - logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] - random: Bytes32 - block_number: uint64 - gas_limit: uint64 - gas_used: uint64 - timestamp: uint64 - extra_data: ByteList[MAX_EXTRA_DATA_BYTES] - base_fee_per_gas: uint256 - # Extra payload fields - block_hash: Hash32 # Hash of execution block - transactions_root: Root - - -class PowBlock(Container): - block_hash: Hash32 - parent_hash: Hash32 - total_difficulty: uint256 - - -class WithdrawalReceipt(Container): - index: WithdrawalReceiptIndex - address: ExecutionAddress - amount: Gwei - - -class BeaconState(Container): - # Versioning - genesis_time: uint64 - genesis_validators_root: Root - slot: Slot - fork: Fork - # History - latest_block_header: BeaconBlockHeader - block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] - # Eth1 - eth1_data: Eth1Data - eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] - eth1_deposit_index: uint64 - # Registry - validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] - balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] - # Randomness - randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] - # Slashings - slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances - # Participation - previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - # Finality - justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch - previous_justified_checkpoint: Checkpoint - current_justified_checkpoint: Checkpoint - finalized_checkpoint: Checkpoint - # Inactivity - inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] - # Sync - current_sync_committee: SyncCommittee - next_sync_committee: SyncCommittee - # Execution - latest_execution_payload_header: ExecutionPayloadHeader - # Withdrawals - withdrawal_receipts: List[WithdrawalReceipt, WITHDRAWAL_RECEIPT_LIMIT] # [New in Withdrawals] - - -@dataclass(eq=True, frozen=True) -class LatestMessage(object): - epoch: Epoch - root: Root - - -@dataclass -class Store(object): - time: uint64 - genesis_time: uint64 - justified_checkpoint: Checkpoint - finalized_checkpoint: Checkpoint - best_justified_checkpoint: Checkpoint - proposer_boost_root: Root - blocks: Dict[Root, BeaconBlock] = field(default_factory=dict) - block_states: Dict[Root, BeaconState] = field(default_factory=dict) - checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict) - latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict) - - -@dataclass -class LightClientStore(object): - snapshot: LightClientSnapshot - valid_updates: Set[LightClientUpdate] - - -@dataclass -class PayloadAttributes(object): - timestamp: uint64 - random: Bytes32 - suggested_fee_recipient: ExecutionAddress - - -class ExecutionEngine(Protocol): - - def execute_payload(self, execution_payload: ExecutionPayload) -> bool: - """ - Return ``True`` if and only if ``execution_payload`` is valid with respect to ``self.execution_state``. - """ - ... - - def notify_forkchoice_updated(self, - head_block_hash: Hash32, - finalized_block_hash: Hash32, - payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]: - ... - - def get_payload(self, payload_id: PayloadId) -> ExecutionPayload: - """ - Return ``execution_payload`` object. - """ - ... - - -def integer_squareroot(n: uint64) -> uint64: - """ - Return the largest integer ``x`` such that ``x**2 <= n``. - """ - x = n - y = (x + 1) // 2 - while y < x: - x = y - y = (x + n // x) // 2 - return x - - -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)) - - -def bytes_to_uint64(data: bytes) -> uint64: - """ - Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian. - """ - return uint64(int.from_bytes(data, ENDIANNESS)) - - -def is_active_validator(validator: Validator, epoch: Epoch) -> bool: - """ - Check if ``validator`` is active. - """ - return validator.activation_epoch <= epoch < validator.exit_epoch - - -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 - ) - - -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 - ) - - -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) - - -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) - ) - - -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) - - -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 - - -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 - - -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 - - -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)] - - -def compute_epoch_at_slot(slot: Slot) -> Epoch: - """ - Return the epoch number at ``slot``. - """ - return Epoch(slot // SLOTS_PER_EPOCH) - - -def compute_start_slot_at_epoch(epoch: Epoch) -> Slot: - """ - Return the start slot of ``epoch``. - """ - return Slot(epoch * SLOTS_PER_EPOCH) - - -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) - - -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, - )) - - -def compute_fork_digest(current_version: Version, genesis_validators_root: Root) -> ForkDigest: - """ - Return the 4-byte fork digest for the ``current_version`` and ``genesis_validators_root``. - This is a digest primarily used for domain separation on the p2p layer. - 4-bytes suffices for practical separation of forks/chains. - """ - return ForkDigest(compute_fork_data_root(current_version, genesis_validators_root)[:4]) - - -def compute_domain(domain_type: DomainType, fork_version: Version=None, genesis_validators_root: Root=None) -> Domain: - """ - Return the domain for the ``domain_type`` and ``fork_version``. - """ - if fork_version is None: - fork_version = config.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]) - - -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, - )) - - -def get_current_epoch(state: BeaconState) -> Epoch: - """ - Return the current epoch. - """ - return compute_epoch_at_slot(state.slot) - - -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) - - -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)) - - -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] - - -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] - - -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)] - - -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(config.MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // config.CHURN_LIMIT_QUOTIENT) - - -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) - - -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, - )) - - -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, - ) - - -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) - - -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, afterwhich this overflows uint64. - """ - return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, sum([state.validators[index].effective_balance for index in indices]))) - - -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)))) - - -def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch=None) -> Domain: - """ - Return the signature domain (fork version concatenated with domain type) of a message. - """ - epoch = get_current_epoch(state) if epoch is None else epoch - fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version - return compute_domain(domain_type, fork_version, state.genesis_validators_root) - - -def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> IndexedAttestation: - """ - Return the indexed attestation corresponding to ``attestation``. - """ - attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) - - return IndexedAttestation( - attesting_indices=sorted(attesting_indices), - data=attestation.data, - signature=attestation.signature, - ) - - -def get_attesting_indices(state: BeaconState, - data: AttestationData, - bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]: - """ - Return the set of attesting indices corresponding to ``data`` and ``bits``. - """ - committee = get_beacon_committee(state, data.slot, data.index) - return set(index for i, index in enumerate(committee) if bits[i]) - - -def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: - """ - Increase the validator balance at index ``index`` by ``delta``. - """ - state.balances[index] += delta - - -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 - - -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 + config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY) - - -def slash_validator(state: BeaconState, - slashed_index: ValidatorIndex, - whistleblower_index: ValidatorIndex=None) -> None: - """ - Slash the validator with index ``slashed_index``. - """ - epoch = get_current_epoch(state) - initiate_validator_exit(state, slashed_index) - validator = state.validators[slashed_index] - validator.slashed = True - validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR)) - state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance - slashing_penalty = validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_MERGE # [Modified in Merge] - 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)) - - -def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, - eth1_timestamp: uint64, - deposits: Sequence[Deposit], - execution_payload_header: ExecutionPayloadHeader=ExecutionPayloadHeader() - ) -> BeaconState: - fork = Fork( - previous_version=config.MERGE_FORK_VERSION, # [Modified in Merge] for testing only - current_version=config.MERGE_FORK_VERSION, # [Modified in Merge] - epoch=GENESIS_EPOCH, - ) - state = BeaconState( - genesis_time=eth1_timestamp + config.GENESIS_DELAY, - fork=fork, - eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), - 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) - - # Fill in sync committees - # Note: A duplicate committee is assigned for the current and next committee at genesis - state.current_sync_committee = get_next_sync_committee(state) - state.next_sync_committee = get_next_sync_committee(state) - - # [New in Merge] Initialize the execution payload header - # If empty, will initialize a chain that has not yet gone through the Merge transition - state.latest_execution_payload_header = execution_payload_header - - return state - - -def is_valid_genesis_state(state: BeaconState) -> bool: - if state.genesis_time < config.MIN_GENESIS_TIME: - return False - if len(get_active_validator_indices(state, GENESIS_EPOCH)) < config.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: - return False - return True - - -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) - - -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) - - -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) - - -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 - - -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) - process_historical_roots_update(state) - process_participation_flag_updates(state) - process_sync_committee_updates(state) - process_withdrawals(state) # [New in Withdrawals] - - -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 - - -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) - ] - - -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) - ] - - -def get_unslashed_attesting_indices(state: BeaconState, - attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]: - output = set() # type: Set[ValidatorIndex] - for a in attestations: - output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits)) - return set(filter(lambda index: not state.validators[index].slashed, output)) - - -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)) - - -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) - - -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 - - -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)) - - -def get_proposer_reward(state: BeaconState, attesting_index: ValidatorIndex) -> Gwei: - return Gwei(get_base_reward(state, attesting_index) // PROPOSER_REWARD_QUOTIENT) - - -def get_finality_delay(state: BeaconState) -> uint64: - return get_previous_epoch(state) - state.finalized_checkpoint.epoch - - -def is_in_inactivity_leak(state: BeaconState) -> bool: - return get_finality_delay(state) > MIN_EPOCHS_TO_INACTIVITY_PENALTY - - -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) - ] - - -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 - - -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) - - -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) - - -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) - - -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.data, a.aggregation_bits) - ], 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 - - -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 Merge] - penalty_denominator = config.INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_MERGE - penalties[index] += Gwei(penalty_numerator // penalty_denominator) - return rewards, penalties - - -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 - - -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]) - - -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 <= config.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)) - - -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_MERGE, # [Modified in Merge] - 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) - - -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 = [] - - -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) - - -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) - - -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) - - -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)) - - -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 = [] - - -def process_block(state: BeaconState, block: BeaconBlock) -> None: - process_block_header(state, block) - if is_execution_enabled(state, block.body): - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Merge] - process_randao(state, block.body) - process_eth1_data(state, block.body) - process_operations(state, block.body) - process_sync_aggregate(state, block.body.sync_aggregate) - - -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 - - -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 - - -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 - - -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) - - -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) - - -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 - - -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, data, attestation.aggregation_bits): - 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) - - -def get_validator_from_deposit(state: BeaconState, deposit: Deposit) -> Validator: - amount = deposit.data.amount - effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) - - return Validator( - pubkey=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - activation_eligibility_epoch=FAR_FUTURE_EPOCH, - activation_epoch=FAR_FUTURE_EPOCH, - exit_epoch=FAR_FUTURE_EPOCH, - withdrawable_epoch=FAR_FUTURE_EPOCH, - effective_balance=effective_balance, - ) - - -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, - depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in - index=state.eth1_deposit_index, - root=state.eth1_data.deposit_root, - ) - - # Deposits must be processed in order - state.eth1_deposit_index += 1 - - pubkey = deposit.data.pubkey - amount = deposit.data.amount - validator_pubkeys = [validator.pubkey for validator 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=deposit.data.pubkey, - withdrawal_credentials=deposit.data.withdrawal_credentials, - amount=deposit.data.amount, - ) - domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks - signing_root = compute_signing_root(deposit_message, domain) - # Initialize validator if the deposit signature is valid - if bls.Verify(pubkey, signing_root, deposit.data.signature): - state.validators.append(get_validator_from_deposit(state, deposit)) - state.balances.append(amount) - state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) - state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) - state.inactivity_scores.append(uint64(0)) - else: - # Increase balance by deposit amount - index = ValidatorIndex(validator_pubkeys.index(pubkey)) - increase_balance(state, index, amount) - - -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 + config.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) - - -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 + config.SECONDS_PER_SLOT * anchor_state.slot), - genesis_time=anchor_state.genesis_time, - justified_checkpoint=justified_checkpoint, - finalized_checkpoint=finalized_checkpoint, - best_justified_checkpoint=justified_checkpoint, - proposer_boost_root=proposer_boost_root, - blocks={anchor_root: copy(anchor_block)}, - block_states={anchor_root: copy(anchor_state)}, - checkpoint_states={justified_checkpoint: copy(anchor_state)}, - ) - - -def get_slots_since_genesis(store: Store) -> int: - return (store.time - store.genesis_time) // config.SECONDS_PER_SLOT - - -def get_current_slot(store: Store) -> Slot: - return Slot(GENESIS_SLOT + get_slots_since_genesis(store)) - - -def compute_slots_since_epoch_start(slot: Slot) -> int: - return slot - compute_start_slot_at_epoch(compute_epoch_at_slot(slot)) - - -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) - elif block.slot == slot: - return root - else: - # root is older than queried slot, thus a skip slot. Return most recent root prior to slot - return root - - -def get_latest_attesting_balance(store: Store, root: Root) -> Gwei: - state = store.checkpoint_states[store.justified_checkpoint] - active_indices = get_active_validator_indices(state, get_current_epoch(state)) - attestation_score = Gwei(sum( - state.validators[i].effective_balance for i in active_indices - if (i in store.latest_messages - and get_ancestor(store, store.latest_messages[i].root, store.blocks[root].slot) == root) - )) - proposer_score = Gwei(0) - if store.proposer_boost_root != Root(): - block = store.blocks[root] - if get_ancestor(store, root, block.slot) == store.proposer_boost_root: - num_validators = len(get_active_validator_indices(state, get_current_epoch(state))) - avg_balance = get_total_active_balance(state) // num_validators - committee_size = num_validators // SLOTS_PER_EPOCH - committee_weight = committee_size * avg_balance - proposer_score = (committee_weight * config.PROPOSER_SCORE_BOOST) // 100 - return attestation_score + proposer_score - - -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 - - # If leaf block, check finalized/justified checkpoints as matching latest. - head_state = store.block_states[block_root] - - correct_justified = ( - store.justified_checkpoint.epoch == GENESIS_EPOCH - or head_state.current_justified_checkpoint == store.justified_checkpoint - ) - correct_finalized = ( - store.finalized_checkpoint.epoch == GENESIS_EPOCH - or head_state.finalized_checkpoint == store.finalized_checkpoint - ) - # 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 - - -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 - - -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 - head = max(children, key=lambda root: (get_latest_attesting_balance(store, root), root)) - - -def should_update_justified_checkpoint(store: Store, new_justified_checkpoint: Checkpoint) -> bool: - """ - To address the bouncing attack, only update conflicting justified - checkpoints in the fork choice if in the early slots of the epoch. - Otherwise, delay incorporation of new justified checkpoint until next epoch boundary. - - See https://ethresear.ch/t/prevention-of-bouncing-attack-on-ffg/6114 for more detailed analysis and discussion. - """ - if compute_slots_since_epoch_start(get_current_slot(store)) < SAFE_SLOTS_TO_UPDATE_JUSTIFIED: - return True - - justified_slot = compute_start_slot_at_epoch(store.justified_checkpoint.epoch) - if not get_ancestor(store, new_justified_checkpoint.root, justified_slot) == store.justified_checkpoint.root: - return False - - return True - - -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 = compute_epoch_at_slot(get_current_slot(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] - - -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) - - # Attestations target be for a known block. If target block is unknown, delay consideration until the 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 - target_slot = compute_start_slot_at_epoch(target.epoch) - assert target.root == get_ancestor(store, attestation.data.beacon_block_root, target_slot) - - # 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 - - -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 - - -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 - for i in 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) - - -def on_tick(store: Store, time: uint64) -> None: - previous_slot = get_current_slot(store) - - # update store time - store.time = time - - current_slot = get_current_slot(store) - - # Reset store.proposer_boost_root if this is a new slot - if current_slot > previous_slot: - store.proposer_boost_root = Root() - - # Not a new epoch, return - if not (current_slot > previous_slot and compute_slots_since_epoch_start(current_slot) == 0): - return - - # Update store.justified_checkpoint if a better checkpoint on the store.finalized_checkpoint chain - if store.best_justified_checkpoint.epoch > store.justified_checkpoint.epoch: - finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) - ancestor_at_finalized_slot = get_ancestor(store, store.best_justified_checkpoint.root, finalized_slot) - if ancestor_at_finalized_slot == store.finalized_checkpoint.root: - store.justified_checkpoint = store.best_justified_checkpoint - - -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 the 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 - assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root - - # Check the block is valid and compute the post-state - state = pre_state.copy() - state_transition(state, signed_block, True) - - # [New in Merge] - if is_merge_transition_block(pre_state, block.body): - validate_merge_block(block) - - # Add new block to the store - store.blocks[hash_tree_root(block)] = block - # Add new state for this block to the store - store.block_states[hash_tree_root(block)] = state - - # Add proposer score boost if the block is timely - time_into_slot = (store.time - store.genesis_time) % config.SECONDS_PER_SLOT - is_before_attesting_interval = time_into_slot < config.SECONDS_PER_SLOT // INTERVALS_PER_SLOT - if get_current_slot(store) == block.slot and is_before_attesting_interval: - store.proposer_boost_root = hash_tree_root(block) - - # Update justified checkpoint - if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch: - if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch: - store.best_justified_checkpoint = state.current_justified_checkpoint - if should_update_justified_checkpoint(store, state.current_justified_checkpoint): - store.justified_checkpoint = state.current_justified_checkpoint - - # Update finalized checkpoint - if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch: - store.finalized_checkpoint = state.finalized_checkpoint - store.justified_checkpoint = state.current_justified_checkpoint - - -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) - - -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)) - - -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 - - -def is_proposer(state: BeaconState, validator_index: ValidatorIndex) -> bool: - return get_beacon_proposer_index(state) == validator_index - - -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) - - -def compute_time_at_slot(state: BeaconState, slot: Slot) -> uint64: - return uint64(state.genesis_time + slot * config.SECONDS_PER_SLOT) - - -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) - - -def is_candidate_block(block: Eth1Block, period_start: uint64) -> bool: - return ( - block.timestamp + config.SECONDS_PER_ETH1_BLOCK * config.ETH1_FOLLOW_DISTANCE <= period_start - and block.timestamp + config.SECONDS_PER_ETH1_BLOCK * config.ETH1_FOLLOW_DISTANCE * 2 >= period_start - ) - - -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, - key=lambda v: (valid_votes.count(v), -valid_votes.index(v)), # Tiebreak by smallest distance - default=default_vote - ) - - -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) - - -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) - - -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) - - -def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, committee_index: CommitteeIndex) -> uint64: - """ - 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 uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT) - - -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) - - -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 - - -def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature: - signatures = [attestation.signature for attestation in attestations] - return bls.Aggregate(signatures) - - -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), - ) - - -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) - - -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 = config.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 - - -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 ws_state.latest_block_header.state_root == 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 - - -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 - - -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 - - -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 - - -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) - - -def get_base_reward_per_increment(state: BeaconState) -> Gwei: - return Gwei(EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR // integer_squareroot(get_total_active_balance(state))) - - -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)) - - -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. - """ - if data.target.epoch == get_current_epoch(state): - justified_checkpoint = state.current_justified_checkpoint - else: - justified_checkpoint = state.previous_justified_checkpoint - - # Matching roots - is_matching_source = data.source == justified_checkpoint - is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch) - is_matching_head = is_matching_target and data.beacon_block_root == get_block_root_at_slot(state, data.slot) - 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 - - -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 - - -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 - 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) - 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) - - -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] += config.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(config.INACTIVITY_SCORE_RECOVERY_RATE, state.inactivity_scores[index]) - - -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))] - - -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) - - -def eth_aggregate_pubkeys(pubkeys: Sequence[BLSPubkey]) -> BLSPubkey: - return bls.AggregatePKs(pubkeys) - - -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) - - -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, data, attestation.aggregation_bits): - for flag_index in participation_flag_indices: - epoch_participation[index] = add_flag(epoch_participation[index], flag_index) - - -def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState: - epoch = phase0.get_current_epoch(pre) - post = BeaconState( - # Versioning - 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=config.ALTAIR_FORK_VERSION, - epoch=epoch, - ), - # History - latest_block_header=pre.latest_block_header, - block_roots=pre.block_roots, - state_roots=pre.state_roots, - historical_roots=pre.historical_roots, - # Eth1 - eth1_data=pre.eth1_data, - eth1_data_votes=pre.eth1_data_votes, - eth1_deposit_index=pre.eth1_deposit_index, - # Registry - validators=pre.validators, - balances=pre.balances, - # Randomness - randao_mixes=pre.randao_mixes, - # Slashings - slashings=pre.slashings, - # Participation - previous_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))], - current_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))], - # Finality - 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 - 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 - - -def compute_sync_committee_period(epoch: Epoch) -> uint64: - return epoch // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - - -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 - - -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 - - -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, - ) - - -def compute_subnets_for_sync_committee(state: BeaconState, validator_index: ValidatorIndex) -> Set[uint64]: - 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([ - uint64(index // (SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT)) - for index in sync_committee_indices - ]) - - -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) - - -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 - - -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, - ) - - -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) - - -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] - - -def get_subtree_index(generalized_index: GeneralizedIndex) -> uint64: - return uint64(generalized_index % 2**(floorlog2(generalized_index))) - - -def validate_light_client_update(snapshot: LightClientSnapshot, - update: LightClientUpdate, - genesis_validators_root: Root) -> None: - # Verify update slot is larger than snapshot slot - assert update.header.slot > snapshot.header.slot - - # Verify update does not skip a sync committee period - snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - assert update_period in (snapshot_period, snapshot_period + 1) - - # Verify update header root is the finalized root of the finality header, if specified - if update.finality_header == BeaconBlockHeader(): - signed_header = update.header - assert update.finality_branch == [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))] - else: - signed_header = update.finality_header - assert is_valid_merkle_branch( - leaf=hash_tree_root(update.header), - branch=update.finality_branch, - depth=floorlog2(FINALIZED_ROOT_INDEX), - index=get_subtree_index(FINALIZED_ROOT_INDEX), - root=update.finality_header.state_root, - ) - - # Verify update next sync committee if the update period incremented - if update_period == snapshot_period: - sync_committee = snapshot.current_sync_committee - assert update.next_sync_committee_branch == [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))] - else: - sync_committee = snapshot.next_sync_committee - assert is_valid_merkle_branch( - leaf=hash_tree_root(update.next_sync_committee), - branch=update.next_sync_committee_branch, - depth=floorlog2(NEXT_SYNC_COMMITTEE_INDEX), - index=get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX), - root=update.header.state_root, - ) - - # Verify sync committee has sufficient participants - assert sum(update.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS - - # Verify sync committee aggregate signature - participant_pubkeys = [pubkey for (bit, pubkey) in zip(update.sync_committee_bits, sync_committee.pubkeys) if bit] - domain = compute_domain(DOMAIN_SYNC_COMMITTEE, update.fork_version, genesis_validators_root) - signing_root = compute_signing_root(signed_header, domain) - assert bls.FastAggregateVerify(participant_pubkeys, signing_root, update.sync_committee_signature) - - -def apply_light_client_update(snapshot: LightClientSnapshot, update: LightClientUpdate) -> None: - snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD - if update_period == snapshot_period + 1: - snapshot.current_sync_committee = snapshot.next_sync_committee - snapshot.next_sync_committee = update.next_sync_committee - snapshot.header = update.header - - -def process_light_client_update(store: LightClientStore, update: LightClientUpdate, current_slot: Slot, - genesis_validators_root: Root) -> None: - validate_light_client_update(store.snapshot, update, genesis_validators_root) - store.valid_updates.add(update) - - update_timeout = SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD - if ( - sum(update.sync_committee_bits) * 3 >= len(update.sync_committee_bits) * 2 - and update.finality_header != BeaconBlockHeader() - ): - # Apply update if (1) 2/3 quorum is reached and (2) we have a finality proof. - # Note that (2) means that the current light client design needs finality. - # It may be changed to re-organizable light client design. See the on-going issue consensus-specs#2182. - apply_light_client_update(store.snapshot, update) - store.valid_updates = set() - elif current_slot > store.snapshot.header.slot + update_timeout: - # Forced best update when the update timeout has elapsed - apply_light_client_update(store.snapshot, - max(store.valid_updates, key=lambda update: sum(update.sync_committee_bits))) - store.valid_updates = set() - - -def is_merge_transition_complete(state: BeaconState) -> bool: - return state.latest_execution_payload_header != ExecutionPayloadHeader() - - -def is_merge_transition_block(state: BeaconState, body: BeaconBlockBody) -> bool: - return not is_merge_transition_complete(state) and body.execution_payload != ExecutionPayload() - - -def is_execution_enabled(state: BeaconState, body: BeaconBlockBody) -> bool: - return is_merge_transition_block(state, body) or is_merge_transition_complete(state) - - -def compute_timestamp_at_slot(state: BeaconState, slot: Slot) -> uint64: - slots_since_genesis = slot - GENESIS_SLOT - return uint64(state.genesis_time + slots_since_genesis * config.SECONDS_PER_SLOT) - - -def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: - # 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 random - assert payload.random == get_randao_mix(state, get_current_epoch(state)) - # Verify timestamp - assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) - # Verify the execution payload is valid - assert execution_engine.execute_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, - receipt_root=payload.receipt_root, - logs_bloom=payload.logs_bloom, - random=payload.random, - 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), - ) - - -def upgrade_to_merge(pre: altair.BeaconState) -> BeaconState: - epoch = altair.get_current_epoch(pre) - post = BeaconState( - # Versioning - 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=config.MERGE_FORK_VERSION, - epoch=epoch, - ), - # History - latest_block_header=pre.latest_block_header, - block_roots=pre.block_roots, - state_roots=pre.state_roots, - historical_roots=pre.historical_roots, - # Eth1 - eth1_data=pre.eth1_data, - eth1_data_votes=pre.eth1_data_votes, - eth1_deposit_index=pre.eth1_deposit_index, - # Registry - validators=pre.validators, - balances=pre.balances, - # Randomness - randao_mixes=pre.randao_mixes, - # Slashings - slashings=pre.slashings, - # Participation - previous_epoch_participation=pre.previous_epoch_participation, - current_epoch_participation=pre.current_epoch_participation, - # Finality - 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 - inactivity_scores=pre.inactivity_scores, - # Sync - current_sync_committee=pre.current_sync_committee, - next_sync_committee=pre.next_sync_committee, - # Execution-layer - latest_execution_payload_header=ExecutionPayloadHeader(), - ) - - return post - - -def is_valid_terminal_pow_block(block: PowBlock, parent: PowBlock) -> bool: - is_total_difficulty_reached = block.total_difficulty >= config.TERMINAL_TOTAL_DIFFICULTY - is_parent_total_difficulty_valid = parent.total_difficulty < config.TERMINAL_TOTAL_DIFFICULTY - return is_total_difficulty_reached and is_parent_total_difficulty_valid - - -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 config.TERMINAL_BLOCK_HASH != Hash32(): - # If `config.TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached. - assert compute_epoch_at_slot(block.slot) >= config.TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH - assert block.body.execution_payload.parent_hash == config.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) - - -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 >= config.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 >= config.TERMINAL_TOTAL_DIFFICULTY - if not parent_reached_ttd: - return block - - return None - - -def get_terminal_pow_block(pow_chain: Dict[Hash32, PowBlock]) -> Optional[PowBlock]: - if config.TERMINAL_BLOCK_HASH != Hash32(): - # Terminal block hash override takes precedence over terminal total difficulty - if config.TERMINAL_BLOCK_HASH in pow_chain: - return pow_chain[config.TERMINAL_BLOCK_HASH] - else: - return None - - return get_pow_block_at_terminal_total_difficulty(pow_chain) - - -def prepare_execution_payload(state: BeaconState, - pow_chain: Dict[Hash32, PowBlock], - finalized_block_hash: Hash32, - suggested_fee_recipient: ExecutionAddress, - execution_engine: ExecutionEngine) -> Optional[PayloadId]: - if not is_merge_transition_complete(state): - is_terminal_block_hash_set = config.TERMINAL_BLOCK_HASH != Hash32() - is_activation_epoch_reached = get_current_epoch(state) >= config.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_timestamp_at_slot(state, state.slot), - random=get_randao_mix(state, get_current_epoch(state)), - suggested_fee_recipient=suggested_fee_recipient, - ) - return execution_engine.notify_forkchoice_updated(parent_hash, finalized_block_hash, payload_attributes) - - -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) - - -def withdraw(state: BeaconState, index: ValidatorIndex, amount: Gwei) -> None: - # Decrease the validator's balance - decrease_balance(state, index, amount) - # Create a corresponding withdrawal receipt - receipt = WithdrawalReceipt( - index=WithdrawalReceiptIndex(len(state.withdrawal_receipts)), - address=state.validators[index].withdrawal_credentials[12:], - amount=amount, - ) - state.withdrawal_receipts.append(receipt) - - -def is_withdrawable_validator(validator: Validator, epoch: Epoch) -> bool: - """ - Check if ``validator`` is withdrawable. - """ - return validator.withdrawable_epoch <= epoch - - -def process_withdrawals(state: BeaconState) -> None: - current_epoch = get_current_epoch(state) - for index, validator in enumerate(state.validators): - balance = state.balances[index] - is_balance_nonzero = state.balances[index] == 0 - is_eth1_withdrawal_prefix = validator.withdrawal_credentials[0] != ETH1_ADDRESS_WITHDRAWAL_PREFIX - if is_balance_nonzero and is_eth1_withdrawal_prefix and is_withdrawable_validator(validator, current_epoch): - withdraw(state, index, balance) - - -def get_eth1_data(block: Eth1Block) -> Eth1Data: - """ - A stub function return mocking Eth1Data. - """ - return Eth1Data( - deposit_root=block.deposit_root, - deposit_count=block.deposit_count, - block_hash=hash_tree_root(block)) - - -def cache_this(key_fn, value_fn, lru_size): # type: ignore - cache_dict = LRU(size=lru_size) - - def wrapper(*args, **kw): # type: ignore - key = key_fn(*args, **kw) - nonlocal cache_dict - if key not in cache_dict: - cache_dict[key] = value_fn(*args, **kw) - return cache_dict[key] - return wrapper - - -_compute_shuffled_index = compute_shuffled_index -compute_shuffled_index = cache_this( - lambda index, index_count, seed: (index, index_count, seed), - _compute_shuffled_index, lru_size=SLOTS_PER_EPOCH * 3) - -_get_total_active_balance = get_total_active_balance -get_total_active_balance = cache_this( - lambda state: (state.validators.hash_tree_root(), compute_epoch_at_slot(state.slot)), - _get_total_active_balance, lru_size=10) - -_get_base_reward = get_base_reward -get_base_reward = cache_this( - lambda state, index: (state.validators.hash_tree_root(), state.slot, index), - _get_base_reward, lru_size=2048) - -_get_committee_count_per_slot = get_committee_count_per_slot -get_committee_count_per_slot = cache_this( - lambda state, epoch: (state.validators.hash_tree_root(), epoch), - _get_committee_count_per_slot, lru_size=SLOTS_PER_EPOCH * 3) - -_get_active_validator_indices = get_active_validator_indices -get_active_validator_indices = cache_this( - lambda state, epoch: (state.validators.hash_tree_root(), epoch), - _get_active_validator_indices, lru_size=3) - -_get_beacon_committee = get_beacon_committee -get_beacon_committee = cache_this( - lambda state, slot, index: (state.validators.hash_tree_root(), state.randao_mixes.hash_tree_root(), slot, index), - _get_beacon_committee, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3) - -_get_matching_target_attestations = get_matching_target_attestations -get_matching_target_attestations = cache_this( - lambda state, epoch: (state.hash_tree_root(), epoch), - _get_matching_target_attestations, lru_size=10) - -_get_matching_head_attestations = get_matching_head_attestations -get_matching_head_attestations = cache_this( - lambda state, epoch: (state.hash_tree_root(), epoch), - _get_matching_head_attestations, lru_size=10) - -_get_attesting_indices = get_attesting_indices -get_attesting_indices = cache_this( - lambda state, data, bits: ( - state.randao_mixes.hash_tree_root(), - state.validators.hash_tree_root(), data.hash_tree_root(), bits.hash_tree_root() - ), - _get_attesting_indices, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3) - - -def get_generalized_index(ssz_class: Any, *path: Sequence[PyUnion[int, SSZVariableName]]) -> GeneralizedIndex: - ssz_path = Path(ssz_class) - for item in path: - ssz_path = ssz_path / item - return GeneralizedIndex(ssz_path.gindex()) - - -ExecutionState = Any - - -def get_pow_block(hash: Bytes32) -> Optional[PowBlock]: - return PowBlock(block_hash=hash, parent_hash=Bytes32(), total_difficulty=uint256(0)) - - -def get_execution_state(execution_state_root: Bytes32) -> ExecutionState: - pass - - -def get_pow_chain_head() -> PowBlock: - pass - - -class NoopExecutionEngine(ExecutionEngine): - - def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: - return True - - def notify_forkchoice_updated(self: ExecutionEngine, - head_block_hash: Hash32, - finalized_block_hash: Hash32, - payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]: - pass - - def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload: - raise NotImplementedError("no default block production") - - -EXECUTION_ENGINE = NoopExecutionEngine() - - -assert FINALIZED_ROOT_INDEX == get_generalized_index(BeaconState, 'finalized_checkpoint', 'root') -assert NEXT_SYNC_COMMITTEE_INDEX == get_generalized_index(BeaconState, 'next_sync_committee')