Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

the test blockchains we use in tests were generated before the hard fork 2 and the phase out. These chains won't be valid when run as if hard fork 2 has activated. The proofs of space won't be valid. Once we have new chains we can enable these tests

# 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
60 changes: 4 additions & 56 deletions chia/_tests/core/consensus/test_pot_iterations.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import pytest
from chia_rs import PlotSize
from chia_rs.sized_ints import uint8, uint16, uint32, uint64, uint128
from pytest import raises
Expand All @@ -10,7 +9,6 @@
from chia.consensus.pot_iterations import (
calculate_ip_iters,
calculate_iterations_quality,
calculate_phase_out,
calculate_sp_interval_iters,
calculate_sp_iters,
is_overflow_block,
Expand Down Expand Up @@ -84,17 +82,7 @@ def test_calculate_ip_iters(self):
assert ip_iters == (sp_iters + test_constants.NUM_SP_INTERVALS_EXTRA * sp_interval_iters + required_iters) % ssi
assert sp_iters > ip_iters

@pytest.mark.parametrize(
"height",
[
uint32(0),
test_constants.HARD_FORK2_HEIGHT - 1,
test_constants.HARD_FORK2_HEIGHT,
test_constants.HARD_FORK2_HEIGHT + test_constants.PLOT_V1_PHASE_OUT,
test_constants.HARD_FORK2_HEIGHT + test_constants.PLOT_V1_PHASE_OUT + 1,
],
)
def test_win_percentage(self, height: uint32):
def test_win_percentage(self):
"""
Tests that the percentage of blocks won is proportional to the space of each farmer,
with the assumption that all farmers have access to the same VDF speed.
Expand Down Expand Up @@ -129,59 +117,19 @@ def test_win_percentage(self, height: uint32):
quality = std_hash(
slot_index.to_bytes(4, "big") + plot_k_val.to_bytes(1, "big") + bytes(farmer_index)
)
required_iters = calculate_iterations_quality(
constants, quality, k, difficulty, sp_hash, sub_slot_iters, height
)
required_iters = calculate_iterations_quality(constants, quality, k, difficulty, sp_hash)
if required_iters < sp_interval_iters:
wins[k] += 1
total_wins_in_slot += 1

if height < test_constants.HARD_FORK2_HEIGHT + test_constants.PLOT_V1_PHASE_OUT:
total_space = sum(farmer_space.values())
percentage_space = {k: float(sp / total_space) for k, sp in farmer_space.items()}
else:
# after the phase-out, v1 plots don't count
# all wins are by v2 plots
total_space = sum(0 if k.size_v2 is None else sp for k, sp in farmer_space.items())
percentage_space = {
k: 0.0 if k.size_v2 is None else float(sp / total_space) for k, sp in farmer_space.items()
}
total_space = sum(farmer_space.values())
percentage_space = {k: float(sp / total_space) for k, sp in farmer_space.items()}

win_percentage = {k: wins[k] / sum(wins.values()) for k in farmer_ks.keys()}
for k in farmer_ks.keys():
# Win rate is proportional to percentage of space
assert abs(win_percentage[k] - percentage_space[k]) < 0.01

@pytest.mark.parametrize("sp_interval", [uint64(6250000000), uint64(1), uint64(2), uint64(10), uint64(10000000000)])
def test_calculate_phase_out(self, sp_interval: uint64):
constants = test_constants
sub_slot_iters = uint64(sp_interval * constants.NUM_SPS_SUB_SLOT)
# Before or at HARD_FORK2_HEIGHT, should return 0
assert calculate_phase_out(constants, sub_slot_iters, uint32(constants.HARD_FORK2_HEIGHT - 1)) == 0
assert calculate_phase_out(constants, sub_slot_iters, constants.HARD_FORK2_HEIGHT) == 0
# after HARD_FORK2_HEIGHT, should return value = delta/phase_out_period * sp_interval
assert (
calculate_phase_out(constants, sub_slot_iters, uint32(constants.HARD_FORK2_HEIGHT + 1))
== sp_interval // constants.PLOT_V1_PHASE_OUT
)
assert (
calculate_phase_out(
constants, sub_slot_iters, uint32(constants.HARD_FORK2_HEIGHT + constants.PLOT_V1_PHASE_OUT // 2)
)
== sp_interval // 2
)
assert (
calculate_phase_out(
constants, sub_slot_iters, uint32(constants.HARD_FORK2_HEIGHT + constants.PLOT_V1_PHASE_OUT)
)
== sp_interval
)

# Test with maximum uint32 height to ensure no overflow
max_uint32_height = uint32(0xFFFFFFFF)
result_max_height = calculate_phase_out(constants, sub_slot_iters, max_uint32_height)
assert result_max_height == sp_interval # Should cap at sp_interval


def test_expected_plot_size_v1() -> None:
last_size = 2_400_000
Expand Down
30 changes: 30 additions & 0 deletions chia/_tests/core/custom_types/test_proof_of_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from chia.types.blockchain_format.proof_of_space import (
calculate_prefix_bits,
check_plot_size,
is_v1_phased_out,
make_pos,
passes_plot_filter,
verify_and_get_quality_string,
Expand Down Expand Up @@ -150,6 +151,7 @@ def test_verify_and_get_quality_string(caplog: pytest.LogCaptureFixture, case: P
original_challenge_hash=b32("0x73490e166d0b88347c37d921660b216c27316aae9a3450933d3ff3b854e5831a"),
signage_point=b32("0x7b3e23dbd438f9aceefa9827e2c5538898189987f49b06eceb7a43067e77b531"),
height=case.height,
prev_transaction_block_height=case.height,
)
assert quality_string is None
assert len(caplog.text) == 0 if case.expected_error is None else case.expected_error in caplog.text
Expand Down Expand Up @@ -187,6 +189,7 @@ def test_verify_and_get_quality_string_v2(caplog: pytest.LogCaptureFixture, case
original_challenge_hash=b32("0x73490e166d0b88347c37d921660b216c27316aae9a3450933d3ff3b854e5831a"),
signage_point=b32("0x7b3e23dbd438f9aceefa9827e2c5538898189987f49b06eceb7a43067e77b531"),
height=case.height,
prev_transaction_block_height=case.height,
)
except NotImplementedError as e:
assert case.expected_error is not None
Expand Down Expand Up @@ -281,3 +284,30 @@ def test_calculate_prefix_bits_v1(height: uint32, expected: int) -> None:
)
def test_calculate_prefix_bits_v2(height: uint32, expected: int) -> None:
assert calculate_prefix_bits(DEFAULT_CONSTANTS, height, PlotSize.make_v2(28)) == expected


def test_v1_phase_out() -> None:
constants = DEFAULT_CONSTANTS.replace(HARD_FORK2_HEIGHT=uint32(500000))
rng = random.Random()

phase_out_epochs = 1 << (constants.PLOT_V1_PHASE_OUT // constants.EPOCH_BLOCKS).bit_length()
print(f"phase-out epochs: {phase_out_epochs}")

for epoch in range(-5, phase_out_epochs + 5):
prev_tx_height = uint32(constants.HARD_FORK2_HEIGHT + epoch * constants.EPOCH_BLOCKS)
num_phased_out = 0
rng.seed(1337)
for i in range(1000):
proof = rng.randbytes(32)
if is_v1_phased_out(proof, prev_tx_height, constants):
num_phased_out += 1

expect = min(1.0, max(0.0, epoch / phase_out_epochs))

print(
f"height: {prev_tx_height} "
f"epoch: {epoch} "
f"phased-out: {num_phased_out / 10:0.2f}% "
f"expect: {expect * 100.0:0.2f}%"
)
assert abs((num_phased_out / 1000) - expect) < 0.05
3 changes: 3 additions & 0 deletions chia/_tests/core/full_node/test_full_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -3224,12 +3224,15 @@ async def declare_pos_unfinished_block(
include_signature_source_data=True,
)
await full_node_api.declare_proof_of_space(pospace, dummy_peer)
tx_peak = blockchain.get_tx_peak()
assert tx_peak is not None
q_str: Optional[bytes32] = verify_and_get_quality_string(
block.reward_chain_block.proof_of_space,
blockchain.constants,
challenge,
challenge_chain_sp,
height=block.reward_chain_block.height,
prev_transaction_block_height=tx_peak.height,
)
assert q_str is not None
unfinised_block = None
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
23 changes: 20 additions & 3 deletions chia/_tests/farmer_harvester/test_farmer.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,14 @@ async def test_farmer_new_proof_of_space_for_pool_stats(
}

assert (
verify_and_get_quality_string(pos, DEFAULT_CONSTANTS, case.challenge_hash, case.sp_hash, height=uint32(1))
verify_and_get_quality_string(
pos,
DEFAULT_CONSTANTS,
case.challenge_hash,
case.sp_hash,
height=uint32(1),
prev_transaction_block_height=uint32(1),
)
is not None
)

Expand Down Expand Up @@ -883,7 +890,12 @@ async def test_farmer_pool_response(

assert (
verify_and_get_quality_string(
pos, DEFAULT_CONSTANTS, sp.challenge_hash, sp.challenge_chain_sp, height=uint32(1)
pos,
DEFAULT_CONSTANTS,
sp.challenge_hash,
sp.challenge_chain_sp,
height=uint32(1),
prev_transaction_block_height=uint32(1),
)
is not None
)
Expand Down Expand Up @@ -1251,7 +1263,12 @@ async def test_farmer_additional_headers_on_partial_submit(

assert (
verify_and_get_quality_string(
pos, DEFAULT_CONSTANTS, sp.challenge_hash, sp.challenge_chain_sp, height=uint32(1)
pos,
DEFAULT_CONSTANTS,
sp.challenge_hash,
sp.challenge_chain_sp,
height=uint32(1),
prev_transaction_block_height=uint32(1),
)
is not None
)
Expand Down
1 change: 0 additions & 1 deletion chia/_tests/weight_proof/test_weight_proof.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ async def load_blocks_dont_validate(
cc_sp,
block.height,
difficulty,
sub_slot_iters,
uint32(0), # prev_tx_block(blocks, prev_b), todo need to get height of prev tx block somehow here
)
assert required_iters is not None
Expand Down
1 change: 0 additions & 1 deletion chia/consensus/block_header_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,6 @@ def validate_unfinished_header_block(
cc_sp_hash,
height,
expected_vs.difficulty,
expected_vs.ssi,
prev_tx_block(blocks, prev_b),
)
if required_iters is None:
Expand Down
2 changes: 1 addition & 1 deletion chia/consensus/default_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
HARD_FORK2_HEIGHT=uint32(0xFFFFFFFA),
# starting at the hard fork 2 height, v1 plots will gradually be phased out,
# and stop working entirely after this many blocks
PLOT_V1_PHASE_OUT=uint32(1179648),
PLOT_V1_PHASE_OUT=uint32(255 * 4608),
# June 2027
PLOT_FILTER_128_HEIGHT=uint32(10542000),
# June 2030
Expand Down
1 change: 0 additions & 1 deletion chia/consensus/multiprocess_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,6 @@ async def return_error(error_code: Err) -> PreValidationResult:
cc_sp_hash,
block.height,
vs.difficulty,
vs.ssi,
prev_tx_block(blockchain, prev_b),
)
if required_iters is None:
Expand Down
47 changes: 10 additions & 37 deletions chia/consensus/pot_iterations.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,6 @@ def calculate_sp_iters(constants: ConsensusConstants, sub_slot_iters: uint64, si
return uint64(calculate_sp_interval_iters(constants, sub_slot_iters) * signage_point_index)


def calculate_phase_out(
constants: ConsensusConstants,
sub_slot_iters: uint64,
prev_transaction_block_height: uint32,
) -> uint64:
if prev_transaction_block_height <= constants.HARD_FORK2_HEIGHT:
return uint64(0)
elif uint32(prev_transaction_block_height - constants.HARD_FORK2_HEIGHT) >= constants.PLOT_V1_PHASE_OUT:
return uint64(calculate_sp_interval_iters(constants, sub_slot_iters))

return uint64(
(
uint32(prev_transaction_block_height - constants.HARD_FORK2_HEIGHT)
* calculate_sp_interval_iters(constants, sub_slot_iters)
)
// constants.PLOT_V1_PHASE_OUT
)


def calculate_ip_iters(
constants: ConsensusConstants,
sub_slot_iters: uint64,
Expand Down Expand Up @@ -75,11 +56,15 @@ def validate_pospace_and_get_required_iters(
cc_sp_hash: bytes32,
height: uint32,
difficulty: uint64,
sub_slot_iters: uint64,
prev_transaction_block_height: uint32, # this is the height of the last tx block before the current block SP
) -> Optional[uint64]:
q_str: Optional[bytes32] = verify_and_get_quality_string(
proof_of_space, constants, challenge, cc_sp_hash, height=height
proof_of_space,
constants,
challenge,
cc_sp_hash,
height=height,
prev_transaction_block_height=prev_transaction_block_height,
)
if q_str is None:
return None
Expand All @@ -90,8 +75,6 @@ def validate_pospace_and_get_required_iters(
proof_of_space.size(),
difficulty,
cc_sp_hash,
sub_slot_iters,
prev_transaction_block_height,
)


Expand All @@ -101,27 +84,17 @@ def calculate_iterations_quality(
size: PlotSize,
difficulty: uint64,
cc_sp_output_hash: bytes32,
ssi: uint64,
prev_transaction_block_height: uint32, # this is the height of the last tx block before the current block SP
) -> uint64:
"""
Calculates the number of iterations from the quality. This is derives as the difficulty times the constant factor
times a random number between 0 and 1 (based on quality string), divided by plot size.
"""
if size.size_v1 is not None:
assert size.size_v2 is None
phase_out = calculate_phase_out(constants, ssi, prev_transaction_block_height)
else:
phase_out = uint64(0)

sp_quality_string: bytes32 = std_hash(quality_string + cc_sp_output_hash)
iters = uint64(
(
int(difficulty)
* int(constants.DIFFICULTY_CONSTANT_FACTOR)
* int.from_bytes(sp_quality_string, "big", signed=False)
// (int(pow(2, 256)) * int(_expected_plot_size(size)))
)
+ phase_out
int(difficulty)
* int(constants.DIFFICULTY_CONSTANT_FACTOR)
* int.from_bytes(sp_quality_string, "big", signed=False)
// (int(pow(2, 256)) * int(_expected_plot_size(size)))
)
return max(iters, uint64(1))
Loading
Loading