Example #1
0
 async def test_weight_proof_extend_new_ses(self, default_1000_blocks):
     blocks = default_1000_blocks
     header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(blocks)
     # delete last summary
     last_ses_height = sorted(summaries.keys())[-1]
     last_ses = summaries[last_ses_height]
     del summaries[last_ses_height]
     wpf_synced = WeightProofHandler(test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
     wp = await wpf_synced.get_proof_of_weight(blocks[last_ses_height - 10].header_hash)
     assert wp is not None
     wpf_not_synced = WeightProofHandler(test_constants, BlockCache(sub_blocks, height_to_hash, header_cache, {}))
     valid, fork_point = await wpf_not_synced.validate_weight_proof(wp)
     assert valid
     assert fork_point == 0
     # extend proof with 100 blocks
     wpf = WeightProofHandler(test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
     summaries[last_ses_height] = last_ses
     wpf_synced.blockchain = BlockCache(sub_blocks, header_cache, height_to_hash, summaries)
     new_wp = await wpf_synced._create_proof_of_weight(blocks[-1].header_hash)
     valid, fork_point = await wpf_not_synced.validate_weight_proof(new_wp)
     assert valid
     assert fork_point == 0
     wpf_synced.blockchain = BlockCache(sub_blocks, header_cache, height_to_hash, summaries)
     new_wp = await wpf_synced._create_proof_of_weight(blocks[last_ses_height].header_hash)
     valid, fork_point = await wpf_not_synced.validate_weight_proof(new_wp)
     assert valid
     assert fork_point == 0
     valid, fork_point = await wpf.validate_weight_proof(new_wp)
     assert valid
     assert fork_point != 0
Example #2
0
 async def test_weight_proof_extend_multiple_ses(self, default_1000_blocks):
     blocks = default_1000_blocks
     header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(
         blocks)
     last_ses_height = sorted(summaries.keys())[-1]
     last_ses = summaries[last_ses_height]
     before_last_ses_height = sorted(summaries.keys())[-2]
     before_last_ses = summaries[before_last_ses_height]
     wpf = WeightProofHandler(
         test_constants,
         BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
     wpf_verify = WeightProofHandler(
         test_constants,
         BlockCache(sub_blocks, header_cache, height_to_hash, {}))
     for x in range(10, -1, -1):
         wp = await wpf.get_proof_of_weight(blocks[before_last_ses_height -
                                                   x].header_hash)
         assert wp is not None
         valid, fork_point, _ = await wpf_verify.validate_weight_proof(wp)
         assert valid
         assert fork_point == 0
     # extend proof with 100 blocks
     summaries[last_ses_height] = last_ses
     summaries[before_last_ses_height] = before_last_ses
     wpf = WeightProofHandler(
         test_constants,
         BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
     new_wp = await wpf._create_proof_of_weight(blocks[-1].header_hash)
     valid, fork_point, _ = await wpf.validate_weight_proof(new_wp)
     assert valid
     assert fork_point != 0
Example #3
0
 async def test_weight_proof_extend_no_ses(self, default_1000_blocks):
     blocks = default_1000_blocks
     header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(
         blocks)
     last_ses_height = sorted(summaries.keys())[-1]
     wpf_synced = WeightProofHandler(
         test_constants,
         BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
     wp = await wpf_synced.get_proof_of_weight(
         blocks[last_ses_height].header_hash)
     assert wp is not None
     # todo for each sampled sub epoch, validate number of segments
     wpf_not_synced = WeightProofHandler(
         test_constants,
         BlockCache(sub_blocks, header_cache, height_to_hash, {}))
     valid, fork_point, _ = await wpf_not_synced.validate_weight_proof(wp)
     assert valid
     assert fork_point == 0
     # extend proof with 100 blocks
     new_wp = await wpf_synced._create_proof_of_weight(
         blocks[-1].header_hash)
     valid, fork_point, _ = await wpf_not_synced.validate_weight_proof(
         new_wp)
     assert valid
     assert fork_point == 0
Example #4
0
    async def test_weight_proof10000__blocks_compact(self, default_10000_blocks_compact):
        blocks = default_10000_blocks_compact
        header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(blocks)
        wpf = WeightProofHandler(test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
        wp = await wpf.get_proof_of_weight(blocks[-1].header_hash)
        assert wp is not None
        wpf = WeightProofHandler(test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, {}))
        valid, fork_point = wpf.validate_weight_proof_single_proc(wp)

        assert valid
        assert fork_point == 0
    async def test_weight_proof_from_database(self):
        connection = await aiosqlite.connect("path to db")
        block_store: BlockStore = await BlockStore.create(connection)
        blocks, peak = await block_store.get_block_records()
        peak_height = blocks[peak].height
        headers = await block_store.get_header_blocks_in_range(0, peak_height)
        sub_height_to_hash = {}
        sub_epoch_summaries = {}
        # peak_header = await block_store.get_full_blocks_at([peak_height])
        if len(blocks) == 0:
            return None, None

        assert peak is not None

        # Sets the other state variables (peak_height and height_to_hash)
        curr: BlockRecord = blocks[peak]
        while True:
            sub_height_to_hash[curr.height] = curr.header_hash
            if curr.sub_epoch_summary_included is not None:
                sub_epoch_summaries[
                    curr.height] = curr.sub_epoch_summary_included
            if curr.height == 0:
                break
            curr = blocks[curr.prev_hash]
        assert len(sub_height_to_hash) == peak_height + 1
        block_cache = BlockCache(blocks, headers, sub_height_to_hash,
                                 sub_epoch_summaries)
        wpf = WeightProofHandler(DEFAULT_CONSTANTS, block_cache)
        wp = await wpf._create_proof_of_weight(sub_height_to_hash[peak_height -
                                                                  50])
        valid, fork_point = wpf.validate_weight_proof_single_proc(wp)

        await connection.close()
        assert valid
        print(f"size of proof is {get_size(wp)}")
Example #6
0
 async def test_weight_proof_from_genesis(self, default_400_blocks):
     blocks = default_400_blocks
     header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(blocks)
     wpf = WeightProofHandler(test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
     wp = await wpf.get_proof_of_weight(blocks[-1].header_hash)
     assert wp is not None
     wp = await wpf.get_proof_of_weight(blocks[-1].header_hash)
     assert wp is not None
Example #7
0
 async def test_weight_proof_bad_peak_hash(self, default_1000_blocks):
     blocks = default_1000_blocks
     header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(
         blocks)
     wpf = WeightProofHandler(
         test_constants,
         BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
     wp = await wpf.get_proof_of_weight(b"sadgfhjhgdgsfadfgh")
     assert wp is None
Example #8
0
async def load_blocks_dont_validate(
    blocks,
) -> Tuple[Dict[bytes32, HeaderBlock], Dict[uint32, bytes32], Dict[
        bytes32, BlockRecord], Dict[bytes32, SubEpochSummary]]:
    header_cache: Dict[bytes32, HeaderBlock] = {}
    height_to_hash: Dict[uint32, bytes32] = {}
    sub_blocks: Dict[bytes32, BlockRecord] = {}
    sub_epoch_summaries: Dict[bytes32, SubEpochSummary] = {}
    prev_block = None
    difficulty = test_constants.DIFFICULTY_STARTING
    block: FullBlock
    for block in blocks:
        if block.height > 0:
            assert prev_block is not None
            difficulty = block.reward_chain_block.weight - prev_block.weight

        if block.reward_chain_block.challenge_chain_sp_vdf is None:
            assert block.reward_chain_block.signage_point_index == 0
            cc_sp: bytes32 = block.reward_chain_block.pos_ss_cc_challenge_hash
        else:
            cc_sp = block.reward_chain_block.challenge_chain_sp_vdf.output.get_hash(
            )

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

        required_iters: uint64 = calculate_iterations_quality(
            test_constants.DIFFICULTY_CONSTANT_FACTOR,
            quality_string,
            block.reward_chain_block.proof_of_space.size,
            difficulty,
            cc_sp,
        )

        # TODO: address hint error and remove ignore
        #       error: Argument 2 to "BlockCache" has incompatible type "Dict[uint32, bytes32]"; expected
        #       "Optional[Dict[bytes32, HeaderBlock]]"  [arg-type]
        sub_block = block_to_block_record(
            test_constants,
            BlockCache(sub_blocks, height_to_hash),  # type: ignore[arg-type]
            required_iters,
            block,
            None,
        )
        sub_blocks[block.header_hash] = sub_block
        height_to_hash[block.height] = block.header_hash
        header_cache[block.header_hash] = get_block_header(block, [], [])
        if sub_block.sub_epoch_summary_included is not None:
            sub_epoch_summaries[
                block.height] = sub_block.sub_epoch_summary_included
        prev_block = block
    return header_cache, height_to_hash, sub_blocks, sub_epoch_summaries
Example #9
0
    async def test_weight_proof1000_partial_blocks_compact(self, default_10000_blocks_compact):
        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            100,
            block_list_input=default_10000_blocks_compact,
            seed=b"asdfghjkl",
            normalized_to_identity_cc_ip=True,
            normalized_to_identity_cc_eos=True,
            normalized_to_identity_icc_eos=True,
        )
        header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(blocks)
        wpf = WeightProofHandler(test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
        wp = await wpf.get_proof_of_weight(blocks[-1].header_hash)
        assert wp is not None
        wpf = WeightProofHandler(test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, {}))
        valid, fork_point = wpf.validate_weight_proof_single_proc(wp)

        assert valid
        assert fork_point == 0
Example #10
0
 async def test_check_num_of_samples(self, default_10000_blocks):
     blocks = default_10000_blocks
     header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(blocks)
     wpf = WeightProofHandler(test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
     wp = await wpf.get_proof_of_weight(blocks[-1].header_hash)
     curr = -1
     samples = 0
     for sub_epoch_segment in wp.sub_epoch_segments:
         if sub_epoch_segment.sub_epoch_n > curr:
             curr = sub_epoch_segment.sub_epoch_n
             samples += 1
     assert samples <= wpf.MAX_SAMPLES
Example #11
0
 async def test_weight_proof_summaries_1000_blocks(self, default_1000_blocks):
     blocks = default_1000_blocks
     header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(blocks)
     wpf = WeightProofHandler(test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
     wp = await wpf.get_proof_of_weight(blocks[-1].header_hash)
     summaries, sub_epoch_data_weight, _ = _map_sub_epoch_summaries(
         wpf.constants.SUB_EPOCH_BLOCKS,
         wpf.constants.GENESIS_CHALLENGE,
         wp.sub_epochs,
         wpf.constants.DIFFICULTY_STARTING,
     )
     assert _validate_summaries_weight(test_constants, sub_epoch_data_weight, summaries, wp)
def batch_pre_validate_blocks(
    constants_dict: Dict,
    blocks_pickled: Dict[bytes, bytes],
    header_blocks_pickled: List[bytes],
    transaction_generators: List[Optional[bytes]],
    check_filter: bool,
    expected_difficulty: List[uint64],
    expected_sub_slot_iters: List[uint64],
    validate_transactions: bool,
) -> List[bytes]:
    assert len(header_blocks_pickled) == len(transaction_generators)
    blocks = {}
    for k, v in blocks_pickled.items():
        blocks[k] = BlockRecord.from_bytes(v)
    results: List[PreValidationResult] = []
    constants: ConsensusConstants = dataclass_from_dict(
        ConsensusConstants, constants_dict)
    for i in range(len(header_blocks_pickled)):
        try:
            header_block: HeaderBlock = HeaderBlock.from_bytes(
                header_blocks_pickled[i])
            generator: Optional[bytes] = transaction_generators[i]
            required_iters, error = validate_finished_header_block(
                constants,
                BlockCache(blocks),
                header_block,
                check_filter,
                expected_difficulty[i],
                expected_sub_slot_iters[i],
            )
            cost_result: Optional[CostResult] = None
            error_int: Optional[uint16] = None
            if error is not None:
                error_int = uint16(error.code.value)
            if constants_dict["NETWORK_TYPE"] == NetworkType.MAINNET.value:
                cost_result = None
            else:
                if not error and generator is not None and validate_transactions:
                    cost_result = calculate_cost_of_program(
                        SerializedProgram.from_bytes(generator),
                        constants.CLVM_COST_RATIO_CONSTANT)
            results.append(
                PreValidationResult(error_int, required_iters, cost_result))
        except Exception:
            error_stack = traceback.format_exc()
            log.error(f"Exception: {error_stack}")
            results.append(
                PreValidationResult(uint16(Err.UNKNOWN.value), None, None))
    return [bytes(r) for r in results]
Example #13
0
async def _test_map_summaries(blocks, header_cache, height_to_hash, sub_blocks, summaries):
    curr = sub_blocks[blocks[-1].header_hash]
    orig_summaries: Dict[int, SubEpochSummary] = {}
    while curr.height > 0:
        if curr.sub_epoch_summary_included is not None:
            orig_summaries[curr.height] = curr.sub_epoch_summary_included
        # next sub block
        curr = sub_blocks[curr.prev_hash]

    wpf = WeightProofHandler(test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))

    wp = await wpf.get_proof_of_weight(blocks[-1].header_hash)
    assert wp is not None
    # sub epoch summaries validate hashes
    summaries, sub_epoch_data_weight, _ = _map_sub_epoch_summaries(
        test_constants.SUB_EPOCH_BLOCKS,
        test_constants.GENESIS_CHALLENGE,
        wp.sub_epochs,
        test_constants.DIFFICULTY_STARTING,
    )
    assert len(summaries) == len(orig_summaries)
Example #14
0
    async def test_weight_proof_edge_cases(self, default_400_blocks):
        blocks: List[FullBlock] = default_400_blocks

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
            skip_slots=2)

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
            skip_slots=1)

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
            skip_slots=2)

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
            skip_slots=4,
            normalized_to_identity_cc_eos=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            10,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
            skip_slots=4,
            normalized_to_identity_icc_eos=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            10,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
            skip_slots=4,
            normalized_to_identity_cc_ip=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            10,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
            skip_slots=4,
            normalized_to_identity_cc_sp=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
            skip_slots=4)

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            10,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=True,
        )

        blocks: List[FullBlock] = bt.get_consecutive_blocks(
            300,
            block_list_input=blocks,
            seed=b"asdfghjkl",
            force_overflow=False,
        )

        header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(
            blocks)
        wpf = WeightProofHandler(
            test_constants,
            BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
        wp = await wpf.get_proof_of_weight(blocks[-1].header_hash)
        assert wp is not None
        wpf = WeightProofHandler(
            test_constants,
            BlockCache(sub_blocks, header_cache, height_to_hash, {}))
        valid, fork_point = wpf.validate_weight_proof_single_proc(wp)

        assert valid
        assert fork_point == 0
    async def test_basic_store(self,
                               empty_blockchain,
                               normalized_to_identity: bool = False):
        blockchain = empty_blockchain
        blocks = bt.get_consecutive_blocks(
            10,
            seed=b"1234",
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )

        store = await FullNodeStore.create(test_constants)

        unfinished_blocks = []
        for block in blocks:
            unfinished_blocks.append(
                UnfinishedBlock(
                    block.finished_sub_slots,
                    block.reward_chain_block.get_unfinished(),
                    block.challenge_chain_sp_proof,
                    block.reward_chain_sp_proof,
                    block.foliage,
                    block.foliage_transaction_block,
                    block.transactions_info,
                    block.transactions_generator,
                    [],
                ))

        # Add/get candidate block
        assert store.get_candidate_block(
            unfinished_blocks[0].get_hash()) is None
        for height, unf_block in enumerate(unfinished_blocks):
            store.add_candidate_block(unf_block.get_hash(), height, unf_block)

        assert store.get_candidate_block(
            unfinished_blocks[4].get_hash()) == unfinished_blocks[4]
        store.clear_candidate_blocks_below(uint32(8))
        assert store.get_candidate_block(
            unfinished_blocks[5].get_hash()) is None
        assert store.get_candidate_block(
            unfinished_blocks[8].get_hash()) is not None

        # Test seen unfinished blocks
        h_hash_1 = bytes32(token_bytes(32))
        assert not store.seen_unfinished_block(h_hash_1)
        assert store.seen_unfinished_block(h_hash_1)
        store.clear_seen_unfinished_blocks()
        assert not store.seen_unfinished_block(h_hash_1)

        # Add/get unfinished block
        for height, unf_block in enumerate(unfinished_blocks):
            assert store.get_unfinished_block(unf_block.partial_hash) is None
            store.add_unfinished_block(
                height, unf_block,
                PreValidationResult(None, uint64(123532), None))
            assert store.get_unfinished_block(
                unf_block.partial_hash) == unf_block
            store.remove_unfinished_block(unf_block.partial_hash)
            assert store.get_unfinished_block(unf_block.partial_hash) is None

        blocks = bt.get_consecutive_blocks(
            1,
            skip_slots=5,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
        )
        sub_slots = blocks[0].finished_sub_slots
        assert len(sub_slots) == 5

        assert (store.get_finished_sub_slots(
            BlockCache({}),
            None,
            sub_slots[0].challenge_chain.challenge_chain_end_of_slot_vdf.
            challenge,
        ) == [])
        # Test adding non-connecting sub-slots genesis
        assert store.get_sub_slot(test_constants.GENESIS_CHALLENGE) is None
        assert store.get_sub_slot(
            sub_slots[0].challenge_chain.get_hash()) is None
        assert store.get_sub_slot(
            sub_slots[1].challenge_chain.get_hash()) is None
        assert store.new_finished_sub_slot(sub_slots[1], {}, None,
                                           None) is None
        assert store.new_finished_sub_slot(sub_slots[2], {}, None,
                                           None) is None

        # Test adding sub-slots after genesis
        assert store.new_finished_sub_slot(sub_slots[0], {}, None,
                                           None) is not None
        assert store.get_sub_slot(
            sub_slots[0].challenge_chain.get_hash())[0] == sub_slots[0]
        assert store.get_sub_slot(
            sub_slots[1].challenge_chain.get_hash()) is None
        assert store.new_finished_sub_slot(sub_slots[1], {}, None,
                                           None) is not None
        for i in range(len(sub_slots)):
            assert store.new_finished_sub_slot(sub_slots[i], {}, None,
                                               None) is not None
            assert store.get_sub_slot(
                sub_slots[i].challenge_chain.get_hash())[0] == sub_slots[i]

        assert store.get_finished_sub_slots(
            BlockCache({}), None,
            sub_slots[-1].challenge_chain.get_hash()) == sub_slots
        assert store.get_finished_sub_slots(BlockCache(
            {}), None, std_hash(b"not a valid hash")) is None

        assert (store.get_finished_sub_slots(
            BlockCache({}), None,
            sub_slots[-2].challenge_chain.get_hash()) == sub_slots[:-1])

        # Test adding genesis peak
        await blockchain.receive_block(blocks[0])
        peak = blockchain.get_peak()
        peak_full_block = await blockchain.get_full_peak()
        if peak.overflow:
            store.new_peak(peak, peak_full_block, sub_slots[-2], sub_slots[-1],
                           False, {})
        else:
            store.new_peak(peak, peak_full_block, None, sub_slots[-1], False,
                           {})

        assert store.get_sub_slot(
            sub_slots[0].challenge_chain.get_hash()) is None
        assert store.get_sub_slot(
            sub_slots[1].challenge_chain.get_hash()) is None
        assert store.get_sub_slot(
            sub_slots[2].challenge_chain.get_hash()) is None
        if peak.overflow:
            assert store.get_sub_slot(
                sub_slots[3].challenge_chain.get_hash())[0] == sub_slots[3]
        else:
            assert store.get_sub_slot(
                sub_slots[3].challenge_chain.get_hash()) is None
        assert store.get_sub_slot(
            sub_slots[4].challenge_chain.get_hash())[0] == sub_slots[4]

        assert (store.get_finished_sub_slots(
            blockchain,
            peak,
            sub_slots[-1].challenge_chain.get_hash(),
        ) == [])

        # Test adding non genesis peak directly
        blocks = bt.get_consecutive_blocks(
            2,
            skip_slots=2,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        blocks = bt.get_consecutive_blocks(
            3,
            block_list_input=blocks,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        for block in blocks:
            await blockchain.receive_block(block)
            sb = blockchain.block_record(block.header_hash)
            sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
                block.header_hash)
            res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, False,
                                 blockchain)
            assert res[0] is None

        # Add reorg blocks
        blocks_reorg = bt.get_consecutive_blocks(
            20,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        for block in blocks_reorg:
            res, _, _ = await blockchain.receive_block(block)
            if res == ReceiveBlockResult.NEW_PEAK:
                sb = blockchain.block_record(block.header_hash)
                sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
                    block.header_hash)
                res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, True,
                                     blockchain)
                assert res[0] is None

        # Add slots to the end
        blocks_2 = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks_reorg,
            skip_slots=2,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        for slot in blocks_2[-1].finished_sub_slots:
            store.new_finished_sub_slot(slot, blockchain,
                                        blockchain.get_peak(), await
                                        blockchain.get_full_peak())

        assert store.get_sub_slot(
            sub_slots[3].challenge_chain.get_hash()) is None
        assert store.get_sub_slot(
            sub_slots[4].challenge_chain.get_hash()) is None

        # Test adding signage point
        peak = blockchain.get_peak()
        ss_start_iters = peak.ip_sub_slot_total_iters(test_constants)
        for i in range(
                1, test_constants.NUM_SPS_SUB_SLOT -
                test_constants.NUM_SP_INTERVALS_EXTRA):
            sp = get_signage_point(
                test_constants,
                blockchain,
                peak,
                ss_start_iters,
                uint8(i),
                [],
                peak.sub_slot_iters,
            )
            assert store.new_signage_point(i, blockchain, peak,
                                           peak.sub_slot_iters, sp)

        blocks = blocks_reorg
        while True:
            blocks = bt.get_consecutive_blocks(
                1,
                block_list_input=blocks,
                normalized_to_identity_cc_eos=normalized_to_identity,
                normalized_to_identity_icc_eos=normalized_to_identity,
                normalized_to_identity_cc_ip=normalized_to_identity,
                normalized_to_identity_cc_sp=normalized_to_identity,
            )
            res, _, _ = await blockchain.receive_block(blocks[-1])
            if res == ReceiveBlockResult.NEW_PEAK:
                sb = blockchain.block_record(blocks[-1].header_hash)
                sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
                    blocks[-1].header_hash)

                res = store.new_peak(sb, blocks[-1], sp_sub_slot, ip_sub_slot,
                                     True, blockchain)
                assert res[0] is None
                if sb.overflow and sp_sub_slot is not None:
                    assert sp_sub_slot != ip_sub_slot
                    break

        peak = blockchain.get_peak()
        assert peak.overflow
        # Overflow peak should result in 2 finished sub slots
        assert len(store.finished_sub_slots) == 2

        # Add slots to the end, except for the last one, which we will use to test invalid SP
        blocks_2 = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            skip_slots=3,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        for slot in blocks_2[-1].finished_sub_slots[:-1]:
            store.new_finished_sub_slot(slot, blockchain,
                                        blockchain.get_peak(), await
                                        blockchain.get_full_peak())
        finished_sub_slots = blocks_2[-1].finished_sub_slots
        assert len(store.finished_sub_slots) == 4

        # Test adding signage points for overflow blocks (sp_sub_slot)
        ss_start_iters = peak.sp_sub_slot_total_iters(test_constants)
        # for i in range(peak.signage_point_index, test_constants.NUM_SPS_SUB_SLOT):
        #     if i < peak.signage_point_index:
        #         continue
        #     latest = peak
        #     while latest.total_iters > peak.sp_total_iters(test_constants):
        #         latest = blockchain.blocks[latest.prev_hash]
        #     sp = get_signage_point(
        #         test_constants,
        #         blockchain.blocks,
        #         latest,
        #         ss_start_iters,
        #         uint8(i),
        #         [],
        #         peak.sub_slot_iters,
        #     )
        #     assert store.new_signage_point(i, blockchain.blocks, peak, peak.sub_slot_iters, sp)

        # Test adding signage points for overflow blocks (ip_sub_slot)
        for i in range(
                1, test_constants.NUM_SPS_SUB_SLOT -
                test_constants.NUM_SP_INTERVALS_EXTRA):
            sp = get_signage_point(
                test_constants,
                blockchain,
                peak,
                peak.ip_sub_slot_total_iters(test_constants),
                uint8(i),
                [],
                peak.sub_slot_iters,
            )
            assert store.new_signage_point(i, blockchain, peak,
                                           peak.sub_slot_iters, sp)

        # Test adding future signage point, a few slots forward (good)
        saved_sp_hash = None
        for slot_offset in range(1, len(finished_sub_slots)):
            for i in range(
                    1,
                    test_constants.NUM_SPS_SUB_SLOT -
                    test_constants.NUM_SP_INTERVALS_EXTRA,
            ):
                sp = get_signage_point(
                    test_constants,
                    blockchain,
                    peak,
                    peak.ip_sub_slot_total_iters(test_constants) +
                    slot_offset * peak.sub_slot_iters,
                    uint8(i),
                    finished_sub_slots[:slot_offset],
                    peak.sub_slot_iters,
                )
                assert sp.cc_vdf is not None
                saved_sp_hash = sp.cc_vdf.output.get_hash()
                assert store.new_signage_point(i, blockchain, peak,
                                               peak.sub_slot_iters, sp)

        # Test adding future signage point (bad)
        for i in range(
                1, test_constants.NUM_SPS_SUB_SLOT -
                test_constants.NUM_SP_INTERVALS_EXTRA):
            sp = get_signage_point(
                test_constants,
                blockchain,
                peak,
                peak.ip_sub_slot_total_iters(test_constants) +
                len(finished_sub_slots) * peak.sub_slot_iters,
                uint8(i),
                finished_sub_slots[:len(finished_sub_slots)],
                peak.sub_slot_iters,
            )
            assert not store.new_signage_point(i, blockchain, peak,
                                               peak.sub_slot_iters, sp)

        # Test adding past signage point
        sp = SignagePoint(
            blocks[1].reward_chain_block.challenge_chain_sp_vdf,
            blocks[1].challenge_chain_sp_proof,
            blocks[1].reward_chain_block.reward_chain_sp_vdf,
            blocks[1].reward_chain_sp_proof,
        )
        assert not store.new_signage_point(
            blocks[1].reward_chain_block.signage_point_index,
            {},
            peak,
            blockchain.block_record(
                blocks[1].header_hash).sp_sub_slot_total_iters(test_constants),
            sp,
        )

        # Get signage point by index
        assert (store.get_signage_point_by_index(
            finished_sub_slots[0].challenge_chain.get_hash(),
            4,
            finished_sub_slots[0].reward_chain.get_hash(),
        ) is not None)

        assert (store.get_signage_point_by_index(
            finished_sub_slots[0].challenge_chain.get_hash(), 4,
            std_hash(b"1")) is None)

        # Get signage point by hash
        assert store.get_signage_point(saved_sp_hash) is not None
        assert store.get_signage_point(std_hash(b"2")) is None

        # Test adding signage points before genesis
        store.initialize_genesis_sub_slot()
        assert len(store.finished_sub_slots) == 1
        for i in range(
                1, test_constants.NUM_SPS_SUB_SLOT -
                test_constants.NUM_SP_INTERVALS_EXTRA):
            sp = get_signage_point(
                test_constants,
                BlockCache({}, {}),
                None,
                uint128(0),
                uint8(i),
                [],
                peak.sub_slot_iters,
            )
            assert store.new_signage_point(i, {}, None, peak.sub_slot_iters,
                                           sp)

        blocks_3 = bt.get_consecutive_blocks(
            1,
            skip_slots=2,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        for slot in blocks_3[-1].finished_sub_slots:
            store.new_finished_sub_slot(slot, {}, None, None)
        assert len(store.finished_sub_slots) == 3
        finished_sub_slots = blocks_3[-1].finished_sub_slots

        for slot_offset in range(1, len(finished_sub_slots) + 1):
            for i in range(
                    1,
                    test_constants.NUM_SPS_SUB_SLOT -
                    test_constants.NUM_SP_INTERVALS_EXTRA,
            ):
                sp = get_signage_point(
                    test_constants,
                    BlockCache({}, {}),
                    None,
                    slot_offset * peak.sub_slot_iters,
                    uint8(i),
                    finished_sub_slots[:slot_offset],
                    peak.sub_slot_iters,
                )
                assert store.new_signage_point(i, {}, None,
                                               peak.sub_slot_iters, sp)

        # Test adding signage points after genesis
        blocks_4 = bt.get_consecutive_blocks(
            1,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        blocks_5 = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks_4,
            skip_slots=1,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )

        # If this is not the case, fix test to find a block that is
        assert (blocks_4[-1].reward_chain_block.signage_point_index <
                test_constants.NUM_SPS_SUB_SLOT -
                test_constants.NUM_SP_INTERVALS_EXTRA)
        await blockchain.receive_block(blocks_4[-1])
        sb = blockchain.block_record(blocks_4[-1].header_hash)
        store.new_peak(sb, blocks_4[-1], None, None, False, blockchain)
        for i in range(
                sb.signage_point_index + test_constants.NUM_SP_INTERVALS_EXTRA,
                test_constants.NUM_SPS_SUB_SLOT,
        ):
            if is_overflow_block(test_constants, uint8(i)):
                finished_sub_slots = blocks_5[-1].finished_sub_slots
            else:
                finished_sub_slots = []

            sp = get_signage_point(
                test_constants,
                blockchain,
                sb,
                uint128(0),
                uint8(i),
                finished_sub_slots,
                peak.sub_slot_iters,
            )
            assert store.new_signage_point(i, empty_blockchain, sb,
                                           peak.sub_slot_iters, sp)

        # Test future EOS cache
        store.initialize_genesis_sub_slot()
        blocks = bt.get_consecutive_blocks(
            1,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        await blockchain.receive_block(blocks[-1])
        while True:
            blocks = bt.get_consecutive_blocks(
                1,
                block_list_input=blocks,
                normalized_to_identity_cc_eos=normalized_to_identity,
                normalized_to_identity_icc_eos=normalized_to_identity,
                normalized_to_identity_cc_ip=normalized_to_identity,
                normalized_to_identity_cc_sp=normalized_to_identity,
            )
            await blockchain.receive_block(blocks[-1])
            sb = blockchain.block_record(blocks[-1].header_hash)
            if sb.first_in_sub_slot:
                break
        assert len(blocks) >= 2
        dependant_sub_slots = blocks[-1].finished_sub_slots
        peak = blockchain.get_peak()
        peak_full_block = await blockchain.get_full_peak()
        for block in blocks[:-2]:
            sb = blockchain.block_record(block.header_hash)
            sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
                block.header_hash)
            peak = sb
            peak_full_block = block
            res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, False,
                                 blockchain)
            assert res[0] is None

        assert store.new_finished_sub_slot(dependant_sub_slots[0], blockchain,
                                           peak, peak_full_block) is None
        block = blocks[-2]
        sb = blockchain.block_record(block.header_hash)
        sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
            block.header_hash)
        res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, False,
                             blockchain)
        assert res[0] == dependant_sub_slots[0]
        assert res[1] == res[2] == []

        # Test future IP cache
        store.initialize_genesis_sub_slot()
        blocks = bt.get_consecutive_blocks(
            60,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
        )

        for block in blocks[:5]:
            await blockchain.receive_block(block)
            sb = blockchain.block_record(block.header_hash)

            sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
                block.header_hash)
            res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, False,
                                 blockchain)
            assert res[0] is None

        case_0, case_1 = False, False
        for i in range(5, len(blocks) - 1):
            prev_block = blocks[i]
            block = blocks[i + 1]
            new_ip = NewInfusionPointVDF(
                block.reward_chain_block.get_unfinished().get_hash(),
                block.reward_chain_block.challenge_chain_ip_vdf,
                block.challenge_chain_ip_proof,
                block.reward_chain_block.reward_chain_ip_vdf,
                block.reward_chain_ip_proof,
                block.reward_chain_block.infused_challenge_chain_ip_vdf,
                block.infused_challenge_chain_ip_proof,
            )
            store.add_to_future_ip(new_ip)

            await blockchain.receive_block(prev_block)
            sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
                prev_block.header_hash)
            sb = blockchain.block_record(prev_block.header_hash)
            res = store.new_peak(sb, prev_block, sp_sub_slot, ip_sub_slot,
                                 False, blockchain)
            if len(block.finished_sub_slots) == 0:
                case_0 = True
                assert res[2] == [new_ip]
            else:
                case_1 = True
                assert res[2] == []
                found_ips = []
                for ss in block.finished_sub_slots:
                    found_ips += store.new_finished_sub_slot(
                        ss, blockchain, sb, prev_block)
                assert found_ips == [new_ip]

        # If flaky, increase the number of blocks created
        assert case_0 and case_1
