Exemple #1
0
 def test_truncate_to_significant_bits(self):
     a = -0b001101
     assert truncate_to_significant_bits(a, 2) == -0b1100
     a = -0b001111
     assert truncate_to_significant_bits(a, 2) == -0b1100
     a = 0b1111
     assert truncate_to_significant_bits(a, 2) == 0b1100
     a = 0b1000000111
     assert truncate_to_significant_bits(a, 8) == 0b1000000100
     a = 0b1000000111
     assert truncate_to_significant_bits(a, 0) == 0b0
     a = 0b1000000111
     assert truncate_to_significant_bits(a, 500) == a
     a = 0b10101
     assert truncate_to_significant_bits(a, 5) == a
     a = 0b10101
     assert truncate_to_significant_bits(a, 4) == 0b10100
    def get_min_iters(self, block_record: BlockRecord) -> uint64:
        """
        Returns the min_iters value, which is calculated every epoch. This requires looking
        up the epoch barrier blocks, and taking 10% of the total iterations in the previous
        epoch.
        """
        curr = block_record
        if (curr.height < self.constants["DIFFICULTY_EPOCH"] +
                self.constants["DIFFICULTY_DELAY"]):
            return self.constants["MIN_ITERS_STARTING"]
        if (curr.height % self.constants["DIFFICULTY_EPOCH"] <
                self.constants["DIFFICULTY_DELAY"]):
            # First few blocks of epoch (using old difficulty and min_iters)
            height2 = (curr.height -
                       (curr.height % self.constants["DIFFICULTY_EPOCH"]) -
                       self.constants["DIFFICULTY_EPOCH"] - 1)
        else:
            # The rest of the blocks of epoch (using new difficulty and min iters)
            height2 = (curr.height -
                       (curr.height % self.constants["DIFFICULTY_EPOCH"]) - 1)
        height1 = height2 - self.constants["DIFFICULTY_EPOCH"]
        assert height2 > 0

        iters1: Optional[uint64] = uint64(0)
        iters2: Optional[uint64] = None
        while curr.height > height1 and curr.height > 0:
            if curr.height == height2:
                iters2 = curr.total_iters
            curr = self.block_records[curr.prev_header_hash]
        if height1 > -1:  # For height of -1, total iters is 0
            iters1 = curr.total_iters
        assert iters1 is not None
        assert iters2 is not None
        min_iters_precise = uint64(
            (iters2 - iters1) // (self.constants["DIFFICULTY_EPOCH"] *
                                  self.constants["MIN_ITERS_PROPORTION"]))
        # Truncates to only 12 bits plus 0s. This prevents grinding attacks.
        return uint64(
            truncate_to_significant_bits(min_iters_precise,
                                         self.constants["SIGNIFICANT_BITS"]))
Exemple #3
0
    def get_consecutive_blocks(
            self,
            test_constants: ConsensusConstants,
            num_blocks: int,
            block_list: List[FullBlock] = [],
            seconds_per_block=None,
            seed: bytes = b"",
            reward_puzzlehash: bytes32 = None,
            transaction_data_at_height: Dict[int, Tuple[Program,
                                                        G2Element]] = None,
            fees: uint64 = uint64(0),
    ) -> List[FullBlock]:
        if transaction_data_at_height is None:
            transaction_data_at_height = {}
        if seconds_per_block is None:
            seconds_per_block = test_constants.BLOCK_TIME_TARGET

        if len(block_list) == 0:
            block_list.append(
                FullBlock.from_bytes(test_constants.GENESIS_BLOCK))
            prev_difficulty = test_constants.DIFFICULTY_STARTING
            curr_difficulty = prev_difficulty
            curr_min_iters = test_constants.MIN_ITERS_STARTING
        elif len(block_list) < (test_constants.DIFFICULTY_EPOCH +
                                test_constants.DIFFICULTY_DELAY):
            # First epoch (+delay), so just get first difficulty
            prev_difficulty = block_list[0].weight
            curr_difficulty = block_list[0].weight
            assert test_constants.DIFFICULTY_STARTING == prev_difficulty
            curr_min_iters = test_constants.MIN_ITERS_STARTING
        else:
            curr_difficulty = block_list[-1].weight - block_list[-2].weight
            prev_difficulty = (
                block_list[-1 - test_constants.DIFFICULTY_EPOCH].weight -
                block_list[-2 - test_constants.DIFFICULTY_EPOCH].weight)
            assert block_list[-1].proof_of_time is not None
            curr_min_iters = calculate_min_iters_from_iterations(
                block_list[-1].proof_of_space,
                curr_difficulty,
                block_list[-1].proof_of_time.number_of_iterations,
                test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG,
            )

        starting_height = block_list[-1].height + 1
        timestamp = block_list[-1].header.data.timestamp
        for next_height in range(starting_height,
                                 starting_height + num_blocks):
            if (next_height > test_constants.DIFFICULTY_EPOCH
                    and next_height % test_constants.DIFFICULTY_EPOCH
                    == test_constants.DIFFICULTY_DELAY):
                # Calculates new difficulty
                height1 = uint64(next_height -
                                 (test_constants.DIFFICULTY_EPOCH +
                                  test_constants.DIFFICULTY_DELAY) - 1)
                height2 = uint64(next_height -
                                 (test_constants.DIFFICULTY_EPOCH) - 1)
                height3 = uint64(next_height -
                                 (test_constants.DIFFICULTY_DELAY) - 1)
                if height1 >= 0:
                    block1 = block_list[height1]
                    iters1 = block1.header.data.total_iters
                    timestamp1 = block1.header.data.timestamp
                else:
                    block1 = block_list[0]
                    timestamp1 = uint64(block1.header.data.timestamp -
                                        test_constants.BLOCK_TIME_TARGET)
                    iters1 = uint64(0)
                timestamp2 = block_list[height2].header.data.timestamp
                timestamp3 = block_list[height3].header.data.timestamp

                block3 = block_list[height3]
                iters3 = block3.header.data.total_iters
                term1 = (test_constants.DIFFICULTY_DELAY * prev_difficulty *
                         (timestamp3 - timestamp2) *
                         test_constants.BLOCK_TIME_TARGET)

                term2 = ((test_constants.DIFFICULTY_WARP_FACTOR - 1) *
                         (test_constants.DIFFICULTY_EPOCH -
                          test_constants.DIFFICULTY_DELAY) * curr_difficulty *
                         (timestamp2 - timestamp1) *
                         test_constants.BLOCK_TIME_TARGET)

                # Round down after the division
                new_difficulty_precise: uint64 = uint64(
                    (term1 + term2) // (test_constants.DIFFICULTY_WARP_FACTOR *
                                        (timestamp3 - timestamp2) *
                                        (timestamp2 - timestamp1)))
                new_difficulty = uint64(
                    truncate_to_significant_bits(
                        new_difficulty_precise,
                        test_constants.SIGNIFICANT_BITS))
                max_diff = uint64(
                    truncate_to_significant_bits(
                        test_constants.DIFFICULTY_FACTOR * curr_difficulty,
                        test_constants.SIGNIFICANT_BITS,
                    ))
                min_diff = uint64(
                    truncate_to_significant_bits(
                        curr_difficulty // test_constants.DIFFICULTY_FACTOR,
                        test_constants.SIGNIFICANT_BITS,
                    ))
                if new_difficulty >= curr_difficulty:
                    new_difficulty = min(
                        new_difficulty,
                        max_diff,
                    )
                else:
                    new_difficulty = max([uint64(1), new_difficulty, min_diff])

                min_iters_precise = uint64(
                    (iters3 - iters1) // (test_constants.DIFFICULTY_EPOCH *
                                          test_constants.MIN_ITERS_PROPORTION))
                curr_min_iters = uint64(
                    truncate_to_significant_bits(
                        min_iters_precise, test_constants.SIGNIFICANT_BITS))
                prev_difficulty = curr_difficulty
                curr_difficulty = new_difficulty
            time_taken = seconds_per_block
            timestamp += time_taken

            transactions: Optional[Program] = None
            aggsig: Optional[G2Element] = None
            if next_height in transaction_data_at_height:
                transactions, aggsig = transaction_data_at_height[next_height]

            update_difficulty = (next_height % test_constants.DIFFICULTY_EPOCH
                                 == test_constants.DIFFICULTY_DELAY)
            block_list.append(
                self.create_next_block(
                    test_constants,
                    block_list[-1],
                    timestamp,
                    update_difficulty,
                    curr_difficulty,
                    curr_min_iters,
                    seed,
                    reward_puzzlehash,
                    transactions,
                    aggsig,
                    fees,
                ))
        return block_list
def _get_next_difficulty(
    constants: ConsensusConstants,
    blocks: BlockchainInterface,
    prev_header_hash: bytes32,
    height: uint32,
    current_difficulty: uint64,
    deficit: uint8,
    block_at_height_included_ses: bool,
    new_slot: bool,
    signage_point_total_iters: uint128,
    skip_epoch_check=False,
) -> uint64:
    """
    Returns the difficulty of the next block that extends onto block.
    Used to calculate the number of iterations. WARNING: assumes that the block at height is not the first block
    in a sub-epoch.

    Args:
        constants: consensus constants being used for this chain
        blocks: dictionary from header hash to SBR of all included SBR
        prev_header_hash: header hash of the previous block
        height: the block height of the block to look at
        deficit: deficit of block at height height
        current_difficulty: difficulty at the infusion point of the block at height
        new_slot: whether or not there is a new slot after height
        signage_point_total_iters: signage point iters of the block at height
        skip_epoch_check: don't check correct epoch
    """
    next_height: uint32 = uint32(height + 1)

    if next_height < constants.EPOCH_BLOCKS:
        # We are in the first epoch
        return uint64(constants.DIFFICULTY_STARTING)

    if not blocks.contains_block(prev_header_hash):
        raise ValueError(f"Header hash {prev_header_hash} not in blocks")

    prev_b: BlockRecord = blocks.block_record(prev_header_hash)

    # If we are in the same slot as previous block, return same difficulty
    if not skip_epoch_check:
        _, can_finish_epoch = can_finish_sub_and_full_epoch(
            constants, blocks, height, prev_header_hash, deficit,
            block_at_height_included_ses)
        if not new_slot or not can_finish_epoch:
            return current_difficulty

    last_block_prev: BlockRecord = _get_second_to_last_transaction_block_in_previous_epoch(
        constants, blocks, prev_b)

    # This gets the last transaction block before this block's signage point. Assuming the block at height height
    # is the last block infused in the epoch: If this block ends up being a
    # transaction block, then last_block_curr will be the second to last tx block in the epoch. If this block
    # is not a transaction block, that means there was exactly one other tx block included in between our signage
    # point and infusion point, and therefore last_block_curr is the second to last as well.
    last_block_curr = prev_b
    while last_block_curr.total_iters > signage_point_total_iters or not last_block_curr.is_transaction_block:
        last_block_curr = blocks.block_record(last_block_curr.prev_hash)

    assert last_block_curr.timestamp is not None
    assert last_block_prev.timestamp is not None
    actual_epoch_time: uint64 = uint64(last_block_curr.timestamp -
                                       last_block_prev.timestamp)

    old_difficulty = uint64(prev_b.weight -
                            blocks.block_record(prev_b.prev_hash).weight)

    # Terms are rearranged so there is only one division.
    new_difficulty_precise = uint64(
        (last_block_curr.weight - last_block_prev.weight) *
        constants.SUB_SLOT_TIME_TARGET //
        (constants.SLOT_BLOCKS_TARGET * actual_epoch_time))

    # Only change by a max factor, to prevent attacks, as in greenpaper, and must be at least 1
    max_diff = uint64(constants.DIFFICULTY_CHANGE_MAX_FACTOR * old_difficulty)
    min_diff = uint64(old_difficulty // constants.DIFFICULTY_CHANGE_MAX_FACTOR)

    if new_difficulty_precise >= old_difficulty:
        new_difficulty_precise = uint64(min(new_difficulty_precise, max_diff))
    else:
        new_difficulty_precise = uint64(
            max([uint64(1), new_difficulty_precise, min_diff]))
    new_difficulty = truncate_to_significant_bits(new_difficulty_precise,
                                                  constants.SIGNIFICANT_BITS)
    assert count_significant_bits(new_difficulty) <= constants.SIGNIFICANT_BITS
    return uint64(new_difficulty)
def _get_next_sub_slot_iters(
    constants: ConsensusConstants,
    blocks: BlockchainInterface,
    prev_header_hash: bytes32,
    height: uint32,
    curr_sub_slot_iters: uint64,
    deficit: uint8,
    block_at_height_included_ses: bool,
    new_slot: bool,
    signage_point_total_iters: uint128,
    skip_epoch_check=False,
) -> uint64:
    """
    Returns the slot iterations required for the next block after the one at height, where new_slot is true
    iff the next block will be in the next slot. WARNING: assumes that the block at height is not the first block
    in a sub-epoch.

    Args:
        constants: consensus constants being used for this chain
        blocks: dictionary from header hash to SBR of all included SBR
        prev_header_hash: header hash of the previous block
        height: the block height of the block to look at
        curr_sub_slot_iters: sub-slot iters at the infusion point of the block at height
        deficit: deficit of block at height height
        new_slot: whether or not there is a new slot after height
        signage_point_total_iters: signage point iters of the block at height
        skip_epoch_check: don't check correct epoch
    """
    next_height: uint32 = uint32(height + 1)

    if next_height < constants.EPOCH_BLOCKS:
        return uint64(constants.SUB_SLOT_ITERS_STARTING)

    if not blocks.contains_block(prev_header_hash):
        raise ValueError(f"Header hash {prev_header_hash} not in blocks")

    prev_b: BlockRecord = blocks.block_record(prev_header_hash)

    # If we are in the same epoch, return same ssi
    if not skip_epoch_check:
        _, can_finish_epoch = can_finish_sub_and_full_epoch(
            constants, blocks, height, prev_header_hash, deficit,
            block_at_height_included_ses)
        if not new_slot or not can_finish_epoch:
            return curr_sub_slot_iters

    last_block_prev: BlockRecord = _get_second_to_last_transaction_block_in_previous_epoch(
        constants, blocks, prev_b)

    # This gets the last transaction block before this block's signage point. Assuming the block at height height
    # is the last block infused in the epoch: If this block ends up being a
    # transaction block, then last_block_curr will be the second to last tx block in the epoch. If this block
    # is not a transaction block, that means there was exactly one other tx block included in between our signage
    # point and infusion point, and therefore last_block_curr is the second to last as well.
    last_block_curr = prev_b
    while last_block_curr.total_iters > signage_point_total_iters or not last_block_curr.is_transaction_block:
        last_block_curr = blocks.block_record(last_block_curr.prev_hash)
    assert last_block_curr.timestamp is not None and last_block_prev.timestamp is not None

    # This is computed as the iterations per second in last epoch, times the target number of seconds per slot
    new_ssi_precise: uint64 = uint64(
        constants.SUB_SLOT_TIME_TARGET *
        (last_block_curr.total_iters - last_block_prev.total_iters) //
        (last_block_curr.timestamp - last_block_prev.timestamp))

    # Only change by a max factor as a sanity check
    max_ssi = uint64(constants.DIFFICULTY_CHANGE_MAX_FACTOR *
                     last_block_curr.sub_slot_iters)
    min_ssi = uint64(last_block_curr.sub_slot_iters //
                     constants.DIFFICULTY_CHANGE_MAX_FACTOR)
    if new_ssi_precise >= last_block_curr.sub_slot_iters:
        new_ssi_precise = uint64(min(new_ssi_precise, max_ssi))
    else:
        new_ssi_precise = uint64(
            max([constants.NUM_SPS_SUB_SLOT, new_ssi_precise, min_ssi]))

    new_ssi = truncate_to_significant_bits(new_ssi_precise,
                                           constants.SIGNIFICANT_BITS)
    new_ssi = uint64(
        new_ssi -
        new_ssi % constants.NUM_SPS_SUB_SLOT)  # Must divide the sub slot
    assert count_significant_bits(new_ssi) <= constants.SIGNIFICANT_BITS
    return new_ssi
Exemple #6
0
def get_next_min_iters(
    constants: Dict,
    headers: Dict[bytes32, Header],
    height_to_hash: Dict[uint32, bytes32],
    block: Union[FullBlock, HeaderBlock],
) -> uint64:
    """
    Returns the VDF speed in iterations per seconds, to be used for the next block. This depends on
    the number of iterations of the last epoch, and changes at the same block as the difficulty.
    """
    next_height: uint32 = uint32(block.height + 1)
    if next_height < constants["DIFFICULTY_EPOCH"]:
        # First epoch has a hardcoded vdf speed
        return constants["MIN_ITERS_STARTING"]

    prev_block_header: Header = headers[block.prev_header_hash]

    proof_of_space = block.proof_of_space
    difficulty = get_next_difficulty(
        constants, headers, height_to_hash, prev_block_header
    )
    iterations = uint64(
        block.header.data.total_iters - prev_block_header.data.total_iters
    )
    prev_min_iters = calculate_min_iters_from_iterations(
        proof_of_space, difficulty, iterations
    )

    if next_height % constants["DIFFICULTY_EPOCH"] != constants["DIFFICULTY_DELAY"]:
        # Not at a point where ips would change, so return the previous ips
        # TODO: cache this for efficiency
        return prev_min_iters

    # min iters (along with difficulty) will change in this block, so we need to calculate the new one.
    # The calculation is (iters_2 - iters_1) // epoch size
    # 1 and 2 correspond to height_1 and height_2, being the last block of the second to last, and last
    # block of the last epochs. Basically, it's total iterations per block on average.

    # Height1 is the last block 2 epochs ago, so we can include the iterations taken for mining first block in epoch
    height1 = uint32(
        next_height - constants["DIFFICULTY_EPOCH"] - constants["DIFFICULTY_DELAY"] - 1
    )
    # Height2 is the last block in the previous epoch
    height2 = uint32(next_height - constants["DIFFICULTY_DELAY"] - 1)

    block1: Optional[Header] = None
    block2: Optional[Header] = None

    # We need to backtrack until we merge with the LCA chain, so we can use the height_to_hash dict.
    # This is important if we are on a fork, or beyond the LCA.
    curr: Optional[Header] = block.header
    assert curr is not None
    while (
        curr.height not in height_to_hash
        or height_to_hash[curr.height] != curr.header_hash
    ):
        if curr.height == height1:
            block1 = curr
        elif curr.height == height2:
            block2 = curr
        curr = headers.get(curr.prev_header_hash, None)
        assert curr is not None

    # Once we are before the fork point (and before the LCA), we can use the height_to_hash map
    if block1 is None and height1 >= 0:
        # height1 could be -1, for the first difficulty calculation
        block1 = headers.get(height_to_hash[height1], None)
    if block2 is None:
        block2 = headers.get(height_to_hash[height2], None)
    assert block2 is not None

    if block1 is not None:
        iters1 = block1.data.total_iters
    else:
        # In the case of height == -1, iters = 0
        iters1 = uint64(0)

    iters2 = block2.data.total_iters

    min_iters_precise = uint64(
        (iters2 - iters1)
        // (constants["DIFFICULTY_EPOCH"] * constants["MIN_ITERS_PROPORTION"])
    )
    min_iters = uint64(
        truncate_to_significant_bits(min_iters_precise, constants["SIGNIFICANT_BITS"])
    )
    assert count_significant_bits(min_iters) <= constants["SIGNIFICANT_BITS"]
    return min_iters
Exemple #7
0
def get_next_difficulty(
    constants: Dict,
    headers: Dict[bytes32, Header],
    height_to_hash: Dict[uint32, bytes32],
    block: Header,
) -> uint64:
    """
    Returns the difficulty of the next block that extends onto block.
    Used to calculate the number of iterations. When changing this, also change the implementation
    in wallet_state_manager.py.
    """

    next_height: uint32 = uint32(block.height + 1)
    if next_height < constants["DIFFICULTY_EPOCH"]:
        # We are in the first epoch
        return uint64(constants["DIFFICULTY_STARTING"])

    # Epochs are diffined as intervals of DIFFICULTY_EPOCH blocks, inclusive and indexed at 0.
    # For example, [0-2047], [2048-4095], etc. The difficulty changes DIFFICULTY_DELAY into the
    # epoch, as opposed to the first block (as in Bitcoin).
    elif next_height % constants["DIFFICULTY_EPOCH"] != constants["DIFFICULTY_DELAY"]:
        # Not at a point where difficulty would change
        prev_block: Header = headers[block.prev_header_hash]
        return uint64(block.weight - prev_block.weight)

    #       old diff                  curr diff       new diff
    # ----------|-----|----------------------|-----|-----...
    #           h1    h2                     h3   i-1
    # Height1 is the last block 2 epochs ago, so we can include the time to mine 1st block in previous epoch
    height1 = uint32(
        next_height - constants["DIFFICULTY_EPOCH"] - constants["DIFFICULTY_DELAY"] - 1
    )
    # Height2 is the DIFFICULTY DELAYth block in the previous epoch
    height2 = uint32(next_height - constants["DIFFICULTY_EPOCH"] - 1)
    # Height3 is the last block in the previous epoch
    height3 = uint32(next_height - constants["DIFFICULTY_DELAY"] - 1)

    # h1 to h2 timestamps are mined on previous difficulty, while  and h2 to h3 timestamps are mined on the
    # current difficulty

    block1, block2, block3 = None, None, None

    # We need to backtrack until we merge with the LCA chain, so we can use the height_to_hash dict.
    # This is important if we are on a fork, or beyond the LCA.
    curr: Optional[Header] = block
    assert curr is not None
    while (
        curr.height not in height_to_hash
        or height_to_hash[curr.height] != curr.header_hash
    ):
        if curr.height == height1:
            block1 = curr
        elif curr.height == height2:
            block2 = curr
        elif curr.height == height3:
            block3 = curr
        curr = headers.get(curr.prev_header_hash, None)
        assert curr is not None

    # Once we are before the fork point (and before the LCA), we can use the height_to_hash map
    if not block1 and height1 >= 0:
        # height1 could be -1, for the first difficulty calculation
        block1 = headers[height_to_hash[height1]]
    if not block2:
        block2 = headers[height_to_hash[height2]]
    if not block3:
        block3 = headers[height_to_hash[height3]]
    assert block2 is not None and block3 is not None

    # Current difficulty parameter (diff of block h = i - 1)
    Tc = get_next_difficulty(
        constants, headers, height_to_hash, headers[block.prev_header_hash]
    )

    # Previous difficulty parameter (diff of block h = i - 2048 - 1)
    Tp = get_next_difficulty(
        constants, headers, height_to_hash, headers[block2.prev_header_hash]
    )
    if block1:
        timestamp1 = block1.data.timestamp  # i - 512 - 1
    else:
        # In the case of height == -1, there is no timestamp here, so assume the genesis block
        # took constants["BLOCK_TIME_TARGET"] seconds to mine.
        genesis = headers[height_to_hash[uint32(0)]]
        timestamp1 = genesis.data.timestamp - constants["BLOCK_TIME_TARGET"]
    timestamp2 = block2.data.timestamp  # i - 2048 + 512 - 1
    timestamp3 = block3.data.timestamp  # i - 512 - 1

    # Numerator fits in 128 bits, so big int is not necessary
    # We multiply by the denominators here, so we only have one fraction in the end (avoiding floating point)
    term1 = (
        constants["DIFFICULTY_DELAY"]
        * Tp
        * (timestamp3 - timestamp2)
        * constants["BLOCK_TIME_TARGET"]
    )
    term2 = (
        (constants["DIFFICULTY_WARP_FACTOR"] - 1)
        * (constants["DIFFICULTY_EPOCH"] - constants["DIFFICULTY_DELAY"])
        * Tc
        * (timestamp2 - timestamp1)
        * constants["BLOCK_TIME_TARGET"]
    )

    # Round down after the division
    new_difficulty_precise: uint64 = uint64(
        (term1 + term2)
        // (
            constants["DIFFICULTY_WARP_FACTOR"]
            * (timestamp3 - timestamp2)
            * (timestamp2 - timestamp1)
        )
    )
    # Take only DIFFICULTY_SIGNIFICANT_BITS significant bits
    new_difficulty = uint64(
        truncate_to_significant_bits(
            new_difficulty_precise, constants["SIGNIFICANT_BITS"]
        )
    )
    assert count_significant_bits(new_difficulty) <= constants["SIGNIFICANT_BITS"]

    # Only change by a max factor, to prevent attacks, as in greenpaper, and must be at least 1
    max_diff = uint64(
        truncate_to_significant_bits(
            constants["DIFFICULTY_FACTOR"] * Tc, constants["SIGNIFICANT_BITS"],
        )
    )
    min_diff = uint64(
        truncate_to_significant_bits(
            Tc // constants["DIFFICULTY_FACTOR"], constants["SIGNIFICANT_BITS"],
        )
    )
    if new_difficulty >= Tc:
        return min(new_difficulty, max_diff)
    else:
        return max([uint64(1), new_difficulty, min_diff])
    def get_consecutive_blocks(
        self,
        input_constants: Dict,
        num_blocks: int,
        block_list: List[FullBlock] = [],
        seconds_per_block=None,
        seed: bytes = b"",
        reward_puzzlehash: bytes32 = None,
        transaction_data_at_height: Dict[int, Tuple[Program, BLSSignature]] = None,
        fees: uint64 = uint64(0),
    ) -> List[FullBlock]:
        if transaction_data_at_height is None:
            transaction_data_at_height = {}
        test_constants: Dict[str, Any] = constants.copy()
        for key, value in input_constants.items():
            test_constants[key] = value
        if seconds_per_block is None:
            seconds_per_block = test_constants["BLOCK_TIME_TARGET"]

        if len(block_list) == 0:
            if "GENESIS_BLOCK" in test_constants:
                block_list.append(FullBlock.from_bytes(test_constants["GENESIS_BLOCK"]))
            else:
                block_list.append(
                    self.create_genesis_block(test_constants, std_hash(seed), seed)
                )
            prev_difficulty = test_constants["DIFFICULTY_STARTING"]
            curr_difficulty = prev_difficulty
            curr_min_iters = test_constants["MIN_ITERS_STARTING"]
        elif len(block_list) < (
            test_constants["DIFFICULTY_EPOCH"] + test_constants["DIFFICULTY_DELAY"]
        ):
            # First epoch (+delay), so just get first difficulty
            prev_difficulty = block_list[0].weight
            curr_difficulty = block_list[0].weight
            assert test_constants["DIFFICULTY_STARTING"] == prev_difficulty
            curr_min_iters = test_constants["MIN_ITERS_STARTING"]
        else:
            curr_difficulty = block_list[-1].weight - block_list[-2].weight
            prev_difficulty = (
                block_list[-1 - test_constants["DIFFICULTY_EPOCH"]].weight
                - block_list[-2 - test_constants["DIFFICULTY_EPOCH"]].weight
            )
            assert block_list[-1].proof_of_time is not None
            curr_min_iters = calculate_min_iters_from_iterations(
                block_list[-1].proof_of_space,
                curr_difficulty,
                block_list[-1].proof_of_time.number_of_iterations,
            )

        starting_height = block_list[-1].height + 1
        timestamp = block_list[-1].header.data.timestamp
        for next_height in range(starting_height, starting_height + num_blocks):
            if (
                next_height > test_constants["DIFFICULTY_EPOCH"]
                and next_height % test_constants["DIFFICULTY_EPOCH"]
                == test_constants["DIFFICULTY_DELAY"]
            ):
                # Calculates new difficulty
                height1 = uint64(
                    next_height
                    - (
                        test_constants["DIFFICULTY_EPOCH"]
                        + test_constants["DIFFICULTY_DELAY"]
                    )
                    - 1
                )
                height2 = uint64(next_height - (test_constants["DIFFICULTY_EPOCH"]) - 1)
                height3 = uint64(next_height - (test_constants["DIFFICULTY_DELAY"]) - 1)
                if height1 >= 0:
                    block1 = block_list[height1]
                    iters1 = block1.header.data.total_iters
                    timestamp1 = block1.header.data.timestamp
                else:
                    block1 = block_list[0]
                    timestamp1 = (
                        block1.header.data.timestamp
                        - test_constants["BLOCK_TIME_TARGET"]
                    )
                    iters1 = uint64(0)
                timestamp2 = block_list[height2].header.data.timestamp
                timestamp3 = block_list[height3].header.data.timestamp

                block3 = block_list[height3]
                iters3 = block3.header.data.total_iters
                term1 = (
                    test_constants["DIFFICULTY_DELAY"]
                    * prev_difficulty
                    * (timestamp3 - timestamp2)
                    * test_constants["BLOCK_TIME_TARGET"]
                )

                term2 = (
                    (test_constants["DIFFICULTY_WARP_FACTOR"] - 1)
                    * (
                        test_constants["DIFFICULTY_EPOCH"]
                        - test_constants["DIFFICULTY_DELAY"]
                    )
                    * curr_difficulty
                    * (timestamp2 - timestamp1)
                    * test_constants["BLOCK_TIME_TARGET"]
                )

                # Round down after the division
                new_difficulty_precise: uint64 = uint64(
                    (term1 + term2)
                    // (
                        test_constants["DIFFICULTY_WARP_FACTOR"]
                        * (timestamp3 - timestamp2)
                        * (timestamp2 - timestamp1)
                    )
                )
                new_difficulty = uint64(
                    truncate_to_significant_bits(
                        new_difficulty_precise, test_constants["SIGNIFICANT_BITS"]
                    )
                )
                max_diff = uint64(
                    truncate_to_significant_bits(
                        test_constants["DIFFICULTY_FACTOR"] * curr_difficulty,
                        test_constants["SIGNIFICANT_BITS"],
                    )
                )
                min_diff = uint64(
                    truncate_to_significant_bits(
                        curr_difficulty // test_constants["DIFFICULTY_FACTOR"],
                        test_constants["SIGNIFICANT_BITS"],
                    )
                )
                if new_difficulty >= curr_difficulty:
                    new_difficulty = min(new_difficulty, max_diff,)
                else:
                    new_difficulty = max([uint64(1), new_difficulty, min_diff])

                min_iters_precise = uint64(
                    (iters3 - iters1)
                    // (
                        test_constants["DIFFICULTY_EPOCH"]
                        * test_constants["MIN_ITERS_PROPORTION"]
                    )
                )
                curr_min_iters = uint64(
                    truncate_to_significant_bits(
                        min_iters_precise, test_constants["SIGNIFICANT_BITS"]
                    )
                )
                prev_difficulty = curr_difficulty
                curr_difficulty = new_difficulty
            time_taken = seconds_per_block
            timestamp += time_taken

            transactions: Optional[Program] = None
            aggsig: Optional[BLSSignature] = None
            if next_height in transaction_data_at_height:
                transactions, aggsig = transaction_data_at_height[next_height]

            update_difficulty = (
                next_height % test_constants["DIFFICULTY_EPOCH"]
                == test_constants["DIFFICULTY_DELAY"]
            )
            block_list.append(
                self.create_next_block(
                    test_constants,
                    block_list[-1],
                    timestamp,
                    update_difficulty,
                    curr_difficulty,
                    curr_min_iters,
                    seed,
                    reward_puzzlehash,
                    transactions,
                    aggsig,
                    fees,
                )
            )
        return block_list
