diff --git a/chia/_tests/core/consensus/test_pot_iterations.py b/chia/_tests/core/consensus/test_pot_iterations.py index 2d2ddaeba182..187213263bce 100644 --- a/chia/_tests/core/consensus/test_pot_iterations.py +++ b/chia/_tests/core/consensus/test_pot_iterations.py @@ -83,6 +83,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 + # TODO: todo_v2_plots test this for v2 plots as well def test_win_percentage(self): """ Tests that the percentage of blocks won is proportional to the space of each farmer, @@ -95,7 +96,7 @@ def test_win_percentage(self): uint8(35): 100, uint8(36): 100, } - farmer_space = {k: _expected_plot_size(uint8(k)) * count for k, count in farmer_ks.items()} + farmer_space = {k: _expected_plot_size(PlotSize.make_v1(k)) * count for k, count in farmer_ks.items()} total_space = sum(farmer_space.values()) percentage_space = {k: float(sp / total_space) for k, sp in farmer_space.items()} wins = {k: 0 for k in farmer_ks.keys()} @@ -111,7 +112,6 @@ def test_win_percentage(self): for k, count in farmer_ks.items(): for farmer_index in range(count): quality = std_hash(slot_index.to_bytes(4, "big") + k.to_bytes(1, "big") + bytes(farmer_index)) - # TODO: todo_v2_plots required_iters = calculate_iterations_quality( constants, quality, PlotSize.make_v1(k), difficulty, sp_hash, uint64(100000000), uint32(0) ) @@ -151,3 +151,24 @@ def test_calculate_phase_out(self): 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 = 4800000 + for k in range(18, 50): + plot_size = _expected_plot_size(PlotSize.make_v1(k)) + assert plot_size > last_size + last_size = plot_size + + +def test_expected_plot_size_v2() -> None: + last_size = 1700000000 + for k in range(18, 32, 2): + plot_size = _expected_plot_size(PlotSize.make_v2(k)) + # TODO: todo_v2_plots remove this special case once we support smaller k-sizes + if k < 28: + assert plot_size == 0 + continue + + assert plot_size > last_size + last_size = plot_size diff --git a/chia/_tests/core/custom_types/test_proof_of_space.py b/chia/_tests/core/custom_types/test_proof_of_space.py index 8d99a94254d6..8f78fb9a938f 100644 --- a/chia/_tests/core/custom_types/test_proof_of_space.py +++ b/chia/_tests/core/custom_types/test_proof_of_space.py @@ -5,7 +5,7 @@ from typing import Optional import pytest -from chia_rs import G1Element, PlotSize, ProofOfSpace +from chia_rs import G1Element, PlotSize from chia_rs.sized_bytes import bytes32, bytes48 from chia_rs.sized_ints import uint8, uint32 @@ -15,6 +15,7 @@ calculate_plot_difficulty, calculate_prefix_bits, check_plot_size, + make_pos, passes_plot_filter, verify_and_get_quality_string, ) @@ -24,7 +25,7 @@ class ProofOfSpaceCase: id: str pos_challenge: bytes32 - plot_size: uint8 + plot_size: PlotSize plot_public_key: G1Element pool_public_key: Optional[G1Element] = None pool_contract_puzzle_hash: Optional[bytes32] = None @@ -46,14 +47,14 @@ def b32(key: str) -> bytes32: ProofOfSpaceCase( id="Neither pool public key nor pool contract puzzle hash", pos_challenge=bytes32(b"1" * 32), - plot_size=uint8(0), + plot_size=PlotSize.make_v1(0), plot_public_key=G1Element(), expected_error="Expected pool public key or pool contract puzzle hash but got neither", ), ProofOfSpaceCase( id="Both pool public key and pool contract puzzle hash", pos_challenge=bytes32(b"1" * 32), - plot_size=uint8(0), + plot_size=PlotSize.make_v1(0), plot_public_key=G1Element(), pool_public_key=G1Element(), pool_contract_puzzle_hash=bytes32(b"1" * 32), @@ -62,7 +63,7 @@ def b32(key: str) -> bytes32: ProofOfSpaceCase( id="Lower than minimum plot size", pos_challenge=bytes32(b"1" * 32), - plot_size=uint8(31), + plot_size=PlotSize.make_v1(31), plot_public_key=G1Element(), pool_public_key=G1Element(), expected_error="Plot size is lower than the minimum", @@ -70,7 +71,7 @@ def b32(key: str) -> bytes32: ProofOfSpaceCase( id="Higher than maximum plot size", pos_challenge=bytes32(b"1" * 32), - plot_size=uint8(51), + plot_size=PlotSize.make_v1(51), plot_public_key=G1Element(), pool_public_key=G1Element(), expected_error="Plot size is higher than the maximum", @@ -78,7 +79,7 @@ def b32(key: str) -> bytes32: ProofOfSpaceCase( id="Different challenge", pos_challenge=bytes32(b"1" * 32), - plot_size=uint8(42), + plot_size=PlotSize.make_v1(42), pool_public_key=G1Element(), plot_public_key=G1Element(), expected_error="Calculated pos challenge doesn't match the provided one", @@ -86,7 +87,7 @@ def b32(key: str) -> bytes32: ProofOfSpaceCase( id="Not passing the plot filter with size 9", pos_challenge=b32("08b23cc2844dfb92d2eedaa705a1ce665d571ee753bd81cbb67b92caa6d34722"), - plot_size=uint8(42), + plot_size=PlotSize.make_v1(42), pool_public_key=g1( "b6449c2c68df97c19e884427e42ee7350982d4020571ead08732615ff39bd216bfd630b6460784982bec98b49fea79d0" ), @@ -99,7 +100,7 @@ def b32(key: str) -> bytes32: ProofOfSpaceCase( id="Passing the plot filter with size 8", pos_challenge=b32("08b23cc2844dfb92d2eedaa705a1ce665d571ee753bd81cbb67b92caa6d34722"), - plot_size=uint8(42), + plot_size=PlotSize.make_v1(42), pool_public_key=g1( "b6449c2c68df97c19e884427e42ee7350982d4020571ead08732615ff39bd216bfd630b6460784982bec98b49fea79d0" ), @@ -111,7 +112,7 @@ def b32(key: str) -> bytes32: ProofOfSpaceCase( id="v2 plot size 0", pos_challenge=bytes32(b"1" * 32), - plot_size=uint8(0x80), + plot_size=PlotSize.make_v2(0), plot_public_key=G1Element(), pool_public_key=G1Element(), expected_error="Plot size is lower than the minimum", @@ -119,7 +120,7 @@ def b32(key: str) -> bytes32: ProofOfSpaceCase( id="v2 plot size 34", pos_challenge=bytes32(b"1" * 32), - plot_size=uint8(0x80 | 34), + plot_size=PlotSize.make_v2(34), plot_public_key=G1Element(), pool_public_key=G1Element(), expected_error="Plot size is higher than the maximum", @@ -127,7 +128,7 @@ def b32(key: str) -> bytes32: ProofOfSpaceCase( id="Not passing the plot filter v2", pos_challenge=b32("3d29ea79d19b3f7e99ebf764ae53697cbe143603909873946af6ab1ece606861"), - plot_size=uint8(0x80 | 32), + plot_size=PlotSize.make_v2(32), pool_public_key=g1( "b6449c2c68df97c19e884427e42ee7350982d4020571ead08732615ff39bd216bfd630b6460784982bec98b49fea79d0" ), @@ -138,7 +139,7 @@ def b32(key: str) -> bytes32: ), ) def test_verify_and_get_quality_string(caplog: pytest.LogCaptureFixture, case: ProofOfSpaceCase) -> None: - pos = ProofOfSpace( + pos = make_pos( challenge=case.pos_challenge, pool_public_key=case.pool_public_key, pool_contract_puzzle_hash=case.pool_contract_puzzle_hash, @@ -160,7 +161,7 @@ def test_verify_and_get_quality_string(caplog: pytest.LogCaptureFixture, case: P @datacases( ProofOfSpaceCase( id="v2 plot are not implemented", - plot_size=uint8(0x80 | 30), + plot_size=PlotSize.make_v2(30), pos_challenge=b32("47deb938e145d25d7b3b3c85ca9e3972b76c01aeeb78a02fe5d3b040d282317e"), plot_public_key=g1( "afa3aaf09c03885154be49216ee7fb2e4581b9c4a4d7e9cc402e27280bf0cfdbdf1b9ba674e301fd1d1450234b3b1868" @@ -172,7 +173,7 @@ def test_verify_and_get_quality_string(caplog: pytest.LogCaptureFixture, case: P ), ) def test_verify_and_get_quality_string_v2(caplog: pytest.LogCaptureFixture, case: ProofOfSpaceCase) -> None: - pos = ProofOfSpace( + pos = make_pos( challenge=case.pos_challenge, pool_public_key=case.pool_public_key, pool_contract_puzzle_hash=case.pool_contract_puzzle_hash, diff --git a/chia/_tests/farmer_harvester/test_farmer.py b/chia/_tests/farmer_harvester/test_farmer.py index 95a42c832c9b..252da1d14a48 100644 --- a/chia/_tests/farmer_harvester/test_farmer.py +++ b/chia/_tests/farmer_harvester/test_farmer.py @@ -9,7 +9,7 @@ from unittest.mock import ANY import pytest -from chia_rs import AugSchemeMPL, G1Element, G2Element, PrivateKey, ProofOfSpace +from chia_rs import AugSchemeMPL, G1Element, G2Element, PlotSize, PrivateKey, ProofOfSpace from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint8, uint16, uint32, uint64 from pytest_mock import MockerFixture @@ -31,6 +31,7 @@ from chia.simulator.block_tools import BlockTools from chia.types.blockchain_format.proof_of_space import ( generate_plot_public_key, + make_pos, verify_and_get_quality_string, ) from chia.util.config import load_config, save_config @@ -133,7 +134,7 @@ class NewProofOfSpaceCase: plot_identifier: str signage_point_index: uint8 plot_id: bytes32 - plot_size: uint8 + plot_size: PlotSize plot_challenge: bytes32 plot_public_key: G1Element pool_public_key: Optional[G1Element] @@ -188,7 +189,7 @@ def create_verified_quality_case( plot_identifier="test", signage_point_index=uint8(1), plot_id=bytes32.fromhex("baaa6780c53d4b3739b8807b4ae79a76644ddf0d9e03dc7d0a6a0e613e764d9f"), - plot_size=uint8(32), + plot_size=PlotSize.make_v1(32), plot_challenge=bytes32.fromhex("7580e4c366dc2c94c37ce44943f9629a3cd6e027d7b24cd014adeaa578d4b0a2"), plot_public_key=G1Element.from_bytes( bytes.fromhex( @@ -574,7 +575,7 @@ async def test_farmer_new_proof_of_space_for_pool_stats( peak_height=uint32(1), last_tx_height=uint32(0), ) - pos = ProofOfSpace( + pos = make_pos( challenge=case.plot_challenge, pool_public_key=case.pool_public_key, pool_contract_puzzle_hash=case.pool_contract_puzzle_hash, @@ -715,7 +716,7 @@ def create_valid_pos(farmer: Farmer) -> tuple[farmer_protocol.NewSignagePoint, P peak_height=uint32(1), last_tx_height=uint32(0), ) - pos = ProofOfSpace( + pos = make_pos( challenge=case.plot_challenge, pool_public_key=case.pool_public_key, pool_contract_puzzle_hash=case.pool_contract_puzzle_hash, diff --git a/chia/_tests/plot_sync/test_plot_sync.py b/chia/_tests/plot_sync/test_plot_sync.py index d47ad5c0196f..58d79295600d 100644 --- a/chia/_tests/plot_sync/test_plot_sync.py +++ b/chia/_tests/plot_sync/test_plot_sync.py @@ -195,7 +195,8 @@ async def plot_sync_callback(self, peer_id: bytes32, delta: Optional[Delta]) -> plot = harvester.plot_manager.plots.get(Path(path), None) assert plot is not None assert plot.prover.get_filename() == delta.valid.additions[path].filename - assert plot.prover.get_size() == delta.valid.additions[path].size + # TODO: todo_v2_plots support v2 plots + assert plot.prover.get_size().size_v1 == delta.valid.additions[path].size assert plot.prover.get_id() == delta.valid.additions[path].plot_id assert plot.prover.get_compression_level() == delta.valid.additions[path].compression_level assert plot.pool_public_key == delta.valid.additions[path].pool_public_key @@ -256,7 +257,8 @@ async def run_sync_test(self) -> None: for path, plot_info in plot_manager.plots.items(): assert str(path) in receiver.plots() assert plot_info.prover.get_filename() == receiver.plots()[str(path)].filename - assert plot_info.prover.get_size() == receiver.plots()[str(path)].size + # TODO: todo_v2_plots support v2 plots + assert plot_info.prover.get_size().size_v1 == receiver.plots()[str(path)].size assert plot_info.prover.get_id() == receiver.plots()[str(path)].plot_id assert plot_info.prover.get_compression_level() == receiver.plots()[str(path)].compression_level assert plot_info.pool_public_key == receiver.plots()[str(path)].pool_public_key diff --git a/chia/_tests/plot_sync/test_receiver.py b/chia/_tests/plot_sync/test_receiver.py index 489d916af781..0d155b579435 100644 --- a/chia/_tests/plot_sync/test_receiver.py +++ b/chia/_tests/plot_sync/test_receiver.py @@ -7,7 +7,7 @@ from typing import Any, Callable, Union import pytest -from chia_rs import G1Element +from chia_rs import G1Element, PlotSize from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint8, uint32, uint64 @@ -185,8 +185,12 @@ def plot_sync_setup(seeded_random: random.Random) -> tuple[Receiver, list[SyncSt # Manually add the plots we want to remove in tests receiver._plots = {plot_info.filename: plot_info for plot_info in plot_info_list[0:10]} receiver._total_plot_size = sum(plot.file_size for plot in receiver.plots().values()) + # TODO: todo_v2_plots support v2 plots receiver._total_effective_plot_size = int( - sum(UI_ACTUAL_SPACE_CONSTANT_FACTOR * int(_expected_plot_size(plot.size)) for plot in receiver.plots().values()) + sum( + UI_ACTUAL_SPACE_CONSTANT_FACTOR * int(_expected_plot_size(PlotSize.make_v1(plot.size))) + for plot in receiver.plots().values() + ) ) sync_steps: list[SyncStepData] = [ SyncStepData( @@ -266,7 +270,11 @@ async def test_to_dict(counts_only: bool, seeded_random: random.Random) -> None: assert get_list_or_len(plot_sync_dict_1["duplicates"], not counts_only) == 0 assert plot_sync_dict_1["total_plot_size"] == sum(plot.file_size for plot in receiver.plots().values()) assert plot_sync_dict_1["total_effective_plot_size"] == int( - sum(UI_ACTUAL_SPACE_CONSTANT_FACTOR * int(_expected_plot_size(plot.size)) for plot in receiver.plots().values()) + # TODO: todo_v2_plots support v2 plots + sum( + UI_ACTUAL_SPACE_CONSTANT_FACTOR * int(_expected_plot_size(PlotSize.make_v1(plot.size))) + for plot in receiver.plots().values() + ) ) assert plot_sync_dict_1["syncing"] is None assert plot_sync_dict_1["last_sync_time"] is None @@ -312,8 +320,12 @@ async def test_to_dict(counts_only: bool, seeded_random: random.Random) -> None: assert get_list_or_len(sync_steps[State.duplicates].args[0], counts_only) == plot_sync_dict_3["duplicates"] assert plot_sync_dict_3["total_plot_size"] == sum(plot.file_size for plot in receiver.plots().values()) + # TODO: todo_v2_plots support v2 plots assert plot_sync_dict_3["total_effective_plot_size"] == int( - sum(UI_ACTUAL_SPACE_CONSTANT_FACTOR * int(_expected_plot_size(plot.size)) for plot in receiver.plots().values()) + sum( + UI_ACTUAL_SPACE_CONSTANT_FACTOR * int(_expected_plot_size(PlotSize.make_v1(plot.size))) + for plot in receiver.plots().values() + ) ) assert plot_sync_dict_3["last_sync_time"] > 0 assert plot_sync_dict_3["syncing"] is None diff --git a/chia/_tests/plotting/test_plot_manager.py b/chia/_tests/plotting/test_plot_manager.py index 15ea5c2e3886..d326da25e4d3 100644 --- a/chia/_tests/plotting/test_plot_manager.py +++ b/chia/_tests/plotting/test_plot_manager.py @@ -508,7 +508,8 @@ async def test_plot_info_caching(environment, bt): assert plot_manager.plots[path].prover.get_filename() == plot_info.prover.get_filename() assert plot_manager.plots[path].prover.get_id() == plot_info.prover.get_id() assert plot_manager.plots[path].prover.get_memo() == plot_info.prover.get_memo() - assert plot_manager.plots[path].prover.get_size() == plot_info.prover.get_size() + assert plot_manager.plots[path].prover.get_size().size_v1 == plot_info.prover.get_size().size_v1 + assert plot_manager.plots[path].prover.get_size().size_v2 == plot_info.prover.get_size().size_v2 assert plot_manager.plots[path].prover.get_compression_level() == plot_info.prover.get_compression_level() assert plot_manager.plots[path].pool_public_key == plot_info.pool_public_key assert plot_manager.plots[path].pool_contract_puzzle_hash == plot_info.pool_contract_puzzle_hash diff --git a/chia/_tests/util/test_full_block_utils.py b/chia/_tests/util/test_full_block_utils.py index 7dcfb9168398..3a3b3ed23970 100644 --- a/chia/_tests/util/test_full_block_utils.py +++ b/chia/_tests/util/test_full_block_utils.py @@ -67,14 +67,15 @@ def vdf_proof() -> VDFProof: def get_proof_of_space() -> Generator[ProofOfSpace, None, None]: for pool_pk in [g1(), None]: for plot_hash in [hsh(), None]: - yield ProofOfSpace( - hsh(), # challenge - pool_pk, - plot_hash, - g1(), # plot_public_key - uint8(32), - random.randbytes(8 * 32), - ) + for pos_version in [0, 0x80]: + yield ProofOfSpace( + hsh(), # challenge + pool_pk, + plot_hash, + g1(), # plot_public_key + uint8(pos_version | 32), # this is version and k-size + random.randbytes(8 * 32), + ) def get_reward_chain_block(height: uint32) -> Generator[RewardChainBlock, None, None]: diff --git a/chia/consensus/pos_quality.py b/chia/consensus/pos_quality.py index 5527a3bb5c6a..5d7e78db7e49 100644 --- a/chia/consensus/pos_quality.py +++ b/chia/consensus/pos_quality.py @@ -1,13 +1,21 @@ from __future__ import annotations +from chia_rs import PlotSize from chia_rs.sized_ints import uint64 # The actual space in bytes of a plot, is _expected_plot_size(k) * UI_ACTUAL_SPACE_CONSTANT_FACTO # This is not used in consensus, only for display purposes UI_ACTUAL_SPACE_CONSTANT_FACTOR = 0.78 +# these values are from CHIP-48 +v2_plot_sizes: dict[int, uint64] = { + 28: uint64(1717986918), + 30: uint64(4509715660), + 32: uint64(11381663334), +} -def _expected_plot_size(k: int) -> uint64: + +def _expected_plot_size(size: PlotSize) -> uint64: """ Given the plot size parameter k (which is between 32 and 59), computes the expected size of the plot in bytes (times a constant factor). This is based on efficient encoding @@ -16,4 +24,15 @@ def _expected_plot_size(k: int) -> uint64: is necessary to store the entries in the plot. """ - return uint64(((2 * k) + 1) * (2 ** (k - 1))) + k: int + if size.size_v1 is not None: + k = size.size_v1 + return uint64(((2 * k) + 1) * (2 ** (k - 1))) + else: + assert size.size_v2 is not None + k = size.size_v2 + if k in v2_plot_sizes: + return v2_plot_sizes[k] + else: + # TODO: todo_v2_plots support test plots with lower k-values + return uint64(0) diff --git a/chia/consensus/pot_iterations.py b/chia/consensus/pot_iterations.py index b1dbba146edd..513adab77581 100644 --- a/chia/consensus/pot_iterations.py +++ b/chia/consensus/pot_iterations.py @@ -117,10 +117,11 @@ def calculate_iterations_quality( 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.size_v1))) + // (int(pow(2, 256)) * int(_expected_plot_size(size))) ) + phase_out ) return max(iters, uint64(1)) else: + # TODO: todo_v2_plots support v2 plots raise NotImplementedError diff --git a/chia/harvester/harvester.py b/chia/harvester/harvester.py index cfeccb3905a9..04fb200e2e9a 100644 --- a/chia/harvester/harvester.py +++ b/chia/harvester/harvester.py @@ -190,10 +190,17 @@ def get_plots(self) -> tuple[list[dict[str, Any]], list[str], list[str]]: with self.plot_manager: for path, plot_info in self.plot_manager.plots.items(): prover = plot_info.prover + size = prover.get_size() + if size.size_v1 is not None: + k = size.size_v1 + else: + assert size.size_v2 is not None + k = size.size_v2 + # TODO: todo_v2_plots support v2 plots in RPC response response_plots.append( { "filename": str(path), - "size": prover.get_size(), + "size": k, "plot_id": prover.get_id(), "pool_public_key": plot_info.pool_public_key, "pool_contract_puzzle_hash": plot_info.pool_contract_puzzle_hash, diff --git a/chia/harvester/harvester_api.py b/chia/harvester/harvester_api.py index 994dbb0313a5..74621f51376d 100644 --- a/chia/harvester/harvester_api.py +++ b/chia/harvester/harvester_api.py @@ -6,7 +6,7 @@ from pathlib import Path from typing import TYPE_CHECKING, ClassVar, Optional, cast -from chia_rs import AugSchemeMPL, G1Element, G2Element, PlotSize, ProofOfSpace +from chia_rs import AugSchemeMPL, G1Element, G2Element, ProofOfSpace from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint8, uint32, uint64 @@ -27,6 +27,7 @@ calculate_pos_challenge, calculate_prefix_bits, generate_plot_public_key, + make_pos, passes_plot_filter, ) from chia.wallet.derive_keys import master_sk_to_local_sk @@ -147,11 +148,10 @@ def blocking_lookup(filename: Path, plot_info: PlotInfo) -> list[tuple[bytes32, # Found proofs of space (on average 1 is expected per plot) for index, quality_str in enumerate(quality_strings): - # TODO: todo_v2_plots required_iters: uint64 = calculate_iterations_quality( self.harvester.constants, quality_str, - PlotSize.make_v1(plot_info.prover.get_size()), + plot_info.prover.get_size(), difficulty, new_challenge.sp_hash, sub_slot_iters, @@ -200,12 +200,12 @@ def blocking_lookup(filename: Path, plot_info: PlotInfo) -> list[tuple[bytes32, responses.append( ( quality_str, - ProofOfSpace( + make_pos( sp_challenge_hash, plot_info.pool_public_key, plot_info.pool_contract_puzzle_hash, plot_info.plot_public_key, - uint8(plot_info.prover.get_size()), + plot_info.prover.get_size(), proof_xs, ), ) @@ -250,12 +250,11 @@ async def lookup_challenge( # This is being executed at the beginning of the slot total += 1 - # TODO: todo_v2_plots support v2 plots in PlotManager filter_prefix_bits = uint8( calculate_prefix_bits( self.harvester.constants, new_challenge.peak_height, - PlotSize.make_v1(try_plot_info.prover.get_size()), + try_plot_info.prover.get_size(), ) ) if passes_plot_filter( diff --git a/chia/plot_sync/receiver.py b/chia/plot_sync/receiver.py index 39c18d266eaf..bec063b03d5e 100644 --- a/chia/plot_sync/receiver.py +++ b/chia/plot_sync/receiver.py @@ -6,6 +6,7 @@ from dataclasses import dataclass, field from typing import Any, Callable, Optional, Union +from chia_rs import PlotSize from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import int16, uint32, uint64 from typing_extensions import Protocol @@ -349,7 +350,11 @@ async def _sync_done(self, data: PlotSyncDone) -> None: self._duplicates = self._current_sync.delta.duplicates.additions.copy() self._total_plot_size = sum(plot.file_size for plot in self._plots.values()) self._total_effective_plot_size = int( - sum(UI_ACTUAL_SPACE_CONSTANT_FACTOR * int(_expected_plot_size(plot.size)) for plot in self._plots.values()) + # TODO: todo_v2_plots support v2 plots + sum( + UI_ACTUAL_SPACE_CONSTANT_FACTOR * int(_expected_plot_size(PlotSize.make_v1(plot.size))) + for plot in self._plots.values() + ) ) # Save current sync as last sync and create a new current sync self._last_sync = self._current_sync diff --git a/chia/plot_sync/sender.py b/chia/plot_sync/sender.py index 707d5b7d8731..46189c2c53a0 100644 --- a/chia/plot_sync/sender.py +++ b/chia/plot_sync/sender.py @@ -37,10 +37,13 @@ def _convert_plot_info_list(plot_infos: list[PlotInfo]) -> list[Plot]: converted: list[Plot] = [] for plot_info in plot_infos: + # TODO: todo_v2_plots support v2 plots + k = plot_info.prover.get_size().size_v1 + assert k is not None converted.append( Plot( filename=plot_info.prover.get_filename(), - size=plot_info.prover.get_size(), + size=k, plot_id=plot_info.prover.get_id(), pool_public_key=plot_info.pool_public_key, pool_contract_puzzle_hash=plot_info.pool_contract_puzzle_hash, diff --git a/chia/plotting/cache.py b/chia/plotting/cache.py index d2cbd5000403..d97698df5071 100644 --- a/chia/plotting/cache.py +++ b/chia/plotting/cache.py @@ -165,28 +165,33 @@ def load(self) -> None: # it's here to filter invalid cache entries coming from bladebit RAM plotting. # Related: - https://github.com/Chia-Network/chia-blockchain/issues/13084 # - https://github.com/Chia-Network/chiapos/pull/337 - k = new_entry.prover.get_size() - if k not in estimated_c2_sizes: - estimated_c2_sizes[k] = ceil(2**k / 100_000_000) * ceil(k / 8) - memo_size = len(new_entry.prover.get_memo()) - prover_size = len(cache_entry.prover_data) - # Estimated C2 size + memo size + 2000 (static data + path) - # static data: version(2) + table pointers (<=96) + id(32) + k(1) => ~130 - # path: up to ~1870, all above will lead to false positive. - # See https://github.com/Chia-Network/chiapos/blob/3ee062b86315823dd775453ad320b8be892c7df3/src/prover_disk.hpp#L282-L287 # noqa: E501 - - # Use experimental measurements if more than estimates - # https://github.com/Chia-Network/chia-blockchain/issues/16063 - check_size = estimated_c2_sizes[k] + memo_size + 2000 - if k in measured_sizes: - check_size = max(check_size, measured_sizes[k]) - - if prover_size > check_size: - log.warning( - "Suspicious cache entry dropped. Recommended: stop the harvester, remove " - f"{self._path}, restart. Entry: size {prover_size}, path {path}" - ) - else: + ps = new_entry.prover.get_size() + if ps.size_v1 is not None: + k = ps.size_v1 + if k not in estimated_c2_sizes: + estimated_c2_sizes[k] = ceil(2**k / 100_000_000) * ceil(k / 8) + memo_size = len(new_entry.prover.get_memo()) + prover_size = len(cache_entry.prover_data) + # Estimated C2 size + memo size + 2000 (static data + path) + # static data: version(2) + table pointers (<=96) + id(32) + k(1) => ~130 + # path: up to ~1870, all above will lead to false positive. + # See https://github.com/Chia-Network/chiapos/blob/3ee062b86315823dd775453ad320b8be892c7df3/src/prover_disk.hpp#L282-L287 # noqa: E501 + + # Use experimental measurements if more than estimates + # https://github.com/Chia-Network/chia-blockchain/issues/16063 + check_size = estimated_c2_sizes[k] + memo_size + 2000 + if k in measured_sizes: + check_size = max(check_size, measured_sizes[k]) + + if prover_size > check_size: + log.warning( + "Suspicious cache entry dropped. Recommended: stop the harvester, remove " + f"{self._path}, restart. Entry: size {prover_size}, path {path}" + ) + else: + self._data[Path(path)] = new_entry + elif ps.size_v2 is not None: + # TODO: todo_v2_plots validate prover size self._data[Path(path)] = new_entry log.info(f"Parsed {len(self._data)} cache entries in {time.time() - start:.2f}s") diff --git a/chia/plotting/check_plots.py b/chia/plotting/check_plots.py index f4d2a7ab61b0..9bbe42735144 100644 --- a/chia/plotting/check_plots.py +++ b/chia/plotting/check_plots.py @@ -133,14 +133,16 @@ def check_plots( log.info("") log.info("") log.info(f"Starting to test each plot with {num} challenges each\n") - total_good_plots: Counter[uint8] = Counter() + total_good_plots_v1: Counter[uint8] = Counter() + total_good_plots_v2: Counter[uint8] = Counter() total_size = 0 bad_plots_list: list[Path] = [] with plot_manager: def process_plot(plot_path: Path, plot_info: PlotInfo, num_start: int, num_end: int, lock: Lock) -> None: - nonlocal total_good_plots + nonlocal total_good_plots_v1 + nonlocal total_good_plots_v2 nonlocal total_size nonlocal bad_plots_list @@ -232,8 +234,16 @@ def process_plot(plot_path: Path, plot_info: PlotInfo, num_start: int, num_end: f"\tProofs {total_proofs} / {challenges}, {round(total_proofs / float(challenges), 4)}. " f"Filepath: {plot_path}" ) - total_good_plots[pr.get_size()] += 1 - total_size += plot_path.stat().st_size + version_and_size = pr.get_size() + if version_and_size.size_v1 is not None: + k = version_and_size.size_v1 + total_good_plots_v1[k] += 1 + total_size += plot_path.stat().st_size + else: + assert version_and_size.size_v2 is not None + k = version_and_size.size_v2 + total_good_plots_v2[k] += 1 + total_size += plot_path.stat().st_size else: log.error( f"\tProofs {total_proofs} / {challenges}, {round(total_proofs / float(challenges), 4)} " @@ -255,10 +265,12 @@ def process_plot(plot_path: Path, plot_info: PlotInfo, num_start: int, num_end: log.info("") log.info("") log.info("Summary") - total_plots: int = sum(list(total_good_plots.values())) + total_plots: int = sum(list(total_good_plots_v1.values()) + list(total_good_plots_v2.values())) log.info(f"Found {total_plots} valid plots, total size {total_size / (1024 * 1024 * 1024 * 1024):.5f} TiB") - for k, count in sorted(dict(total_good_plots).items()): - log.info(f"{count} plots of size {k}") + for k, count in sorted(dict(total_good_plots_v1).items()): + log.info(f"{count} v1 plots of size {k}") + for k, count in sorted(dict(total_good_plots_v2).items()): + log.info(f"{count} v2 plots of size {k}") grand_total_bad = len(bad_plots_list) + len(plot_manager.failed_to_open_filenames) if grand_total_bad > 0: log.warning(f"{grand_total_bad} invalid plots found:") diff --git a/chia/plotting/manager.py b/chia/plotting/manager.py index 5cd9acb6eb08..147b5650b3ae 100644 --- a/chia/plotting/manager.py +++ b/chia/plotting/manager.py @@ -333,16 +333,22 @@ def process_file(file_path: Path) -> Optional[PlotInfo]: # TODO: consider checking if the file was just written to (which would mean that the file is still # being copied). A segfault might happen in this edge case. - level = prover.get_compression_level() - if level == 0: - if prover.get_size() >= 30 and stat_info.st_size < 0.98 * expected_size: - log.warning( - f"Not farming plot {file_path}. " - f"Size is {stat_info.st_size / (1024**3)} GiB, " - f"but expected at least: {expected_size / (1024**3)} GiB. " - "We assume the file is being copied." - ) - return None + version_and_size = prover.get_size() + if version_and_size.size_v1 is not None: + k = version_and_size.size_v1 + level = prover.get_compression_level() + if level == 0: + if k >= 30 and stat_info.st_size < 0.98 * expected_size: + log.warning( + f"Not farming plot {file_path}. " + f"Size is {stat_info.st_size / (1024**3)} GiB, " + f"but expected at least: {expected_size / (1024**3)} GiB. " + "We assume the file is being copied." + ) + return None + else: + # TODO: todo_v2_plots do we need to check v2 plots? + pass cache_entry = CacheEntry.from_prover(prover) self.cache.update(file_path, cache_entry) diff --git a/chia/plotting/prover.py b/chia/plotting/prover.py index cd9474b0d9cd..83d5c1c73282 100644 --- a/chia/plotting/prover.py +++ b/chia/plotting/prover.py @@ -3,6 +3,7 @@ from enum import IntEnum from typing import TYPE_CHECKING, ClassVar, Protocol, cast +from chia_rs import PlotSize from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint8 from chiapos import DiskProver @@ -20,7 +21,7 @@ class PlotVersion(IntEnum): class ProverProtocol(Protocol): def get_filename(self) -> str: ... - def get_size(self) -> uint8: ... + def get_size(self) -> PlotSize: ... def get_memo(self) -> bytes: ... def get_compression_level(self) -> uint8: ... def get_version(self) -> PlotVersion: ... @@ -45,7 +46,7 @@ def __init__(self, filename: str): def get_filename(self) -> str: return str(self._filename) - def get_size(self) -> uint8: + def get_size(self) -> PlotSize: # TODO: todo_v2_plots get k size from plot raise NotImplementedError("V2 plot format is not yet implemented") @@ -94,8 +95,8 @@ def __init__(self, disk_prover: DiskProver) -> None: def get_filename(self) -> str: return str(self._disk_prover.get_filename()) - def get_size(self) -> uint8: - return uint8(self._disk_prover.get_size()) + def get_size(self) -> PlotSize: + return PlotSize.make_v1(uint8(self._disk_prover.get_size())) def get_memo(self) -> bytes: return bytes(self._disk_prover.get_memo()) diff --git a/chia/simulator/block_tools.py b/chia/simulator/block_tools.py index 79678a4924fe..500fc69a58b5 100644 --- a/chia/simulator/block_tools.py +++ b/chia/simulator/block_tools.py @@ -28,7 +28,6 @@ G1Element, G2Element, InfusedChallengeChainSubSlot, - PlotSize, PoolTarget, PrivateKey, ProofOfSpace, @@ -94,6 +93,7 @@ calculate_prefix_bits, generate_plot_public_key, generate_taproot_sk, + make_pos, passes_plot_filter, ) from chia.types.blockchain_format.serialized_program import SerializedProgram @@ -1504,8 +1504,7 @@ def get_pospaces_for_challenge( plot_id: bytes32 = plot_info.prover.get_id() if force_plot_id is not None and plot_id != force_plot_id: continue - # TODO: todo_v2_plots support v2 plots in the plot manager - prefix_bits = calculate_prefix_bits(constants, height, PlotSize.make_v1(plot_info.prover.get_size())) + prefix_bits = calculate_prefix_bits(constants, height, plot_info.prover.get_size()) if passes_plot_filter(prefix_bits, plot_id, challenge_hash, signage_point): new_challenge: bytes32 = calculate_pos_challenge(plot_id, challenge_hash, signage_point) qualities = plot_info.prover.get_qualities_for_challenge(new_challenge) @@ -1514,8 +1513,7 @@ def get_pospaces_for_challenge( required_iters = calculate_iterations_quality( constants, quality_str, - # TODO: todo_v2_plots support v2 plots in the plot manager - PlotSize.make_v1(plot_info.prover.get_size()), + plot_info.prover.get_size(), difficulty, signage_point, sub_slot_iters, @@ -1538,7 +1536,7 @@ def get_pospaces_for_challenge( assert isinstance(pool_public_key_or_puzzle_hash, bytes32) include_taproot = True plot_pk = generate_plot_public_key(local_sk.get_g1(), farmer_public_key, include_taproot) - proof_of_space: ProofOfSpace = ProofOfSpace( + proof_of_space: ProofOfSpace = make_pos( new_challenge, plot_info.pool_public_key, plot_info.pool_contract_puzzle_hash, diff --git a/chia/types/blockchain_format/proof_of_space.py b/chia/types/blockchain_format/proof_of_space.py index 651217729043..1b574d13f329 100644 --- a/chia/types/blockchain_format/proof_of_space.py +++ b/chia/types/blockchain_format/proof_of_space.py @@ -14,6 +14,33 @@ log = logging.getLogger(__name__) +def make_pos( + challenge: bytes32, + pool_public_key: Optional[G1Element], + pool_contract_puzzle_hash: Optional[bytes32], + plot_public_key: G1Element, + version_and_size: PlotSize, + proof: bytes, +) -> ProofOfSpace: + k: int + if version_and_size.size_v1 is not None: + k = version_and_size.size_v1 + else: + assert version_and_size.size_v2 is not None + k = version_and_size.size_v2 + assert k is not None + k |= 0x80 + + return ProofOfSpace( + challenge, + pool_public_key, + pool_contract_puzzle_hash, + plot_public_key, + uint8(k), + proof, + ) + + def get_plot_id(pos: ProofOfSpace) -> bytes32: assert pos.pool_public_key is None or pos.pool_contract_puzzle_hash is None if pos.pool_public_key is None: