Skip to content

Commit ba75ad2

Browse files
committed
change quality_string to quality_chain
1 parent 7ab94f3 commit ba75ad2

File tree

12 files changed

+75
-73
lines changed

12 files changed

+75
-73
lines changed

chia/_tests/farmer_harvester/test_farmer_harvester.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def log_is_ready() -> bool:
186186
)
187187
msg = make_msg(ProtocolMessageTypes.respond_signatures, response)
188188
await harvester_service._node.server.send_to_all([msg], NodeType.FARMER)
189-
await time_out_assert(5, log_is_ready)
189+
await time_out_assert(15, log_is_ready)
190190
# We fail the sps record check
191191
expected_error = f"Do not have challenge hash {challenge_hash}"
192192
assert expected_error in caplog.text
@@ -461,7 +461,7 @@ async def test_solution_response_handler(
461461
}
462462

463463
# create solution response
464-
solution_response = solver_protocol.SolverResponse(quality_string=quality, proof=b"test_proof_from_solver")
464+
solution_response = solver_protocol.SolverResponse(quality_chain=quality, proof=b"test_proof_from_solver")
465465
solver_peer = Mock()
466466
solver_peer.peer_node_id = "solver_peer"
467467

@@ -493,7 +493,7 @@ async def test_solution_response_unknown_quality(
493493
solver_peer = await get_solver_peer(farmer)
494494

495495
# create solution response with unknown quality
496-
solution_response = solver_protocol.SolverResponse(quality_string=bytes32(b"1" * 32), proof=b"test_proof")
496+
solution_response = solver_protocol.SolverResponse(quality_chain=bytes(b"1" * 32), proof=b"test_proof")
497497

498498
with unittest.mock.patch.object(farmer_api, "new_proof_of_space", new_callable=AsyncMock) as mock_new_proof:
499499
await farmer_api.solution_response(solution_response, solver_peer)
@@ -543,7 +543,7 @@ async def test_solution_response_empty_proof(
543543
solver_peer = await get_solver_peer(farmer)
544544

545545
# create solution response with empty proof
546-
solution_response = solver_protocol.SolverResponse(quality_string=quality, proof=b"")
546+
solution_response = solver_protocol.SolverResponse(quality_chain=quality, proof=b"")
547547

548548
with unittest.mock.patch.object(farmer_api, "new_proof_of_space", new_callable=AsyncMock) as mock_new_proof:
549549
await farmer_api.solution_response(solution_response, solver_peer)

chia/_tests/solver/test_solver_service.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,29 @@
77
import pytest
88
from chia_rs import ConsensusConstants, FullBlock
99
from chia_rs.sized_bytes import bytes32
10-
from chia_rs.sized_ints import uint8, uint64
10+
from chia_rs.sized_ints import uint64
1111

1212
from chia._tests.blockchain.blockchain_test_utils import _validate_and_add_block
1313
from chia.consensus.blockchain import Blockchain
1414
from chia.consensus.get_block_challenge import get_block_challenge
1515
from chia.consensus.pot_iterations import is_overflow_block
1616
from chia.protocols.solver_protocol import SolverInfo
17+
from chia.simulator.block_tools import create_block_tools_async
1718
from chia.simulator.setup_services import setup_solver
1819
from chia.solver.solver_rpc_client import SolverRpcClient
1920
from chia.types.blockchain_format.proof_of_space import verify_and_get_quality_string
2021

2122

2223
@pytest.mark.anyio
2324
async def test_solver_api_methods(blockchain_constants: ConsensusConstants, tmp_path: Path) -> None:
24-
async with setup_solver(tmp_path, blockchain_constants) as solver_service:
25+
bt = await create_block_tools_async(constants=blockchain_constants)
26+
async with setup_solver(tmp_path, bt, blockchain_constants) as solver_service:
2527
solver = solver_service._node
2628
solver_api = solver_service._api
2729
assert solver_api.ready() is True
2830

2931
# test solve with real SolverInfo
30-
test_info = SolverInfo(plot_size=uint8(32), plot_difficulty=uint64(1500), quality_string=bytes32([42] * 32))
32+
test_info = SolverInfo(plot_difficulty=uint64(1500), quality_chain=b"test_quality_chain_42")
3133

3234
# test normal solve operation (stub returns None)
3335
result = solver.solve(test_info)
@@ -89,7 +91,8 @@ async def test_solver_with_real_blocks_and_signage_points(
8991
plot_size = pos.size()
9092
k_size = plot_size.size_v1 if plot_size.size_v1 is not None else plot_size.size_v2
9193
assert k_size is not None
92-
async with setup_solver(tmp_path, blockchain_constants) as solver_service:
94+
bt = await create_block_tools_async(constants=blockchain_constants)
95+
async with setup_solver(tmp_path, bt, blockchain_constants) as solver_service:
9396
assert solver_service.rpc_server is not None
9497
solver_rpc_client = await SolverRpcClient.create(
9598
self_hostname, solver_service.rpc_server.listen_port, solver_service.root_path, solver_service.config
@@ -105,7 +108,8 @@ async def test_solver_with_real_blocks_and_signage_points(
105108
async def test_solver_error_handling_and_edge_cases(
106109
blockchain_constants: ConsensusConstants, self_hostname: str, tmp_path: Path
107110
) -> None:
108-
async with setup_solver(tmp_path, blockchain_constants) as solver_service:
111+
bt = await create_block_tools_async(constants=blockchain_constants)
112+
async with setup_solver(tmp_path, bt, blockchain_constants) as solver_service:
109113
assert solver_service.rpc_server is not None
110114
solver_rpc_client = await SolverRpcClient.create(
111115
self_hostname, solver_service.rpc_server.listen_port, solver_service.root_path, solver_service.config
@@ -134,7 +138,7 @@ async def test_solver_error_handling_and_edge_cases(
134138

135139
# test solver handles exception in solve method
136140
solver = solver_service._node
137-
test_info = SolverInfo(plot_size=uint8(32), plot_difficulty=uint64(1000), quality_string=bytes32.zeros)
141+
test_info = SolverInfo(plot_difficulty=uint64(1000), quality_chain=b"test_quality_chain_zeros")
138142

139143
with patch.object(solver, "solve", side_effect=RuntimeError("test error")):
140144
# solver api should handle exceptions gracefully

chia/farmer/farmer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def __init__(
144144
self.quality_str_to_identifiers: dict[bytes32, tuple[str, bytes32, bytes32, bytes32]] = {}
145145

146146
# Track pending solver requests, keyed by quality string hex
147-
self.pending_solver_requests: dict[bytes32, dict[str, Any]] = {}
147+
self.pending_solver_requests: dict[bytes, dict[str, Any]] = {}
148148

149149
# number of responses to each signage point
150150
self.number_of_responses: dict[bytes32, int] = {}

chia/farmer/farmer_api.py

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -499,35 +499,34 @@ async def v2_qualities(self, quality_data: V2Qualities, peer: WSChiaConnection)
499499
self.farmer.cache_add_time[quality_data.sp_hash] = uint64(int(time.time()))
500500

501501
self.farmer.log.info(
502-
f"Received V2 quality collection with {len(quality_data.qualities)} qualities "
502+
f"Received V2 quality collection with {len(quality_data.quality_chains)} quality chains "
503503
f"for plot {quality_data.plot_identifier[:10]}... from {peer.peer_node_id}"
504504
)
505505

506-
# Process each quality through solver service to get full proofs
507-
for quality in quality_data.qualities:
506+
# Process each quality chain through solver service to get full proofs
507+
for quality_chain in quality_data.quality_chains:
508508
solver_info = SolverInfo(
509-
plot_size=quality_data.plot_size,
510509
plot_difficulty=quality_data.difficulty,
511-
quality_string=quality,
510+
quality_chain=quality_chain,
512511
)
513512

514513
try:
515514
# store pending request data for matching with response
516-
self.farmer.pending_solver_requests[quality] = {
515+
self.farmer.pending_solver_requests[quality_chain] = {
517516
"quality_data": quality_data,
518517
"peer": peer,
519518
}
520519

521520
# send solve request to all solver connections
522521
msg = make_msg(ProtocolMessageTypes.solve, solver_info)
523522
await self.farmer.server.send_to_all([msg], NodeType.SOLVER)
524-
self.farmer.log.debug(f"Sent solve request for quality {quality.hex()[:10]}...")
523+
self.farmer.log.debug(f"Sent solve request for quality {quality_chain.hex()[:10]}...")
525524

526525
except Exception as e:
527-
self.farmer.log.error(f"Failed to call solver service for quality {quality.hex()[:10]}...: {e}")
526+
self.farmer.log.error(f"Failed to call solver service for quality {quality_chain.hex()[:10]}...: {e}")
528527
# clean up pending request
529-
if quality in self.farmer.pending_solver_requests:
530-
del self.farmer.pending_solver_requests[quality]
528+
if quality_chain in self.farmer.pending_solver_requests:
529+
del self.farmer.pending_solver_requests[quality_chain]
531530

532531
@metadata.request()
533532
async def solution_response(self, response: SolverResponse, peer: WSChiaConnection) -> None:
@@ -539,20 +538,20 @@ async def solution_response(self, response: SolverResponse, peer: WSChiaConnecti
539538

540539
# find the matching pending request using quality_string
541540

542-
if response.quality_string not in self.farmer.pending_solver_requests:
543-
self.farmer.log.warning(f"Received solver response for unknown quality {response.quality_string}")
541+
if response.quality_chain not in self.farmer.pending_solver_requests:
542+
self.farmer.log.warning(f"Received solver response for unknown quality {response.quality_chain.hex()}")
544543
return
545544

546545
# get the original request data
547-
request_data = self.farmer.pending_solver_requests.pop(response.quality_string)
546+
request_data = self.farmer.pending_solver_requests.pop(response.quality_chain)
548547
quality_data = request_data["quality_data"]
549548
original_peer = request_data["peer"]
550-
quality = response.quality_string
549+
quality = response.quality_chain
551550

552551
# create the proof of space with the solver's proof
553552
proof_bytes = response.proof
554553
if proof_bytes is None or len(proof_bytes) == 0:
555-
self.farmer.log.warning(f"Received empty proof from solver for quality {quality.hex()[:10]}...")
554+
self.farmer.log.warning(f"Received empty proof from solver for quality {quality.hex()}...")
556555
return
557556

558557
sp_challenge_hash = quality_data.challenge_hash
@@ -577,6 +576,14 @@ async def solution_response(self, response: SolverResponse, peer: WSChiaConnecti
577576
# process the proof of space
578577
await self.new_proof_of_space(new_proof_of_space, original_peer)
579578

579+
def _derive_quality_string_from_chain(self, quality_chain: bytes) -> bytes32:
580+
"""Derive 32-byte quality string from quality chain (16 * k bits blob)."""
581+
# TODO: todo_v2_plots implement actual quality string derivation algorithm
582+
# For now, hash the quality chain to get a 32-byte result
583+
from chia.util.hash import std_hash
584+
585+
return std_hash(quality_chain)
586+
580587
@metadata.request()
581588
async def respond_signatures(self, response: harvester_protocol.RespondSignatures) -> None:
582589
request = self._process_respond_signatures(response)

chia/harvester/harvester_api.py

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -107,47 +107,29 @@ def blocking_lookup_v2_qualities(filename: Path, plot_info: PlotInfo) -> Optiona
107107
new_challenge.sp_hash,
108108
)
109109
try:
110-
quality_strings = plot_info.prover.get_qualities_for_challenge(sp_challenge_hash)
110+
quality_chains = plot_info.prover.get_quality_chains_for_challenge(sp_challenge_hash)
111111
except Exception as e:
112-
self.harvester.log.error(f"Exception fetching qualities for V2 plot {filename}. {e}")
112+
self.harvester.log.error(f"Exception fetching quality chains for V2 plot {filename}. {e}")
113113
return None
114114

115-
if quality_strings is not None and len(quality_strings) > 0:
115+
if quality_chains is not None and len(quality_chains) > 0:
116116
# Get the appropriate difficulty for this plot
117117
difficulty = new_challenge.difficulty
118-
sub_slot_iters = new_challenge.sub_slot_iters
119118
if plot_info.pool_contract_puzzle_hash is not None:
120119
# Check for pool-specific difficulty
121120
for pool_difficulty in new_challenge.pool_difficulties:
122121
if pool_difficulty.pool_contract_puzzle_hash == plot_info.pool_contract_puzzle_hash:
123122
difficulty = pool_difficulty.difficulty
124-
sub_slot_iters = pool_difficulty.sub_slot_iters
125123
break
126124

127-
# Filter qualities that pass the required_iters check (same as V1 flow)
128-
good_qualities = []
129-
sp_interval_iters = calculate_sp_interval_iters(self.harvester.constants, sub_slot_iters)
130-
131-
for quality_str in quality_strings:
132-
required_iters: uint64 = calculate_iterations_quality(
133-
self.harvester.constants,
134-
quality_str,
135-
PlotSize.make_v1(plot_info.prover.get_size()), # TODO: todo_v2_plots update for V2
136-
difficulty,
137-
new_challenge.sp_hash,
138-
sub_slot_iters,
139-
new_challenge.last_tx_height,
140-
)
141-
142-
if required_iters < sp_interval_iters:
143-
good_qualities.append(quality_str)
125+
# Filter quality chains that pass the required_iters check
144126

145-
if len(good_qualities) > 0:
127+
if len(quality_chains) > 0:
146128
return V2Qualities(
147129
new_challenge.challenge_hash,
148130
new_challenge.sp_hash,
149-
good_qualities[0].hex() + str(filename.resolve()),
150-
good_qualities,
131+
quality_chains[0].hex() + str(filename.resolve()),
132+
quality_chains,
151133
new_challenge.signage_point_index,
152134
plot_info.prover.get_size(),
153135
difficulty,
@@ -375,7 +357,7 @@ async def lookup_challenge(
375357
f" to minimize risk of losing rewards."
376358
)
377359
if v2_qualities is not None:
378-
total_v2_qualities_found += len(v2_qualities.qualities)
360+
total_v2_qualities_found += len(v2_qualities.quality_chains)
379361
msg = make_msg(ProtocolMessageTypes.v2_qualities, v2_qualities)
380362
await peer.send_message(msg)
381363

@@ -411,6 +393,14 @@ async def lookup_challenge(
411393
},
412394
)
413395

396+
# def _derive_quality_string_from_chain(self, quality_chain: bytes) -> bytes32:
397+
# """Derive 32-byte quality string from quality chain (16 * k bits blob)."""
398+
# # TODO: todo_v2_plots implement actual quality string derivation algorithm
399+
# # For now, hash the quality chain to get a 32-byte result
400+
# from chia.util.hash import std_hash
401+
402+
# return std_hash(quality_chain)
403+
414404
@metadata.request(reply_types=[ProtocolMessageTypes.respond_signatures])
415405
async def request_signatures(self, request: harvester_protocol.RequestSignatures) -> Optional[Message]:
416406
"""

chia/plotting/prover.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def get_version(self) -> PlotVersion: ...
2727
def __bytes__(self) -> bytes: ...
2828
def get_id(self) -> bytes32: ...
2929
def get_qualities_for_challenge(self, challenge: bytes32) -> list[bytes32]: ...
30+
def get_quality_chains_for_challenge(self, challenge: bytes) -> list[bytes]: ...
3031
def get_full_proof(self, challenge: bytes, index: int, parallel_read: bool = True) -> bytes: ...
3132

3233
@classmethod
@@ -72,6 +73,10 @@ def get_qualities_for_challenge(self, challenge: bytes) -> list[bytes32]:
7273
# TODO: todo_v2_plots Implement plot quality lookup
7374
raise NotImplementedError("V2 plot format is not yet implemented")
7475

76+
def get_quality_chains_for_challenge(self, challenge: bytes) -> list[bytes]:
77+
# TODO: todo_v2_plots Implement quality chain lookup (16 * k bits blobs)
78+
raise NotImplementedError("V2 plot format is not yet implemented")
79+
7580
def get_full_proof(self, challenge: bytes, index: int, parallel_read: bool = True) -> bytes:
7681
# TODO: todo_v2_plots Implement plot proof generation
7782
raise NotImplementedError("V2 plot format require solver to get full proof")
@@ -115,6 +120,9 @@ def get_id(self) -> bytes32:
115120
def get_qualities_for_challenge(self, challenge: bytes32) -> list[bytes32]:
116121
return [bytes32(quality) for quality in self._disk_prover.get_qualities_for_challenge(challenge)]
117122

123+
def get_quality_chains_for_challenge(self, challenge: bytes) -> list[bytes]:
124+
raise NotImplementedError("V1 does not implement quality chains, only qualities")
125+
118126
def get_full_proof(self, challenge: bytes, index: int, parallel_read: bool = True) -> bytes:
119127
return bytes(self._disk_prover.get_full_proof(challenge, index, parallel_read))
120128

chia/protocols/harvester_protocol.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class V2Qualities(Streamable):
6969
challenge_hash: bytes32
7070
sp_hash: bytes32
7171
plot_identifier: str
72-
qualities: list[bytes32]
72+
quality_chains: list[bytes] # 16 * k bits blobs instead of 32-byte quality strings
7373
signage_point_index: uint8
7474
plot_size: uint8
7575
difficulty: uint64

chia/protocols/shared_protocol.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
NodeType.INTRODUCER: "0.0.36",
1818
NodeType.WALLET: "0.0.38",
1919
NodeType.DATA_LAYER: "0.0.36",
20+
NodeType.SOLVER: "0.0.37",
2021
}
2122

2223
"""
@@ -65,7 +66,7 @@ class Capability(IntEnum):
6566
NodeType.INTRODUCER: _capabilities,
6667
NodeType.WALLET: _capabilities,
6768
NodeType.DATA_LAYER: _capabilities,
68-
NodeType.SOLVER: [],
69+
NodeType.SOLVER: _capabilities,
6970
}
7071

7172

chia/protocols/solver_protocol.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,20 @@
22

33
from dataclasses import dataclass
44

5-
from chia_rs.sized_bytes import bytes32
6-
from chia_rs.sized_ints import uint8, uint64
5+
from chia_rs.sized_ints import uint64
76

87
from chia.util.streamable import Streamable, streamable
98

109

1110
@streamable
1211
@dataclass(frozen=True)
1312
class SolverInfo(Streamable):
14-
plot_size: uint8
1513
plot_difficulty: uint64
16-
quality_string: bytes32
14+
quality_chain: bytes # 16 * k bits blob, k (plot size) can be derived from this
1715

1816

1917
@streamable
2018
@dataclass(frozen=True)
2119
class SolverResponse(Streamable):
22-
quality_string: bytes32
20+
quality_chain: bytes
2321
proof: bytes

chia/solver/solver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ async def manage(self) -> AsyncIterator[None]:
6868
self.log.info("Solver service shutdown complete")
6969

7070
def solve(self, info: SolverInfo) -> Optional[bytes]:
71-
self.log.debug(f"Solve request: quality={info.quality_string.hex()}")
71+
self.log.debug(f"Solve request: quality={info.quality_chain.hex()}")
7272
# todo implement actualy calling the solver
7373
return None
7474

0 commit comments

Comments
 (0)