def get_next_difficulty(
    constants: ConsensusConstants,
    sub_blocks: Dict[bytes32, SubBlockRecord],
    height_to_hash: Dict[uint32, bytes32],
    prev_header_hash: bytes32,
    sub_block_height: uint32,
    current_difficulty: uint64,
    deficit: uint8,
    new_slot: bool,
    signage_point_total_iters: uint128,
    skip_epoch_check=False,
) -> uint64:
    """
    Returns the difficulty of the next sub-block that extends onto sub-block.
    Used to calculate the number of iterations. When changing this, also change the implementation
    in wallet_state_manager.py.

    Args:
        constants: consensus constants being used for this chain
        sub_blocks: dictionary from header hash to SBR of all included SBR
        height_to_hash: sub-block height to header hash map for sub-blocks in peak path
        prev_header_hash: header hash of the previous sub-block
        sub_block_height: the sub-block height of the sub-block to look at
        current_difficulty: difficulty at the infusion point of the sub_block at sub_block_height
        deficit: deficit of the sub_block at sub_block_height
        new_slot: whether or not there is a new slot after sub_block_height
        signage_point_total_iters: signage point iters of the sub_block at sub_block_height
        skip_epoch_check: don't check correct epoch
    """
    next_sub_block_height: uint32 = uint32(sub_block_height + 1)

    if next_sub_block_height < (constants.EPOCH_SUB_BLOCKS -
                                constants.MAX_SUB_SLOT_SUB_BLOCKS):
        # We are in the first epoch
        return uint64(constants.DIFFICULTY_STARTING)

    if prev_header_hash not in sub_blocks:
        raise ValueError(f"Header hash {prev_header_hash} not in sub blocks")

    prev_sb: SubBlockRecord = sub_blocks[prev_header_hash]

    # If we are in the same slot as previous sub-block, return same difficulty
    if not skip_epoch_check:
        _, can_finish_epoch = can_finish_sub_and_full_epoch(
            constants, sub_block_height, deficit, sub_blocks, prev_header_hash,
            False)
        if not new_slot or not can_finish_epoch:
            return current_difficulty

    last_block_prev: SubBlockRecord = _get_last_block_in_previous_epoch(
        constants, height_to_hash, sub_blocks, prev_sb)

    # Ensure we get a block for the last block as well, and that it is before the signage point
    last_block_curr = prev_sb
    while last_block_curr.total_iters > signage_point_total_iters or not last_block_curr.is_block:
        last_block_curr = sub_blocks[last_block_curr.prev_hash]

    assert last_block_curr.timestamp is not None
    assert last_block_prev.timestamp is not None
    actual_epoch_time: uint64 = uint64(last_block_curr.timestamp -
                                       last_block_prev.timestamp)

    old_difficulty = uint64(prev_sb.weight -
                            sub_blocks[prev_sb.prev_hash].weight)

    # Terms are rearranged so there is only one division.
    new_difficulty_precise = (
        (last_block_curr.weight - last_block_prev.weight) *
        constants.SUB_SLOT_TIME_TARGET //
        (constants.SLOT_SUB_BLOCKS_TARGET * actual_epoch_time))
    # Take only DIFFICULTY_SIGNIFICANT_BITS significant bits
    new_difficulty = uint64(
        truncate_to_significant_bits(new_difficulty_precise,
                                     constants.SIGNIFICANT_BITS))
    assert count_significant_bits(new_difficulty) <= constants.SIGNIFICANT_BITS

    # Only change by a max factor, to prevent attacks, as in greenpaper, and must be at least 1
    max_diff = uint64(
        truncate_to_significant_bits(
            constants.DIFFICULTY_FACTOR * old_difficulty,
            constants.SIGNIFICANT_BITS,
        ))
    min_diff = uint64(
        truncate_to_significant_bits(
            old_difficulty // constants.DIFFICULTY_FACTOR,
            constants.SIGNIFICANT_BITS,
        ))
    if new_difficulty >= old_difficulty:
        return min(new_difficulty, max_diff)
    else:
        return max([uint64(1), new_difficulty, min_diff])
