mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-08-24 10:05:29 -05:00
* Add missing type annotations and remove 10 mypy exclusions Add return type annotations, parameter types, and generic type parameters to 10 modules that had trivial mypy strict-mode violations (1-2 errors each). Remove them from mypy-exclusions.txt so they are now covered by strict type checking. Modules fixed: - chia.cmds.start_funcs (Popen generic param) - chia.wallet.wallet_pool_store (classmethod return type) - installhelper (return type) - chia._tests.blockchain.blockchain_test_utils (return type) - chia._tests.core.test_daemon_rpc (param + return types) - chia._tests.core.test_filter (param + return types) - chia._tests.pools.test_pool_config (param + return types) - chia._tests.core.custom_types.test_coin (return types) - chia._tests.core.util.test_lru_cache (return type + var annotation) - chia._tests.core.util.test_significant_bits (return types) Also fixes int-to-uint32 arg-type errors in test_wallet_pool_store.py that surfaced once wallet_pool_store.py became strictly typed. * Some better types and await ws_server.stop(). --------- Co-authored-by: Amine Khaldi <amine.khaldi@reactos.org>
199 lines
8.1 KiB
Python
199 lines
8.1 KiB
Python
from __future__ import annotations
|
|
|
|
from chia_rs import FullBlock, SpendBundleConditions
|
|
from chia_rs.sized_ints import uint32, uint64
|
|
|
|
from chia.consensus.augmented_chain import AugmentedBlockchain
|
|
from chia.consensus.block_body_validation import ForkInfo
|
|
from chia.consensus.blockchain import AddBlockResult, Blockchain
|
|
from chia.consensus.difficulty_adjustment import get_next_sub_slot_iters_and_difficulty
|
|
from chia.consensus.multiprocess_validation import PreValidationResult, pre_validate_block
|
|
from chia.types.validation_state import ValidationState
|
|
from chia.util.errors import Err
|
|
|
|
|
|
async def check_block_store_invariant(bc: Blockchain) -> None:
|
|
db_wrapper = bc.block_store.db_wrapper
|
|
|
|
if db_wrapper.db_version == 1:
|
|
return
|
|
|
|
in_chain = set()
|
|
max_height = -1
|
|
async with bc.block_store.transaction() as conn:
|
|
async with conn.execute("SELECT height, in_main_chain FROM full_blocks") as cursor:
|
|
rows = await cursor.fetchall()
|
|
for row in rows:
|
|
height = row[0]
|
|
|
|
# if this block is in-chain, ensure we haven't found another block
|
|
# at this height that's also in chain. That would be an invariant
|
|
# violation
|
|
if row[1]:
|
|
# make sure we don't have any duplicate heights. Each block
|
|
# height can only have a single block with in_main_chain set
|
|
assert height not in in_chain
|
|
in_chain.add(height)
|
|
max_height = max(max_height, height)
|
|
|
|
# make sure every height is represented in the set
|
|
assert len(in_chain) == max_height + 1
|
|
|
|
|
|
async def _validate_and_add_block(
|
|
blockchain: Blockchain,
|
|
block: FullBlock,
|
|
*,
|
|
expected_result: AddBlockResult | None = None,
|
|
expected_error: Err | None = None,
|
|
skip_prevalidation: bool = False,
|
|
fork_info: ForkInfo | None = None,
|
|
augmented_blockchain: AugmentedBlockchain | None = None,
|
|
) -> None:
|
|
# Tries to validate and add the block, and checks that there are no errors in the process and that the
|
|
# block is added to the peak.
|
|
# If expected_result is not None, that result will be enforced.
|
|
# If expected_error is not None, that error will be enforced. If expected_error is not None,
|
|
# add_block must return Err.INVALID_BLOCK.
|
|
# If expected_result == INVALID_BLOCK but expected_error is None, we will allow for errors to happen
|
|
|
|
# use augmented blockchain if provided otherwise new instance
|
|
aug_blockchain = augmented_blockchain if augmented_blockchain is not None else AugmentedBlockchain(blockchain)
|
|
prev_b = None
|
|
prev_ses_block = None
|
|
if block.height > 0:
|
|
prev_b = await aug_blockchain.get_block_record_from_db(block.prev_header_hash)
|
|
if prev_b is not None: # some negative tests require this
|
|
curr = prev_b
|
|
while curr.height > 0 and curr.sub_epoch_summary_included is None:
|
|
curr = aug_blockchain.block_record(curr.prev_hash)
|
|
prev_ses_block = curr
|
|
new_slot = len(block.finished_sub_slots) > 0
|
|
ssi, diff = get_next_sub_slot_iters_and_difficulty(blockchain.constants, new_slot, prev_b, aug_blockchain)
|
|
await check_block_store_invariant(blockchain)
|
|
|
|
if skip_prevalidation:
|
|
if block.transactions_generator is None:
|
|
conds = None
|
|
else:
|
|
# fake the signature validation. Just say True here.
|
|
conds = SpendBundleConditions([], 0, 0, 0, None, None, [], 0, 0, 0, True, 0, 0, 0, 0, 0)
|
|
results = PreValidationResult(None, uint64(1), conds, uint32(0))
|
|
else:
|
|
future = await pre_validate_block(
|
|
blockchain.constants,
|
|
aug_blockchain,
|
|
block,
|
|
blockchain.pool,
|
|
None,
|
|
ValidationState(ssi, diff, prev_ses_block),
|
|
)
|
|
results = await future
|
|
if results.error is not None:
|
|
if expected_result == AddBlockResult.INVALID_BLOCK and expected_error is None:
|
|
# We expected an error but didn't specify which one
|
|
await check_block_store_invariant(blockchain)
|
|
return None
|
|
if expected_error is None:
|
|
# We did not expect an error
|
|
raise AssertionError(Err(results.error))
|
|
elif Err(results.error) != expected_error:
|
|
# We expected an error but a different one
|
|
raise AssertionError(f"Expected {expected_error} but got {Err(results.error)}")
|
|
await check_block_store_invariant(blockchain)
|
|
return None
|
|
if fork_info is None:
|
|
fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
|
|
|
|
(
|
|
result,
|
|
err,
|
|
_,
|
|
) = await blockchain.add_block(block, results, ssi, fork_info=fork_info)
|
|
await check_block_store_invariant(blockchain)
|
|
|
|
if expected_error is None and expected_result != AddBlockResult.INVALID_BLOCK:
|
|
# Expecting an error here (but didn't specify which), let's check if we actually got an error
|
|
if err is not None:
|
|
# Got an error
|
|
raise AssertionError(err)
|
|
# Here we will enforce checking of the exact error
|
|
elif err != expected_error:
|
|
# Did not get the right error, or did not get an error
|
|
raise AssertionError(f"Expected {expected_error} but got {err}")
|
|
|
|
if expected_result is not None and expected_result != result:
|
|
raise AssertionError(f"Expected {expected_result} but got {result}")
|
|
elif expected_result is None:
|
|
# If we expected an error assume that expected_result = INVALID_BLOCK
|
|
if expected_error is not None and result != AddBlockResult.INVALID_BLOCK:
|
|
raise AssertionError(f"Block should be invalid, but received: {result}")
|
|
# Otherwise, assume that expected_result = NEW_PEAK
|
|
if expected_error is None and result != AddBlockResult.NEW_PEAK:
|
|
raise AssertionError(f"Block was not added: {result}")
|
|
|
|
|
|
async def _validate_and_add_block_multi_error(
|
|
blockchain: Blockchain,
|
|
block: FullBlock,
|
|
expected_errors: list[Err],
|
|
skip_prevalidation: bool = False,
|
|
fork_info: ForkInfo | None = None,
|
|
) -> None:
|
|
# Checks that the blockchain returns one of the expected errors
|
|
try:
|
|
await _validate_and_add_block(blockchain, block, skip_prevalidation=skip_prevalidation, fork_info=fork_info)
|
|
except Exception as e:
|
|
assert isinstance(e, AssertionError)
|
|
assert e.args[0] in expected_errors
|
|
return
|
|
|
|
raise AssertionError("Did not return an error")
|
|
|
|
|
|
async def _validate_and_add_block_multi_result(
|
|
blockchain: Blockchain,
|
|
block: FullBlock,
|
|
expected_result: list[AddBlockResult],
|
|
skip_prevalidation: bool = False,
|
|
fork_info: ForkInfo | None = None,
|
|
augmented_blockchain: AugmentedBlockchain | None = None,
|
|
) -> None:
|
|
try:
|
|
await _validate_and_add_block(
|
|
blockchain,
|
|
block,
|
|
skip_prevalidation=skip_prevalidation,
|
|
fork_info=fork_info,
|
|
augmented_blockchain=augmented_blockchain,
|
|
)
|
|
except Exception as e:
|
|
assert isinstance(e, AssertionError)
|
|
assert "Block was not added" in e.args[0]
|
|
expected_list: list[str] = [f"Block was not added: {res}" for res in expected_result]
|
|
if e.args[0] not in expected_list:
|
|
raise AssertionError(f"{e.args[0].split('Block was not added: ')[1]} not in {expected_result}")
|
|
|
|
|
|
async def _validate_and_add_block_no_error(
|
|
blockchain: Blockchain,
|
|
block: FullBlock,
|
|
skip_prevalidation: bool = False,
|
|
fork_info: ForkInfo | None = None,
|
|
augmented_blockchain: AugmentedBlockchain | None = None,
|
|
) -> None:
|
|
# adds a block and ensures that there is no error. However, does not ensure that block extended the peak of
|
|
# the blockchain
|
|
await _validate_and_add_block_multi_result(
|
|
blockchain,
|
|
block,
|
|
expected_result=[
|
|
AddBlockResult.ALREADY_HAVE_BLOCK,
|
|
AddBlockResult.NEW_PEAK,
|
|
AddBlockResult.ADDED_AS_ORPHAN,
|
|
],
|
|
skip_prevalidation=skip_prevalidation,
|
|
fork_info=fork_info,
|
|
augmented_blockchain=augmented_blockchain,
|
|
)
|