Skip to content

Commit

Permalink
Add ConfigPatcher
Browse files Browse the repository at this point in the history
  • Loading branch information
felixbrucker committed Nov 4, 2023
1 parent 15727d7 commit 6579889
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 160 deletions.
88 changes: 88 additions & 0 deletions foxy_farmer/foundation/config/config_patcher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from dataclasses import dataclass
from typing import Dict, Any, List, Optional, Union

from chia.util.bech32m import decode_puzzle_hash
from typing_extensions import Self

from foxy_farmer.foundation.util.dictionary import get_nested_dict_value, set_nested_dict_value, del_nested_dict_value


@dataclass
class ConfigPatcherResult:
foxy_farmer_config_was_updated: bool
chia_config_was_updated: bool


class ConfigPatcher:
_foxy_farmer_config: Dict[str, Any]
_chia_config: Dict[str, Any]
_is_chia_config_updated: bool = False
_is_foxy_farmer_config_updated: bool = False

def __init__(self, foxy_farmer_config: Dict[str, Any], chia_config: Dict[str, Any]):
self._foxy_farmer_config = foxy_farmer_config
self._chia_config = chia_config

def patch(
self,
foxy_farmer_config_key_path: Union[List[str], str],
chia_config_key_path: Optional[Union[List[str], str]] = None
) -> Self:
if chia_config_key_path is None:
chia_config_key_path = foxy_farmer_config_key_path

resolved_foxy_farmer_config_key_path: List[str] = foxy_farmer_config_key_path if isinstance(foxy_farmer_config_key_path, list) else foxy_farmer_config_key_path.split(".")
resolved_chia_config_key_path: List[str] = chia_config_key_path if isinstance(chia_config_key_path, list) else chia_config_key_path.split(".")

foxy_farmer_config_value = get_nested_dict_value(self._foxy_farmer_config, resolved_foxy_farmer_config_key_path)
chia_config_value = get_nested_dict_value(self._chia_config, resolved_chia_config_key_path)

if foxy_farmer_config_value is not None and chia_config_value != foxy_farmer_config_value:
set_nested_dict_value(self._chia_config, resolved_chia_config_key_path, foxy_farmer_config_value)
self._is_chia_config_updated = True

return self

def patch_value(self, chia_config_key_path: Union[List[str], str], value: Any) -> Self:
resolved_chia_config_key_path: List[str] = chia_config_key_path if isinstance(chia_config_key_path, list) else chia_config_key_path.split(".")
chia_config_value = get_nested_dict_value(self._chia_config, resolved_chia_config_key_path)

if chia_config_value != value:
set_nested_dict_value(self._chia_config, resolved_chia_config_key_path, value)
self._is_chia_config_updated = True

return self

def remove_config_key(self, chia_config_key_path: Union[List[str], str]) -> Self:
resolved_chia_config_key_path: List[str] = chia_config_key_path if isinstance(chia_config_key_path, list) else chia_config_key_path.split(".")
chia_config_value = get_nested_dict_value(self._chia_config, resolved_chia_config_key_path)

if chia_config_value is not None:
del_nested_dict_value(self._chia_config, resolved_chia_config_key_path)
self._is_chia_config_updated = True

return self

def sync_pool_payout_address(self) -> Self:
pool_payout_address_ph = decode_puzzle_hash(self._foxy_farmer_config["pool_payout_address"]).hex()
if self._foxy_farmer_config.get("plot_nfts") is not None:
for pool in self._foxy_farmer_config["plot_nfts"]:
if pool["payout_instructions"] != pool_payout_address_ph:
pool["payout_instructions"] = pool_payout_address_ph
self._is_foxy_farmer_config_updated = True

self.patch("plot_nfts", "pool.pool_list")

if self._chia_config["pool"].get("pool_list") is not None:
for pool in self._chia_config["pool"]["pool_list"]:
if pool["payout_instructions"] != pool_payout_address_ph:
pool["payout_instructions"] = pool_payout_address_ph
self._is_chia_config_updated = True

return self

def get_result(self) -> ConfigPatcherResult:
return ConfigPatcherResult(
foxy_farmer_config_was_updated=self._is_foxy_farmer_config_updated,
chia_config_was_updated=self._is_chia_config_updated
)
23 changes: 0 additions & 23 deletions foxy_farmer/foundation/config/patch_from_foxy_farmer_config.py

This file was deleted.

3 changes: 2 additions & 1 deletion foxy_farmer/foundation/keychain/generate_login_links.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
from chia.wallet.derive_keys import find_authentication_sk

from foxy_farmer.foundation.pool.pool_info import get_pool_info
from foxy_farmer.foundation.util.hex import strip_hex_prefix


async def generate_login_links(keychain_proxy: KeychainProxy, pool_list: List[Dict[str, Any]]) -> List[Tuple[str, str]]:
all_root_sks = [sk for sk, _ in await keychain_proxy.get_all_private_keys()]

login_links: List[Tuple[str, str]] = []
for pool in pool_list:
launcher_id = pool["launcher_id"]
launcher_id = strip_hex_prefix(pool["launcher_id"])
if pool.get("pool_url", "") == "":
# Skip solo PlotNFT
continue
Expand Down
7 changes: 7 additions & 0 deletions foxy_farmer/foundation/util/dictionary.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,10 @@ def get_nested_dict_value(dic: Dict[str, Any], keys: List[str]) -> Optional[Any]
dic = dic.get(key, {})

return dic.get(keys[-1])


def del_nested_dict_value(dic: Dict[str, Any], keys: List[str]):
for key in keys[:-1]:
dic = dic.get(key, {})

dic.pop(keys[-1], None)
4 changes: 4 additions & 0 deletions foxy_farmer/foundation/util/hex.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
def ensure_hex_prefix(string: str) -> str:
return string if string.startswith("0x") else f"0x{string}"


def strip_hex_prefix(string: str) -> str:
return string[2:] if string.startswith("0x") else string
203 changes: 70 additions & 133 deletions foxy_farmer/foxy_chia_config_manager.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from os import environ
from pathlib import Path
from shutil import copyfile
from typing import Dict, Optional, Tuple
from typing import Dict, Any

from chia.cmds.init_funcs import chia_init, check_keys
from chia.cmds.keys_funcs import add_private_key_seed
from chia.util.bech32m import decode_puzzle_hash
from chia.util.config import load_config, save_config
from chia.util.default_root import DEFAULT_ROOT_PATH, DEFAULT_KEYS_ROOT_PATH

from foxy_farmer.foundation.config.config_patcher import ConfigPatcher
from foxy_farmer.foxy_config_manager import FoxyConfigManager


Expand Down Expand Up @@ -70,142 +70,79 @@ def ensure_foxy_config(self, config_path: Path):
print(f"You are missing a 'farmer_reward_address' and/or 'pool_payout_address' in {config_path}, please update the config and run again.")
exit(1)

