mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-08-24 10:05:29 -05:00
[CHIA-3795] change v1 plot phase-out (#20216)
* change v1 plot phase-out to not affect required iters, and to be quantized to epoch blocks * disable hard-fork 2 tests until we have a new test cache * fix rate limits test * address review comments
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -10,7 +9,6 @@ from chia.consensus.pos_quality import _expected_plot_size
|
||||
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,
|
||||
@@ -84,17 +82,7 @@ class TestPotIterations:
|
||||
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.
|
||||
@@ -129,59 +117,19 @@ class TestPotIterations:
|
||||
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
|
||||
|
||||
@@ -14,6 +14,7 @@ from chia.consensus.default_constants import DEFAULT_CONSTANTS
|
||||
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,
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,7 +24,6 @@ from chia.types.peer_info import PeerInfo
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -84,7 +84,7 @@ DEFAULT_CONSTANTS = ConsensusConstants(
|
||||
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
|
||||
|
||||
@@ -220,7 +220,6 @@ async def pre_validate_block(
|
||||
cc_sp_hash,
|
||||
block.height,
|
||||
vs.difficulty,
|
||||
vs.ssi,
|
||||
prev_tx_block(blockchain, prev_b),
|
||||
)
|
||||
if required_iters is None:
|
||||
|
||||
@@ -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,
|
||||
@@ -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
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -111,6 +111,7 @@ class FarmerAPI:
|
||||
new_proof_of_space.challenge_hash,
|
||||
new_proof_of_space.sp_hash,
|
||||
height=sp.peak_height,
|
||||
prev_transaction_block_height=sp.last_tx_height,
|
||||
)
|
||||
if computed_quality_string is None:
|
||||
plotid: bytes32 = get_plot_id(new_proof_of_space.proof)
|
||||
@@ -125,8 +126,6 @@ class FarmerAPI:
|
||||
new_proof_of_space.proof.size(),
|
||||
sp.difficulty,
|
||||
new_proof_of_space.sp_hash,
|
||||
sp.sub_slot_iters,
|
||||
sp.last_tx_height,
|
||||
)
|
||||
|
||||
# If the iters are good enough to make a block, proceed with the block making flow
|
||||
@@ -234,8 +233,6 @@ class FarmerAPI:
|
||||
new_proof_of_space.proof.size(),
|
||||
pool_state_dict["current_difficulty"],
|
||||
new_proof_of_space.sp_hash,
|
||||
sp.sub_slot_iters,
|
||||
sp.last_tx_height,
|
||||
)
|
||||
if required_iters >= calculate_sp_interval_iters(
|
||||
self.farmer.constants, self.farmer.constants.POOL_SUB_SLOT_ITERS
|
||||
@@ -813,6 +810,7 @@ class FarmerAPI:
|
||||
is_sp_signatures: bool = False
|
||||
sps = self.farmer.sps[response.sp_hash]
|
||||
peak_height = sps[0].peak_height
|
||||
last_tx_height = sps[0].last_tx_height
|
||||
signage_point_index = sps[0].signage_point_index
|
||||
found_sp_hash_debug = False
|
||||
for sp_candidate in sps:
|
||||
@@ -831,7 +829,12 @@ class FarmerAPI:
|
||||
include_taproot: bool = pospace.pool_contract_puzzle_hash is not None
|
||||
|
||||
computed_quality_string = verify_and_get_quality_string(
|
||||
pospace, self.farmer.constants, response.challenge_hash, response.sp_hash, height=peak_height
|
||||
pospace,
|
||||
self.farmer.constants,
|
||||
response.challenge_hash,
|
||||
response.sp_hash,
|
||||
height=peak_height,
|
||||
prev_transaction_block_height=last_tx_height,
|
||||
)
|
||||
if computed_quality_string is None:
|
||||
self.farmer.log.warning(f"Have invalid PoSpace {pospace}")
|
||||
|
||||
@@ -877,21 +877,28 @@ class FullNodeAPI:
|
||||
new_block_gen: Optional[NewBlockGenerator]
|
||||
async with self.full_node.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.high):
|
||||
peak: Optional[BlockRecord] = self.full_node.blockchain.get_peak()
|
||||
tx_peak: Optional[BlockRecord] = self.full_node.blockchain.get_tx_peak()
|
||||
|
||||
# Checks that the proof of space is valid
|
||||
height: uint32
|
||||
if peak is None:
|
||||
tx_height: uint32
|
||||
if peak is None or tx_peak is None:
|
||||
height = uint32(0)
|
||||
tx_height = uint32(0)
|
||||
else:
|
||||
height = peak.height
|
||||
tx_height = tx_peak.height
|
||||
quality_string: Optional[bytes32] = verify_and_get_quality_string(
|
||||
request.proof_of_space,
|
||||
self.full_node.constants,
|
||||
cc_challenge_hash,
|
||||
request.challenge_chain_sp,
|
||||
height=height,
|
||||
prev_transaction_block_height=tx_height,
|
||||
)
|
||||
assert quality_string is not None and len(quality_string) == 32
|
||||
if quality_string is None:
|
||||
self.log.warning("Received invalid proof of space in DeclareProofOfSpace from farmer")
|
||||
return None
|
||||
|
||||
if peak is not None:
|
||||
# Finds the last transaction block before this one
|
||||
@@ -1024,8 +1031,6 @@ class FullNodeAPI:
|
||||
request.proof_of_space.size(),
|
||||
difficulty,
|
||||
request.challenge_chain_sp,
|
||||
sub_slot_iters,
|
||||
tx_peak.height if tx_peak is not None else uint32(0),
|
||||
)
|
||||
sp_iters: uint64 = calculate_sp_iters(self.full_node.constants, sub_slot_iters, request.signage_point_index)
|
||||
ip_iters: uint64 = calculate_ip_iters(
|
||||
|
||||
@@ -1000,7 +1000,7 @@ def _validate_segment(
|
||||
if sampled and sub_slot_data.is_challenge():
|
||||
after_challenge = True
|
||||
required_iters = __validate_pospace(
|
||||
constants, segment, idx, curr_difficulty, curr_ssi, ses, first_segment_in_se, height
|
||||
constants, segment, idx, curr_difficulty, ses, first_segment_in_se, height
|
||||
)
|
||||
if required_iters is None:
|
||||
return False, uint64(0), uint64(0), uint64(0), []
|
||||
@@ -1267,7 +1267,7 @@ def validate_recent_blocks(
|
||||
required_iters = caluclated_required_iters
|
||||
else:
|
||||
ret = _validate_pospace_recent_chain(
|
||||
constants, sub_blocks, block, challenge, diff, ssi, overflow, prev_challenge
|
||||
constants, sub_blocks, block, challenge, diff, overflow, prev_challenge
|
||||
)
|
||||
if ret is None:
|
||||
return False, []
|
||||
@@ -1310,7 +1310,6 @@ def _validate_pospace_recent_chain(
|
||||
block: HeaderBlock,
|
||||
challenge: bytes32,
|
||||
diff: uint64,
|
||||
ssi: uint64,
|
||||
overflow: bool,
|
||||
prev_challenge: bytes32,
|
||||
) -> Optional[uint64]:
|
||||
@@ -1328,7 +1327,6 @@ def _validate_pospace_recent_chain(
|
||||
cc_sp_hash,
|
||||
block.height,
|
||||
diff,
|
||||
ssi,
|
||||
prev_tx_block(blocks, blocks.block_record(block.prev_header_hash)),
|
||||
)
|
||||
if required_iters is None:
|
||||
@@ -1343,7 +1341,6 @@ def __validate_pospace(
|
||||
segment: SubEpochChallengeSegment,
|
||||
idx: int,
|
||||
curr_diff: uint64,
|
||||
curr_sub_slot_iters: uint64,
|
||||
ses: Optional[SubEpochSummary],
|
||||
first_in_sub_epoch: bool,
|
||||
height: uint32,
|
||||
@@ -1370,6 +1367,9 @@ def __validate_pospace(
|
||||
# validate proof of space
|
||||
assert sub_slot_data.proof_of_space is not None
|
||||
|
||||
# when sampling blocks as part of weight proof validation, the previous
|
||||
# transaction height is a conservative estimate, since we don't have direct
|
||||
# access to it.
|
||||
required_iters = validate_pospace_and_get_required_iters(
|
||||
constants,
|
||||
sub_slot_data.proof_of_space,
|
||||
@@ -1377,8 +1377,7 @@ def __validate_pospace(
|
||||
cc_sp_hash,
|
||||
height,
|
||||
curr_diff,
|
||||
curr_sub_slot_iters,
|
||||
uint32(0), # prev_tx_block(blocks, prev_b), todo need to get height of prev tx block somehow here
|
||||
uint32(max(0, height - constants.MAX_SUB_SLOT_BLOCKS)),
|
||||
)
|
||||
if required_iters is None:
|
||||
log.error("could not verify proof of space")
|
||||
|
||||
@@ -29,6 +29,7 @@ from chia.types.blockchain_format.proof_of_space import (
|
||||
calculate_pos_challenge,
|
||||
calculate_prefix_bits,
|
||||
generate_plot_public_key,
|
||||
is_v1_phased_out,
|
||||
make_pos,
|
||||
passes_plot_filter,
|
||||
)
|
||||
@@ -192,8 +193,6 @@ class HarvesterAPI:
|
||||
plot_info.prover.get_size(),
|
||||
difficulty,
|
||||
new_challenge.sp_hash,
|
||||
sub_slot_iters,
|
||||
new_challenge.last_tx_height,
|
||||
)
|
||||
|
||||
if required_iters >= sp_interval_iters:
|
||||
@@ -287,8 +286,6 @@ class HarvesterAPI:
|
||||
plot_info.prover.get_size(),
|
||||
difficulty,
|
||||
new_challenge.sp_hash,
|
||||
sub_slot_iters,
|
||||
new_challenge.last_tx_height,
|
||||
)
|
||||
sp_interval_iters = calculate_sp_interval_iters(self.harvester.constants, sub_slot_iters)
|
||||
if required_iters < sp_interval_iters:
|
||||
@@ -299,6 +296,17 @@ class HarvesterAPI:
|
||||
proof_xs = plot_info.prover.get_full_proof(
|
||||
sp_challenge_hash, index, self.harvester.parallel_read
|
||||
)
|
||||
|
||||
if is_v1_phased_out(proof_xs, new_challenge.last_tx_height, constants):
|
||||
self.harvester.log.info(
|
||||
f"Proof dropped due to hard fork phase-out of v1 plots: {filename}"
|
||||
)
|
||||
self.harvester.log.info(
|
||||
f"File: {filename} Plot ID: {plot_id.hex()}, challenge: {sp_challenge_hash}, "
|
||||
f"plot_info: {plot_info}"
|
||||
)
|
||||
continue
|
||||
|
||||
except RuntimeError as e:
|
||||
if str(e) == "GRResult_NoProof received":
|
||||
self.harvester.log.info(
|
||||
|
||||
@@ -96,6 +96,7 @@ from chia.types.blockchain_format.proof_of_space import (
|
||||
calculate_prefix_bits,
|
||||
generate_plot_public_key,
|
||||
generate_taproot_sk,
|
||||
is_v1_phased_out,
|
||||
make_pos,
|
||||
passes_plot_filter,
|
||||
)
|
||||
@@ -153,6 +154,7 @@ test_constants = DEFAULT_CONSTANTS.replace(
|
||||
# Allows creating blockchains with timestamps up to 10 days in the future, for testing
|
||||
MAX_FUTURE_TIME2=uint32(3600 * 24 * 10),
|
||||
MEMPOOL_BLOCK_BUFFER=uint8(6),
|
||||
PLOT_V1_PHASE_OUT=uint32(5 * 340),
|
||||
)
|
||||
|
||||
|
||||
@@ -1550,6 +1552,10 @@ class BlockTools:
|
||||
if not passes_plot_filter(prefix_bits, plot_id, challenge_hash, signage_point):
|
||||
continue
|
||||
|
||||
# TODO: todo_v2_plots change the existing PLOT_V1_PHASE_OUT constant to
|
||||
# direclty specify the power-of-two epochs to phase-out over
|
||||
phase_out_epochs = 1 << (constants.PLOT_V1_PHASE_OUT // constants.EPOCH_BLOCKS).bit_length()
|
||||
|
||||
if plot_info.prover.get_version() == PlotVersion.V2:
|
||||
# v2 plots aren't valid until after the hard fork
|
||||
if prev_tx_height < constants.HARD_FORK2_HEIGHT:
|
||||
@@ -1561,7 +1567,7 @@ class BlockTools:
|
||||
f"cannot be used for farming: {plot_info.prover.get_filename()}"
|
||||
)
|
||||
continue
|
||||
elif prev_tx_height >= constants.HARD_FORK2_HEIGHT + constants.PLOT_V1_PHASE_OUT:
|
||||
elif prev_tx_height >= constants.HARD_FORK2_HEIGHT + phase_out_epochs * constants.EPOCH_BLOCKS:
|
||||
continue
|
||||
|
||||
new_challenge: bytes32 = calculate_pos_challenge(plot_id, challenge_hash, signage_point)
|
||||
@@ -1577,8 +1583,6 @@ class BlockTools:
|
||||
plot_info.prover.get_size(),
|
||||
difficulty,
|
||||
signage_point,
|
||||
sub_slot_iters,
|
||||
prev_tx_height,
|
||||
)
|
||||
if required_iters >= calculate_sp_interval_iters(constants, sub_slot_iters):
|
||||
continue
|
||||
@@ -1586,6 +1590,8 @@ class BlockTools:
|
||||
proof = b""
|
||||
if isinstance(plot_info.prover, V1Prover):
|
||||
proof = plot_info.prover.get_full_proof(new_challenge, idx)
|
||||
if is_v1_phased_out(proof, prev_tx_height, constants):
|
||||
continue
|
||||
elif isinstance(plot_info.prover, V2Prover):
|
||||
assert isinstance(quality, V2Quality)
|
||||
partial_proof = plot_info.prover.get_partial_proof(quality)
|
||||
@@ -1852,7 +1858,6 @@ def load_block_list(
|
||||
sp_hash,
|
||||
full_block.height,
|
||||
uint64(difficulty),
|
||||
sub_slot_iters,
|
||||
prev_transaction_b_height,
|
||||
)
|
||||
assert required_iters is not None
|
||||
|
||||
@@ -34,7 +34,6 @@ def iters_from_block(
|
||||
cc_sp,
|
||||
height,
|
||||
difficulty,
|
||||
sub_slot_iters,
|
||||
prev_transaction_block_height,
|
||||
)
|
||||
assert required_iters is not None
|
||||
|
||||
@@ -75,6 +75,40 @@ def check_plot_size(constants: ConsensusConstants, ps: PlotSize) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def is_v1_phased_out(
|
||||
proof: bytes,
|
||||
prev_transaction_block_height: uint32, # this is the height of the last tx block before the current block SP
|
||||
constants: ConsensusConstants,
|
||||
) -> bool:
|
||||
if prev_transaction_block_height < constants.HARD_FORK2_HEIGHT:
|
||||
return False
|
||||
|
||||
# This is a v1 plot and the phase-out period has started
|
||||
# The probability of having been phased out is proportional on the
|
||||
# number of epochs since hard fork activation
|
||||
|
||||
# TODO: todo_v2_plots change the existing PLOT_V1_PHASE_OUT constant to
|
||||
# direclty specify the power-of-two epochs to phase-out over
|
||||
phase_out_epoch_bits = (constants.PLOT_V1_PHASE_OUT // constants.EPOCH_BLOCKS).bit_length()
|
||||
|
||||
phase_out_epoch_mask = (1 << phase_out_epoch_bits) - 1
|
||||
|
||||
# we just look at one byte so the mask can't be bigger than that
|
||||
assert phase_out_epoch_mask < 256
|
||||
|
||||
# this counter is counting down to zero
|
||||
epoch_counter = (1 << phase_out_epoch_bits) - (
|
||||
prev_transaction_block_height - constants.HARD_FORK2_HEIGHT
|
||||
) // constants.EPOCH_BLOCKS
|
||||
|
||||
# if we're past the phase-out, v1 plots are unconditionally invalid
|
||||
if epoch_counter <= 0:
|
||||
return True
|
||||
|
||||
proof_value = std_hash(proof + b"chia proof-of-space v1 phase-out")[0] & phase_out_epoch_mask
|
||||
return proof_value > epoch_counter
|
||||
|
||||
|
||||
def verify_and_get_quality_string(
|
||||
pos: ProofOfSpace,
|
||||
constants: ConsensusConstants,
|
||||
@@ -82,7 +116,14 @@ def verify_and_get_quality_string(
|
||||
signage_point: bytes32,
|
||||
*,
|
||||
height: uint32,
|
||||
prev_transaction_block_height: uint32, # this is the height of the last tx block before the current block SP
|
||||
) -> Optional[bytes32]:
|
||||
plot_size = pos.size()
|
||||
|
||||
if plot_size.size_v1 is not None and is_v1_phased_out(pos.proof, prev_transaction_block_height, constants):
|
||||
log.info("v1 proof has been phased-out and is no longer valid")
|
||||
return None
|
||||
|
||||
# Exactly one of (pool_public_key, pool_contract_puzzle_hash) must not be None
|
||||
if (pos.pool_public_key is None) and (pos.pool_contract_puzzle_hash is None):
|
||||
log.error("Expected pool public key or pool contract puzzle hash but got neither")
|
||||
@@ -91,7 +132,6 @@ def verify_and_get_quality_string(
|
||||
log.error("Expected pool public key or pool contract puzzle hash but got both")
|
||||
return None
|
||||
|
||||
plot_size = pos.size()
|
||||
if not check_plot_size(constants, plot_size):
|
||||
return None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user