Skip to content

Commit 63fb5f2

Browse files
committed
fix service tests, cleanup
1 parent 93e1d00 commit 63fb5f2

File tree

6 files changed

+52
-158
lines changed

6 files changed

+52
-158
lines changed

chia/_tests/farmer_harvester/test_farmer_harvester.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,6 @@ async def test_farmer_respond_signatures(
167167
# messages even though it didn't request them, to cover when the farmer doesn't know
168168
# about an sp_hash, so it fails at the sp record check.
169169

170-
def log_is_ready() -> bool:
171-
return len(caplog.text) > 0
172-
173170
_, _, harvester_service, _, _ = harvester_farmer_environment
174171
# We won't have an sp record for this one
175172
challenge_hash = bytes32(b"1" * 32)
@@ -184,11 +181,16 @@ def log_is_ready() -> bool:
184181
include_source_signature_data=False,
185182
farmer_reward_address_override=None,
186183
)
184+
185+
expected_error = f"Do not have challenge hash {challenge_hash}"
186+
187+
def expected_log_is_ready() -> bool:
188+
return expected_error in caplog.text
189+
187190
msg = make_msg(ProtocolMessageTypes.respond_signatures, response)
188191
await harvester_service._node.server.send_to_all([msg], NodeType.FARMER)
189-
await time_out_assert(15, log_is_ready)
190-
# We fail the sps record check
191-
expected_error = f"Do not have challenge hash {challenge_hash}"
192+
await time_out_assert(10, expected_log_is_ready)
193+
# We should find the error message
192194
assert expected_error in caplog.text
193195

194196

Lines changed: 35 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,56 @@
11
from __future__ import annotations
22

33
from pathlib import Path
4-
from typing import Optional
54
from unittest.mock import patch
65

76
import pytest
8-
from chia_rs import ConsensusConstants, FullBlock
9-
from chia_rs.sized_bytes import bytes32
7+
from chia_rs import ConsensusConstants
108
from chia_rs.sized_ints import uint64
119

12-
from chia._tests.blockchain.blockchain_test_utils import _validate_and_add_block
13-
from chia.consensus.blockchain import Blockchain
14-
from chia.consensus.get_block_challenge import get_block_challenge
15-
from chia.consensus.pot_iterations import is_overflow_block
10+
from chia.protocols.outbound_message import Message
1611
from chia.protocols.solver_protocol import SolverInfo
1712
from chia.simulator.block_tools import create_block_tools_async
13+
from chia.simulator.keyring import TempKeyring
1814
from chia.simulator.setup_services import setup_solver
1915
from chia.solver.solver_rpc_client import SolverRpcClient
20-
from chia.types.blockchain_format.proof_of_space import verify_and_get_quality_string
2116

2217

2318
@pytest.mark.anyio
2419
async def test_solver_api_methods(blockchain_constants: ConsensusConstants, tmp_path: Path) -> None:
25-
bt = await create_block_tools_async(constants=blockchain_constants)
26-
async with setup_solver(tmp_path, bt, blockchain_constants) as solver_service:
27-
solver = solver_service._node
28-
solver_api = solver_service._api
29-
assert solver_api.ready() is True
30-
31-
# test solve with real SolverInfo
32-
test_info = SolverInfo(plot_difficulty=uint64(1500), quality_chain=b"test_quality_chain_42")
33-
34-
# test normal solve operation (stub returns None)
35-
result = solver.solve(test_info)
36-
assert result is None
37-
38-
# test with mocked return value to verify full flow
39-
expected_proof = b"test_proof_data_12345"
40-
with patch.object(solver, "solve", return_value=expected_proof):
41-
api_result = await solver_api.solve(test_info)
42-
assert api_result is not None
43-
# api returns protocol message for peer communication
44-
from chia.protocols.outbound_message import Message
45-
46-
assert isinstance(api_result, Message)
47-
48-
# test error handling - solver not started
49-
original_started = solver.started
50-
solver.started = False
51-
api_result = await solver_api.solve(test_info)
52-
assert api_result is None
53-
solver.started = original_started
20+
with TempKeyring(populate=True) as keychain:
21+
bt = await create_block_tools_async(constants=blockchain_constants, keychain=keychain)
22+
async with setup_solver(tmp_path, bt, blockchain_constants) as solver_service:
23+
solver = solver_service._node
24+
solver_api = solver_service._api
25+
assert solver_api.ready() is True
26+
test_info = SolverInfo(plot_difficulty=uint64(1500), quality_chain=b"test_quality_chain_42")
27+
expected_proof = b"test_proof_data_12345"
28+
with patch.object(solver, "solve", return_value=expected_proof):
29+
api_result = await solver_api.solve(test_info)
30+
assert api_result is not None
31+
assert isinstance(api_result, Message)
5432

5533

