예제 #1
0
 async def test_clvm_strict_mode(self):
     block = Program.from_bytes(bytes(SMALL_BLOCK_GENERATOR))
     disassembly = binutils.disassemble(block)
     # this is a valid generator program except the first clvm
     # if-condition, that depends on executing an unknown operator
     # ("0xfe"). In strict mode, this should fail, but in non-strict
     # mode, the unknown operator should be treated as if it returns ().
     program = SerializedProgram.from_bytes(
         binutils.assemble(
             f"(i (0xfe (q . 0)) (q . ()) {disassembly})").as_bin())
     error, npc_list, cost = get_name_puzzle_conditions(program, True)
     assert error is not None
     error, npc_list, cost = get_name_puzzle_conditions(program, False)
     assert error is None
    async def test_basics(self):
        wallet_tool = bt.get_pool_wallet_tool()

        num_blocks = 2
        blocks = bt.get_consecutive_blocks(
            test_constants,
            num_blocks,
            [],
            10,
        )

        spend_bundle = wallet_tool.generate_signed_transaction(
            blocks[1].get_coinbase().amount,
            BURN_PUZZLE_HASH,
            blocks[1].get_coinbase(),
        )
        assert spend_bundle is not None
        program = best_solution_program(spend_bundle)

        ratio = test_constants.CLVM_COST_RATIO_CONSTANT

        error, npc_list, clvm_cost = calculate_cost_of_program(program, ratio)

        error, npc_list, cost = get_name_puzzle_conditions(program)

        # Create condition + agg_sig_condition + length + cpu_cost
        assert (clvm_cost == 200 * ratio + 20 * ratio +
                len(bytes(program)) * ratio + cost)
예제 #3
0
    async def test_strict_mode(self):
        wallet_tool = bt.get_pool_wallet_tool()
        ph = wallet_tool.get_new_puzzlehash()

        num_blocks = 3
        blocks = bt.get_consecutive_blocks(num_blocks, [],
                                           guarantee_transaction_block=True,
                                           pool_reward_puzzle_hash=ph,
                                           farmer_reward_puzzle_hash=ph)

        coinbase = None
        for coin in blocks[2].get_included_reward_coins():
            if coin.puzzle_hash == ph:
                coinbase = coin
                break
        assert coinbase is not None
        spend_bundle = wallet_tool.generate_signed_transaction(
            coinbase.amount,
            BURN_PUZZLE_HASH,
            coinbase,
        )
        assert spend_bundle is not None
        program = SerializedProgram.from_bytes(
            binutils.assemble(
                "(q . ((0x3d2331635a58c0d49912bc1427d7db51afe3f20a7b4bcaffa17ee250dcbcbfaa"
                " (((c (q . ((c (q . ((c (i 11 (q . ((c (i (= 5 (point_add 11"
                " (pubkey_for_exp (sha256 11 ((c 6 (c 2 (c 23 (q . ())))))))))"
                " (q . ((c 23 47))) (q . (x))) 1))) (q . (c (c 4 (c 5 (c ((c 6 (c 2"
                " (c 23 (q . ()))))) (q . ())))) ((c 23 47))))) 1))) (c (q . (57 (c"
                " (i (l 5) (q . (sha256 (q . 2) ((c 6 (c 2 (c 9 (q . ()))))) ((c 6 (c"
                " 2 (c 13 (q . ()))))))) (q . (sha256 (q . 1) 5))) 1))) 1)))) (c"
                " (q . 0x88bc9360319e7c54ab42e19e974288a2d7a817976f7633f4b43"
                "f36ce72074e59c4ab8ddac362202f3e366f0aebbb6280)"
                ' 1))) (() (q . ((65 "00000000000000000000000000000000" 0x0cbba106e000))) ())))))'
            ).as_bin())
        error, npc_list, cost = get_name_puzzle_conditions(program, True)
        assert error is not None
        error, npc_list, cost = get_name_puzzle_conditions(program, False)
        assert error is None

        coin_name = npc_list[0].coin_name
        error, puzzle, solution = get_puzzle_and_solution_for_coin(
            program, coin_name)
        assert error is None
