예제 #1
0
def iters_from_block(
    constants,
    reward_chain_block: Union[RewardChainBlock, RewardChainBlockUnfinished],
    sub_slot_iters: uint64,
    difficulty: uint64,
) -> Tuple[uint64, uint64]:
    if reward_chain_block.challenge_chain_sp_vdf is None:
        assert reward_chain_block.signage_point_index == 0
        cc_sp: bytes32 = reward_chain_block.pos_ss_cc_challenge_hash
    else:
        cc_sp = reward_chain_block.challenge_chain_sp_vdf.output.get_hash()

    quality_string: Optional[
        bytes32] = reward_chain_block.proof_of_space.verify_and_get_quality_string(
            constants,
            reward_chain_block.pos_ss_cc_challenge_hash,
            cc_sp,
        )
    assert quality_string is not None

    required_iters: uint64 = calculate_iterations_quality(
        constants.DIFFICULTY_CONSTANT_FACTOR,
        quality_string,
        reward_chain_block.proof_of_space.size,
        difficulty,
        cc_sp,
    )
    return (
        calculate_sp_iters(constants, sub_slot_iters,
                           reward_chain_block.signage_point_index),
        calculate_ip_iters(
            constants,
            sub_slot_iters,
            reward_chain_block.signage_point_index,
            required_iters,
        ),
    )
예제 #2
0
    async def _check_for_new_ip(self, iter_to_look_for: uint64):
        if len(self.unfinished_blocks) == 0:
            return None
        infusion_iters = [
            iteration for iteration, t in self.iteration_to_proof_type.items() if t == IterationType.INFUSION_POINT
        ]
        for iteration in infusion_iters:
            if iteration != iter_to_look_for:
                continue
            proofs_with_iter = [
                (chain, info, proof)
                for chain, info, proof, label in self.proofs_finished
                if info.number_of_iterations == iteration and label == self.num_resets
            ]
            if self.last_state.get_challenge(Chain.INFUSED_CHALLENGE_CHAIN) is not None:
                chain_count = 3
            else:
                chain_count = 2
            if len(proofs_with_iter) == chain_count:
                block = None
                ip_iters = None
                for unfinished_block in self.unfinished_blocks:
                    try:
                        _, ip_iters = iters_from_block(
                            self.constants,
                            unfinished_block.reward_chain_block,
                            self.last_state.get_sub_slot_iters(),
                            self.last_state.get_difficulty(),
                        )
                    except Exception as e:
                        log.error(f"Error {e}")
                        continue
                    if ip_iters - self.last_state.get_last_ip() == iteration:
                        block = unfinished_block
                        break
                assert ip_iters is not None
                if block is not None:
                    ip_total_iters = self.last_state.get_total_iters() + iteration
                    challenge = block.reward_chain_block.get_hash()
                    icc_info: Optional[VDFInfo] = None
                    icc_proof: Optional[VDFProof] = None
                    cc_info: Optional[VDFInfo] = None
                    cc_proof: Optional[VDFProof] = None
                    rc_info: Optional[VDFInfo] = None
                    rc_proof: Optional[VDFProof] = None
                    for chain, info, proof in proofs_with_iter:
                        if chain == Chain.CHALLENGE_CHAIN:
                            cc_info = info
                            cc_proof = proof
                        if chain == Chain.REWARD_CHAIN:
                            rc_info = info
                            rc_proof = proof
                        if chain == Chain.INFUSED_CHALLENGE_CHAIN:
                            icc_info = info
                            icc_proof = proof
                    if cc_info is None or cc_proof is None or rc_info is None or rc_proof is None:
                        log.error(f"Insufficient VDF proofs for infusion point ch: {challenge} iterations:{iteration}")
                        return None

                    rc_challenge = self.last_state.get_challenge(Chain.REWARD_CHAIN)
                    if rc_info.challenge != rc_challenge:
                        assert rc_challenge is not None
                        log.warning(
                            f"Do not have correct challenge {rc_challenge.hex()} "
                            f"has {rc_info.challenge}, partial hash {block.reward_chain_block.get_hash()}"
                        )
                        # This proof is on an outdated challenge, so don't use it
                        continue

                    self.iters_finished.add(iter_to_look_for)
                    self.last_active_time = time.time()
                    log.debug(f"Generated infusion point for challenge: {challenge} iterations: {iteration}.")

                    overflow = is_overflow_block(self.constants, block.reward_chain_block.signage_point_index)

                    if not self.last_state.can_infuse_block(overflow):
                        log.warning("Too many blocks, or overflow in new epoch, cannot infuse, discarding")
                        return None

                    cc_info = dataclasses.replace(cc_info, number_of_iterations=ip_iters)
                    response = timelord_protocol.NewInfusionPointVDF(
                        challenge,
                        cc_info,
                        cc_proof,
                        rc_info,
                        rc_proof,
                        icc_info,
                        icc_proof,
                    )
                    msg = make_msg(ProtocolMessageTypes.new_infusion_point_vdf, response)
                    if self.server is not None:
                        await self.server.send_to_all([msg], NodeType.FULL_NODE)

                    self.proofs_finished = self._clear_proof_list(iteration)

                    if (
                        self.last_state.get_last_block_total_iters() is None
                        and not self.last_state.state_type == StateType.FIRST_SUB_SLOT
                    ):
                        # We don't know when the last block was, so we can't make peaks
                        return None

                    sp_total_iters = (
                        ip_total_iters
                        - ip_iters
                        + calculate_sp_iters(
                            self.constants,
                            block.sub_slot_iters,
                            block.reward_chain_block.signage_point_index,
                        )
                        - (block.sub_slot_iters if overflow else 0)
                    )
                    if self.last_state.state_type == StateType.FIRST_SUB_SLOT:
                        is_transaction_block = True
                        height: uint32 = uint32(0)
                    else:
                        last_block_ti = self.last_state.get_last_block_total_iters()
                        assert last_block_ti is not None
                        is_transaction_block = last_block_ti < sp_total_iters
                        height = uint32(self.last_state.get_height() + 1)

                    if height < 5:
                        # Don't directly update our state for the first few blocks, because we cannot validate
                        # whether the pre-farm is correct
                        return None

                    new_reward_chain_block = RewardChainBlock(
                        uint128(self.last_state.get_weight() + block.difficulty),
                        height,
                        uint128(ip_total_iters),
                        block.reward_chain_block.signage_point_index,
                        block.reward_chain_block.pos_ss_cc_challenge_hash,
                        block.reward_chain_block.proof_of_space,
                        block.reward_chain_block.challenge_chain_sp_vdf,
                        block.reward_chain_block.challenge_chain_sp_signature,
                        cc_info,
                        block.reward_chain_block.reward_chain_sp_vdf,
                        block.reward_chain_block.reward_chain_sp_signature,
                        rc_info,
                        icc_info,
                        is_transaction_block,
                    )
                    if self.last_state.state_type == StateType.FIRST_SUB_SLOT:
                        # Genesis
                        new_deficit = self.constants.MIN_BLOCKS_PER_CHALLENGE_BLOCK - 1
                    elif overflow and self.last_state.deficit == self.constants.MIN_BLOCKS_PER_CHALLENGE_BLOCK:
                        if self.last_state.peak is not None:
                            assert self.last_state.subslot_end is None
                            # This means the previous block is also an overflow block, and did not manage
                            # to lower the deficit, therefore we cannot lower it either. (new slot)
                            new_deficit = self.constants.MIN_BLOCKS_PER_CHALLENGE_BLOCK
                        else:
                            # This means we are the first infusion in this sub-slot. This may be a new slot or not.
                            assert self.last_state.subslot_end is not None
                            if self.last_state.subslot_end.infused_challenge_chain is None:
                                # There is no ICC, which means we are not finishing a slot. We can reduce the deficit.
                                new_deficit = self.constants.MIN_BLOCKS_PER_CHALLENGE_BLOCK - 1
                            else:
                                # There is an ICC, which means we are finishing a slot. Different slot, so can't change
                                # the deficit
                                new_deficit = self.constants.MIN_BLOCKS_PER_CHALLENGE_BLOCK
                    else:
                        new_deficit = max(self.last_state.deficit - 1, 0)

                    if new_deficit == self.constants.MIN_BLOCKS_PER_CHALLENGE_BLOCK - 1:
                        last_csb_or_eos = ip_total_iters
                    else:
                        last_csb_or_eos = self.last_state.last_challenge_sb_or_eos_total_iters

                    if self.last_state.just_infused_sub_epoch_summary():
                        new_sub_epoch_summary = None
                        passed_ses_height_but_not_yet_included = False
                    else:
                        new_sub_epoch_summary = block.sub_epoch_summary
                        if new_reward_chain_block.height % self.constants.SUB_EPOCH_BLOCKS == 0:
                            passed_ses_height_but_not_yet_included = True
                        else:
                            passed_ses_height_but_not_yet_included = (
                                self.last_state.get_passed_ses_height_but_not_yet_included()
                            )

                    self.new_peak = timelord_protocol.NewPeakTimelord(
                        new_reward_chain_block,
                        block.difficulty,
                        uint8(new_deficit),
                        block.sub_slot_iters,
                        new_sub_epoch_summary,
                        self.last_state.reward_challenge_cache,
                        uint128(last_csb_or_eos),
                        passed_ses_height_but_not_yet_included,
                    )

                    await self._handle_new_peak()
                    # Break so we alternate between checking SP and IP
                    break
예제 #3
0
 def sp_iters(self, constants: ConsensusConstants) -> uint64:
     return calculate_sp_iters(constants, self.sub_slot_iters,
                               self.signage_point_index)
