From e68bca42e9382adc6b2480e06235823c9080b8db Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Tue, 29 Oct 2024 20:07:20 +0100 Subject: [PATCH] Simplify return values for validate_block_body. --- chia/_tests/wallet/conftest.py | 5 +- chia/consensus/block_body_validation.py | 84 ++++++++++++------------- chia/consensus/blockchain.py | 9 +-- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/chia/_tests/wallet/conftest.py b/chia/_tests/wallet/conftest.py index 3f2df050e49f..e36f04b1db36 100644 --- a/chia/_tests/wallet/conftest.py +++ b/chia/_tests/wallet/conftest.py @@ -11,7 +11,6 @@ from chia._tests.util.setup_nodes import setup_simulators_and_wallets_service from chia._tests.wallet.wallet_block_tools import WalletBlockTools from chia.consensus.constants import ConsensusConstants -from chia.consensus.cost_calculator import NPCResult from chia.full_node.full_node import FullNode from chia.rpc.full_node_rpc_client import FullNodeRpcClient from chia.rpc.wallet_rpc_client import WalletRpcClient @@ -56,8 +55,8 @@ async def ignore_block_validation( if "standard_block_tools" in request.keywords: return None - async def validate_block_body(*args: Any, **kwargs: Any) -> tuple[Literal[None], NPCResult]: - return None, args[7] + async def validate_block_body(*args: Any, **kwargs: Any) -> Literal[None]: + return None def create_wrapper(original_create: Any) -> Any: async def new_create(*args: Any, **kwargs: Any) -> Any: diff --git a/chia/consensus/block_body_validation.py b/chia/consensus/block_body_validation.py index 7bc5dbf41eaf..364b59ab8812 100644 --- a/chia/consensus/block_body_validation.py +++ b/chia/consensus/block_body_validation.py @@ -191,14 +191,12 @@ async def validate_block_body( bls_cache: Optional[BLSCache], *, validate_signature: bool = True, -) -> tuple[Optional[Err], Optional[SpendBundleConditions]]: +) -> Optional[Err]: """ This assumes the header block has been completely validated. Validates the transactions and body of the block. - Returns None for the first value if everything validates correctly, or an - Err if something does not validate. - For the second value, returns a SpendBundleConditions only if validation - succeeded, and there are transactions. In other cases it returns None. + Returns None if everything validates correctly, or an Err if something does + not validate. conds is the result of running the generator with the previous generators refs. It must be set for transaction blocks and must be None for non-transaction blocks. @@ -220,7 +218,7 @@ async def validate_block_body( or block.transactions_info is not None or block.transactions_generator is not None ): - return Err.NOT_BLOCK_BUT_HAS_DATA, None + return Err.NOT_BLOCK_BUT_HAS_DATA prev_tb: Optional[BlockRecord] = records.block_record(block.prev_header_hash) assert prev_tb is not None @@ -229,16 +227,18 @@ async def validate_block_body( assert prev_tb is not None assert prev_tb.timestamp is not None if len(block.transactions_generator_ref_list) > 0: - return Err.NOT_BLOCK_BUT_HAS_DATA, None + return Err.NOT_BLOCK_BUT_HAS_DATA assert fork_info.peak_height == height - 1 - return None, None # This means the block is valid + assert conds is None + # This means the block is valid + return None # All checks below this point correspond to transaction blocks # 2. For blocks, foliage block, transactions info must not be empty if block.foliage_transaction_block is None or block.transactions_info is None: - return Err.IS_TRANSACTION_BLOCK_BUT_NO_DATA, None + return Err.IS_TRANSACTION_BLOCK_BUT_NO_DATA assert block.foliage_transaction_block is not None # keeps track of the reward coins that need to be incorporated @@ -246,11 +246,11 @@ async def validate_block_body( # 3. The transaction info hash in the Foliage block must match the transaction info if block.foliage_transaction_block.transactions_info_hash != std_hash(block.transactions_info): - return Err.INVALID_TRANSACTIONS_INFO_HASH, None + return Err.INVALID_TRANSACTIONS_INFO_HASH # 4. The foliage block hash in the foliage block must match the foliage block if block.foliage.foliage_transaction_block_hash != std_hash(block.foliage_transaction_block): - return Err.INVALID_FOLIAGE_BLOCK_HASH, None + return Err.INVALID_FOLIAGE_BLOCK_HASH # 5. The reward claims must be valid for the previous blocks, and current block fees # If height == 0, expected_reward_coins will be left empty @@ -303,10 +303,10 @@ async def validate_block_body( assert curr_b is not None if set(block.transactions_info.reward_claims_incorporated) != expected_reward_coins: - return Err.INVALID_REWARD_COINS, None + return Err.INVALID_REWARD_COINS if len(block.transactions_info.reward_claims_incorporated) != len(expected_reward_coins): - return Err.INVALID_REWARD_COINS, None + return Err.INVALID_REWARD_COINS removals: list[bytes32] = [] @@ -326,10 +326,10 @@ async def validate_block_body( # the generator for this block (or zeroes if no generator) if block.transactions_generator is not None: if std_hash(bytes(block.transactions_generator)) != block.transactions_info.generator_root: - return Err.INVALID_TRANSACTIONS_GENERATOR_HASH, None + return Err.INVALID_TRANSACTIONS_GENERATOR_HASH else: if block.transactions_info.generator_root != bytes([0] * 32): - return Err.INVALID_TRANSACTIONS_GENERATOR_HASH, None + return Err.INVALID_TRANSACTIONS_GENERATOR_HASH # 8a. The generator_ref_list must be the hash of the serialized bytes of # the generator ref list for this block (or 'one' bytes [0x01] if no generator) @@ -337,20 +337,20 @@ async def validate_block_body( # 8c. The generator ref list must not point to a height >= this block's height if block.transactions_generator_ref_list in (None, []): if block.transactions_info.generator_refs_root != bytes([1] * 32): - return Err.INVALID_TRANSACTIONS_GENERATOR_REFS_ROOT, None + return Err.INVALID_TRANSACTIONS_GENERATOR_REFS_ROOT else: # If we have a generator reference list, we must have a generator if block.transactions_generator is None: - return Err.INVALID_TRANSACTIONS_GENERATOR_REFS_ROOT, None + return Err.INVALID_TRANSACTIONS_GENERATOR_REFS_ROOT # The generator_refs_root must be the hash of the concatenation of the list[uint32] generator_refs_hash = std_hash(b"".join([i.stream_to_bytes() for i in block.transactions_generator_ref_list])) if block.transactions_info.generator_refs_root != generator_refs_hash: - return Err.INVALID_TRANSACTIONS_GENERATOR_REFS_ROOT, None + return Err.INVALID_TRANSACTIONS_GENERATOR_REFS_ROOT if len(block.transactions_generator_ref_list) > constants.MAX_GENERATOR_REF_LIST_SIZE: - return Err.TOO_MANY_GENERATOR_REFS, None + return Err.TOO_MANY_GENERATOR_REFS if any([index >= height for index in block.transactions_generator_ref_list]): - return Err.FUTURE_GENERATOR_REFS, None + return Err.FUTURE_GENERATOR_REFS if block.transactions_generator is not None: # Get List of names removed, puzzles hashes for removed coins and conditions created @@ -363,7 +363,7 @@ async def validate_block_body( f"percent full: {round(100 * (cost / constants.MAX_BLOCK_COST_CLVM), 2)}%" ) if cost > constants.MAX_BLOCK_COST_CLVM: - return Err.BLOCK_COST_EXCEEDS_MAX, None + return Err.BLOCK_COST_EXCEEDS_MAX # 8. The CLVM program must not return any errors assert conds is not None @@ -379,7 +379,7 @@ async def validate_block_body( # 9. Check that the correct cost is in the transactions info if block.transactions_info.cost != cost: - return Err.INVALID_BLOCK_COST, None + return Err.INVALID_BLOCK_COST additions_dic: dict[bytes32, Coin] = {} # 10. Check additions for max coin amount @@ -388,10 +388,10 @@ async def validate_block_body( for coin, coin_name in additions + coinbase_additions: additions_dic[coin_name] = coin if coin.amount < 0: - return Err.COIN_AMOUNT_NEGATIVE, None + return Err.COIN_AMOUNT_NEGATIVE if coin.amount > constants.MAX_COIN_AMOUNT: - return Err.COIN_AMOUNT_EXCEEDS_MAXIMUM, None + return Err.COIN_AMOUNT_EXCEEDS_MAXIMUM # 11. Validate addition and removal roots root_error = validate_block_merkle_roots( @@ -400,8 +400,8 @@ async def validate_block_body( additions + coinbase_additions, removals, ) - if root_error: - return root_error, None + if root_error is not None: + return root_error # 12. The additions and removals must result in the correct filter byte_array_tx: list[bytearray] = [] @@ -416,19 +416,19 @@ async def validate_block_body( filter_hash = std_hash(encoded_filter) if filter_hash != block.foliage_transaction_block.filter_hash: - return Err.INVALID_TRANSACTIONS_FILTER_HASH, None + return Err.INVALID_TRANSACTIONS_FILTER_HASH # 13. Check for duplicate outputs in additions addition_counter = collections.Counter(coin_name for _, coin_name in additions + coinbase_additions) for count in addition_counter.values(): if count > 1: - return Err.DUPLICATE_OUTPUT, None + return Err.DUPLICATE_OUTPUT # 14. Check for duplicate spends inside block removal_counter = collections.Counter(removals) for count in removal_counter.values(): if count > 1: - return Err.DOUBLE_SPEND, None + return Err.DOUBLE_SPEND # 15. Check if removals exist and were not previously spent. (unspent_db + diff_store + this_block) # The fork point is the last block in common between the peak chain and the chain of `block` @@ -458,7 +458,7 @@ async def validate_block_body( if rem in fork_info.removals_since_fork: # This coin was spent in the fork log.error(f"Err.DOUBLE_SPEND_IN_FORK {fork_info.removals_since_fork[rem]}") - return Err.DOUBLE_SPEND_IN_FORK, None + return Err.DOUBLE_SPEND_IN_FORK removals_from_db.append(rem) unspent_records = await get_coin_records(removals_from_db) @@ -472,7 +472,7 @@ async def validate_block_body( # (We ignore all coins confirmed after fork) if unspent.spent == 1 and unspent.spent_block_index <= fork_info.fork_height: # Check for coins spent in an ancestor block - return Err.DOUBLE_SPEND, None + return Err.DOUBLE_SPEND removal_coin_records[unspent.name] = unspent else: look_in_fork.append(unspent.name) @@ -491,7 +491,7 @@ async def validate_block_body( if rem not in fork_info.additions_since_fork: # Check for spending a coin that does not exist in this fork log.error(f"Err.UNKNOWN_UNSPENT: COIN ID: {rem} fork_info: {fork_info}") - return Err.UNKNOWN_UNSPENT, None + return Err.UNKNOWN_UNSPENT addition: ForkAdd = fork_info.additions_since_fork[rem] new_coin_record: CoinRecord = CoinRecord( addition.coin, @@ -512,7 +512,7 @@ async def validate_block_body( # 16. Check that the total coin amount for added is <= removed if removed < added: - return Err.MINTING_COIN, None + return Err.MINTING_COIN fees = removed - added assert fees >= 0 @@ -523,20 +523,20 @@ async def validate_block_body( # 17. Check that the assert fee sum <= fees, and that each reserved fee is non-negative if fees < assert_fee_sum: - return Err.RESERVE_FEE_CONDITION_FAILED, None + return Err.RESERVE_FEE_CONDITION_FAILED # 18. Check that the fee amount + farmer reward < maximum coin amount if fees + calculate_base_farmer_reward(height) > constants.MAX_COIN_AMOUNT: - return Err.COIN_AMOUNT_EXCEEDS_MAXIMUM, None + return Err.COIN_AMOUNT_EXCEEDS_MAXIMUM # 19. Check that the computed fees are equal to the fees in the block header if block.transactions_info.fees != fees: - return Err.INVALID_BLOCK_FEE_AMOUNT, None + return Err.INVALID_BLOCK_FEE_AMOUNT # 20. Verify that removed coin puzzle_hashes match with calculated puzzle_hashes for unspent in removal_coin_records.values(): if unspent.coin.puzzle_hash != removals_puzzle_dic[unspent.name]: - return Err.WRONG_PUZZLE_HASH, None + return Err.WRONG_PUZZLE_HASH # 21. Verify conditions # verify absolute/relative height/time conditions @@ -548,7 +548,7 @@ async def validate_block_body( prev_transaction_block_timestamp, ) if error is not None: - return error, None + return error # create hash_key list for aggsig check pairs_pks: list[G1Element] = [] @@ -559,7 +559,7 @@ async def validate_block_body( # 22. Verify aggregated signature # TODO: move this to pre_validate_blocks_multiprocessing so we can sync faster if not block.transactions_info.aggregated_signature: - return Err.BAD_AGGREGATE_SIGNATURE, None + return Err.BAD_AGGREGATE_SIGNATURE # The pairing cache is not useful while syncing as each pairing is seen # only once, so the extra effort of populating it is not justified. @@ -569,9 +569,9 @@ async def validate_block_body( if validate_signature: if bls_cache is None: if not AugSchemeMPL.aggregate_verify(pairs_pks, pairs_msgs, block.transactions_info.aggregated_signature): - return Err.BAD_AGGREGATE_SIGNATURE, None + return Err.BAD_AGGREGATE_SIGNATURE else: if not bls_cache.aggregate_verify(pairs_pks, pairs_msgs, block.transactions_info.aggregated_signature): - return Err.BAD_AGGREGATE_SIGNATURE, None + return Err.BAD_AGGREGATE_SIGNATURE - return None, conds + return None diff --git a/chia/consensus/blockchain.py b/chia/consensus/blockchain.py index 97d65deaf05d..4834a7c9df76 100644 --- a/chia/consensus/blockchain.py +++ b/chia/consensus/blockchain.py @@ -358,7 +358,7 @@ async def add_block( assert fork_info.peak_height == block.height - 1 assert block.height == 0 or fork_info.peak_hash == block.prev_header_hash - error_code, _ = await validate_block_body( + error_code = await validate_block_body( self.constants, self, self.coin_store.get_coin_records, @@ -712,13 +712,14 @@ async def validate_unfinished_block( fork_info = ForkInfo(prev_height, prev_height, block.prev_header_hash) - error_code, cost_result = await validate_block_body( + conds = None if npc_result is None else npc_result.conds + error_code = await validate_block_body( self.constants, self, self.coin_store.get_coin_records, block, uint32(prev_height + 1), - None if npc_result is None else npc_result.conds, + conds, fork_info, None, validate_signature=False, # Signature was already validated before calling this method, no need to validate @@ -727,7 +728,7 @@ async def validate_unfinished_block( if error_code is not None: return PreValidationResult(uint16(error_code.value), None, None, False, uint32(0)) - return PreValidationResult(None, required_iters, cost_result, False, uint32(0)) + return PreValidationResult(None, required_iters, conds, False, uint32(0)) def contains_block(self, header_hash: bytes32) -> bool: """