def batch_pre_validate_blocks(
    constants_dict: Dict,
    blocks_pickled: Dict[bytes, bytes],
    full_blocks_pickled: Optional[List[bytes]],
    header_blocks_pickled: Optional[List[bytes]],
    prev_transaction_generators: List[Optional[bytes]],
    npc_results: Dict[uint32, bytes],
    check_filter: bool,
    expected_difficulty: List[uint64],
    expected_sub_slot_iters: List[uint64],
) -> List[bytes]:
    blocks = {}
    for k, v in blocks_pickled.items():
        blocks[k] = BlockRecord.from_bytes(v)
    results: List[PreValidationResult] = []
    constants: ConsensusConstants = dataclass_from_dict(
        ConsensusConstants, constants_dict)
    if full_blocks_pickled is not None and header_blocks_pickled is not None:
        assert ValueError("Only one should be passed here")
    if full_blocks_pickled is not None:
        for i in range(len(full_blocks_pickled)):
            try:
                block: FullBlock = FullBlock.from_bytes(full_blocks_pickled[i])
                tx_additions: List[Coin] = []
                removals: List[bytes32] = []
                npc_result: Optional[NPCResult] = None
                if block.height in npc_results:
                    npc_result = NPCResult.from_bytes(
                        npc_results[block.height])
                    assert npc_result is not None
                    if npc_result.npc_list is not None:
                        removals, tx_additions = tx_removals_and_additions(
                            npc_result.npc_list)
                    else:
                        removals, tx_additions = [], []

                if block.transactions_generator is not None and npc_result is None:
                    prev_generator_bytes = prev_transaction_generators[i]
                    assert prev_generator_bytes is not None
                    assert block.transactions_info is not None
                    block_generator: BlockGenerator = BlockGenerator.from_bytes(
                        prev_generator_bytes)
                    assert block_generator.program == block.transactions_generator
                    npc_result = get_name_puzzle_conditions(
                        block_generator,
                        min(constants.MAX_BLOCK_COST_CLVM,
                            block.transactions_info.cost), True)
                    removals, tx_additions = tx_removals_and_additions(
                        npc_result.npc_list)

                header_block = get_block_header(block, tx_additions, removals)
                required_iters, error = validate_finished_header_block(
                    constants,
                    BlockCache(blocks),
                    header_block,
                    check_filter,
                    expected_difficulty[i],
                    expected_sub_slot_iters[i],
                )
                error_int: Optional[uint16] = None
                if error is not None:
                    error_int = uint16(error.code.value)

                results.append(
                    PreValidationResult(error_int, required_iters, npc_result))
            except Exception:
                error_stack = traceback.format_exc()
                log.error(f"Exception: {error_stack}")
                results.append(
                    PreValidationResult(uint16(Err.UNKNOWN.value), None, None))
    elif header_blocks_pickled is not None:
        for i in range(len(header_blocks_pickled)):
            try:
                header_block = HeaderBlock.from_bytes(header_blocks_pickled[i])
                required_iters, error = validate_finished_header_block(
                    constants,
                    BlockCache(blocks),
                    header_block,
                    check_filter,
                    expected_difficulty[i],
                    expected_sub_slot_iters[i],
                )
                error_int = None
                if error is not None:
                    error_int = uint16(error.code.value)
                results.append(
                    PreValidationResult(error_int, required_iters, None))
            except Exception:
                error_stack = traceback.format_exc()
                log.error(f"Exception: {error_stack}")
                results.append(
                    PreValidationResult(uint16(Err.UNKNOWN.value), None, None))
    return [bytes(r) for r in results]
