def compute_proposer_index( validators: Sequence[Validator], indices: Sequence[ValidatorIndex], seed: Hash32, max_effective_balance: Gwei, shuffle_round_count: int, ) -> ValidatorIndex: """ Return from ``indices`` a random index sampled by effective balance. """ if len(indices) == 0: raise ValidationError("There is no any active validator.") i = 0 while True: candidate_index = indices[compute_shuffled_index( ValidatorIndex(i % len(indices)), len(indices), seed, shuffle_round_count, )] random_byte = hash_eth2(seed + (i // 32).to_bytes(8, "little"))[i % 32] effective_balance = validators[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= max_effective_balance * random_byte: return ValidatorIndex(candidate_index) # Log the warning message in case it happends. if i % len(indices) == 0 and i > 0: logger.warning( "Tried over %d times in compute_proposer_index while loop.", i) i += 1
def get_validator_index_for_public_key( self, public_key: BLSPubkey) -> Optional[ValidatorIndex]: for index, validator in enumerate(self.validators): if validator.pubkey == public_key: return ValidatorIndex(index) else: return None
def _churn_validators(state: BeaconState, config: BeaconConfig, check_should_churn_fn: Callable[..., Any], churn_fn: Callable[..., Any], max_balance_churn: int) -> BeaconState: """ Churn the validators. The number of the churning validators is based on the given ``max_balance_churn``. :param check_should_churn_fn: the funcation to determine if the validator should be churn :param churn_fn``: the function to churn the validators; it could be ``activate_validator`` or ``exit_validator`` """ balance_churn = 0 for index in range(len(state.validator_registry)): index = ValidatorIndex(index) should_churn = check_should_churn_fn( state, index, ) if should_churn: # Check the balance churn would be within the allowance balance_churn += get_effective_balance( state.validator_balances, index, config.MAX_DEPOSIT_AMOUNT, ) if balance_churn > max_balance_churn: break state = churn_fn(state, index) return state
def sample_pending_attestation_record_params(sample_attestation_data_params): return { "aggregation_bits": (True, False) * 4 * 16, "data": AttestationData.create(**sample_attestation_data_params), "inclusion_delay": 1, "proposer_index": ValidatorIndex(12), }
def sample_pending_attestation_record_params(sample_attestation_data_params): return { 'aggregation_bits': (True, False) * 4 * 16, 'data': AttestationData(**sample_attestation_data_params), 'inclusion_delay': 1, 'proposer_index': ValidatorIndex(12), }
def create_mock_slashable_attestation( state: BeaconState, config: Eth2Config, keymap: Dict[BLSPubkey, int], attestation_slot: Slot, ) -> IndexedAttestation: """ Create an `IndexedAttestation` that is signed by one attester. """ attester_index = ValidatorIndex(0) committee = (attester_index, ) shard = Shard(0) # Use genesis block root as `beacon_block_root`, only for tests. beacon_block_root = get_block_root_at_slot( state, attestation_slot, config.SLOTS_PER_HISTORICAL_ROOT) # Get `target_root` target_root = _get_target_root(state, config, beacon_block_root) # Get `source_root` source_root = get_block_root_at_slot( state, compute_start_slot_of_epoch(state.current_justified_checkpoint.epoch, config.SLOTS_PER_EPOCH), config.SLOTS_PER_HISTORICAL_ROOT, ) previous_crosslink = state.current_crosslinks[shard] attestation_data = AttestationData( beacon_block_root=beacon_block_root, source=Checkpoint(epoch=state.current_justified_checkpoint.epoch, root=source_root), target=Checkpoint( epoch=compute_epoch_of_slot(attestation_slot, config.SLOTS_PER_EPOCH), root=target_root, ), crosslink=previous_crosslink, ) message_hash = _get_mock_message(attestation_data) attesting_indices = _get_mock_attesting_indices(committee, num_voted_attesters=1) signature = sign_transaction( message_hash=message_hash, privkey=keymap[state.validators[attesting_indices[0]].pubkey], state=state, slot=attestation_slot, signature_domain=SignatureDomain.DOMAIN_ATTESTATION, slots_per_epoch=config.SLOTS_PER_EPOCH, ) validator_indices = tuple(committee[i] for i in attesting_indices) return IndexedAttestation( custody_bit_0_indices=validator_indices, custody_bit_1_indices=tuple(), data=attestation_data, signature=signature, )
def process_rewards_and_penalties( state: BeaconState, config: Eth2Config ) -> BeaconState: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) if current_epoch == config.GENESIS_EPOCH: return state rewards_for_attestations, penalties_for_attestations = get_attestation_deltas( state, config ) rewards_for_crosslinks, penalties_for_crosslinks = get_crosslink_deltas( state, config ) for index in range(len(state.validators)): index = ValidatorIndex(index) state = increase_balance( state, index, Gwei(rewards_for_attestations[index] + rewards_for_crosslinks[index]), ) state = decrease_balance( state, index, Gwei(penalties_for_attestations[index] + penalties_for_crosslinks[index]), ) return state
def get_active_validator_indices(validators: Sequence[Validator], epoch: Epoch) -> Tuple[ValidatorIndex, ...]: """ Get indices of active validators from ``validators``. """ return tuple( ValidatorIndex(index) for index, validator in enumerate(validators) if validator.is_active(epoch))
def process_deposit(state: BeaconState, deposit: Deposit, slots_per_epoch: int, deposit_contract_tree_depth: int) -> BeaconState: """ Process a deposit from Ethereum 1.0. """ validate_deposit(state, deposit, deposit_contract_tree_depth) # Increment the next deposit index we are expecting. Note that this # needs to be done here because while the deposit contract will never # create an invalid Merkle branch, it may admit an invalid deposit # object, and we need to be able to skip over it state = state.copy(deposit_index=state.deposit_index + 1, ) validator_pubkeys = tuple(v.pubkey for v in state.validator_registry) deposit_input = deposit.deposit_data.deposit_input pubkey = deposit_input.pubkey amount = deposit.deposit_data.amount withdrawal_credentials = deposit_input.withdrawal_credentials if pubkey not in validator_pubkeys: # Verify the proof of possession proof_is_valid = bls.verify( pubkey=pubkey, message_hash=deposit_input.signing_root, signature=deposit_input.signature, domain=get_domain( state.fork, state.current_epoch(slots_per_epoch), SignatureDomain.DOMAIN_DEPOSIT, ), ) if not proof_is_valid: return state validator = Validator.create_pending_validator( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, ) # Note: In phase 2 registry indices that has been withdrawn for a long time # will be recycled. state = add_pending_validator( state, validator, amount, ) else: # Top-up - increase balance by deposit index = ValidatorIndex(validator_pubkeys.index(pubkey)) validator = state.validator_registry[index] # Update validator's balance and state state = state.update_validator_balance( validator_index=index, balance=state.validator_balances[index] + amount, ) return state
def get_active_validator_indices( validators: Sequence['ValidatorRecord'], slot: SlotNumber) -> Tuple[ValidatorIndex, ...]: """ Get indices of active validators from ``validators``. """ return tuple( ValidatorIndex(index) for index, validator in enumerate(validators) if validator.is_active(slot))
def _process_rewards_and_penalties_for_crosslinks( state: BeaconState, config: Eth2Config, effective_balances: Dict[ValidatorIndex, Gwei], base_rewards: Dict[ValidatorIndex, Gwei] ) -> Tuple[Dict[ValidatorIndex, Gwei], Dict[ValidatorIndex, Gwei]]: # noqa: E501 previous_epoch_start_slot = get_epoch_start_slot( state.previous_epoch(config.SLOTS_PER_EPOCH), config.SLOTS_PER_EPOCH, ) current_epoch_start_slot = get_epoch_start_slot( state.current_epoch(config.SLOTS_PER_EPOCH), config.SLOTS_PER_EPOCH, ) rewards_received = { ValidatorIndex(index): Gwei(0) for index in range(len(state.validator_registry)) } penalties_received = rewards_received.copy() for slot in range(previous_epoch_start_slot, current_epoch_start_slot): crosslink_committees_at_slot = get_crosslink_committees_at_slot( state, slot, CommitteeConfig(config), ) for crosslink_committee, shard in crosslink_committees_at_slot: winning_root, attesting_validator_indices = get_winning_root_and_participants( state=state, shard=shard, effective_balances=effective_balances, committee_config=CommitteeConfig(config), ) total_attesting_balance = get_total_balance( state.validator_balances, attesting_validator_indices, config.MAX_DEPOSIT_AMOUNT, ) total_balance = get_total_balance_from_effective_balances( effective_balances, crosslink_committee, ) for index in attesting_validator_indices: rewards_received = _update_rewards_or_penalies( index, base_rewards[index] * total_attesting_balance // total_balance, rewards_received, ) for index in set(crosslink_committee).difference( attesting_validator_indices): penalties_received = _update_rewards_or_penalies( index, base_rewards[index], penalties_received, ) return (rewards_received, penalties_received)
def process_deposit(state: BeaconState, deposit: Deposit, config: Eth2Config) -> BeaconState: """ Process a deposit from Ethereum 1.0. """ validate_deposit_proof(state, deposit, DEPOSIT_CONTRACT_TREE_DEPTH) # Increment the next deposit index we are expecting. Note that this # needs to be done here because while the deposit contract will never # create an invalid Merkle branch, it may admit an invalid deposit # object, and we need to be able to skip over it state = state.copy( eth1_deposit_index=state.eth1_deposit_index + 1, ) pubkey = deposit.data.pubkey amount = deposit.data.amount validator_pubkeys = tuple(v.pubkey for v in state.validators) if pubkey not in validator_pubkeys: # Verify the deposit signature (proof of possession) for new validators. # Note: The deposit contract does not check signatures. # Note: Deposits are valid across forks, thus the deposit domain # is retrieved directly from `compute_domain`. is_valid_proof_of_possession = bls.verify( message_hash=deposit.data.signing_root, pubkey=pubkey, signature=deposit.data.signature, domain=compute_domain( SignatureDomain.DOMAIN_DEPOSIT, ), ) if not is_valid_proof_of_possession: return state withdrawal_credentials = deposit.data.withdrawal_credentials validator = Validator.create_pending_validator( pubkey, withdrawal_credentials, amount, config, ) return state.copy( validators=state.validators + (validator,), balances=state.balances + (amount, ), ) else: index = ValidatorIndex(validator_pubkeys.index(pubkey)) return increase_balance( state, index, amount, )
def process_deposit(*, state: BeaconState, pubkey: BLSPubkey, amount: Gwei, proof_of_possession: BLSSignature, withdrawal_credentials: Hash32, randao_commitment: Hash32, custody_commitment: Hash32, epoch_length: int) -> BeaconState: """ Process a deposit from Ethereum 1.0. """ validate_proof_of_possession( state=state, pubkey=pubkey, proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, epoch_length=epoch_length, ) validator_pubkeys = tuple(v.pubkey for v in state.validator_registry) if pubkey not in validator_pubkeys: validator = ValidatorRecord.create_pending_validator( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, ) # Note: In phase 2 registry indices that has been withdrawn for a long time # will be recycled. state = add_pending_validator( state, validator, amount, ) else: # Top-up - increase balance by deposit index = ValidatorIndex(validator_pubkeys.index(pubkey)) validator = state.validator_registry[index] if validator.withdrawal_credentials != withdrawal_credentials: raise ValidationError("`withdrawal_credentials` are incorrect:\n" "\texpected: %s, found: %s" % ( validator.withdrawal_credentials, validator.withdrawal_credentials, )) # Update validator's balance and state state = state.update_validator_balance( validator_index=index, balance=state.validator_balances[index] + amount, ) return state
def initialize_beacon_state_from_eth1(*, eth1_block_hash: Hash32, eth1_timestamp: Timestamp, deposits: Sequence[Deposit], config: Eth2Config) -> BeaconState: fork = Fork.create( previous_version=config.GENESIS_FORK_VERSION, current_version=config.GENESIS_FORK_VERSION, epoch=GENESIS_EPOCH, ) state = BeaconState.create( genesis_time=_genesis_time_from_eth1_timestamp(eth1_timestamp, config.GENESIS_DELAY), fork=fork, eth1_data=Eth1Data.create(block_hash=eth1_block_hash, deposit_count=len(deposits)), latest_block_header=BeaconBlockHeader.create( body_root=BeaconBlockBody.create().hash_tree_root), block_roots=(ZERO_ROOT, ) * config.SLOTS_PER_HISTORICAL_ROOT, state_roots=(ZERO_HASH32, ) * config.SLOTS_PER_HISTORICAL_ROOT, randao_mixes=(eth1_block_hash, ) * config.EPOCHS_PER_HISTORICAL_VECTOR, slashings=(Gwei(0), ) * config.EPOCHS_PER_SLASHINGS_VECTOR, config=config, ) # Process genesis deposits for index, deposit in enumerate(deposits): deposit_data_list = tuple(deposit.data for deposit in deposits[:index + 1]) deposit_root = ssz.get_hash_tree_root( deposit_data_list, ssz.List(DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH)) state = state.transform(("eth1_data", "deposit_root"), deposit_root) state = process_deposit(state=state, deposit=deposit, config=config) # Process genesis activations for validator_index in range(len(state.validators)): validator_index = ValidatorIndex(validator_index) balance = state.balances[validator_index] effective_balance = calculate_effective_balance(balance, config) state = state.transform( ("validators", validator_index, "effective_balance"), effective_balance) if effective_balance == config.MAX_EFFECTIVE_BALANCE: activated_validator = activate_validator( state.validators[validator_index], GENESIS_EPOCH) state = state.transform(("validators", validator_index), activated_validator) return state.set("genesis_validators_root", state.validators.hash_tree_root)
def process_exit_queue(state: BeaconState, config: BeaconConfig) -> BeaconState: """ Process the exit queue. """ def eligible(index: ValidatorIndex) -> bool: validator = state.validator_registry[index] # Filter out dequeued validators if validator.withdrawable_epoch != FAR_FUTURE_EPOCH: return False # Dequeue if the minimum amount of time has passed else: return (state.current_epoch( config.SLOTS_PER_EPOCH) >= validator.exit_epoch + config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY) eligible_indices = filter( eligible, tuple( [ValidatorIndex(i) for i in range(len(state.validator_registry))])) # Sort in order of exit epoch, and validators that exit within the same epoch exit # in order of validator index sorted_indices = sorted( eligible_indices, key=lambda index: state.validator_registry[index].exit_epoch, ) for dequeues, index in enumerate(sorted_indices): if dequeues >= config.MAX_EXIT_DEQUEUES_PER_EPOCH: break state = prepare_validator_for_withdrawal( state, ValidatorIndex(index), slots_per_epoch=config.SLOTS_PER_EPOCH, min_validator_withdrawability_delay=config. MIN_VALIDATOR_WITHDRAWABILITY_DELAY, ) return state
def process_slashings(state: BeaconState, config: Eth2Config) -> BeaconState: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) total_balance = get_total_active_balance(state, config) slashing_period = config.EPOCHS_PER_SLASHINGS_VECTOR // 2 for index, validator in enumerate(state.validators): index = ValidatorIndex(index) if validator.slashed and current_epoch + slashing_period == validator.withdrawable_epoch: penalty = _determine_slashing_penalty( Gwei(sum(state.slashings)), total_balance, validator.effective_balance, config.EFFECTIVE_BALANCE_INCREMENT, ) state = decrease_balance(state, index, penalty) return state
def _get_mock_message_and_attesting_indices( attestation_data: AttestationData, committee: Sequence[ValidatorIndex], num_voted_attesters: int) -> Tuple[Hash32, Tuple[ValidatorIndex, ...]]: """ Get ``message_hash`` and voting indices of the given ``committee``. """ message_hash = AttestationDataAndCustodyBit(data=attestation_data, custody_bit=False).root committee_size = len(committee) assert num_voted_attesters <= committee_size # Index in committee attesting_indices = tuple( ValidatorIndex(i) for i in random.sample(range(committee_size), num_voted_attesters)) return message_hash, tuple(sorted(attesting_indices))
def create_mock_genesis_validator_deposits_and_root( num_validators: int, config: Eth2Config, pubkeys: Sequence[BLSPubkey], keymap: Dict[BLSPubkey, int]) -> Tuple[Tuple[Deposit, ...], Hash32]: # Mock data withdrawal_credentials = Hash32(b'\x22' * 32) fork = Fork( previous_version=config.GENESIS_FORK_VERSION.to_bytes(4, 'little'), current_version=config.GENESIS_FORK_VERSION.to_bytes(4, 'little'), epoch=config.GENESIS_EPOCH, ) deposit_data_array = tuple() # type: Tuple[DepositData, ...] deposit_data_leaves = tuple() # type: Tuple[Hash32, ...] for i in range(num_validators): deposit_data = create_mock_deposit_data( config=config, pubkeys=pubkeys, keymap=keymap, validator_index=ValidatorIndex(i), withdrawal_credentials=withdrawal_credentials, fork=fork, ) item = hash_eth2(ssz.encode(deposit_data)) deposit_data_leaves += (item,) deposit_data_array += (deposit_data,) tree = calc_merkle_tree_from_leaves(deposit_data_leaves) root = get_merkle_root(deposit_data_leaves) genesis_validator_deposits = tuple( Deposit( proof=get_merkle_proof(tree, item_index=i), index=i, deposit_data=deposit_data_array[i], ) for i in range(num_validators) ) return genesis_validator_deposits, root
def initialize_beacon_state_from_eth1(*, eth1_block_hash: Hash32, eth1_timestamp: Timestamp, deposits: Sequence[Deposit], config: Eth2Config) -> BeaconState: state = BeaconState( genesis_time=_genesis_time_from_eth1_timestamp(eth1_timestamp), eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=len(deposits)), latest_block_header=BeaconBlockHeader( body_root=BeaconBlockBody().hash_tree_root), randao_mixes=(eth1_block_hash, ) * config.EPOCHS_PER_HISTORICAL_VECTOR, config=config, ) # Process genesis deposits for index, deposit in enumerate(deposits): deposit_data_list = tuple(deposit.data for deposit in deposits[:index + 1]) state = state.copy(eth1_data=state.eth1_data.copy( deposit_root=ssz.get_hash_tree_root( deposit_data_list, ssz.List(DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH), ))) state = process_deposit(state=state, deposit=deposit, config=config) # Process genesis activations for validator_index in range(len(state.validators)): validator_index = ValidatorIndex(validator_index) balance = state.balances[validator_index] effective_balance = calculate_effective_balance(balance, config) state = state.update_validator_with_fn( validator_index, lambda v, *_: v.copy(effective_balance=effective_balance)) if effective_balance == config.MAX_EFFECTIVE_BALANCE: state = state.update_validator_with_fn(validator_index, activate_validator, config.GENESIS_EPOCH) return state
def process_slashings(state: BeaconState, config: BeaconConfig) -> BeaconState: """ Process the slashings. """ latest_slashed_exit_length = config.LATEST_SLASHED_EXIT_LENGTH max_deposit_amount = config.MAX_DEPOSIT_AMOUNT current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) active_validator_indices = get_active_validator_indices( state.validator_registry, current_epoch) total_balance = Gwei( sum( get_effective_balance(state.validator_balances, i, max_deposit_amount) for i in active_validator_indices)) total_penalties = _compute_total_penalties( state, config, current_epoch, ) for validator_index, validator in enumerate(state.validator_registry): validator_index = ValidatorIndex(validator_index) is_halfway_to_withdrawable_epoch = ( current_epoch == validator.withdrawable_epoch - latest_slashed_exit_length // 2) if validator.slashed and is_halfway_to_withdrawable_epoch: penalty = _compute_individual_penalty( state=state, config=config, validator_index=validator_index, total_penalties=total_penalties, total_balance=total_balance, ) state = state.update_validator_balance( validator_index=validator_index, balance=state.validator_balances[validator_index] - penalty, ) return state
def get_genesis_beacon_state(*, genesis_deposits: Sequence[Deposit], genesis_time: Timestamp, genesis_eth1_data: Eth1Data, config: Eth2Config) -> BeaconState: state = BeaconState( genesis_time=genesis_time, eth1_data=genesis_eth1_data, latest_block_header=BeaconBlockHeader( body_root=BeaconBlockBody().root, ), config=config, ) # Process genesis deposits for deposit in genesis_deposits: state = process_deposit( state=state, deposit=deposit, config=config, ) # Process genesis activations for validator_index in range(len(state.validators)): validator_index = ValidatorIndex(validator_index) effective_balance = state.validators[validator_index].effective_balance is_enough_effective_balance = effective_balance >= config.MAX_EFFECTIVE_BALANCE if is_enough_effective_balance: state = state.update_validator_with_fn( validator_index, activate_validator, config.GENESIS_EPOCH, ) return genesis_state_with_active_index_roots( state, config, )
def process_slashings(state: BeaconState, config: Eth2Config) -> BeaconState: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) total_balance = get_total_active_balance(state, config) start_index = (current_epoch + 1) % config.EPOCHS_PER_SLASHED_BALANCES_VECTOR total_at_start = state.slashed_balances[start_index] end_index = current_epoch % config.EPOCHS_PER_SLASHED_BALANCES_VECTOR total_at_end = state.slashed_balances[end_index] total_penalties = total_at_end - total_at_start slashing_period = config.EPOCHS_PER_SLASHED_BALANCES_VECTOR // 2 for index, validator in enumerate(state.validators): index = ValidatorIndex(index) if validator.slashed and current_epoch == validator.withdrawable_epoch - slashing_period: penalty = _determine_slashing_penalty( total_penalties, total_balance, validator.effective_balance, config.MIN_SLASHING_PENALTY_QUOTIENT, ) state = decrease_balance(state, index, penalty) return state
def get_initial_beacon_state(*, initial_validator_deposits: Sequence[Deposit], genesis_time: Timestamp, latest_eth1_data: Eth1Data, genesis_slot: SlotNumber, genesis_fork_version: int, genesis_start_shard: ShardNumber, shard_count: int, latest_block_roots_length: int, epoch_length: int, target_committee_size: int, max_deposit: Ether, latest_penalized_exit_length: int, latest_randao_mixes_length: int, entry_exit_delay: int) -> BeaconState: state = BeaconState( # Misc slot=genesis_slot, genesis_time=genesis_time, fork=Fork( previous_version=genesis_fork_version, current_version=genesis_fork_version, slot=genesis_slot, ), # Validator registry validator_registry=(), validator_balances=(), validator_registry_update_slot=genesis_slot, validator_registry_exit_count=0, validator_registry_delta_chain_tip=ZERO_HASH32, # Randomness and committees latest_randao_mixes=tuple(ZERO_HASH32 for _ in range(latest_randao_mixes_length)), latest_vdf_outputs=tuple(ZERO_HASH32 for _ in range(latest_randao_mixes_length // epoch_length)), # TODO Remove `persistent_committees`, `persistent_committee_reassignments` persistent_committees=(), persistent_committee_reassignments=(), previous_epoch_start_shard=genesis_start_shard, current_epoch_start_shard=genesis_start_shard, previous_epoch_calculation_slot=genesis_slot, current_epoch_calculation_slot=genesis_slot, previous_epoch_randao_mix=ZERO_HASH32, current_epoch_randao_mix=ZERO_HASH32, # Custody challenges custody_challenges=(), # Finality previous_justified_slot=genesis_slot, justified_slot=genesis_slot, justification_bitfield=0, finalized_slot=genesis_slot, # Recent state latest_crosslinks=tuple([ CrosslinkRecord(slot=genesis_slot, shard_block_root=ZERO_HASH32) for _ in range(shard_count) ]), latest_block_roots=tuple(ZERO_HASH32 for _ in range(latest_block_roots_length)), latest_penalized_balances=tuple( Gwei(0) for _ in range(latest_penalized_exit_length)), latest_attestations=(), batched_block_roots=(), # Ethereum 1.0 chain data latest_eth1_data=latest_eth1_data, eth1_data_votes=(), ) # Process initial deposits for deposit in initial_validator_deposits: state = process_deposit( state=state, pubkey=deposit.deposit_data.deposit_input.pubkey, amount=deposit.deposit_data.amount, proof_of_possession=deposit.deposit_data.deposit_input. proof_of_possession, withdrawal_credentials=deposit.deposit_data.deposit_input. withdrawal_credentials, randao_commitment=deposit.deposit_data.deposit_input. randao_commitment, custody_commitment=deposit.deposit_data.deposit_input. custody_commitment, ) for validator_index, _ in enumerate(state.validator_registry): validator_index = ValidatorIndex(validator_index) is_enough_effective_balance = get_effective_balance( state.validator_balances, validator_index, max_deposit, ) >= max_deposit * GWEI_PER_ETH if is_enough_effective_balance: state = activate_validator( state, validator_index, genesis=True, genesis_slot=genesis_slot, entry_exit_delay=entry_exit_delay, ) return state
def get_genesis_beacon_state( *, genesis_validator_deposits: Sequence[Deposit], genesis_time: Timestamp, genesis_eth1_data: Eth1Data, genesis_slot: Slot, genesis_epoch: Epoch, genesis_fork_version: int, genesis_start_shard: Shard, shard_count: int, min_seed_lookahead: int, slots_per_historical_root: int, latest_active_index_roots_length: int, slots_per_epoch: int, max_deposit_amount: Gwei, latest_slashed_exit_length: int, latest_randao_mixes_length: int, activation_exit_delay: int, deposit_contract_tree_depth: int, block_class: Type[BaseBeaconBlock]) -> BeaconState: state = BeaconState( # Misc slot=genesis_slot, genesis_time=genesis_time, fork=Fork( previous_version=genesis_fork_version.to_bytes(4, 'little'), current_version=genesis_fork_version.to_bytes(4, 'little'), epoch=genesis_epoch, ), # Validator registry validator_registry=(), validator_balances=(), validator_registry_update_epoch=genesis_epoch, # Randomness and committees latest_randao_mixes=(ZERO_HASH32, ) * latest_randao_mixes_length, previous_shuffling_start_shard=genesis_start_shard, current_shuffling_start_shard=genesis_start_shard, previous_shuffling_epoch=genesis_epoch, current_shuffling_epoch=genesis_epoch, previous_shuffling_seed=ZERO_HASH32, current_shuffling_seed=ZERO_HASH32, # Finality previous_epoch_attestations=(), current_epoch_attestations=(), previous_justified_epoch=genesis_epoch, current_justified_epoch=genesis_epoch, previous_justified_root=ZERO_HASH32, current_justified_root=ZERO_HASH32, justification_bitfield=0, finalized_epoch=genesis_epoch, finalized_root=ZERO_HASH32, # Recent state latest_crosslinks=((CrosslinkRecord( epoch=genesis_epoch, crosslink_data_root=ZERO_HASH32), ) * shard_count), latest_block_roots=(ZERO_HASH32, ) * slots_per_historical_root, latest_state_roots=(ZERO_HASH32, ) * slots_per_historical_root, latest_active_index_roots=(ZERO_HASH32, ) * latest_active_index_roots_length, latest_slashed_balances=(Gwei(0), ) * latest_slashed_exit_length, latest_block_header=get_temporary_block_header( BeaconBlock.create_empty_block(genesis_slot), ), historical_roots=(), # Ethereum 1.0 chain data latest_eth1_data=genesis_eth1_data, eth1_data_votes=(), deposit_index=0, ) # Process genesis deposits for deposit in genesis_validator_deposits: state = process_deposit( state=state, deposit=deposit, slots_per_epoch=slots_per_epoch, deposit_contract_tree_depth=deposit_contract_tree_depth, ) # Process genesis activations for validator_index, _ in enumerate(state.validator_registry): validator_index = ValidatorIndex(validator_index) is_enough_effective_balance = get_effective_balance( state.validator_balances, validator_index, max_deposit_amount, ) >= max_deposit_amount if is_enough_effective_balance: state = activate_validator( state=state, index=validator_index, is_genesis=True, genesis_epoch=genesis_epoch, slots_per_epoch=slots_per_epoch, activation_exit_delay=activation_exit_delay, ) # TODO: chanege to hash_tree_root active_validator_indices = get_active_validator_indices( state.validator_registry, genesis_epoch, ) genesis_active_index_root = hash_eth2(b''.join( [index.to_bytes(32, 'little') for index in active_validator_indices])) latest_active_index_roots = ( genesis_active_index_root, ) * latest_active_index_roots_length state = state.copy(latest_active_index_roots=latest_active_index_roots, ) current_shuffling_seed = generate_seed( state=state, epoch=genesis_epoch, slots_per_epoch=slots_per_epoch, min_seed_lookahead=min_seed_lookahead, activation_exit_delay=activation_exit_delay, latest_active_index_roots_length=latest_active_index_roots_length, latest_randao_mixes_length=latest_randao_mixes_length, ) state = state.copy(current_shuffling_seed=current_shuffling_seed, ) return state
def get_genesis_beacon_state(*, genesis_validator_deposits: Sequence[Deposit], genesis_time: Timestamp, latest_eth1_data: Eth1Data, genesis_slot: SlotNumber, genesis_epoch: EpochNumber, genesis_fork_version: int, genesis_start_shard: ShardNumber, shard_count: int, seed_lookahead: int, latest_block_roots_length: int, latest_index_roots_length: int, epoch_length: int, max_deposit_amount: Gwei, latest_penalized_exit_length: int, latest_randao_mixes_length: int, entry_exit_delay: int) -> BeaconState: state = BeaconState( # Misc slot=genesis_slot, genesis_time=genesis_time, fork=Fork( previous_version=genesis_fork_version, current_version=genesis_fork_version, epoch=genesis_epoch, ), # Validator registry validator_registry=(), validator_balances=(), validator_registry_update_epoch=genesis_epoch, # Randomness and committees latest_randao_mixes=(ZERO_HASH32,) * latest_randao_mixes_length, previous_epoch_start_shard=genesis_start_shard, current_epoch_start_shard=genesis_start_shard, previous_calculation_epoch=genesis_epoch, current_calculation_epoch=genesis_epoch, previous_epoch_seed=ZERO_HASH32, current_epoch_seed=ZERO_HASH32, # Finality previous_justified_epoch=genesis_epoch, justified_epoch=genesis_epoch, justification_bitfield=0, finalized_epoch=genesis_epoch, # Recent state latest_crosslinks=( (CrosslinkRecord(epoch=genesis_epoch, shard_block_root=ZERO_HASH32),) * shard_count ), latest_block_roots=(ZERO_HASH32,) * latest_block_roots_length, latest_index_roots=(ZERO_HASH32,) * latest_index_roots_length, latest_penalized_balances=(Gwei(0),) * latest_penalized_exit_length, latest_attestations=(), batched_block_roots=(), # Ethereum 1.0 chain data latest_eth1_data=latest_eth1_data, eth1_data_votes=(), deposit_index=len(genesis_validator_deposits), ) # Process initial deposits for deposit in genesis_validator_deposits: state = process_deposit( state=state, pubkey=deposit.deposit_data.deposit_input.pubkey, amount=deposit.deposit_data.amount, proof_of_possession=deposit.deposit_data.deposit_input.proof_of_possession, withdrawal_credentials=deposit.deposit_data.deposit_input.withdrawal_credentials, epoch_length=epoch_length, ) # Process initial activations for validator_index, _ in enumerate(state.validator_registry): validator_index = ValidatorIndex(validator_index) is_enough_effective_balance = get_effective_balance( state.validator_balances, validator_index, max_deposit_amount, ) >= max_deposit_amount if is_enough_effective_balance: state = activate_validator( state=state, index=validator_index, is_genesis=True, genesis_epoch=genesis_epoch, epoch_length=epoch_length, entry_exit_delay=entry_exit_delay, ) # TODO: chanege to hash_tree_root active_validator_indices = get_active_validator_indices( state.validator_registry, genesis_epoch, ) genesis_active_index_root = hash_eth2( b''.join( [ index.to_bytes(32, 'big') for index in active_validator_indices ] ) ) latest_index_roots = (genesis_active_index_root,) * latest_index_roots_length state = state.copy( latest_index_roots=latest_index_roots, ) current_epoch_seed = generate_seed( state=state, epoch=genesis_epoch, epoch_length=epoch_length, seed_lookahead=seed_lookahead, entry_exit_delay=entry_exit_delay, latest_index_roots_length=latest_index_roots_length, latest_randao_mixes_length=latest_randao_mixes_length, ) state = state.copy( current_epoch_seed=current_epoch_seed, ) return state
def create_mock_slashable_attestation( state: BeaconState, config: Eth2Config, keymap: Dict[BLSPubkey, int], attestation_slot: Slot) -> SlashableAttestation: """ Create `SlashableAttestation` that is signed by one attester. """ attester_index = ValidatorIndex(0) committee = (attester_index, ) shard = Shard(0) # Use genesis block root as `beacon_block_root`, only for tests. beacon_block_root = get_block_root( state, config.GENESIS_SLOT, config.SLOTS_PER_HISTORICAL_ROOT, ) # Get `target_root` target_root = _get_target_root(state, config, beacon_block_root) # Get `source_root` source_root = get_block_root( state, get_epoch_start_slot(state.current_justified_epoch, config.SLOTS_PER_EPOCH), config.SLOTS_PER_HISTORICAL_ROOT, ) previous_crosslink = state.latest_crosslinks[shard] attestation_data = AttestationData( slot=attestation_slot, beacon_block_root=beacon_block_root, source_epoch=state.current_justified_epoch, source_root=source_root, target_root=target_root, shard=shard, previous_crosslink=previous_crosslink, crosslink_data_root=ZERO_HASH32, ) message_hash, voting_committee_indices = _get_mock_message_and_voting_committee_indices( attestation_data, committee, num_voted_attesters=1, ) signature = sign_transaction( message_hash=message_hash, privkey=keymap[state.validator_registry[ voting_committee_indices[0]].pubkey], fork=state.fork, slot=attestation_slot, signature_domain=SignatureDomain.DOMAIN_ATTESTATION, slots_per_epoch=config.SLOTS_PER_EPOCH, ) validator_indices = tuple(committee[i] for i in voting_committee_indices) return SlashableAttestation( validator_indices=sorted(validator_indices), data=attestation_data, custody_bitfield=get_empty_bitfield(len(voting_committee_indices)), aggregate_signature=signature, )
def get_attestation_deltas( state: BeaconState, config: Eth2Config) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: rewards = tuple(0 for _ in range(len(state.validators))) penalties = tuple(0 for _ in range(len(state.validators))) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH) total_balance = get_total_active_balance(state, config) eligible_validator_indices = tuple( ValidatorIndex(index) for index, v in enumerate(state.validators) if v.is_active(previous_epoch) or ( v.slashed and previous_epoch + 1 < v.withdrawable_epoch)) matching_source_attestations = get_matching_source_attestations( state, previous_epoch, config) matching_target_attestations = get_matching_target_attestations( state, previous_epoch, config) matching_head_attestations = get_matching_head_attestations( state, previous_epoch, config) increment = config.EFFECTIVE_BALANCE_INCREMENT total_balance_in_increment = total_balance // increment for attestations in ( matching_source_attestations, matching_target_attestations, matching_head_attestations, ): unslashed_attesting_indices = get_unslashed_attesting_indices( state, attestations, config) attesting_balance = get_total_balance(state, unslashed_attesting_indices, config) attesting_balance_in_increment = attesting_balance // increment for index in eligible_validator_indices: if index in unslashed_attesting_indices: if is_in_inactivity_leak(state, config): reward = get_base_reward(state, index, config) else: reward = Gwei((get_base_reward(state, index, config) * attesting_balance_in_increment) // total_balance_in_increment) rewards = update_tuple_item_with_fn( rewards, index, lambda balance, delta: balance + delta, reward) else: penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, get_base_reward(state, index, config), ) for index in get_unslashed_attesting_indices(state, matching_source_attestations, config): attestation = min( (a for a in matching_source_attestations if index in get_attesting_indices(state, a.data, a.aggregation_bits, config)), key=lambda a: a.inclusion_delay, ) proposer_reward = get_proposer_reward(state, index, config) rewards = update_tuple_item_with_fn( rewards, attestation.proposer_index, lambda balance, delta: balance + delta, proposer_reward, ) base_reward = get_base_reward(state, index, config) max_attester_reward = base_reward - proposer_reward rewards = update_tuple_item_with_fn( rewards, index, lambda balance, delta: balance + delta, (max_attester_reward // attestation.inclusion_delay), ) if is_in_inactivity_leak(state, config): matching_target_attesting_indices = get_unslashed_attesting_indices( state, matching_target_attestations, config) for index in eligible_validator_indices: base_reward = get_base_reward(state, index, config) penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, BASE_REWARDS_PER_EPOCH * base_reward - get_proposer_reward(state, index, config), ) if index not in matching_target_attesting_indices: effective_balance = state.validators[index].effective_balance penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, effective_balance * get_finality_delay(state, config.SLOTS_PER_EPOCH) // config.INACTIVITY_PENALTY_QUOTIENT, ) return ( tuple(Gwei(reward) for reward in rewards), tuple(Gwei(penalty) for penalty in penalties), )
def get_attestation_deltas(state: BeaconState, config: Eth2Config) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: committee_config = CommitteeConfig(config) rewards = tuple( 0 for _ in range(len(state.validators)) ) penalties = tuple( 0 for _ in range(len(state.validators)) ) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) total_balance = get_total_active_balance(state, config) eligible_validator_indices = tuple( ValidatorIndex(index) for index, v in enumerate(state.validators) if v.is_active(previous_epoch) or ( v.slashed and previous_epoch + 1 < v.withdrawable_epoch ) ) matching_source_attestations = get_matching_source_attestations( state, previous_epoch, config, ) matching_target_attestations = get_matching_target_attestations( state, previous_epoch, config, ) matching_head_attestations = get_matching_head_attestations( state, previous_epoch, config, ) for attestations in ( matching_source_attestations, matching_target_attestations, matching_head_attestations ): unslashed_attesting_indices = get_unslashed_attesting_indices( state, attestations, committee_config, ) attesting_balance = get_total_balance(state, unslashed_attesting_indices) for index in eligible_validator_indices: if index in unslashed_attesting_indices: rewards = update_tuple_item_with_fn( rewards, index, lambda balance, delta: balance + delta, get_base_reward( state, index, config, ) * attesting_balance // total_balance, ) else: penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, get_base_reward( state, index, config, ), ) for index in get_unslashed_attesting_indices( state, matching_source_attestations, committee_config, ): attestation = min( ( a for a in matching_source_attestations if index in get_attesting_indices( state, a.data, a.aggregation_bitfield, committee_config, ) ), key=lambda a: a.inclusion_delay, ) base_reward = get_base_reward(state, index, config) proposer_reward = base_reward // config.PROPOSER_REWARD_QUOTIENT rewards = update_tuple_item_with_fn( rewards, attestation.proposer_index, lambda balance, delta: balance + delta, proposer_reward, ) max_attester_reward = base_reward - proposer_reward rewards = update_tuple_item_with_fn( rewards, index, lambda balance, delta: balance + delta, ( max_attester_reward * config.MIN_ATTESTATION_INCLUSION_DELAY // attestation.inclusion_delay ) ) finality_delay = previous_epoch - state.finalized_epoch if finality_delay > config.MIN_EPOCHS_TO_INACTIVITY_PENALTY: matching_target_attesting_indices = get_unslashed_attesting_indices( state, matching_target_attestations, committee_config, ) for index in eligible_validator_indices: penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, BASE_REWARDS_PER_EPOCH * get_base_reward( state, index, config, ), ) if index not in matching_target_attesting_indices: effective_balance = state.validators[index].effective_balance penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, effective_balance * finality_delay // config.INACTIVITY_PENALTY_QUOTIENT, ) return tuple( Gwei(reward) for reward in rewards ), tuple( Gwei(penalty) for penalty in penalties )
def create_mock_slashable_attestation( state: BeaconState, config: Eth2Config, keymap: Dict[BLSPubkey, int], attestation_slot: Slot, ) -> IndexedAttestation: """ Create an `IndexedAttestation` that is signed by one attester. """ attester_index = ValidatorIndex(0) committee = (attester_index, ) # Use genesis block root as `beacon_block_root`, only for tests. beacon_block_root = get_block_root_at_slot( state, attestation_slot, config.SLOTS_PER_HISTORICAL_ROOT) # Get `target_root` target_root = _get_target_root(state, config, beacon_block_root) # Get `source_root` source_root = get_block_root_at_slot( state, compute_start_slot_at_epoch(state.current_justified_checkpoint.epoch, config.SLOTS_PER_EPOCH), config.SLOTS_PER_HISTORICAL_ROOT, ) committees_per_slot = get_committee_count_at_slot( state, Slot(attestation_slot), config.MAX_COMMITTEES_PER_SLOT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) # Use the first committee assert committees_per_slot > 0 committee_index = CommitteeIndex(0) attestation_data = AttestationData.create( slot=attestation_slot, index=committee_index, beacon_block_root=beacon_block_root, source=Checkpoint.create( epoch=state.current_justified_checkpoint.epoch, root=source_root), target=Checkpoint.create( epoch=compute_epoch_at_slot(attestation_slot, config.SLOTS_PER_EPOCH), root=target_root, ), ) message_hash = attestation_data.hash_tree_root attesting_indices = _get_mock_attesting_indices(committee, num_voted_attesters=1) signature = sign_transaction( message_hash=message_hash, privkey=keymap[state.validators[attesting_indices[0]].pubkey], state=state, slot=attestation_slot, signature_domain=SignatureDomain.DOMAIN_BEACON_ATTESTER, slots_per_epoch=config.SLOTS_PER_EPOCH, ) validator_indices = tuple(committee[i] for i in attesting_indices) return IndexedAttestation.create(attesting_indices=validator_indices, data=attestation_data, signature=signature)
def _mini_stf(state: BeaconState, block: Optional[BeaconBlock], config: Eth2Config) -> BeaconState: """ A simplified state transition for testing state storage. - updates ``state_roots`` with the previous slot's state root - updates ``block_roots`` with the previous slot's block root - updates ``randao_mixes`` with an arbitrary mix at the current epoch - creates a new ``latest_block_header`` and adds it to the state - fills in the rest of the attributes with arbitrary values """ current_slot = state.slot + 1 current_epoch = current_slot // config.SLOTS_PER_EPOCH if block: latest_block_header = block.header else: latest_block_header = state.latest_block_header # state changes that depend on the previous state for retrieval randao_mix = Root(Hash32(current_slot.to_bytes(32, byteorder="little"))) state = (state.transform( ("state_roots", state.slot % config.SLOTS_PER_HISTORICAL_ROOT), state.hash_tree_root, ).transform( ("block_roots", state.slot % config.SLOTS_PER_HISTORICAL_ROOT), state.latest_block_header.hash_tree_root, ).transform( ("randao_mixes", current_epoch % config.EPOCHS_PER_HISTORICAL_VECTOR), randao_mix, ).mset("slot", current_slot, "latest_block_header", latest_block_header)) # state changes that do not depend on the previous state for retrieval new_validators = [ Validator.create(pubkey=BLSPubkey(n.to_bytes(48, byteorder="little"))) for n in range(current_slot, current_slot + 20) ] new_eth1_data_votes = [ Eth1Data.create( deposit_root=Root(Hash32(n.to_bytes(32, byteorder="little")))) for n in range(current_slot, current_slot + 7) ] new_previous_epoch_attestations = [ PendingAttestation.create(proposer_index=ValidatorIndex(n)) for n in range(current_slot, current_slot + 5) ] new_current_epoch_attestations = [ PendingAttestation.create(proposer_index=ValidatorIndex(n)) for n in range(current_slot + 5, current_slot + 10) ] state = state.mset( "validators", new_validators, "balances", (32, ) * len(new_validators), "eth1_data_votes", new_eth1_data_votes, "eth1_data", new_eth1_data_votes[0], "previous_epoch_attestations", new_previous_epoch_attestations, "current_epoch_attestations", new_current_epoch_attestations, "previous_justified_checkpoint", Checkpoint.create(epoch=Epoch(current_slot + 42)), "current_justified_checkpoint", Checkpoint.create(epoch=Epoch(current_slot + 43)), "finalized_checkpoint", Checkpoint.create(epoch=Epoch(current_slot + 44)), ) return state