PoS2-prover (#20159)

* bump chia_rs to 0.31.0

* integrate new pos2 API from chia_rs into V2Prover

* fixup test_harvester_api mocking of Prover

* review-comments: rename get_quality() to get_string() on Quality protocol

* reivew comments

* fixup bump chia_rs

* restore quality string to bytes32
This commit is contained in:
Arvid Norberg
2025-10-22 15:40:25 -07:00
committed by GitHub
parent e57358aea6
commit 794cce7826
22 changed files with 354 additions and 326 deletions
@@ -13,7 +13,6 @@ from chia._tests.util.misc import Marks, datacases
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,
@@ -200,29 +199,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 +248,9 @@ class TestProofOfSpace:
@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 +267,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
@@ -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
+11 -4
View File
@@ -15,6 +15,7 @@ from chia._tests.conftest import HarvesterFarmerEnvironment
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:
+51 -59
View File
@@ -12,57 +12,6 @@ from chia.plotting.prover import PlotVersion, V1Prover, V2Prover, get_prover_fro
class TestProver:
def test_v2_prover_init_with_nonexistent_file(self) -> None:
prover = V2Prover("/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")
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")
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")
assert prover.get_compression_level() == uint8(0)
def test_v2_prover_get_id_raises_error(self) -> None:
prover = V2Prover("/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")
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)
def test_v2_prover_bytes_raises_error(self) -> None:
prover = V2Prover("/nonexistent/path/test.plot2")
with pytest.raises(NotImplementedError, match="V2 plot format is not yet implemented"):
bytes(prover)
def test_v2_prover_from_bytes_raises_error(self) -> None:
with pytest.raises(NotImplementedError, match="V2 plot format is not yet implemented"):
V2Prover.from_bytes(b"test_data")
def test_get_prover_from_file(self) -> None:
prover = get_prover_from_file("/nonexistent/path/test.plot2")
assert prover.get_version() == PlotVersion.V2
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
@@ -78,6 +27,56 @@ class TestProver:
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.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.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.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.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.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.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), uint8(5))
def test_v2_prover_bytes_raises_error(self) -> None:
prover = V2Prover.from_filename("/nonexistent/path/test.plot2")
with pytest.raises(NotImplementedError, match="V2 plot format is not yet implemented"):
bytes(prover)
def test_v2_prover_from_bytes_raises_error(self) -> None:
with pytest.raises(NotImplementedError, match="V2 plot format is not yet implemented"):
V2Prover.from_bytes(b"test_data")
def test_get_prover_from_file(self) -> None:
prover = get_prover_from_file("/nonexistent/path/test.plot2")
assert prover.get_version() == PlotVersion.V2
with pytest.raises(NotImplementedError, match="V2 plot format is not yet implemented"):
prover.get_size()
class TestV1Prover:
def test_v1_prover_get_version(self) -> None:
"""Test that V1Prover.get_version() returns PlotVersion.V1"""
@@ -87,13 +86,6 @@ class TestV1Prover:
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 @@ class TestGetProverFromBytes:
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")
+8 -1
View File
@@ -5,6 +5,8 @@ from unittest.mock import patch
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)
+10 -3
View File
@@ -155,9 +155,11 @@ partial_proof = harvester_protocol.PartialProofsData(
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"
@@ -1116,6 +1118,11 @@ respond_compact_proof_of_time = timelord_protocol.RespondCompactProofOfTime(
)
# 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")
Binary file not shown.
+10 -6
View File
@@ -69,9 +69,11 @@ partial_proof_json: dict[str, Any] = {
"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",
@@ -2730,9 +2732,11 @@ error_without_data_json: dict[str, Any] = {"code": 1, "message": "Unknown", "dat
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"}
@@ -56,7 +56,6 @@ test_constants = ConsensusConstants(
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 @@ test_constants = ConsensusConstants(
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),
)
+6 -6
View File
@@ -76,11 +76,11 @@ DEFAULT_CONSTANTS = ConsensusConstants(
# 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 @@ DEFAULT_CONSTANTS = ConsensusConstants(
# 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),
)
+17 -14
View File
@@ -507,11 +507,17 @@ class FarmerAPI:
# 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 @@ class FarmerAPI:
# 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 @@ class FarmerAPI:
# 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 @@ class FarmerAPI:
# 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
+25 -20
View File
@@ -16,7 +16,7 @@ from chia.consensus.pot_iterations import (
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.server.ws_connection import WSChiaConnection
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 @@ class HarvesterAPI:
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 @@ class HarvesterAPI:
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 @@ class HarvesterAPI:
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_string(),
plot_info.prover.get_size(),
difficulty,
new_challenge.sp_hash,
@@ -203,8 +196,14 @@ class HarvesterAPI:
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 @@ class HarvesterAPI:
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 @@ class HarvesterAPI:
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 @@ class HarvesterAPI:
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 @@ class HarvesterAPI:
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_string(),
plot_info.prover.get_size(),
difficulty,
new_challenge.sp_hash,
@@ -292,6 +295,7 @@ class HarvesterAPI:
# 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 @@ class HarvesterAPI:
)
continue
quality_str = bytes32(quality.get_string())
responses.append(
(
quality_str,
+15 -21
View File
@@ -7,16 +7,15 @@ from collections.abc import Sequence
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 @@ from chia.plotting.util import (
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 check_plots(
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 check_plots(
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_string()
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(
+56 -39
View File
@@ -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_string(self) -> bytes32: ...
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_string(self) -> bytes32:
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 @@ class V2Prover:
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: bytes32
def get_string(self) -> bytes32:
return self._quality
class V1Prover:
@@ -106,6 +123,9 @@ class V1Prover:
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 @@ class V1Prover:
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 @@ class 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:
+3 -1
View File
@@ -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
+8 -2
View File
@@ -2,17 +2,23 @@ from __future__ import annotations
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] # 64 proof fragments
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
+30 -33
View File
@@ -14,7 +14,7 @@ from collections.abc import Sequence
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 @@ from chia_rs import (
SubSlotProofs,
UnfinishedBlock,
solution_generator,
solve_proof,
)
from chia_rs.sized_bytes import bytes32
from chia_rs.sized_ints import uint8, uint16, uint32, uint64, uint128
@@ -64,7 +65,7 @@ from chia.daemon.keychain_proxy import KeychainProxy, connect_to_keychain_and_va
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.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.program import DEFAULT_FLAGS, INFINITE_COST, P
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
@@ -1505,8 +1503,6 @@ class BlockTools:
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 +1511,28 @@ class BlockTools:
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
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
new_challenge: bytes32 = calculate_pos_challenge(plot_id, challenge_hash, signage_point)
# 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)
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_string(),
plot_info.prover.get_size(),
difficulty,
signage_point,
@@ -1550,11 +1542,16 @@ class BlockTools:
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 +1573,7 @@ class BlockTools:
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
+6 -5
View File
@@ -8,13 +8,14 @@ from concurrent.futures.thread import ThreadPoolExecutor
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 @@ class Solver:
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
+4 -4
View File
@@ -40,12 +40,12 @@ class SolverAPI:
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 @@ class SolverAPI:
)
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
+9 -24
View File
@@ -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
@@ -142,8 +136,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 +159,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 +181,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)
+29 -29
View File
@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.2.1 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.32.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.32.0-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:3a423b3317246bb0337a9e73831932cec6757b9d71fe3e1556a1dbedf77f7a6a"},
{file = "chia_rs-0.32.0-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:eecbf98f7fd6a29436c71b4d8901aaf1d5c15a7fb7c66f04c251595f8c915d9b"},
{file = "chia_rs-0.32.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:508a81789a839212963601f06c2646e068436f8f1250d8379a33c107c4931497"},
{file = "chia_rs-0.32.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:82d7334d4c9d15765dbda8c8e2a13fa88f44d3b2b97bded48717768edd595f25"},
{file = "chia_rs-0.32.0-cp310-cp310-win_amd64.whl", hash = "sha256:e7095b5840b63d5d3e9f309b6adba53708c827711897ef0eba2ead9d8d137aee"},
{file = "chia_rs-0.32.0-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:32b20eeb971628d13b3ca7605eb6d76e887f253bc25c2c77624fa2e96fb3a6fe"},
{file = "chia_rs-0.32.0-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:be53d2533b0bcf3160d8c1c9d43a225f1951cff343dfc15a682ba3b98530da82"},
{file = "chia_rs-0.32.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4456c6cf824af867437e9176f7052fd96203ff50e1f17985bed85c994a06b0a4"},
{file = "chia_rs-0.32.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76c342357f2f931931354ac16564fe4d7996e6eced48fe2760e6f220a5ab6e93"},
{file = "chia_rs-0.32.0-cp311-cp311-win_amd64.whl", hash = "sha256:aa4d7c1774bce3bb359b3a1441d88aa7aa0c0bd2122763789e58fa806bf2f67e"},
{file = "chia_rs-0.32.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:297ef6a57772e5c5c9ce825f7c2e43e6c52291cc2bbab705d9d64b6300b6f4e5"},
{file = "chia_rs-0.32.0-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:0c94e1c9e6c5d71b25734d47d264514fb9167616377fbd4b518de5447331d3c5"},
{file = "chia_rs-0.32.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5523413a04eadbc7583504e22c0e9360ed64e0d51440adb1b48511a91c4fdfd2"},
{file = "chia_rs-0.32.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4b1131c0547e3be280b36562d84fd8433e805e5306fdaaba769d474b0fc02992"},
{file = "chia_rs-0.32.0-cp312-cp312-win_amd64.whl", hash = "sha256:7016ecab7b06284abc4e926c7fa96146dafd20d830b84c697597b008b6dced2b"},
{file = "chia_rs-0.32.0-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:624a30a9a18400b6e4ce92020e6cbc34f393b3629825455da4c1593e856487ab"},
{file = "chia_rs-0.32.0-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:e67e5b46eaeee5887f150abc33b7b24ef6af6b862afc4b0a4d255453ee978e57"},
{file = "chia_rs-0.32.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:6e695e44428d2f9c02388578291d664dcd54d9fe4608adb51ae44931d210f02a"},
{file = "chia_rs-0.32.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:846a51f87725524c0743ee809a21e876dfda3613624fd4b34dce40269d9e75bc"},
{file = "chia_rs-0.32.0-cp313-cp313-win_amd64.whl", hash = "sha256:63bb1a87feb6c1782154d7deb292a461e36c010840026c4c733495d6935bdcdb"},
{file = "chia_rs-0.32.0-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:5b768d1585bfb6fa8a7f61106ddd5b4f9a7a274a3ebd86b6a03896af29e280da"},
{file = "chia_rs-0.32.0-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:5feabc844caa2ee2a39fd8c40f57497c164f71c2b58ff0a045510529ee1259bf"},
{file = "chia_rs-0.32.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:65b084624ded4382d98cb8e7de8517ec062338df0af4578316fbd1a42ef33ab0"},
{file = "chia_rs-0.32.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:89e2a8d93571fc0067e0f7fe4f7a43f62277696fa4e1a6c493eb89aa3af8700b"},
{file = "chia_rs-0.32.0-cp39-cp39-win_amd64.whl", hash = "sha256:565b4e657936cff798ef684c0e3f496f0e189f98ab37c02d7b1018ab97e23a6b"},
{file = "chia_rs-0.32.0.tar.gz", hash = "sha256:42b4901a0ce1fc94971d2c33b585a950a5528360560619e427bc9d7f534085e9"},
]
[package.dependencies]
@@ -3997,4 +3997,4 @@ upnp = ["miniupnpc"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.10, <4"
content-hash = "593d5a538eed9036f2a2a58bfbf9069cb973bb34a54eb13ef1382cf4c92a0a1b"
content-hash = "6ed8a051283de31228deb72a90abdf55ef4d6aa8fb70cd8173950a6b93c710fd"
+1 -1
View File
@@ -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.32, <0.33"
chiavdf = ">=1.1.10" # timelord and vdf verification
click = ">=8.1.7" # For the CLI
clvm = ">=0.9.14"