예제 #4
0
    async def test_strict_mode(self):
        wallet_tool = bt.get_pool_wallet_tool()
        ph = wallet_tool.get_new_puzzlehash()

        num_blocks = 3
        blocks = bt.get_consecutive_blocks(num_blocks, [],
                                           guarantee_transaction_block=True,
                                           pool_reward_puzzle_hash=ph,
                                           farmer_reward_puzzle_hash=ph)

        coinbase = None
        for coin in blocks[2].get_included_reward_coins():
            if coin.puzzle_hash == ph:
                coinbase = coin
                break
        assert coinbase is not None
        spend_bundle = wallet_tool.generate_signed_transaction(
            coinbase.amount,
            BURN_PUZZLE_HASH,
            coinbase,
        )
        assert spend_bundle is not None

        pk = bytes.fromhex(
            "88bc9360319e7c54ab42e19e974288a2d7a817976f7633f4b43f36ce72074e59c4ab8ddac362202f3e366f0aebbb6280"
        )
        puzzle = p2_delegated_puzzle_or_hidden_puzzle.puzzle_for_pk(pk)
        disassembly = binutils.disassemble(puzzle)
        program = SerializedProgram.from_bytes(
            binutils.assemble(
                f"(q . (((0x3d2331635a58c0d49912bc1427d7db51afe3f20a7b4bcaffa17ee250dcbcbfaa 300)"
                f" ({disassembly} (() (q . ((65 '00000000000000000000000000000000' 0x0cbba106e000))) ())))))"
            ).as_bin())
        error, npc_list, cost = get_name_puzzle_conditions(program, True)
        assert error is not None
        error, npc_list, cost = get_name_puzzle_conditions(program, False)
        assert error is None

        coin_name = npc_list[0].coin_name
        error, puzzle, solution = get_puzzle_and_solution_for_coin(
            program, coin_name)
        assert error is None
예제 #5
0
def calculate_cost_of_program(program: SerializedProgram,
                              clvm_cost_ratio_constant: int,
                              strict_mode: bool = False) -> CostResult:
    """
    This function calculates the total cost of either a block or a spendbundle
    """
    total_clvm_cost = 0
    error, npc_list, cost = get_name_puzzle_conditions(program, strict_mode)
    if error:
        raise Exception("get_name_puzzle_conditions raised error:" +
                        str(error))
    total_clvm_cost += cost

    # Add cost of conditions
    npc: NPC
    total_vbyte_cost = 0
    for npc in npc_list:
        for condition, cvp_list in npc.condition_dict.items():
            if condition is ConditionOpcode.AGG_SIG or condition is ConditionOpcode.AGG_SIG_ME:
                total_vbyte_cost += len(cvp_list) * ConditionCost.AGG_SIG.value
            elif condition is ConditionOpcode.CREATE_COIN:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.CREATE_COIN.value
            elif condition is ConditionOpcode.ASSERT_TIME_EXCEEDS:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.ASSERT_TIME_EXCEEDS.value
            elif condition is ConditionOpcode.ASSERT_BLOCK_AGE_EXCEEDS:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.ASSERT_BLOCK_AGE_EXCEEDS.value
            elif condition is ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.ASSERT_BLOCK_INDEX_EXCEEDS.value
            elif condition is ConditionOpcode.ASSERT_MY_COIN_ID:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.ASSERT_MY_COIN_ID.value
            elif condition is ConditionOpcode.ASSERT_FEE:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.ASSERT_FEE.value
            elif condition is ConditionOpcode.CREATE_ANNOUNCEMENT:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.CREATE_ANNOUNCEMENT.value
            elif condition is ConditionOpcode.ASSERT_ANNOUNCEMENT:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.ASSERT_ANNOUNCEMENT.value
            else:
                # We ignore unknown conditions in order to allow for future soft forks
                pass

    # Add raw size of the program
    total_vbyte_cost += len(bytes(program))

    total_clvm_cost += total_vbyte_cost * clvm_cost_ratio_constant

    return CostResult(error, npc_list, uint64(total_clvm_cost))
