Esempio n. 1
0
async def test_validator_propose_block_fails(caplog, event_loop, event_bus):
    alice, bob = await get_linked_validators(event_loop=event_loop,
                                             event_bus=event_bus)
    state_machine = alice.chain.get_state_machine()
    state = state_machine.state
    slot = state.slot + 1

    proposer_index = _get_proposer_index(
        state=state,
        slot=slot,
        config=state_machine.config,
    )
    # select the wrong validator as proposer
    v: Validator = None
    if proposer_index == alice.validator_index:
        v = bob
    else:
        v = alice
    head = v.chain.get_canonical_head()
    # test: if a non-proposer validator proposes a block, the block validation should fail.
    with pytest.raises(ValidationError):
        v.propose_block(
            slot=slot,
            state=state,
            state_machine=state_machine,
            head_block=head,
        )
Esempio n. 2
0
 async def propose_or_skip_block(self, slot: Slot,
                                 is_second_tick: bool) -> None:
     head = self.chain.get_canonical_head()
     state_machine = self.chain.get_state_machine()
     state = state_machine.state
     self.logger.debug(
         bold_green(
             f"head: slot={head.slot}, state root={head.state_root.hex()}"))
     proposer_index = _get_proposer_index(
         state,
         slot,
         state_machine.config,
     )
     # Since it's expected to tick twice in one slot, `latest_proposed_epoch` is used to prevent
     # proposing twice in the same slot.
     has_proposed = slot_to_epoch(
         slot, self.slots_per_epoch) <= self.latest_proposed_epoch
     if not has_proposed and proposer_index in self.validator_privkeys:
         self.propose_block(
             proposer_index=proposer_index,
             slot=slot,
             state=state,
             state_machine=state_machine,
             head_block=head,
         )
         self.latest_proposed_epoch = slot_to_epoch(slot,
                                                    self.slots_per_epoch)
     # skip the block if it's second half of the slot and we are not proposing
     elif is_second_tick and proposer_index not in self.validator_privkeys:
         self.skip_block(
             slot=slot,
             state=state,
             state_machine=state_machine,
         )
Esempio n. 3
0
 async def new_slot(self, slot: Slot) -> None:
     head = self.chain.get_canonical_head()
     state_machine = self.chain.get_state_machine()
     state = state_machine.state
     self.logger.debug(
         bold_green(
             f"head: slot={head.slot}, state root={head.state_root}"))
     proposer_index = _get_proposer_index(
         state,
         slot,
         state_machine.config,
     )
     if self.validator_index == proposer_index:
         self.propose_block(
             slot=slot,
             state=state,
             state_machine=state_machine,
             head_block=head,
         )
     else:
         self.skip_block(
             slot=slot,
             state=state,
             state_machine=state_machine,
         )
Esempio n. 4
0
async def test_validator_include_ready_attestations(event_loop, event_bus, monkeypatch):
    # Alice controls all validators
    alice_indices = list(range(8))
    alice = await get_validator(event_loop=event_loop, event_bus=event_bus, indices=alice_indices)
    state_machine = alice.chain.get_state_machine()
    state = state_machine.state

    attesting_slot = state.slot + 1
    attestations = await alice.attest(attesting_slot)

    # Mock `get_ready_attestations_fn` so it returns the attestation alice
    # attested to.
    def get_ready_attestations_fn(slog):
        return attestations
    monkeypatch.setattr(alice, 'get_ready_attestations', get_ready_attestations_fn)

    proposing_slot = attesting_slot + XIAO_LONG_BAO_CONFIG.MIN_ATTESTATION_INCLUSION_DELAY
    proposer_index = _get_proposer_index(
        state,
        proposing_slot,
        state_machine.config,
    )

    head = alice.chain.get_canonical_head()
    block = alice.propose_block(
        proposer_index=proposer_index,
        slot=proposing_slot,
        state=state,
        state_machine=state_machine,
        head_block=head,
    )

    # Check that attestation is included in the proposed block.
    assert attestations[0] in block.body.attestations
