Skip to content

Commit

Permalink
Clarify cases when transactions filter is computed with just reward c…
Browse files Browse the repository at this point in the history
…oins.
  • Loading branch information
AmineKhaldi committed Jan 7, 2025
1 parent b6d5afc commit 7c84f8e
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 22 deletions.
16 changes: 16 additions & 0 deletions chia/_tests/blockchain/test_blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -4184,3 +4184,19 @@ async def get_fork_info(blockchain: Blockchain, block: FullBlock, peak: BlockRec
)

return fork_info


@pytest.mark.anyio
async def test_get_header_blocks_in_range_tx_filter_non_tx_block(empty_blockchain: Blockchain, bt: BlockTools) -> None:
"""
Covers the case of calling `get_header_blocks_in_range`, requesting
transactions filter, on a non transaction block.
"""
b = empty_blockchain
blocks = bt.get_consecutive_blocks(2)
for block in blocks:
await _validate_and_add_block(b, block)
non_tx_block = next(block for block in blocks if not block.is_transaction_block())
blocks_with_filter = await b.get_header_blocks_in_range(0, 42, tx_filter=True)
empty_tx_filter = b"\x00"
assert blocks_with_filter[non_tx_block.header_hash].transactions_filter == empty_tx_filter
16 changes: 10 additions & 6 deletions chia/consensus/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -902,18 +902,22 @@ async def get_header_blocks_in_range(
raise ValueError(f"Block at {block.header_hash} is no longer in the blockchain (it's in a fork)")
if tx_filter is False:
header = get_block_header(block)
elif block.transactions_generator is None:
# There is no point in getting additions and removals for
# blocks that do not have transactions.
header = get_block_header(block)
else:
elif block.transactions_generator is not None:
added_coins_records, removed_coins_records = await asyncio.gather(
self.coin_store.get_coins_added_at_height(block.height),
self.coin_store.get_coins_removed_at_height(block.height),
)
tx_additions = [cr.coin for cr in added_coins_records if not cr.coinbase]
removed = [cr.coin.name() for cr in removed_coins_records]
header = get_block_header(block, (tx_additions, removed))
header = get_block_header(block, (removed, tx_additions))
elif block.is_transaction_block():
# This is a transaction block with just reward coins.
# We're sending empty additions and removals to signal that we
# want the transactions filter to be computed.
header = get_block_header(block, ([], []))
else:
# Non transaction block.
header = get_block_header(block)
header_blocks[header.header_hash] = header

return header_blocks
Expand Down
14 changes: 8 additions & 6 deletions chia/consensus/multiprocess_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import logging
import time
import traceback
from collections.abc import Awaitable
from collections.abc import Awaitable, Collection
from concurrent.futures import Executor
from dataclasses import dataclass
from typing import Optional
Expand Down Expand Up @@ -94,12 +94,11 @@ def _pre_validate_block(

try:
validation_start = time.monotonic()
tx_additions: list[Coin] = []
removals: list[bytes32] = []
removals_and_additions: Optional[tuple[Collection[bytes32], Collection[Coin]]] = None
if conds is not None:
assert conds.validated_signature is True
assert block.transactions_generator is not None
removals, tx_additions = tx_removals_and_additions(conds)
removals_and_additions = tx_removals_and_additions(conds)
elif block.transactions_generator is not None:
assert prev_generators is not None
assert block.transactions_info is not None
Expand All @@ -118,10 +117,13 @@ def _pre_validate_block(
return PreValidationResult(uint16(err), None, None, uint32(validation_time * 1000))
assert conds is not None
assert conds.validated_signature is True
removals, tx_additions = tx_removals_and_additions(conds)
removals_and_additions = tx_removals_and_additions(conds)
elif block.is_transaction_block():
# This is a transaction block with just reward coins.
removals_and_additions = ([], [])

assert conds is None or conds.validated_signature is True
header_block = get_block_header(block, (tx_additions, removals))
header_block = get_block_header(block, removals_and_additions)
required_iters, error = validate_finished_header_block(
constants,
blockchain,
Expand Down
14 changes: 8 additions & 6 deletions chia/full_node/full_node_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import time
import traceback
from collections.abc import Collection
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime, timezone
from typing import TYPE_CHECKING, ClassVar, Optional, cast
Expand Down Expand Up @@ -1209,8 +1210,7 @@ async def request_block_header(self, request: wallet_protocol.RequestBlockHeader
if block is None:
return None

tx_removals: list[bytes32] = []
tx_additions: list[Coin] = []
removals_and_additions: Optional[tuple[Collection[bytes32], Collection[Coin]]] = None

if block.transactions_generator is not None:
block_generator: Optional[BlockGenerator] = await get_block_generator(
Expand All @@ -1233,10 +1233,12 @@ async def request_block_header(self, request: wallet_protocol.RequestBlockHeader
)
# strip the hint from additions, and compute the puzzle hash for
# removals
tx_additions = [add[0] for add in additions]
tx_removals = [rem.name() for rem in removals]
removals_and_additions = ([r.name() for r in removals], [a[0] for a in additions])
elif block.is_transaction_block():
# This is a transaction block with just reward coins.
removals_and_additions = ([], [])

header_block = get_block_header(block, (tx_additions, tx_removals))
header_block = get_block_header(block, removals_and_additions)
msg = make_msg(
ProtocolMessageTypes.respond_block_header,
wallet_protocol.RespondBlockHeader(header_block),
Expand Down Expand Up @@ -1528,7 +1530,7 @@ async def request_header_blocks(self, request: wallet_protocol.RequestHeaderBloc
)
added_coins = [record.coin for record in added_coins_records if not record.coinbase]
removal_names = [record.coin.name() for record in removed_coins_records]
header_block = get_block_header(block, (added_coins, removal_names))
header_block = get_block_header(block, (removal_names, added_coins))
header_blocks.append(header_block)

msg = make_msg(
Expand Down
10 changes: 6 additions & 4 deletions chia/util/generator_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@


def get_block_header(
block: FullBlock, additions_and_removals: Optional[tuple[Collection[Coin], Collection[bytes32]]] = None
block: FullBlock, removals_and_additions: Optional[tuple[Collection[bytes32], Collection[Coin]]] = None
) -> HeaderBlock:
"""
Returns a HeaderBlock from a FullBlock.
If `additions_and_removals` is not None, account for them, as well as
If `removals_and_additions` is not None, account for them, as well as
reward coins, in the creation of the transactions filter, otherwise create
an empty one.
"""
# Guard against passing removals and additions for a non-transaction block.
assert removals_and_additions is None or block.is_transaction_block()
# Create an empty filter to begin with
byte_array_tx: list[bytearray] = []
if additions_and_removals is not None and block.is_transaction_block():
tx_addition_coins, removals_names = additions_and_removals
if removals_and_additions is not None and block.is_transaction_block():
removals_names, tx_addition_coins = removals_and_additions
for coin in tx_addition_coins:
byte_array_tx.append(bytearray(coin.puzzle_hash))
for coin in block.get_included_reward_coins():
Expand Down

0 comments on commit 7c84f8e

Please sign in to comment.