예제 #6
0
def calculate_cost_of_program(
    program: Program,
    clvm_cost_ratio_constant: int,
) -> Tuple[Optional[Err], List[NPC], uint64]:
    """
    This function calculates the total cost of either block or a spendbundle
    """
    total_clvm_cost = 0
    error, npc_list, cost = get_name_puzzle_conditions(program)
    if error:
        raise Exception("get_name_puzzle_conditions raised error" + str(error))
    total_clvm_cost += cost

    # Add cost of conditions
    npc: NPC
    total_vbyte_cost = 0
    for npc in npc_list:
        for condition, cvp_list in npc.condition_dict.items():
            if condition is ConditionOpcode.AGG_SIG:
                total_vbyte_cost += len(cvp_list) * ConditionCost.AGG_SIG.value
            elif condition is ConditionOpcode.CREATE_COIN:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.CREATE_COIN.value
            elif condition is ConditionOpcode.ASSERT_TIME_EXCEEDS:
                total_vbyte_cost += (len(cvp_list) *
                                     ConditionCost.ASSERT_TIME_EXCEEDS.value)
            elif condition is ConditionOpcode.ASSERT_BLOCK_AGE_EXCEEDS:
                total_vbyte_cost += (
                    len(cvp_list) *
                    ConditionCost.ASSERT_BLOCK_AGE_EXCEEDS.value)
            elif condition is ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS:
                total_vbyte_cost += (
                    len(cvp_list) *
                    ConditionCost.ASSERT_BLOCK_INDEX_EXCEEDS.value)
            elif condition is ConditionOpcode.ASSERT_MY_COIN_ID:
                total_vbyte_cost += (len(cvp_list) *
                                     ConditionCost.ASSERT_MY_COIN_ID.value)
            elif condition is ConditionOpcode.ASSERT_COIN_CONSUMED:
                total_vbyte_cost += (len(cvp_list) *
                                     ConditionCost.ASSERT_COIN_CONSUMED.value)
            elif condition is ConditionOpcode.ASSERT_FEE:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.ASSERT_FEE.value
            else:
                # We ignore unknown conditions in order to allow for future soft forks
                pass

    # Add raw size of the program
    total_vbyte_cost += len(bytes(program))

    total_clvm_cost += total_vbyte_cost * clvm_cost_ratio_constant

    return error, npc_list, uint64(total_clvm_cost)
예제 #7
0
    async def test_tx_generator_speed(self, large_txn_hex):
        generator = hexstr_to_bytes(large_txn_hex)
        program = SerializedProgram.from_bytes(generator)

        start_time = time.time()
        err, npc, cost = get_name_puzzle_conditions(program, False)
        end_time = time.time()
        duration = end_time - start_time
        assert err is None
        assert len(npc) == 687
        log.info(f"Time spent: {duration}")

        assert duration < 3
예제 #8
0
    def additions(self) -> List[Coin]:
        additions: List[Coin] = []

        if self.transactions_generator is not None:
            # This should never throw here, block must be valid if it comes to here
            err, npc_list, cost = get_name_puzzle_conditions(self.transactions_generator, False)
            # created coins
            if npc_list is not None:
                additions.extend(additions_for_npc(npc_list))

        additions.extend(self.get_included_reward_coins())

        return additions
예제 #9
0
    async def test_tx_generator_speed(self):
        LARGE_BLOCK_COIN_CONSUMED_COUNT = 687
        generator = large_block_generator(LARGE_BLOCK_COIN_CONSUMED_COUNT)
        program = SerializedProgram.from_bytes(generator)

        start_time = time.time()
        err, npc, cost = get_name_puzzle_conditions(program, False)
        end_time = time.time()
        duration = end_time - start_time
        assert err is None
        assert len(npc) == LARGE_BLOCK_COIN_CONSUMED_COUNT
        log.info(f"Time spent: {duration}")

        assert duration < 3