config_was_updated = self.ensure_different_ports(config, config_was_updated)
config_was_updated = self.ensure_no_full_node_peer_for_farmer(config, config_was_updated)
config_was_updated, foxy_config_was_updated = self.update_foxy_chia_config_from_foxy_config(
chia_foxy_config=config,
foxy_config=foxy_config,
config_was_updated=config_was_updated,
foxy_config_was_updated=foxy_config_was_updated
config_patcher = ConfigPatcher(foxy_farmer_config=foxy_config, chia_config=config)
self.patch_configs(
config_patcher=config_patcher,
chia_config=config,
foxy_farmer_config=foxy_config,
)

config_patcher_result = config_patcher.get_result()
config_was_updated = config_was_updated or config_patcher_result.chia_config_was_updated
foxy_config_was_updated = foxy_config_was_updated or config_patcher_result.foxy_farmer_config_was_updated

if foxy_config_was_updated:
foxy_config_manager.save_config(foxy_config)

if config_was_updated:
save_config(self._root_path, "config.yaml", config)

def update_foxy_chia_config_from_foxy_config(
self,
chia_foxy_config: Dict,
foxy_config: Dict,
config_was_updated: bool = False,
foxy_config_was_updated: bool = False,
) -> Tuple[bool, bool]:
if "full_node_peer" in chia_foxy_config["wallet"] or "full_node_peers" in chia_foxy_config["wallet"]:
chia_foxy_config["wallet"].pop("full_node_peer", None)
chia_foxy_config["wallet"].pop("full_node_peers", None)
config_was_updated = True
if chia_foxy_config["logging"]["log_level"] != foxy_config["log_level"] or chia_foxy_config["logging"]["log_stdout"] is not False:
chia_foxy_config["logging"]["log_level"] = foxy_config["log_level"]
chia_foxy_config["logging"]["log_stdout"] = False
config_was_updated = True
if chia_foxy_config["self_hostname"] != foxy_config["listen_host"]:
chia_foxy_config["self_hostname"] = foxy_config["listen_host"]
config_was_updated = True

def set_service_option_from_foxy_config(service: str, foxy_config_key: str, chia_config_key: Optional[str] = None):
if chia_config_key is None:
chia_config_key = foxy_config_key

nonlocal config_was_updated
if foxy_config.get(foxy_config_key) is not None and chia_foxy_config[service].get(chia_config_key) != foxy_config[foxy_config_key]:
chia_foxy_config[service][chia_config_key] = foxy_config[foxy_config_key]
config_was_updated = True

set_service_option_from_foxy_config("harvester", foxy_config_key="harvester_num_threads", chia_config_key="num_threads")
set_service_option_from_foxy_config("harvester", "plot_directories")
set_service_option_from_foxy_config("harvester", "recursive_plot_scan")
set_service_option_from_foxy_config("harvester", "parallel_decompressor_count")
set_service_option_from_foxy_config("harvester", "decompressor_thread_count")
set_service_option_from_foxy_config("harvester", "disable_cpu_affinity")
set_service_option_from_foxy_config("harvester", "max_compression_level_allowed")
set_service_option_from_foxy_config("harvester", "use_gpu_harvesting")
set_service_option_from_foxy_config("harvester", "gpu_index")
set_service_option_from_foxy_config("harvester", "enforce_gpu_index")
set_service_option_from_foxy_config("harvester", "decompressor_timeout")
if chia_foxy_config["harvester"].get("plots_refresh_parameter") is not None and chia_foxy_config["harvester"]["plots_refresh_parameter"]["interval_seconds"] != foxy_config["plot_refresh_interval_seconds"]:
chia_foxy_config["harvester"]["plots_refresh_parameter"]["interval_seconds"] = foxy_config["plot_refresh_interval_seconds"]
config_was_updated = True

set_service_option_from_foxy_config("farmer", foxy_config_key="farmer_reward_address", chia_config_key="xch_target_address")

# Ensure all PlotNFTs use the same payout address
pool_payout_address_ph = decode_puzzle_hash(foxy_config["pool_payout_address"]).hex()
if foxy_config.get("plot_nfts") is not None:
for pool in foxy_config["plot_nfts"]:
if pool["payout_instructions"] != pool_payout_address_ph:
pool["payout_instructions"] = pool_payout_address_ph
foxy_config_was_updated = True

set_service_option_from_foxy_config("pool", foxy_config_key="plot_nfts", chia_config_key="pool_list")

if chia_foxy_config["pool"].get("pool_list") is not None:
for pool in chia_foxy_config["pool"]["pool_list"]:
if pool["payout_instructions"] != pool_payout_address_ph:
pool["payout_instructions"] = pool_payout_address_ph
config_was_updated = True

# Update og payout address if og pooling is enabled
if foxy_config["enable_og_pooling"] is True and chia_foxy_config["farmer"].get("pool_payout_address") != foxy_config["pool_payout_address"]:
chia_foxy_config["farmer"]["pool_payout_address"] = foxy_config["pool_payout_address"]
config_was_updated = True

# Ensure we explicitly disable og pooling as the og client defaults to og pooling
if chia_foxy_config["farmer"].get("disable_og_pooling") is None or chia_foxy_config["farmer"]["disable_og_pooling"] == foxy_config["enable_og_pooling"]:
chia_foxy_config["farmer"]["disable_og_pooling"] = not foxy_config["enable_og_pooling"]
config_was_updated = True

# Ensure the og reward address is the farmer reward address if not og pooling
if foxy_config["enable_og_pooling"] is False and chia_foxy_config["pool"]["xch_target_address"] != foxy_config["farmer_reward_address"]:
chia_foxy_config["pool"]["xch_target_address"] = foxy_config["farmer_reward_address"]
config_was_updated = True

# Ensure the pool reward address is set to any valid address when og pooling, the og client will auto adjust to
# the correct address on launch
if foxy_config["enable_og_pooling"] is True and chia_foxy_config["pool"]["xch_target_address"] == "":
chia_foxy_config["pool"]["xch_target_address"] = foxy_config["farmer_reward_address"]
config_was_updated = True

# Ensure the wallet syncs with unknown peers
if chia_foxy_config["wallet"].get("connect_to_unknown_peers") is not True:
chia_foxy_config["wallet"]["connect_to_unknown_peers"] = True
config_was_updated = True

return config_was_updated, foxy_config_was_updated

def ensure_no_full_node_peer_for_farmer(self, config: Dict, config_was_updated: bool = False):
# Until peer lists are released manually add peers in the service factory instead
if "full_node_peer" in config["farmer"]:
config["farmer"].pop("full_node_peer", None)
config_was_updated = True

return config_was_updated

def ensure_different_ports(self, config: Dict, config_was_updated: bool = False):
if config["daemon_port"] != 55469:
config["daemon_port"] = 55469
config_was_updated = True
if config["farmer"]["harvester_peer"]["port"] != 18448:
config["farmer"]["harvester_peer"]["port"] = 18448
config_was_updated = True
if config["farmer"]["port"] != 18447:
config["farmer"]["port"] = 18447
config_was_updated = True
if config["farmer"]["rpc_port"] != 18559:
config["farmer"]["rpc_port"] = 18559
config_was_updated = True
if config["harvester"]["farmer_peer"]["port"] != 18447:
config["harvester"]["farmer_peer"]["port"] = 18447
config_was_updated = True
if config["harvester"]["port"] != 18448:
config["harvester"]["port"] = 18448
config_was_updated = True
if config["harvester"]["rpc_port"] != 18560:
config["harvester"]["rpc_port"] = 18560
config_was_updated = True
if config["wallet"]["rpc_port"] != 19256:
config["wallet"]["rpc_port"] = 19256
config_was_updated = True

return config_was_updated
def patch_configs(
self,
config_patcher: ConfigPatcher,
chia_config: Dict[str, Any],
foxy_farmer_config: Dict[str, Any],
):
(config_patcher
# Ensure different ports
.patch_value("daemon_port", foxy_farmer_config.get("chia_daemon_port", 55469))
.patch_value("farmer.port", foxy_farmer_config.get("chia_farmer_port", 18447))
.patch_value("farmer.rpc_port", foxy_farmer_config.get("chia_farmer_rpc_port", 18559))
# Deprecated: harvester port can be dropped with the next chia release
.patch_value("harvester.port", foxy_farmer_config.get("chia_harvester_port", 18448))
.patch_value("harvester.rpc_port", foxy_farmer_config.get("chia_harvester_rpc_port", 18560))
.patch_value("wallet.rpc_port", foxy_farmer_config.get("chia_wallet_rpc_port", 19256))
# Ensure the wallet does not try to connect to localhost
.remove_config_key("wallet.full_node_peer")
.remove_config_key("wallet.full_node_peers")
# Sync logging
.patch("log_level", "logging.log_level")
.patch_value("logging.log_stdout", False)
.patch("listen_host", "self_hostname")
# Sync harvester
.patch("harvester_num_threads", "harvester.num_threads")
.patch("plot_directories", "harvester.plot_directories")
.patch("recursive_plot_scan", "harvester.recursive_plot_scan")
.patch("parallel_decompressor_count", "harvester.parallel_decompressor_count")
.patch("decompressor_thread_count", "harvester.decompressor_thread_count")
.patch("disable_cpu_affinity", "harvester.disable_cpu_affinity")
.patch("max_compression_level_allowed", "harvester.max_compression_level_allowed")
.patch("use_gpu_harvesting", "harvester.use_gpu_harvesting")
.patch("gpu_index", "harvester.gpu_index")
.patch("enforce_gpu_index", "harvester.enforce_gpu_index")
.patch("decompressor_timeout", "harvester.decompressor_timeout")
.patch("plot_refresh_interval_seconds", "harvester.plots_refresh_parameter.interval_seconds")
.patch("plot_refresh_batch_size", "harvester.plots_refresh_parameter.batch_size")
.patch("plot_refresh_batch_sleep_ms", "harvester.plots_refresh_parameter.batch_sleep_milliseconds")
# Sync reward and payout addresses
.patch("farmer_reward_address", "farmer.xch_target_address")
.sync_pool_payout_address()
# Ensure we explicitly disable og pooling as the og client defaults to og pooling
.patch_value("farmer.disable_og_pooling", not foxy_farmer_config["enable_og_pooling"])
# Ensure the wallet syncs with unknown peers
.patch_value("wallet.connect_to_unknown_peers", True)
)

if foxy_farmer_config["enable_og_pooling"] is True:
# Update og payout address
config_patcher.patch("pool_payout_address", "farmer.pool_payout_address")
# Ensure the pool reward address is set to any valid address, the og client will auto adjust to the correct
# address on launch
if chia_config["pool"]["xch_target_address"] == "":
config_patcher.patch("farmer_reward_address", "pool.xch_target_address")
else:
# Ensure the og reward address is the farmer reward address
config_patcher.patch("farmer_reward_address", "pool.xch_target_address")

# Deprecated: Update to set list of peers with next chia release. Drop adding peers in service factory.
config_patcher.remove_config_key("farmer.full_node_peer")
Loading

0 comments on commit 6579889

Please sign in to comment.