def update_time(self, frequency=frequency) -> None: # Moving validators' clocks by one step # To keep it simple, we assume frequency is a power of ten (see `assert` above) self.data.time_ms = self.data.time_ms + int(1000 / frequency) if self.data.time_ms % 1000 == 0: # The store is updated each second in the specs specs.on_tick(self.store, self.store.time + 1) # If a new slot starts, we update if specs.get_current_slot(self.store) != self.data.slot: self.update_data()
def __init__(self, state, validator_index): # Validator constructor # We preload a bunch of things, to be updated later on as needed # The validator is initialised from some base state, and given a `validator_index` self.validator_index = validator_index self.store = specs.get_forkchoice_store(state) self.history = [] self.data = ValidatorData() self.data.time_ms = self.store.time * 1000 self.data.recorded_attestations = [] self.data.slot = specs.get_current_slot(self.store) self.data.current_epoch = specs.compute_epoch_at_slot(self.data.slot) self.data.head_root = self.get_head() current_state = state.copy() if current_state.slot < self.data.slot: specs.process_slots(current_state, self.data.slot) self.update_attester(current_state, self.data.current_epoch) self.update_proposer(current_state) self.update_data()
def update_data(self) -> None: # The head may change if we recorded a new block/new attestation # Attester/proposer responsibilities may change if head changes *and* # canonical chain changes to further back from start current epoch # # ---x------ # \ x is fork point # ----- # # In the following attester = attester responsibilities for current epoch # proposer = proposer responsibilities for current epoch # # - If x after current epoch change (---|--x , | = start current epoch), proposer and attester don't change # - If x between start of previous epoch and start of current epoch (--||--x---|-- , || = start previous epoch) # proposer changes but not attester # - If x before start of previous epoch (--x--||-----|----) both proposer and attester change slot = specs.get_current_slot(self.store) new_slot = self.data.slot != slot # Current epoch in validator view current_epoch = specs.compute_epoch_at_slot(slot) self.update_attest_move() self.update_propose_move() # Did the validator receive a block in this slot? received_block = len([ block for block_root, block in self.store.blocks.items() if block.slot == slot ]) > 0 if not new_slot: # It's not a new slot, we are here because a new block/attestation was received # Getting the current state, fast-forwarding from the head head_root = self.get_head() if self.data.head_root != head_root: lca = lowest_common_ancestor(self.store, self.data.head_root, head_root) lca_epoch = specs.compute_epoch_at_slot(lca.slot) if lca_epoch == current_epoch: # do nothing pass else: current_state = self.process_to_slot(head_root, slot) if lca_epoch == current_epoch - 1: self.update_proposer(current_state) else: self.update_proposer(current_state) self.update_attester(current_state, current_epoch) self.data.head_root = head_root else: # It's a new slot. We should update our proposer/attester duties # if it's also a new epoch. If not we do nothing. if self.data.current_epoch != current_epoch: current_state = self.process_to_slot(self.data.head_root, slot) # We need to check our proposer role for this new epoch self.update_proposer(current_state) # We need to check our attester role for this new epoch self.update_attester(current_state, current_epoch) self.data.slot = slot self.data.current_epoch = current_epoch self.data.received_block = received_block