예제 #10
0
 async def test_clvm_strict_mode(self):
     program = SerializedProgram.from_bytes(
         # this is a valid generator program except the first clvm
         # if-condition, that depends on executing an unknown operator
         # ("0xfe"). In strict mode, this should fail, but in non-strict
         # mode, the unknown operator should be treated as if it returns ().
         binutils.assemble(
             "(i (a (q . 0xfe) (q . ())) (q . ()) "
             "(q . ((0x3d2331635a58c0d49912bc1427d7db51afe3f20a7b4bcaffa17ee250dcbcbfaa"
             " (((c (q . ((c (q . ((c (i 11 (q . ((c (i (= 5 (point_add 11"
             " (pubkey_for_exp (sha256 11 ((c 6 (c 2 (c 23 (q . ())))))))))"
             " (q . ((c 23 47))) (q . (x))) 1))) (q . (c (c 4 (c 5 (c ((c 6 (c 2"
             " (c 23 (q . ()))))) (q . ())))) ((c 23 47))))) 1))) (c (q . (57 (c"
             " (i (l 5) (q . (sha256 (q . 2) ((c 6 (c 2 (c 9 (q . ()))))) ((c 6 (c"
             " 2 (c 13 (q . ()))))))) (q . (sha256 (q . 1) 5))) 1))) 1)))) (c"
             " (q . 0x88bc9360319e7c54ab42e19e974288a2d7a817976f7633f4b43"
             "f36ce72074e59c4ab8ddac362202f3e366f0aebbb6280)"
             ' 1))) (() (q . ((51 "00000000000000000000000000000000" 0x0cbba106e000))) ())))))'
             ")").as_bin())
     error, npc_list, cost = get_name_puzzle_conditions(program, True)
     assert error is not None
     error, npc_list, cost = get_name_puzzle_conditions(program, False)
     assert error is None
예제 #11
0
    def tx_removals_and_additions(self) -> Tuple[List[bytes32], List[Coin]]:
        """
        Doesn't return farmer and pool reward.
        This call assumes that this block has been validated already,
        get_name_puzzle_conditions should not return error here
        """
        removals: List[bytes32] = []
        additions: List[Coin] = []

        if self.transactions_generator is not None:
            # This should never throw here, block must be valid if it comes to here
            err, npc_list, cost = get_name_puzzle_conditions(self.transactions_generator, False)
            # build removals list
            if npc_list is None:
                return [], []
            for npc in npc_list:
                removals.append(npc.coin_name)

            additions.extend(additions_for_npc(npc_list))

        return removals, additions
예제 #12
0
    async def test_basics(self):
        wallet_tool = bt.get_pool_wallet_tool()
        ph = wallet_tool.get_new_puzzlehash()
        num_blocks = 3
        blocks = bt.get_consecutive_blocks(num_blocks, [],
                                           guarantee_transaction_block=True,
                                           pool_reward_puzzle_hash=ph,
                                           farmer_reward_puzzle_hash=ph)
        coinbase = None
        for coin in blocks[2].get_included_reward_coins():
            if coin.puzzle_hash == ph:
                coinbase = coin
                break
        assert coinbase is not None
        spend_bundle = wallet_tool.generate_signed_transaction(
            coinbase.amount,
            BURN_PUZZLE_HASH,
            coinbase,
        )
        assert spend_bundle is not None
        program = best_solution_program(spend_bundle)

        ratio = test_constants.CLVM_COST_RATIO_CONSTANT

        result: CostResult = calculate_cost_of_program(program, ratio)
        clvm_cost = result.cost

        error, npc_list, cost = get_name_puzzle_conditions(program, False)
        assert error is None
        coin_name = npc_list[0].coin_name
        error, puzzle, solution = get_puzzle_and_solution_for_coin(
            program, coin_name)
        assert error is None

        # Create condition + agg_sig_condition + length + cpu_cost
        assert clvm_cost == 200 * ratio + 92 * ratio + len(
            bytes(program)) * ratio + cost