async def pre_validate_blocks_multiprocessing(
    constants: ConsensusConstants,
    constants_json: Dict,
    block_records: BlockchainInterface,
    blocks: Sequence[Union[FullBlock, HeaderBlock]],
    pool: ProcessPoolExecutor,
    check_filter: bool,
    npc_results: Dict[uint32, NPCResult],
    get_block_generator: Optional[Callable],
    batch_size: int,
    wp_summaries: Optional[List[SubEpochSummary]] = None,
) -> Optional[List[PreValidationResult]]:
    """
    This method must be called under the blockchain lock
    If all the full blocks pass pre-validation, (only validates header), returns the list of required iters.
    if any validation issue occurs, returns False.

    Args:
        check_filter:
        constants_json:
        pool:
        constants:
        block_records:
        blocks: list of full blocks to validate (must be connected to current chain)
        npc_results
        get_block_generator
    """
    prev_b: Optional[BlockRecord] = None
    # Collects all the recent blocks (up to the previous sub-epoch)
    recent_blocks: Dict[bytes32, BlockRecord] = {}
    recent_blocks_compressed: Dict[bytes32, BlockRecord] = {}
    num_sub_slots_found = 0
    num_blocks_seen = 0
    if blocks[0].height > 0:
        if not block_records.contains_block(blocks[0].prev_header_hash):
            return [
                PreValidationResult(uint16(Err.INVALID_PREV_BLOCK_HASH.value),
                                    None, None)
            ]
        curr = block_records.block_record(blocks[0].prev_header_hash)
        num_sub_slots_to_look_for = 3 if curr.overflow else 2
        while (curr.sub_epoch_summary_included is None
               or num_blocks_seen < constants.NUMBER_OF_TIMESTAMPS
               or num_sub_slots_found < num_sub_slots_to_look_for
               ) and curr.height > 0:
            if num_blocks_seen < constants.NUMBER_OF_TIMESTAMPS or num_sub_slots_found < num_sub_slots_to_look_for:
                recent_blocks_compressed[curr.header_hash] = curr

            if curr.first_in_sub_slot:
                assert curr.finished_challenge_slot_hashes is not None
                num_sub_slots_found += len(curr.finished_challenge_slot_hashes)
            recent_blocks[curr.header_hash] = curr
            if curr.is_transaction_block:
                num_blocks_seen += 1
            curr = block_records.block_record(curr.prev_hash)
        recent_blocks[curr.header_hash] = curr
        recent_blocks_compressed[curr.header_hash] = curr
    block_record_was_present = []
    for block in blocks:
        block_record_was_present.append(
            block_records.contains_block(block.header_hash))

    diff_ssis: List[Tuple[uint64, uint64]] = []
    for block in blocks:
        if block.height != 0:
            assert block_records.contains_block(block.prev_header_hash)
            if prev_b is None:
                prev_b = block_records.block_record(block.prev_header_hash)

        sub_slot_iters, difficulty = get_next_sub_slot_iters_and_difficulty(
            constants,
            len(block.finished_sub_slots) > 0, prev_b, block_records)

        overflow = is_overflow_block(
            constants, block.reward_chain_block.signage_point_index)
        challenge = get_block_challenge(constants, block,
                                        BlockCache(recent_blocks),
                                        prev_b is None, overflow, False)
        if block.reward_chain_block.challenge_chain_sp_vdf is None:
            cc_sp_hash: bytes32 = challenge
        else:
            cc_sp_hash = block.reward_chain_block.challenge_chain_sp_vdf.output.get_hash(
            )
        q_str: Optional[
            bytes32] = block.reward_chain_block.proof_of_space.verify_and_get_quality_string(
                constants, challenge, cc_sp_hash)
        if q_str is None:
            for i, block_i in enumerate(blocks):
                if not block_record_was_present[
                        i] and block_records.contains_block(
                            block_i.header_hash):
                    block_records.remove_block_record(block_i.header_hash)
            return None

        required_iters: uint64 = calculate_iterations_quality(
            constants.DIFFICULTY_CONSTANT_FACTOR,
            q_str,
            block.reward_chain_block.proof_of_space.size,
            difficulty,
            cc_sp_hash,
        )

        block_rec = block_to_block_record(
            constants,
            block_records,
            required_iters,
            block,
            None,
        )

        if block_rec.sub_epoch_summary_included is not None and wp_summaries is not None:
            idx = int(block.height / constants.SUB_EPOCH_BLOCKS) - 1
            next_ses = wp_summaries[idx]
            if not block_rec.sub_epoch_summary_included.get_hash(
            ) == next_ses.get_hash():
                log.error(
                    "sub_epoch_summary does not match wp sub_epoch_summary list"
                )
                return None
        # Makes sure to not override the valid blocks already in block_records
        if not block_records.contains_block(block_rec.header_hash):
            block_records.add_block_record(
                block_rec)  # Temporarily add block to dict
            recent_blocks[block_rec.header_hash] = block_rec
            recent_blocks_compressed[block_rec.header_hash] = block_rec
        else:
            recent_blocks[block_rec.header_hash] = block_records.block_record(
                block_rec.header_hash)
            recent_blocks_compressed[
                block_rec.header_hash] = block_records.block_record(
                    block_rec.header_hash)
        prev_b = block_rec
        diff_ssis.append((difficulty, sub_slot_iters))

    block_dict: Dict[bytes32, Union[FullBlock, HeaderBlock]] = {}
    for i, block in enumerate(blocks):
        block_dict[block.header_hash] = block
        if not block_record_was_present[i]:
            block_records.remove_block_record(block.header_hash)

    recent_sb_compressed_pickled = {
        bytes(k): bytes(v)
        for k, v in recent_blocks_compressed.items()
    }
    npc_results_pickled = {}
    for k, v in npc_results.items():
        npc_results_pickled[k] = bytes(v)
    futures = []
    # Pool of workers to validate blocks concurrently
    for i in range(0, len(blocks), batch_size):
        end_i = min(i + batch_size, len(blocks))
        blocks_to_validate = blocks[i:end_i]
        if any([
                len(block.finished_sub_slots) > 0
                for block in blocks_to_validate
        ]):
            final_pickled = {
                bytes(k): bytes(v)
                for k, v in recent_blocks.items()
            }
        else:
            final_pickled = recent_sb_compressed_pickled
        b_pickled: Optional[List[bytes]] = None
        hb_pickled: Optional[List[bytes]] = None
        previous_generators: List[Optional[bytes]] = []
        for block in blocks_to_validate:
            # We ONLY add blocks which are in the past, based on header hashes (which are validated later) to the
            # prev blocks dict. This is important since these blocks are assumed to be valid and are used as previous
            # generator references
            prev_blocks_dict: Dict[uint32, Union[FullBlock, HeaderBlock]] = {}
            curr_b: Union[FullBlock, HeaderBlock] = block

            while curr_b.prev_header_hash in block_dict:
                curr_b = block_dict[curr_b.prev_header_hash]
                prev_blocks_dict[curr_b.header_hash] = curr_b

            if isinstance(block, FullBlock):
                assert get_block_generator is not None
                if b_pickled is None:
                    b_pickled = []
                b_pickled.append(bytes(block))
                try:
                    block_generator: Optional[
                        BlockGenerator] = await get_block_generator(
                            block, prev_blocks_dict)
                except ValueError:
                    return None
                if block_generator is not None:
                    previous_generators.append(bytes(block_generator))
                else:
                    previous_generators.append(None)
            else:
                if hb_pickled is None:
                    hb_pickled = []
                hb_pickled.append(bytes(block))

        futures.append(asyncio.get_running_loop().run_in_executor(
            pool,
            batch_pre_validate_blocks,
            constants_json,
            final_pickled,
            b_pickled,
            hb_pickled,
            previous_generators,
            npc_results_pickled,
            check_filter,
            [diff_ssis[j][0] for j in range(i, end_i)],
            [diff_ssis[j][1] for j in range(i, end_i)],
        ))
    # Collect all results into one flat list
    return [
        PreValidationResult.from_bytes(result)
        for batch_result in (await asyncio.gather(*futures))
        for result in batch_result
    ]