Esempio n. 5
0
 async def handle_first_tick(self, slot: Slot) -> None:
     head = self.chain.get_canonical_head()
     state_machine = self.chain.get_state_machine()
     state = state_machine.state
     self.logger.debug(
         # Align with debug log below
         bold_green("Head       epoch=%s slot=%s state_root=%s"),
         state.current_epoch(self.slots_per_epoch),
         head.slot,
         encode_hex(head.state_root),
     )
     self.logger.debug(
         bold_green("Justified  epoch=%s root=%s  (current)"),
         state.current_justified_epoch,
         encode_hex(state.current_justified_root),
     )
     self.logger.debug(
         bold_green("Justified  epoch=%s root=%s  (previous)"),
         state.previous_justified_epoch,
         encode_hex(state.previous_justified_root),
     )
     self.logger.debug(
         bold_green("Finalized  epoch=%s root=%s"),
         state.finalized_epoch,
         encode_hex(state.finalized_root),
     )
     self.logger.debug(
         bold_green("current_epoch_attestations  %s"),
         state.current_epoch_attestations,
     )
     self.logger.debug(
         bold_green("previous_epoch_attestations %s"),
         state.previous_epoch_attestations,
     )
     proposer_index = _get_proposer_index(
         state,
         slot,
         state_machine.config,
     )
     # `latest_proposed_epoch` is used to prevent validator from erraneously proposing twice
     # in the same epoch due to service crashing.
     epoch = slot_to_epoch(slot, self.slots_per_epoch)
     if proposer_index in self.validator_privkeys:
         has_proposed = epoch <= self.latest_proposed_epoch[proposer_index]
         if not has_proposed:
             self.propose_block(
                 proposer_index=proposer_index,
                 slot=slot,
                 state=state,
                 state_machine=state_machine,
                 head_block=head,
             )
             self.latest_proposed_epoch[proposer_index] = epoch
Esempio n. 6
0
def _get_slot_with_validator_selected(is_desired_proposer_index, start_slot, state, state_machine):
    slot = start_slot
    num_trials = 1000
    while True:
        if (slot - start_slot) > num_trials:
            raise Exception("Failed to find a slot where we have validators selected as a proposer")
        proposer_index = _get_proposer_index(
            state,
            slot,
            state_machine.config,
        )
        if is_desired_proposer_index(proposer_index):
            return slot, proposer_index
        slot += 1
Esempio n. 7
0
 async def handle_first_tick(self, slot: Slot) -> None:
     head = self.chain.get_canonical_head()
     state_machine = self.chain.get_state_machine()
     state = self.chain.get_head_state()
     self.logger.debug(
         bold_green(
             "status at slot %s in epoch %s: state_root %s, finalized_checkpoint %s"
         ),
         state.slot,
         state.current_epoch(self.slots_per_epoch),
         humanize_hash(head.state_root),
         state.finalized_checkpoint,
     )
     self.logger.debug(
         ("status at slot %s in epoch %s:"
          " previous_justified_checkpoint %s, current_justified_checkpoint %s"
          ),
         state.slot,
         state.current_epoch(self.slots_per_epoch),
         state.previous_justified_checkpoint,
         state.current_justified_checkpoint,
     )
     self.logger.debug(
         ("status at slot %s in epoch %s:"
          " previous_epoch_attestations %s, current_epoch_attestations %s"),
         state.slot,
         state.current_epoch(self.slots_per_epoch),
         state.previous_epoch_attestations,
         state.current_epoch_attestations,
     )
     proposer_index = _get_proposer_index(
         state.copy(slot=slot, ),
         state_machine.config,
     )
     # `latest_proposed_epoch` is used to prevent validator from erraneously proposing twice
     # in the same epoch due to service crashing.
     epoch = compute_epoch_of_slot(slot, self.slots_per_epoch)
     if proposer_index in self.validator_privkeys:
         has_proposed = epoch <= self.latest_proposed_epoch[proposer_index]
         if not has_proposed:
             await self.propose_block(
                 proposer_index=proposer_index,
                 slot=slot,
                 state=state,
                 state_machine=state_machine,
                 head_block=head,
             )
             self.latest_proposed_epoch[proposer_index] = epoch
Esempio n. 8
0
async def test_validator_new_slot(caplog, event_loop, event_bus, monkeypatch):
    caplog.set_level(logging.DEBUG)
    alice = await get_validator(event_loop=event_loop,
                                event_bus=event_bus,
                                index=0)
    state_machine = alice.chain.get_state_machine()
    state = state_machine.state
    new_slot = state.slot + 1
    # test: `new_slot` should call `propose_block` if the validator get selected,
    #   else calls `skip_block`.
    index = _get_proposer_index(
        state,
        new_slot,
        state_machine.config,
    )

    is_proposing = True

    def propose_block(slot, state, state_machine, head_block):
        nonlocal is_proposing
        is_proposing = True

    def skip_block(slot, state, state_machine):
        nonlocal is_proposing
        is_proposing = False

    monkeypatch.setattr(alice, 'propose_block', propose_block)
    monkeypatch.setattr(alice, 'skip_block', skip_block)

    await alice.new_slot(new_slot)

    # test: either `propose_block` or `skip_block` should be called.
    assert is_proposing is not None
    if alice.validator_index == index:
        assert is_proposing
    else:
        assert not is_proposing