5634
@pytest.mark.anyio
57-
async def test_solver_with_real_blocks_and_signage_points(
58-
blockchain_constants: ConsensusConstants,
59-
default_400_blocks: list[FullBlock],
60-
empty_blockchain: Blockchain,
61-
self_hostname: str,
62-
tmp_path: Path,
63-
) -> None:
64-
blockchain = empty_blockchain
65-
blocks = default_400_blocks[:3]
66-
for block in blocks:
67-
await _validate_and_add_block(empty_blockchain, block)
68-
block = blocks[-1] # always use the last block
69-
overflow = is_overflow_block(blockchain_constants, block.reward_chain_block.signage_point_index)
70-
challenge = get_block_challenge(blockchain_constants, block, blockchain, False, overflow, False)
71-
assert block.reward_chain_block.pos_ss_cc_challenge_hash == challenge
72-
if block.reward_chain_block.challenge_chain_sp_vdf is None:
73-
challenge_chain_sp: bytes32 = challenge
74-
else:
75-
challenge_chain_sp = block.reward_chain_block.challenge_chain_sp_vdf.output.get_hash()
76-
# extract real quality data from blocks using chia's proof of space verification
77-
pos = block.reward_chain_block.proof_of_space
78-
# calculate real quality string from proof of space data
79-
quality_string: Optional[bytes32] = verify_and_get_quality_string(
80-
block.reward_chain_block.proof_of_space,
81-
blockchain_constants,
82-
challenge,
83-
challenge_chain_sp,
84-
height=block.reward_chain_block.height,
85-
)
86-
87-
assert quality_string is not None
88-
quality_hex = quality_string.hex()
89-
90-
# test solver with real blockchain quality
91-
plot_size = pos.size()
92-
k_size = plot_size.size_v1 if plot_size.size_v1 is not None else plot_size.size_v2
93-
assert k_size is not None
94-
bt = await create_block_tools_async(constants=blockchain_constants)
95-
async with setup_solver(tmp_path, bt, blockchain_constants) as solver_service:
96-
assert solver_service.rpc_server is not None
97-
solver_rpc_client = await SolverRpcClient.create(
98-
self_hostname, solver_service.rpc_server.listen_port, solver_service.root_path, solver_service.config
99-
)
100-
solve_response = await solver_rpc_client.solve(quality_hex, int(k_size), 1000)
101-
assert solve_response["success"] is True
102-
assert "proof" in solve_response
103-
# stub implementation returns None, real implementation would return actual proof
104-
assert solve_response["proof"] is None
105-
106-
107-
@pytest.mark.anyio
108-
async def test_solver_error_handling_and_edge_cases(
35+
async def test_solver_error_handling(
10936
blockchain_constants: ConsensusConstants, self_hostname: str, tmp_path: Path
11037
) -> None:
111-
bt = await create_block_tools_async(constants=blockchain_constants)
112-
async with setup_solver(tmp_path, bt, blockchain_constants) as solver_service:
113-
assert solver_service.rpc_server is not None
114-
solver_rpc_client = await SolverRpcClient.create(
115-
self_hostname, solver_service.rpc_server.listen_port, solver_service.root_path, solver_service.config
116-
)
117-
118-
# test invalid quality string format
119-
try:
120-
await solver_rpc_client.solve("invalid_hex")
121-
assert False, "should have raised exception for invalid hex"
122-
except Exception:
123-
pass # expected
124-
125-
# test edge case parameters
126-
valid_quality = "1234567890abcdef" * 4
127-
128-
# test with edge case plot sizes and difficulties
129-
edge_cases = [
130-
(18, 1), # minimum plot size, minimum difficulty
131-
(50, 999999), # large plot size, high difficulty
132-
]
133-
134-
for plot_size, difficulty in edge_cases:
135-
response = await solver_rpc_client.solve(valid_quality, plot_size, difficulty)
136-
assert response["success"] is True
137-
assert "proof" in response
138-
139-
# test solver handles exception in solve method
140-
solver = solver_service._node
141-
test_info = SolverInfo(plot_difficulty=uint64(1000), quality_chain=b"test_quality_chain_zeros")
142-
143-
with patch.object(solver, "solve", side_effect=RuntimeError("test error")):
144-
# solver api should handle exceptions gracefully
145-
result = await solver_service._api.solve(test_info)
146-
assert result is None # api returns None on error
38+
with TempKeyring(populate=True) as keychain:
39+
bt = await create_block_tools_async(constants=blockchain_constants, keychain=keychain)
40+
async with setup_solver(tmp_path, bt, blockchain_constants) as solver_service:
41+
assert solver_service.rpc_server is not None
42+
solver_rpc_client = await SolverRpcClient.create(
43+
self_hostname, solver_service.rpc_server.listen_port, solver_service.root_path, solver_service.config
44+
)
45+
try:
46+
await solver_rpc_client.solve("invalid_hex")
47+
assert False, "should have raised exception for invalid hex"
48+
except Exception:
49+
pass # expected
50+
# test solver handles exception in solve method
51+
solver = solver_service._node
52+
test_info = SolverInfo(plot_difficulty=uint64(1000), quality_chain=b"test_quality_chain_zeros")
53+
with patch.object(solver, "solve", side_effect=RuntimeError("test error")):
54+
# solver api should handle exceptions gracefully
55+
result = await solver_service._api.solve(test_info)
56+
assert result is None # api returns None on error

chia/farmer/farmer_api.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ async def new_proof_of_space(
7575
"""
7676
if new_proof_of_space.sp_hash not in self.farmer.number_of_responses:
7777
self.farmer.number_of_responses[new_proof_of_space.sp_hash] = 0
78-
self.farmer.cache_add_time[new_proof_of_space.sp_hash] = uint64(int(time.time()))
78+
self.farmer.cache_add_time[new_proof_of_space.sp_hash] = uint64(time.time())
7979

8080
max_pos_per_sp = 5
8181

@@ -172,14 +172,14 @@ async def new_proof_of_space(
172172
new_proof_of_space.proof,
173173
)
174174
)
175-
self.farmer.cache_add_time[new_proof_of_space.sp_hash] = uint64(int(time.time()))
175+
self.farmer.cache_add_time[new_proof_of_space.sp_hash] = uint64(time.time())
176176
self.farmer.quality_str_to_identifiers[computed_quality_string] = (
177177
new_proof_of_space.plot_identifier,
178178
new_proof_of_space.challenge_hash,
179179
new_proof_of_space.sp_hash,
180180
peer.peer_node_id,
181181
)
182-
self.farmer.cache_add_time[computed_quality_string] = uint64(int(time.time()))
182+
self.farmer.cache_add_time[computed_quality_string] = uint64(time.time())
183183

184184
await peer.send_message(make_msg(ProtocolMessageTypes.request_signatures, request))
185185

@@ -488,15 +488,15 @@ async def v2_qualities(self, quality_data: V2Qualities, peer: WSChiaConnection)
488488
"""
489489
if quality_data.sp_hash not in self.farmer.number_of_responses:
490490
self.farmer.number_of_responses[quality_data.sp_hash] = 0
491-
self.farmer.cache_add_time[quality_data.sp_hash] = uint64(int(time.time()))
491+
self.farmer.cache_add_time[quality_data.sp_hash] = uint64(time.time())
492492

493493
if quality_data.sp_hash not in self.farmer.sps:
494494
self.farmer.log.warning(
495495
f"Received V2 quality collection for a signage point that we do not have {quality_data.sp_hash}"
496496
)
497497
return None
498498

499-
self.farmer.cache_add_time[quality_data.sp_hash] = uint64(int(time.time()))
499+
self.farmer.cache_add_time[quality_data.sp_hash] = uint64(time.time())
500500

501501
self.farmer.log.info(
502502
f"Received V2 quality collection with {len(quality_data.quality_chains)} quality chains "
@@ -576,14 +576,6 @@ async def solution_response(self, response: SolverResponse, peer: WSChiaConnecti
576576
# process the proof of space
577577
await self.new_proof_of_space(new_proof_of_space, original_peer)
578578

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-
587579
@metadata.request()
588580
async def respond_signatures(self, response: harvester_protocol.RespondSignatures) -> None:
589581
request = self._process_respond_signatures(response)
@@ -664,7 +656,7 @@ async def new_signage_point(self, new_signage_point: farmer_protocol.NewSignageP
664656

665657
pool_dict[key] = strip_old_entries(pairs=pool_dict[key], before=cutoff_24h)
666658

667-
now = uint64(int(time.time()))
659+
now = uint64(time.time())
668660
self.farmer.cache_add_time[new_signage_point.challenge_chain_sp] = now
669661
missing_signage_points = self.farmer.check_missing_signage_points(now, new_signage_point)
670662
self.farmer.state_changed(

chia/harvester/harvester_api.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ async def lookup_challenge(
363363
msg = make_msg(ProtocolMessageTypes.v2_qualities, v2_qualities)
364364
await peer.send_message(msg)
365365

366-
now = uint64(int(time.time()))
366+
now = uint64(time.time())
367367

368368
farming_info = FarmingInfo(
369369
new_challenge.challenge_hash,
@@ -395,14 +395,6 @@ async def lookup_challenge(
395395
},
396396
)
397397

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

chia/solver/solver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ async def manage(self) -> AsyncIterator[None]:
6969

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

7575
def get_connections(self, request_node_type: Optional[NodeType]) -> list[dict[str, Any]]:

chia/solver/solver_rpc_client.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,4 @@ async def get_state(self) -> dict[str, Any]:
2020
async def solve(self, quality_string: str, plot_size: int = 32, plot_difficulty: int = 1000) -> dict[str, Any]:
2121
"""Solve a quality string with optional plot size and difficulty."""
2222
quality = bytes32.from_hexstr(quality_string)
23-
return await self.fetch(
24-
"solve", {"quality_string": quality.hex(), "plot_size": plot_size, "plot_difficulty": plot_difficulty}
25-
)
23+
return await self.fetch("solve", {"quality_chain": quality.hex(), "plot_difficulty": plot_difficulty})

0 commit comments

Comments
 (0)