Example #18
0
    async def test_basic_store(self,
                               empty_blockchain,
                               normalized_to_identity: bool = False):
        blockchain = empty_blockchain
        blocks = bt.get_consecutive_blocks(
            10,
            seed=b"1234",
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )

        store = FullNodeStore(test_constants)

        unfinished_blocks = []
        for block in blocks:
            unfinished_blocks.append(
                UnfinishedBlock(
                    block.finished_sub_slots,
                    block.reward_chain_block.get_unfinished(),
                    block.challenge_chain_sp_proof,
                    block.reward_chain_sp_proof,
                    block.foliage,
                    block.foliage_transaction_block,
                    block.transactions_info,
                    block.transactions_generator,
                    [],
                ))

        # Add/get candidate block
        assert store.get_candidate_block(
            unfinished_blocks[0].get_hash()) is None
        for height, unf_block in enumerate(unfinished_blocks):
            store.add_candidate_block(unf_block.get_hash(), uint32(height),
                                      unf_block)

        candidate = store.get_candidate_block(unfinished_blocks[4].get_hash())
        assert candidate is not None
        assert candidate[1] == unfinished_blocks[4]
        store.clear_candidate_blocks_below(uint32(8))
        assert store.get_candidate_block(
            unfinished_blocks[5].get_hash()) is None
        assert store.get_candidate_block(
            unfinished_blocks[8].get_hash()) is not None

        # Test seen unfinished blocks
        h_hash_1 = bytes32(token_bytes(32))
        assert not store.seen_unfinished_block(h_hash_1)
        assert store.seen_unfinished_block(h_hash_1)
        store.clear_seen_unfinished_blocks()
        assert not store.seen_unfinished_block(h_hash_1)

        # Add/get unfinished block
        for height, unf_block in enumerate(unfinished_blocks):
            assert store.get_unfinished_block(unf_block.partial_hash) is None
            store.add_unfinished_block(
                uint32(height), unf_block,
                PreValidationResult(None, uint64(123532), None, False))
            assert store.get_unfinished_block(
                unf_block.partial_hash) == unf_block
            store.remove_unfinished_block(unf_block.partial_hash)
            assert store.get_unfinished_block(unf_block.partial_hash) is None

        blocks = bt.get_consecutive_blocks(
            1,
            skip_slots=5,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
        )
        sub_slots = blocks[0].finished_sub_slots
        assert len(sub_slots) == 5

        assert (store.get_finished_sub_slots(
            BlockCache({}),
            None,
            sub_slots[0].challenge_chain.challenge_chain_end_of_slot_vdf.
            challenge,
        ) == [])
        # Test adding non-connecting sub-slots genesis
        assert store.get_sub_slot(test_constants.GENESIS_CHALLENGE) is None
        assert store.get_sub_slot(
            sub_slots[0].challenge_chain.get_hash()) is None
        assert store.get_sub_slot(
            sub_slots[1].challenge_chain.get_hash()) is None
        assert store.new_finished_sub_slot(sub_slots[1], blockchain, None,
                                           None) is None
        assert store.new_finished_sub_slot(sub_slots[2], blockchain, None,
                                           None) is None

        # Test adding sub-slots after genesis
        assert store.new_finished_sub_slot(sub_slots[0], blockchain, None,
                                           None) is not None
        sub_slot = store.get_sub_slot(sub_slots[0].challenge_chain.get_hash())
        assert sub_slot is not None
        assert sub_slot[0] == sub_slots[0]
        assert store.get_sub_slot(
            sub_slots[1].challenge_chain.get_hash()) is None
        assert store.new_finished_sub_slot(sub_slots[1], blockchain, None,
                                           None) is not None
        for i in range(len(sub_slots)):
            assert store.new_finished_sub_slot(sub_slots[i], blockchain, None,
                                               None) is not None
            slot_i = store.get_sub_slot(
                sub_slots[i].challenge_chain.get_hash())
            assert slot_i is not None
            assert slot_i[0] == sub_slots[i]

        assert store.get_finished_sub_slots(
            BlockCache({}), None,
            sub_slots[-1].challenge_chain.get_hash()) == sub_slots
        assert store.get_finished_sub_slots(BlockCache(
            {}), None, std_hash(b"not a valid hash")) is None

        assert (store.get_finished_sub_slots(
            BlockCache({}), None,
            sub_slots[-2].challenge_chain.get_hash()) == sub_slots[:-1])

        # Test adding genesis peak
        await _validate_and_add_block(blockchain, blocks[0])
        peak = blockchain.get_peak()
        peak_full_block = await blockchain.get_full_peak()
        if peak.overflow:
            store.new_peak(peak, peak_full_block, sub_slots[-2], sub_slots[-1],
                           None, blockchain)
        else:
            store.new_peak(peak, peak_full_block, None, sub_slots[-1], None,
                           blockchain)

        assert store.get_sub_slot(
            sub_slots[0].challenge_chain.get_hash()) is None
        assert store.get_sub_slot(
            sub_slots[1].challenge_chain.get_hash()) is None
        assert store.get_sub_slot(
            sub_slots[2].challenge_chain.get_hash()) is None
        if peak.overflow:
            slot_3 = store.get_sub_slot(
                sub_slots[3].challenge_chain.get_hash())
            assert slot_3 is not None
            assert slot_3[0] == sub_slots[3]
        else:
            assert store.get_sub_slot(
                sub_slots[3].challenge_chain.get_hash()) is None

        slot_4 = store.get_sub_slot(sub_slots[4].challenge_chain.get_hash())
        assert slot_4 is not None
        assert slot_4[0] == sub_slots[4]

        assert (store.get_finished_sub_slots(
            blockchain,
            peak,
            sub_slots[-1].challenge_chain.get_hash(),
        ) == [])

        # Test adding non genesis peak directly
        blocks = bt.get_consecutive_blocks(
            2,
            skip_slots=2,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        blocks = bt.get_consecutive_blocks(
            3,
            block_list_input=blocks,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        for block in blocks:
            await _validate_and_add_block_no_error(blockchain, block)
            sb = blockchain.block_record(block.header_hash)
            sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
                block.header_hash)
            res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, None,
                                 blockchain)
            assert res.added_eos is None

        # Add reorg blocks
        blocks_reorg = bt.get_consecutive_blocks(
            20,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        for block in blocks_reorg:
            peak = blockchain.get_peak()
            assert peak is not None

            await _validate_and_add_block_no_error(blockchain, block)

            if blockchain.get_peak().header_hash == block.header_hash:
                sb = blockchain.block_record(block.header_hash)
                fork = find_fork_point_in_chain(
                    blockchain, peak, blockchain.block_record(sb.header_hash))
                if fork > 0:
                    fork_block = blockchain.height_to_block_record(fork)
                else:
                    fork_block = None
                sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
                    block.header_hash)
                res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot,
                                     fork_block, blockchain)
                assert res.added_eos is None

        # Add slots to the end
        blocks_2 = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks_reorg,
            skip_slots=2,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        for slot in blocks_2[-1].finished_sub_slots:
            store.new_finished_sub_slot(slot, blockchain,
                                        blockchain.get_peak(), await
                                        blockchain.get_full_peak())

        assert store.get_sub_slot(
            sub_slots[3].challenge_chain.get_hash()) is None
        assert store.get_sub_slot(
            sub_slots[4].challenge_chain.get_hash()) is None

        # Test adding signage point
        peak = blockchain.get_peak()
        ss_start_iters = peak.ip_sub_slot_total_iters(test_constants)
        for i in range(
                1, test_constants.NUM_SPS_SUB_SLOT -
                test_constants.NUM_SP_INTERVALS_EXTRA):
            sp = get_signage_point(
                test_constants,
                blockchain,
                peak,
                ss_start_iters,
                uint8(i),
                [],
                peak.sub_slot_iters,
            )
            assert store.new_signage_point(uint8(i), blockchain, peak,
                                           peak.sub_slot_iters, sp)

        blocks = blocks_reorg
        while True:
            blocks = bt.get_consecutive_blocks(
                1,
                block_list_input=blocks,
                normalized_to_identity_cc_eos=normalized_to_identity,
                normalized_to_identity_icc_eos=normalized_to_identity,
                normalized_to_identity_cc_ip=normalized_to_identity,
                normalized_to_identity_cc_sp=normalized_to_identity,
            )
            await _validate_and_add_block(blockchain, blocks[-1])
            if blockchain.get_peak().header_hash == blocks[-1].header_hash:
                sb = blockchain.block_record(blocks[-1].header_hash)
                fork = find_fork_point_in_chain(
                    blockchain, peak, blockchain.block_record(sb.header_hash))
                if fork > 0:
                    fork_block = blockchain.height_to_block_record(fork)
                else:
                    fork_block = None
                sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
                    blocks[-1].header_hash)

                res = store.new_peak(sb, blocks[-1], sp_sub_slot, ip_sub_slot,
                                     fork_block, blockchain)
                assert res.added_eos is None
                if sb.overflow and sp_sub_slot is not None:
                    assert sp_sub_slot != ip_sub_slot
                    break

        peak = blockchain.get_peak()
        assert peak.overflow
        # Overflow peak should result in 2 finished sub slots
        assert len(store.finished_sub_slots) == 2

        # Add slots to the end, except for the last one, which we will use to test invalid SP
        blocks_2 = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks,
            skip_slots=3,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        for slot in blocks_2[-1].finished_sub_slots[:-1]:
            store.new_finished_sub_slot(slot, blockchain,
                                        blockchain.get_peak(), await
                                        blockchain.get_full_peak())
        finished_sub_slots = blocks_2[-1].finished_sub_slots
        assert len(store.finished_sub_slots) == 4

        # Test adding signage points for overflow blocks (sp_sub_slot)
        ss_start_iters = peak.sp_sub_slot_total_iters(test_constants)
        # for i in range(peak.signage_point_index, test_constants.NUM_SPS_SUB_SLOT):
        #     if i < peak.signage_point_index:
        #         continue
        #     latest = peak
        #     while latest.total_iters > peak.sp_total_iters(test_constants):
        #         latest = blockchain.blocks[latest.prev_hash]
        #     sp = get_signage_point(
        #         test_constants,
        #         blockchain.blocks,
        #         latest,
        #         ss_start_iters,
        #         uint8(i),
        #         [],
        #         peak.sub_slot_iters,
        #     )
        #     assert store.new_signage_point(i, blockchain.blocks, peak, peak.sub_slot_iters, sp)

        # Test adding signage points for overflow blocks (ip_sub_slot)
        for i in range(
                1, test_constants.NUM_SPS_SUB_SLOT -
                test_constants.NUM_SP_INTERVALS_EXTRA):
            sp = get_signage_point(
                test_constants,
                blockchain,
                peak,
                peak.ip_sub_slot_total_iters(test_constants),
                uint8(i),
                [],
                peak.sub_slot_iters,
            )
            assert store.new_signage_point(uint8(i), blockchain, peak,
                                           peak.sub_slot_iters, sp)

        # Test adding future signage point, a few slots forward (good)
        saved_sp_hash = None
        for slot_offset in range(1, len(finished_sub_slots)):
            for i in range(
                    1,
                    test_constants.NUM_SPS_SUB_SLOT -
                    test_constants.NUM_SP_INTERVALS_EXTRA,
            ):
                sp = get_signage_point(
                    test_constants,
                    blockchain,
                    peak,
                    peak.ip_sub_slot_total_iters(test_constants) +
                    slot_offset * peak.sub_slot_iters,
                    uint8(i),
                    finished_sub_slots[:slot_offset],
                    peak.sub_slot_iters,
                )
                assert sp.cc_vdf is not None
                saved_sp_hash = sp.cc_vdf.output.get_hash()
                assert store.new_signage_point(uint8(i), blockchain, peak,
                                               peak.sub_slot_iters, sp)

        # Test adding future signage point (bad)
        for i in range(
                1, test_constants.NUM_SPS_SUB_SLOT -
                test_constants.NUM_SP_INTERVALS_EXTRA):
            sp = get_signage_point(
                test_constants,
                blockchain,
                peak,
                peak.ip_sub_slot_total_iters(test_constants) +
                len(finished_sub_slots) * peak.sub_slot_iters,
                uint8(i),
                finished_sub_slots[:len(finished_sub_slots)],
                peak.sub_slot_iters,
            )
            assert not store.new_signage_point(uint8(i), blockchain, peak,
                                               peak.sub_slot_iters, sp)

        # Test adding past signage point
        sp = SignagePoint(
            blocks[1].reward_chain_block.challenge_chain_sp_vdf,
            blocks[1].challenge_chain_sp_proof,
            blocks[1].reward_chain_block.reward_chain_sp_vdf,
            blocks[1].reward_chain_sp_proof,
        )
        assert not store.new_signage_point(
            blocks[1].reward_chain_block.signage_point_index,
            blockchain,
            peak,
            blockchain.block_record(
                blocks[1].header_hash).sp_sub_slot_total_iters(test_constants),
            sp,
        )

        # Get signage point by index
        assert (store.get_signage_point_by_index(
            finished_sub_slots[0].challenge_chain.get_hash(),
            uint8(4),
            finished_sub_slots[0].reward_chain.get_hash(),
        ) is not None)

        assert (store.get_signage_point_by_index(
            finished_sub_slots[0].challenge_chain.get_hash(), uint8(4),
            std_hash(b"1")) is None)

        # Get signage point by hash
        # TODO: address hint error and remove ignore
        #       error: Argument 1 to "get_signage_point" of "FullNodeStore" has incompatible type "Optional[bytes32]";
        #       expected "bytes32"  [arg-type]
        assert store.get_signage_point(
            saved_sp_hash) is not None  # type: ignore[arg-type]
        assert store.get_signage_point(std_hash(b"2")) is None

        # Test adding signage points before genesis
        store.initialize_genesis_sub_slot()
        assert len(store.finished_sub_slots) == 1
        for i in range(
                1, test_constants.NUM_SPS_SUB_SLOT -
                test_constants.NUM_SP_INTERVALS_EXTRA):
            sp = get_signage_point(
                test_constants,
                BlockCache({}, {}),
                None,
                uint128(0),
                uint8(i),
                [],
                peak.sub_slot_iters,
            )
            assert store.new_signage_point(uint8(i), blockchain, None,
                                           peak.sub_slot_iters, sp)

        blocks_3 = bt.get_consecutive_blocks(
            1,
            skip_slots=2,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        for slot in blocks_3[-1].finished_sub_slots:
            store.new_finished_sub_slot(slot, blockchain, None, None)
        assert len(store.finished_sub_slots) == 3
        finished_sub_slots = blocks_3[-1].finished_sub_slots

        for slot_offset in range(1, len(finished_sub_slots) + 1):
            for i in range(
                    1,
                    test_constants.NUM_SPS_SUB_SLOT -
                    test_constants.NUM_SP_INTERVALS_EXTRA,
            ):
                sp = get_signage_point(
                    test_constants,
                    BlockCache({}, {}),
                    None,
                    slot_offset * peak.sub_slot_iters,
                    uint8(i),
                    finished_sub_slots[:slot_offset],
                    peak.sub_slot_iters,
                )
                assert store.new_signage_point(uint8(i), blockchain, None,
                                               peak.sub_slot_iters, sp)

        # Test adding signage points after genesis
        blocks_4 = bt.get_consecutive_blocks(
            1,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        blocks_5 = bt.get_consecutive_blocks(
            1,
            block_list_input=blocks_4,
            skip_slots=1,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )

        # If this is not the case, fix test to find a block that is
        assert (blocks_4[-1].reward_chain_block.signage_point_index <
                test_constants.NUM_SPS_SUB_SLOT -
                test_constants.NUM_SP_INTERVALS_EXTRA)
        await _validate_and_add_block(
            blockchain,
            blocks_4[-1],
            expected_result=ReceiveBlockResult.ADDED_AS_ORPHAN)

        sb = blockchain.block_record(blocks_4[-1].header_hash)
        store.new_peak(sb, blocks_4[-1], None, None, None, blockchain)
        for i in range(
                sb.signage_point_index + test_constants.NUM_SP_INTERVALS_EXTRA,
                test_constants.NUM_SPS_SUB_SLOT,
        ):
            if is_overflow_block(test_constants, uint8(i)):
                finished_sub_slots = blocks_5[-1].finished_sub_slots
            else:
                finished_sub_slots = []

            sp = get_signage_point(
                test_constants,
                blockchain,
                sb,
                uint128(0),
                uint8(i),
                finished_sub_slots,
                peak.sub_slot_iters,
            )
            assert store.new_signage_point(uint8(i), empty_blockchain, sb,
                                           peak.sub_slot_iters, sp)

        # Test future EOS cache
        store.initialize_genesis_sub_slot()
        blocks = bt.get_consecutive_blocks(
            1,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
        )
        await _validate_and_add_block_no_error(blockchain, blocks[-1])
        while True:
            blocks = bt.get_consecutive_blocks(
                1,
                block_list_input=blocks,
                normalized_to_identity_cc_eos=normalized_to_identity,
                normalized_to_identity_icc_eos=normalized_to_identity,
                normalized_to_identity_cc_ip=normalized_to_identity,
                normalized_to_identity_cc_sp=normalized_to_identity,
            )
            await _validate_and_add_block_no_error(blockchain, blocks[-1])
            sb = blockchain.block_record(blocks[-1].header_hash)
            if sb.first_in_sub_slot:
                break
        assert len(blocks) >= 2
        dependant_sub_slots = blocks[-1].finished_sub_slots
        peak = blockchain.get_peak()
        peak_full_block = await blockchain.get_full_peak()
        for block in blocks[:-2]:
            sb = blockchain.block_record(block.header_hash)
            sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
                block.header_hash)
            peak = sb
            peak_full_block = block
            res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, None,
                                 blockchain)
            assert res.added_eos is None

        assert store.new_finished_sub_slot(dependant_sub_slots[0], blockchain,
                                           peak, peak_full_block) is None
        block = blocks[-2]
        sb = blockchain.block_record(block.header_hash)
        sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
            block.header_hash)
        res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, None,
                             blockchain)
        assert res.added_eos == dependant_sub_slots[0]
        assert res.new_signage_points == res.new_infusion_points == []

        # Test future IP cache
        store.initialize_genesis_sub_slot()
        blocks = bt.get_consecutive_blocks(
            60,
            normalized_to_identity_cc_ip=normalized_to_identity,
            normalized_to_identity_cc_sp=normalized_to_identity,
            normalized_to_identity_cc_eos=normalized_to_identity,
            normalized_to_identity_icc_eos=normalized_to_identity,
        )

        for block in blocks[:5]:
            await _validate_and_add_block_no_error(blockchain, block)
            sb = blockchain.block_record(block.header_hash)

            sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
                block.header_hash)
            res = store.new_peak(sb, block, sp_sub_slot, ip_sub_slot, None,
                                 blockchain)
            assert res.added_eos is None

        case_0, case_1 = False, False
        for i in range(5, len(blocks) - 1):
            prev_block = blocks[i]
            block = blocks[i + 1]
            new_ip = NewInfusionPointVDF(
                block.reward_chain_block.get_unfinished().get_hash(),
                block.reward_chain_block.challenge_chain_ip_vdf,
                block.challenge_chain_ip_proof,
                block.reward_chain_block.reward_chain_ip_vdf,
                block.reward_chain_ip_proof,
                block.reward_chain_block.infused_challenge_chain_ip_vdf,
                block.infused_challenge_chain_ip_proof,
            )
            store.add_to_future_ip(new_ip)

            await _validate_and_add_block_no_error(blockchain, prev_block)
            sp_sub_slot, ip_sub_slot = await blockchain.get_sp_and_ip_sub_slots(
                prev_block.header_hash)
            sb = blockchain.block_record(prev_block.header_hash)
            res = store.new_peak(sb, prev_block, sp_sub_slot, ip_sub_slot,
                                 None, blockchain)
            if len(block.finished_sub_slots) == 0:
                case_0 = True
                assert res.new_infusion_points == [new_ip]
            else:
                case_1 = True
                assert res.new_infusion_points == []
                found_ips: List[timelord_protocol.NewInfusionPointVDF] = []
                for ss in block.finished_sub_slots:
                    ipvdf = store.new_finished_sub_slot(
                        ss, blockchain, sb, prev_block)
                    assert ipvdf is not None
                    found_ips += ipvdf
                assert found_ips == [new_ip]

        # If flaky, increase the number of blocks created
        assert case_0 and case_1

        # Try to get two blocks in the same slot, such that we have
        # SP, B2 SP .... SP B1
        #     i2 .........  i1
        # Then do a reorg up to B2, removing all signage points after B2, but not before
        log.warning(f"Adding blocks up to {blocks[-1]}")
        for block in blocks:
            await _validate_and_add_block_no_error(blockchain, block)

        log.warning(f"Starting loop")
        while True:
            log.warning("Looping")
            blocks = bt.get_consecutive_blocks(1,
                                               block_list_input=blocks,
                                               skip_slots=1)
            await _validate_and_add_block_no_error(blockchain, blocks[-1])
            peak = blockchain.get_peak()
            sub_slots = await blockchain.get_sp_and_ip_sub_slots(
                peak.header_hash)
            store.new_peak(peak, blocks[-1], sub_slots[0], sub_slots[1], None,
                           blockchain)

            blocks = bt.get_consecutive_blocks(
                2, block_list_input=blocks, guarantee_transaction_block=True)

            i3 = blocks[-3].reward_chain_block.signage_point_index
            i2 = blocks[-2].reward_chain_block.signage_point_index
            i1 = blocks[-1].reward_chain_block.signage_point_index
            if (len(blocks[-2].finished_sub_slots) == len(
                    blocks[-1].finished_sub_slots) == 0
                    and not is_overflow_block(test_constants,
                                              signage_point_index=i2)
                    and not is_overflow_block(test_constants,
                                              signage_point_index=i1)
                    and i2 > i3 + 3 and i1 > (i2 + 3)):
                # We hit all the conditions that we want
                all_sps: List[Optional[SignagePoint]] = [
                    None
                ] * test_constants.NUM_SPS_SUB_SLOT

                def assert_sp_none(sp_index: int, is_none: bool):
                    sp_to_check: Optional[SignagePoint] = all_sps[sp_index]
                    assert sp_to_check is not None
                    assert sp_to_check.cc_vdf is not None
                    fetched = store.get_signage_point(
                        sp_to_check.cc_vdf.output.get_hash())
                    assert (fetched is None) == is_none
                    if fetched is not None:
                        assert fetched == sp_to_check

                for i in range(i3 + 1, test_constants.NUM_SPS_SUB_SLOT - 3):
                    finished_sub_slots = []
                    sp = get_signage_point(
                        test_constants,
                        blockchain,
                        peak,
                        uint128(peak.ip_sub_slot_total_iters(bt.constants)),
                        uint8(i),
                        finished_sub_slots,
                        peak.sub_slot_iters,
                    )
                    all_sps[i] = sp
                    assert store.new_signage_point(uint8(i), blockchain, peak,
                                                   peak.sub_slot_iters, sp)

                # Adding a new peak clears all SPs after that peak
                await _validate_and_add_block_no_error(blockchain, blocks[-2])
                peak = blockchain.get_peak()
                sub_slots = await blockchain.get_sp_and_ip_sub_slots(
                    peak.header_hash)
                store.new_peak(peak, blocks[-2], sub_slots[0], sub_slots[1],
                               None, blockchain)

                assert_sp_none(i2, False)
                assert_sp_none(i2 + 1, False)
                assert_sp_none(i1, True)
                assert_sp_none(i1 + 1, True)
                assert_sp_none(i1 + 4, True)

                for i in range(i2, test_constants.NUM_SPS_SUB_SLOT):
                    if is_overflow_block(test_constants, uint8(i)):
                        blocks_alt = bt.get_consecutive_blocks(
                            1, block_list_input=blocks[:-1], skip_slots=1)
                        finished_sub_slots = blocks_alt[-1].finished_sub_slots
                    else:
                        finished_sub_slots = []
                    sp = get_signage_point(
                        test_constants,
                        blockchain,
                        peak,
                        uint128(peak.ip_sub_slot_total_iters(bt.constants)),
                        uint8(i),
                        finished_sub_slots,
                        peak.sub_slot_iters,
                    )
                    all_sps[i] = sp
                    assert store.new_signage_point(uint8(i), blockchain, peak,
                                                   peak.sub_slot_iters, sp)

                assert_sp_none(i2, False)
                assert_sp_none(i2 + 1, False)
                assert_sp_none(i1, False)
                assert_sp_none(i1 + 1, False)
                assert_sp_none(i1 + 4, False)

                await _validate_and_add_block_no_error(blockchain, blocks[-1])
                peak = blockchain.get_peak()
                sub_slots = await blockchain.get_sp_and_ip_sub_slots(
                    peak.header_hash)

                # Do a reorg, which should remove everything after B2
                store.new_peak(
                    peak,
                    blocks[-1],
                    sub_slots[0],
                    sub_slots[1],
                    (await
                     blockchain.get_block_records_at([blocks[-2].height]))[0],
                    blockchain,
                )

                assert_sp_none(i2, False)
                assert_sp_none(i2 + 1, False)
                assert_sp_none(i1, True)
                assert_sp_none(i1 + 1, True)
                assert_sp_none(i1 + 4, True)
                break
            else:
                for block in blocks[-2:]:
                    await _validate_and_add_block_no_error(blockchain, block)
