Ejemplo n.º 1
0
def lowest_common_ancestor(store, old_head,
                           new_head) -> Optional[specs.BeaconBlock]:
    # in most cases, old_head <- new_head
    # we sort of (loosely) optimise for this

    new_head_ancestors = [new_head]
    current_block = store.blocks[new_head]
    keep_searching = True
    while keep_searching:
        parent_root = current_block.parent_root
        parent_block = store.blocks[parent_root]
        if parent_root == old_head:
            return store.blocks[old_head]
        elif parent_block.slot == 0 or \
        specs.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) > specs.compute_epoch_at_slot(parent_block.slot):
            keep_searching = False
        else:
            new_head_ancestors += [parent_root]
            current_block = parent_block

    # At this point, old_head wasn't an ancestor to new_head
    # We need to find old_head's ancestors
    current_block = store.blocks[old_head]
    keep_searching = True
    while keep_searching:
        parent_root = current_block.parent_root
        parent_block = store.blocks[parent_root]
        if parent_root in new_head_ancestors:
            return parent_block
        elif parent_root == Bytes32() or \
        specs.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) < specs.compute_epoch_at_slot(parent_block.slot):
            return None
Ejemplo n.º 2
0
    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()
Ejemplo n.º 3
0
    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