def test_get_matching_target_attestations(genesis_state, config): some_epoch = GENESIS_EPOCH + 20 some_slot = compute_start_slot_at_epoch(some_epoch, config.SLOTS_PER_EPOCH) some_target_root = b"\x33" * 32 target_attestations = tuple( PendingAttestation.create( data=AttestationData.create(target=Checkpoint.create(root=some_target_root)) ) for _ in range(3) ) current_epoch_attestations = target_attestations + tuple( PendingAttestation.create( data=AttestationData.create(target=Checkpoint.create(root=b"\x44" * 32)) ) for _ in range(3) ) state = genesis_state.transform( ["slot"], some_slot + 1, ["block_roots", some_slot % config.SLOTS_PER_HISTORICAL_ROOT], some_target_root, ["current_epoch_attestations"], current_epoch_attestations, ) attestations = get_matching_target_attestations(state, some_epoch, config) assert attestations == target_attestations
def test_get_matching_head_attestations(genesis_state, config): some_epoch = config.GENESIS_EPOCH + 20 some_slot = ( compute_start_slot_at_epoch(some_epoch, config.SLOTS_PER_EPOCH) + config.SLOTS_PER_EPOCH // 4) some_target_root = b"\x33" * 32 target_attestations = tuple( (PendingAttestation.create(data=AttestationData.create( slot=some_slot - 1, index=0, beacon_block_root=some_target_root, target=Checkpoint.create(epoch=some_epoch - 1), )) for i in range(3))) current_epoch_attestations = target_attestations + tuple( (PendingAttestation.create(data=AttestationData.create( beacon_block_root=b"\x44" * 32, target=Checkpoint.create(epoch=some_epoch - 1), )) for _ in range(3))) state = genesis_state.mset( "slot", some_slot, "block_roots", tuple(some_target_root for _ in range(config.SLOTS_PER_HISTORICAL_ROOT)), "current_epoch_attestations", current_epoch_attestations, ) attestations = get_matching_head_attestations(state, some_epoch, config) assert attestations == target_attestations
def sample_attestation_data_params(): return { "slot": 5, "index": 1, "beacon_block_root": b"\x11" * 32, "source": Checkpoint.create(epoch=11, root=b"\x22" * 32), "target": Checkpoint.create(epoch=12, root=b"\x33" * 32), }
def test_process_justification_and_finalization( genesis_state, current_epoch, current_epoch_justifiable, previous_epoch_justifiable, previous_justified_epoch, current_justified_epoch, justification_bits, finalized_epoch, justified_epoch_after, justification_bits_after, finalized_epoch_after, config, ): justification_bits = _convert_to_bitfield(justification_bits) justification_bits_after = _convert_to_bitfield(justification_bits_after) previous_epoch = max(current_epoch - 1, 0) slot = (current_epoch + 1) * config.SLOTS_PER_EPOCH - 1 state = genesis_state.mset( "slot", slot, "previous_justified_checkpoint", Checkpoint.create(epoch=previous_justified_epoch), "current_justified_checkpoint", Checkpoint.create(epoch=current_justified_epoch), "justification_bits", justification_bits, "finalized_checkpoint", Checkpoint.create(epoch=finalized_epoch), "block_roots", tuple( i.to_bytes(32, "little") for i in range(config.SLOTS_PER_HISTORICAL_ROOT)), ) if previous_epoch_justifiable: attestations = mk_all_pending_attestations_with_full_participation_in_epoch( state, previous_epoch, config) state = state.set("previous_epoch_attestations", attestations) if current_epoch_justifiable: attestations = mk_all_pending_attestations_with_full_participation_in_epoch( state, current_epoch, config) state = state.set("current_epoch_attestations", attestations) post_state = process_justification_and_finalization(state, config) assert (post_state.previous_justified_checkpoint.epoch == state.current_justified_checkpoint.epoch) assert post_state.current_justified_checkpoint.epoch == justified_epoch_after assert post_state.justification_bits == justification_bits_after assert post_state.finalized_checkpoint.epoch == finalized_epoch_after
def create_mock_signed_attestations_at_slot( state: BeaconState, config: Eth2Config, state_machine: BaseBeaconStateMachine, attestation_slot: Slot, beacon_block_root: Root, keymap: Dict[BLSPubkey, int], voted_attesters_ratio: float = 1.0, ) -> Iterable[Attestation]: """ Create the mocking attestations of the given ``attestation_slot`` slot with ``keymap``. """ if voted_attesters_ratio == 0: return () committees_per_slot = get_committee_count_at_slot( state, attestation_slot, config.MAX_COMMITTEES_PER_SLOT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) # Get `target_root` target_root = _get_target_root(state, config, beacon_block_root) target_epoch = compute_epoch_at_slot(state.slot, config.SLOTS_PER_EPOCH) for committee, committee_index, _ in iterate_committees_at_slot( state, attestation_slot, committees_per_slot, config): attestation_data = AttestationData.create( slot=attestation_slot, index=CommitteeIndex(committee_index), beacon_block_root=beacon_block_root, source=Checkpoint.create( epoch=state.current_justified_checkpoint.epoch, root=state.current_justified_checkpoint.root, ), target=Checkpoint.create(root=target_root, epoch=target_epoch), ) num_voted_attesters = max(int(len(committee) * voted_attesters_ratio), 1) yield _create_mock_signed_attestation( state, attestation_data, attestation_slot, committee, num_voted_attesters, keymap, config.SLOTS_PER_EPOCH, )
def sample_beacon_state_params( config, genesis_slot, genesis_epoch, sample_fork_params, sample_eth1_data_params, sample_block_header_params, ): return { # Versioning "genesis_time": 0, "slot": genesis_slot + 100, "fork": Fork.create(**sample_fork_params), # History "latest_block_header": BeaconBlockHeader.create(**sample_block_header_params), "block_roots": (ZERO_HASH32, ) * config.SLOTS_PER_HISTORICAL_ROOT, "state_roots": (ZERO_HASH32, ) * config.SLOTS_PER_HISTORICAL_ROOT, "historical_roots": (), # Eth1 "eth1_data": Eth1Data.create(**sample_eth1_data_params), "eth1_data_votes": (), "eth1_deposit_index": 0, # Registry "validators": (), "balances": (), # Shuffling "randao_mixes": (ZERO_HASH32, ) * config.EPOCHS_PER_HISTORICAL_VECTOR, # Slashings "slashings": (0, ) * config.EPOCHS_PER_SLASHINGS_VECTOR, # Attestations "previous_epoch_attestations": (), "current_epoch_attestations": (), # Justification "justification_bits": (False, ) * JUSTIFICATION_BITS_LENGTH, "previous_justified_checkpoint": Checkpoint.create(epoch=0, root=b"\x99" * 32), "current_justified_checkpoint": Checkpoint.create(epoch=0, root=b"\x55" * 32), # Finality "finalized_checkpoint": Checkpoint.create(epoch=0, root=b"\x33" * 32), }
def test_get_attesting_indices(genesis_state, config): state = genesis_state.set( "slot", compute_start_slot_at_epoch(3, config.SLOTS_PER_EPOCH)) target_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) target_slot = compute_start_slot_at_epoch(target_epoch, config.SLOTS_PER_EPOCH) committee_index = 0 some_committee = get_beacon_committee(state, target_slot, committee_index, config) data = AttestationData.create( slot=target_slot, index=committee_index, target=Checkpoint.create(epoch=target_epoch), ) some_subset_count = random.randrange(1, len(some_committee) // 2) some_subset = random.sample(some_committee, some_subset_count) bitfield = get_empty_bitfield(len(some_committee)) for i, index in enumerate(some_committee): if index in some_subset: bitfield = set_voted(bitfield, i) indices = get_attesting_indices(state, data, bitfield, config) assert set(indices) == set(some_subset) assert len(indices) == len(some_subset)
def _determine_new_finalized_checkpoint(state: BeaconState, justification_bits: Bitfield, config: Eth2Config) -> Checkpoint: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) new_finalized_epoch = _determine_new_finalized_epoch( state.finalized_checkpoint.epoch, state.previous_justified_checkpoint.epoch, state.current_justified_checkpoint.epoch, current_epoch, justification_bits, ) if new_finalized_epoch != state.finalized_checkpoint.epoch: # NOTE: we only want to call ``get_block_root`` # upon some change, not unconditionally # Given the way it reads the block roots, it can cause # validation problems with some configurations, esp. in testing. # This is implicitly happening above for the justified roots. new_finalized_root = get_block_root( state, new_finalized_epoch, config.SLOTS_PER_EPOCH, config.SLOTS_PER_HISTORICAL_ROOT, ) else: new_finalized_root = state.finalized_checkpoint.root return Checkpoint.create(epoch=new_finalized_epoch, root=new_finalized_root)
def _load_checkpoints(self) -> None: justified = self._chain_db.get_justified_head(BeaconBlock) finalized = self._chain_db.get_finalized_head(BeaconBlock) state_machine = self.get_state_machine(self._current_head.slot) config = state_machine.config self._justified_checkpoint = Checkpoint.create( epoch=compute_epoch_at_slot(justified.slot, config.SLOTS_PER_EPOCH), root=justified.hash_tree_root, ) self._finalized_checkpoint = Checkpoint.create( epoch=compute_epoch_at_slot(finalized.slot, config.SLOTS_PER_EPOCH), root=finalized.hash_tree_root, )
def _find_collision(state, config, validator_index, epoch, block_producer): """ Given a target epoch, make the attestation expected for the validator w/ the given ``validator_index``. """ for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, config): if slot >= state.slot: # do not make attestations in the future return {} if validator_index in committee: # TODO(ralexstokes) refactor w/ tools/builder block = block_producer(slot) root = block.message.hash_tree_root attestation_data = AttestationData.create( slot=slot, index=committee_index, target=Checkpoint.create(epoch=epoch, root=root), beacon_block_root=root, ) 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_attestation_inputs_in_epoch(epoch, block_producer, state, config): for committee, committee_index, slot in iterate_committees_at_epoch( state, epoch, config): if not committee: # empty committee this slot continue if slot >= state.slot: # do not make attestations in the future break block = block_producer(slot) root = block.message.hash_tree_root attestation_data = AttestationData.create( slot=slot, index=committee_index, target=Checkpoint.create(epoch=epoch, root=root), beacon_block_root=root, ) committee_size = len(committee) aggregation_bits = bitfield.get_empty_bitfield(committee_size) for index in range(committee_size): aggregation_bits = bitfield.set_voted(aggregation_bits, index) for index in committee: yield ( index, (attestation_data.slot, (aggregation_bits, attestation_data)), )
def __init__( self, finalized_block_node: BlockNode[BaseBeaconBlock], justified_checkpoint: Checkpoint, justified_state: BeaconState, config: Eth2Config, block_sink: BlockSink, ) -> None: self._config = config finalized_checkpoint = Checkpoint.create( epoch=compute_epoch_at_slot(finalized_block_node.slot, config.SLOTS_PER_EPOCH), root=finalized_block_node.root, ) self._impl = ProtoArrayForkChoice( finalized_block_node, finalized_checkpoint, justified_checkpoint, block_sink, config, ) self._justified = justified_checkpoint self._finalized = finalized_checkpoint self.update_justified(justified_state)
def create_signed_attestation_at_slot( state: BeaconState, config: Eth2Config, state_machine: BaseBeaconStateMachine, attestation_slot: Slot, beacon_block_root: SigningRoot, validator_privkeys: Dict[ValidatorIndex, int], committee: Tuple[ValidatorIndex, ...], committee_index: CommitteeIndex, attesting_indices: Sequence[CommitteeValidatorIndex], ) -> Attestation: """ Create the attestations of the given ``attestation_slot`` slot with ``validator_privkeys``. """ state_transition = state_machine.state_transition state = state_transition.apply_state_transition( state, future_slot=attestation_slot) target_epoch = compute_epoch_at_slot(attestation_slot, config.SLOTS_PER_EPOCH) target_root = _get_target_root(state, config, beacon_block_root) 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=state.current_justified_checkpoint.root, ), target=Checkpoint.create(root=target_root, epoch=target_epoch), ) return _create_mock_signed_attestation( state, attestation_data, attestation_slot, committee, len(committee), keymapper(lambda index: state.validators[index].pubkey, validator_privkeys), config.SLOTS_PER_EPOCH, is_for_simulation=False, attesting_indices=attesting_indices, )
def _get_target_checkpoint(state: BeaconState, head_root: Root, config: Eth2Config) -> Checkpoint: epoch = state.current_epoch(config.SLOTS_PER_EPOCH) start_slot = compute_start_slot_at_epoch(epoch, config.SLOTS_PER_EPOCH) if start_slot == state.slot: root = head_root else: root = get_block_root_at_slot(state, start_slot, config.SLOTS_PER_HISTORICAL_ROOT) return Checkpoint.create(epoch=epoch, root=root)
def from_genesis(cls, genesis_state: BeaconState, config: Eth2Config, block_sink: BlockSink) -> "LMDGHOSTForkChoice": # NOTE: patch up genesis state to reflect the genesis block as an initial checkpoint # this only has to be patched once at genesis genesis_block = get_genesis_block(genesis_state.hash_tree_root, BeaconBlock) genesis_block_node = _block_to_block_node(genesis_block) justified_checkpoint = Checkpoint.create() return cls(genesis_block_node, justified_checkpoint, genesis_state, config, block_sink)
def create_atteatsion_data( state: BeaconState, config: Eth2Config, state_machine: BaseBeaconStateMachine, attestation_slot: Slot, beacon_block_root: Root, committee_index: CommitteeIndex, ) -> AttestationData: target_epoch = compute_epoch_at_slot(attestation_slot, config.SLOTS_PER_EPOCH) target_root = _get_target_root(state, config, beacon_block_root) return AttestationData.create( slot=attestation_slot, index=committee_index, beacon_block_root=beacon_block_root, source=Checkpoint.create( epoch=state.current_justified_checkpoint.epoch, root=state.current_justified_checkpoint.root, ), target=Checkpoint.create(root=target_root, epoch=target_epoch), )
def test_validate_attestation_data( genesis_state, sample_attestation_data_params, attestation_source_epoch, attestation_target_epoch, current_epoch, previous_justified_epoch, current_justified_epoch, slots_per_epoch, config, is_valid, ): state = genesis_state.mset( "slot", compute_start_slot_at_epoch(current_epoch, slots_per_epoch) + 5, "previous_justified_checkpoint", Checkpoint.create(epoch=previous_justified_epoch), "current_justified_checkpoint", Checkpoint.create(epoch=current_justified_epoch), ) target_slot = compute_start_slot_at_epoch(current_epoch, config.SLOTS_PER_EPOCH) committee_index = 0 attestation_data = AttestationData.create(**sample_attestation_data_params).mset( "slot", target_slot, "index", committee_index, "source", Checkpoint.create(epoch=attestation_source_epoch), "target", Checkpoint.create(epoch=attestation_target_epoch), ) if is_valid: _validate_attestation_data(state, attestation_data, config) else: with pytest.raises(ValidationError): _validate_attestation_data(state, attestation_data, config)
def test_chaindb_get_justified_head( chaindb_at_genesis, genesis_block, genesis_state, sample_beacon_block_params, fork_choice_scoring, config, ): chaindb = chaindb_at_genesis block = SignedBeaconBlock.from_parent(genesis_block, FromBlockParams(slot=1)) assert chaindb.get_finalized_head(genesis_block.__class__) == genesis_block assert chaindb.get_justified_head(genesis_block.__class__) == genesis_block # test that there is only one justified head per epoch state_with_bad_epoch = genesis_state.set( "current_justified_checkpoint", Checkpoint.create(root=block.message.hash_tree_root, epoch=config.GENESIS_EPOCH), ) chaindb.persist_state(state_with_bad_epoch) chaindb.persist_block(block, SignedBeaconBlock, fork_choice_scoring) assert chaindb.get_finalized_head(genesis_block.__class__) == genesis_block assert chaindb.get_justified_head(genesis_block.__class__) == genesis_block # test that the we can update justified head if we satisfy the invariants state_with_justified_block = genesis_state.set( "current_justified_checkpoint", Checkpoint.create(root=block.message.hash_tree_root, epoch=config.GENESIS_EPOCH + 1), ) chaindb.persist_state(state_with_justified_block) assert chaindb.get_finalized_head(genesis_block.__class__) == genesis_block assert (chaindb.get_justified_head(SignedBeaconBlock).message. hash_tree_root == block.message.hash_tree_root)
def _mk_pending_attestation( bitfield: Bitfield = default_bitfield, target_root: Root = ZERO_ROOT, target_epoch: Epoch = default_epoch, slot: Slot = default_slot, committee_index: CommitteeIndex = default_committee_index, ) -> PendingAttestation: return PendingAttestation.create( aggregation_bits=bitfield, data=AttestationData.create( slot=slot, index=committee_index, target=Checkpoint.create(epoch=target_epoch, root=target_root), ), )
def _mk_attestation_for_block_with_committee(block, committee, committee_index, config): 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) attestation = Attestation.create( aggregation_bits=aggregation_bits, data=AttestationData.create( slot=block.slot, index=committee_index, beacon_block_root=block.message.hash_tree_root, target=Checkpoint.create(epoch=compute_epoch_at_slot( block.slot, config.SLOTS_PER_EPOCH)), ), ) return attestation
def from_db(cls, chain_db: BaseBeaconChainDB, config: Eth2Config, block_sink: BlockSink) -> "LMDGHOSTForkChoice": canonical_head = chain_db.get_canonical_head(BeaconBlock) justified_head = chain_db.get_justified_head(BeaconBlock) finalized_head = chain_db.get_finalized_head(BeaconBlock) assert canonical_head.slot >= justified_head.slot >= finalized_head.slot finalized_head_node = _block_to_block_node(finalized_head) justified_checkpoint = Checkpoint.create( epoch=compute_epoch_at_slot(justified_head.slot, config.SLOTS_PER_EPOCH), root=justified_head.hash_tree_root, ) justified_state = chain_db.get_state_by_root(justified_head.state_root, BeaconState, config) fork_choice = cls( finalized_head_node, justified_checkpoint, justified_state, config, block_sink, ) is_consistent_with_justified = False parent_root = canonical_head.parent_root chain: Tuple[BaseBeaconBlock, ...] = (canonical_head, ) while parent_root != finalized_head.hash_tree_root: if parent_root == justified_head.hash_tree_root: is_consistent_with_justified = True parent = chain_db.get_block_by_root(parent_root, BeaconBlock) chain += (parent, ) parent_root = parent.parent_root assert is_consistent_with_justified for block in reversed(chain): fork_choice.on_block(block) return fork_choice
def test_chaindb_get_finalized_head( chaindb_at_genesis, genesis_block, genesis_state, sample_beacon_block_params, fork_choice_scoring, ): chaindb = chaindb_at_genesis block = BeaconBlock.create(**sample_beacon_block_params).set( "parent_root", genesis_block.signing_root) assert chaindb.get_finalized_head(genesis_block.__class__) == genesis_block assert chaindb.get_justified_head(genesis_block.__class__) == genesis_block state_with_finalized_block = genesis_state.set( "finalized_checkpoint", Checkpoint.create(root=block.signing_root)) chaindb.persist_state(state_with_finalized_block) chaindb.persist_block(block, BeaconBlock, fork_choice_scoring) assert chaindb.get_finalized_head( BeaconBlock).signing_root == block.signing_root assert chaindb.get_justified_head(genesis_block.__class__) == genesis_block
def _determine_new_justified_checkpoint_and_bitfield( state: BeaconState, config: Eth2Config) -> Tuple[Checkpoint, Bitfield]: ( new_current_justified_epoch, justification_bits, ) = _determine_new_justified_epoch_and_bitfield(state, config) if new_current_justified_epoch != state.current_justified_checkpoint.epoch: new_current_justified_root = get_block_root( state, new_current_justified_epoch, config.SLOTS_PER_EPOCH, config.SLOTS_PER_HISTORICAL_ROOT, ) else: new_current_justified_root = state.current_justified_checkpoint.root return ( Checkpoint.create(epoch=new_current_justified_epoch, root=new_current_justified_root), justification_bits, )
def test_get_unslashed_attesting_indices(genesis_state, config): state = genesis_state.set( "slot", compute_start_slot_at_epoch(3, config.SLOTS_PER_EPOCH) ) target_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) target_slot = compute_start_slot_at_epoch(target_epoch, config.SLOTS_PER_EPOCH) committee_index = 0 some_committee = get_beacon_committee( state, target_slot, committee_index, CommitteeConfig(config) ) data = AttestationData.create( slot=state.slot, index=committee_index, target=Checkpoint.create(epoch=target_epoch), ) some_subset_count = random.randrange(1, len(some_committee) // 2) some_subset = random.sample(some_committee, some_subset_count) bitfield = get_empty_bitfield(len(some_committee)) for i, index in enumerate(some_committee): if index in some_subset: if random.choice([True, False]): state = state.transform(["validators", index, "slashed"], True) bitfield = set_voted(bitfield, i) some_subset = tuple( filter(lambda index: not state.validators[index].slashed, some_subset) ) indices = get_unslashed_attesting_indices( state, (PendingAttestation.create(data=data, aggregation_bits=bitfield),), CommitteeConfig(config), ) assert set(indices) == set(some_subset) assert len(indices) == len(some_subset)
def test_chaindb_get_finalized_head( chaindb_at_genesis, genesis_block, genesis_state, sample_beacon_block_params, fork_choice_scoring, ): chaindb = chaindb_at_genesis block = SignedBeaconBlock.from_parent(genesis_block, FromBlockParams(slot=1)) assert chaindb.get_finalized_head(genesis_block.__class__) == genesis_block assert chaindb.get_justified_head(genesis_block.__class__) == genesis_block state_with_finalized_block = genesis_state.set( "finalized_checkpoint", Checkpoint.create(root=block.message.hash_tree_root)) chaindb.persist_state(state_with_finalized_block) chaindb.persist_block(block, SignedBeaconBlock, fork_choice_scoring) assert (chaindb.get_finalized_head(SignedBeaconBlock).message. hash_tree_root == block.message.hash_tree_root) assert chaindb.get_justified_head(genesis_block.__class__) == genesis_block
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 finalized_checkpoint(self) -> Checkpoint: return Checkpoint.create(root=self.finalized_root, epoch=self.finalized_epoch)
def test_lmd_ghost_fork_choice_scoring( sample_beacon_block_params, chaindb_at_genesis, # see note below on how this is used fork_choice_scoring, forking_descriptor, forking_asymmetry, genesis_state, genesis_block, config, ): """ Given some blocks and some attestations, can we score them correctly? """ chain_db = chaindb_at_genesis root_block = chain_db.get_canonical_head(SignedBeaconBlock) some_epoch = 3 some_slot_offset = 10 state = genesis_state.mset( "slot", compute_start_slot_at_epoch(some_epoch, config.SLOTS_PER_EPOCH) + some_slot_offset, "current_justified_checkpoint", Checkpoint.create(epoch=some_epoch, root=root_block.message.hash_tree_root), ) assert some_epoch >= state.current_justified_checkpoint.epoch # NOTE: the attestations have to be aligned to the blocks which start from ``base_slot``. base_slot = compute_start_slot_at_epoch(some_epoch, config.SLOTS_PER_EPOCH) + 1 block_tree = _build_block_tree( sample_beacon_block_params, root_block, base_slot, forking_descriptor, forking_asymmetry, config, ) slot_count = len(forking_descriptor) committee_sampling_fraction = 1 committees_by_slot = tuple( _get_committees(state, base_slot + slot_offset, config, committee_sampling_fraction) for slot_offset in range(slot_count)) _attach_committees_to_block_tree(state, block_tree, committees_by_slot, config, forking_asymmetry) _attach_attestations_to_block_tree_with_committees(block_tree, config) context = Context.from_genesis(genesis_state, genesis_block) store = Store(chain_db, SignedBeaconBlock, config, context) score_index = _build_score_index_from_decorated_block_tree( block_tree, store, state, config) for block in _iter_block_tree_by_block(block_tree): # NOTE: we use the ``fork_choice_scoring`` fixture, it doesn't matter for this test chain_db.persist_block(block, SignedBeaconBlock, fork_choice_scoring) for block in _iter_block_tree_by_block(block_tree): score = store.scoring(block) expected_score = score_index[block.message.hash_tree_root] assert score == expected_score
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
def test_get_attestation_deltas( genesis_state, config, slots_per_epoch, target_committee_size, max_committees_per_slot, min_attestation_inclusion_delay, inactivity_penalty_quotient, finalized_epoch, current_slot, sample_pending_attestation_record_params, sample_attestation_data_params, ): state = genesis_state.mset( "slot", current_slot, "finalized_checkpoint", Checkpoint.create(epoch=finalized_epoch), ) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) has_inactivity_penalty = (previous_epoch - finalized_epoch > config.MIN_EPOCHS_TO_INACTIVITY_PENALTY) indices_to_check = set() prev_epoch_attestations = tuple() for committee, committee_index, slot in iterate_committees_at_epoch( state, previous_epoch, config): participants_bitfield = get_empty_bitfield(len(committee)) for i, index in enumerate(committee): indices_to_check.add(index) participants_bitfield = set_voted(participants_bitfield, i) prev_epoch_attestations += (PendingAttestation.create( **sample_pending_attestation_record_params).mset( "aggregation_bits", participants_bitfield, "inclusion_delay", min_attestation_inclusion_delay, "proposer_index", get_beacon_proposer_index(state.set("slot", slot), CommitteeConfig(config)), "data", AttestationData.create(**sample_attestation_data_params).mset( "slot", slot, "index", committee_index, "beacon_block_root", get_block_root_at_slot(state, slot, config.SLOTS_PER_HISTORICAL_ROOT), "target", Checkpoint.create( epoch=previous_epoch, root=get_block_root( state, previous_epoch, config.SLOTS_PER_EPOCH, config.SLOTS_PER_HISTORICAL_ROOT, ), ), ), ), ) state = state.set("previous_epoch_attestations", prev_epoch_attestations) rewards_received, penalties_received = get_attestation_deltas( state, config) if has_inactivity_penalty: assert sum(penalties_received) > 0 else: assert sum(penalties_received) == 0 assert all(reward > 0 for reward in rewards_received)