def batch_pre_validate_blocks(
    constants_dict: Dict,
    blocks_pickled: Dict[bytes, bytes],
    full_blocks_pickled: Optional[List[bytes]],
    header_blocks_pickled: Optional[List[bytes]],
    prev_transaction_generators: List[Optional[bytes]],
    npc_results: Dict[uint32, bytes],
    check_filter: bool,
    expected_difficulty: List[uint64],
    expected_sub_slot_iters: List[uint64],
    validate_signatures: bool,
) -> List[bytes]:
    blocks: Dict[bytes, BlockRecord] = {}
    for k, v in blocks_pickled.items():
        blocks[k] = BlockRecord.from_bytes(v)
    results: List[PreValidationResult] = []
    constants: ConsensusConstants = dataclass_from_dict(
        ConsensusConstants, constants_dict)
    if full_blocks_pickled is not None and header_blocks_pickled is not None:
        assert ValueError("Only one should be passed here")

    # In this case, we are validating full blocks, not headers
    if full_blocks_pickled is not None:
        for i in range(len(full_blocks_pickled)):
            try:
                block: FullBlock = FullBlock.from_bytes(full_blocks_pickled[i])
                tx_additions: List[Coin] = []
                removals: List[bytes32] = []
                npc_result: Optional[NPCResult] = None
                if block.height in npc_results:
                    npc_result = NPCResult.from_bytes(
                        npc_results[block.height])
                    assert npc_result is not None
                    if npc_result.npc_list is not None:
                        removals, tx_additions = tx_removals_and_additions(
                            npc_result.npc_list)
                    else:
                        removals, tx_additions = [], []

                if block.transactions_generator is not None and npc_result is None:
                    prev_generator_bytes = prev_transaction_generators[i]
                    assert prev_generator_bytes is not None
                    assert block.transactions_info is not None
                    block_generator: BlockGenerator = BlockGenerator.from_bytes(
                        prev_generator_bytes)
                    assert block_generator.program == block.transactions_generator
                    npc_result = get_name_puzzle_conditions(
                        block_generator,
                        min(constants.MAX_BLOCK_COST_CLVM,
                            block.transactions_info.cost),
                        cost_per_byte=constants.COST_PER_BYTE,
                        mempool_mode=False,
                        height=block.height,
                    )
                    removals, tx_additions = tx_removals_and_additions(
                        npc_result.npc_list)
                if npc_result is not None and npc_result.error is not None:
                    results.append(
                        PreValidationResult(uint16(npc_result.error), None,
                                            npc_result, False))
                    continue

                header_block = get_block_header(block, tx_additions, removals)
                # TODO: address hint error and remove ignore
                #       error: Argument 1 to "BlockCache" has incompatible type "Dict[bytes, BlockRecord]"; expected
                #       "Dict[bytes32, BlockRecord]"  [arg-type]
                required_iters, error = validate_finished_header_block(
                    constants,
                    BlockCache(blocks),  # type: ignore[arg-type]
                    header_block,
                    check_filter,
                    expected_difficulty[i],
                    expected_sub_slot_iters[i],
                )
                error_int: Optional[uint16] = None
                if error is not None:
                    error_int = uint16(error.code.value)

                successfully_validated_signatures = False
                # If we failed CLVM, no need to validate signature, the block is already invalid
                if error_int is None:

                    # If this is False, it means either we don't have a signature (not a tx block) or we have an invalid
                    # signature (which also puts in an error) or we didn't validate the signature because we want to
                    # validate it later. receive_block will attempt to validate the signature later.
                    if validate_signatures:
                        if npc_result is not None and block.transactions_info is not None:
                            pairs_pks, pairs_msgs = pkm_pairs(
                                npc_result.npc_list,
                                constants.AGG_SIG_ME_ADDITIONAL_DATA)
                            pks_objects: List[G1Element] = [
                                G1Element.from_bytes(pk) for pk in pairs_pks
                            ]
                            if not AugSchemeMPL.aggregate_verify(
                                    pks_objects, pairs_msgs, block.
                                    transactions_info.aggregated_signature):
                                error_int = uint16(
                                    Err.BAD_AGGREGATE_SIGNATURE.value)
                            else:
                                successfully_validated_signatures = True

                results.append(
                    PreValidationResult(error_int, required_iters, npc_result,
                                        successfully_validated_signatures))
            except Exception:
                error_stack = traceback.format_exc()
                log.error(f"Exception: {error_stack}")
                results.append(
                    PreValidationResult(uint16(Err.UNKNOWN.value), None, None,
                                        False))
    # In this case, we are validating header blocks
    elif header_blocks_pickled is not None:
        for i in range(len(header_blocks_pickled)):
            try:
                header_block = HeaderBlock.from_bytes(header_blocks_pickled[i])
                # TODO: address hint error and remove ignore
                #       error: Argument 1 to "BlockCache" has incompatible type "Dict[bytes, BlockRecord]"; expected
                #       "Dict[bytes32, BlockRecord]"  [arg-type]
                required_iters, error = validate_finished_header_block(
                    constants,
                    BlockCache(blocks),  # type: ignore[arg-type]
                    header_block,
                    check_filter,
                    expected_difficulty[i],
                    expected_sub_slot_iters[i],
                )
                error_int = None
                if error is not None:
                    error_int = uint16(error.code.value)
                results.append(
                    PreValidationResult(error_int, required_iters, None,
                                        False))
            except Exception:
                error_stack = traceback.format_exc()
                log.error(f"Exception: {error_stack}")
                results.append(
                    PreValidationResult(uint16(Err.UNKNOWN.value), None, None,
                                        False))
    return [bytes(r) for r in results]