예제 #13
0
    def _create_block(
            self,
            test_constants: ConsensusConstants,
            challenge_hash: bytes32,
            height: uint32,
            prev_header_hash: bytes32,
            prev_iters: uint64,
            prev_weight: uint128,
            timestamp: uint64,
            difficulty: int,
            min_iters: int,
            seed: bytes,
            genesis: bool = False,
            reward_puzzlehash: bytes32 = None,
            transactions: Program = None,
            aggsig: G2Element = None,
            fees: uint64 = uint64(0),
    ) -> FullBlock:
        """
        Creates a block with the specified details. Uses the stored plots to create a proof of space,
        and also evaluates the VDF for the proof of time.
        """
        selected_plot_info = None
        selected_proof_index = 0
        selected_quality: Optional[bytes] = None
        best_quality = 0
        plots = [
            pinfo for _, pinfo in sorted(list(self.plots.items()),
                                         key=lambda x: str(x[0]))
        ]
        if self.use_any_pos:
            random.seed(seed)
            for i in range(len(plots) * 3):
                # Allow passing in seed, to create reorgs and different chains
                seeded_pn = random.randint(0, len(plots) - 1)
                plot_info = plots[seeded_pn]
                plot_id = plot_info.prover.get_id()
                ccp = ProofOfSpace.can_create_proof(
                    plot_id,
                    challenge_hash,
                    test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG,
                )
                if not ccp:
                    continue
                qualities = plot_info.prover.get_qualities_for_challenge(
                    challenge_hash)
                if len(qualities) > 0:
                    selected_plot_info = plot_info
                    selected_quality = qualities[0]
                    break
        else:
            for i in range(len(plots)):
                plot_info = plots[i]
                j = 0
                plot_id = plot_info.prover.get_id()
                ccp = ProofOfSpace.can_create_proof(
                    plot_id,
                    challenge_hash,
                    test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG,
                )
                if not ccp:
                    continue
                qualities = plot_info.prover.get_qualities_for_challenge(
                    challenge_hash)
                for quality in qualities:
                    qual_int = int.from_bytes(quality, "big", signed=False)
                    if qual_int > best_quality:
                        best_quality = qual_int
                        selected_quality = quality
                        selected_plot_info = plot_info
                        selected_proof_index = j
                    j += 1

        assert selected_plot_info is not None
        if selected_quality is None:
            raise RuntimeError("No proofs for this challenge")

        proof_xs: bytes = selected_plot_info.prover.get_full_proof(
            challenge_hash, selected_proof_index)

        plot_pk = ProofOfSpace.generate_plot_public_key(
            selected_plot_info.local_sk.get_g1(),
            selected_plot_info.farmer_public_key,
        )
        proof_of_space: ProofOfSpace = ProofOfSpace(
            challenge_hash,
            selected_plot_info.pool_public_key,
            plot_pk,
            selected_plot_info.prover.get_size(),
            proof_xs,
        )

        number_iters: uint64 = pot_iterations.calculate_iterations(
            proof_of_space,
            difficulty,
            min_iters,
            test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG,
        )
        if self.real_plots:
            print(f"Performing {number_iters} VDF iterations")

        int_size = (test_constants.DISCRIMINANT_SIZE_BITS + 16) >> 4

        result = prove(challenge_hash, test_constants.DISCRIMINANT_SIZE_BITS,
                       number_iters)

        output = ClassgroupElement(
            int512(int.from_bytes(
                result[0:int_size],
                "big",
                signed=True,
            )),
            int512(
                int.from_bytes(
                    result[int_size:2 * int_size],
                    "big",
                    signed=True,
                )),
        )
        proof_bytes = result[2 * int_size:4 * int_size]

        proof_of_time = ProofOfTime(
            challenge_hash,
            number_iters,
            output,
            uint8(0),
            proof_bytes,
        )

        # Use the extension data to create different blocks based on header hash
        extension_data: bytes32 = bytes32(
            [random.randint(0, 255) for _ in range(32)])
        cost = uint64(0)

        fee_reward = uint64(block_rewards.calculate_base_fee(height) + fees)

        std_hash(std_hash(height))

        # Create filter
        byte_array_tx: List[bytes32] = []
        tx_additions: List[Coin] = []
        tx_removals: List[bytes32] = []
        if transactions:
            error, npc_list, _ = get_name_puzzle_conditions(transactions)
            additions: List[Coin] = additions_for_npc(npc_list)
            for coin in additions:
                tx_additions.append(coin)
                byte_array_tx.append(bytearray(coin.puzzle_hash))
            for npc in npc_list:
                tx_removals.append(npc.coin_name)
                byte_array_tx.append(bytearray(npc.coin_name))
        farmer_ph = self.farmer_ph
        pool_ph = self.pool_ph
        if reward_puzzlehash is not None:
            farmer_ph = reward_puzzlehash
            pool_ph = reward_puzzlehash

        byte_array_tx.append(bytearray(farmer_ph))
        byte_array_tx.append(bytearray(pool_ph))
        bip158: PyBIP158 = PyBIP158(byte_array_tx)
        encoded = bytes(bip158.GetEncoded())

        removal_merkle_set = MerkleSet()
        addition_merkle_set = MerkleSet()

        # Create removal Merkle set
        for coin_name in tx_removals:
            removal_merkle_set.add_already_hashed(coin_name)

        # Create addition Merkle set
        puzzlehash_coin_map: Dict[bytes32, List[Coin]] = {}
        cb_reward = calculate_block_reward(height)
        cb_coin = create_coinbase_coin(height, pool_ph, cb_reward)
        fees_coin = create_fees_coin(height, farmer_ph, fee_reward)
        for coin in tx_additions + [cb_coin, fees_coin]:
            if coin.puzzle_hash in puzzlehash_coin_map:
                puzzlehash_coin_map[coin.puzzle_hash].append(coin)
            else:
                puzzlehash_coin_map[coin.puzzle_hash] = [coin]

        # Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash
        for puzzle, coins in puzzlehash_coin_map.items():
            addition_merkle_set.add_already_hashed(puzzle)
            addition_merkle_set.add_already_hashed(hash_coin_list(coins))

        additions_root = addition_merkle_set.get_root()
        removal_root = removal_merkle_set.get_root()

        generator_hash = (transactions.get_tree_hash()
                          if transactions is not None else bytes32([0] * 32))
        filter_hash = std_hash(encoded)

        pool_target = PoolTarget(pool_ph, uint32(height))
        pool_target_signature = self.get_pool_key_signature(
            pool_target, proof_of_space.pool_public_key)
        assert pool_target_signature is not None
        final_aggsig: G2Element = pool_target_signature
        if aggsig is not None:
            final_aggsig = AugSchemeMPL.aggregate([final_aggsig, aggsig])

        header_data: HeaderData = HeaderData(
            height,
            prev_header_hash,
            timestamp,
            filter_hash,
            proof_of_space.get_hash(),
            uint128(prev_weight + difficulty),
            uint64(prev_iters + number_iters),
            additions_root,
            removal_root,
            farmer_ph,
            fee_reward,
            pool_target,
            final_aggsig,
            cost,
            extension_data,
            generator_hash,
        )

        header_hash_sig: G2Element = self.get_plot_signature(
            header_data, plot_pk)

        header: Header = Header(header_data, header_hash_sig)

        full_block: FullBlock = FullBlock(proof_of_space, proof_of_time,
                                          header, transactions, encoded)

        return full_block
