Skip to content
Open
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
3 changes: 2 additions & 1 deletion chia/_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ class ConsensusMode(ComparableEnum):

@pytest.fixture(
scope="session",
params=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0, ConsensusMode.HARD_FORK_3_0],
# TODO: todo_v2_plots add HARD_FORK_3_0 mode as well as after phase-out
params=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0],
)
def consensus_mode(request):
return request.param
Expand Down
12 changes: 7 additions & 5 deletions chia/_tests/core/server/test_rate_limits.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
rl_v2 = [Capability.BASE, Capability.BLOCK_HEADERS, Capability.RATE_LIMITS_V2]
rl_v1 = [Capability.BASE]
node_with_params_b = node_with_params
test_different_versions_results: list[int] = []


@dataclass
Expand Down Expand Up @@ -418,11 +417,14 @@ async def test_different_versions(
# The following code checks whether all of the runs resulted in the same number of items in "rate_limits_tx",
# which would mean the same rate limits are always used. This should not happen, since two nodes with V2
# will use V2.
total_tx_msg_count = len(get_rate_limits_to_use(a_con.local_capabilities, a_con.peer_capabilities))
rate_limits = get_rate_limits_to_use(a_con.local_capabilities, a_con.peer_capabilities)[0]
limit = rate_limits[ProtocolMessageTypes.request_header_blocks]
assert isinstance(limit, RLSettings)

test_different_versions_results.append(total_tx_msg_count)
if len(test_different_versions_results) >= 4:
assert len(set(test_different_versions_results)) >= 2
if Capability.RATE_LIMITS_V2 in a_con.local_capabilities and Capability.RATE_LIMITS_V2 in a_con.peer_capabilities:
assert limit.frequency == 5000
else:
assert limit.frequency == 500


@pytest.mark.anyio
Expand Down
6 changes: 5 additions & 1 deletion chia/consensus/block_body_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,10 +341,14 @@ async def validate_block_body(
# the generator ref list for this block (or 'one' bytes [0x01] if no generator)
# 8b. The generator ref list length must be less than or equal to MAX_GENERATOR_REF_LIST_SIZE entries
# 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_generator_ref_list == []:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just a tidy-up, or is it required?

Copy link
Contributor Author

@arvidn arvidn Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct, this is unrelated. I was a bit surprised mypy didn't flag this. transaction_generator_ref_list is not Optional[]. It can't be None.

if block.transactions_info.generator_refs_root != bytes([1] * 32):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is such a bizarre magic number. I'm very puzzled why we wouldn't just use the serialization for the empty list. But I don't suppose it much matters now.

Copy link
Contributor Author

@arvidn arvidn Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't a list, it's a bytes32. it must always be 32 bytes in the serialization. Still, It could be a named constant.

return Err.INVALID_TRANSACTIONS_GENERATOR_REFS_ROOT
else:
# With hard fork 2 we ban transactions_generator_ref_list.
if prev_transaction_block_height >= constants.HARD_FORK2_HEIGHT:
return Err.TOO_MANY_GENERATOR_REFS

# 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
Expand Down
13 changes: 9 additions & 4 deletions chia/consensus/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from chia.consensus.find_fork_point import lookup_fork_chain
from chia.consensus.full_block_to_block_record import block_to_block_record
from chia.consensus.generator_tools import get_block_header
from chia.consensus.get_block_challenge import prev_tx_block
from chia.consensus.get_block_generator import get_block_generator
from chia.consensus.multiprocess_validation import PreValidationResult
from chia.full_node.block_store import BlockStore
Expand Down Expand Up @@ -698,12 +699,16 @@ async def validate_unfinished_block_header(
if len(block.transactions_generator_ref_list) > self.constants.MAX_GENERATOR_REF_LIST_SIZE:
return None, Err.TOO_MANY_GENERATOR_REFS

if (
self.try_block_record(block.prev_header_hash) is None
and block.prev_header_hash != self.constants.GENESIS_CHALLENGE
):
prev_b = self.try_block_record(block.prev_header_hash)
if prev_b is None and block.prev_header_hash != self.constants.GENESIS_CHALLENGE:
return None, Err.INVALID_PREV_BLOCK_HASH

prev_tx_height = prev_tx_block(self, prev_b)

# With hard fork 2 we ban transactions_generator_ref_list.
if prev_tx_height >= self.constants.HARD_FORK2_HEIGHT and block.transactions_generator_ref_list != []:
return None, Err.TOO_MANY_GENERATOR_REFS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit puzzled as to why this is checked in multiple places. Maybe one is for all blocks and another is just for "transaction blocks"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We check it in BlockTools, to make sure no test is trying to create an invalid block.
We check it in block body validation, which is the main block validation function.
We also check it when validating unfinished blocks, which is a bit trickier because the height of the block isn't as well known.


if block.transactions_info is not None:
if block.transactions_generator is not None:
if std_hash(bytes(block.transactions_generator)) != block.transactions_info.generator_root:
Expand Down
4 changes: 4 additions & 0 deletions chia/simulator/block_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,10 @@ def setup_new_gen(
transaction_data: Optional[SpendBundle],
block_refs: list[uint32],
) -> Optional[NewBlockGenerator]:
if prev_tx_height >= self.constants.HARD_FORK2_HEIGHT:
assert block_refs == [], "block references are not allowed after hard fork 2"
dummy_block_references = False

# we don't know if the new block will be a transaction
# block or not, so even though we prepare a block
# generator, we can't update our state (like,
Expand Down
Loading