server: Introduce ApiProtocol (#15466)

* server: Introduce `ApiProtocol`

* genericize (#5)

* `ApiProtocol.api_ready` -> `ApiProtocol.ready()`

* Add `ApiProtocol.log` and give APIs separate loggers

* Fix `CrawlerAPI`

* Drop some unrelated removals

* Fix some of the generic hinting

* Revert some changes in `timelord_api.py`

* Fix `CawlerAPI` readiness

* Fix hinting

* Get some `CrawlerAPI` coverage

---------

Co-authored-by: Kyle Altendorf <sda@fstab.net>
This commit is contained in:
dustinface 2023-06-14 03:12:25 +02:00 committed by GitHub
parent 46a244ae09
commit 49140b2b3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 288 additions and 112 deletions

View File

@ -7,9 +7,11 @@ from chia.server.server import ChiaServer
class DataLayerAPI: class DataLayerAPI:
log: logging.Logger
data_layer: DataLayer data_layer: DataLayer
def __init__(self, data_layer: DataLayer) -> None: def __init__(self, data_layer: DataLayer) -> None:
self.log = logging.getLogger(__name__)
self.data_layer = data_layer self.data_layer = data_layer
# def _set_state_changed_callback(self, callback: StateChangedProtocol) -> None: # def _set_state_changed_callback(self, callback: StateChangedProtocol) -> None:
@ -19,10 +21,5 @@ class DataLayerAPI:
def server(self) -> ChiaServer: def server(self) -> ChiaServer:
return self.data_layer.server return self.data_layer.server
@property def ready(self) -> bool:
def log(self) -> logging.Logger:
return self.data_layer.log
@property
def api_ready(self) -> bool:
return self.data_layer.initialized return self.data_layer.initialized

View File

@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import json import json
import logging
import time import time
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple
@ -53,11 +54,16 @@ def strip_old_entries(pairs: List[Tuple[float, Any]], before: float) -> List[Tup
class FarmerAPI: class FarmerAPI:
log: logging.Logger
farmer: Farmer farmer: Farmer
def __init__(self, farmer: Farmer) -> None: def __init__(self, farmer: Farmer) -> None:
self.log = logging.getLogger(__name__)
self.farmer = farmer self.farmer = farmer
def ready(self) -> bool:
return self.farmer.started
@api_request(peer_required=True) @api_request(peer_required=True)
async def new_proof_of_space( async def new_proof_of_space(
self, new_proof_of_space: harvester_protocol.NewProofOfSpace, peer: WSChiaConnection self, new_proof_of_space: harvester_protocol.NewProofOfSpace, peer: WSChiaConnection

View File

@ -69,10 +69,12 @@ else:
class FullNodeAPI: class FullNodeAPI:
log: logging.Logger
full_node: FullNode full_node: FullNode
executor: ThreadPoolExecutor executor: ThreadPoolExecutor
def __init__(self, full_node: FullNode) -> None: def __init__(self, full_node: FullNode) -> None:
self.log = logging.getLogger(__name__)
self.full_node = full_node self.full_node = full_node
self.executor = ThreadPoolExecutor(max_workers=1) self.executor = ThreadPoolExecutor(max_workers=1)
@ -81,12 +83,7 @@ class FullNodeAPI:
assert self.full_node.server is not None assert self.full_node.server is not None
return self.full_node.server return self.full_node.server
@property def ready(self) -> bool:
def log(self) -> logging.Logger:
return self.full_node.log
@property
def api_ready(self) -> bool:
return self.full_node.initialized return self.full_node.initialized
@api_request(peer_required=True, reply_types=[ProtocolMessageTypes.respond_peers]) @api_request(peer_required=True, reply_types=[ProtocolMessageTypes.respond_peers])

View File

@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
import logging
import time import time
from pathlib import Path from pathlib import Path
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
@ -29,11 +30,16 @@ from chia.wallet.derive_keys import master_sk_to_local_sk
class HarvesterAPI: class HarvesterAPI:
log: logging.Logger
harvester: Harvester harvester: Harvester
def __init__(self, harvester: Harvester): def __init__(self, harvester: Harvester):
self.log = logging.getLogger(__name__)
self.harvester = harvester self.harvester = harvester
def ready(self) -> bool:
return True
@api_request(peer_required=True) @api_request(peer_required=True)
async def harvester_handshake( async def harvester_handshake(
self, harvester_handshake: harvester_protocol.HarvesterHandshake, peer: WSChiaConnection self, harvester_handshake: harvester_protocol.HarvesterHandshake, peer: WSChiaConnection

View File

@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import logging
from typing import Optional from typing import Optional
from chia.introducer.introducer import Introducer from chia.introducer.introducer import Introducer
@ -14,11 +15,16 @@ from chia.util.ints import uint64
class IntroducerAPI: class IntroducerAPI:
log: logging.Logger
introducer: Introducer introducer: Introducer
def __init__(self, introducer) -> None: def __init__(self, introducer) -> None:
self.log = logging.getLogger(__name__)
self.introducer = introducer self.introducer = introducer
def ready(self) -> bool:
return True
def _set_state_changed_callback(self, callback: StateChangedProtocol) -> None: def _set_state_changed_callback(self, callback: StateChangedProtocol) -> None:
pass pass

View File

@ -12,9 +12,11 @@ from chia.util.api_decorators import api_request
class CrawlerAPI: class CrawlerAPI:
log: logging.Logger
crawler: Crawler crawler: Crawler
def __init__(self, crawler: Crawler) -> None: def __init__(self, crawler: Crawler) -> None:
self.log = logging.getLogger(__name__)
self.crawler = crawler self.crawler = crawler
@property @property
@ -22,9 +24,8 @@ class CrawlerAPI:
assert self.crawler.server is not None assert self.crawler.server is not None
return self.crawler.server return self.crawler.server
@property def ready(self) -> bool:
def log(self) -> logging.Logger: return True
return self.crawler.log
@api_request(peer_required=True) @api_request(peer_required=True)
async def request_peers( async def request_peers(

View File

@ -29,7 +29,7 @@ def create_full_node_crawler_service(
config: Dict, config: Dict,
consensus_constants: ConsensusConstants, consensus_constants: ConsensusConstants,
connect_to_daemon: bool = True, connect_to_daemon: bool = True,
) -> Service[Crawler]: ) -> Service[Crawler, CrawlerAPI]:
service_config = config[SERVICE_NAME] service_config = config[SERVICE_NAME]
crawler = Crawler( crawler = Crawler(

View File

@ -0,0 +1,12 @@
from __future__ import annotations
from logging import Logger
from typing_extensions import Protocol
class ApiProtocol(Protocol):
log: Logger
def ready(self) -> bool:
...

View File

@ -28,6 +28,7 @@ from chia.protocols.protocol_message_types import ProtocolMessageTypes
from chia.protocols.protocol_state_machine import message_requires_reply from chia.protocols.protocol_state_machine import message_requires_reply
from chia.protocols.protocol_timing import INVALID_PROTOCOL_BAN_SECONDS from chia.protocols.protocol_timing import INVALID_PROTOCOL_BAN_SECONDS
from chia.protocols.shared_protocol import protocol_version from chia.protocols.shared_protocol import protocol_version
from chia.server.api_protocol import ApiProtocol
from chia.server.introducer_peers import IntroducerPeers from chia.server.introducer_peers import IntroducerPeers
from chia.server.outbound_message import Message, NodeType from chia.server.outbound_message import Message, NodeType
from chia.server.ssl_context import private_ssl_paths, public_ssl_paths from chia.server.ssl_context import private_ssl_paths, public_ssl_paths
@ -122,7 +123,7 @@ class ChiaServer:
_network_id: str _network_id: str
_inbound_rate_limit_percent: int _inbound_rate_limit_percent: int
_outbound_rate_limit_percent: int _outbound_rate_limit_percent: int
api: Any api: ApiProtocol
node: Any node: Any
root_path: Path root_path: Path
config: Dict[str, Any] config: Dict[str, Any]
@ -147,7 +148,7 @@ class ChiaServer:
cls, cls,
port: int, port: int,
node: Any, node: Any,
api: Any, api: ApiProtocol,
local_type: NodeType, local_type: NodeType,
ping_interval: int, ping_interval: int,
network_id: str, network_id: str,

View File

@ -17,6 +17,7 @@ from chia.util.config import load_config, load_config_cli
from chia.util.default_root import DEFAULT_ROOT_PATH from chia.util.default_root import DEFAULT_ROOT_PATH
from chia.util.ints import uint16 from chia.util.ints import uint16
from chia.wallet.wallet_node import WalletNode from chia.wallet.wallet_node import WalletNode
from chia.wallet.wallet_node_api import WalletNodeAPI
# See: https://bugs.python.org/issue29288 # See: https://bugs.python.org/issue29288
"".encode("idna") "".encode("idna")
@ -31,9 +32,9 @@ def create_data_layer_service(
config: Dict[str, Any], config: Dict[str, Any],
downloaders: List[str], downloaders: List[str],
uploaders: List[str], # dont add FilesystemUploader to this, it is the default uploader uploaders: List[str], # dont add FilesystemUploader to this, it is the default uploader
wallet_service: Optional[Service[WalletNode]] = None, wallet_service: Optional[Service[WalletNode, WalletNodeAPI]] = None,
connect_to_daemon: bool = True, connect_to_daemon: bool = True,
) -> Service[DataLayer]: ) -> Service[DataLayer, DataLayerAPI]:
if uploaders is None: if uploaders is None:
uploaders = [] uploaders = []
if downloaders is None: if downloaders is None:

View File

@ -30,7 +30,7 @@ def create_farmer_service(
consensus_constants: ConsensusConstants, consensus_constants: ConsensusConstants,
keychain: Optional[Keychain] = None, keychain: Optional[Keychain] = None,
connect_to_daemon: bool = True, connect_to_daemon: bool = True,
) -> Service[Farmer]: ) -> Service[Farmer, FarmerAPI]:
service_config = config[SERVICE_NAME] service_config = config[SERVICE_NAME]
fnp = service_config.get("full_node_peer") fnp = service_config.get("full_node_peer")

View File

@ -33,7 +33,7 @@ def create_full_node_service(
consensus_constants: ConsensusConstants, consensus_constants: ConsensusConstants,
connect_to_daemon: bool = True, connect_to_daemon: bool = True,
override_capabilities: Optional[List[Tuple[uint16, str]]] = None, override_capabilities: Optional[List[Tuple[uint16, str]]] = None,
) -> Service[FullNode]: ) -> Service[FullNode, FullNodeAPI]:
service_config = config[SERVICE_NAME] service_config = config[SERVICE_NAME]
full_node = FullNode( full_node = FullNode(

View File

@ -28,7 +28,7 @@ def create_harvester_service(
consensus_constants: ConsensusConstants, consensus_constants: ConsensusConstants,
farmer_peer: Optional[UnresolvedPeerInfo], farmer_peer: Optional[UnresolvedPeerInfo],
connect_to_daemon: bool = True, connect_to_daemon: bool = True,
) -> Service[Harvester]: ) -> Service[Harvester, HarvesterAPI]:
service_config = config[SERVICE_NAME] service_config = config[SERVICE_NAME]
overrides = service_config["network_overrides"]["constants"][service_config["selected_network"]] overrides = service_config["network_overrides"]["constants"][service_config["selected_network"]]

View File

@ -23,7 +23,7 @@ def create_introducer_service(
config: Dict[str, Any], config: Dict[str, Any],
advertised_port: Optional[int] = None, advertised_port: Optional[int] = None,
connect_to_daemon: bool = True, connect_to_daemon: bool = True,
) -> Service[Introducer]: ) -> Service[Introducer, IntroducerAPI]:
service_config = config[SERVICE_NAME] service_config = config[SERVICE_NAME]
if advertised_port is None: if advertised_port is None:

View File

@ -14,6 +14,7 @@ from typing import Any, Awaitable, Callable, Coroutine, Dict, Generic, List, Opt
from chia.cmds.init_funcs import chia_full_version_str from chia.cmds.init_funcs import chia_full_version_str
from chia.daemon.server import service_launch_lock_path from chia.daemon.server import service_launch_lock_path
from chia.rpc.rpc_server import RpcApiProtocol, RpcServer, RpcServiceProtocol, start_rpc_server from chia.rpc.rpc_server import RpcApiProtocol, RpcServer, RpcServiceProtocol, start_rpc_server
from chia.server.api_protocol import ApiProtocol
from chia.server.chia_policy import set_chia_policy from chia.server.chia_policy import set_chia_policy
from chia.server.outbound_message import NodeType from chia.server.outbound_message import NodeType
from chia.server.server import ChiaServer from chia.server.server import ChiaServer
@ -34,6 +35,7 @@ main_pid: Optional[int] = None
T = TypeVar("T") T = TypeVar("T")
_T_RpcServiceProtocol = TypeVar("_T_RpcServiceProtocol", bound=RpcServiceProtocol) _T_RpcServiceProtocol = TypeVar("_T_RpcServiceProtocol", bound=RpcServiceProtocol)
_T_ApiProtocol = TypeVar("_T_ApiProtocol", bound=ApiProtocol)
RpcInfo = Tuple[Type[RpcApiProtocol], int] RpcInfo = Tuple[Type[RpcApiProtocol], int]
@ -42,12 +44,12 @@ class ServiceException(Exception):
pass pass
class Service(Generic[_T_RpcServiceProtocol]): class Service(Generic[_T_RpcServiceProtocol, _T_ApiProtocol]):
def __init__( def __init__(
self, self,
root_path: Path, root_path: Path,
node: _T_RpcServiceProtocol, node: _T_RpcServiceProtocol,
peer_api: Any, peer_api: _T_ApiProtocol,
node_type: NodeType, node_type: NodeType,
advertised_port: int, advertised_port: int,
service_name: str, service_name: str,

View File

@ -31,7 +31,7 @@ def create_timelord_service(
config: Dict[str, Any], config: Dict[str, Any],
constants: ConsensusConstants, constants: ConsensusConstants,
connect_to_daemon: bool = True, connect_to_daemon: bool = True,
) -> Service[Timelord]: ) -> Service[Timelord, TimelordAPI]:
service_config = config[SERVICE_NAME] service_config = config[SERVICE_NAME]
connect_peers = { connect_peers = {

View File

@ -33,7 +33,7 @@ def create_wallet_service(
consensus_constants: ConsensusConstants, consensus_constants: ConsensusConstants,
keychain: Optional[Keychain] = None, keychain: Optional[Keychain] = None,
connect_to_daemon: bool = True, connect_to_daemon: bool = True,
) -> Service[WalletNode]: ) -> Service[WalletNode, WalletNodeAPI]:
service_config = config[SERVICE_NAME] service_config = config[SERVICE_NAME]
overrides = service_config["network_overrides"]["constants"][service_config["selected_network"]] overrides = service_config["network_overrides"]["constants"][service_config["selected_network"]]

View File

@ -21,6 +21,7 @@ from chia.protocols.protocol_message_types import ProtocolMessageTypes
from chia.protocols.protocol_state_machine import message_response_ok from chia.protocols.protocol_state_machine import message_response_ok
from chia.protocols.protocol_timing import API_EXCEPTION_BAN_SECONDS, INTERNAL_PROTOCOL_ERROR_BAN_SECONDS from chia.protocols.protocol_timing import API_EXCEPTION_BAN_SECONDS, INTERNAL_PROTOCOL_ERROR_BAN_SECONDS
from chia.protocols.shared_protocol import Capability, Handshake from chia.protocols.shared_protocol import Capability, Handshake
from chia.server.api_protocol import ApiProtocol
from chia.server.capabilities import known_active_capabilities from chia.server.capabilities import known_active_capabilities
from chia.server.outbound_message import Message, NodeType, make_msg from chia.server.outbound_message import Message, NodeType, make_msg
from chia.server.rate_limits import RateLimiter from chia.server.rate_limits import RateLimiter
@ -66,7 +67,7 @@ class WSChiaConnection:
""" """
ws: WebSocket = field(repr=False) ws: WebSocket = field(repr=False)
api: Any = field(repr=False) api: ApiProtocol = field(repr=False)
local_type: NodeType local_type: NodeType
local_port: int local_port: int
local_capabilities_for_handshake: List[Tuple[uint16, str]] = field(repr=False) local_capabilities_for_handshake: List[Tuple[uint16, str]] = field(repr=False)
@ -123,7 +124,7 @@ class WSChiaConnection:
cls, cls,
local_type: NodeType, local_type: NodeType,
ws: WebSocket, ws: WebSocket,
api: Any, api: ApiProtocol,
server_port: int, server_port: int,
log: logging.Logger, log: logging.Logger,
is_outbound: bool, is_outbound: bool,
@ -373,9 +374,9 @@ class WSChiaConnection:
raise ProtocolError(Err.INVALID_PROTOCOL_MESSAGE, [message_type]) raise ProtocolError(Err.INVALID_PROTOCOL_MESSAGE, [message_type])
# If api is not ready ignore the request # If api is not ready ignore the request
if hasattr(self.api, "api_ready"): if not self.api.ready():
if self.api.api_ready is False: self.log.warning(f"API not ready, ignore request: {full_message}")
return None return None
timeout: Optional[int] = 600 timeout: Optional[int] = 600
if metadata.execute_task: if metadata.execute_task:

View File

@ -3,14 +3,16 @@ from __future__ import annotations
import asyncio import asyncio
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Any, AsyncGenerator, AsyncIterator, Dict, List, Optional, Tuple, Union from typing import Any, AsyncGenerator, AsyncIterator, Dict, List, Optional, Tuple, Union, cast
from chia.consensus.constants import ConsensusConstants from chia.consensus.constants import ConsensusConstants
from chia.daemon.server import WebSocketServer from chia.daemon.server import WebSocketServer
from chia.farmer.farmer import Farmer from chia.farmer.farmer import Farmer
from chia.farmer.farmer_api import FarmerAPI
from chia.full_node.full_node import FullNode from chia.full_node.full_node import FullNode
from chia.full_node.full_node_api import FullNodeAPI from chia.full_node.full_node_api import FullNodeAPI
from chia.harvester.harvester import Harvester from chia.harvester.harvester import Harvester
from chia.harvester.harvester_api import HarvesterAPI
from chia.protocols.shared_protocol import Capability from chia.protocols.shared_protocol import Capability
from chia.server.server import ChiaServer from chia.server.server import ChiaServer
from chia.server.start_service import Service from chia.server.start_service import Service
@ -31,15 +33,19 @@ from chia.simulator.setup_services import (
from chia.simulator.socket import find_available_listen_port from chia.simulator.socket import find_available_listen_port
from chia.simulator.time_out_assert import time_out_assert_custom_interval from chia.simulator.time_out_assert import time_out_assert_custom_interval
from chia.timelord.timelord import Timelord from chia.timelord.timelord import Timelord
from chia.timelord.timelord_api import TimelordAPI
from chia.types.blockchain_format.sized_bytes import bytes32 from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.peer_info import UnresolvedPeerInfo from chia.types.peer_info import UnresolvedPeerInfo
from chia.util.hash import std_hash from chia.util.hash import std_hash
from chia.util.ints import uint16, uint32 from chia.util.ints import uint16, uint32
from chia.util.keychain import Keychain from chia.util.keychain import Keychain
from chia.wallet.wallet_node import WalletNode from chia.wallet.wallet_node import WalletNode
from chia.wallet.wallet_node_api import WalletNodeAPI
SimulatorsAndWallets = Tuple[List[FullNodeSimulator], List[Tuple[WalletNode, ChiaServer]], BlockTools] SimulatorsAndWallets = Tuple[List[FullNodeSimulator], List[Tuple[WalletNode, ChiaServer]], BlockTools]
SimulatorsAndWalletsServices = Tuple[List[Service[FullNode]], List[Service[WalletNode]], BlockTools] SimulatorsAndWalletsServices = Tuple[
List[Service[FullNode, FullNodeSimulator]], List[Service[WalletNode, WalletNodeAPI]], BlockTools
]
def cleanup_keyring(keyring: TempKeyring) -> None: def cleanup_keyring(keyring: TempKeyring) -> None:
@ -146,7 +152,7 @@ async def setup_simulators_and_wallets(
db_version: int = 1, db_version: int = 1,
config_overrides: Optional[Dict[str, int]] = None, config_overrides: Optional[Dict[str, int]] = None,
disable_capabilities: Optional[List[Capability]] = None, disable_capabilities: Optional[List[Capability]] = None,
) -> AsyncGenerator[Tuple[List[FullNodeAPI], List[Tuple[WalletNode, ChiaServer]], BlockTools], None]: ) -> AsyncGenerator[Tuple[List[FullNodeSimulator], List[Tuple[WalletNode, ChiaServer]], BlockTools], None]:
with TempKeyring(populate=True) as keychain1, TempKeyring(populate=True) as keychain2: with TempKeyring(populate=True) as keychain1, TempKeyring(populate=True) as keychain2:
res = await setup_simulators_and_wallets_inner( res = await setup_simulators_and_wallets_inner(
db_version, db_version,
@ -189,7 +195,9 @@ async def setup_simulators_and_wallets_service(
db_version: int = 1, db_version: int = 1,
config_overrides: Optional[Dict[str, int]] = None, config_overrides: Optional[Dict[str, int]] = None,
disable_capabilities: Optional[List[Capability]] = None, disable_capabilities: Optional[List[Capability]] = None,
) -> AsyncGenerator[Tuple[List[Service[FullNode]], List[Service[WalletNode]], BlockTools], None]: ) -> AsyncGenerator[
Tuple[List[Service[FullNode, FullNodeSimulator]], List[Service[WalletNode, WalletNodeAPI]], BlockTools], None
]:
with TempKeyring(populate=True) as keychain1, TempKeyring(populate=True) as keychain2: with TempKeyring(populate=True) as keychain1, TempKeyring(populate=True) as keychain2:
res = await setup_simulators_and_wallets_inner( res = await setup_simulators_and_wallets_inner(
db_version, db_version,
@ -227,13 +235,15 @@ async def setup_simulators_and_wallets_inner(
disable_capabilities: Optional[List[Capability]], disable_capabilities: Optional[List[Capability]],
) -> Tuple[ ) -> Tuple[
List[BlockTools], List[BlockTools],
List[AsyncGenerator[Union[Service[FullNode], Service[WalletNode]], None]], List[AsyncGenerator[Union[Service[FullNode, FullNodeSimulator], Service[WalletNode, WalletNodeAPI]], None]],
List[Service[FullNode]], List[Service[FullNode, FullNodeSimulator]],
List[Service[WalletNode]], List[Service[WalletNode, WalletNodeAPI]],
]: ]:
simulators: List[Service[FullNode]] = [] simulators: List[Service[FullNode, FullNodeSimulator]] = []
wallets: List[Service[WalletNode]] = [] wallets: List[Service[WalletNode, WalletNodeAPI]] = []
node_iters: List[AsyncGenerator[Union[Service[FullNode], Service[WalletNode]], None]] = [] node_iters: List[
AsyncGenerator[Union[Service[FullNode, FullNodeSimulator], Service[WalletNode, WalletNodeAPI]], None]
] = []
bt_tools: List[BlockTools] = [] bt_tools: List[BlockTools] = []
consensus_constants: ConsensusConstants = constants_for_dic(dic) consensus_constants: ConsensusConstants = constants_for_dic(dic)
for index in range(0, simulator_count): for index in range(0, simulator_count):
@ -243,14 +253,17 @@ async def setup_simulators_and_wallets_inner(
consensus_constants, const_dict=dic, keychain=keychain1, config_overrides=config_overrides consensus_constants, const_dict=dic, keychain=keychain1, config_overrides=config_overrides
) )
) # block tools modifies constants ) # block tools modifies constants
sim = setup_full_node( sim = cast(
consensus_constants=bt_tools[index].constants, AsyncGenerator[Service[FullNode, FullNodeSimulator], None],
db_name=db_name, setup_full_node(
self_hostname=bt_tools[index].config["self_hostname"], consensus_constants=bt_tools[index].constants,
local_bt=bt_tools[index], db_name=db_name,
simulator=True, self_hostname=bt_tools[index].config["self_hostname"],
db_version=db_version, local_bt=bt_tools[index],
disable_capabilities=disable_capabilities, simulator=True,
db_version=db_version,
disable_capabilities=disable_capabilities,
),
) )
service = await sim.__anext__() service = await sim.__anext__()
simulators.append(service) simulators.append(service)
@ -289,7 +302,7 @@ async def setup_farmer_multi_harvester(
consensus_constants: ConsensusConstants, consensus_constants: ConsensusConstants,
*, *,
start_services: bool, start_services: bool,
) -> AsyncIterator[Tuple[List[Service[Harvester]], Service[Farmer], BlockTools]]: ) -> AsyncIterator[Tuple[List[Service[Harvester, HarvesterAPI]], Service[Farmer, FarmerAPI], BlockTools]]:
farmer_node_iterators = [ farmer_node_iterators = [
setup_farmer( setup_farmer(
block_tools, block_tools,
@ -342,7 +355,9 @@ async def setup_full_system(
b_tools: Optional[BlockTools] = None, b_tools: Optional[BlockTools] = None,
b_tools_1: Optional[BlockTools] = None, b_tools_1: Optional[BlockTools] = None,
db_version: int = 1, db_version: int = 1,
) -> AsyncGenerator[Tuple[Any, Any, Harvester, Farmer, Any, Service[Timelord], object, object, Any, ChiaServer], None]: ) -> AsyncGenerator[
Tuple[Any, Any, Harvester, Farmer, Any, Service[Timelord, TimelordAPI], object, object, Any, ChiaServer], None
]:
with TempKeyring(populate=True) as keychain1, TempKeyring(populate=True) as keychain2: with TempKeyring(populate=True) as keychain1, TempKeyring(populate=True) as keychain2:
daemon_ws, node_iters, ret = await setup_full_system_inner( daemon_ws, node_iters, ret = await setup_full_system_inner(
b_tools, b_tools_1, False, consensus_constants, db_version, keychain1, keychain2, shared_b_tools b_tools, b_tools_1, False, consensus_constants, db_version, keychain1, keychain2, shared_b_tools
@ -361,7 +376,17 @@ async def setup_full_system_connect_to_deamon(
db_version: int = 1, db_version: int = 1,
) -> AsyncGenerator[ ) -> AsyncGenerator[
Tuple[ Tuple[
Any, Any, Harvester, Farmer, Any, Service[Timelord], object, object, Any, ChiaServer, Optional[WebSocketServer] Any,
Any,
Harvester,
Farmer,
Any,
Service[Timelord, TimelordAPI],
object,
object,
Any,
ChiaServer,
Optional[WebSocketServer],
], ],
None, None,
]: ]:
@ -387,7 +412,7 @@ async def setup_full_system_inner(
) -> Tuple[ ) -> Tuple[
Optional[WebSocketServer], Optional[WebSocketServer],
List[AsyncGenerator[object, None]], List[AsyncGenerator[object, None]],
Tuple[Any, Any, Harvester, Farmer, Any, Service[Timelord], object, object, Any, ChiaServer], Tuple[Any, Any, Harvester, Farmer, Any, Service[Timelord, TimelordAPI], object, object, Any, ChiaServer],
]: ]:
if b_tools is None: if b_tools is None:
b_tools = await create_block_tools_async(constants=consensus_constants, keychain=keychain1) b_tools = await create_block_tools_async(constants=consensus_constants, keychain=keychain1)

View File

@ -14,11 +14,16 @@ from chia.cmds.init_funcs import init
from chia.consensus.constants import ConsensusConstants from chia.consensus.constants import ConsensusConstants
from chia.daemon.server import WebSocketServer, daemon_launch_lock_path from chia.daemon.server import WebSocketServer, daemon_launch_lock_path
from chia.farmer.farmer import Farmer from chia.farmer.farmer import Farmer
from chia.farmer.farmer_api import FarmerAPI
from chia.full_node.full_node import FullNode from chia.full_node.full_node import FullNode
from chia.full_node.full_node_api import FullNodeAPI
from chia.harvester.harvester import Harvester from chia.harvester.harvester import Harvester
from chia.harvester.harvester_api import HarvesterAPI
from chia.introducer.introducer import Introducer from chia.introducer.introducer import Introducer
from chia.introducer.introducer_api import IntroducerAPI
from chia.protocols.shared_protocol import Capability, capabilities from chia.protocols.shared_protocol import Capability, capabilities
from chia.seeder.crawler import Crawler from chia.seeder.crawler import Crawler
from chia.seeder.crawler_api import CrawlerAPI
from chia.seeder.start_crawler import create_full_node_crawler_service from chia.seeder.start_crawler import create_full_node_crawler_service
from chia.server.start_farmer import create_farmer_service from chia.server.start_farmer import create_farmer_service
from chia.server.start_full_node import create_full_node_service from chia.server.start_full_node import create_full_node_service
@ -31,6 +36,7 @@ from chia.simulator.block_tools import BlockTools
from chia.simulator.keyring import TempKeyring from chia.simulator.keyring import TempKeyring
from chia.simulator.start_simulator import create_full_node_simulator_service from chia.simulator.start_simulator import create_full_node_simulator_service
from chia.timelord.timelord import Timelord from chia.timelord.timelord import Timelord
from chia.timelord.timelord_api import TimelordAPI
from chia.timelord.timelord_launcher import kill_processes, spawn_process from chia.timelord.timelord_launcher import kill_processes, spawn_process
from chia.types.peer_info import UnresolvedPeerInfo from chia.types.peer_info import UnresolvedPeerInfo
from chia.util.bech32m import encode_puzzle_hash from chia.util.bech32m import encode_puzzle_hash
@ -39,6 +45,7 @@ from chia.util.ints import uint16
from chia.util.keychain import bytes_to_mnemonic from chia.util.keychain import bytes_to_mnemonic
from chia.util.lock import Lockfile from chia.util.lock import Lockfile
from chia.wallet.wallet_node import WalletNode from chia.wallet.wallet_node import WalletNode
from chia.wallet.wallet_node_api import WalletNodeAPI
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -102,7 +109,7 @@ async def setup_full_node(
disable_capabilities: Optional[List[Capability]] = None, disable_capabilities: Optional[List[Capability]] = None,
*, *,
reuse_db: bool = False, reuse_db: bool = False,
) -> AsyncGenerator[Service[FullNode], None]: ) -> AsyncGenerator[Service[FullNode, FullNodeAPI], None]:
db_path = local_bt.root_path / f"{db_name}" db_path = local_bt.root_path / f"{db_name}"
if not reuse_db and db_path.exists(): if not reuse_db and db_path.exists():
# TODO: remove (maybe) when fixed https://github.com/python/cpython/issues/97641 # TODO: remove (maybe) when fixed https://github.com/python/cpython/issues/97641
@ -169,7 +176,7 @@ async def setup_full_node(
async def setup_crawler( async def setup_crawler(
bt: BlockTools, bt: BlockTools,
) -> AsyncGenerator[Service[Crawler], None]: ) -> AsyncGenerator[Service[Crawler, CrawlerAPI], None]:
config = bt.config config = bt.config
service_config = config["seeder"] service_config = config["seeder"]
service_config["selected_network"] = "testnet0" service_config["selected_network"] = "testnet0"
@ -205,7 +212,7 @@ async def setup_wallet_node(
introducer_port: Optional[uint16] = None, introducer_port: Optional[uint16] = None,
key_seed: Optional[bytes] = None, key_seed: Optional[bytes] = None,
initial_num_public_keys: int = 5, initial_num_public_keys: int = 5,
) -> AsyncGenerator[Service[WalletNode], None]: ) -> AsyncGenerator[Service[WalletNode, WalletNodeAPI], None]:
with TempKeyring(populate=True) as keychain: with TempKeyring(populate=True) as keychain:
config = local_bt.config config = local_bt.config
service_config = config["wallet"] service_config = config["wallet"]
@ -275,7 +282,7 @@ async def setup_harvester(
farmer_peer: Optional[UnresolvedPeerInfo], farmer_peer: Optional[UnresolvedPeerInfo],
consensus_constants: ConsensusConstants, consensus_constants: ConsensusConstants,
start_service: bool = True, start_service: bool = True,
) -> AsyncGenerator[Service[Harvester], None]: ) -> AsyncGenerator[Service[Harvester, HarvesterAPI], None]:
with create_lock_and_load_config(b_tools.root_path / "config" / "ssl" / "ca", root_path) as config: with create_lock_and_load_config(b_tools.root_path / "config" / "ssl" / "ca", root_path) as config:
config["logging"]["log_stdout"] = True config["logging"]["log_stdout"] = True
config["selected_network"] = "testnet0" config["selected_network"] = "testnet0"
@ -309,7 +316,7 @@ async def setup_farmer(
full_node_port: Optional[uint16] = None, full_node_port: Optional[uint16] = None,
start_service: bool = True, start_service: bool = True,
port: uint16 = uint16(0), port: uint16 = uint16(0),
) -> AsyncGenerator[Service[Farmer], None]: ) -> AsyncGenerator[Service[Farmer, FarmerAPI], None]:
with create_lock_and_load_config(b_tools.root_path / "config" / "ssl" / "ca", root_path) as root_config: with create_lock_and_load_config(b_tools.root_path / "config" / "ssl" / "ca", root_path) as root_config:
root_config["logging"]["log_stdout"] = True root_config["logging"]["log_stdout"] = True
root_config["selected_network"] = "testnet0" root_config["selected_network"] = "testnet0"
@ -348,7 +355,7 @@ async def setup_farmer(
await service.wait_closed() await service.wait_closed()
async def setup_introducer(bt: BlockTools, port: int) -> AsyncGenerator[Service[Introducer], None]: async def setup_introducer(bt: BlockTools, port: int) -> AsyncGenerator[Service[Introducer, IntroducerAPI], None]:
service = create_introducer_service( service = create_introducer_service(
bt.root_path, bt.root_path,
bt.config, bt.config,
@ -411,7 +418,7 @@ async def setup_timelord(
consensus_constants: ConsensusConstants, consensus_constants: ConsensusConstants,
b_tools: BlockTools, b_tools: BlockTools,
vdf_port: uint16 = uint16(0), vdf_port: uint16 = uint16(0),
) -> AsyncGenerator[Service[Timelord], None]: ) -> AsyncGenerator[Service[Timelord, TimelordAPI], None]:
config = b_tools.config config = b_tools.config
service_config = config["timelord"] service_config = config["timelord"]
service_config["full_node_peer"]["port"] = full_node_port service_config["full_node_peer"]["port"] = full_node_port

View File

@ -5,6 +5,7 @@ from typing import Dict, List
from chia.rpc.full_node_rpc_api import FullNodeRpcApi from chia.rpc.full_node_rpc_api import FullNodeRpcApi
from chia.rpc.rpc_server import Endpoint, EndpointResult from chia.rpc.rpc_server import Endpoint, EndpointResult
from chia.simulator.full_node_simulator import FullNodeSimulator
from chia.simulator.simulator_protocol import FarmNewBlockProtocol, GetAllCoinsProtocol, ReorgProtocol from chia.simulator.simulator_protocol import FarmNewBlockProtocol, GetAllCoinsProtocol, ReorgProtocol
from chia.types.blockchain_format.sized_bytes import bytes32 from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.coin_record import CoinRecord from chia.types.coin_record import CoinRecord
@ -14,6 +15,11 @@ from chia.util.ints import uint32
class SimulatorFullNodeRpcApi(FullNodeRpcApi): class SimulatorFullNodeRpcApi(FullNodeRpcApi):
@property
def simulator_api(self) -> FullNodeSimulator:
assert isinstance(self.service.server.api, FullNodeSimulator)
return self.service.server.api
def get_routes(self) -> Dict[str, Endpoint]: def get_routes(self) -> Dict[str, Endpoint]:
routes = super().get_routes() routes = super().get_routes()
routes["/get_all_blocks"] = self.get_all_blocks routes["/get_all_blocks"] = self.get_all_blocks
@ -28,7 +34,7 @@ class SimulatorFullNodeRpcApi(FullNodeRpcApi):
return routes return routes
async def get_all_blocks(self, _request: Dict[str, object]) -> EndpointResult: async def get_all_blocks(self, _request: Dict[str, object]) -> EndpointResult:
all_blocks: List[FullBlock] = await self.service.server.api.get_all_full_blocks() all_blocks: List[FullBlock] = await self.simulator_api.get_all_full_blocks()
return {"blocks": [block.to_json_dict() for block in all_blocks]} return {"blocks": [block.to_json_dict() for block in all_blocks]}
async def farm_block(self, _request: Dict[str, object]) -> EndpointResult: async def farm_block(self, _request: Dict[str, object]) -> EndpointResult:
@ -40,30 +46,30 @@ class SimulatorFullNodeRpcApi(FullNodeRpcApi):
cur_height = self.service.blockchain.get_peak_height() cur_height = self.service.blockchain.get_peak_height()
if guarantee_tx_block: if guarantee_tx_block:
for i in range(blocks): # these can only be tx blocks for i in range(blocks): # these can only be tx blocks
await self.service.server.api.farm_new_transaction_block(req) await self.simulator_api.farm_new_transaction_block(req)
else: else:
for i in range(blocks): # these can either be full blocks or tx blocks for i in range(blocks): # these can either be full blocks or tx blocks
await self.service.server.api.farm_new_block(req) await self.simulator_api.farm_new_block(req)
return {"new_peak_height": (cur_height if cur_height is not None else 0) + blocks} return {"new_peak_height": (cur_height if cur_height is not None else 0) + blocks}
async def set_auto_farming(self, _request: Dict[str, object]) -> EndpointResult: async def set_auto_farming(self, _request: Dict[str, object]) -> EndpointResult:
auto_farm = bool(_request["auto_farm"]) auto_farm = bool(_request["auto_farm"])
result = await self.service.server.api.update_autofarm_config(auto_farm) result = await self.simulator_api.update_autofarm_config(auto_farm)
return {"auto_farm_enabled": result} return {"auto_farm_enabled": result}
async def get_auto_farming(self, _request: Dict[str, object]) -> EndpointResult: async def get_auto_farming(self, _request: Dict[str, object]) -> EndpointResult:
return {"auto_farm_enabled": self.service.server.api.auto_farm} return {"auto_farm_enabled": self.simulator_api.auto_farm}
async def get_farming_ph(self, _request: Dict[str, object]) -> EndpointResult: async def get_farming_ph(self, _request: Dict[str, object]) -> EndpointResult:
return {"puzzle_hash": self.service.server.api.bt.farmer_ph.hex()} return {"puzzle_hash": self.simulator_api.bt.farmer_ph.hex()}
async def get_all_coins(self, _request: Dict[str, object]) -> EndpointResult: async def get_all_coins(self, _request: Dict[str, object]) -> EndpointResult:
p_request = GetAllCoinsProtocol(bool((_request.get("include_spent_coins", False)))) p_request = GetAllCoinsProtocol(bool((_request.get("include_spent_coins", False))))
result: List[CoinRecord] = await self.service.server.api.get_all_coins(p_request) result: List[CoinRecord] = await self.simulator_api.get_all_coins(p_request)
return {"coin_records": [coin_record.to_json_dict() for coin_record in result]} return {"coin_records": [coin_record.to_json_dict() for coin_record in result]}
async def get_all_puzzle_hashes(self, _request: Dict[str, object]) -> EndpointResult: async def get_all_puzzle_hashes(self, _request: Dict[str, object]) -> EndpointResult:
result = await self.service.server.api.get_all_puzzle_hashes() result = await self.simulator_api.get_all_puzzle_hashes()
return { return {
"puzzle_hashes": {puzzle_hash.hex(): (amount, num_tx) for (puzzle_hash, (amount, num_tx)) in result.items()} "puzzle_hashes": {puzzle_hash.hex(): (amount, num_tx) for (puzzle_hash, (amount, num_tx)) in result.items()}
} }
@ -76,7 +82,7 @@ class SimulatorFullNodeRpcApi(FullNodeRpcApi):
raise ValueError("No blocks to revert") raise ValueError("No blocks to revert")
new_height = (height - blocks) if not all_blocks else 1 new_height = (height - blocks) if not all_blocks else 1
assert new_height >= 1 assert new_height >= 1
await self.service.server.api.revert_block_height(new_height) await self.simulator_api.revert_block_height(uint32(new_height))
return {"new_peak_height": new_height} return {"new_peak_height": new_height}
async def reorg_blocks(self, _request: Dict[str, object]) -> EndpointResult: async def reorg_blocks(self, _request: Dict[str, object]) -> EndpointResult:
@ -91,8 +97,6 @@ class SimulatorFullNodeRpcApi(FullNodeRpcApi):
fork_height = (cur_height - fork_blocks) if not all_blocks else 1 fork_height = (cur_height - fork_blocks) if not all_blocks else 1
new_height = cur_height + new_blocks # any number works as long as its not 0 new_height = cur_height + new_blocks # any number works as long as its not 0
assert fork_height >= 1 and new_height - 1 >= cur_height assert fork_height >= 1 and new_height - 1 >= cur_height
request = ReorgProtocol( request = ReorgProtocol(uint32(fork_height), uint32(new_height), self.simulator_api.bt.farmer_ph, random_seed)
uint32(fork_height), uint32(new_height), self.service.server.api.bt.farmer_ph, random_seed await self.simulator_api.reorg_from_index_to_new_index(request)
)
await self.service.server.api.reorg_from_index_to_new_index(request)
return {"new_peak_height": new_height} return {"new_peak_height": new_height}

View File

@ -7,6 +7,7 @@ from pathlib import Path
from typing import Dict, List, Optional, Tuple from typing import Dict, List, Optional, Tuple
from chia.full_node.full_node import FullNode from chia.full_node.full_node import FullNode
from chia.full_node.full_node_api import FullNodeAPI
from chia.server.outbound_message import NodeType from chia.server.outbound_message import NodeType
from chia.server.start_service import Service, async_run from chia.server.start_service import Service, async_run
from chia.simulator.block_tools import BlockTools, test_constants from chia.simulator.block_tools import BlockTools, test_constants
@ -34,7 +35,7 @@ def create_full_node_simulator_service(
bt: BlockTools, bt: BlockTools,
connect_to_daemon: bool = True, connect_to_daemon: bool = True,
override_capabilities: List[Tuple[uint16, str]] = None, override_capabilities: List[Tuple[uint16, str]] = None,
) -> Service[FullNode]: ) -> Service[FullNode, FullNodeAPI]:
service_config = config[SERVICE_NAME] service_config = config[SERVICE_NAME]
constants = bt.constants constants = bt.constants

View File

@ -16,11 +16,16 @@ log = logging.getLogger(__name__)
class TimelordAPI: class TimelordAPI:
log: logging.Logger
timelord: Timelord timelord: Timelord
def __init__(self, timelord) -> None: def __init__(self, timelord) -> None:
self.log = logging.getLogger(__name__)
self.timelord = timelord self.timelord = timelord
def ready(self) -> bool:
return True
def _set_state_changed_callback(self, callback: StateChangedProtocol) -> None: def _set_state_changed_callback(self, callback: StateChangedProtocol) -> None:
self.timelord.state_changed_callback = callback self.timelord.state_changed_callback = callback

View File

@ -1,5 +1,7 @@
from __future__ import annotations from __future__ import annotations
import logging
from chia.protocols import full_node_protocol, introducer_protocol, wallet_protocol from chia.protocols import full_node_protocol, introducer_protocol, wallet_protocol
from chia.server.outbound_message import NodeType from chia.server.outbound_message import NodeType
from chia.server.ws_connection import WSChiaConnection from chia.server.ws_connection import WSChiaConnection
@ -10,17 +12,14 @@ from chia.wallet.wallet_node import PeerPeak, WalletNode
class WalletNodeAPI: class WalletNodeAPI:
log: logging.Logger
wallet_node: WalletNode wallet_node: WalletNode
def __init__(self, wallet_node) -> None: def __init__(self, wallet_node) -> None:
self.log = logging.getLogger(__name__)
self.wallet_node = wallet_node self.wallet_node = wallet_node
@property def ready(self) -> bool:
def log(self):
return self.wallet_node.log
@property
def api_ready(self):
return self.wallet_node.logged_in return self.wallet_node.logged_in
@api_request(peer_required=True) @api_request(peer_required=True)

View File

@ -25,6 +25,7 @@ from chia.full_node.full_node_api import FullNodeAPI
from chia.protocols import full_node_protocol from chia.protocols import full_node_protocol
from chia.rpc.wallet_rpc_client import WalletRpcClient from chia.rpc.wallet_rpc_client import WalletRpcClient
from chia.seeder.crawler import Crawler from chia.seeder.crawler import Crawler
from chia.seeder.crawler_api import CrawlerAPI
from chia.server.server import ChiaServer from chia.server.server import ChiaServer
from chia.server.start_service import Service from chia.server.start_service import Service
from chia.simulator.full_node_simulator import FullNodeSimulator from chia.simulator.full_node_simulator import FullNodeSimulator
@ -48,6 +49,7 @@ from chia.util.task_timing import main as task_instrumentation_main
from chia.util.task_timing import start_task_instrumentation, stop_task_instrumentation from chia.util.task_timing import start_task_instrumentation, stop_task_instrumentation
from chia.wallet.wallet import Wallet from chia.wallet.wallet import Wallet
from chia.wallet.wallet_node import WalletNode from chia.wallet.wallet_node import WalletNode
from chia.wallet.wallet_node_api import WalletNodeAPI
from tests.core.data_layer.util import ChiaRoot from tests.core.data_layer.util import ChiaRoot
from tests.core.node_height import node_height_at_least from tests.core.node_height import node_height_at_least
from tests.simulation.test_simulation import test_constants_modified from tests.simulation.test_simulation import test_constants_modified
@ -378,7 +380,7 @@ async def two_wallet_nodes(request):
@pytest_asyncio.fixture(scope="function") @pytest_asyncio.fixture(scope="function")
async def two_wallet_nodes_services() -> AsyncIterator[ async def two_wallet_nodes_services() -> AsyncIterator[
Tuple[List[Service[FullNode]], List[Service[WalletNode]], BlockTools] Tuple[List[Service[FullNode, FullNodeSimulator]], List[Service[WalletNode, WalletNodeAPI]], BlockTools]
]: ]:
async for _ in setup_simulators_and_wallets_service(1, 2, {}): async for _ in setup_simulators_and_wallets_service(1, 2, {}):
yield _ yield _
@ -821,7 +823,7 @@ async def timelord_service(bt):
@pytest_asyncio.fixture(scope="function") @pytest_asyncio.fixture(scope="function")
async def crawler_service(bt: BlockTools) -> AsyncIterator[Service[Crawler]]: async def crawler_service(bt: BlockTools) -> AsyncIterator[Service[Crawler, CrawlerAPI]]:
async for service in setup_crawler(bt): async for service in setup_crawler(bt):
yield service yield service

View File

@ -33,6 +33,7 @@ from chia.wallet.trading.offer import Offer as TradingOffer
from chia.wallet.transaction_record import TransactionRecord from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.wallet import Wallet from chia.wallet.wallet import Wallet
from chia.wallet.wallet_node import WalletNode from chia.wallet.wallet_node import WalletNode
from chia.wallet.wallet_node_api import WalletNodeAPI
pytestmark = pytest.mark.data_layer pytestmark = pytest.mark.data_layer
nodes = Tuple[WalletNode, FullNodeSimulator] nodes = Tuple[WalletNode, FullNodeSimulator]
@ -43,7 +44,10 @@ two_wallets_with_port = Tuple[Tuple[wallet_and_port_tuple, wallet_and_port_tuple
@contextlib.asynccontextmanager @contextlib.asynccontextmanager
async def init_data_layer( async def init_data_layer(
wallet_rpc_port: uint16, bt: BlockTools, db_path: Path, wallet_service: Optional[Service[WalletNode]] = None wallet_rpc_port: uint16,
bt: BlockTools,
db_path: Path,
wallet_service: Optional[Service[WalletNode, WalletNodeAPI]] = None,
) -> AsyncIterator[DataLayer]: ) -> AsyncIterator[DataLayer]:
config = bt.config config = bt.config
config["data_layer"]["wallet_peer"]["port"] = int(wallet_rpc_port) config["data_layer"]["wallet_peer"]["port"] = int(wallet_rpc_port)

View File

@ -5,7 +5,7 @@ import dataclasses
import random import random
import time import time
from secrets import token_bytes from secrets import token_bytes
from typing import Dict, List, Optional, Tuple from typing import Dict, List, Optional, Tuple, cast
import pytest import pytest
from blspy import AugSchemeMPL, G2Element, PrivateKey from blspy import AugSchemeMPL, G2Element, PrivateKey
@ -26,6 +26,7 @@ from chia.server.address_manager import AddressManager
from chia.server.outbound_message import Message, NodeType from chia.server.outbound_message import Message, NodeType
from chia.server.server import ChiaServer from chia.server.server import ChiaServer
from chia.simulator.block_tools import BlockTools, create_block_tools_async, get_signage_point from chia.simulator.block_tools import BlockTools, create_block_tools_async, get_signage_point
from chia.simulator.full_node_simulator import FullNodeSimulator
from chia.simulator.keyring import TempKeyring from chia.simulator.keyring import TempKeyring
from chia.simulator.setup_services import setup_full_node from chia.simulator.setup_services import setup_full_node
from chia.simulator.simulator_protocol import FarmNewBlockProtocol from chia.simulator.simulator_protocol import FarmNewBlockProtocol
@ -2018,10 +2019,12 @@ async def test_node_start_with_existing_blocks(db_version: int) -> None:
db_version=db_version, db_version=db_version,
reuse_db=True, reuse_db=True,
): ):
await service._api.farm_blocks_to_puzzlehash(count=blocks_per_cycle) simulator_api = cast(FullNodeSimulator, service._api)
await simulator_api.farm_blocks_to_puzzlehash(count=blocks_per_cycle)
expected_height += blocks_per_cycle expected_height += blocks_per_cycle
block_record = service._api.full_node._blockchain.get_peak() assert simulator_api.full_node._blockchain is not None
block_record = simulator_api.full_node._blockchain.get_peak()
assert block_record is not None, f"block_record is None on cycle {cycle + 1}" assert block_record is not None, f"block_record is None on cycle {cycle + 1}"
assert block_record.height == expected_height, f"wrong height on cycle {cycle + 1}" assert block_record.height == expected_height, f"wrong height on cycle {cycle + 1}"

View File

@ -1,18 +1,23 @@
from __future__ import annotations from __future__ import annotations
from typing import Callable, Tuple import logging
from typing import Callable, Tuple, cast
import pytest import pytest
from packaging.version import Version from packaging.version import Version
from chia.cmds.init_funcs import chia_full_version_str from chia.cmds.init_funcs import chia_full_version_str
from chia.full_node.full_node_api import FullNodeAPI from chia.full_node.full_node_api import FullNodeAPI
from chia.protocols.protocol_message_types import ProtocolMessageTypes
from chia.protocols.shared_protocol import protocol_version from chia.protocols.shared_protocol import protocol_version
from chia.protocols.wallet_protocol import RejectHeaderRequest
from chia.server.outbound_message import make_msg
from chia.server.server import ChiaServer from chia.server.server import ChiaServer
from chia.simulator.block_tools import BlockTools from chia.simulator.block_tools import BlockTools
from chia.simulator.setup_nodes import SimulatorsAndWalletsServices from chia.simulator.setup_nodes import SimulatorsAndWalletsServices
from chia.simulator.time_out_assert import time_out_assert
from chia.types.peer_info import PeerInfo from chia.types.peer_info import PeerInfo
from chia.util.ints import uint16 from chia.util.ints import uint16, uint32
from tests.connection_utils import connect_and_get_peer from tests.connection_utils import connect_and_get_peer
@ -48,10 +53,38 @@ async def test_connection_versions(
[full_node_service], [wallet_service], _ = one_wallet_and_one_simulator_services [full_node_service], [wallet_service], _ = one_wallet_and_one_simulator_services
wallet_node = wallet_service._node wallet_node = wallet_service._node
full_node = full_node_service._node full_node = full_node_service._node
await wallet_node.server.start_client(PeerInfo(self_hostname, uint16(full_node_service._api.server._port)), None) await wallet_node.server.start_client(
PeerInfo(self_hostname, uint16(cast(FullNodeAPI, full_node_service._api).server._port)), None
)
outgoing_connection = wallet_node.server.all_connections[full_node.server.node_id] outgoing_connection = wallet_node.server.all_connections[full_node.server.node_id]
incoming_connection = full_node.server.all_connections[wallet_node.server.node_id] incoming_connection = full_node.server.all_connections[wallet_node.server.node_id]
for connection in [outgoing_connection, incoming_connection]: for connection in [outgoing_connection, incoming_connection]:
assert connection.protocol_version == Version(protocol_version) assert connection.protocol_version == Version(protocol_version)
assert connection.version == Version(chia_full_version_str()) assert connection.version == Version(chia_full_version_str())
assert connection.get_version() == chia_full_version_str() assert connection.get_version() == chia_full_version_str()
@pytest.mark.asyncio
async def test_api_not_ready(
self_hostname: str,
one_wallet_and_one_simulator_services: SimulatorsAndWalletsServices,
caplog: pytest.LogCaptureFixture,
) -> None:
[full_node_service], [wallet_service], _ = one_wallet_and_one_simulator_services
wallet_node = wallet_service._node
full_node = full_node_service._node
await wallet_node.server.start_client(
PeerInfo(self_hostname, uint16(cast(FullNodeAPI, full_node_service._api).server._port)), None
)
wallet_node.log_out()
assert not wallet_service._api.ready()
connection = full_node.server.all_connections[wallet_node.server.node_id]
def request_ignored() -> bool:
return "API not ready, ignore request: {'data': '0x00000000', 'id': None, 'type': 53}" in caplog.text
with caplog.at_level(logging.WARNING):
assert await connection.send_message(
make_msg(ProtocolMessageTypes.reject_header_request, RejectHeaderRequest(uint32(0)))
)
await time_out_assert(10, request_ignored)

View File

@ -6,23 +6,25 @@ from typing import cast
import pytest import pytest
from chia.full_node.full_node_api import FullNodeAPI from chia.full_node.full_node_api import FullNodeAPI
from chia.protocols.full_node_protocol import NewPeak
from chia.protocols.protocol_message_types import ProtocolMessageTypes from chia.protocols.protocol_message_types import ProtocolMessageTypes
from chia.protocols.wallet_protocol import RequestChildren from chia.protocols.wallet_protocol import RequestChildren
from chia.seeder.crawler import Crawler from chia.seeder.crawler import Crawler
from chia.seeder.crawler_api import CrawlerAPI
from chia.server.outbound_message import make_msg from chia.server.outbound_message import make_msg
from chia.server.start_service import Service from chia.server.start_service import Service
from chia.simulator.setup_nodes import SimulatorsAndWalletsServices from chia.simulator.setup_nodes import SimulatorsAndWalletsServices
from chia.simulator.time_out_assert import time_out_assert from chia.simulator.time_out_assert import time_out_assert
from chia.types.blockchain_format.sized_bytes import bytes32 from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.peer_info import PeerInfo from chia.types.peer_info import PeerInfo
from chia.util.ints import uint16 from chia.util.ints import uint16, uint32, uint128
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_unknown_messages( async def test_unknown_messages(
self_hostname: str, self_hostname: str,
one_node: SimulatorsAndWalletsServices, one_node: SimulatorsAndWalletsServices,
crawler_service: Service[Crawler], crawler_service: Service[Crawler, CrawlerAPI],
caplog: pytest.LogCaptureFixture, caplog: pytest.LogCaptureFixture,
) -> None: ) -> None:
[full_node_service], _, _ = one_node [full_node_service], _, _ = one_node
@ -40,3 +42,29 @@ async def test_unknown_messages(
msg = make_msg(ProtocolMessageTypes.request_children, RequestChildren(bytes32(b"\0" * 32))) msg = make_msg(ProtocolMessageTypes.request_children, RequestChildren(bytes32(b"\0" * 32)))
assert await connection.send_message(msg) assert await connection.send_message(msg)
await time_out_assert(10, receiving_failed) await time_out_assert(10, receiving_failed)
@pytest.mark.asyncio
async def test_valid_message(
self_hostname: str,
one_node: SimulatorsAndWalletsServices,
crawler_service: Service[Crawler, CrawlerAPI],
caplog: pytest.LogCaptureFixture,
) -> None:
[full_node_service], _, _ = one_node
crawler = crawler_service._node
full_node = full_node_service._node
assert await crawler.server.start_client(
PeerInfo(self_hostname, uint16(cast(FullNodeAPI, full_node_service._api).server._port)), None
)
connection = full_node.server.all_connections[crawler.server.node_id]
def peer_added() -> bool:
return crawler.server.all_connections[full_node.server.node_id].get_peer_logging() in crawler.with_peak
msg = make_msg(
ProtocolMessageTypes.new_peak,
NewPeak(bytes32(b"\0" * 32), uint32(2), uint128(1), uint32(1), bytes32(b"\1" * 32)),
)
assert await connection.send_message(msg)
await time_out_assert(10, peer_added)

View File

@ -16,7 +16,9 @@ import pytest_asyncio
from chia.consensus.coinbase import create_puzzlehash_for_pk from chia.consensus.coinbase import create_puzzlehash_for_pk
from chia.farmer.farmer import Farmer from chia.farmer.farmer import Farmer
from chia.farmer.farmer_api import FarmerAPI
from chia.harvester.harvester import Harvester from chia.harvester.harvester import Harvester
from chia.harvester.harvester_api import HarvesterAPI
from chia.plot_sync.receiver import Receiver from chia.plot_sync.receiver import Receiver
from chia.plotting.util import add_plot_directory from chia.plotting.util import add_plot_directory
from chia.protocols import farmer_protocol from chia.protocols import farmer_protocol
@ -64,12 +66,15 @@ async def wait_for_synced_receiver(farmer: Farmer, harvester_id: bytes32) -> Non
await time_out_assert(30, wait) await time_out_assert(30, wait)
HarvesterFarmerEnvironment = Tuple[Service[Farmer], FarmerRpcClient, Service[Harvester], HarvesterRpcClient, BlockTools] HarvesterFarmerEnvironment = Tuple[
Service[Farmer, FarmerAPI], FarmerRpcClient, Service[Harvester, HarvesterAPI], HarvesterRpcClient, BlockTools
]
@pytest_asyncio.fixture(scope="function") @pytest_asyncio.fixture(scope="function")
async def harvester_farmer_environment( async def harvester_farmer_environment(
farmer_one_harvester: Tuple[List[Service[Harvester]], Service[Farmer], BlockTools], self_hostname: str farmer_one_harvester: Tuple[List[Service[Harvester, HarvesterAPI]], Service[Farmer, FarmerAPI], BlockTools],
self_hostname: str,
) -> AsyncIterator[HarvesterFarmerEnvironment]: ) -> AsyncIterator[HarvesterFarmerEnvironment]:
harvesters, farmer_service, bt = farmer_one_harvester harvesters, farmer_service, bt = farmer_one_harvester
harvester_service = harvesters[0] harvester_service = harvesters[0]

View File

@ -6,7 +6,9 @@ from typing import List, Tuple
import pytest import pytest
from chia.farmer.farmer import Farmer from chia.farmer.farmer import Farmer
from chia.farmer.farmer_api import FarmerAPI
from chia.harvester.harvester import Harvester from chia.harvester.harvester import Harvester
from chia.harvester.harvester_api import HarvesterAPI
from chia.server.start_service import Service from chia.server.start_service import Service
from chia.simulator.block_tools import BlockTools from chia.simulator.block_tools import BlockTools
from chia.simulator.time_out_assert import time_out_assert from chia.simulator.time_out_assert import time_out_assert
@ -20,7 +22,9 @@ def farmer_is_started(farmer: Farmer) -> bool:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_start_with_empty_keychain( async def test_start_with_empty_keychain(
farmer_one_harvester_not_started: Tuple[List[Service[Harvester]], Service[Farmer], BlockTools] farmer_one_harvester_not_started: Tuple[
List[Service[Harvester, HarvesterAPI]], Service[Farmer, FarmerAPI], BlockTools
]
) -> None: ) -> None:
_, farmer_service, bt = farmer_one_harvester_not_started _, farmer_service, bt = farmer_one_harvester_not_started
farmer: Farmer = farmer_service._node farmer: Farmer = farmer_service._node
@ -45,7 +49,9 @@ async def test_start_with_empty_keychain(
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_harvester_handshake( async def test_harvester_handshake(
farmer_one_harvester_not_started: Tuple[List[Service[Harvester]], Service[Farmer], BlockTools] farmer_one_harvester_not_started: Tuple[
List[Service[Harvester, HarvesterAPI]], Service[Farmer, FarmerAPI], BlockTools
]
) -> None: ) -> None:
harvesters, farmer_service, bt = farmer_one_harvester_not_started harvesters, farmer_service, bt = farmer_one_harvester_not_started
harvester_service = harvesters[0] harvester_service = harvesters[0]

View File

@ -11,6 +11,7 @@ from chia.rpc.full_node_rpc_api import FullNodeRpcApi
from chia.rpc.full_node_rpc_client import FullNodeRpcClient from chia.rpc.full_node_rpc_client import FullNodeRpcClient
from chia.server.start_service import Service from chia.server.start_service import Service
from chia.simulator.block_tools import BlockTools from chia.simulator.block_tools import BlockTools
from chia.simulator.full_node_simulator import FullNodeSimulator
from chia.simulator.simulator_protocol import FarmNewBlockProtocol from chia.simulator.simulator_protocol import FarmNewBlockProtocol
from chia.simulator.wallet_tools import WalletTool from chia.simulator.wallet_tools import WalletTool
from chia.types.blockchain_format.coin import Coin from chia.types.blockchain_format.coin import Coin
@ -18,16 +19,19 @@ from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.spend_bundle import SpendBundle from chia.types.spend_bundle import SpendBundle
from chia.util.ints import uint64 from chia.util.ints import uint64
from chia.wallet.wallet_node import WalletNode from chia.wallet.wallet_node import WalletNode
from chia.wallet.wallet_node_api import WalletNodeAPI
@pytest_asyncio.fixture(scope="function") @pytest_asyncio.fixture(scope="function")
async def setup_node_and_rpc( async def setup_node_and_rpc(
two_wallet_nodes_services: Tuple[List[Service[FullNode]], List[Service[WalletNode]], BlockTools], two_wallet_nodes_services: Tuple[
List[Service[FullNode, FullNodeSimulator]], List[Service[WalletNode, WalletNodeAPI]], BlockTools
],
) -> Tuple[FullNodeRpcClient, FullNodeRpcApi]: ) -> Tuple[FullNodeRpcClient, FullNodeRpcApi]:
full_nodes, wallets, bt = two_wallet_nodes_services full_nodes, wallets, bt = two_wallet_nodes_services
wallet = wallets[0]._node.wallet_state_manager.main_wallet wallet = wallets[0]._node.wallet_state_manager.main_wallet
full_node_apis = [full_node_service._api for full_node_service in full_nodes] full_node_apis = [full_node_service._api for full_node_service in full_nodes]
full_node_api = full_node_apis[0] full_node_api: FullNodeSimulator = full_node_apis[0]
full_node_service_1 = full_nodes[0] full_node_service_1 = full_nodes[0]
assert full_node_service_1.rpc_server is not None assert full_node_service_1.rpc_server is not None
client = await FullNodeRpcClient.create( client = await FullNodeRpcClient.create(
@ -48,11 +52,11 @@ async def setup_node_and_rpc(
@pytest_asyncio.fixture(scope="function") @pytest_asyncio.fixture(scope="function")
async def one_node_no_blocks( async def one_node_no_blocks(
one_node: Tuple[List[Service[FullNode]], List[Service[WalletNode]], BlockTools] one_node: Tuple[List[Service[FullNode, FullNodeSimulator]], List[Service[WalletNode, WalletNodeAPI]], BlockTools]
) -> Tuple[FullNodeRpcClient, FullNodeRpcApi]: ) -> Tuple[FullNodeRpcClient, FullNodeRpcApi]:
full_nodes, wallets, bt = one_node full_nodes, wallets, bt = one_node
full_node_apis = [full_node_service._api for full_node_service in full_nodes] full_node_apis = [full_node_service._api for full_node_service in full_nodes]
full_node_api = full_node_apis[0] full_node_api: FullNodeSimulator = full_node_apis[0]
full_node_service_1 = full_nodes[0] full_node_service_1 = full_nodes[0]
assert full_node_service_1.rpc_server is not None assert full_node_service_1.rpc_server is not None
client = await FullNodeRpcClient.create( client = await FullNodeRpcClient.create(

View File

@ -12,7 +12,9 @@ import pytest_asyncio
from blspy import G1Element from blspy import G1Element
from chia.farmer.farmer import Farmer from chia.farmer.farmer import Farmer
from chia.farmer.farmer_api import FarmerAPI
from chia.harvester.harvester import Harvester from chia.harvester.harvester import Harvester
from chia.harvester.harvester_api import HarvesterAPI
from chia.plot_sync.delta import Delta, PathListDelta, PlotListDelta from chia.plot_sync.delta import Delta, PathListDelta, PlotListDelta
from chia.plot_sync.receiver import Receiver from chia.plot_sync.receiver import Receiver
from chia.plot_sync.sender import Sender from chia.plot_sync.sender import Sender
@ -108,8 +110,8 @@ class ExpectedResult:
@dataclass @dataclass
class Environment: class Environment:
root_path: Path root_path: Path
harvester_services: List[Service[Harvester]] harvester_services: List[Service[Harvester, HarvesterAPI]]
farmer_service: Service[Farmer] farmer_service: Service[Farmer, FarmerAPI]
harvesters: List[Harvester] harvesters: List[Harvester]
farmer: Farmer farmer: Farmer
dir_1: Directory dir_1: Directory
@ -272,7 +274,10 @@ class Environment:
@pytest_asyncio.fixture(scope="function") @pytest_asyncio.fixture(scope="function")
async def environment( async def environment(
tmp_path: Path, farmer_two_harvester_not_started: Tuple[List[Service[Harvester]], Service[Farmer], BlockTools] tmp_path: Path,
farmer_two_harvester_not_started: Tuple[
List[Service[Harvester, HarvesterAPI]], Service[Farmer, FarmerAPI], BlockTools
],
) -> Environment: ) -> Environment:
def new_test_dir(name: str, plot_list: List[Path]) -> Directory: def new_test_dir(name: str, plot_list: List[Path]) -> Directory:
return Directory(tmp_path / "plots" / name, plot_list) return Directory(tmp_path / "plots" / name, plot_list)
@ -558,7 +563,7 @@ async def test_farmer_restart(environment: Environment) -> None:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_sync_start_and_disconnect_while_sync_is_active( async def test_sync_start_and_disconnect_while_sync_is_active(
farmer_one_harvester: Tuple[List[Service[Harvester]], Service[Farmer], BlockTools] farmer_one_harvester: Tuple[List[Service[Harvester, HarvesterAPI]], Service[Farmer, FarmerAPI], BlockTools]
) -> None: ) -> None:
harvesters, farmer_service, _ = farmer_one_harvester harvesters, farmer_service, _ = farmer_one_harvester
harvester_service = harvesters[0] harvester_service = harvesters[0]

View File

@ -14,7 +14,9 @@ import pytest
from blspy import G1Element from blspy import G1Element
from chia.farmer.farmer import Farmer from chia.farmer.farmer import Farmer
from chia.farmer.farmer_api import FarmerAPI
from chia.harvester.harvester import Harvester from chia.harvester.harvester import Harvester
from chia.harvester.harvester_api import HarvesterAPI
from chia.plot_sync.receiver import Receiver from chia.plot_sync.receiver import Receiver
from chia.plot_sync.sender import Sender from chia.plot_sync.sender import Sender
from chia.plot_sync.util import Constants from chia.plot_sync.util import Constants
@ -239,8 +241,8 @@ async def _testable_process(
async def create_test_runner( async def create_test_runner(
harvester_services: List[Service[Harvester]], harvester_services: List[Service[Harvester, HarvesterAPI]],
farmer_service: Service[Farmer], farmer_service: Service[Farmer, FarmerAPI],
event_loop: asyncio.events.AbstractEventLoop, event_loop: asyncio.events.AbstractEventLoop,
) -> TestRunner: ) -> TestRunner:
await farmer_service.start() await farmer_service.start()
@ -288,7 +290,9 @@ def create_example_plots(count: int) -> List[PlotInfo]:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_sync_simulated( async def test_sync_simulated(
farmer_three_harvester_not_started: Tuple[List[Service[Harvester]], Service[Farmer], BlockTools], farmer_three_harvester_not_started: Tuple[
List[Service[Harvester, HarvesterAPI]], Service[Farmer, FarmerAPI], BlockTools
],
event_loop: asyncio.events.AbstractEventLoop, event_loop: asyncio.events.AbstractEventLoop,
) -> None: ) -> None:
harvester_services, farmer_service, _ = farmer_three_harvester_not_started harvester_services, farmer_service, _ = farmer_three_harvester_not_started
@ -367,7 +371,9 @@ async def test_sync_simulated(
) )
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_farmer_error_simulation( async def test_farmer_error_simulation(
farmer_one_harvester_not_started: Tuple[List[Service[Harvester]], Service[Farmer], BlockTools], farmer_one_harvester_not_started: Tuple[
List[Service[Harvester, HarvesterAPI]], Service[Farmer, FarmerAPI], BlockTools
],
event_loop: asyncio.events.AbstractEventLoop, event_loop: asyncio.events.AbstractEventLoop,
simulate_error: ErrorSimulation, simulate_error: ErrorSimulation,
) -> None: ) -> None:
@ -392,7 +398,9 @@ async def test_farmer_error_simulation(
@pytest.mark.parametrize("simulate_error", [ErrorSimulation.NonRecoverableError, ErrorSimulation.NotConnected]) @pytest.mark.parametrize("simulate_error", [ErrorSimulation.NonRecoverableError, ErrorSimulation.NotConnected])
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_sync_reset_cases( async def test_sync_reset_cases(
farmer_one_harvester_not_started: Tuple[List[Service[Harvester]], Service[Farmer], BlockTools], farmer_one_harvester_not_started: Tuple[
List[Service[Harvester, HarvesterAPI]], Service[Farmer, FarmerAPI], BlockTools
],
event_loop: asyncio.events.AbstractEventLoop, event_loop: asyncio.events.AbstractEventLoop,
simulate_error: ErrorSimulation, simulate_error: ErrorSimulation,
) -> None: ) -> None:

View File

@ -6,7 +6,9 @@ from secrets import token_bytes
from typing import Optional from typing import Optional
from chia.farmer.farmer import Farmer from chia.farmer.farmer import Farmer
from chia.farmer.farmer_api import FarmerAPI
from chia.harvester.harvester import Harvester from chia.harvester.harvester import Harvester
from chia.harvester.harvester_api import HarvesterAPI
from chia.plot_sync.sender import Sender from chia.plot_sync.sender import Sender
from chia.protocols.harvester_protocol import PlotSyncIdentifier from chia.protocols.harvester_protocol import PlotSyncIdentifier
from chia.server.outbound_message import Message, NodeType from chia.server.outbound_message import Message, NodeType
@ -36,7 +38,9 @@ def plot_sync_identifier(current_sync_id: uint64, message_id: uint64) -> PlotSyn
return PlotSyncIdentifier(uint64(int(time.time())), current_sync_id, message_id) return PlotSyncIdentifier(uint64(int(time.time())), current_sync_id, message_id)
async def start_harvester_service(harvester_service: Service[Harvester], farmer_service: Service[Farmer]) -> Harvester: async def start_harvester_service(
harvester_service: Service[Harvester, HarvesterAPI], farmer_service: Service[Farmer, FarmerAPI]
) -> Harvester:
# Set the `last_refresh_time` of the plot manager to avoid initial plot loading # Set the `last_refresh_time` of the plot manager to avoid initial plot loading
harvester: Harvester = harvester_service._node harvester: Harvester = harvester_service._node
harvester.plot_manager.last_refresh_time = time.time() harvester.plot_manager.last_refresh_time = time.time()

View File

@ -36,6 +36,7 @@ from chia.wallet.derive_keys import find_authentication_sk, find_owner_sk
from chia.wallet.transaction_record import TransactionRecord from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.util.wallet_types import WalletType from chia.wallet.util.wallet_types import WalletType
from chia.wallet.wallet_node import WalletNode from chia.wallet.wallet_node import WalletNode
from chia.wallet.wallet_node_api import WalletNodeAPI
# TODO: Compare deducted fees in all tests against reported total_fee # TODO: Compare deducted fees in all tests against reported total_fee
@ -136,7 +137,9 @@ Setup = Tuple[FullNodeSimulator, WalletNode, bytes32, int, WalletRpcClient]
@pytest_asyncio.fixture(scope="function") @pytest_asyncio.fixture(scope="function")
async def setup( async def setup(
one_wallet_and_one_simulator_services: Tuple[List[Service[FullNode]], List[Service[WalletNode]], BlockTools], one_wallet_and_one_simulator_services: Tuple[
List[Service[FullNode, FullNodeSimulator]], List[Service[WalletNode, WalletNodeAPI]], BlockTools
],
trusted: bool, trusted: bool,
self_hostname: str, self_hostname: str,
) -> AsyncIterator[Setup]: ) -> AsyncIterator[Setup]: