diff --git a/chia/_tests/conftest.py b/chia/_tests/conftest.py index 9305887a267b..d3facf98d5fd 100644 --- a/chia/_tests/conftest.py +++ b/chia/_tests/conftest.py @@ -1288,6 +1288,7 @@ async def farmer_harvester_2_simulators_zero_bits_plot_filter( num_og_plots=0, num_pool_plots=0, num_non_keychain_plots=0, + num_v2_plots=0, config_overrides=config_overrides, ) for _ in range(2) 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 7bddd83d3696..71c40ebc6bdb 100644 --- a/chia/_tests/core/custom_types/test_proof_of_space.py +++ b/chia/_tests/core/custom_types/test_proof_of_space.py @@ -13,7 +13,6 @@ from chia.consensus.default_constants import DEFAULT_CONSTANTS from chia.types.blockchain_format.proof_of_space import ( calculate_prefix_bits, - calculate_required_plot_strength, check_plot_size, make_pos, passes_plot_filter, @@ -113,25 +112,23 @@ def b32(key: str) -> bytes32: id="v2 plot size 0", pos_challenge=bytes32(b"1" * 32), plot_size=PlotSize.make_v2(0), + pool_contract_puzzle_hash=bytes32(b"1" * 32), plot_public_key=G1Element(), - pool_public_key=G1Element(), expected_error="Plot size is lower than the minimum", ), ProofOfSpaceCase( id="v2 plot size 34", pos_challenge=bytes32(b"1" * 32), plot_size=PlotSize.make_v2(34), + pool_contract_puzzle_hash=bytes32(b"1" * 32), plot_public_key=G1Element(), - pool_public_key=G1Element(), expected_error="Plot size is higher than the maximum", ), ProofOfSpaceCase( id="Not passing the plot filter v2", - pos_challenge=b32("3d29ea79d19b3f7e99ebf764ae53697cbe143603909873946af6ab1ece606861"), + pos_challenge=b32("4cfaacbd2782db64d07cf490ca938534adb07dfbd2f92b0e479e2e5b196178db"), plot_size=PlotSize.make_v2(32), - pool_public_key=g1( - "b6449c2c68df97c19e884427e42ee7350982d4020571ead08732615ff39bd216bfd630b6460784982bec98b49fea79d0" - ), + pool_contract_puzzle_hash=bytes32(b"1" * 32), plot_public_key=g1( "879526b4e7b616cfd64984d8ad140d0798b048392a6f11e2faf09054ef467ea44dc0dab5e5edb2afdfa850c5c8b629cc" ), @@ -161,16 +158,15 @@ def test_verify_and_get_quality_string(caplog: pytest.LogCaptureFixture, case: P @datacases( ProofOfSpaceCase( id="v2 plot are not implemented", - plot_size=PlotSize.make_v2(30), - pos_challenge=b32("47deb938e145d25d7b3b3c85ca9e3972b76c01aeeb78a02fe5d3b040d282317e"), + plot_size=PlotSize.make_v2(28), + pos_challenge=b32("6a85c4c5f21e5728c6668b01c0758e33bb7f3ae20d38703d4863ad151f983d9c"), plot_public_key=g1( "afa3aaf09c03885154be49216ee7fb2e4581b9c4a4d7e9cc402e27280bf0cfdbdf1b9ba674e301fd1d1450234b3b1868" ), - pool_public_key=g1( - "b6449c2c68df97c19e884427e42ee7350982d4020571ead08732615ff39bd216bfd630b6460784982bec98b49fea79d0" - ), - expected_error="NotImplementedError", + pool_contract_puzzle_hash=bytes32(b"1" * 32), + expected_error="Did not pass the plot filter", ), + # TODO: todo_v2_plots add test case that passes the plot filter ) def test_verify_and_get_quality_string_v2(caplog: pytest.LogCaptureFixture, case: ProofOfSpaceCase) -> None: pos = make_pos( @@ -200,29 +196,6 @@ def test_verify_and_get_quality_string_v2(caplog: pytest.LogCaptureFixture, case assert len(caplog.text) == 0 if case.expected_error is None else case.expected_error in caplog.text -@pytest.mark.parametrize( - "height, strength", - [ - (0, 2), - (DEFAULT_CONSTANTS.HARD_FORK_HEIGHT, 2), - (DEFAULT_CONSTANTS.HARD_FORK2_HEIGHT, 2), - (DEFAULT_CONSTANTS.PLOT_STRENGTH_4_HEIGHT - 1, 2), - (DEFAULT_CONSTANTS.PLOT_STRENGTH_4_HEIGHT, 4), - (DEFAULT_CONSTANTS.PLOT_STRENGTH_5_HEIGHT - 1, 4), - (DEFAULT_CONSTANTS.PLOT_STRENGTH_5_HEIGHT, 5), - (DEFAULT_CONSTANTS.PLOT_STRENGTH_6_HEIGHT - 1, 5), - (DEFAULT_CONSTANTS.PLOT_STRENGTH_6_HEIGHT, 6), - (DEFAULT_CONSTANTS.PLOT_STRENGTH_7_HEIGHT - 1, 6), - (DEFAULT_CONSTANTS.PLOT_STRENGTH_7_HEIGHT, 7), - (DEFAULT_CONSTANTS.PLOT_STRENGTH_8_HEIGHT - 1, 7), - (DEFAULT_CONSTANTS.PLOT_STRENGTH_8_HEIGHT, 8), - (DEFAULT_CONSTANTS.PLOT_STRENGTH_8_HEIGHT + 1000000, 8), - ], -) -def test_calculate_plot_strength(height: uint32, strength: uint8) -> None: - assert calculate_required_plot_strength(DEFAULT_CONSTANTS, height) == strength - - @pytest.mark.parametrize( "size, valid", [ @@ -272,12 +245,9 @@ def test_can_create_proof(self, prefix_bits: int, seeded_random: random.Random) @pytest.mark.parametrize("height,expected", [(0, 3), (5496000, 2), (10542000, 1), (15592000, 0), (20643000, 0)]) -@pytest.mark.parametrize("plot_size", [PlotSize.make_v1(32), PlotSize.make_v2(28)]) -def test_calculate_prefix_bits_clamp_zero(height: uint32, expected: int, plot_size: PlotSize) -> None: +def test_calculate_prefix_bits_clamp_zero_v1(height: uint32, expected: int) -> None: constants = DEFAULT_CONSTANTS.replace(NUMBER_ZERO_BITS_PLOT_FILTER_V1=uint8(3)) - if plot_size.size_v2 is not None: - expected = constants.NUMBER_ZERO_BITS_PLOT_FILTER_V2 - assert calculate_prefix_bits(constants, height, plot_size) == expected + assert calculate_prefix_bits(constants, height, PlotSize.make_v1(32)) == expected @pytest.mark.parametrize( @@ -294,9 +264,20 @@ def test_calculate_prefix_bits_clamp_zero(height: uint32, expected: int, plot_si (20643000, 5), ], ) -@pytest.mark.parametrize("plot_size", [PlotSize.make_v1(32), PlotSize.make_v2(28)]) -def test_calculate_prefix_bits_default(height: uint32, expected: int, plot_size: PlotSize) -> None: - constants = DEFAULT_CONSTANTS - if plot_size.size_v2 is not None: - expected = DEFAULT_CONSTANTS.NUMBER_ZERO_BITS_PLOT_FILTER_V2 - assert calculate_prefix_bits(constants, height, plot_size) == expected +def test_calculate_prefix_bits_v1(height: uint32, expected: int) -> None: + assert calculate_prefix_bits(DEFAULT_CONSTANTS, height, PlotSize.make_v1(32)) == expected + + +@pytest.mark.parametrize( + argnames=["height", "expected"], + argvalues=[ + (0, 5), + (0xFFFFFFFA, 5), + (0xFFFFFFFB, 6), + (0xFFFFFFFC, 7), + (0xFFFFFFFD, 8), + (0xFFFFFFFF, 8), + ], +) +def test_calculate_prefix_bits_v2(height: uint32, expected: int) -> None: + assert calculate_prefix_bits(DEFAULT_CONSTANTS, height, PlotSize.make_v2(28)) == expected diff --git a/chia/_tests/core/test_farmer_harvester_rpc.py b/chia/_tests/core/test_farmer_harvester_rpc.py index 77ceb597b63f..5929ecbfccea 100644 --- a/chia/_tests/core/test_farmer_harvester_rpc.py +++ b/chia/_tests/core/test_farmer_harvester_rpc.py @@ -294,7 +294,7 @@ async def wait_for_plot_sync() -> bool: await farmer_api.farmer.update_pool_state() pool_plot_count: int = (await farmer_rpc_client.get_pool_state())["pool_state"][0]["plot_count"] - assert pool_plot_count == 5 + assert pool_plot_count == 20 # TODO: Maybe improve this to not remove from Receiver directly but instead from the harvester and then wait for # plot sync event. @@ -334,8 +334,8 @@ def test_plot_matches_filter(filter_item: FilterItem, match: bool) -> None: @pytest.mark.parametrize( "endpoint, filtering, sort_key, reverse, expected_plot_count", [ - (FarmerRpcClient.get_harvester_plots_valid, [], "filename", False, 20), - (FarmerRpcClient.get_harvester_plots_valid, [], "size", True, 20), + (FarmerRpcClient.get_harvester_plots_valid, [], "filename", False, 35), + (FarmerRpcClient.get_harvester_plots_valid, [], "size", True, 35), ( FarmerRpcClient.get_harvester_plots_valid, [FilterItem("pool_contract_puzzle_hash", None)], @@ -350,6 +350,20 @@ def test_plot_matches_filter(filter_item: FilterItem, match: bool) -> None: False, 4, ), + ( + FarmerRpcClient.get_harvester_plots_valid, + [FilterItem("strength", "2")], + "size", + True, + 15, + ), + ( + FarmerRpcClient.get_harvester_plots_valid, + [FilterItem("strength", "0")], + "size", + True, + 20, + ), (FarmerRpcClient.get_harvester_plots_invalid, [], None, True, 13), (FarmerRpcClient.get_harvester_plots_invalid, ["invalid_0"], None, False, 6), (FarmerRpcClient.get_harvester_plots_invalid, ["inval", "lid_1"], None, False, 2), diff --git a/chia/_tests/farmer_harvester/test_farmer_harvester.py b/chia/_tests/farmer_harvester/test_farmer_harvester.py index 9d25e6815f5a..481d16ea8b1d 100644 --- a/chia/_tests/farmer_harvester/test_farmer_harvester.py +++ b/chia/_tests/farmer_harvester/test_farmer_harvester.py @@ -338,9 +338,11 @@ async def test_v2_partial_proofs_new_sp_hash( challenge_hash=bytes32(b"2" * 32), sp_hash=sp_hash, plot_identifier="test_plot_id", - partial_proofs=[b"test_partial_proof_1"], + partial_proofs=[[uint64(1), uint64(2), uint64(3), uint64(4)]], signage_point_index=uint8(0), plot_size=uint8(32), + strength=uint8(5), + plot_id=bytes32.fromhex("abababababababababababababababababababababababababababababababab"), pool_public_key=None, pool_contract_puzzle_hash=bytes32(b"4" * 32), plot_public_key=G1Element(), @@ -367,9 +369,11 @@ async def test_v2_partial_proofs_missing_sp_hash( challenge_hash=bytes32(b"2" * 32), sp_hash=sp_hash, plot_identifier="test_plot_id", - partial_proofs=[b"test_partial_proof_1"], + partial_proofs=[[uint64(1), uint64(2), uint64(3), uint64(4)]], signage_point_index=uint8(0), plot_size=uint8(32), + plot_id=bytes32.fromhex("abababababababababababababababababababababababababababababababab"), + strength=uint8(5), pool_public_key=None, pool_contract_puzzle_hash=bytes32(b"4" * 32), plot_public_key=G1Element(), @@ -409,9 +413,11 @@ async def test_v2_partial_proofs_with_existing_sp( challenge_hash=challenge_hash, sp_hash=sp_hash, plot_identifier="test_plot_id", - partial_proofs=[b"test_partial_proof_1", b"test_partial_proof_2"], + partial_proofs=[[uint64(1), uint64(2), uint64(3), uint64(4)], [uint64(2), uint64(3), uint64(4), uint64(5)]], signage_point_index=uint8(0), plot_size=uint8(32), + plot_id=bytes32.fromhex("abababababababababababababababababababababababababababababababab"), + strength=uint8(5), pool_public_key=G1Element(), pool_contract_puzzle_hash=bytes32(b"4" * 32), plot_public_key=G1Element(), @@ -441,9 +447,11 @@ async def test_solution_response_handler( challenge_hash=challenge_hash, sp_hash=sp_hash, plot_identifier="test_plot_id", - partial_proofs=[b"test_partial_proof_for_quality"], + partial_proofs=[[uint64(1), uint64(2), uint64(3), uint64(4)]], signage_point_index=uint8(0), plot_size=uint8(32), + plot_id=bytes32.fromhex("abababababababababababababababababababababababababababababababab"), + strength=uint8(5), pool_public_key=G1Element(), pool_contract_puzzle_hash=bytes32(b"4" * 32), plot_public_key=G1Element(), @@ -452,7 +460,8 @@ async def test_solution_response_handler( harvester_peer = await get_harvester_peer(farmer) # manually add pending request - farmer.pending_solver_requests[partial_proofs.partial_proofs[0]] = { + key = bytes(partial_proofs.partial_proofs[0]) + farmer.pending_solver_requests[key] = { "proof_data": partial_proofs, "peer": harvester_peer, } @@ -477,7 +486,8 @@ async def test_solution_response_handler( assert original_peer == harvester_peer # verify pending request was removed - assert partial_proofs.partial_proofs[0] not in farmer.pending_solver_requests + key = bytes(partial_proofs.partial_proofs[0]) + assert key not in farmer.pending_solver_requests @pytest.mark.anyio @@ -492,7 +502,9 @@ async def test_solution_response_unknown_quality( solver_peer = await get_solver_peer(farmer) # create solution response with unknown quality - solution_response = solver_protocol.SolverResponse(partial_proof=bytes(b"1" * 32), proof=b"test_proof") + solution_response = solver_protocol.SolverResponse( + partial_proof=[uint64(1), uint64(2), uint64(3), uint64(4)], proof=b"test_proof" + ) with unittest.mock.patch.object(farmer_api, "new_proof_of_space", new_callable=AsyncMock) as mock_new_proof: await farmer_api.solution_response(solution_response, solver_peer) @@ -518,9 +530,11 @@ async def test_solution_response_empty_proof( challenge_hash=challenge_hash, sp_hash=sp_hash, plot_identifier="test_plot_id", - partial_proofs=[b"test_partial_proof_for_quality"], + partial_proofs=[[uint64(1), uint64(2), uint64(3), uint64(4)], [uint64(2), uint64(3), uint64(4), uint64(5)]], signage_point_index=uint8(0), plot_size=uint8(32), + plot_id=bytes32.fromhex("abababababababababababababababababababababababababababababababab"), + strength=uint8(5), pool_public_key=G1Element(), pool_contract_puzzle_hash=bytes32(b"4" * 32), plot_public_key=G1Element(), @@ -530,8 +544,9 @@ async def test_solution_response_empty_proof( harvester_peer.peer_node_id = "harvester_peer" # manually add pending request - farmer.pending_solver_requests[partial_proofs.partial_proofs[0]] = { - "proof_data": partial_proofs.partial_proofs[0], + key = bytes(partial_proofs.partial_proofs[0]) + farmer.pending_solver_requests[key] = { + "proof_data": partial_proofs, "peer": harvester_peer, } @@ -548,7 +563,8 @@ async def test_solution_response_empty_proof( mock_new_proof.assert_not_called() # verify pending request was removed (cleanup still happens) - assert partial_proofs.partial_proofs[0] not in farmer.pending_solver_requests + key = bytes(partial_proofs.partial_proofs[0]) + assert key not in farmer.pending_solver_requests @pytest.mark.anyio @@ -579,9 +595,11 @@ async def test_v2_partial_proofs_solver_exception( challenge_hash=challenge_hash, sp_hash=sp_hash, plot_identifier="test_plot_id", - partial_proofs=[b"test_partial_proof_1"], + partial_proofs=[[uint64(1), uint64(2), uint64(3), uint64(4)], [uint64(2), uint64(3), uint64(4), uint64(5)]], signage_point_index=uint8(0), plot_size=uint8(32), + plot_id=bytes32.fromhex("abababababababababababababababababababababababababababababababab"), + strength=uint8(5), pool_public_key=G1Element(), pool_contract_puzzle_hash=bytes32(b"4" * 32), plot_public_key=G1Element(), @@ -594,4 +612,5 @@ async def test_v2_partial_proofs_solver_exception( await farmer_api.partial_proofs(partial_proofs, harvester_peer) # verify pending request was cleaned up after exception - assert partial_proofs.partial_proofs[0] not in farmer.pending_solver_requests + key = bytes(partial_proofs.partial_proofs[0]) + assert key not in farmer.pending_solver_requests diff --git a/chia/_tests/harvester/test_harvester_api.py b/chia/_tests/harvester/test_harvester_api.py index 5c9e6d59ba2c..088206aedda2 100644 --- a/chia/_tests/harvester/test_harvester_api.py +++ b/chia/_tests/harvester/test_harvester_api.py @@ -15,6 +15,7 @@ from chia._tests.plotting.util import get_test_plots from chia._tests.util.time_out_assert import time_out_assert from chia.harvester.harvester_api import HarvesterAPI +from chia.plotting.prover import V1Prover, V2Prover from chia.plotting.util import PlotInfo from chia.protocols import harvester_protocol from chia.protocols.harvester_protocol import PoolDifficulty @@ -85,10 +86,16 @@ def create_test_setup( @contextmanager def mock_successful_proof(plot_info: PlotInfo) -> Iterator[None]: - with patch.object(plot_info.prover, "get_full_proof") as mock_get_proof: - mock_proof = MagicMock(spec=ProofOfSpace) - mock_get_proof.return_value = mock_proof, None - yield + if isinstance(plot_info.prover, V1Prover): + with patch.object(plot_info.prover, "get_full_proof") as mock_get_proof: + mock_proof = MagicMock(spec=ProofOfSpace) + mock_get_proof.return_value = mock_proof, None + yield + elif isinstance(plot_info.prover, V2Prover): + with patch.object(plot_info.prover, "get_partial_proof") as mock_get_proof: + mock_proof = MagicMock(spec=ProofOfSpace) + mock_get_proof.return_value = [uint64(1)] * 64, None + yield def assert_farming_info_sent(mock_peer: MagicMock) -> None: diff --git a/chia/_tests/plot_sync/test_delta.py b/chia/_tests/plot_sync/test_delta.py index c5b4b5b1ff97..11c7433b771e 100644 --- a/chia/_tests/plot_sync/test_delta.py +++ b/chia/_tests/plot_sync/test_delta.py @@ -17,6 +17,7 @@ def dummy_plot(path: str) -> Plot: return Plot( filename=path, size=uint8(32), + strength=uint8(0), plot_id=bytes32(b"\00" * 32), pool_public_key=G1Element(), pool_contract_puzzle_hash=None, diff --git a/chia/_tests/plot_sync/test_plot_sync.py b/chia/_tests/plot_sync/test_plot_sync.py index 58d79295600d..e0993f11bbe8 100644 --- a/chia/_tests/plot_sync/test_plot_sync.py +++ b/chia/_tests/plot_sync/test_plot_sync.py @@ -68,6 +68,7 @@ def create_mock_plot(info: MockPlotInfo) -> Plot: return Plot( info.prover.get_filename(), uint8(0), + uint8(0), bytes32.zeros, None, None, diff --git a/chia/_tests/plot_sync/test_receiver.py b/chia/_tests/plot_sync/test_receiver.py index 0d155b579435..1074f2e0c3cc 100644 --- a/chia/_tests/plot_sync/test_receiver.py +++ b/chia/_tests/plot_sync/test_receiver.py @@ -171,6 +171,7 @@ def plot_sync_setup(seeded_random: random.Random) -> tuple[Receiver, list[SyncSt Plot( filename=str(x), size=uint8(0), + strength=uint8(0), plot_id=bytes32.random(seeded_random), pool_contract_puzzle_hash=None, pool_public_key=None, diff --git a/chia/_tests/plotting/test_prover.py b/chia/_tests/plotting/test_prover.py index c5ee38e6fee6..66b4a5f6c076 100644 --- a/chia/_tests/plotting/test_prover.py +++ b/chia/_tests/plotting/test_prover.py @@ -12,44 +12,57 @@ class TestProver: + def test_get_prover_from_file_with_plot1_still_works(self) -> None: + with tempfile.NamedTemporaryFile(suffix=".plot", delete=False) as f: + temp_path = f.name + try: + with pytest.raises(Exception) as exc_info: + get_prover_from_file(temp_path) + assert not isinstance(exc_info.value, NotImplementedError) + finally: + Path(temp_path).unlink() + + def test_unsupported_file_extension_raises_value_error(self) -> None: + with pytest.raises(ValueError, match="Unsupported plot file"): + get_prover_from_file("/nonexistent/path/test.txt") + + +# TODO: todo_v2_plots enable these tests once we have test plots we can load +@pytest.mark.skip("we don't have v2 test plots yet") +class TestV2Prover: def test_v2_prover_init_with_nonexistent_file(self) -> None: - prover = V2Prover("/nonexistent/path/test.plot2") + prover = V2Prover.from_filename("/nonexistent/path/test.plot2") assert prover.get_version() == PlotVersion.V2 assert prover.get_filename() == "/nonexistent/path/test.plot2" def test_v2_prover_get_size_raises_error(self) -> None: - prover = V2Prover("/nonexistent/path/test.plot2") + prover = V2Prover.from_filename("/nonexistent/path/test.plot2") with pytest.raises(NotImplementedError, match="V2 plot format is not yet implemented"): prover.get_size() def test_v2_prover_get_memo_raises_error(self) -> None: - prover = V2Prover("/nonexistent/path/test.plot2") + prover = V2Prover.from_filename("/nonexistent/path/test.plot2") with pytest.raises(NotImplementedError, match="V2 plot format is not yet implemented"): prover.get_memo() def test_v2_prover_get_compression_level(self) -> None: - prover = V2Prover("/nonexistent/path/test.plot2") + prover = V2Prover.from_filename("/nonexistent/path/test.plot2") assert prover.get_compression_level() == uint8(0) def test_v2_prover_get_id_raises_error(self) -> None: - prover = V2Prover("/nonexistent/path/test.plot2") + prover = V2Prover.from_filename("/nonexistent/path/test.plot2") with pytest.raises(NotImplementedError, match="V2 plot format is not yet implemented"): prover.get_id() def test_v2_prover_get_qualities_for_challenge_raises_error(self) -> None: - prover = V2Prover("/nonexistent/path/test.plot2") + prover = V2Prover.from_filename("/nonexistent/path/test.plot2") with pytest.raises( AssertionError, match="V2 plot format does not support qualities directly, use partial proofs" ): - prover.get_qualities_for_challenge(bytes32(b"1" * 32)) - - def test_v2_prover_get_full_proof_raises_error(self) -> None: - prover = V2Prover("/nonexistent/path/test.plot2") - with pytest.raises(AssertionError, match="V2 plot format require solver to get full proof"): - prover.get_full_proof(bytes32(b"1" * 32), 0) + prover.get_qualities_for_challenge(bytes32(b"1" * 32), uint8(5)) def test_v2_prover_bytes_raises_error(self) -> None: - prover = V2Prover("/nonexistent/path/test.plot2") + prover = V2Prover.from_filename("/nonexistent/path/test.plot2") with pytest.raises(NotImplementedError, match="V2 plot format is not yet implemented"): bytes(prover) @@ -63,20 +76,6 @@ def test_get_prover_from_file(self) -> None: with pytest.raises(NotImplementedError, match="V2 plot format is not yet implemented"): prover.get_size() - def test_get_prover_from_file_with_plot1_still_works(self) -> None: - with tempfile.NamedTemporaryFile(suffix=".plot", delete=False) as f: - temp_path = f.name - try: - with pytest.raises(Exception) as exc_info: - get_prover_from_file(temp_path) - assert not isinstance(exc_info.value, NotImplementedError) - finally: - Path(temp_path).unlink() - - def test_unsupported_file_extension_raises_value_error(self) -> None: - with pytest.raises(ValueError, match="Unsupported plot file"): - get_prover_from_file("/nonexistent/path/test.txt") - class TestV1Prover: def test_v1_prover_get_version(self) -> None: @@ -87,13 +86,6 @@ def test_v1_prover_get_version(self) -> None: class TestGetProverFromBytes: - def test_get_prover_from_bytes_v2_plot(self) -> None: - with patch("chia.plotting.prover.V2Prover.from_bytes") as mock_v2_from_bytes: - mock_prover = MagicMock() - mock_v2_from_bytes.return_value = mock_prover - result = get_prover_from_bytes("test.plot2", b"test_data") - assert result == mock_prover - def test_get_prover_from_bytes_v1_plot(self) -> None: with patch("chia.plotting.prover.DiskProver") as mock_disk_prover_class: mock_disk_prover = MagicMock() @@ -102,5 +94,5 @@ def test_get_prover_from_bytes_v1_plot(self) -> None: assert isinstance(result, V1Prover) def test_get_prover_from_bytes_unsupported_extension(self) -> None: - with pytest.raises(ValueError, match="Unsupported plot file"): + with pytest.raises((RuntimeError, ValueError)): get_prover_from_bytes("test.txt", b"test_data") diff --git a/chia/_tests/solver/test_solver_service.py b/chia/_tests/solver/test_solver_service.py index ccb683a9ef39..9e65715291c3 100644 --- a/chia/_tests/solver/test_solver_service.py +++ b/chia/_tests/solver/test_solver_service.py @@ -5,6 +5,8 @@ import pytest from chia_rs import ConsensusConstants +from chia_rs.sized_bytes import bytes32 +from chia_rs.sized_ints import uint8, uint64 from chia.protocols.outbound_message import Message from chia.protocols.solver_protocol import SolverInfo @@ -21,7 +23,12 @@ async def test_solver_api_methods(blockchain_constants: ConsensusConstants, tmp_ solver = solver_service._node solver_api = solver_service._api assert solver_api.ready() is True - test_info = SolverInfo(partial_proof=b"test_partial_proof_42") + test_info = SolverInfo( + partial_proof=[uint64(1), uint64(2), uint64(3), uint64(4)], + plot_id=bytes32.fromhex("abababababababababababababababababababababababababababababababab"), + strength=uint8(5), + size=uint8(28), + ) expected_proof = b"test_proof_data_12345" with patch.object(solver, "solve", return_value=expected_proof): api_result = await solver_api.solve(test_info) diff --git a/chia/_tests/util/network_protocol_data.py b/chia/_tests/util/network_protocol_data.py index 465a6834aa25..6b0d3a85385c 100644 --- a/chia/_tests/util/network_protocol_data.py +++ b/chia/_tests/util/network_protocol_data.py @@ -155,9 +155,11 @@ bytes32.fromhex("42743566108589c11bb3811b347900b6351fd3e25bad6c956c0bf1c05a4d93fb"), bytes32.fromhex("8a346e8dc02e9b44c0571caa74fd99f163d4c5d7deaedac87125528721493f7a"), "plot-filename", - [b"partial-proof1", b"partial-proof2"], + [[uint64(1), uint64(2), uint64(3), uint64(4)], [uint64(2), uint64(3), uint64(4), uint64(5)]], uint8(4), uint8(32), + uint8(5), + bytes32.fromhex("346e8dc02e9b44c0571caa74fd99f163d4c5d7deaedac87125528721493f7a8a"), G1Element.from_bytes( bytes.fromhex( "a04c6b5ac7dfb935f6feecfdd72348ccf1d4be4fe7e26acf271ea3b7d308da61e0a308f7a62495328a81f5147b66634c" @@ -916,6 +918,7 @@ plot = harvester_protocol.Plot( "plot_1", uint8(124), + uint8(0), bytes32(bytes.fromhex("b2eb7e5c5239e8610a9dd0e137e185966ebb430faf31ae4a0e55d86251065b98")), G1Element.from_bytes( bytes.fromhex( @@ -1116,6 +1119,11 @@ ) # SOLVER PROTOCOL -solver_info = solver_protocol.SolverInfo(partial_proof=b"partial-proof") +solver_info = solver_protocol.SolverInfo( + partial_proof=[uint64(1), uint64(2), uint64(3), uint64(4)], + plot_id=bytes32.fromhex("071bef40d098cfadc2614d8b57db924788f7f2ea0fde8cf4bfaeae2894caa442"), + strength=uint8(5), + size=uint8(28), +) -solver_response = solver_protocol.SolverResponse(b"partial-proof", b"full-proof") +solver_response = solver_protocol.SolverResponse([uint64(1), uint64(2), uint64(3), uint64(4)], b"full-proof") diff --git a/chia/_tests/util/protocol_messages_bytes-v1.0 b/chia/_tests/util/protocol_messages_bytes-v1.0 index 9b3c128ca799..0ab939bc6524 100644 Binary files a/chia/_tests/util/protocol_messages_bytes-v1.0 and b/chia/_tests/util/protocol_messages_bytes-v1.0 differ diff --git a/chia/_tests/util/protocol_messages_json.py b/chia/_tests/util/protocol_messages_json.py index dcac00927b95..b3d8eb2f8388 100644 --- a/chia/_tests/util/protocol_messages_json.py +++ b/chia/_tests/util/protocol_messages_json.py @@ -69,9 +69,11 @@ "challenge_hash": "0x42743566108589c11bb3811b347900b6351fd3e25bad6c956c0bf1c05a4d93fb", "sp_hash": "0x8a346e8dc02e9b44c0571caa74fd99f163d4c5d7deaedac87125528721493f7a", "plot_identifier": "plot-filename", - "partial_proofs": ["0x7061727469616c2d70726f6f6631", "0x7061727469616c2d70726f6f6632"], + "partial_proofs": [[1, 2, 3, 4], [2, 3, 4, 5]], "signage_point_index": 4, "plot_size": 32, + "strength": 5, + "plot_id": "0x346e8dc02e9b44c0571caa74fd99f163d4c5d7deaedac87125528721493f7a8a", "pool_public_key": "0xa04c6b5ac7dfb935f6feecfdd72348ccf1d4be4fe7e26acf271ea3b7d308da61e0a308f7a62495328a81f5147b66634c", "pool_contract_puzzle_hash": "0x91240fbacdf93b44c0571caa74fd99f163d4c5d7deaedac87125528721493f7a", "plot_public_key": "0xa04c6b5ac7dfb935f6feecfdd72348ccf1d4be4fe7e26acf271ea3b7d308da61e0a308f7a62495328a81f5147b66634c", @@ -2295,6 +2297,7 @@ plot_json: dict[str, Any] = { "filename": "plot_1", "size": 124, + "strength": 0, "plot_id": "0xb2eb7e5c5239e8610a9dd0e137e185966ebb430faf31ae4a0e55d86251065b98", "pool_public_key": "0xa04c6b5ac7dfb935f6feecfdd72348ccf1d4be4fe7e26acf271ea3b7d308da61e0a308f7a62495328a81f5147b66634c", "pool_contract_puzzle_hash": "0x1c96d26def7be696f12e7ebb91d50211e6217ce5d9087c9cd1b84782d5d4b237", @@ -2311,6 +2314,7 @@ { "filename": "plot_1", "size": 124, + "strength": 0, "plot_id": "0xb2eb7e5c5239e8610a9dd0e137e185966ebb430faf31ae4a0e55d86251065b98", "pool_public_key": "0xa04c6b5ac7dfb935f6feecfdd72348ccf1d4be4fe7e26acf271ea3b7d308da61e0a308f7a62495328a81f5147b66634c", "pool_contract_puzzle_hash": "0x1c96d26def7be696f12e7ebb91d50211e6217ce5d9087c9cd1b84782d5d4b237", @@ -2730,9 +2734,11 @@ error_with_data_json: dict[str, Any] = {"code": 1, "message": "Unknown", "data": "0x65787472612064617461"} -solver_info_json: dict[str, Any] = {"partial_proof": "0x7061727469616c2d70726f6f66"} - -solver_response_json: dict[str, Any] = { - "partial_proof": "0x7061727469616c2d70726f6f66", - "proof": "0x66756c6c2d70726f6f66", +solver_info_json: dict[str, Any] = { + "partial_proof": [1, 2, 3, 4], + "plot_id": "0x071bef40d098cfadc2614d8b57db924788f7f2ea0fde8cf4bfaeae2894caa442", + "strength": 5, + "size": 28, } + +solver_response_json: dict[str, Any] = {"partial_proof": [1, 2, 3, 4], "proof": "0x66756c6c2d70726f6f66"} diff --git a/chia/_tests/util/test_replace_str_to_bytes.py b/chia/_tests/util/test_replace_str_to_bytes.py index 20525ac9ef87..9fdf16ace95d 100644 --- a/chia/_tests/util/test_replace_str_to_bytes.py +++ b/chia/_tests/util/test_replace_str_to_bytes.py @@ -56,7 +56,6 @@ BLOCKS_CACHE_SIZE=uint32(4608 + (128 * 4)), WEIGHT_PROOF_RECENT_BLOCKS=uint32(1000), MAX_BLOCK_COUNT_PER_REQUESTS=uint32(32), - MAX_GENERATOR_SIZE=uint32(1000000), MAX_GENERATOR_REF_LIST_SIZE=uint32(512), POOL_SUB_SLOT_ITERS=uint64(37600000000), HARD_FORK_HEIGHT=uint32(5496000), @@ -66,11 +65,10 @@ PLOT_FILTER_64_HEIGHT=uint32(15592000), PLOT_FILTER_32_HEIGHT=uint32(20643000), PLOT_STRENGTH_INITIAL=uint8(2), - PLOT_STRENGTH_4_HEIGHT=uint32(0xFFFFFFFF), - PLOT_STRENGTH_5_HEIGHT=uint32(0xFFFFFFFF), - PLOT_STRENGTH_6_HEIGHT=uint32(0xFFFFFFFF), - PLOT_STRENGTH_7_HEIGHT=uint32(0xFFFFFFFF), - PLOT_STRENGTH_8_HEIGHT=uint32(0xFFFFFFFF), + QUALITY_PROOF_SCAN_FILTER=uint8(5), + PLOT_FILTER_V2_FIRST_ADJUSTMENT_HEIGHT=uint32(0xFFFFFFFA), + PLOT_FILTER_V2_SECOND_ADJUSTMENT_HEIGHT=uint32(0xFFFFFFFB), + PLOT_FILTER_V2_THIRD_ADJUSTMENT_HEIGHT=uint32(0xFFFFFFFC), ) diff --git a/chia/consensus/default_constants.py b/chia/consensus/default_constants.py index 7d2b93fe5ad4..1be1b298fe97 100644 --- a/chia/consensus/default_constants.py +++ b/chia/consensus/default_constants.py @@ -76,11 +76,11 @@ # inclusive, so the max allowed range of 32 is a request for 33 blocks # (which is allowed) MAX_BLOCK_COUNT_PER_REQUESTS=uint32(32), - MAX_GENERATOR_SIZE=uint32(1000000), MAX_GENERATOR_REF_LIST_SIZE=uint32(512), # Number of references allowed in the block generator ref list POOL_SUB_SLOT_ITERS=uint64(37600000000), # iters limit * NUM_SPS # June 2024 HARD_FORK_HEIGHT=uint32(5496000), + # TODO: todo_v2_plots finalize fork height 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 @@ -92,11 +92,11 @@ # June 2033 PLOT_FILTER_32_HEIGHT=uint32(20643000), PLOT_STRENGTH_INITIAL=uint8(2), - PLOT_STRENGTH_4_HEIGHT=uint32(0xFFFFFFFB), - PLOT_STRENGTH_5_HEIGHT=uint32(0xFFFFFFFC), - PLOT_STRENGTH_6_HEIGHT=uint32(0xFFFFFFFD), - PLOT_STRENGTH_7_HEIGHT=uint32(0xFFFFFFFE), - PLOT_STRENGTH_8_HEIGHT=uint32(0xFFFFFFFF), + QUALITY_PROOF_SCAN_FILTER=uint8(5), + # TODO: todo_v2_plots finalize plot filter schedule + PLOT_FILTER_V2_FIRST_ADJUSTMENT_HEIGHT=uint32(0xFFFFFFFB), + PLOT_FILTER_V2_SECOND_ADJUSTMENT_HEIGHT=uint32(0xFFFFFFFC), + PLOT_FILTER_V2_THIRD_ADJUSTMENT_HEIGHT=uint32(0xFFFFFFFD), ) diff --git a/chia/consensus/pot_iterations.py b/chia/consensus/pot_iterations.py index f61a5feca883..e17909a7cb3a 100644 --- a/chia/consensus/pot_iterations.py +++ b/chia/consensus/pot_iterations.py @@ -97,7 +97,7 @@ def validate_pospace_and_get_required_iters( def calculate_iterations_quality( constants: ConsensusConstants, - quality_string: bytes32, + quality_string: bytes, size: PlotSize, difficulty: uint64, cc_sp_output_hash: bytes32, diff --git a/chia/farmer/farmer_api.py b/chia/farmer/farmer_api.py index 58255d11252d..25b39f3ed6a3 100644 --- a/chia/farmer/farmer_api.py +++ b/chia/farmer/farmer_api.py @@ -507,11 +507,17 @@ async def partial_proofs(self, partial_proof_data: PartialProofsData, peer: WSCh # Process each partial proof chain through solver service to get full proofs for partial_proof in partial_proof_data.partial_proofs: - solver_info = SolverInfo(partial_proof=partial_proof) + solver_info = SolverInfo( + partial_proof=partial_proof, + plot_id=partial_proof_data.plot_id, + strength=partial_proof_data.strength, + size=partial_proof_data.plot_size, + ) + key = bytes(partial_proof) try: # store pending request data for matching with response - self.farmer.pending_solver_requests[partial_proof] = { + self.farmer.pending_solver_requests[key] = { "proof_data": partial_proof_data, "peer": peer, } @@ -519,15 +525,13 @@ async def partial_proofs(self, partial_proof_data: PartialProofsData, peer: WSCh # send solve request to all solver connections msg = make_msg(ProtocolMessageTypes.solve, solver_info) await self.farmer.server.send_to_all([msg], NodeType.SOLVER) - self.farmer.log.debug(f"Sent solve request for partial proof {partial_proof.hex()[:10]}...") + self.farmer.log.debug(f"Sent solve request for partial proof {partial_proof[:5]}...") except Exception as e: - self.farmer.log.error( - f"Failed to call solver service for partial proof {partial_proof.hex()[:10]}...: {e}" - ) + self.farmer.log.error(f"Failed to call solver service for partial proof {partial_proof[:5]}...: {e}") # clean up pending request - if partial_proof in self.farmer.pending_solver_requests: - del self.farmer.pending_solver_requests[partial_proof] + if key in self.farmer.pending_solver_requests: + del self.farmer.pending_solver_requests[key] @metadata.request() async def solution_response(self, response: SolverResponse, peer: WSChiaConnection) -> None: @@ -539,14 +543,13 @@ async def solution_response(self, response: SolverResponse, peer: WSChiaConnecti # find the matching pending request using partial_proof - if response.partial_proof not in self.farmer.pending_solver_requests: - self.farmer.log.warning( - f"Received solver response for unknown partial proof {response.partial_proof.hex()}" - ) + key = bytes(response.partial_proof) + if key not in self.farmer.pending_solver_requests: + self.farmer.log.warning(f"Received solver response for unknown partial proof {response.partial_proof[:5]}") return # get the original request data - request_data = self.farmer.pending_solver_requests.pop(response.partial_proof) + request_data = self.farmer.pending_solver_requests.pop(key) proof_data = request_data["proof_data"] original_peer = request_data["peer"] partial_proof = response.partial_proof @@ -554,7 +557,7 @@ async def solution_response(self, response: SolverResponse, peer: WSChiaConnecti # create the proof of space with the solver's proof proof_bytes = response.proof if proof_bytes is None or len(proof_bytes) == 0: - self.farmer.log.warning(f"Received empty proof from solver for proof {partial_proof.hex()}...") + self.farmer.log.warning(f"Received empty proof from solver for proof {partial_proof[:5]}...") return sp_challenge_hash = proof_data.challenge_hash diff --git a/chia/harvester/harvester.py b/chia/harvester/harvester.py index 04fb200e2e9a..412d6ba5c5d5 100644 --- a/chia/harvester/harvester.py +++ b/chia/harvester/harvester.py @@ -193,14 +193,16 @@ def get_plots(self) -> tuple[list[dict[str, Any]], list[str], list[str]]: size = prover.get_size() if size.size_v1 is not None: k = size.size_v1 + strength = 0 else: assert size.size_v2 is not None k = size.size_v2 - # TODO: todo_v2_plots support v2 plots in RPC response + strength = prover.get_strength() response_plots.append( { "filename": str(path), "size": k, + "strength": strength, "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 5438d19c4289..5450e85774dc 100644 --- a/chia/harvester/harvester_api.py +++ b/chia/harvester/harvester_api.py @@ -16,7 +16,7 @@ calculate_sp_interval_iters, ) from chia.harvester.harvester import Harvester -from chia.plotting.prover import PlotVersion +from chia.plotting.prover import PlotVersion, V1Prover, V2Prover, V2Quality from chia.plotting.util import PlotInfo, parse_plot_info from chia.protocols import harvester_protocol from chia.protocols.farmer_protocol import FarmingInfo @@ -28,11 +28,9 @@ from chia.types.blockchain_format.proof_of_space import ( calculate_pos_challenge, calculate_prefix_bits, - calculate_required_plot_strength, generate_plot_public_key, make_pos, passes_plot_filter, - quality_for_partial_proof, ) from chia.wallet.derive_keys import master_sk_to_local_sk @@ -153,10 +151,6 @@ async def new_signage_point_harvester( start = time.monotonic() assert len(new_challenge.challenge_hash) == 32 - required_plot_strength = calculate_required_plot_strength( - self.harvester.constants, new_challenge.last_tx_height - ) - loop = asyncio.get_running_loop() def blocking_lookup_v2_partial_proofs(filename: Path, plot_info: PlotInfo) -> Optional[PartialProofsData]: @@ -168,12 +162,12 @@ def blocking_lookup_v2_partial_proofs(filename: Path, plot_info: PlotInfo) -> Op new_challenge.challenge_hash, new_challenge.sp_hash, ) - partial_proofs = plot_info.prover.get_partial_proofs_for_challenge( - sp_challenge_hash, required_plot_strength + qualities = plot_info.prover.get_qualities_for_challenge( + sp_challenge_hash, self.harvester.constants.QUALITY_PROOF_SCAN_FILTER ) # If no partial proofs are found, return None - if len(partial_proofs) == 0: + if len(qualities) == 0: return None # Get the appropriate difficulty for this plot @@ -191,11 +185,10 @@ def blocking_lookup_v2_partial_proofs(filename: Path, plot_info: PlotInfo) -> Op good_partial_proofs = [] sp_interval_iters = calculate_sp_interval_iters(self.harvester.constants, sub_slot_iters) - for partial_proof in partial_proofs: - quality_str = quality_for_partial_proof(partial_proof, new_challenge.challenge_hash) + for quality in qualities: required_iters: uint64 = calculate_iterations_quality( self.harvester.constants, - quality_str, + quality.get_quality(), plot_info.prover.get_size(), difficulty, new_challenge.sp_hash, @@ -203,8 +196,14 @@ def blocking_lookup_v2_partial_proofs(filename: Path, plot_info: PlotInfo) -> Op new_challenge.last_tx_height, ) - if required_iters < sp_interval_iters: - good_partial_proofs.append(partial_proof) + if required_iters >= sp_interval_iters: + continue + + assert isinstance(plot_info.prover, V2Prover) + assert isinstance(quality, V2Quality) + + partial_proof = plot_info.prover.get_partial_proof(quality) + good_partial_proofs.append(partial_proof) if len(good_partial_proofs) == 0: return None @@ -214,10 +213,12 @@ def blocking_lookup_v2_partial_proofs(filename: Path, plot_info: PlotInfo) -> Op return PartialProofsData( new_challenge.challenge_hash, new_challenge.sp_hash, - good_partial_proofs[0].hex() + str(filename.resolve()), + str(filename.resolve()), good_partial_proofs, new_challenge.signage_point_index, size, + plot_info.prover.get_strength(), + plot_id, plot_info.pool_public_key, plot_info.pool_contract_puzzle_hash, plot_info.plot_public_key, @@ -238,7 +239,9 @@ def blocking_lookup(filename: Path, plot_info: PlotInfo) -> list[tuple[bytes32, new_challenge.sp_hash, ) try: - quality_strings = plot_info.prover.get_qualities_for_challenge(sp_challenge_hash) + qualities = plot_info.prover.get_qualities_for_challenge( + sp_challenge_hash, self.harvester.constants.QUALITY_PROOF_SCAN_FILTER + ) except RuntimeError as e: if str(e) == "Timeout waiting for context queue.": self.harvester.log.warning( @@ -264,7 +267,7 @@ def blocking_lookup(filename: Path, plot_info: PlotInfo) -> list[tuple[bytes32, return [] responses: list[tuple[bytes32, ProofOfSpace]] = [] - if quality_strings is not None: + if len(qualities) > 0: difficulty = new_challenge.difficulty sub_slot_iters = new_challenge.sub_slot_iters if plot_info.pool_contract_puzzle_hash is not None: @@ -277,10 +280,10 @@ def blocking_lookup(filename: Path, plot_info: PlotInfo) -> list[tuple[bytes32, sub_slot_iters = pool_difficulty.sub_slot_iters # Found proofs of space (on average 1 is expected per plot) - for index, quality_str in enumerate(quality_strings): + for index, quality in enumerate(qualities): required_iters: uint64 = calculate_iterations_quality( self.harvester.constants, - quality_str, + quality.get_quality(), plot_info.prover.get_size(), difficulty, new_challenge.sp_hash, @@ -292,6 +295,7 @@ def blocking_lookup(filename: Path, plot_info: PlotInfo) -> list[tuple[bytes32, # Found a very good proof of space! will fetch the whole proof from disk, # then send to farmer try: + assert isinstance(plot_info.prover, V1Prover) proof_xs = plot_info.prover.get_full_proof( sp_challenge_hash, index, self.harvester.parallel_read ) @@ -327,6 +331,7 @@ def blocking_lookup(filename: Path, plot_info: PlotInfo) -> list[tuple[bytes32, ) continue + quality_str = bytes32(quality.get_quality()) responses.append( ( quality_str, @@ -511,6 +516,7 @@ async def request_plots(self, _: harvester_protocol.RequestPlots) -> Message: Plot( plot["filename"], plot["size"], + plot["strength"], plot["plot_id"], plot["pool_public_key"], plot["pool_contract_puzzle_hash"], diff --git a/chia/plot_sync/receiver.py b/chia/plot_sync/receiver.py index bec063b03d5e..ff7e8a01efe6 100644 --- a/chia/plot_sync/receiver.py +++ b/chia/plot_sync/receiver.py @@ -113,8 +113,8 @@ def __init__( async def trigger_callback(self, update: Optional[Delta] = None) -> None: try: await self._update_callback(self._connection.peer_node_id, update) - except Exception as e: - log.error(f"_update_callback: node_id {self.connection().peer_node_id}, raised {e}") + except Exception: + log.exception(f"_update_callback: node_id {self.connection().peer_node_id}") def reset(self) -> None: log.info(f"reset: node_id {self.connection().peer_node_id}, current_sync: {self._current_sync}") @@ -181,13 +181,13 @@ async def send_response(plot_sync_error: Optional[PlotSyncError] = None) -> None await method(message) await send_response() except InvalidIdentifierError as e: - log.warning(f"_process: node_id {self.connection().peer_node_id}, InvalidIdentifierError {e}") + log.exception(f"_process: node_id {self.connection().peer_node_id}") await send_response(PlotSyncError(int16(e.error_code), f"{e}", e.expected_identifier)) except PlotSyncException as e: - log.warning(f"_process: node_id {self.connection().peer_node_id}, Error {e}") + log.exception(f"_process: node_id {self.connection().peer_node_id}") await send_response(PlotSyncError(int16(e.error_code), f"{e}", None)) except Exception as e: - log.warning(f"_process: node_id {self.connection().peer_node_id}, Exception {e}") + log.exception(f"_process: node_id {self.connection().peer_node_id}") await send_response(PlotSyncError(int16(ErrorCodes.unknown), f"{e}", None)) def _validate_identifier(self, identifier: PlotSyncIdentifier, start: bool = False) -> None: @@ -349,12 +349,16 @@ async def _sync_done(self, data: PlotSyncDone) -> None: self._keys_missing = self._current_sync.delta.keys_missing.additions.copy() self._duplicates = self._current_sync.delta.duplicates.additions.copy() self._total_plot_size = sum(plot.file_size for plot in self._plots.values()) + + def expected_plot_size(pi: Plot) -> int: + if pi.strength == 0: + plot_size = PlotSize.make_v1(pi.size) + else: + plot_size = PlotSize.make_v2(pi.size) + return int(_expected_plot_size(plot_size)) + self._total_effective_plot_size = int( - # 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() - ) + sum(UI_ACTUAL_SPACE_CONSTANT_FACTOR * expected_plot_size(plot) 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 46189c2c53a0..c8dd448ebef8 100644 --- a/chia/plot_sync/sender.py +++ b/chia/plot_sync/sender.py @@ -9,7 +9,7 @@ from pathlib import Path from typing import Any, Generic, Optional, TypeVar -from chia_rs.sized_ints import int16, uint32, uint64 +from chia_rs.sized_ints import int16, uint8, uint32, uint64 from typing_extensions import Protocol from chia.plot_sync.exceptions import AlreadyStartedError, InvalidConnectionTypeError @@ -37,13 +37,23 @@ 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 + plot_size = plot_info.prover.get_size() + if plot_size.size_v1 is not None: + k = plot_size.size_v1 + strength = 0 + else: + # todo_v2_plots the k-size should probably be set to the constant, + # fixed k-size for v2 plots. Then we would need access to + # ConsensusConstants in here. + assert plot_size.size_v2 is not None + k = plot_size.size_v2 + strength = plot_info.prover.get_strength() + converted.append( Plot( filename=plot_info.prover.get_filename(), - size=k, + size=uint8(k), + strength=uint8(strength), 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/check_plots.py b/chia/plotting/check_plots.py index 848c0300874b..2e1e676b12b2 100644 --- a/chia/plotting/check_plots.py +++ b/chia/plotting/check_plots.py @@ -7,16 +7,15 @@ from pathlib import Path from threading import Lock from time import monotonic, sleep -from typing import Optional, Union +from typing import Optional -from chia_rs import G1Element -from chia_rs.sized_bytes import bytes32 +from chia_rs import G1Element, solve_proof from chia_rs.sized_ints import uint8, uint32 from chiapos import Verifier from chia.consensus.default_constants import DEFAULT_CONSTANTS from chia.plotting.manager import PlotManager -from chia.plotting.prover import PlotVersion +from chia.plotting.prover import QualityProtocol, V1Prover, V2Prover, V2Quality from chia.plotting.util import ( PlotInfo, PlotRefreshEvents, @@ -26,10 +25,6 @@ get_plot_filenames, parse_plot_info, ) -from chia.types.blockchain_format.proof_of_space import ( - quality_for_partial_proof, - solve_proof, -) from chia.util.bech32m import encode_puzzle_hash from chia.util.config import load_config from chia.util.cpu import available_logical_cores @@ -176,18 +171,13 @@ def process_plot(plot_path: Path, plot_info: PlotInfo, num_start: int, num_end: total_proofs = 0 caught_exception: bool = False - version = pr.get_version() for i in range(num_start, num_end): challenge = std_hash(i.to_bytes(32, "big")) - # these are either qualities (v1) or partial proofs (v2) - proofs: Sequence[Union[bytes32, bytes]] + qualities: Sequence[QualityProtocol] # Some plot errors cause get_qualities_for_challenge to throw a RuntimeError try: quality_start_time = round(monotonic() * 1000) - if version == PlotVersion.V1: - proofs = pr.get_qualities_for_challenge(challenge) - else: - proofs = pr.get_partial_proofs_for_challenge(challenge, DEFAULT_CONSTANTS.PLOT_STRENGTH_INITIAL) + qualities = pr.get_qualities_for_challenge(challenge, DEFAULT_CONSTANTS.PLOT_STRENGTH_INITIAL) quality_spent_time = round(monotonic() * 1000) - quality_start_time if quality_spent_time > 8000: log.warning( @@ -215,18 +205,22 @@ def process_plot(plot_path: Path, plot_info: PlotInfo, num_start: int, num_end: caught_exception = True break - for index, proof in enumerate(proofs): + for index, quality in enumerate(qualities): # Other plot errors cause get_full_proof or validate_proof to throw an AssertionError try: proof_start_time = round(monotonic() * 1000) - if version == PlotVersion.V1: - quality_str = bytes32(proof) + quality_str = quality.get_quality() + + if isinstance(pr, V1Prover): full_proof = pr.get_full_proof(challenge, index, parallel_read) proof_spent_time = round(monotonic() * 1000) - proof_start_time - else: - quality_str = quality_for_partial_proof(proof, challenge) + elif isinstance(pr, V2Prover): + assert isinstance(quality, V2Quality) + partial_proof = pr.get_partial_proof(quality) proof_spent_time = round(monotonic() * 1000) - proof_start_time - full_proof = solve_proof(proof) + size = pr.get_size().size_v2 + assert size is not None + full_proof = solve_proof(partial_proof, pr.get_id(), pr.get_strength(), size) if proof_spent_time > 15000: log.warning( diff --git a/chia/plotting/create_plots.py b/chia/plotting/create_plots.py index 14b056c88046..e395135cce62 100644 --- a/chia/plotting/create_plots.py +++ b/chia/plotting/create_plots.py @@ -5,8 +5,9 @@ from pathlib import Path from typing import Optional -from chia_rs import AugSchemeMPL, G1Element, PrivateKey +from chia_rs import AugSchemeMPL, G1Element, PrivateKey, create_v2_plot from chia_rs.sized_bytes import bytes32 +from chia_rs.sized_ints import uint8 from chiapos import DiskPlotter from chia.daemon.keychain_proxy import KeychainProxy, connect_to_keychain_and_validate, wrap_local_keychain @@ -14,6 +15,7 @@ from chia.types.blockchain_format.proof_of_space import ( calculate_plot_id_ph, calculate_plot_id_pk, + calculate_plot_id_v2, generate_plot_public_key, ) from chia.util.bech32m import decode_puzzle_hash @@ -275,3 +277,63 @@ async def create_plots( log.info(created_path.name) return created_plots, existing_plots + + +async def create_v2_plots( + final_dir: Path, + *, + pool_ph: bytes32, + farmer_pk: G1Element, + size: int = 28, + strength: int = 2, + num: int = 1, + use_datetime: bool = True, + test_private_keys: Optional[list[PrivateKey]] = None, +) -> tuple[dict[bytes32, Path], dict[bytes32, Path]]: + log.info( + f"Creating {num} plots of size {size}, pool contract address: " + f"{pool_ph} farmer public key: {bytes(farmer_pk).hex()}" + ) + + final_dir.mkdir(parents=True, exist_ok=True) + + created_plots: dict[bytes32, Path] = {} + existing_plots: dict[bytes32, Path] = {} + for i in range(num): + # Generate a random master secret key + if test_private_keys is not None: + assert len(test_private_keys) == num + sk: PrivateKey = test_private_keys[i] + else: + sk = AugSchemeMPL.key_gen(bytes32.secret()) + + # The plot public key is the combination of the harvester and farmer keys + # New plots will also include a taproot of the keys, for extensibility + plot_public_key = generate_plot_public_key(master_sk_to_local_sk(sk).get_g1(), farmer_pk, include_taproot=True) + + # The plot id is based on the harvester, farmer, pool contract puzzle + # hash and strength + plot_id = calculate_plot_id_v2(pool_ph, plot_public_key, uint8(strength)) + plot_memo = stream_plot_info_ph(pool_ph, farmer_pk, sk) + + dt_string = datetime.now().strftime("%Y-%m-%d-%H-%M") + + if use_datetime: + filename: str = f"plot-k{size}-{dt_string}-{plot_id}.plot2" + else: + filename = f"plot-k{size}-{plot_id}.plot2" + full_path: Path = final_dir / filename + + if not full_path.exists(): + log.info(f"Starting plot {i + 1}/{num}") + create_v2_plot(str(full_path.absolute()), size, strength, plot_id, plot_memo) + created_plots[plot_id] = full_path + else: + log.info(f"Plot {filename} already exists") + existing_plots[plot_id] = full_path + + log.info(f"Created a total of {len(created_plots)} new plots") + for created_path in created_plots.values(): + log.info(created_path.name) + + return created_plots, existing_plots diff --git a/chia/plotting/manager.py b/chia/plotting/manager.py index 6ba06c8fa005..8996b28259f1 100644 --- a/chia/plotting/manager.py +++ b/chia/plotting/manager.py @@ -278,8 +278,8 @@ def _refresh_task(self, sleep_interval_ms: int): f"total_result.removed {len(total_result.removed)}, " f"total_duration {total_result.duration:.2f} seconds" ) - except Exception as e: - log.error(f"_refresh_callback raised: {e} with the traceback: {traceback.format_exc()}") + except Exception: + log.exception("_refresh_callback raised") self.reset() def refresh_batch(self, plot_paths: list[Path], plot_directories: set[Path]) -> PlotRefreshResult: diff --git a/chia/plotting/prover.py b/chia/plotting/prover.py index 06173338eff2..135a65848bc5 100644 --- a/chia/plotting/prover.py +++ b/chia/plotting/prover.py @@ -1,11 +1,12 @@ from __future__ import annotations +from dataclasses import dataclass from enum import IntEnum from typing import TYPE_CHECKING, ClassVar, Protocol, cast -from chia_rs import PlotSize +from chia_rs import PlotSize, Prover, QualityProof from chia_rs.sized_bytes import bytes32 -from chia_rs.sized_ints import uint8 +from chia_rs.sized_ints import uint8, uint64 from chiapos import DiskProver if TYPE_CHECKING: @@ -19,46 +20,65 @@ class PlotVersion(IntEnum): V2 = 2 +class QualityProtocol(Protocol): + def get_quality(self) -> bytes: ... + + class ProverProtocol(Protocol): def get_filename(self) -> str: ... def get_size(self) -> PlotSize: ... + def get_strength(self) -> uint8: ... def get_memo(self) -> bytes: ... def get_compression_level(self) -> uint8: ... def get_version(self) -> PlotVersion: ... def __bytes__(self) -> bytes: ... def get_id(self) -> bytes32: ... - def get_qualities_for_challenge(self, challenge: bytes32) -> list[bytes32]: ... - - # this is only supported by v2 plots - def get_partial_proofs_for_challenge(self, challenge: bytes32, required_plot_strength: uint8) -> list[bytes]: ... - - # this is only supported by v1 plots. v2 plots first get the partial proof - # and turn it into a full proof by calling solve_proof(), or pass it to the solver service - def get_full_proof(self, challenge: bytes32, index: int, parallel_read: bool = True) -> bytes: ... + def get_qualities_for_challenge( + self, challenge: bytes32, proof_fragment_filter: uint8 + ) -> list[QualityProtocol]: ... @classmethod def from_bytes(cls, data: bytes) -> ProverProtocol: ... +@dataclass(frozen=True) +class V2Quality(QualityProtocol): + _quality_proof: QualityProof + + def get_quality(self) -> bytes: + return self._quality_proof.serialize() + + class V2Prover: """Placeholder for future V2 plot format support""" + _prover: Prover + if TYPE_CHECKING: _protocol_check: ClassVar[ProverProtocol] = cast("V2Prover", None) - def __init__(self, filename: str): - self._filename = filename + @classmethod + def from_filename(cls, path: str) -> V2Prover: + return V2Prover(Prover(path)) + + @classmethod + def from_bytes(cls, data: bytes) -> V2Prover: + return V2Prover(Prover.from_bytes(data)) + + def __init__(self, prover: Prover): + self._prover = prover def get_filename(self) -> str: - return str(self._filename) + return self._prover.get_filename() def get_size(self) -> PlotSize: - # TODO: todo_v2_plots get k size from plot - raise NotImplementedError("V2 plot format is not yet implemented") + return PlotSize.make_v2(self._prover.size()) + + def get_strength(self) -> uint8: + return uint8(self._prover.get_strength()) def get_memo(self) -> bytes: - # TODO: todo_v2_plots - raise NotImplementedError("V2 plot format is not yet implemented") + return self._prover.get_memo() def get_compression_level(self) -> uint8: # v2 plots are never compressed @@ -68,27 +88,24 @@ def get_version(self) -> PlotVersion: return PlotVersion.V2 def __bytes__(self) -> bytes: - # TODO: todo_v2_plots Implement prover serialization for caching - raise NotImplementedError("V2 plot format is not yet implemented") + return self._prover.to_bytes() def get_id(self) -> bytes32: - # TODO: Extract plot ID from V2 plot file - raise NotImplementedError("V2 plot format is not yet implemented") + return self._prover.plot_id() - def get_qualities_for_challenge(self, challenge: bytes32) -> list[bytes32]: - raise AssertionError("V2 plot format does not support qualities directly, use partial proofs") + def get_qualities_for_challenge(self, challenge: bytes32, proof_fragment_filter: uint8) -> list[QualityProtocol]: + return [V2Quality(q) for q in self._prover.get_qualities_for_challenge(challenge, proof_fragment_filter)] - def get_partial_proofs_for_challenge(self, challenge: bytes, required_plot_strength: uint8) -> list[bytes]: - # TODO: todo_v2_plots Implement plot partial proof lookup - raise NotImplementedError("V2 plot format is not yet implemented") + def get_partial_proof(self, quality: V2Quality) -> list[uint64]: + return self._prover.get_partial_proof(quality._quality_proof)[0] - def get_full_proof(self, challenge: bytes32, index: int, parallel_read: bool = True) -> bytes: - raise AssertionError("V2 plot format require solver to get full proof") - @classmethod - def from_bytes(cls, data: bytes) -> V2Prover: - # TODO: todo_v2_plots Implement prover deserialization from cache - raise NotImplementedError("V2 plot format is not yet implemented") +@dataclass(frozen=True) +class V1Quality(QualityProtocol): + _quality: bytes + + def get_quality(self) -> bytes: + return self._quality class V1Prover: @@ -106,6 +123,9 @@ def get_filename(self) -> str: def get_size(self) -> PlotSize: return PlotSize.make_v1(uint8(self._disk_prover.get_size())) + def get_strength(self) -> uint8: + raise AssertionError("V1 plot format doesn't use strength") + def get_memo(self) -> bytes: return bytes(self._disk_prover.get_memo()) @@ -121,11 +141,8 @@ def __bytes__(self) -> bytes: def get_id(self) -> bytes32: return bytes32(self._disk_prover.get_id()) - def get_qualities_for_challenge(self, challenge: bytes32) -> list[bytes32]: - return [bytes32(quality) for quality in self._disk_prover.get_qualities_for_challenge(challenge)] - - def get_partial_proofs_for_challenge(self, challenge: bytes32, required_plot_strength: uint8) -> list[bytes]: - raise AssertionError("V1 plot format doesn't use partial proofs") + def get_qualities_for_challenge(self, challenge: bytes32, proof_fragment_filter: uint8) -> list[QualityProtocol]: + return [V1Quality(bytes32(quality)) for quality in self._disk_prover.get_qualities_for_challenge(challenge)] def get_full_proof(self, challenge: bytes32, index: int, parallel_read: bool = True) -> bytes: return bytes(self._disk_prover.get_full_proof(challenge, index, parallel_read)) @@ -137,7 +154,7 @@ def from_bytes(cls, data: bytes) -> V1Prover: def get_prover_from_bytes(filename: str, prover_data: bytes) -> ProverProtocol: if filename.endswith(".plot2"): - return V2Prover.from_bytes(prover_data) + return V2Prover(Prover.from_bytes(prover_data)) elif filename.endswith(".plot"): return V1Prover(DiskProver.from_bytes(prover_data)) else: @@ -146,7 +163,7 @@ def get_prover_from_bytes(filename: str, prover_data: bytes) -> ProverProtocol: def get_prover_from_file(filename: str) -> ProverProtocol: if filename.endswith(".plot2"): - return V2Prover(filename) + return V2Prover(Prover(filename)) elif filename.endswith(".plot"): return V1Prover(DiskProver(filename)) else: diff --git a/chia/protocols/harvester_protocol.py b/chia/protocols/harvester_protocol.py index ff1213c82544..e2ea78153e72 100644 --- a/chia/protocols/harvester_protocol.py +++ b/chia/protocols/harvester_protocol.py @@ -83,9 +83,11 @@ class PartialProofsData(Streamable): challenge_hash: bytes32 sp_hash: bytes32 plot_identifier: str - partial_proofs: list[bytes] # 16 * k bits blobs instead of 32-byte quality strings + partial_proofs: list[list[uint64]] # 16 * k bits blobs instead of 32-byte quality strings signage_point_index: uint8 plot_size: uint8 + strength: uint8 + plot_id: bytes32 pool_public_key: Optional[G1Element] pool_contract_puzzle_hash: Optional[bytes32] plot_public_key: G1Element @@ -141,6 +143,7 @@ class RespondSignatures(Streamable): class Plot(Streamable): filename: str size: uint8 + strength: uint8 # This is 0 for v1 plots plot_id: bytes32 pool_public_key: Optional[G1Element] pool_contract_puzzle_hash: Optional[bytes32] diff --git a/chia/protocols/solver_protocol.py b/chia/protocols/solver_protocol.py index 891bfb846bb9..a3b4a75721ed 100644 --- a/chia/protocols/solver_protocol.py +++ b/chia/protocols/solver_protocol.py @@ -2,17 +2,23 @@ from dataclasses import dataclass +from chia_rs.sized_bytes import bytes32 +from chia_rs.sized_ints import uint8, uint64 + from chia.util.streamable import Streamable, streamable @streamable @dataclass(frozen=True) class SolverInfo(Streamable): - partial_proof: bytes # 16 * k bits blob, k (plot size) can be derived from this + partial_proof: list[uint64] # 16 * k bits blob, k (plot size) can be derived from this + plot_id: bytes32 + strength: uint8 + size: uint8 # k-size @streamable @dataclass(frozen=True) class SolverResponse(Streamable): - partial_proof: bytes + partial_proof: list[uint64] proof: bytes diff --git a/chia/simulator/block_tools.py b/chia/simulator/block_tools.py index 77b8ed9f0811..270378215a46 100644 --- a/chia/simulator/block_tools.py +++ b/chia/simulator/block_tools.py @@ -14,7 +14,7 @@ from dataclasses import dataclass, replace from pathlib import Path from random import Random -from typing import Any, Callable, Optional, Union +from typing import Any, Callable, Optional import anyio from chia_puzzles_py.programs import CHIALISP_DESERIALISATION, ROM_BOOTSTRAP_GENERATOR @@ -37,6 +37,7 @@ SubSlotProofs, UnfinishedBlock, solution_generator, + solve_proof, ) from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint8, uint16, uint32, uint64, uint128 @@ -62,9 +63,9 @@ from chia.consensus.vdf_info_computation import get_signage_point_vdf_info from chia.daemon.keychain_proxy import KeychainProxy, connect_to_keychain_and_validate, wrap_local_keychain from chia.full_node.bundle_tools import simple_solution_generator, simple_solution_generator_backrefs -from chia.plotting.create_plots import PlotKeys, create_plots +from chia.plotting.create_plots import PlotKeys, create_plots, create_v2_plots from chia.plotting.manager import PlotManager -from chia.plotting.prover import PlotVersion +from chia.plotting.prover import PlotVersion, QualityProtocol, V1Prover, V2Prover, V2Quality from chia.plotting.util import ( Params, PlotRefreshEvents, @@ -92,13 +93,10 @@ from chia.types.blockchain_format.proof_of_space import ( calculate_pos_challenge, calculate_prefix_bits, - calculate_required_plot_strength, generate_plot_public_key, generate_taproot_sk, make_pos, passes_plot_filter, - quality_for_partial_proof, - solve_proof, ) from chia.types.blockchain_format.serialized_program import SerializedProgram from chia.types.blockchain_format.vdf import VDFInfo, VDFProof @@ -321,6 +319,7 @@ def __init__( self.temp_dir.mkdir(parents=True, exist_ok=True) self.expected_plots: dict[bytes32, Path] = {} self.created_plots: int = 0 + self.created_plots2: int = 0 self.total_result = PlotRefreshResult() def test_callback(event: PlotRefreshEvents, update_result: PlotRefreshResult) -> None: @@ -340,6 +339,29 @@ def test_callback(event: PlotRefreshEvents, update_result: PlotRefreshResult) -> assert self.total_result.processed == update_result.processed assert self.total_result.duration == update_result.duration assert update_result.remaining == 0 + + expected_plots: set[str] = set() + found_plots: set[str] = set() + if len(self.plot_manager.plots) != len(self.expected_plots): + for pid, filename in self.expected_plots.items(): + expected_plots.add(filename.name) + for filename, _ in self.plot_manager.plots.items(): + found_plots.add(filename.name) + print(f"directory: {self.plot_dir}") + print(f"expected: {len(expected_plots)}") + for f in expected_plots: + print(f) + print(f"plot manager: {len(found_plots)}") + for f in found_plots: + print(f) + diff = found_plots.difference(expected_plots) + print(f"found unexpected: {len(diff)}") + for f in diff: + print(f) + diff = expected_plots.difference(found_plots) + print(f"not found: {len(diff)}") + for f in diff: + print(f) assert len(self.plot_manager.plots) == len(self.expected_plots) self.plot_manager: PlotManager = PlotManager( @@ -500,11 +522,18 @@ async def setup_plots( num_og_plots: int = 15, num_pool_plots: int = 5, num_non_keychain_plots: int = 3, + num_v2_plots: int = 15, plot_size: int = 20, bitfield: bool = True, ) -> bool: + print( + f"setup_plots({num_og_plots}, {num_pool_plots}, " + f"{num_non_keychain_plots}, {num_v2_plots}) " + f"plot-dir: {self.plot_dir}" + ) self.add_plot_directory(self.plot_dir) assert self.created_plots == 0 + assert self.created_plots2 == 0 existing_plots: bool = True # OG Plots for i in range(num_og_plots): @@ -527,6 +556,11 @@ async def setup_plots( ) if plot.new_plot: existing_plots = False + # v2 plots + for i in range(num_v2_plots): + plot = await self.new_plot2(plot_size=18) + if plot.new_plot: + existing_plots = False await self.refresh_plots() assert len(self.plot_manager.plots) == len(self.expected_plots) return existing_plots @@ -606,6 +640,48 @@ async def new_plot( shutil.rmtree(self.temp_dir, ignore_errors=True) sys.exit(1) + async def new_plot2( + self, + path: Optional[Path] = None, + exclude_plots: bool = False, + plot_size: int = 18, + ) -> BlockToolsNewPlotResult: + final_dir = self.plot_dir + if path is not None: + final_dir = path + final_dir.mkdir(parents=True, exist_ok=True) + + # No datetime in the filename, to get deterministic filenames and not re-plot + created, existed = await create_v2_plots( + final_dir=Path(final_dir), + size=plot_size, + pool_ph=self.pool_ph, + farmer_pk=self.farmer_pk, + use_datetime=False, + test_private_keys=[AugSchemeMPL.key_gen(std_hash(self.created_plots2.to_bytes(2, "big")))], + ) + self.created_plots2 += 1 + + plot_id_new: Optional[bytes32] = None + path_new: Optional[Path] = None + new_plot: bool = True + + if len(created): + assert len(existed) == 0 + plot_id_new, path_new = next(iter(created.items())) + + if len(existed): + assert len(created) == 0 + plot_id_new, path_new = next(iter(existed.items())) + new_plot = False + assert plot_id_new is not None + assert path_new is not None + + if not exclude_plots: + self.expected_plots[plot_id_new] = path_new + + return BlockToolsNewPlotResult(plot_id_new, new_plot) + async def refresh_plots(self) -> None: self.plot_manager.refresh_parameter = replace( self.plot_manager.refresh_parameter, batch_size=uint32(4 if len(self.expected_plots) % 3 == 0 else 3) @@ -1505,8 +1581,6 @@ def get_pospaces_for_challenge( rng = random.Random() rng.seed(seed) - required_plot_strength = calculate_required_plot_strength(constants, prev_transaction_b_height) - for plot_info in self.plot_manager.plots.values(): plot_id: bytes32 = plot_info.prover.get_id() if force_plot_id is not None and plot_id != force_plot_id: @@ -1515,32 +1589,28 @@ def get_pospaces_for_challenge( if not passes_plot_filter(prefix_bits, plot_id, challenge_hash, signage_point): continue - # v2 plots aren't valid until after the hard fork - if ( - prev_transaction_b_height < constants.HARD_FORK2_HEIGHT - and plot_info.prover.get_version() == PlotVersion.V2 - ): - continue + if plot_info.prover.get_version() == PlotVersion.V2: + # v2 plots aren't valid until after the hard fork + if prev_transaction_b_height < constants.HARD_FORK2_HEIGHT: + continue - new_challenge: bytes32 = calculate_pos_challenge(plot_id, challenge_hash, signage_point) + if plot_info.prover.get_strength() < constants.PLOT_STRENGTH_INITIAL: + self.log.warn( + f"Plot strength ({plot_info.prover.get_strength()}) too low, " + f"cannot be used for farming: {plot_info.prover.get_filename()}" + ) + continue - # these are either qualities (v1) or partial proofs (v2) - proofs: Sequence[Union[bytes32, bytes]] - v = plot_info.prover.get_version() - if v == PlotVersion.V1: - proofs = plot_info.prover.get_qualities_for_challenge(new_challenge) - else: - proofs = plot_info.prover.get_partial_proofs_for_challenge(new_challenge, required_plot_strength) + new_challenge: bytes32 = calculate_pos_challenge(plot_id, challenge_hash, signage_point) - for proof_index, proof in enumerate(proofs): - if v == PlotVersion.V2: - quality_str = quality_for_partial_proof(proof, new_challenge) - elif v == PlotVersion.V1: - quality_str = bytes32(proof) + qualities: Sequence[QualityProtocol] = plot_info.prover.get_qualities_for_challenge( + new_challenge, constants.QUALITY_PROOF_SCAN_FILTER + ) + for idx, quality in enumerate(qualities): required_iters = calculate_iterations_quality( constants, - quality_str, + quality.get_quality(), plot_info.prover.get_size(), difficulty, signage_point, @@ -1550,11 +1620,16 @@ def get_pospaces_for_challenge( if required_iters >= calculate_sp_interval_iters(constants, sub_slot_iters): continue - proof_xs: bytes - if v == PlotVersion.V1: - proof_xs = plot_info.prover.get_full_proof(new_challenge, proof_index) - else: - proof_xs = solve_proof(proof) + proof = b"" + if isinstance(plot_info.prover, V1Prover): + proof = plot_info.prover.get_full_proof(new_challenge, idx) + elif isinstance(plot_info.prover, V2Prover): + assert isinstance(quality, V2Quality) + partial_proof = plot_info.prover.get_partial_proof(quality) + k_size = plot_info.prover.get_size().size_v2 + strength = plot_info.prover.get_strength() + assert k_size is not None + proof = solve_proof(partial_proof, plot_id, strength, k_size) # Look up local_sk from plot to save locked memory ( @@ -1576,7 +1651,7 @@ def get_pospaces_for_challenge( plot_info.pool_contract_puzzle_hash, plot_pk, plot_info.prover.get_size(), - proof_xs, + proof, ) found_proofs.append((required_iters, proof_of_space)) random_sample = found_proofs @@ -2083,6 +2158,7 @@ async def create_block_tools_async( num_og_plots: int = 15, num_pool_plots: int = 5, num_non_keychain_plots: int = 3, + num_v2_plots: int = 15, ) -> BlockTools: global create_block_tools_async_count create_block_tools_async_count += 1 @@ -2093,6 +2169,7 @@ async def create_block_tools_async( num_og_plots=num_og_plots, num_pool_plots=num_pool_plots, num_non_keychain_plots=num_non_keychain_plots, + num_v2_plots=num_v2_plots, ) return bt diff --git a/chia/simulator/start_simulator.py b/chia/simulator/start_simulator.py index 0e2967f340bd..8899993ec6e1 100644 --- a/chia/simulator/start_simulator.py +++ b/chia/simulator/start_simulator.py @@ -117,7 +117,9 @@ async def async_main( plot_dir=plot_dir, ) await bt.setup_keys(fingerprint=fingerprint, reward_ph=farming_puzzle_hash) - await bt.setup_plots(num_og_plots=PLOTS, num_pool_plots=0, num_non_keychain_plots=0, plot_size=PLOT_SIZE) + await bt.setup_plots( + num_og_plots=PLOTS, num_pool_plots=0, num_non_keychain_plots=0, plot_size=PLOT_SIZE, num_v2_plots=PLOTS + ) # Everything after this is not simulator specific, excluding the if test_mode. initialize_logging( service_name=SERVICE_NAME, diff --git a/chia/solver/solver.py b/chia/solver/solver.py index 2483bb0c903f..06c17cd09718 100644 --- a/chia/solver/solver.py +++ b/chia/solver/solver.py @@ -8,13 +8,14 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar, Optional, cast -from chia_rs import ConsensusConstants +from chia_rs import ConsensusConstants, solve_proof +from chia_rs.sized_bytes import bytes32 +from chia_rs.sized_ints import uint64 from chia.protocols.outbound_message import NodeType from chia.rpc.rpc_server import StateChangedProtocol, default_get_connections from chia.server.server import ChiaServer from chia.server.ws_connection import WSChiaConnection -from chia.types.blockchain_format.proof_of_space import solve_proof log = logging.getLogger(__name__) @@ -67,10 +68,10 @@ async def manage(self) -> AsyncIterator[None]: self.executor.shutdown(wait=True) self.log.info("Solver service shutdown complete") - def solve(self, partial_proof: bytes) -> Optional[bytes]: - self.log.debug(f"Solve request: partial={partial_proof.hex()}") + def solve(self, partial_proof: list[uint64], plot_id: bytes32, strength: int, size: int) -> Optional[bytes]: + self.log.info(f"Solve request: partial={partial_proof[:5]} plot-id: {plot_id} k: {size}") try: - return solve_proof(partial_proof) + return solve_proof(partial_proof, plot_id, strength, size) except Exception: self.log.exception("solve_proof()") return None diff --git a/chia/solver/solver_api.py b/chia/solver/solver_api.py index 4ae365fc17c1..417e5066fc1a 100644 --- a/chia/solver/solver_api.py +++ b/chia/solver/solver_api.py @@ -40,12 +40,12 @@ async def solve( self.log.error("Solver is not started") return None - self.log.debug(f"Solving partial {request.partial_proof.hex()}") + self.log.debug(f"Solving partial {request.partial_proof[:5]}") try: - proof = self.solver.solve(request.partial_proof) + proof = self.solver.solve(request.partial_proof, request.plot_id, request.strength, request.size) if proof is None: - self.log.warning(f"Solver returned no proof for parital {request.partial_proof.hex()}") + self.log.warning(f"Solver returned no proof for parital {request.partial_proof[:5]}") return None self.log.debug(f"Successfully solved partial proof, returning {len(proof)} byte proof") @@ -55,5 +55,5 @@ async def solve( ) except Exception as e: - self.log.error(f"Error solving parital {request.partial_proof.hex()}: {e}") + self.log.error(f"Error solving parital {request.partial_proof[:5]}: {e}") return None diff --git a/chia/types/blockchain_format/proof_of_space.py b/chia/types/blockchain_format/proof_of_space.py index cb1f63128963..c29032934cd6 100644 --- a/chia/types/blockchain_format/proof_of_space.py +++ b/chia/types/blockchain_format/proof_of_space.py @@ -24,12 +24,6 @@ def validate_proof_v2( raise NotImplementedError -# this is compute intensive, solving a partial proof returning a full proof -def solve_proof(partial_proof: bytes) -> bytes: - # TODO: todo_v2_plots call into new chiapos library - raise NotImplementedError - - # given a partial proof, computes the quality. This is used to compute required iters. def quality_for_partial_proof(partial_proof: bytes, challenge: bytes32) -> bytes32: # TODO: todo_v2_plots call into new chiapos library @@ -65,6 +59,14 @@ def make_pos( def get_plot_id(pos: ProofOfSpace) -> bytes32: assert pos.pool_public_key is None or pos.pool_contract_puzzle_hash is None + + plot_size = pos.size() + if plot_size.size_v2 is not None: + assert pos.pool_contract_puzzle_hash + # v2 plots have a fixed k-size, so we store the *strength* in this field + # instead + return calculate_plot_id_v2(pos.pool_contract_puzzle_hash, pos.plot_public_key, uint8(plot_size.size_v2)) + if pos.pool_public_key is None: assert pos.pool_contract_puzzle_hash is not None return calculate_plot_id_ph(pos.pool_contract_puzzle_hash, pos.plot_public_key) @@ -142,8 +144,9 @@ def verify_and_get_quality_string( # === V2 plots === assert plot_size.size_v2 is not None - required_plot_strength = calculate_required_plot_strength(constants, height) - return validate_proof_v2(plot_id, plot_size.size_v2, required_plot_strength, pos.challenge, bytes(pos.proof)) + return validate_proof_v2( + plot_id, plot_size.size_v2, constants.PLOT_STRENGTH_INITIAL, pos.challenge, bytes(pos.proof) + ) def passes_plot_filter( @@ -164,8 +167,13 @@ def passes_plot_filter( def calculate_prefix_bits(constants: ConsensusConstants, height: uint32, plot_size: PlotSize) -> int: - # v2 plots have a constant plot filter size if plot_size.size_v2 is not None: + if height >= constants.PLOT_FILTER_V2_THIRD_ADJUSTMENT_HEIGHT: + return constants.NUMBER_ZERO_BITS_PLOT_FILTER_V2 + 3 + if height >= constants.PLOT_FILTER_V2_SECOND_ADJUSTMENT_HEIGHT: + return constants.NUMBER_ZERO_BITS_PLOT_FILTER_V2 + 2 + if height >= constants.PLOT_FILTER_V2_FIRST_ADJUSTMENT_HEIGHT: + return constants.NUMBER_ZERO_BITS_PLOT_FILTER_V2 + 1 return constants.NUMBER_ZERO_BITS_PLOT_FILTER_V2 prefix_bits = int(constants.NUMBER_ZERO_BITS_PLOT_FILTER_V1) @@ -181,21 +189,6 @@ def calculate_prefix_bits(constants: ConsensusConstants, height: uint32, plot_si return max(0, prefix_bits) -def calculate_required_plot_strength(constants: ConsensusConstants, height: uint32) -> uint8: - if height < constants.PLOT_STRENGTH_4_HEIGHT: - return constants.PLOT_STRENGTH_INITIAL - if height < constants.PLOT_STRENGTH_5_HEIGHT: - return uint8(4) - if height < constants.PLOT_STRENGTH_6_HEIGHT: - return uint8(5) - if height < constants.PLOT_STRENGTH_7_HEIGHT: - return uint8(6) - if height < constants.PLOT_STRENGTH_8_HEIGHT: - return uint8(7) - else: - return uint8(8) - - def calculate_plot_filter_input(plot_id: bytes32, challenge_hash: bytes32, signage_point: bytes32) -> bytes32: return std_hash(plot_id + challenge_hash + signage_point) @@ -218,6 +211,10 @@ def calculate_plot_id_ph( return std_hash(bytes(pool_contract_puzzle_hash) + bytes(plot_public_key)) +def calculate_plot_id_v2(pool_contract_puzzle_hash: bytes32, plot_public_key: G1Element, strength: uint8) -> bytes32: + return std_hash(bytes(pool_contract_puzzle_hash) + bytes(plot_public_key) + strength.stream_to_bytes()) + + def generate_taproot_sk(local_pk: G1Element, farmer_pk: G1Element) -> PrivateKey: taproot_message: bytes = bytes(local_pk + farmer_pk) + bytes(local_pk) + bytes(farmer_pk) taproot_hash: bytes32 = std_hash(taproot_message) diff --git a/poetry.lock b/poetry.lock index 8ce48cbd3cbd..1219c2be9b76 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.0 and should not be changed by hand. [[package]] name = "aiofiles" @@ -876,38 +876,38 @@ pytest = ">=8.3.3,<9.0.0" [[package]] name = "chia-rs" -version = "0.30.0" +version = "0.31.0" description = "" optional = false python-versions = "*" groups = ["main"] files = [ - {file = "chia_rs-0.30.0-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:a2ca9669e0ffad97cc3e39e49839fef568013dcb4a645cfa802c68f5acb321c6"}, - {file = "chia_rs-0.30.0-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:ef12c719f4c6fb0d427b110f331e026b4bcb6fa95856a0095f0d885ebf50d0a9"}, - {file = "chia_rs-0.30.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7d140a0de8ab59fa75421590550491ea042a8a6e17f1b789b92b7f267561c568"}, - {file = "chia_rs-0.30.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b9dbd5b2265e8743c1038b7e8cfff6131ef8a56a68fc8675b11ba18d4a3d9f97"}, - {file = "chia_rs-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:67e5b5a5f9acb42ff364dcf72e5fc7fab7c6e2daad26a99edd9b24a7d4c932e8"}, - {file = "chia_rs-0.30.0-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:2b995c05429343c8b91bdfa1cdca97e23b72393b1b54f0ab6703e63e74016f1d"}, - {file = "chia_rs-0.30.0-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:2cc43223e3e092621523e35a38320a87941886315d3eb29b69e0470c7546e1c2"}, - {file = "chia_rs-0.30.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:b30d3868b004dba2fbc59c004cb3345b598f9f5b500ce426d23ccb3aaa1f608b"}, - {file = "chia_rs-0.30.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45284e3d4655c1b8fca2024c050777bf6919de429949e26c5ff83ffe553fbd87"}, - {file = "chia_rs-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:d892e63730ec94ab16fbb21cb25e07889d8136973487252c7b65d2611d759346"}, - {file = "chia_rs-0.30.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:004259970cba586f4bc70b6759ce9d1fc8310fdd96bb25d9e68e79a497a55bc9"}, - {file = "chia_rs-0.30.0-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:5c9ffcd52640edc31860e52ce482e91e2090b46373e70c07fe5828b99be7b10b"}, - {file = "chia_rs-0.30.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b1850d32ee34cc92e3d912a445a528dc2cfde08e5eed2304fb050209f4637fa5"}, - {file = "chia_rs-0.30.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3ce97f9baa4adc6233935b1138193446b81a0742993d7d49f186dbc61a8a2f91"}, - {file = "chia_rs-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:9edd53c61d1462aae5a91265b9499afee19fea40a69c36f79d8ce7e519b3ad54"}, - {file = "chia_rs-0.30.0-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:828477104cb13e57a1d82f286cb2a9961f711748da6b6459fbc522e14a6eeaa3"}, - {file = "chia_rs-0.30.0-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:707035a2596a48ada8a16b1daae7454360cfefc41074860ccf55dd6db0de1bcd"}, - {file = "chia_rs-0.30.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:3e1437b5af745b7f080882204c96b82ca50df394cca71ca4e977d86c0df5881a"}, - {file = "chia_rs-0.30.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e3aa7eb08d34641d9059a2cd4d27ccf03e86c1b371b1ae465bbb5b1d84e1d3eb"}, - {file = "chia_rs-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:6961a0c946217a6c25abcd5964e7e6d484f54456f70f226348b3a2a60b2c7840"}, - {file = "chia_rs-0.30.0-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:5209a94bf27e3209292b5a18cd582f6a185180b42a1b70d0bf69463c41ac440e"}, - {file = "chia_rs-0.30.0-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:203d52fd1622b96e970ad9c047d516132b19bc136ecf62e526c4e8ca9f23de1e"}, - {file = "chia_rs-0.30.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ef930b975b4f74b5c7089d263f9183826c5afdf076ae6fc5e765bafcad36b983"}, - {file = "chia_rs-0.30.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:68374a5b3e9e7d27a81d0d96b06053f24ace0238d60795ab7188ba1dee556112"}, - {file = "chia_rs-0.30.0-cp39-cp39-win_amd64.whl", hash = "sha256:de9da557ef7afd56dbc28b46d53747c39ce9cb919b78fb50be22ed335b22293d"}, - {file = "chia_rs-0.30.0.tar.gz", hash = "sha256:87937b76a0dab8cb304c641fb9af77a59c8270d59686b00d1a81766c4c8acfed"}, + {file = "chia_rs-0.31.0-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:4d94d9ff8f04030e1082d0103734d7a1c95267811fe77f7f3c1ddb40382560d1"}, + {file = "chia_rs-0.31.0-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:e1d38a6e2c076213e124d892b1e9f6021fb3146a063e14f4dad1eb41b4aa6355"}, + {file = "chia_rs-0.31.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:856859000e945e5a0a6e0bb442dd01f6019d2c103e14a6c3d0d3c725136820b7"}, + {file = "chia_rs-0.31.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:dfcb510103bf9400581597ff9bca87a2e9ee7b90e31af42b6028c564819e6d0f"}, + {file = "chia_rs-0.31.0-cp310-cp310-win_amd64.whl", hash = "sha256:5ab67a38dabd0a5c8c8a7d5df8407dd8e167937905daaf79f57a678da7bb8953"}, + {file = "chia_rs-0.31.0-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:fea8dc7f1cfb04d072c0fe2f728cf27943e770382b4111cf48e57d9aaea6cb14"}, + {file = "chia_rs-0.31.0-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:ddf84214e1a0c2fc0b37588e3180826b95d4de8403cbe2b0911c5269afb6d0a3"}, + {file = "chia_rs-0.31.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:05252ecb5025ef170cff8ad425cab33f1f9dc037a0eba0461467fa4099a5765b"}, + {file = "chia_rs-0.31.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:b52255426519f9841cb4ab4a4d3005b755e2e87192285e3c55fb78bacef268f5"}, + {file = "chia_rs-0.31.0-cp311-cp311-win_amd64.whl", hash = "sha256:48ddba48c61837fef458f960da597ec38409c2fd6cfe668d64e3b49f693551b0"}, + {file = "chia_rs-0.31.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:a58f7629f76e8816ec0c243ccf79b18519db953de22d829cb4f1eb0f3ce56ccb"}, + {file = "chia_rs-0.31.0-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:7918e0229e038faf2aa7ad06400a32ad9afb330ce14d11d71e7703b1da8c6eac"}, + {file = "chia_rs-0.31.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:2e8c068482bd58c4ee0bf6f4e23b128c2e50cc8b152320960c2c470035846825"}, + {file = "chia_rs-0.31.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:1ca6341c5fdf13d57697d5fd5936dd1ad40044593ba6d40163a88ac8447d19f5"}, + {file = "chia_rs-0.31.0-cp312-cp312-win_amd64.whl", hash = "sha256:c15ab05ecb6512d70015d6fb7afdf33c7323d9803cc952f7946dd9eac5a7c703"}, + {file = "chia_rs-0.31.0-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:80f0a1895993c1e6d66ebab9f90ae52bae23a5471b4ad19000e05f12f1604cce"}, + {file = "chia_rs-0.31.0-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:a7c728c7d06089f407cc300b001935a2a093e9c8cabf308146826dc8175fc072"}, + {file = "chia_rs-0.31.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:432a21ad80e9da4b8fff325222d06e6ae2aa991d631ba5f093ab0ece264e7a2d"}, + {file = "chia_rs-0.31.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:faf7cb810278c222b2be6b3290626300042c1fa3252be345b99ef5e0f7f1ebca"}, + {file = "chia_rs-0.31.0-cp313-cp313-win_amd64.whl", hash = "sha256:027e4381a280a9d40a80da8fc2e3d9da21e2d49c30d5397e7b485b860e86a70d"}, + {file = "chia_rs-0.31.0-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:1505101d11e032e95792c3f7a754bfbf990ea606754c8313841c62bb6fb3f9f5"}, + {file = "chia_rs-0.31.0-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:4f7161c437f7b7102595a0f4e64800f49dbe25759e2cfdc2f7e90e3627fcb3e1"}, + {file = "chia_rs-0.31.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:0ec09937a837b58dd3c62abf59d6c2146a1265194bd041e0425963bd9c2fb24c"}, + {file = "chia_rs-0.31.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:ffb4fb620f81aa0bf90ce93f88776633b8c5e737d99c05283ba92f492a62fb8c"}, + {file = "chia_rs-0.31.0-cp39-cp39-win_amd64.whl", hash = "sha256:c1a9c5387a3b360199486702ba8a39cfc8b162b0d8eda8e109a878bb1369d4d3"}, + {file = "chia_rs-0.31.0.tar.gz", hash = "sha256:b86c654a13cde29787a6502835db485d4b699d2e81216ae07c42492e28bc95c2"}, ] [package.dependencies] @@ -3938,19 +3938,36 @@ markers = "python_version == \"3.12\"" files = [ {file = "zstd-1.5.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5cd20afab8d13c52d2b2219bf18cc765eae87b8219343bce20647007890adab"}, {file = "zstd-1.5.5.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:0f467ab9b57ab8b4b874e6974d38b802f20406803bb7ec9308df923553cd48f7"}, + {file = "zstd-1.5.5.1-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:1866fd2eb37bc80cd0a1bab8f099df5e778a773e829378c633f9fa1325a5dae1"}, + {file = "zstd-1.5.5.1-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:b90ab3e3a2cc196c55151b8d0c506bc7488cb7d7c6479a9f535a047703c29427"}, + {file = "zstd-1.5.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdf76b7c92d50e0ba3e76db6684ca58a419877436de948e39a8b72df1e3a0ff2"}, {file = "zstd-1.5.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ca7df4319ef56b50b441ee6f6427e94672db811ad890bbb487d42bb0a6eebeb"}, + {file = "zstd-1.5.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9a4d2d6302834a22550a5885f9ae27d1baa1896afb0d40d70e0e9090c026fa5b"}, {file = "zstd-1.5.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:06d91d45b912417e83793a78a3f6668be631a1744a7841ecde4dbfeee309f5ca"}, {file = "zstd-1.5.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:408d3f56bbf0f091a2bab33a3dee62ac9bafe94bb96940b5e4611e43dcf0ee68"}, {file = "zstd-1.5.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23f6fb9a631f7e7934cccdfbb69e06a47e1a5e64d69b09a7a2a5cf6e1db5014d"}, {file = "zstd-1.5.5.1-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:02ecf7d9c41ae9a5685a7fdbf2aebbfb185125c07622819512fed41459233d51"}, + {file = "zstd-1.5.5.1-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:abfdcc4df4388dbbde1f1999e0d3cdb794cab371ca03521500192aa6689da403"}, + {file = "zstd-1.5.5.1-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:38e9f1505bd181111defca1c00bc772674ad12a581413cea33dc954002dfefd1"}, + {file = "zstd-1.5.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1f0734b74587f4a14d3b1ac194cb260a7e919d665118eeed3155bade21f9c8e"}, {file = "zstd-1.5.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3d13647ff05da2f8a9c1067b6e4182102fc4b6547c49ecdd3031e7ae2cadc1a"}, + {file = "zstd-1.5.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2892f9e76a9ba9e0fcd32977d5b1fc8394568465dd54eac5bef24f84804bc208"}, {file = "zstd-1.5.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0f0c81d4241226eab831a500120ddaf9a35dd59593eeb48ed7879e2c722c24c"}, {file = "zstd-1.5.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:62d2ab79a32bb2d09c74e550636cf4639c1ff3760625f03dccd30e2606b24e66"}, {file = "zstd-1.5.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ec16bc70e3db77a88129c001fb62ce0956a1a3321e0ea4ee389c78d7f616ac50"}, {file = "zstd-1.5.5.1-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b8e7f5ee15e7d5d9a5deebc49f62316c901000d8485e68c5f5d602312bfba9ae"}, + {file = "zstd-1.5.5.1-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:a4efaba2324628af8f31662c6e5adc363778a8454f6605ac01ece37dfcc9ba6e"}, + {file = "zstd-1.5.5.1-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:0ffd07b4a44668ece31dc6281021c36517d6802ce8e20cc455863a99007ea324"}, + {file = "zstd-1.5.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46237c84f7817dc4d9c40cc43c7c9325a587f5ec593f364bae501cccada534ac"}, {file = "zstd-1.5.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55bcbcca3a342e8298fd23e58f300b13a33e65667e837cb34dd16c8126cdf77a"}, + {file = "zstd-1.5.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3aabfce08716b06357718fc73a63b5ead4e9ad3fe6765f8b37386ab94c58fb49"}, {file = "zstd-1.5.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3f06af07fc8976114bb233c426c499b3ac219a6545d163b3c1a4989c40288"}, {file = "zstd-1.5.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:ab96e69cd4a70c5a4d3c613e85d6492817c35a9ba3921ee86f9d26a788c1522b"}, + {file = "zstd-1.5.5.1-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:d6599f5e0783728388c574e743640bc7d0f826c4d73030ff1541b16f3dd0a36f"}, + {file = "zstd-1.5.5.1-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:60631652d49f2cd892cd8beadadb0e18a940489baf59a54f17cba8dfd197bb27"}, + {file = "zstd-1.5.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b003e7319286b8f87ec4da872e9d1be811edb057ab99b3fde498168d48853987"}, + {file = "zstd-1.5.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:858a7892483cc5a2902fa91b5da33b18d7c495a51e429ec5a6d8abe34907da2d"}, + {file = "zstd-1.5.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:020f2c4805d2c6dff58da71a9f84689e64852fa885869faf73c39c9230dd8540"}, {file = "zstd-1.5.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1b111beaf6a4d98b6382e0891287a0d61ae44c3ec819b37f2256a482cc2ccc08"}, {file = "zstd-1.5.5.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:4a434d83bcfa8a0d843451bc073fe60bae9f710dce796c9dba716c66df49c311"}, {file = "zstd-1.5.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cccbafaeda62b98f3d3c70b5e686c6c539c0ea76cab88a9817f1cf3842d4272d"}, @@ -3958,7 +3975,11 @@ files = [ {file = "zstd-1.5.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:eb17b3401e3fa2c16ecfee4b00cca82fbc71ffd6c64c97619605b2779bc7f5d0"}, {file = "zstd-1.5.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8612bb288facb0e24d171f7010f2ea2159d8bd7d8243667f707994f398b6d26d"}, {file = "zstd-1.5.5.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:8cee6c496fe49a27f4a641c75814681ee932201680eb4b07c88afde1b730b633"}, + {file = "zstd-1.5.5.1-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:905577dfb0182aafeb4bee396ca9426a63efe38b384c6684102f98433ac411f7"}, + {file = "zstd-1.5.5.1-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:7c36475a570d5e6c031afd12a6f9990c57afc59106dd2f55e52388fd3d79e429"}, + {file = "zstd-1.5.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:52e9e5d9f2e71f5bb86f795bb9f7648be8407f5305bcb3b698d271d6a9678b02"}, {file = "zstd-1.5.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b629e950bd0bcd169149af8c9c312d8091a922c07adeef0cd6e9fb06dc11216a"}, + {file = "zstd-1.5.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d87a075de6347d8804e41441fd41d9d17e362c07bac9d39ec361caef92c6ae6"}, {file = "zstd-1.5.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:86a968e443d735ff4716c18fda91b88d30fee7419c381d26ab2f92e779e69c22"}, {file = "zstd-1.5.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:7e4a460b169110b16d39eb05249d36c9b09c9d1b4831e72ead13a484b5404029"}, ] @@ -3976,4 +3997,4 @@ upnp = ["miniupnpc"] [metadata] lock-version = "2.1" python-versions = ">=3.10, <4" -content-hash = "593d5a538eed9036f2a2a58bfbf9069cb973bb34a54eb13ef1382cf4c92a0a1b" +content-hash = "8c011d4c65d219e4c7cecc6827d2b25e303cf0761a3febc459b3e573bc4ee8d6" diff --git a/pyproject.toml b/pyproject.toml index 9cb63f2e5143..2eb904fd289e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,7 +53,7 @@ boto3 = ">=1.35.43" # AWS S3 for Data Layer S3 plugin chiabip158 = ">=1.5.2" # bip158-style wallet filters chiapos = ">=2.0.10" # proof of space chia-puzzles-py = ">=0.20.1" -chia_rs = ">=0.30, <0.31" +chia_rs = ">=0.31, <0.32" chiavdf = ">=1.1.10" # timelord and vdf verification click = ">=8.1.7" # For the CLI clvm = ">=0.9.14"