Пример #1
0
    async def get_spendable_for_index(
        self, index: uint32, wallet_id: int
    ) -> Set[WalletCoinRecord]:
        """
        Returns set of unspent coin records that are not coinbases, or if they are coinbases,
        must have been confirmed at or before index.
        """
        coins = set()

        cursor_coinbase_coins = await self.db_connection.execute(
            "SELECT * from coin_record WHERE spent=? and confirmed_index<=? and wallet_id=? and coinbase=?",
            (0, int(index), wallet_id, 1),
        )

        coinbase_rows = await cursor_coinbase_coins.fetchall()
        await cursor_coinbase_coins.close()

        cursor_regular_coins = await self.db_connection.execute(
            "SELECT * from coin_record WHERE spent=? and wallet_id=? and coinbase=?",
            (0, wallet_id, 0,),
        )

        regular_rows = await cursor_regular_coins.fetchall()
        await cursor_regular_coins.close()

        for row in coinbase_rows + regular_rows:
            coin = Coin(
                bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]
            )
            coins.add(
                WalletCoinRecord(
                    coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]
                )
            )
        return coins
Пример #2
0
 async def get_coin_records_by_spent(
     self, spent: bool, spend_before_height: Optional[uint32] = None
 ) -> Set[WalletCoinRecord]:
     """ Returns set of CoinRecords that have not been spent yet. """
     coins = set()
     if spend_before_height:
         cursor = await self.db_connection.execute(
             "SELECT * from coin_record WHERE spent=? OR spent_index>=?",
             (int(spent), spend_before_height),
         )
     else:
         cursor = await self.db_connection.execute(
             "SELECT * from coin_record WHERE spent=?", (int(spent),)
         )
     rows = await cursor.fetchall()
     await cursor.close()
     for row in rows:
         coin = Coin(
             bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]
         )
         coins.add(
             WalletCoinRecord(
                 coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]
             )
         )
     return coins
    async def get_unspent_coins_for_wallet(self, wallet_id: int) -> Set[WalletCoinRecord]:
        """ Returns set of CoinRecords that have not been spent yet for a wallet. """
        async with self.wallet_cache_lock:
            if wallet_id in self.coin_wallet_record_cache:
                wallet_coins: Dict[bytes32, WalletCoinRecord] = self.coin_wallet_record_cache[wallet_id]
                return set(wallet_coins.values())

            coin_set = set()

            cursor = await self.db_connection.execute(
                "SELECT * from coin_record WHERE spent=0 and wallet_id=?",
                (wallet_id,),
            )
            rows = await cursor.fetchall()
            await cursor.close()
            cache_dict = {}
            for row in rows:
                coin = Coin(bytes32(bytes.fromhex(row[8])), bytes32(bytes.fromhex(row[7])), row[9])
                coin_record = WalletCoinRecord(
                    coin, row[1], row[2], row[3], row[4], row[5], row[6], WalletType(row[10]), row[11]
                )
                coin_set.add(coin_record)
                cache_dict[coin.name()] = coin_record

            self.coin_wallet_record_cache[wallet_id] = cache_dict
            return coin_set
Пример #4
0
 async def get_unspent_coins_at_height(
     self, height: Optional[uint32] = None
 ) -> Set[WalletCoinRecord]:
     """
     Returns set of CoinRecords that have not been spent yet. If a height is specified,
     We can also return coins that were unspent at this height (but maybe spent later).
     Finally, the coins must be confirmed at the height or less.
     """
     coins = set()
     if height is not None:
         cursor = await self.db_connection.execute(
             "SELECT * from coin_record WHERE (spent=? OR spent_index>?) AND confirmed_index<=?",
             (0, height, height),
         )
     else:
         cursor = await self.db_connection.execute(
             "SELECT * from coin_record WHERE spent=?", (0,)
         )
     rows = await cursor.fetchall()
     await cursor.close()
     for row in rows:
         coin = Coin(
             bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]
         )
         coins.add(
             WalletCoinRecord(
                 coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]
             )
         )
     return coins
Пример #5
0
    async def coins_of_interest_added(self, coins: List[Coin],
                                      height: uint32) -> List[Coin]:
        (
            trade_removals,
            trade_additions,
        ) = await self.trade_manager.get_coins_of_interest()
        trade_adds: List[Coin] = []
        sub_block: Optional[
            SubBlockRecord] = await self.blockchain.get_sub_block_from_db(
                self.blockchain.height_to_hash(height))
        assert sub_block is not None

        pool_rewards = set()
        farmer_rewards = set()

        prev = await self.blockchain.get_sub_block_from_db(sub_block.prev_hash)
        # [sub 1] [sub 2] [block 3] [sub 4] [sub 5] [block6]
        # [block 6] will contain rewards for [sub 1] [sub 2] [block 3]
        while prev is not None:
            # step 1 find previous block
            if prev.is_block:
                break
            prev = await self.blockchain.get_sub_block_from_db(prev.prev_hash)

        if prev is not None:
            # include last block
            pool_rewards.add(bytes32(prev.height.to_bytes(32, "big")))
            farmer_rewards.add(std_hash(std_hash(prev.height)))
            prev = await self.blockchain.get_sub_block_from_db(prev.prev_hash)

        while prev is not None:
            # step 2 traverse from previous block to the block before it
            pool_rewards.add(bytes32(prev.height.to_bytes(32, "big")))
            farmer_rewards.add(std_hash(std_hash(prev.height)))
            if prev.is_block:
                break
            prev = await self.blockchain.get_sub_block_from_db(prev.prev_hash)

        for coin in coins:
            if coin.name() in trade_additions:
                trade_adds.append(coin)

            is_coinbase = False
            is_fee_reward = False
            if coin.parent_coin_info in pool_rewards:
                is_coinbase = True
            if coin.parent_coin_info in farmer_rewards:
                is_fee_reward = True

            info = await self.puzzle_store.wallet_info_for_puzzle_hash(
                coin.puzzle_hash)
            if info is not None:
                wallet_id, wallet_type = info
                await self.coin_added(coin, is_coinbase, is_fee_reward,
                                      uint32(wallet_id), wallet_type, height)

        return trade_adds
 async def get_coin_records_by_puzzle_hash(self, puzzle_hash: bytes32) -> List[WalletCoinRecord]:
     """Returns a list of all coin records with the given puzzle hash"""
     coins = set()
     cursor = await self.db_connection.execute("SELECT * from coin_record WHERE puzzle_hash=?", (puzzle_hash.hex(),))
     rows = await cursor.fetchall()
     await cursor.close()
     for row in rows:
         coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7]))
         coins.add(WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]))
     return list(coins)
    async def get_all_coins(self) -> Set[WalletCoinRecord]:
        """ Returns set of all CoinRecords."""
        coins = set()

        cursor = await self.db_connection.execute("SELECT * from coin_record")
        rows = await cursor.fetchall()
        await cursor.close()
        for row in rows:
            coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7]))
            coins.add(WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]))
        return coins
 async def get_coin_record(self, coin_name: bytes32) -> Optional[WalletCoinRecord]:
     """ Returns CoinRecord with specified coin id. """
     if coin_name in self.coin_record_cache:
         return self.coin_record_cache[coin_name]
     cursor = await self.db_connection.execute("SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),))
     row = await cursor.fetchone()
     await cursor.close()
     if row is not None:
         coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7]))
         return WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9])
     return None
    async def get_coin_record_by_coin_id(self, coin_id: bytes32) -> Optional[WalletCoinRecord]:
        """Returns a coin records with the given name, if it exists"""
        cursor = await self.db_connection.execute("SELECT * from coin_record WHERE coin_name=?", (coin_id.hex(),))
        row = await cursor.fetchone()
        await cursor.close()
        if row is None:
            return None

        coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7]))
        coin_record = WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9])
        return coin_record
Пример #10
0
 async def get_unspent_coin_records(self) -> List[CoinRecord]:
     coins = set()
     cursor = await self.coin_record_db.execute(
         "SELECT * from coin_record WHERE spent=0")
     rows = await cursor.fetchall()
     await cursor.close()
     for row in rows:
         coin = Coin(bytes32(bytes.fromhex(row[6])),
                     bytes32(bytes.fromhex(row[5])), row[7])
         coins.add(CoinRecord(coin, row[1], row[2], row[3], row[4], row[8]))
     return list(coins)
