github.com/prysmaticlabs/prysm@v1.4.4/tools/specs-checker/data/specs/phase0/beacon-chain.md (about) 1 ```python 2 def integer_squareroot(n: uint64) -> uint64: 3 """ 4 Return the largest integer ``x`` such that ``x**2 <= n``. 5 """ 6 x = n 7 y = (x + 1) // 2 8 while y < x: 9 x = y 10 y = (x + n // x) // 2 11 return x 12 ``` 13 ```python 14 def xor(bytes_1: Bytes32, bytes_2: Bytes32) -> Bytes32: 15 """ 16 Return the exclusive-or of two 32-byte strings. 17 """ 18 return Bytes32(a ^ b for a, b in zip(bytes_1, bytes_2)) 19 ``` 20 ```python 21 def bytes_to_uint64(data: bytes) -> uint64: 22 """ 23 Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian. 24 """ 25 return uint64(int.from_bytes(data, ENDIANNESS)) 26 ``` 27 ```python 28 def is_active_validator(validator: Validator, epoch: Epoch) -> bool: 29 """ 30 Check if ``validator`` is active. 31 """ 32 return validator.activation_epoch <= epoch < validator.exit_epoch 33 ``` 34 ```python 35 def is_eligible_for_activation_queue(validator: Validator) -> bool: 36 """ 37 Check if ``validator`` is eligible to be placed into the activation queue. 38 """ 39 return ( 40 validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH 41 and validator.effective_balance == MAX_EFFECTIVE_BALANCE 42 ) 43 ``` 44 ```python 45 def is_eligible_for_activation(state: BeaconState, validator: Validator) -> bool: 46 """ 47 Check if ``validator`` is eligible for activation. 48 """ 49 return ( 50 # Placement in queue is finalized 51 validator.activation_eligibility_epoch <= state.finalized_checkpoint.epoch 52 # Has not yet been activated 53 and validator.activation_epoch == FAR_FUTURE_EPOCH 54 ) 55 ``` 56 ```python 57 def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool: 58 """ 59 Check if ``validator`` is slashable. 60 """ 61 return (not validator.slashed) and (validator.activation_epoch <= epoch < validator.withdrawable_epoch) 62 ``` 63 ```python 64 def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationData) -> bool: 65 """ 66 Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG rules. 67 """ 68 return ( 69 # Double vote 70 (data_1 != data_2 and data_1.target.epoch == data_2.target.epoch) or 71 # Surround vote 72 (data_1.source.epoch < data_2.source.epoch and data_2.target.epoch < data_1.target.epoch) 73 ) 74 ``` 75 ```python 76 def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool: 77 """ 78 Check if ``indexed_attestation`` is not empty, has sorted and unique indices and has a valid aggregate signature. 79 """ 80 # Verify indices are sorted and unique 81 indices = indexed_attestation.attesting_indices 82 if len(indices) == 0 or not indices == sorted(set(indices)): 83 return False 84 # Verify aggregate signature 85 pubkeys = [state.validators[i].pubkey for i in indices] 86 domain = get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch) 87 signing_root = compute_signing_root(indexed_attestation.data, domain) 88 return bls.FastAggregateVerify(pubkeys, signing_root, indexed_attestation.signature) 89 ``` 90 ```python 91 def is_valid_merkle_branch(leaf: Bytes32, branch: Sequence[Bytes32], depth: uint64, index: uint64, root: Root) -> bool: 92 """ 93 Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and ``branch``. 94 """ 95 value = leaf 96 for i in range(depth): 97 if index // (2**i) % 2: 98 value = hash(branch[i] + value) 99 else: 100 value = hash(value + branch[i]) 101 return value == root 102 ``` 103 ```python 104 def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> uint64: 105 """ 106 Return the shuffled index corresponding to ``seed`` (and ``index_count``). 107 """ 108 assert index < index_count 109 110 # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) 111 # See the 'generalized domain' algorithm on page 3 112 for current_round in range(SHUFFLE_ROUND_COUNT): 113 pivot = bytes_to_uint64(hash(seed + uint_to_bytes(uint8(current_round)))[0:8]) % index_count 114 flip = (pivot + index_count - index) % index_count 115 position = max(index, flip) 116 source = hash( 117 seed 118 + uint_to_bytes(uint8(current_round)) 119 + uint_to_bytes(uint32(position // 256)) 120 ) 121 byte = uint8(source[(position % 256) // 8]) 122 bit = (byte >> (position % 8)) % 2 123 index = flip if bit else index 124 125 return index 126 ``` 127 ```python 128 def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Bytes32) -> ValidatorIndex: 129 """ 130 Return from ``indices`` a random index sampled by effective balance. 131 """ 132 assert len(indices) > 0 133 MAX_RANDOM_BYTE = 2**8 - 1 134 i = uint64(0) 135 total = uint64(len(indices)) 136 while True: 137 candidate_index = indices[compute_shuffled_index(i % total, total, seed)] 138 random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32] 139 effective_balance = state.validators[candidate_index].effective_balance 140 if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: 141 return candidate_index 142 i += 1 143 ``` 144 ```python 145 def compute_committee(indices: Sequence[ValidatorIndex], 146 seed: Bytes32, 147 index: uint64, 148 count: uint64) -> Sequence[ValidatorIndex]: 149 """ 150 Return the committee corresponding to ``indices``, ``seed``, ``index``, and committee ``count``. 151 """ 152 start = (len(indices) * index) // count 153 end = (len(indices) * uint64(index + 1)) // count 154 return [indices[compute_shuffled_index(uint64(i), uint64(len(indices)), seed)] for i in range(start, end)] 155 ``` 156 ```python 157 def compute_epoch_at_slot(slot: Slot) -> Epoch: 158 """ 159 Return the epoch number at ``slot``. 160 """ 161 return Epoch(slot // SLOTS_PER_EPOCH) 162 ``` 163 ```python 164 def compute_start_slot_at_epoch(epoch: Epoch) -> Slot: 165 """ 166 Return the start slot of ``epoch``. 167 """ 168 return Slot(epoch * SLOTS_PER_EPOCH) 169 ``` 170 ```python 171 def compute_activation_exit_epoch(epoch: Epoch) -> Epoch: 172 """ 173 Return the epoch during which validator activations and exits initiated in ``epoch`` take effect. 174 """ 175 return Epoch(epoch + 1 + MAX_SEED_LOOKAHEAD) 176 ``` 177 ```python 178 def compute_fork_data_root(current_version: Version, genesis_validators_root: Root) -> Root: 179 """ 180 Return the 32-byte fork data root for the ``current_version`` and ``genesis_validators_root``. 181 This is used primarily in signature domains to avoid collisions across forks/chains. 182 """ 183 return hash_tree_root(ForkData( 184 current_version=current_version, 185 genesis_validators_root=genesis_validators_root, 186 )) 187 ``` 188 ```python 189 def compute_fork_digest(current_version: Version, genesis_validators_root: Root) -> ForkDigest: 190 """ 191 Return the 4-byte fork digest for the ``current_version`` and ``genesis_validators_root``. 192 This is a digest primarily used for domain separation on the p2p layer. 193 4-bytes suffices for practical separation of forks/chains. 194 """ 195 return ForkDigest(compute_fork_data_root(current_version, genesis_validators_root)[:4]) 196 ``` 197 ```python 198 def compute_domain(domain_type: DomainType, fork_version: Version=None, genesis_validators_root: Root=None) -> Domain: 199 """ 200 Return the domain for the ``domain_type`` and ``fork_version``. 201 """ 202 if fork_version is None: 203 fork_version = GENESIS_FORK_VERSION 204 if genesis_validators_root is None: 205 genesis_validators_root = Root() # all bytes zero by default 206 fork_data_root = compute_fork_data_root(fork_version, genesis_validators_root) 207 return Domain(domain_type + fork_data_root[:28]) 208 ``` 209 ```python 210 def compute_signing_root(ssz_object: SSZObject, domain: Domain) -> Root: 211 """ 212 Return the signing root for the corresponding signing data. 213 """ 214 return hash_tree_root(SigningData( 215 object_root=hash_tree_root(ssz_object), 216 domain=domain, 217 )) 218 ``` 219 ```python 220 def get_current_epoch(state: BeaconState) -> Epoch: 221 """ 222 Return the current epoch. 223 """ 224 return compute_epoch_at_slot(state.slot) 225 ``` 226 ```python 227 def get_previous_epoch(state: BeaconState) -> Epoch: 228 """` 229 Return the previous epoch (unless the current epoch is ``GENESIS_EPOCH``). 230 """ 231 current_epoch = get_current_epoch(state) 232 return GENESIS_EPOCH if current_epoch == GENESIS_EPOCH else Epoch(current_epoch - 1) 233 ``` 234 ```python 235 def get_block_root(state: BeaconState, epoch: Epoch) -> Root: 236 """ 237 Return the block root at the start of a recent ``epoch``. 238 """ 239 return get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch)) 240 ``` 241 ```python 242 def get_block_root_at_slot(state: BeaconState, slot: Slot) -> Root: 243 """ 244 Return the block root at a recent ``slot``. 245 """ 246 assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT 247 return state.block_roots[slot % SLOTS_PER_HISTORICAL_ROOT] 248 ``` 249 ```python 250 def get_randao_mix(state: BeaconState, epoch: Epoch) -> Bytes32: 251 """ 252 Return the randao mix at a recent ``epoch``. 253 """ 254 return state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR] 255 ``` 256 ```python 257 def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]: 258 """ 259 Return the sequence of active validator indices at ``epoch``. 260 """ 261 return [ValidatorIndex(i) for i, v in enumerate(state.validators) if is_active_validator(v, epoch)] 262 ``` 263 ```python 264 def get_validator_churn_limit(state: BeaconState) -> uint64: 265 """ 266 Return the validator churn limit for the current epoch. 267 """ 268 active_validator_indices = get_active_validator_indices(state, get_current_epoch(state)) 269 return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT) 270 ``` 271 ```python 272 def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Bytes32: 273 """ 274 Return the seed at ``epoch``. 275 """ 276 mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow 277 return hash(domain_type + uint_to_bytes(epoch) + mix) 278 ``` 279 ```python 280 def get_committee_count_per_slot(state: BeaconState, epoch: Epoch) -> uint64: 281 """ 282 Return the number of committees in each slot for the given ``epoch``. 283 """ 284 return max(uint64(1), min( 285 MAX_COMMITTEES_PER_SLOT, 286 uint64(len(get_active_validator_indices(state, epoch))) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, 287 )) 288 ``` 289 ```python 290 def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) -> Sequence[ValidatorIndex]: 291 """ 292 Return the beacon committee at ``slot`` for ``index``. 293 """ 294 epoch = compute_epoch_at_slot(slot) 295 committees_per_slot = get_committee_count_per_slot(state, epoch) 296 return compute_committee( 297 indices=get_active_validator_indices(state, epoch), 298 seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER), 299 index=(slot % SLOTS_PER_EPOCH) * committees_per_slot + index, 300 count=committees_per_slot * SLOTS_PER_EPOCH, 301 ) 302 ``` 303 ```python 304 def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: 305 """ 306 Return the beacon proposer index at the current slot. 307 """ 308 epoch = get_current_epoch(state) 309 seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + uint_to_bytes(state.slot)) 310 indices = get_active_validator_indices(state, epoch) 311 return compute_proposer_index(state, indices, seed) 312 ``` 313 ```python 314 def get_total_balance(state: BeaconState, indices: Set[ValidatorIndex]) -> Gwei: 315 """ 316 Return the combined effective balance of the ``indices``. 317 ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero. 318 Math safe up to ~10B ETH, afterwhich this overflows uint64. 319 """ 320 return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, sum([state.validators[index].effective_balance for index in indices]))) 321 ``` 322 ```python 323 def get_total_active_balance(state: BeaconState) -> Gwei: 324 """ 325 Return the combined effective balance of the active validators. 326 Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero. 327 """ 328 return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state)))) 329 ``` 330 ```python 331 def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch=None) -> Domain: 332 """ 333 Return the signature domain (fork version concatenated with domain type) of a message. 334 """ 335 epoch = get_current_epoch(state) if epoch is None else epoch 336 fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version 337 return compute_domain(domain_type, fork_version, state.genesis_validators_root) 338 ``` 339 ```python 340 def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> IndexedAttestation: 341 """ 342 Return the indexed attestation corresponding to ``attestation``. 343 """ 344 attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) 345 346 return IndexedAttestation( 347 attesting_indices=sorted(attesting_indices), 348 data=attestation.data, 349 signature=attestation.signature, 350 ) 351 ``` 352 ```python 353 def get_attesting_indices(state: BeaconState, 354 data: AttestationData, 355 bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]: 356 """ 357 Return the set of attesting indices corresponding to ``data`` and ``bits``. 358 """ 359 committee = get_beacon_committee(state, data.slot, data.index) 360 return set(index for i, index in enumerate(committee) if bits[i]) 361 ``` 362 ```python 363 def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: 364 """ 365 Increase the validator balance at index ``index`` by ``delta``. 366 """ 367 state.balances[index] += delta 368 ``` 369 ```python 370 def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: 371 """ 372 Decrease the validator balance at index ``index`` by ``delta``, with underflow protection. 373 """ 374 state.balances[index] = 0 if delta > state.balances[index] else state.balances[index] - delta 375 ``` 376 ```python 377 def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: 378 """ 379 Initiate the exit of the validator with index ``index``. 380 """ 381 # Return if validator already initiated exit 382 validator = state.validators[index] 383 if validator.exit_epoch != FAR_FUTURE_EPOCH: 384 return 385 386 # Compute exit queue epoch 387 exit_epochs = [v.exit_epoch for v in state.validators if v.exit_epoch != FAR_FUTURE_EPOCH] 388 exit_queue_epoch = max(exit_epochs + [compute_activation_exit_epoch(get_current_epoch(state))]) 389 exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch]) 390 if exit_queue_churn >= get_validator_churn_limit(state): 391 exit_queue_epoch += Epoch(1) 392 393 # Set validator exit epoch and withdrawable epoch 394 validator.exit_epoch = exit_queue_epoch 395 validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY) 396 ``` 397 ```python 398 def slash_validator(state: BeaconState, 399 slashed_index: ValidatorIndex, 400 whistleblower_index: ValidatorIndex=None) -> None: 401 """ 402 Slash the validator with index ``slashed_index``. 403 """ 404 epoch = get_current_epoch(state) 405 initiate_validator_exit(state, slashed_index) 406 validator = state.validators[slashed_index] 407 validator.slashed = True 408 validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR)) 409 state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance 410 decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT) 411 412 # Apply proposer and whistleblower rewards 413 proposer_index = get_beacon_proposer_index(state) 414 if whistleblower_index is None: 415 whistleblower_index = proposer_index 416 whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT) 417 proposer_reward = Gwei(whistleblower_reward // PROPOSER_REWARD_QUOTIENT) 418 increase_balance(state, proposer_index, proposer_reward) 419 increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward)) 420 ``` 421 ```python 422 def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32, 423 eth1_timestamp: uint64, 424 deposits: Sequence[Deposit]) -> BeaconState: 425 fork = Fork( 426 previous_version=GENESIS_FORK_VERSION, 427 current_version=GENESIS_FORK_VERSION, 428 epoch=GENESIS_EPOCH, 429 ) 430 state = BeaconState( 431 genesis_time=eth1_timestamp + GENESIS_DELAY, 432 fork=fork, 433 eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), 434 latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())), 435 randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy 436 ) 437 438 # Process deposits 439 leaves = list(map(lambda deposit: deposit.data, deposits)) 440 for index, deposit in enumerate(deposits): 441 deposit_data_list = List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1]) 442 state.eth1_data.deposit_root = hash_tree_root(deposit_data_list) 443 process_deposit(state, deposit) 444 445 # Process activations 446 for index, validator in enumerate(state.validators): 447 balance = state.balances[index] 448 validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) 449 if validator.effective_balance == MAX_EFFECTIVE_BALANCE: 450 validator.activation_eligibility_epoch = GENESIS_EPOCH 451 validator.activation_epoch = GENESIS_EPOCH 452 453 # Set genesis validators root for domain separation and chain versioning 454 state.genesis_validators_root = hash_tree_root(state.validators) 455 456 return state 457 ``` 458 ```python 459 def is_valid_genesis_state(state: BeaconState) -> bool: 460 if state.genesis_time < MIN_GENESIS_TIME: 461 return False 462 if len(get_active_validator_indices(state, GENESIS_EPOCH)) < MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 463 return False 464 return True 465 ``` 466 ```python 467 def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool=True) -> None: 468 block = signed_block.message 469 # Process slots (including those with no blocks) since block 470 process_slots(state, block.slot) 471 # Verify signature 472 if validate_result: 473 assert verify_block_signature(state, signed_block) 474 # Process block 475 process_block(state, block) 476 # Verify state root 477 if validate_result: 478 assert block.state_root == hash_tree_root(state) 479 ``` 480 ```python 481 def verify_block_signature(state: BeaconState, signed_block: SignedBeaconBlock) -> bool: 482 proposer = state.validators[signed_block.message.proposer_index] 483 signing_root = compute_signing_root(signed_block.message, get_domain(state, DOMAIN_BEACON_PROPOSER)) 484 return bls.Verify(proposer.pubkey, signing_root, signed_block.signature) 485 ``` 486 ```python 487 def process_slots(state: BeaconState, slot: Slot) -> None: 488 assert state.slot < slot 489 while state.slot < slot: 490 process_slot(state) 491 # Process epoch on the start slot of the next epoch 492 if (state.slot + 1) % SLOTS_PER_EPOCH == 0: 493 process_epoch(state) 494 state.slot = Slot(state.slot + 1) 495 ``` 496 ```python 497 def process_slot(state: BeaconState) -> None: 498 # Cache state root 499 previous_state_root = hash_tree_root(state) 500 state.state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root 501 # Cache latest block header state root 502 if state.latest_block_header.state_root == Bytes32(): 503 state.latest_block_header.state_root = previous_state_root 504 # Cache block root 505 previous_block_root = hash_tree_root(state.latest_block_header) 506 state.block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root 507 ``` 508 ```python 509 def process_epoch(state: BeaconState) -> None: 510 process_justification_and_finalization(state) 511 process_rewards_and_penalties(state) 512 process_registry_updates(state) 513 process_slashings(state) 514 process_eth1_data_reset(state) 515 process_effective_balance_updates(state) 516 process_slashings_reset(state) 517 process_randao_mixes_reset(state) 518 process_historical_roots_update(state) 519 process_participation_record_updates(state) 520 ``` 521 ```python 522 def get_matching_source_attestations(state: BeaconState, epoch: Epoch) -> Sequence[PendingAttestation]: 523 assert epoch in (get_previous_epoch(state), get_current_epoch(state)) 524 return state.current_epoch_attestations if epoch == get_current_epoch(state) else state.previous_epoch_attestations 525 ``` 526 ```python 527 def get_matching_target_attestations(state: BeaconState, epoch: Epoch) -> Sequence[PendingAttestation]: 528 return [ 529 a for a in get_matching_source_attestations(state, epoch) 530 if a.data.target.root == get_block_root(state, epoch) 531 ] 532 ``` 533 ```python 534 def get_matching_head_attestations(state: BeaconState, epoch: Epoch) -> Sequence[PendingAttestation]: 535 return [ 536 a for a in get_matching_target_attestations(state, epoch) 537 if a.data.beacon_block_root == get_block_root_at_slot(state, a.data.slot) 538 ] 539 ``` 540 ```python 541 def get_unslashed_attesting_indices(state: BeaconState, 542 attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]: 543 output = set() # type: Set[ValidatorIndex] 544 for a in attestations: 545 output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits)) 546 return set(filter(lambda index: not state.validators[index].slashed, output)) 547 ``` 548 ```python 549 def get_attesting_balance(state: BeaconState, attestations: Sequence[PendingAttestation]) -> Gwei: 550 """ 551 Return the combined effective balance of the set of unslashed validators participating in ``attestations``. 552 Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero. 553 """ 554 return get_total_balance(state, get_unslashed_attesting_indices(state, attestations)) 555 ``` 556 ```python 557 def process_justification_and_finalization(state: BeaconState) -> None: 558 # Initial FFG checkpoint values have a `0x00` stub for `root`. 559 # Skip FFG updates in the first two epochs to avoid corner cases that might result in modifying this stub. 560 if get_current_epoch(state) <= GENESIS_EPOCH + 1: 561 return 562 previous_attestations = get_matching_target_attestations(state, get_previous_epoch(state)) 563 current_attestations = get_matching_target_attestations(state, get_current_epoch(state)) 564 total_active_balance = get_total_active_balance(state) 565 previous_target_balance = get_attesting_balance(state, previous_attestations) 566 current_target_balance = get_attesting_balance(state, current_attestations) 567 weigh_justification_and_finalization(state, total_active_balance, previous_target_balance, current_target_balance) 568 ``` 569 ```python 570 def weigh_justification_and_finalization(state: BeaconState, 571 total_active_balance: Gwei, 572 previous_epoch_target_balance: Gwei, 573 current_epoch_target_balance: Gwei) -> None: 574 previous_epoch = get_previous_epoch(state) 575 current_epoch = get_current_epoch(state) 576 old_previous_justified_checkpoint = state.previous_justified_checkpoint 577 old_current_justified_checkpoint = state.current_justified_checkpoint 578 579 # Process justifications 580 state.previous_justified_checkpoint = state.current_justified_checkpoint 581 state.justification_bits[1:] = state.justification_bits[:JUSTIFICATION_BITS_LENGTH - 1] 582 state.justification_bits[0] = 0b0 583 if previous_epoch_target_balance * 3 >= total_active_balance * 2: 584 state.current_justified_checkpoint = Checkpoint(epoch=previous_epoch, 585 root=get_block_root(state, previous_epoch)) 586 state.justification_bits[1] = 0b1 587 if current_epoch_target_balance * 3 >= total_active_balance * 2: 588 state.current_justified_checkpoint = Checkpoint(epoch=current_epoch, 589 root=get_block_root(state, current_epoch)) 590 state.justification_bits[0] = 0b1 591 592 # Process finalizations 593 bits = state.justification_bits 594 # The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source 595 if all(bits[1:4]) and old_previous_justified_checkpoint.epoch + 3 == current_epoch: 596 state.finalized_checkpoint = old_previous_justified_checkpoint 597 # The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source 598 if all(bits[1:3]) and old_previous_justified_checkpoint.epoch + 2 == current_epoch: 599 state.finalized_checkpoint = old_previous_justified_checkpoint 600 # The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source 601 if all(bits[0:3]) and old_current_justified_checkpoint.epoch + 2 == current_epoch: 602 state.finalized_checkpoint = old_current_justified_checkpoint 603 # The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source 604 if all(bits[0:2]) and old_current_justified_checkpoint.epoch + 1 == current_epoch: 605 state.finalized_checkpoint = old_current_justified_checkpoint 606 ``` 607 ```python 608 def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei: 609 total_balance = get_total_active_balance(state) 610 effective_balance = state.validators[index].effective_balance 611 return Gwei(effective_balance * BASE_REWARD_FACTOR // integer_squareroot(total_balance) // BASE_REWARDS_PER_EPOCH) 612 ``` 613 ```python 614 def get_proposer_reward(state: BeaconState, attesting_index: ValidatorIndex) -> Gwei: 615 return Gwei(get_base_reward(state, attesting_index) // PROPOSER_REWARD_QUOTIENT) 616 ``` 617 ```python 618 def get_finality_delay(state: BeaconState) -> uint64: 619 return get_previous_epoch(state) - state.finalized_checkpoint.epoch 620 ``` 621 ```python 622 def is_in_inactivity_leak(state: BeaconState) -> bool: 623 return get_finality_delay(state) > MIN_EPOCHS_TO_INACTIVITY_PENALTY 624 ``` 625 ```python 626 def get_eligible_validator_indices(state: BeaconState) -> Sequence[ValidatorIndex]: 627 previous_epoch = get_previous_epoch(state) 628 return [ 629 ValidatorIndex(index) for index, v in enumerate(state.validators) 630 if is_active_validator(v, previous_epoch) or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch) 631 ] 632 ``` 633 ```python 634 def get_attestation_component_deltas(state: BeaconState, 635 attestations: Sequence[PendingAttestation] 636 ) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: 637 """ 638 Helper with shared logic for use by get source, target, and head deltas functions 639 """ 640 rewards = [Gwei(0)] * len(state.validators) 641 penalties = [Gwei(0)] * len(state.validators) 642 total_balance = get_total_active_balance(state) 643 unslashed_attesting_indices = get_unslashed_attesting_indices(state, attestations) 644 attesting_balance = get_total_balance(state, unslashed_attesting_indices) 645 for index in get_eligible_validator_indices(state): 646 if index in unslashed_attesting_indices: 647 increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from balance totals to avoid uint64 overflow 648 if is_in_inactivity_leak(state): 649 # Since full base reward will be canceled out by inactivity penalty deltas, 650 # optimal participation receives full base reward compensation here. 651 rewards[index] += get_base_reward(state, index) 652 else: 653 reward_numerator = get_base_reward(state, index) * (attesting_balance // increment) 654 rewards[index] += reward_numerator // (total_balance // increment) 655 else: 656 penalties[index] += get_base_reward(state, index) 657 return rewards, penalties 658 ``` 659 ```python 660 def get_source_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: 661 """ 662 Return attester micro-rewards/penalties for source-vote for each validator. 663 """ 664 matching_source_attestations = get_matching_source_attestations(state, get_previous_epoch(state)) 665 return get_attestation_component_deltas(state, matching_source_attestations) 666 ``` 667 ```python 668 def get_target_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: 669 """ 670 Return attester micro-rewards/penalties for target-vote for each validator. 671 """ 672 matching_target_attestations = get_matching_target_attestations(state, get_previous_epoch(state)) 673 return get_attestation_component_deltas(state, matching_target_attestations) 674 ``` 675 ```python 676 def get_head_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: 677 """ 678 Return attester micro-rewards/penalties for head-vote for each validator. 679 """ 680 matching_head_attestations = get_matching_head_attestations(state, get_previous_epoch(state)) 681 return get_attestation_component_deltas(state, matching_head_attestations) 682 ``` 683 ```python 684 def get_inclusion_delay_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: 685 """ 686 Return proposer and inclusion delay micro-rewards/penalties for each validator. 687 """ 688 rewards = [Gwei(0) for _ in range(len(state.validators))] 689 matching_source_attestations = get_matching_source_attestations(state, get_previous_epoch(state)) 690 for index in get_unslashed_attesting_indices(state, matching_source_attestations): 691 attestation = min([ 692 a for a in matching_source_attestations 693 if index in get_attesting_indices(state, a.data, a.aggregation_bits) 694 ], key=lambda a: a.inclusion_delay) 695 rewards[attestation.proposer_index] += get_proposer_reward(state, index) 696 max_attester_reward = Gwei(get_base_reward(state, index) - get_proposer_reward(state, index)) 697 rewards[index] += Gwei(max_attester_reward // attestation.inclusion_delay) 698 699 # No penalties associated with inclusion delay 700 penalties = [Gwei(0) for _ in range(len(state.validators))] 701 return rewards, penalties 702 ``` 703 ```python 704 def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: 705 """ 706 Return inactivity reward/penalty deltas for each validator. 707 """ 708 penalties = [Gwei(0) for _ in range(len(state.validators))] 709 if is_in_inactivity_leak(state): 710 matching_target_attestations = get_matching_target_attestations(state, get_previous_epoch(state)) 711 matching_target_attesting_indices = get_unslashed_attesting_indices(state, matching_target_attestations) 712 for index in get_eligible_validator_indices(state): 713 # If validator is performing optimally this cancels all rewards for a neutral balance 714 base_reward = get_base_reward(state, index) 715 penalties[index] += Gwei(BASE_REWARDS_PER_EPOCH * base_reward - get_proposer_reward(state, index)) 716 if index not in matching_target_attesting_indices: 717 effective_balance = state.validators[index].effective_balance 718 penalties[index] += Gwei(effective_balance * get_finality_delay(state) // INACTIVITY_PENALTY_QUOTIENT) 719 720 # No rewards associated with inactivity penalties 721 rewards = [Gwei(0) for _ in range(len(state.validators))] 722 return rewards, penalties 723 ``` 724 ```python 725 def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: 726 """ 727 Return attestation reward/penalty deltas for each validator. 728 """ 729 source_rewards, source_penalties = get_source_deltas(state) 730 target_rewards, target_penalties = get_target_deltas(state) 731 head_rewards, head_penalties = get_head_deltas(state) 732 inclusion_delay_rewards, _ = get_inclusion_delay_deltas(state) 733 _, inactivity_penalties = get_inactivity_penalty_deltas(state) 734 735 rewards = [ 736 source_rewards[i] + target_rewards[i] + head_rewards[i] + inclusion_delay_rewards[i] 737 for i in range(len(state.validators)) 738 ] 739 740 penalties = [ 741 source_penalties[i] + target_penalties[i] + head_penalties[i] + inactivity_penalties[i] 742 for i in range(len(state.validators)) 743 ] 744 745 return rewards, penalties 746 ``` 747 ```python 748 def process_rewards_and_penalties(state: BeaconState) -> None: 749 # No rewards are applied at the end of `GENESIS_EPOCH` because rewards are for work done in the previous epoch 750 if get_current_epoch(state) == GENESIS_EPOCH: 751 return 752 753 rewards, penalties = get_attestation_deltas(state) 754 for index in range(len(state.validators)): 755 increase_balance(state, ValidatorIndex(index), rewards[index]) 756 decrease_balance(state, ValidatorIndex(index), penalties[index]) 757 ``` 758 ```python 759 def process_registry_updates(state: BeaconState) -> None: 760 # Process activation eligibility and ejections 761 for index, validator in enumerate(state.validators): 762 if is_eligible_for_activation_queue(validator): 763 validator.activation_eligibility_epoch = get_current_epoch(state) + 1 764 765 if is_active_validator(validator, get_current_epoch(state)) and validator.effective_balance <= EJECTION_BALANCE: 766 initiate_validator_exit(state, ValidatorIndex(index)) 767 768 # Queue validators eligible for activation and not yet dequeued for activation 769 activation_queue = sorted([ 770 index for index, validator in enumerate(state.validators) 771 if is_eligible_for_activation(state, validator) 772 # Order by the sequence of activation_eligibility_epoch setting and then index 773 ], key=lambda index: (state.validators[index].activation_eligibility_epoch, index)) 774 # Dequeued validators for activation up to churn limit 775 for index in activation_queue[:get_validator_churn_limit(state)]: 776 validator = state.validators[index] 777 validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state)) 778 ``` 779 ```python 780 def process_slashings(state: BeaconState) -> None: 781 epoch = get_current_epoch(state) 782 total_balance = get_total_active_balance(state) 783 adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER, total_balance) 784 for index, validator in enumerate(state.validators): 785 if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch: 786 increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow 787 penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance 788 penalty = penalty_numerator // total_balance * increment 789 decrease_balance(state, ValidatorIndex(index), penalty) 790 ``` 791 ```python 792 def process_eth1_data_reset(state: BeaconState) -> None: 793 next_epoch = Epoch(get_current_epoch(state) + 1) 794 # Reset eth1 data votes 795 if next_epoch % EPOCHS_PER_ETH1_VOTING_PERIOD == 0: 796 state.eth1_data_votes = [] 797 ``` 798 ```python 799 def process_effective_balance_updates(state: BeaconState) -> None: 800 # Update effective balances with hysteresis 801 for index, validator in enumerate(state.validators): 802 balance = state.balances[index] 803 HYSTERESIS_INCREMENT = uint64(EFFECTIVE_BALANCE_INCREMENT // HYSTERESIS_QUOTIENT) 804 DOWNWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER 805 UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER 806 if ( 807 balance + DOWNWARD_THRESHOLD < validator.effective_balance 808 or validator.effective_balance + UPWARD_THRESHOLD < balance 809 ): 810 validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) 811 ``` 812 ```python 813 def process_slashings_reset(state: BeaconState) -> None: 814 next_epoch = Epoch(get_current_epoch(state) + 1) 815 # Reset slashings 816 state.slashings[next_epoch % EPOCHS_PER_SLASHINGS_VECTOR] = Gwei(0) 817 ``` 818 ```python 819 def process_randao_mixes_reset(state: BeaconState) -> None: 820 current_epoch = get_current_epoch(state) 821 next_epoch = Epoch(current_epoch + 1) 822 # Set randao mix 823 state.randao_mixes[next_epoch % EPOCHS_PER_HISTORICAL_VECTOR] = get_randao_mix(state, current_epoch) 824 ``` 825 ```python 826 def process_historical_roots_update(state: BeaconState) -> None: 827 # Set historical root accumulator 828 next_epoch = Epoch(get_current_epoch(state) + 1) 829 if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: 830 historical_batch = HistoricalBatch(block_roots=state.block_roots, state_roots=state.state_roots) 831 state.historical_roots.append(hash_tree_root(historical_batch)) 832 ``` 833 ```python 834 def process_participation_record_updates(state: BeaconState) -> None: 835 # Rotate current/previous epoch attestations 836 state.previous_epoch_attestations = state.current_epoch_attestations 837 state.current_epoch_attestations = [] 838 ``` 839 ```python 840 def process_block(state: BeaconState, block: BeaconBlock) -> None: 841 process_block_header(state, block) 842 process_randao(state, block.body) 843 process_eth1_data(state, block.body) 844 process_operations(state, block.body) 845 ``` 846 ```python 847 def process_block_header(state: BeaconState, block: BeaconBlock) -> None: 848 # Verify that the slots match 849 assert block.slot == state.slot 850 # Verify that the block is newer than latest block header 851 assert block.slot > state.latest_block_header.slot 852 # Verify that proposer index is the correct index 853 assert block.proposer_index == get_beacon_proposer_index(state) 854 # Verify that the parent matches 855 assert block.parent_root == hash_tree_root(state.latest_block_header) 856 # Cache current block as the new latest block 857 state.latest_block_header = BeaconBlockHeader( 858 slot=block.slot, 859 proposer_index=block.proposer_index, 860 parent_root=block.parent_root, 861 state_root=Bytes32(), # Overwritten in the next process_slot call 862 body_root=hash_tree_root(block.body), 863 ) 864 865 # Verify proposer is not slashed 866 proposer = state.validators[block.proposer_index] 867 assert not proposer.slashed 868 ``` 869 ```python 870 def process_randao(state: BeaconState, body: BeaconBlockBody) -> None: 871 epoch = get_current_epoch(state) 872 # Verify RANDAO reveal 873 proposer = state.validators[get_beacon_proposer_index(state)] 874 signing_root = compute_signing_root(epoch, get_domain(state, DOMAIN_RANDAO)) 875 assert bls.Verify(proposer.pubkey, signing_root, body.randao_reveal) 876 # Mix in RANDAO reveal 877 mix = xor(get_randao_mix(state, epoch), hash(body.randao_reveal)) 878 state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR] = mix 879 ``` 880 ```python 881 def process_eth1_data(state: BeaconState, body: BeaconBlockBody) -> None: 882 state.eth1_data_votes.append(body.eth1_data) 883 if state.eth1_data_votes.count(body.eth1_data) * 2 > EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH: 884 state.eth1_data = body.eth1_data 885 ``` 886 ```python 887 def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: 888 # Verify that outstanding deposits are processed up to the maximum number of deposits 889 assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index) 890 891 def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: 892 for operation in operations: 893 fn(state, operation) 894 895 for_ops(body.proposer_slashings, process_proposer_slashing) 896 for_ops(body.attester_slashings, process_attester_slashing) 897 for_ops(body.attestations, process_attestation) 898 for_ops(body.deposits, process_deposit) 899 for_ops(body.voluntary_exits, process_voluntary_exit) 900 ``` 901 ```python 902 def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None: 903 header_1 = proposer_slashing.signed_header_1.message 904 header_2 = proposer_slashing.signed_header_2.message 905 906 # Verify header slots match 907 assert header_1.slot == header_2.slot 908 # Verify header proposer indices match 909 assert header_1.proposer_index == header_2.proposer_index 910 # Verify the headers are different 911 assert header_1 != header_2 912 # Verify the proposer is slashable 913 proposer = state.validators[header_1.proposer_index] 914 assert is_slashable_validator(proposer, get_current_epoch(state)) 915 # Verify signatures 916 for signed_header in (proposer_slashing.signed_header_1, proposer_slashing.signed_header_2): 917 domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(signed_header.message.slot)) 918 signing_root = compute_signing_root(signed_header.message, domain) 919 assert bls.Verify(proposer.pubkey, signing_root, signed_header.signature) 920 921 slash_validator(state, header_1.proposer_index) 922 ``` 923 ```python 924 def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None: 925 attestation_1 = attester_slashing.attestation_1 926 attestation_2 = attester_slashing.attestation_2 927 assert is_slashable_attestation_data(attestation_1.data, attestation_2.data) 928 assert is_valid_indexed_attestation(state, attestation_1) 929 assert is_valid_indexed_attestation(state, attestation_2) 930 931 slashed_any = False 932 indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices) 933 for index in sorted(indices): 934 if is_slashable_validator(state.validators[index], get_current_epoch(state)): 935 slash_validator(state, index) 936 slashed_any = True 937 assert slashed_any 938 ``` 939 ```python 940 def process_attestation(state: BeaconState, attestation: Attestation) -> None: 941 data = attestation.data 942 assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state)) 943 assert data.target.epoch == compute_epoch_at_slot(data.slot) 944 assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH 945 assert data.index < get_committee_count_per_slot(state, data.target.epoch) 946 947 committee = get_beacon_committee(state, data.slot, data.index) 948 assert len(attestation.aggregation_bits) == len(committee) 949 950 pending_attestation = PendingAttestation( 951 data=data, 952 aggregation_bits=attestation.aggregation_bits, 953 inclusion_delay=state.slot - data.slot, 954 proposer_index=get_beacon_proposer_index(state), 955 ) 956 957 if data.target.epoch == get_current_epoch(state): 958 assert data.source == state.current_justified_checkpoint 959 state.current_epoch_attestations.append(pending_attestation) 960 else: 961 assert data.source == state.previous_justified_checkpoint 962 state.previous_epoch_attestations.append(pending_attestation) 963 964 # Verify signature 965 assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation)) 966 ``` 967 ```python 968 def get_validator_from_deposit(state: BeaconState, deposit: Deposit) -> Validator: 969 amount = deposit.data.amount 970 effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) 971 972 return Validator( 973 pubkey=deposit.data.pubkey, 974 withdrawal_credentials=deposit.data.withdrawal_credentials, 975 activation_eligibility_epoch=FAR_FUTURE_EPOCH, 976 activation_epoch=FAR_FUTURE_EPOCH, 977 exit_epoch=FAR_FUTURE_EPOCH, 978 withdrawable_epoch=FAR_FUTURE_EPOCH, 979 effective_balance=effective_balance, 980 ) 981 ``` 982 ```python 983 def process_deposit(state: BeaconState, deposit: Deposit) -> None: 984 # Verify the Merkle branch 985 assert is_valid_merkle_branch( 986 leaf=hash_tree_root(deposit.data), 987 branch=deposit.proof, 988 depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in 989 index=state.eth1_deposit_index, 990 root=state.eth1_data.deposit_root, 991 ) 992 993 # Deposits must be processed in order 994 state.eth1_deposit_index += 1 995 996 pubkey = deposit.data.pubkey 997 amount = deposit.data.amount 998 validator_pubkeys = [v.pubkey for v in state.validators] 999 if pubkey not in validator_pubkeys: 1000 # Verify the deposit signature (proof of possession) which is not checked by the deposit contract 1001 deposit_message = DepositMessage( 1002 pubkey=deposit.data.pubkey, 1003 withdrawal_credentials=deposit.data.withdrawal_credentials, 1004 amount=deposit.data.amount, 1005 ) 1006 domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks 1007 signing_root = compute_signing_root(deposit_message, domain) 1008 if not bls.Verify(pubkey, signing_root, deposit.data.signature): 1009 return 1010 1011 # Add validator and balance entries 1012 state.validators.append(get_validator_from_deposit(state, deposit)) 1013 state.balances.append(amount) 1014 else: 1015 # Increase balance by deposit amount 1016 index = ValidatorIndex(validator_pubkeys.index(pubkey)) 1017 increase_balance(state, index, amount) 1018 ``` 1019 ```python 1020 def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None: 1021 voluntary_exit = signed_voluntary_exit.message 1022 validator = state.validators[voluntary_exit.validator_index] 1023 # Verify the validator is active 1024 assert is_active_validator(validator, get_current_epoch(state)) 1025 # Verify exit has not been initiated 1026 assert validator.exit_epoch == FAR_FUTURE_EPOCH 1027 # Exits must specify an epoch when they become valid; they are not valid before then 1028 assert get_current_epoch(state) >= voluntary_exit.epoch 1029 # Verify the validator has been active long enough 1030 assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD 1031 # Verify signature 1032 domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch) 1033 signing_root = compute_signing_root(voluntary_exit, domain) 1034 assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature) 1035 # Initiate exit 1036 initiate_validator_exit(state, voluntary_exit.validator_index) 1037 ```