def get_next_sub_slot_iters(
    constants: ConsensusConstants,
    sub_blocks: Dict[bytes32, SubBlockRecord],
    height_to_hash: Dict[uint32, bytes32],
    prev_header_hash: bytes32,
    sub_block_height: uint32,
    curr_sub_slot_iters: uint64,
    deficit: uint8,
    new_slot: bool,
    signage_point_total_iters: uint128,
    skip_epoch_check=False,
) -> uint64:
    """
    Returns the slot iterations required for the next block after the one at sub_block_height, where new_slot is true
    iff the next block will be in the next slot.

    Args:
        constants: consensus constants being used for this chain
        sub_blocks: dictionary from header hash to SBR of all included SBR
        height_to_hash: sub-block height to header hash map for sub-blocks in peak path
        prev_header_hash: header hash of the previous sub-block
        sub_block_height: the sub-block height of the sub-block to look at
        curr_sub_slot_iters: sub-slot iters at the infusion point of the sub_block at sub_block_height
        deficit: deficit of the sub_block at sub_block_height
        new_slot: whether or not there is a new slot after sub_block_height
        signage_point_total_iters: signage point iters of the sub_block at sub_block_height
        skip_epoch_check: don't check correct epoch
    """
    next_sub_block_height: uint32 = uint32(sub_block_height + 1)

    if next_sub_block_height < (constants.EPOCH_SUB_BLOCKS -
                                constants.MAX_SUB_SLOT_SUB_BLOCKS):
        return uint64(constants.SUB_SLOT_ITERS_STARTING)

    if prev_header_hash not in sub_blocks:
        raise ValueError(f"Header hash {prev_header_hash} not in sub blocks")

    prev_sb: SubBlockRecord = sub_blocks[prev_header_hash]

    # If we are in the same epoch, return same ssi
    if not skip_epoch_check:
        _, can_finish_epoch = can_finish_sub_and_full_epoch(
            constants, sub_block_height, deficit, sub_blocks, prev_header_hash,
            False)
        if not new_slot or not can_finish_epoch:
            return curr_sub_slot_iters

    last_block_prev: SubBlockRecord = _get_last_block_in_previous_epoch(
        constants, height_to_hash, sub_blocks, prev_sb)

    # Ensure we get a block for the last block as well, and that it is before the signage point
    last_block_curr = prev_sb
    while last_block_curr.total_iters > signage_point_total_iters or not last_block_curr.is_block:
        last_block_curr = sub_blocks[last_block_curr.prev_hash]
    assert last_block_curr.timestamp is not None and last_block_prev.timestamp is not None

    # This is computed as the iterations per second in last epoch, times the target number of seconds per slot
    new_ssi_precise: uint64 = uint64(
        constants.SUB_SLOT_TIME_TARGET *
        (last_block_curr.total_iters - last_block_prev.total_iters) //
        (last_block_curr.timestamp - last_block_prev.timestamp))
    new_ssi = uint64(
        truncate_to_significant_bits(new_ssi_precise,
                                     constants.SIGNIFICANT_BITS))

    # Only change by a max factor as a sanity check
    max_ssi = uint64(
        truncate_to_significant_bits(
            constants.DIFFICULTY_FACTOR * last_block_curr.sub_slot_iters,
            constants.SIGNIFICANT_BITS,
        ))
    min_ssi = uint64(
        truncate_to_significant_bits(
            last_block_curr.sub_slot_iters // constants.DIFFICULTY_FACTOR,
            constants.SIGNIFICANT_BITS,
        ))
    if new_ssi >= last_block_curr.sub_slot_iters:
        new_ssi = min(new_ssi, max_ssi)
    else:
        new_ssi = uint64(max([constants.NUM_SPS_SUB_SLOT, new_ssi, min_ssi]))

    new_ssi = uint64(
        new_ssi -
        new_ssi % constants.NUM_SPS_SUB_SLOT)  # Must divide the sub slot
    assert count_significant_bits(new_ssi) <= constants.SIGNIFICANT_BITS
    return new_ssi
    async def validate_header_block(self, br: BlockRecord,
                                    header_block: HeaderBlock) -> bool:
        """
        Fully validates a header block. This requires the ancestors to be present in the blockchain.
        This method also validates that the header block is consistent with the block record.
        """
        # POS challenge hash == POT challenge hash == Challenge prev challenge hash
        if (header_block.proof_of_space.challenge_hash !=
                header_block.proof_of_time.challenge_hash):
            return False
        if (header_block.proof_of_space.challenge_hash !=
                header_block.challenge.prev_challenge_hash):
            return False

        if br.height > 0:
            prev_br = self.block_records[br.prev_header_hash]
            # If prev header block, check prev header block hash matches
            if prev_br.new_challenge_hash is not None:
                if (header_block.proof_of_space.challenge_hash !=
                        prev_br.new_challenge_hash):
                    return False

        # Validate PoS and get quality
        quality_str: Optional[
            bytes32] = header_block.proof_of_space.verify_and_get_quality_string(
            )
        if quality_str is None:
            return False

        difficulty: uint64
        min_iters: uint64 = self.get_min_iters(br)
        prev_block: Optional[BlockRecord]
        if (br.height % self.constants["DIFFICULTY_EPOCH"] !=
                self.constants["DIFFICULTY_DELAY"]):
            # Only allow difficulty changes once per epoch
            if br.height > 1:
                prev_block = self.block_records[br.prev_header_hash]
                assert prev_block is not None
                prev_prev_block = self.block_records[
                    prev_block.prev_header_hash]
                assert prev_prev_block is not None
                difficulty = uint64(br.weight - prev_block.weight)
                assert difficulty == prev_block.weight - prev_prev_block.weight
            elif br.height == 1:
                prev_block = self.block_records[br.prev_header_hash]
                assert prev_block is not None
                difficulty = uint64(br.weight - prev_block.weight)
                assert difficulty == prev_block.weight
            else:
                difficulty = uint64(br.weight)
                assert difficulty == self.constants["DIFFICULTY_STARTING"]
        else:
            # This is a difficulty change, so check whether it's within the allowed range.
            # (But don't check whether it's the right amount).
            prev_block = self.block_records[br.prev_header_hash]
            assert prev_block is not None
            prev_prev_block = self.block_records[prev_block.prev_header_hash]
            assert prev_prev_block is not None
            difficulty = uint64(br.weight - prev_block.weight)
            prev_difficulty = uint64(prev_block.weight -
                                     prev_prev_block.weight)

            # Ensures the challenge for this block is valid (contains correct diff reset)
            if prev_block.header_hash in self.difficulty_resets_prev:
                if self.difficulty_resets_prev[
                        prev_block.header_hash] != difficulty:
                    return False

            max_diff = uint64(
                truncate_to_significant_bits(
                    prev_difficulty * self.constants["DIFFICULTY_FACTOR"],
                    self.constants["SIGNIFICANT_BITS"],
                ))
            min_diff = uint64(
                truncate_to_significant_bits(
                    prev_difficulty // self.constants["DIFFICULTY_FACTOR"],
                    self.constants["SIGNIFICANT_BITS"],
                ))

            if difficulty < min_diff or difficulty > max_diff:
                return False

        number_of_iters: uint64 = calculate_iterations_quality(
            quality_str,
            header_block.proof_of_space.size,
            difficulty,
            min_iters,
        )

        if header_block.proof_of_time is None:
            return False

        if number_of_iters != header_block.proof_of_time.number_of_iterations:
            return False

        # Check PoT
        if not header_block.proof_of_time.is_valid(
                self.constants["DISCRIMINANT_SIZE_BITS"]):
            return False

        # Validate challenge
        proofs_hash = std_hash(header_block.proof_of_space.get_hash() +
                               header_block.proof_of_time.output.get_hash())
        if proofs_hash != header_block.challenge.proofs_hash:
            return False
        # Note that we are not validating the work difficulty reset (since we don't know the
        # next block yet. When we process the next block, we will check that it matches).

        # Validate header:
        if header_block.header.header_hash != br.header_hash:
            return False
        if header_block.header.prev_header_hash != br.prev_header_hash:
            return False
        if header_block.height != br.height:
            return False
        if header_block.weight != br.weight:
            return False
        if br.height > 0:
            assert prev_block is not None
            if prev_block.weight + difficulty != br.weight:
                return False
            if prev_block.total_iters is not None and br.total_iters is not None:
                if prev_block.total_iters + number_of_iters != br.total_iters:
                    return False
            if prev_block.height + 1 != br.height:
                return False
        else:
            if br.weight != difficulty:
                return False
            if br.total_iters != number_of_iters:
                return False

        # Check that block is not far in the future
        if (header_block.header.data.timestamp >
                time.time() + self.constants["MAX_FUTURE_TIME"]):
            return False

        # Check header pos hash
        if (header_block.proof_of_space.get_hash() !=
                header_block.header.data.proof_of_space_hash):
            return False

        # Check coinbase sig
        pair = header_block.header.data.coinbase_signature.PkMessagePair(
            header_block.proof_of_space.pool_pubkey,
            header_block.header.data.coinbase.name(),
        )

        if not header_block.header.data.coinbase_signature.validate([pair]):
            return False

        # Check coinbase and fees amount
        coinbase_reward = calculate_block_reward(br.height)
        if coinbase_reward != header_block.header.data.coinbase.amount:
            return False
        return True