Пример #11
0
 async def get_coin_record(self,
                           coin_name: bytes32) -> Optional[CoinRecord]:
     if coin_name.hex() in self.coin_record_cache:
         return self.coin_record_cache[coin_name.hex()]
     cursor = await self.coin_record_db.execute(
         "SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(), ))
     row = await cursor.fetchone()
     await cursor.close()
     if row is not None:
         coin = Coin(bytes32(bytes.fromhex(row[6])),
                     bytes32(bytes.fromhex(row[5])), row[7])
         return CoinRecord(coin, row[1], row[2], row[3], row[4], row[8])
     return None
Пример #12
0
    async def get_coin_records_by_puzzle_hash(
            self, puzzle_hash: bytes32) -> List[CoinRecord]:
        coins = set()
        cursor = await self.coin_record_db.execute(
            "SELECT * from coin_record WHERE puzzle_hash=?",
            (puzzle_hash.hex(), ))
        rows = await cursor.fetchall()

        await cursor.close()
        for row in rows:
            coin = Coin(bytes32(bytes.fromhex(row[6])),
                        bytes32(bytes.fromhex(row[5])), row[7])
            coins.add(CoinRecord(coin, row[1], row[2], row[3], row[4], row[8]))
        return list(coins)
Пример #13
0
def _tree_hash(node: SExp, precalculated: Set[bytes32]) -> bytes32:
    """
    Hash values in `precalculated` are presumed to have been hashed already.
    """
    if node.listp():
        left = _tree_hash(node.first(), precalculated)
        right = _tree_hash(node.rest(), precalculated)
        s = b"\2" + left + right
    else:
        atom = node.as_atom()
        if atom in precalculated:
            return bytes32(atom)
        s = b"\1" + atom
    return bytes32(std_hash(s))
Пример #14
0
 def _tree_hash(self, precalculated: Set[bytes32]) -> bytes32:
     """
     Hash values in `precalculated` are presumed to have been hashed already.
     """
     if self.listp():
         left = self.to(self.first())._tree_hash(precalculated)
         right = self.to(self.rest())._tree_hash(precalculated)
         s = b"\2" + left + right
     else:
         atom = self.as_atom()
         if atom in precalculated:
             return bytes32(atom)
         s = b"\1" + atom
     return bytes32(std_hash(s))
Пример #15
0
def hash_key_pairs_for_conditions_dict(
    conditions_dict: Dict[ConditionOpcode, List[ConditionVarPair]], coin_name: bytes32
) -> List[BLSSignature.PkMessagePair]:
    pairs: List[BLSSignature.PkMessagePair] = []
    for cvp in conditions_dict.get(ConditionOpcode.AGG_SIG, []):
        # TODO: check types
        # assert len(_) == 3
        blspubkey: BLSPublicKey = BLSPublicKey(cvp.var1)
        message: bytes32 = bytes32(blspy.Util.hash256(cvp.var2))
        pairs.append(BLSSignature.PkMessagePair(blspubkey, message))
    for cvp in conditions_dict.get(ConditionOpcode.AGG_SIG_ME, []):
        aggsigme_blspubkey: BLSPublicKey = BLSPublicKey(cvp.var1)
        aggsigme_message: bytes32 = bytes32(blspy.Util.hash256(cvp.var2 + coin_name))
        pairs.append(BLSSignature.PkMessagePair(aggsigme_blspubkey, aggsigme_message))
    return pairs
Пример #16
0
 async def get_lca(self) -> Optional[bytes32]:
     cursor = await self.db.execute("SELECT * from lca")
     row = await cursor.fetchone()
     await cursor.close()
     if row is not None:
         return bytes32(bytes.fromhex(row[0]))
     return None
Пример #17
0
 def get_quality_string(self, plot_id: bytes32) -> Optional[bytes32]:
     quality_str = Verifier().validate_proof(plot_id, self.size,
                                             self.challenge,
                                             bytes(self.proof))
     if not quality_str:
         return None
     return bytes32(quality_str)
def service_kwargs_for_harvester(
    root_path: pathlib.Path,
    config: Dict,
    consensus_constants: ConsensusConstants,
) -> Dict:
    connect_peers = [
        PeerInfo(config["farmer_peer"]["host"], config["farmer_peer"]["port"])
    ]

    genesis_challenge = bytes32(
        bytes.fromhex(
            config["network_genesis_challenges"][config["selected_network"]]))
    harvester = Harvester(
        root_path, config,
        consensus_constants.replace(GENESIS_CHALLENGE=genesis_challenge))
    peer_api = HarvesterAPI(harvester)

    kwargs = dict(
        root_path=root_path,
        node=harvester,
        peer_api=peer_api,
        node_type=NodeType.HARVESTER,
        advertised_port=config["port"],
        service_name=SERVICE_NAME,
        server_listen_ports=[config["port"]],
        connect_peers=connect_peers,
        auth_connect_peers=True,
        network_id=genesis_challenge,
    )
    if config["start_rpc_server"]:
        kwargs["rpc_info"] = (HarvesterRpcApi, config["rpc_port"])
    return kwargs
Пример #19
0
 def calculate_plot_id_ph(
     pool_contract_puzzle_hash: bytes32,
     plot_public_key: G1Element,
 ) -> bytes32:
     return bytes32(
         std_hash(
             bytes(pool_contract_puzzle_hash) + bytes(plot_public_key)))
Пример #20
0
def service_kwargs_for_full_node_simulator(
    root_path: Path,
    config: Dict,
    consensus_constants: ConsensusConstants,
    bt: BlockTools,
) -> Dict:
    mkdir(path_from_root(root_path, config["database_path"]).parent)
    genesis_challenge = bytes32(
        bytes.fromhex(
            config["network_genesis_challenges"][config["selected_network"]]))

    node = FullNode(
        config,
        root_path=root_path,
        consensus_constants=consensus_constants,
        name=SERVICE_NAME,
    )

    peer_api = FullNodeSimulator(node, bt)

    kwargs = dict(
        root_path=root_path,
        node=node,
        peer_api=peer_api,
        node_type=NodeType.FULL_NODE,
        advertised_port=config["port"],
        service_name=SERVICE_NAME,
        server_listen_ports=[config["port"]],
        on_connect_callback=node.on_connect,
        rpc_info=(FullNodeRpcApi, config["rpc_port"]),
        network_id=genesis_challenge,
    )
    return kwargs
Пример #21
0
def service_kwargs_for_timelord(
    root_path: pathlib.Path,
    config: Dict,
    constants: ConsensusConstants,
) -> Dict:

    connect_peers = [
        PeerInfo(config["full_node_peer"]["host"],
                 config["full_node_peer"]["port"])
    ]

    genesis_challenge = bytes32(
        bytes.fromhex(
            config["network_genesis_challenges"][config["selected_network"]]))
    node = Timelord(config,
                    constants.replace(GENESIS_CHALLENGE=genesis_challenge))
    peer_api = TimelordAPI(node)

    kwargs = dict(
        root_path=root_path,
        peer_api=peer_api,
        node=node,
        node_type=NodeType.TIMELORD,
        advertised_port=config["port"],
        service_name=SERVICE_NAME,
        server_listen_ports=[config["port"]],
        connect_peers=connect_peers,
        auth_connect_peers=False,
        network_id=genesis_challenge,
    )
    return kwargs
Пример #22
0
def service_kwargs_for_farmer(
    root_path: pathlib.Path,
    config: Dict,
    config_pool: Dict,
    keychain: Keychain,
    consensus_constants: ConsensusConstants,
) -> Dict:

    connect_peers = []
    fnp = config.get("full_node_peer")
    if fnp is not None:
        connect_peers.append(PeerInfo(fnp["host"], fnp["port"]))

    genesis_challenge = bytes32(bytes.fromhex(config["network_genesis_challenges"][config["selected_network"]]))

    farmer = Farmer(config, config_pool, keychain, consensus_constants.replace(GENESIS_CHALLENGE=genesis_challenge))
    peer_api = FarmerAPI(farmer)

    kwargs = dict(
        root_path=root_path,
        node=farmer,
        peer_api=peer_api,
        node_type=NodeType.FARMER,
        advertised_port=config["port"],
        service_name=SERVICE_NAME,
        server_listen_ports=[config["port"]],
        connect_peers=connect_peers,
        auth_connect_peers=False,
        on_connect_callback=farmer.on_connect,
        network_id=genesis_challenge,
    )
    if config["start_rpc_server"]:
        kwargs["rpc_info"] = (FarmerRpcApi, config["rpc_port"])
    return kwargs
def service_kwargs_for_full_node(
        root_path: pathlib.Path, config: Dict,
        consensus_constants: ConsensusConstants) -> Dict:
    genesis_challenge = bytes32(
        bytes.fromhex(
            config["network_genesis_challenges"][config["selected_network"]]))
    full_node = FullNode(
        config,
        root_path=root_path,
        consensus_constants=consensus_constants.replace(
            GENESIS_CHALLENGE=genesis_challenge),
    )
    api = FullNodeAPI(full_node)

    upnp_list = []
    if config["enable_upnp"]:
        upnp_list = [config["port"]]

    kwargs = dict(
        root_path=root_path,
        node=api.full_node,
        peer_api=api,
        node_type=NodeType.FULL_NODE,
        advertised_port=config["port"],
        service_name=SERVICE_NAME,
        upnp_ports=upnp_list,
        server_listen_ports=[config["port"]],
        on_connect_callback=full_node.on_connect,
        network_id=genesis_challenge,
    )
    if config["start_rpc_server"]:
        kwargs["rpc_info"] = (FullNodeRpcApi, config["rpc_port"])
    return kwargs
Пример #24
0
 async def ping():
     while not self._pipeline_task.done():
         to_close: List[Connection] = []
         for connection in self.global_connections.get_connections():
             if connection.handshake_finished:
                 if (time.time() - connection.get_last_message_time() >
                         config["timeout_duration"]
                         and connection.connection_type
                         == NodeType.FULL_NODE
                         and self._local_type == NodeType.FULL_NODE):
                     # Only closes down full_node <-> full_node connections, which can be recovered
                     to_close.append(connection)
         for connection in to_close:
             log.info(
                 f"Removing connection {connection} due to timeout.")
             self.global_connections.close(connection)
         msg = Message("ping", Ping(bytes32(token_bytes(32))))
         self.push_message(
             OutboundMessage(NodeType.FARMER, msg, Delivery.BROADCAST))
         self.push_message(
             OutboundMessage(NodeType.TIMELORD, msg,
                             Delivery.BROADCAST))
         self.push_message(
             OutboundMessage(NodeType.FULL_NODE, msg,
                             Delivery.BROADCAST))
         self.push_message(
             OutboundMessage(NodeType.HARVESTER, msg,
                             Delivery.BROADCAST))
         await asyncio.sleep(config["ping_interval"])
Пример #25
0
    async def get_sub_block_records_close_to_peak(
        self, blocks_n: int
    ) -> Tuple[Dict[bytes32, SubBlockRecord], Optional[bytes32]]:
        """
        Returns a dictionary with all sub blocks, as well as the header hash of the peak,
        if present.
        """

        res = await self.db.execute(
            "SELECT header_hash, height from block_records WHERE is_peak = 1")
        row = await res.fetchone()
        await res.close()
        if row is None:
            return {}, None
        header_hash_bytes, peak_height = row
        peak: bytes32 = bytes32(bytes.fromhex(header_hash_bytes))

        formatted_str = f"SELECT header_hash, block from block_records WHERE height >= {peak_height - blocks_n}"
        cursor = await self.db.execute(formatted_str)
        rows = await cursor.fetchall()
        await cursor.close()
        ret: Dict[bytes32, SubBlockRecord] = {}
        for row in rows:
            header_hash_bytes, sub_block_bytes = row
            header_hash = bytes.fromhex(header_hash_bytes)
            ret[header_hash] = SubBlockRecord.from_bytes(sub_block_bytes)
        return ret, peak
Пример #26
0
    async def get_unspent_coins_for_wallet(
            self, wallet_id: int) -> Set[WalletCoinRecord]:
        """ Returns set of CoinRecords that have not been spent yet for a wallet. """
        coins = set()

        cursor = await self.db_connection.execute(
            "SELECT * from coin_record WHERE spent=0 and wallet_id=?",
            (wallet_id, ),
        )
        rows = await cursor.fetchall()
        await cursor.close()
        for row in rows:
            coin = Coin(bytes32(bytes.fromhex(row[6])),
                        bytes32(bytes.fromhex(row[5])), row[7])
            coins.add(
                WalletCoinRecord(coin, row[1], row[2], row[3], row[4],
                                 WalletType(row[8]), row[9]))
        return coins
Пример #27
0
 async def get_unspent_coin_records(self,
                                    header: Header = None
                                    ) -> List[CoinRecord]:
     coins = set()
     if header is not None and header.header_hash in self.head_diffs:
         diff_store = self.head_diffs[header.header_hash]
         for _, record in diff_store.diffs.items():
             if not record.spent:
                 coins.add(record)
     cursor = await self.coin_record_db.execute(
         "SELECT * from coin_record WHERE spent=0")
     rows = await cursor.fetchall()
     await cursor.close()
     for row in rows:
         coin = Coin(bytes32(bytes.fromhex(row[6])),
                     bytes32(bytes.fromhex(row[5])), row[7])
         coins.add(CoinRecord(coin, row[1], row[2], row[3], row[4]))
     return list(coins)
Пример #28
0
 async def get_coin_record(self,
                           coin_name: bytes32,
                           header: Header = None) -> Optional[CoinRecord]:
     if header is not None and header.header_hash in self.head_diffs:
         diff_store = self.head_diffs[header.header_hash]
         if coin_name.hex() in diff_store.diffs:
             return diff_store.diffs[coin_name.hex()]
     if coin_name.hex() in self.lca_coin_records:
         return self.lca_coin_records[coin_name.hex()]
     cursor = await self.coin_record_db.execute(
         "SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(), ))
     row = await cursor.fetchone()
     await cursor.close()
     if row is not None:
         coin = Coin(bytes32(bytes.fromhex(row[6])),
                     bytes32(bytes.fromhex(row[5])), row[7])
         return CoinRecord(coin, row[1], row[2], row[3], row[4])
     return None
Пример #29
0
 def get_tree_hash(self) -> bytes32:
     if self.listp():
         left = self.to(self.first()).get_tree_hash()
         right = self.to(self.rest()).get_tree_hash()
         s = b"\2" + left + right
     else:
         atom = self.as_atom()
         s = b"\1" + atom
     return bytes32(std_hash(s))
Пример #30
0
    async def validate_block_body(self, block: FullBlock) -> Optional[Err]:
        """
        Validates the transactions and body of the block. Returns None if everything
        validates correctly, or an Err if something does not validate.
        """

        # 6. The compact block filter must be correct, according to the body (BIP158)
        if block.header.data.filter_hash != bytes32([0] * 32):
            if block.transactions_filter is None:
                return Err.INVALID_TRANSACTIONS_FILTER_HASH
            if std_hash(block.transactions_filter
                        ) != block.header.data.filter_hash:
                return Err.INVALID_TRANSACTIONS_FILTER_HASH
        elif block.transactions_filter is not None:
            return Err.INVALID_TRANSACTIONS_FILTER_HASH

        fee_base = calculate_base_fee(block.height)
        # target reward_fee = 1/8 coinbase reward + tx fees
        if block.transactions_generator is not None:
            # 14. Make sure transactions generator hash is valid (or all 0 if not present)
            if (block.transactions_generator.get_tree_hash() !=
                    block.header.data.generator_hash):
                return Err.INVALID_TRANSACTIONS_GENERATOR_HASH

            # 15. If not genesis, the transactions must be valid and fee must be valid
            # Verifies that fee_base + TX fees = fee_coin.amount
            err = await self._validate_transactions(block, fee_base)
            if err is not None:
                return err
        else:
            # Make sure transactions generator hash is valid (or all 0 if not present)
            if block.header.data.generator_hash != bytes32(bytes([0] * 32)):
                return Err.INVALID_TRANSACTIONS_GENERATOR_HASH

            # 16. If genesis, the fee must be the base fee, agg_sig must be None, and merkle roots must be valid
            if fee_base != block.header.data.fees_coin.amount:
                return Err.INVALID_BLOCK_FEE_AMOUNT
            root_error = self._validate_merkle_root(block)
            if root_error:
                return root_error
            if block.header.data.aggregated_signature is not None:
                log.error("1")
                return Err.BAD_AGGREGATE_SIGNATURE
        return None