예제 #4
0
 def test_calculate_sp_iters(self):
     ssi: uint64 = uint64(100001 * 64 * 4)
     with raises(ValueError):
         calculate_sp_iters(test_constants, ssi, uint8(32))
     calculate_sp_iters(test_constants, ssi, uint8(31))
def next_sub_epoch_summary(
    constants: ConsensusConstants,
    blocks: BlockchainInterface,
    required_iters: uint64,
    block: Union[UnfinishedBlock, FullBlock],
    can_finish_soon: bool = False,
) -> Optional[SubEpochSummary]:
    """
    Returns the sub-epoch summary that can be included in the block after block. If it should include one. Block
    must be eligible to be the last block in the epoch. If not, returns None. Assumes that there is a new slot
    ending after block.

    Args:
        constants: consensus constants being used for this chain
        blocks: interface to cached SBR
        required_iters: required iters of the proof of space in block
        block: the (potentially) last block in the new epoch
        can_finish_soon: this is useful when sending SES to timelords. We might not be able to finish it, but we will
            soon (within MAX_SUB_SLOT_BLOCKS)

    Returns:
        object: the new sub-epoch summary
    """
    signage_point_index = block.reward_chain_block.signage_point_index
    prev_b: Optional[BlockRecord] = blocks.try_block_record(
        block.prev_header_hash)
    if prev_b is None or prev_b.height == 0:
        return None

    if len(block.finished_sub_slots) > 0 and block.finished_sub_slots[
            0].challenge_chain.new_difficulty is not None:
        # We just included a sub-epoch summary
        return None

    assert prev_b is not None
    # This is the ssi of the current block

    sub_slot_iters = get_next_sub_slot_iters_and_difficulty(
        constants,
        len(block.finished_sub_slots) > 0, prev_b, blocks)[0]
    overflow = is_overflow_block(constants, signage_point_index)

    if (len(block.finished_sub_slots) > 0 and
            block.finished_sub_slots[0].challenge_chain.subepoch_summary_hash
            is not None):
        return None

    if can_finish_soon:
        deficit: uint8 = uint8(
            0)  # Assume that our deficit will go to zero soon
        can_finish_se = True
        if height_can_be_first_in_epoch(constants, uint32(prev_b.height + 2)):
            can_finish_epoch = True
            if (prev_b.height + 2) % constants.SUB_EPOCH_BLOCKS > 1:
                curr: BlockRecord = prev_b
                while curr.height % constants.SUB_EPOCH_BLOCKS > 0:
                    if (curr.sub_epoch_summary_included is not None
                            and curr.sub_epoch_summary_included.new_difficulty
                            is not None):
                        can_finish_epoch = False
                    curr = blocks.block_record(curr.prev_hash)

                if (curr.sub_epoch_summary_included is not None
                        and curr.sub_epoch_summary_included.new_difficulty
                        is not None):
                    can_finish_epoch = False
        elif height_can_be_first_in_epoch(
                constants,
                uint32(prev_b.height + constants.MAX_SUB_SLOT_BLOCKS + 2)):
            can_finish_epoch = True
        else:
            can_finish_epoch = False
    else:
        deficit = calculate_deficit(
            constants,
            uint32(prev_b.height + 1),
            prev_b,
            overflow,
            len(block.finished_sub_slots),
        )
        can_finish_se, can_finish_epoch = can_finish_sub_and_full_epoch(
            constants,
            blocks,
            uint32(prev_b.height + 1),
            prev_b.header_hash if prev_b is not None else None,
            deficit,
            False,
        )

    # can't finish se, no summary
    if not can_finish_se:
        return None

    next_difficulty = None
    next_sub_slot_iters = None

    # if can finish epoch, new difficulty and ssi
    if can_finish_epoch:
        sp_iters = calculate_sp_iters(constants, sub_slot_iters,
                                      signage_point_index)
        ip_iters = calculate_ip_iters(constants, sub_slot_iters,
                                      signage_point_index, required_iters)

        next_difficulty = _get_next_difficulty(
            constants,
            blocks,
            block.prev_header_hash,
            uint32(prev_b.height + 1),
            uint64(prev_b.weight -
                   blocks.block_record(prev_b.prev_hash).weight),
            deficit,
            False,  # Already checked above
            True,
            uint128(block.total_iters - ip_iters + sp_iters -
                    (sub_slot_iters if overflow else 0)),
            True,
        )
        next_sub_slot_iters = _get_next_sub_slot_iters(
            constants,
            blocks,
            block.prev_header_hash,
            uint32(prev_b.height + 1),
            sub_slot_iters,
            deficit,
            False,  # Already checked above
            True,
            uint128(block.total_iters - ip_iters + sp_iters -
                    (sub_slot_iters if overflow else 0)),
            True,
        )

    return make_sub_epoch_summary(
        constants,
        blocks,
        uint32(prev_b.height + 2),
        prev_b,
        next_difficulty,
        next_sub_slot_iters,
    )