def get_committee_assignment( state: BeaconState, config: Eth2Config, epoch: Epoch, validator_index: ValidatorIndex, ) -> CommitteeAssignment: """ Return the ``CommitteeAssignment`` in the ``epoch`` for ``validator_index``. ``CommitteeAssignment.committee`` is the tuple array of validators in the committee ``CommitteeAssignment.shard`` is the shard to which the committee is assigned ``CommitteeAssignment.slot`` is the slot at which the committee is assigned ``CommitteeAssignment.is_proposer`` is a bool signalling if the validator is expected to propose a beacon block at the assigned slot. """ next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH) if epoch > next_epoch: raise ValidationError( f"Epoch for committee assignment ({epoch}) must not be after next epoch {next_epoch}." ) active_validators = get_active_validator_indices(state.validators, epoch) committees_per_slot = ( get_committee_count( len(active_validators), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) // config.SLOTS_PER_EPOCH ) epoch_start_slot = compute_start_slot_of_epoch(epoch, config.SLOTS_PER_EPOCH) epoch_start_shard = get_start_shard(state, epoch, CommitteeConfig(config)) for slot in range(epoch_start_slot, epoch_start_slot + config.SLOTS_PER_EPOCH): offset = committees_per_slot * (slot % config.SLOTS_PER_EPOCH) slot_start_shard = (epoch_start_shard + offset) % config.SHARD_COUNT for i in range(committees_per_slot): shard = Shard((slot_start_shard + i) % config.SHARD_COUNT) committee = get_crosslink_committee( state, epoch, shard, CommitteeConfig(config) ) if validator_index in committee: is_proposer = validator_index == get_beacon_proposer_index( state.copy(slot=slot), CommitteeConfig(config) ) return CommitteeAssignment( committee, Shard(shard), Slot(slot), is_proposer ) raise NoCommitteeAssignment
def __init__(self, chain: BeaconChain, p2p_node: Node, validator_privkeys: Dict[ValidatorIndex, int], event_bus: EndpointAPI, get_ready_attestations_fn: GetReadyAttestationsFn, token: CancelToken = None) -> None: super().__init__(token) self.chain = chain self.p2p_node = p2p_node self.validator_privkeys = validator_privkeys self.event_bus = event_bus config = self.chain.get_state_machine().config self.slots_per_epoch = config.SLOTS_PER_EPOCH # TODO: `latest_proposed_epoch` and `latest_attested_epoch` should be written # into/read from validator's own db. self.latest_proposed_epoch = {} self.latest_attested_epoch = {} self.this_epoch_assignment = {} for validator_index in validator_privkeys: self.latest_proposed_epoch[validator_index] = Epoch(-1) self.latest_attested_epoch[validator_index] = Epoch(-1) self.this_epoch_assignment[validator_index] = ( Epoch(-1), CommitteeAssignment((), Shard(-1), Slot(-1), False), ) self.get_ready_attestations: GetReadyAttestationsFn = get_ready_attestations_fn
def get_compact_committees_root(state: BeaconState, epoch: Epoch, config: CommitteeConfig) -> Hash32: shard_count = config.SHARD_COUNT committees = (CompactCommittee(), ) * shard_count start_shard = get_start_shard(state, epoch, config) active_validator_indices = get_active_validator_indices( state.validators, epoch, ) committee_count = get_committee_count( len(active_validator_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) for committee_number in range(committee_count): shard = Shard((start_shard + committee_number) % shard_count) compact_committee = _compute_compact_committee_for_shard_in_epoch( state, epoch, shard, config, ) committees = update_tuple_item( committees, shard, compact_committee, ) return ssz.get_hash_tree_root(committees, sedes=ssz.sedes.Vector( CompactCommittee, shard_count))
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 get_crosslink_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))) epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) active_validators_indices = get_active_validator_indices( state.validators, epoch) epoch_committee_count = get_committee_count( len(active_validators_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) epoch_start_shard = get_start_shard( state, epoch, CommitteeConfig(config), ) for shard_offset in range(epoch_committee_count): shard = Shard((epoch_start_shard + shard_offset) % config.SHARD_COUNT) crosslink_committee = set( get_crosslink_committee( state, epoch, shard, CommitteeConfig(config), )) _, attesting_indices = get_winning_crosslink_and_attesting_indices( state=state, epoch=epoch, shard=shard, config=config, ) total_attesting_balance = get_total_balance( state, attesting_indices, ) total_committee_balance = get_total_balance( state, crosslink_committee, ) for index in crosslink_committee: base_reward = get_base_reward(state, index, config) if index in attesting_indices: rewards = update_tuple_item_with_fn( rewards, index, lambda balance, delta: balance + delta, base_reward * total_attesting_balance // total_committee_balance) else: penalties = update_tuple_item_with_fn( penalties, index, lambda balance, delta: balance + delta, base_reward, ) return tuple(Gwei(reward) for reward in rewards), tuple( Gwei(penalty) for penalty in penalties)
def process_crosslinks(state: BeaconState, config: Eth2Config) -> BeaconState: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) new_current_crosslinks = state.current_crosslinks for epoch in (previous_epoch, current_epoch): active_validators_indices = get_active_validator_indices( state.validators, epoch) epoch_committee_count = get_committee_count( len(active_validators_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) epoch_start_shard = get_start_shard( state, epoch, CommitteeConfig(config), ) for shard_offset in range(epoch_committee_count): shard = Shard( (epoch_start_shard + shard_offset) % config.SHARD_COUNT) crosslink_committee = set( get_crosslink_committee( state, epoch, shard, CommitteeConfig(config), )) if not crosslink_committee: # empty crosslink committee this epoch continue winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices( state=state, epoch=epoch, shard=shard, config=config, ) threshold_met = _is_threshold_met_against_committee( state, attesting_indices, crosslink_committee, ) if threshold_met: new_current_crosslinks = update_tuple_item( new_current_crosslinks, shard, winning_crosslink, ) return state.copy( previous_crosslinks=state.current_crosslinks, current_crosslinks=new_current_crosslinks, )
def _mk_attestation_inputs_in_epoch(epoch, state, config): active_validators_indices = get_active_validator_indices(state.validators, epoch) epoch_committee_count = get_committee_count( len(active_validators_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) epoch_start_shard = get_start_shard( state, epoch, CommitteeConfig(config), ) for shard_offset in random.sample(range(epoch_committee_count), epoch_committee_count): shard = Shard((epoch_start_shard + shard_offset) % config.SHARD_COUNT) committee = get_crosslink_committee( state, epoch, shard, CommitteeConfig(config), ) if not committee: # empty crosslink committee this epoch continue attestation_data = AttestationData( target=Checkpoint( epoch=epoch, ), crosslink=Crosslink( shard=shard, ), ) committee_count = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_count) for index in range(committee_count): aggregation_bits = bitfield.set_voted(aggregation_bits, index) for index in committee: yield ( index, ( get_attestation_data_slot( state, attestation_data, config, ), ( aggregation_bits, attestation_data, ), ), )
def get_crosslink_committees_at_slot( state: BeaconState, slot: Slot, config: Eth2Config ) -> Tuple[Tuple[Tuple[ValidatorIndex, ...], Shard], ...]: epoch = slot_to_epoch(slot, config.SLOTS_PER_EPOCH) active_validators = get_active_validator_indices(state.validators, epoch) committees_per_slot = get_epoch_committee_count( len(active_validators), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) // config.SLOTS_PER_EPOCH results = [] offset = committees_per_slot * (slot % config.SLOTS_PER_EPOCH) slot_start_shard = Shard( (get_epoch_start_shard(state, epoch, CommitteeConfig(config)) + offset) % config.SHARD_COUNT) for i in range(committees_per_slot): shard = (slot_start_shard + i) % config.SHARD_COUNT committee = get_crosslink_committee(state, epoch, shard, CommitteeConfig(config)) results.append((committee, Shard(shard))) return tuple(results)
def _find_collision(state, config, index, epoch): """ Given a target epoch, make the attestation expected for the validator w/ the given index. """ active_validators = get_active_validator_indices(state.validators, epoch) committees_per_slot = get_committee_count( len(active_validators), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) // config.SLOTS_PER_EPOCH epoch_start_slot = compute_start_slot_of_epoch( epoch, config.SLOTS_PER_EPOCH, ) epoch_start_shard = get_start_shard(state, epoch, CommitteeConfig(config)) for slot in range(epoch_start_slot, epoch_start_slot + config.SLOTS_PER_EPOCH): offset = committees_per_slot * (slot % config.SLOTS_PER_EPOCH) slot_start_shard = (epoch_start_shard + offset) % config.SHARD_COUNT for i in range(committees_per_slot): shard = Shard((slot_start_shard + i) % config.SHARD_COUNT) committee = get_crosslink_committee(state, epoch, shard, CommitteeConfig(config)) if index in committee: # TODO(ralexstokes) refactor w/ tools/builder attestation_data = AttestationData( target=Checkpoint( epoch=epoch, ), crosslink=Crosslink( shard=shard, ), ) committee_count = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_count) for i in range(committee_count): aggregation_bits = bitfield.set_voted(aggregation_bits, i) return { index: ( slot, (aggregation_bits, attestation_data) ) for index in committee } else: raise Exception("should have found a duplicate validator")
def _mk_some_pending_attestations_with_some_participation_in_epoch( state: BeaconState, epoch: Epoch, config: Eth2Config, participation_ratio: float, number_of_shards_to_check: int) -> Iterable[PendingAttestation]: block_root = get_block_root( state, epoch, config.SLOTS_PER_EPOCH, config.SLOTS_PER_HISTORICAL_ROOT, ) epoch_start_shard = get_epoch_start_shard( state, epoch, CommitteeConfig(config), ) if epoch == state.current_epoch(config.SLOTS_PER_EPOCH): parent_crosslinks = state.current_crosslinks else: parent_crosslinks = state.previous_crosslinks for shard in range(epoch_start_shard, epoch_start_shard + number_of_shards_to_check): shard = Shard(shard % config.SHARD_COUNT) crosslink_committee = get_crosslink_committee( state, epoch, shard, CommitteeConfig(config), ) if not crosslink_committee: continue participants_count = math.ceil(participation_ratio * len(crosslink_committee)) if not participants_count: return tuple() yield mk_pending_attestation_from_committee( parent_crosslinks[shard], participants_count, shard, target_epoch=epoch, target_root=block_root, )
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, )
MAX_EXIT_DEQUEUES_PER_EPOCH=2**2, # (= 4) SHUFFLE_ROUND_COUNT=90, # Deposit contract DEPOSIT_CONTRACT_ADDRESS=ZERO_ADDRESS, # TBD DEPOSIT_CONTRACT_TREE_DEPTH=2**5, # (= 32) # Gwei values MIN_DEPOSIT_AMOUNT=Gwei(2**0 * GWEI_PER_ETH), # (= 1,000,000,000) Gwei MAX_DEPOSIT_AMOUNT=Gwei(2**5 * GWEI_PER_ETH), # (= 32,000,000,00) Gwei FORK_CHOICE_BALANCE_INCREMENT=Gwei(2**0 * GWEI_PER_ETH), # (= 1,000,000,000) Gwei EJECTION_BALANCE=Gwei(2**4 * GWEI_PER_ETH), # (= 16,000,000,000) Gwei # Initial values GENESIS_FORK_VERSION=0, GENESIS_SLOT=GENESIS_SLOT, GENESIS_EPOCH=slot_to_epoch(GENESIS_SLOT, SLOTS_PER_EPOCH), GENESIS_START_SHARD=Shard(0), BLS_WITHDRAWAL_PREFIX_BYTE=b'\x00', # Time parameters SECONDS_PER_SLOT=Second(6), # seconds MIN_ATTESTATION_INCLUSION_DELAY=2**2, # (= 4) slots SLOTS_PER_EPOCH=SLOTS_PER_EPOCH, # (= 64) slots MIN_SEED_LOOKAHEAD=2**0, # (= 1) epochs ACTIVATION_EXIT_DELAY=2**2, # (= 4) epochs EPOCHS_PER_ETH1_VOTING_PERIOD=2**4, # (= 16) epochs MIN_VALIDATOR_WITHDRAWABILITY_DELAY=2**8, # (= 256) epochs PERSISTENT_COMMITTEE_PERIOD=2**11, # (= 2,048) epochs # State list lengths SLOTS_PER_HISTORICAL_ROOT=2**13, # (= 8,192) slots LATEST_ACTIVE_INDEX_ROOTS_LENGTH=2**13, # (= 8,192) epochs LATEST_RANDAO_MIXES_LENGTH=2**13, # (= 8,192) epochs LATEST_SLASHED_EXIT_LENGTH=2**13, # (= 8,192) epochs
def get_crosslink_committees_at_slot( state: 'BeaconState', slot: Slot, committee_config: CommitteeConfig, registry_change: bool=False) -> Iterable[Tuple[Sequence[ValidatorIndex], Shard]]: """ Return the list of ``(committee, shard)`` tuples for the ``slot``. """ shard_count = committee_config.SHARD_COUNT slots_per_epoch = committee_config.SLOTS_PER_EPOCH epoch = slot_to_epoch(slot, slots_per_epoch) current_epoch = state.current_epoch(slots_per_epoch) previous_epoch = state.previous_epoch(slots_per_epoch) next_epoch = state.next_epoch(slots_per_epoch) validate_epoch_within_previous_and_next(epoch, previous_epoch, next_epoch) if epoch == current_epoch: shuffling_context = _get_shuffling_context_is_current_epoch(state, committee_config) elif epoch == previous_epoch: shuffling_context = _get_shuffling_context_is_previous_epoch(state, committee_config) elif epoch == next_epoch: epochs_since_last_registry_update = current_epoch - state.validator_registry_update_epoch should_reseed = ( epochs_since_last_registry_update > 1 and is_power_of_two(epochs_since_last_registry_update) ) if registry_change: shuffling_context = _get_shuffling_contextis_next_epoch_registry_change( state, next_epoch, committee_config, ) elif should_reseed: shuffling_context = _get_shuffling_contextis_next_epoch_should_reseed( state, next_epoch, committee_config, ) else: shuffling_context = _get_shuffling_contextis_next_epoch_no_registry_change_no_reseed( state, committee_config, ) shuffling = get_shuffling( seed=shuffling_context.seed, validators=state.validator_registry, epoch=shuffling_context.shuffling_epoch, committee_config=committee_config, ) offset = slot % slots_per_epoch committees_per_slot = shuffling_context.committees_per_epoch // slots_per_epoch slot_start_shard = ( shuffling_context.shuffling_start_shard + committees_per_slot * offset ) % shard_count for index in range(committees_per_slot): committee = shuffling[committees_per_slot * offset + index] yield ( committee, Shard((slot_start_shard + index) % shard_count), )
Gwei, Second, Shard, Slot, ) GENESIS_SLOT = Slot(2**32) SLOTS_PER_EPOCH = 2**6 SERENITY_CONFIG = Eth2Config( # Misc SHARD_COUNT=2**10, # (= 1,024) shards TARGET_COMMITTEE_SIZE=2**7, # (= 128) validators EJECTION_BALANCE=Gwei(2**4 * GWEI_PER_ETH), # (= 16,000,000,000) Gwei MAX_BALANCE_CHURN_QUOTIENT=2**5, # (= 32) BEACON_CHAIN_SHARD_NUMBER=Shard(2**64 - 1), MAX_INDICES_PER_SLASHABLE_VOTE=2**12, # (= 4,096) votes MAX_EXIT_DEQUEUES_PER_EPOCH=2**2, # (= 4) SHUFFLE_ROUND_COUNT=90, # State list lengths SLOTS_PER_HISTORICAL_ROOT=2**13, # (= 8,192) slots LATEST_ACTIVE_INDEX_ROOTS_LENGTH=2**13, # (= 8,192) epochs LATEST_RANDAO_MIXES_LENGTH=2**13, # (= 8,192) epochs LATEST_SLASHED_EXIT_LENGTH=2**13, # (= 8,192) epochs # Deposit contract DEPOSIT_CONTRACT_ADDRESS=ZERO_ADDRESS, # TBD DEPOSIT_CONTRACT_TREE_DEPTH=2**5, # (= 32) MIN_DEPOSIT_AMOUNT=Gwei(2**0 * GWEI_PER_ETH), # (= 1,000,000,000) Gwei MAX_DEPOSIT_AMOUNT=Gwei(2**5 * GWEI_PER_ETH), # (= 32,000,000,00) Gwei # Genesis values GENESIS_FORK_VERSION=0,