Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CHIA-1562] move signature validation into run_block_generator() #18812

Merged
merged 3 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions chia/_tests/blockchain/blockchain_test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import asyncio
from typing import Optional

from chia_rs import BLSCache
from chia_rs import SpendBundleConditions

from chia.consensus.block_body_validation import ForkInfo
from chia.consensus.blockchain import AddBlockResult, Blockchain
Expand Down Expand Up @@ -52,7 +52,6 @@ async def _validate_and_add_block(
expected_error: Optional[Err] = None,
skip_prevalidation: bool = False,
fork_info: Optional[ForkInfo] = None,
use_bls_cache: bool = False,
) -> 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.
Expand All @@ -73,19 +72,22 @@ async def _validate_and_add_block(
new_slot = len(block.finished_sub_slots) > 0
ssi, diff = get_next_sub_slot_iters_and_difficulty(blockchain.constants, new_slot, prev_b, blockchain)
await check_block_store_invariant(blockchain)

if skip_prevalidation:
results = PreValidationResult(None, uint64(1), None, False, uint32(0))
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)
results = PreValidationResult(None, uint64(1), conds, uint32(0))
else:
# validate_signatures must be False in order to trigger add_block() to
# validate the signature.
futures = await pre_validate_blocks_multiprocessing(
blockchain.constants,
AugmentedBlockchain(blockchain),
[block],
blockchain.pool,
{},
ValidationState(ssi, diff, prev_ses_block),
validate_signatures=False,
)
pre_validation_results: list[PreValidationResult] = list(await asyncio.gather(*futures))
assert pre_validation_results is not None
Expand All @@ -105,16 +107,12 @@ async def _validate_and_add_block(
return None
if fork_info is None:
fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
if use_bls_cache:
bls_cache = BLSCache(100)
else:
bls_cache = None

(
result,
err,
_,
) = await blockchain.add_block(block, results, bls_cache, ssi, fork_info=fork_info)
) = 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:
Expand Down
57 changes: 23 additions & 34 deletions chia/_tests/blockchain/test_blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1799,7 +1799,6 @@ async def test_pre_validation_fails_bad_blocks(self, empty_blockchain: Blockchai
empty_blockchain.pool,
{},
ValidationState(ssi, difficulty, None),
validate_signatures=True,
)
res: list[PreValidationResult] = list(await asyncio.gather(*futures))
assert res[0].error is None
Expand Down Expand Up @@ -1827,7 +1826,6 @@ async def test_pre_validation(
empty_blockchain.pool,
{},
ValidationState(ssi, difficulty, None),
validate_signatures=True,
)
res: list[PreValidationResult] = list(await asyncio.gather(*futures))
end_pv = time.time()
Expand All @@ -1839,7 +1837,7 @@ async def test_pre_validation(
block = blocks_to_validate[n]
start_rb = time.time()
fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
result, err, _ = await empty_blockchain.add_block(block, res[n], None, ssi, fork_info=fork_info)
result, err, _ = await empty_blockchain.add_block(block, res[n], ssi, fork_info=fork_info)
end_rb = time.time()
times_rb.append(end_rb - start_rb)
assert err is None
Expand Down Expand Up @@ -1935,16 +1933,13 @@ async def test_conditions(
b.pool,
{},
ValidationState(ssi, diff, None),
validate_signatures=False,
)
pre_validation_results: list[PreValidationResult] = list(await asyncio.gather(*futures))
# Ignore errors from pre-validation, we are testing block_body_validation
repl_preval_results = replace(pre_validation_results[0], error=None, required_iters=uint64(1))
block = blocks[-1]
fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
code, err, state_change = await b.add_block(
block, repl_preval_results, None, sub_slot_iters=ssi, fork_info=fork_info
)
code, err, state_change = await b.add_block(block, repl_preval_results, sub_slot_iters=ssi, fork_info=fork_info)
assert code == AddBlockResult.NEW_PEAK
assert err is None
assert state_change is not None
Expand Down Expand Up @@ -2062,13 +2057,12 @@ async def test_timelock_conditions(
b.pool,
{},
ValidationState(ssi, diff, None),
validate_signatures=True,
)
pre_validation_results: list[PreValidationResult] = list(await asyncio.gather(*futures))
assert pre_validation_results is not None
block = blocks[-1]
fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
assert (await b.add_block(block, pre_validation_results[0], None, sub_slot_iters=ssi, fork_info=fork_info))[
assert (await b.add_block(block, pre_validation_results[0], sub_slot_iters=ssi, fork_info=fork_info))[
0
] == expected

Expand Down Expand Up @@ -2146,15 +2140,14 @@ async def test_aggsig_garbage(
b.pool,
{},
ValidationState(ssi, diff, None),
validate_signatures=False,
)
pre_validation_results: list[PreValidationResult] = list(await asyncio.gather(*futures))
# Ignore errors from pre-validation, we are testing block_body_validation
repl_preval_results = replace(pre_validation_results[0], error=None, required_iters=uint64(1))
block = blocks[-1]
fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
res, error, state_change = await b.add_block(
block, repl_preval_results, None, sub_slot_iters=ssi, fork_info=fork_info
block, repl_preval_results, sub_slot_iters=ssi, fork_info=fork_info
)
assert res == AddBlockResult.NEW_PEAK
assert error is None
Expand Down Expand Up @@ -2275,13 +2268,12 @@ async def test_ephemeral_timelock(
b.pool,
{},
ValidationState(ssi, diff, None),
validate_signatures=True,
)
pre_validation_results: list[PreValidationResult] = list(await asyncio.gather(*futures))
assert pre_validation_results is not None
block = blocks[-1]
fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
assert (await b.add_block(block, pre_validation_results[0], None, sub_slot_iters=ssi, fork_info=fork_info))[
assert (await b.add_block(block, pre_validation_results[0], sub_slot_iters=ssi, fork_info=fork_info))[
0
] == expected

Expand Down Expand Up @@ -2613,6 +2605,7 @@ async def test_cost_exceeds_max(
)

assert blocks[-1].transactions_generator is not None
assert blocks[-1].transactions_info is not None
block_generator = BlockGenerator(blocks[-1].transactions_generator, [])
npc_result = get_name_puzzle_conditions(
block_generator,
Expand All @@ -2621,15 +2614,15 @@ async def test_cost_exceeds_max(
height=softfork_height,
constants=bt.constants,
)
assert npc_result.conds is not None
ssi = b.constants.SUB_SLOT_ITERS_STARTING
diff = b.constants.DIFFICULTY_STARTING
block = blocks[-1]
fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
err = (
await b.add_block(
blocks[-1],
PreValidationResult(None, uint64(1), npc_result.conds, True, uint32(0)),
None,
PreValidationResult(None, uint64(1), npc_result.conds.replace(validated_signature=True), uint32(0)),
sub_slot_iters=ssi,
fork_info=fork_info,
)
Expand All @@ -2642,7 +2635,6 @@ async def test_cost_exceeds_max(
b.pool,
{},
ValidationState(ssi, diff, None),
validate_signatures=False,
)
results: list[PreValidationResult] = list(await asyncio.gather(*futures))
assert results is not None
Expand Down Expand Up @@ -2704,12 +2696,12 @@ async def test_invalid_cost_in_block(
height=softfork_height,
constants=bt.constants,
)
assert npc_result.conds is not None
ssi = b.constants.SUB_SLOT_ITERS_STARTING
fork_info = ForkInfo(block_2.height - 1, block_2.height - 1, block_2.prev_header_hash)
_, err, _ = await b.add_block(
block_2,
PreValidationResult(None, uint64(1), npc_result.conds, False, uint32(0)),
None,
PreValidationResult(None, uint64(1), npc_result.conds.replace(validated_signature=True), uint32(0)),
sub_slot_iters=ssi,
fork_info=fork_info,
)
Expand Down Expand Up @@ -2739,11 +2731,11 @@ async def test_invalid_cost_in_block(
height=softfork_height,
constants=bt.constants,
)
assert npc_result.conds is not None
fork_info = ForkInfo(block_2.height - 1, block_2.height - 1, block_2.prev_header_hash)
_, err, _ = await b.add_block(
block_2,
PreValidationResult(None, uint64(1), npc_result.conds, False, uint32(0)),
None,
PreValidationResult(None, uint64(1), npc_result.conds.replace(validated_signature=True), uint32(0)),
sub_slot_iters=ssi,
fork_info=fork_info,
)
Expand Down Expand Up @@ -2772,13 +2764,17 @@ async def test_invalid_cost_in_block(
else b.constants.MAX_BLOCK_COST_CLVM * 1000
)
npc_result = get_name_puzzle_conditions(
block_generator, max_cost, mempool_mode=False, height=softfork_height, constants=bt.constants
block_generator,
max_cost,
mempool_mode=False,
height=softfork_height,
constants=bt.constants,
)
assert npc_result.conds is not None
fork_info = ForkInfo(block_2.height - 1, block_2.height - 1, block_2.prev_header_hash)
_result, err, _ = await b.add_block(
block_2,
PreValidationResult(None, uint64(1), npc_result.conds, False, uint32(0)),
None,
PreValidationResult(None, uint64(1), npc_result.conds.replace(validated_signature=True), uint32(0)),
sub_slot_iters=ssi,
fork_info=fork_info,
)
Expand Down Expand Up @@ -3236,8 +3232,6 @@ async def test_invalid_agg_sig(self, empty_blockchain: Blockchain, bt: BlockTool

# Bad signature fails during add_block
await _validate_and_add_block(b, last_block, expected_error=Err.BAD_AGGREGATE_SIGNATURE)
# Also test the same case but when using BLSCache
await _validate_and_add_block(b, last_block, expected_error=Err.BAD_AGGREGATE_SIGNATURE, use_bls_cache=True)

# Bad signature also fails in prevalidation
ssi = b.constants.SUB_SLOT_ITERS_STARTING
Expand All @@ -3249,7 +3243,6 @@ async def test_invalid_agg_sig(self, empty_blockchain: Blockchain, bt: BlockTool
b.pool,
{},
ValidationState(ssi, diff, None),
validate_signatures=True,
)
preval_results: list[PreValidationResult] = list(await asyncio.gather(*futures))
assert preval_results is not None
Expand Down Expand Up @@ -3369,7 +3362,6 @@ async def test_long_reorg(
b.pool,
{},
ValidationState(ssi, diff, None),
validate_signatures=False,
)
pre_validation_results: list[PreValidationResult] = list(await asyncio.gather(*futures))
for i, block in enumerate(blocks):
Expand All @@ -3383,7 +3375,7 @@ async def test_long_reorg(
fork_info: ForkInfo = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
assert fork_info is not None
(result, err, _) = await b.add_block(
block, pre_validation_results[i], None, sub_slot_iters=ssi, fork_info=fork_info
block, pre_validation_results[i], sub_slot_iters=ssi, fork_info=fork_info
)
await check_block_store_invariant(b)
assert err is None
Expand Down Expand Up @@ -3937,11 +3929,10 @@ async def test_reorg_flip_flop(empty_blockchain: Blockchain, bt: BlockTools) ->
b.pool,
{},
ValidationState(ssi, diff, None),
validate_signatures=False,
)
preval: list[PreValidationResult] = list(await asyncio.gather(*futures))
fork_info = ForkInfo(block1.height - 1, block1.height - 1, block1.prev_header_hash)
_, err, _ = await b.add_block(block1, preval[0], None, sub_slot_iters=ssi, fork_info=fork_info)
_, err, _ = await b.add_block(block1, preval[0], sub_slot_iters=ssi, fork_info=fork_info)
assert err is None
futures = await pre_validate_blocks_multiprocessing(
b.constants,
Expand All @@ -3950,11 +3941,10 @@ async def test_reorg_flip_flop(empty_blockchain: Blockchain, bt: BlockTools) ->
b.pool,
{},
ValidationState(ssi, diff, None),
validate_signatures=False,
)
preval = list(await asyncio.gather(*futures))
fork_info = ForkInfo(block2.height - 1, block2.height - 1, block2.prev_header_hash)
_, err, _ = await b.add_block(block2, preval[0], None, sub_slot_iters=ssi, fork_info=fork_info)
_, err, _ = await b.add_block(block2, preval[0], sub_slot_iters=ssi, fork_info=fork_info)
assert err is None

peak = b.get_peak()
Expand Down Expand Up @@ -3985,15 +3975,14 @@ async def test_get_tx_peak(default_400_blocks: list[FullBlock], empty_blockchain
bc.pool,
{},
ValidationState(ssi, diff, None),
validate_signatures=False,
)
res: list[PreValidationResult] = list(await asyncio.gather(*futures))

last_tx_block_record = None
for b, prevalidation_res in zip(test_blocks, res):
assert bc.get_tx_peak() == last_tx_block_record
fork_info = ForkInfo(b.height - 1, b.height - 1, b.prev_header_hash)
_, err, _ = await bc.add_block(b, prevalidation_res, None, sub_slot_iters=ssi, fork_info=fork_info)
_, err, _ = await bc.add_block(b, prevalidation_res, sub_slot_iters=ssi, fork_info=fork_info)
assert err is None

if b.is_transaction_block():
Expand Down
16 changes: 6 additions & 10 deletions chia/_tests/clvm/test_puzzles.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from collections.abc import Iterable

from chia_rs import AugSchemeMPL, G1Element, G2Element
from chia_rs import AugSchemeMPL, G1Element

from chia._tests.util.key_tool import KeyTool
from chia.consensus.default_constants import DEFAULT_CONSTANTS
Expand Down Expand Up @@ -60,8 +60,7 @@ def do_test_spend(
This method will farm a coin paid to the hash of `puzzle_reveal`, then try to spend it
with `solution`, and verify that the created coins correspond to `payments`.

The `key_lookup` is used to create a signed version of the `SpendBundle`, although at
this time, signatures are not verified.
The `key_lookup` is used to create a signed version of the `SpendBundle`
"""

coin_db = CoinStore(DEFAULT_CONSTANTS)
Expand All @@ -74,7 +73,9 @@ def do_test_spend(
# spend it
coin_spend = make_spend(coin, puzzle_reveal, solution)

spend_bundle = SpendBundle([coin_spend], G2Element())
# sign the solution
signature = key_lookup.signature_for_solution(coin_spend, DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA)
spend_bundle = SpendBundle([coin_spend], signature)
coin_db.update_coin_store_for_spend_bundle(spend_bundle, spend_time, MAX_BLOCK_COST_CLVM)

# ensure all outputs are there
Expand All @@ -85,12 +86,7 @@ def do_test_spend(
else:
assert 0

# make sure we can actually sign the solution
signatures: list[G2Element] = []
for coin_spend in spend_bundle.coin_spends:
signature = key_lookup.signature_for_solution(coin_spend, bytes([2] * 32))
signatures.append(signature)
return SpendBundle(spend_bundle.coin_spends, AugSchemeMPL.aggregate(signatures))
return spend_bundle


def default_payments_and_conditions(
Expand Down
1 change: 1 addition & 0 deletions chia/_tests/core/full_node/stores/test_coin_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ async def test_basic_coin_store(db_version: int, softfork_height: uint32, bt: Bl
should_be_included.add(pool_coin)
if block.is_transaction_block():
if block.transactions_generator is not None:
assert block.transactions_info is not None
block_gen: BlockGenerator = BlockGenerator(block.transactions_generator, [])
npc_result = get_name_puzzle_conditions(
block_gen,
Expand Down
10 changes: 3 additions & 7 deletions chia/_tests/core/full_node/stores/test_full_node_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@ async def test_unfinished_block_rank(
# shuffle them to ensure the order we add them to the store isn't relevant
seeded_random.shuffle(unfinished)
for new_unf in unfinished:
store.add_unfinished_block(
uint32(2), new_unf, PreValidationResult(None, uint64(123532), None, False, uint32(0))
)
store.add_unfinished_block(uint32(2), new_unf, PreValidationResult(None, uint64(123532), None, uint32(0)))

# now ask for "the" unfinished block given the proof-of-space.
# the FullNodeStore should return the one with the lowest foliage tx block
Expand Down Expand Up @@ -221,7 +219,7 @@ async def test_basic_store(
assert store.get_unfinished_block(unf_block.partial_hash) is None
assert store.get_unfinished_block2(unf_block.partial_hash, None) == (None, 0, False)
store.add_unfinished_block(
uint32(height), unf_block, PreValidationResult(None, uint64(123532), None, False, uint32(0))
uint32(height), unf_block, PreValidationResult(None, uint64(123532), None, uint32(0))
)
assert store.get_unfinished_block(unf_block.partial_hash) == unf_block
assert store.get_unfinished_block2(
Expand Down Expand Up @@ -277,9 +275,7 @@ async def test_basic_store(
assert unf3.foliage.foliage_transaction_block_hash is None
assert unf4.foliage.foliage_transaction_block_hash is None
for val, unf_block in enumerate([unf1, unf2, unf3, unf4]):
store.add_unfinished_block(
uint32(height), unf_block, PreValidationResult(None, uint64(val), None, False, uint32(0))
)
store.add_unfinished_block(uint32(height), unf_block, PreValidationResult(None, uint64(val), None, uint32(0)))

# when not specifying a foliage hash, you get the "best" one
# best is defined as the lowest foliage hash
Expand Down
Loading
Loading