예제 #14
0
def create_foliage(
    constants: ConsensusConstants,
    reward_sub_block: RewardChainSubBlockUnfinished,
    spend_bundle: Optional[SpendBundle],
    prev_sub_block: Optional[SubBlockRecord],
    sub_blocks: Dict[bytes32, SubBlockRecord],
    total_iters_sp: uint128,
    timestamp: uint64,
    farmer_reward_puzzlehash: bytes32,
    pool_target: PoolTarget,
    get_plot_signature: Callable[[bytes32, G1Element], G2Element],
    get_pool_signature: Callable[[PoolTarget, G1Element], G2Element],
    seed: bytes32 = b"",
) -> Tuple[FoliageSubBlock, Optional[FoliageBlock], Optional[TransactionsInfo],
           Optional[Program]]:
    """
    Creates a foliage for a given reward chain sub block. This may or may not be a block. In the case of a block,
    the return values are not None. This is called at the signage point, so some of this information may be
    tweaked at the infusion point.

    Args:
        constants: consensus constants being used for this chain
        reward_sub_block: the reward sub block to look at, potentially at the signage point
        spend_bundle: the spend bundle including all transactions
        prev_sub_block: the previous sub-block at the signage point
        sub_blocks: dict from header hash to sub-blocks, of all ancestor sub-blocks
        total_iters_sp: total iters at the signage point
        timestamp: timestamp to put into the foliage block
        farmer_reward_puzzlehash: where to pay out farming reward
        pool_target: where to pay out pool reward
        get_plot_signature: retrieve the signature corresponding to the plot public key
        get_pool_signature: retrieve the signature corresponding to the pool public key
        seed: seed to randomize block

    """

    if prev_sub_block is not None:
        res = get_prev_block(prev_sub_block, sub_blocks, total_iters_sp)
        is_block: bool = res[0]
        prev_block: Optional[SubBlockRecord] = res[1]
    else:
        # Genesis is a block
        prev_block = None
        is_block = True

    random.seed(seed)
    # Use the extension data to create different blocks based on header hash
    extension_data: bytes32 = random.randint(0, 100000000).to_bytes(32, "big")
    if prev_sub_block is None:
        sub_block_height: uint32 = uint32(0)
    else:
        sub_block_height = uint32(prev_sub_block.sub_block_height + 1)

    if prev_block is None:
        sub_height: uint32 = uint32(0)
        height: uint32 = uint32(0)
    else:
        sub_height = uint32(prev_block.sub_block_height + 1)
        prev_is_block = prev_block.is_block
        if prev_is_block:
            height = uint32(prev_block.height + 1)
        else:
            height = uint32(prev_block.height)

    # Create filter
    byte_array_tx: List[bytes32] = []
    tx_additions: List[Coin] = []
    tx_removals: List[bytes32] = []

    pool_target_signature: Optional[G2Element] = get_pool_signature(
        pool_target, reward_sub_block.proof_of_space.pool_public_key)
    assert pool_target_signature is not None

    foliage_sub_block_data = FoliageSubBlockData(
        reward_sub_block.get_hash(),
        pool_target,
        pool_target_signature,
        farmer_reward_puzzlehash,
        extension_data,
    )

    foliage_sub_block_signature: G2Element = get_plot_signature(
        foliage_sub_block_data.get_hash(),
        reward_sub_block.proof_of_space.plot_public_key,
    )

    prev_sub_block_hash: bytes32 = constants.GENESIS_PREV_HASH
    if sub_block_height != 0:
        assert prev_sub_block is not None
        prev_sub_block_hash = prev_sub_block.header_hash

    solution_program: Optional[Program] = None
    if is_block:
        spend_bundle_fees: int = 0
        aggregate_sig: G2Element = G2Element.infinity()
        cost = uint64(0)

        if spend_bundle is not None:
            solution_program = best_solution_program(spend_bundle)
            spend_bundle_fees = spend_bundle.fees()
            aggregate_sig = spend_bundle.aggregated_signature

        # Calculate the cost of transactions
        if solution_program is not None:
            result: CostResult = calculate_cost_of_program(
                solution_program, constants.CLVM_COST_RATIO_CONSTANT)
            cost = result.cost
        # TODO: prev generators root
        reward_claims_incorporated = []
        if sub_height > 0:
            assert prev_block is not None
            assert prev_sub_block is not None
            curr: SubBlockRecord = prev_sub_block
            while not curr.is_block:
                curr = sub_blocks[curr.prev_hash]

            assert curr.fees is not None
            pool_coin = create_pool_coin(
                curr.sub_block_height,
                curr.pool_puzzle_hash,
                calculate_pool_reward(curr.height),
            )

            farmer_coin = create_farmer_coin(
                curr.sub_block_height,
                curr.farmer_puzzle_hash,
                uint64(calculate_base_farmer_reward(curr.height) + curr.fees),
            )
            assert curr.header_hash == prev_block.header_hash
            reward_claims_incorporated += [pool_coin, farmer_coin]

            if curr.sub_block_height > 0:
                curr = sub_blocks[curr.prev_hash]
                # Prev block is not genesis
                while not curr.is_block:
                    pool_coin = create_pool_coin(
                        curr.sub_block_height,
                        curr.pool_puzzle_hash,
                        calculate_pool_reward(curr.height),
                    )
                    farmer_coin = create_farmer_coin(
                        curr.sub_block_height,
                        curr.farmer_puzzle_hash,
                        calculate_base_farmer_reward(curr.height),
                    )
                    reward_claims_incorporated += [pool_coin, farmer_coin]
                    curr = sub_blocks[curr.prev_hash]
        additions: List[Coin] = reward_claims_incorporated.copy()
        npc_list = []
        if solution_program is not None:
            error, npc_list, _ = get_name_puzzle_conditions(
                solution_program, False)
            additions += additions_for_npc(npc_list)
        for coin in additions:
            tx_additions.append(coin)
            byte_array_tx.append(bytearray(coin.puzzle_hash))
        for npc in npc_list:
            tx_removals.append(npc.coin_name)
            byte_array_tx.append(bytearray(npc.coin_name))

        bip158: PyBIP158 = PyBIP158(byte_array_tx)
        encoded = bytes(bip158.GetEncoded())

        removal_merkle_set = MerkleSet()
        addition_merkle_set = MerkleSet()

        # Create removal Merkle set
        for coin_name in tx_removals:
            removal_merkle_set.add_already_hashed(coin_name)

        # Create addition Merkle set
        puzzlehash_coin_map: Dict[bytes32, List[Coin]] = {}

        for coin in tx_additions:
            if coin.puzzle_hash in puzzlehash_coin_map:
                puzzlehash_coin_map[coin.puzzle_hash].append(coin)
            else:
                puzzlehash_coin_map[coin.puzzle_hash] = [coin]

        # Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash
        for puzzle, coins in puzzlehash_coin_map.items():
            addition_merkle_set.add_already_hashed(puzzle)
            addition_merkle_set.add_already_hashed(hash_coin_list(coins))

        additions_root = addition_merkle_set.get_root()
        removals_root = removal_merkle_set.get_root()

        generator_hash = solution_program.get_tree_hash(
        ) if solution_program is not None else bytes32([0] * 32)
        filter_hash: bytes32 = std_hash(encoded)

        transactions_info: Optional[TransactionsInfo] = TransactionsInfo(
            bytes([0] * 32),
            generator_hash,
            aggregate_sig,
            uint64(spend_bundle_fees),
            cost,
            reward_claims_incorporated,
        )
        if prev_block is None:
            prev_block_hash: bytes32 = constants.GENESIS_PREV_HASH
        else:
            prev_block_hash = prev_block.header_hash

        assert transactions_info is not None
        foliage_block: Optional[FoliageBlock] = FoliageBlock(
            prev_block_hash,
            timestamp,
            filter_hash,
            additions_root,
            removals_root,
            transactions_info.get_hash(),
            height,
        )
        assert foliage_block is not None
        foliage_block_hash: Optional[bytes32] = foliage_block.get_hash()
        foliage_block_signature: Optional[G2Element] = get_plot_signature(
            foliage_block_hash,
            reward_sub_block.proof_of_space.plot_public_key)
        assert foliage_block_signature is not None
    else:
        foliage_block_hash = None
        foliage_block_signature = None
        foliage_block = None
        transactions_info = None
    assert (foliage_block_hash is None) == (foliage_block_signature is None)

    foliage_sub_block = FoliageSubBlock(
        prev_sub_block_hash,
        reward_sub_block.get_hash(),
        foliage_sub_block_data,
        foliage_sub_block_signature,
        foliage_block_hash,
        foliage_block_signature,
    )

    return foliage_sub_block, foliage_block, transactions_info, solution_program