mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-08-28 10:05:05 -05:00
* Enable PEP604 Ruff rules * Fix harcoded signature in test * Hack CLVMStreamable test with note to fast follow
372 lines
14 KiB
Python
372 lines
14 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import shutil
|
|
import sqlite3
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import yaml
|
|
|
|
from chia.cmds.configure import configure
|
|
from chia.ssl.create_ssl import create_all_ssl
|
|
from chia.ssl.ssl_check import (
|
|
DEFAULT_PERMISSIONS_CERT_FILE,
|
|
DEFAULT_PERMISSIONS_KEY_FILE,
|
|
RESTRICT_MASK_CERT_FILE,
|
|
RESTRICT_MASK_KEY_FILE,
|
|
check_and_fix_permissions_for_ssl_file,
|
|
fix_ssl,
|
|
)
|
|
from chia.util.bech32m import encode_puzzle_hash
|
|
from chia.util.config import (
|
|
create_default_chia_config,
|
|
initial_config_file,
|
|
load_config,
|
|
lock_and_load_config,
|
|
save_config,
|
|
unflatten_properties,
|
|
)
|
|
from chia.util.db_version import set_db_version
|
|
from chia.util.keychain import Keychain
|
|
from chia.util.path import path_from_root
|
|
from chia.wallet.derive_keys import (
|
|
_derive_path,
|
|
_derive_path_unhardened,
|
|
master_sk_to_pool_sk,
|
|
master_sk_to_wallet_sk_intermediate,
|
|
master_sk_to_wallet_sk_unhardened_intermediate,
|
|
)
|
|
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import puzzle_hash_for_pk
|
|
|
|
|
|
def dict_add_new_default(updated: dict[str, Any], default: dict[str, Any], do_not_migrate_keys: dict[str, Any]) -> None:
|
|
for k, v in do_not_migrate_keys.items():
|
|
if k in updated and v == "":
|
|
updated.pop(k)
|
|
for k, v in default.items():
|
|
ignore = False
|
|
if k in do_not_migrate_keys:
|
|
do_not_data = do_not_migrate_keys[k]
|
|
if isinstance(do_not_data, dict):
|
|
ignore = False
|
|
else:
|
|
ignore = True
|
|
if isinstance(v, dict) and k in updated and ignore is False:
|
|
# If there is an intermediate key with empty string value, do not migrate all descendants
|
|
if do_not_migrate_keys.get(k, None) == "":
|
|
do_not_migrate_keys[k] = v
|
|
dict_add_new_default(updated[k], v, do_not_migrate_keys.get(k, {}))
|
|
elif k not in updated or ignore is True:
|
|
updated[k] = v
|
|
|
|
|
|
def check_keys(new_root: Path, keychain: Keychain | None = None) -> None:
|
|
if keychain is None:
|
|
keychain = Keychain()
|
|
all_sks = keychain.get_all_private_keys()
|
|
if len(all_sks) == 0:
|
|
print("No keys are present in the keychain. Generate them with 'chia keys generate'")
|
|
return None
|
|
|
|
with lock_and_load_config(new_root, "config.yaml") as config:
|
|
pool_child_pubkeys = [master_sk_to_pool_sk(sk).get_g1() for sk, _ in all_sks]
|
|
all_targets = []
|
|
stop_searching_for_farmer = "xch_target_address" not in config["farmer"]
|
|
stop_searching_for_pool = "xch_target_address" not in config["pool"]
|
|
number_of_ph_to_search = 50
|
|
selected = config["selected_network"]
|
|
prefix = config["network_overrides"]["config"][selected]["address_prefix"]
|
|
|
|
intermediates = {}
|
|
for sk, _ in all_sks:
|
|
intermediates[bytes(sk)] = {
|
|
"observer": master_sk_to_wallet_sk_unhardened_intermediate(sk),
|
|
"non-observer": master_sk_to_wallet_sk_intermediate(sk),
|
|
}
|
|
|
|
for i in range(number_of_ph_to_search):
|
|
if stop_searching_for_farmer and stop_searching_for_pool and i > 0:
|
|
break
|
|
for sk, _ in all_sks:
|
|
intermediate_n = intermediates[bytes(sk)]["non-observer"]
|
|
intermediate_o = intermediates[bytes(sk)]["observer"]
|
|
|
|
all_targets.append(
|
|
encode_puzzle_hash(
|
|
puzzle_hash_for_pk(_derive_path_unhardened(intermediate_o, [i]).get_g1()), prefix
|
|
)
|
|
)
|
|
all_targets.append(
|
|
encode_puzzle_hash(puzzle_hash_for_pk(_derive_path(intermediate_n, [i]).get_g1()), prefix)
|
|
)
|
|
if all_targets[-1] == config["farmer"].get("xch_target_address") or all_targets[-2] == config[
|
|
"farmer"
|
|
].get("xch_target_address"):
|
|
stop_searching_for_farmer = True
|
|
if all_targets[-1] == config["pool"].get("xch_target_address") or all_targets[-2] == config["pool"].get(
|
|
"xch_target_address"
|
|
):
|
|
stop_searching_for_pool = True
|
|
|
|
# Set the destinations, if necessary
|
|
updated_target: bool = False
|
|
if "xch_target_address" not in config["farmer"]:
|
|
print(
|
|
f"Setting the xch destination for the farmer reward (1/8 plus fees, solo and pooling)"
|
|
f" to {all_targets[0]}"
|
|
)
|
|
config["farmer"]["xch_target_address"] = all_targets[0]
|
|
updated_target = True
|
|
elif config["farmer"]["xch_target_address"] not in all_targets:
|
|
print(
|
|
f"WARNING: using a farmer address which we might not have the private"
|
|
f" keys for. We searched the first {number_of_ph_to_search} addresses. Consider overriding "
|
|
f"{config['farmer']['xch_target_address']} with {all_targets[0]}"
|
|
)
|
|
|
|
if "pool" not in config:
|
|
config["pool"] = {}
|
|
if "xch_target_address" not in config["pool"]:
|
|
print(f"Setting the xch destination address for pool reward (7/8 for solo only) to {all_targets[0]}")
|
|
config["pool"]["xch_target_address"] = all_targets[0]
|
|
updated_target = True
|
|
elif config["pool"]["xch_target_address"] not in all_targets:
|
|
print(
|
|
f"WARNING: using a pool address which we might not have the private"
|
|
f" keys for. We searched the first {number_of_ph_to_search} addresses. Consider overriding "
|
|
f"{config['pool']['xch_target_address']} with {all_targets[0]}"
|
|
)
|
|
if updated_target:
|
|
print(
|
|
f"To change the XCH destination addresses, edit the `xch_target_address` entries in"
|
|
f" {(new_root / 'config' / 'config.yaml').absolute()}."
|
|
)
|
|
|
|
# Set the pool pks in the farmer
|
|
pool_pubkeys_hex = {bytes(pk).hex() for pk in pool_child_pubkeys}
|
|
if "pool_public_keys" in config["farmer"]:
|
|
for pk_hex in config["farmer"]["pool_public_keys"]:
|
|
# Add original ones in config
|
|
pool_pubkeys_hex.add(pk_hex)
|
|
|
|
config["farmer"]["pool_public_keys"] = pool_pubkeys_hex
|
|
save_config(new_root, "config.yaml", config)
|
|
|
|
|
|
def copy_files_rec(old_path: Path, new_path: Path) -> None:
|
|
if old_path.is_file():
|
|
print(f"{new_path}")
|
|
new_path.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy(old_path, new_path)
|
|
elif old_path.is_dir():
|
|
for old_path_child in old_path.iterdir():
|
|
new_path_child = new_path / old_path_child.name
|
|
copy_files_rec(old_path_child, new_path_child)
|
|
|
|
|
|
def migrate_from(
|
|
old_root: Path,
|
|
new_root: Path,
|
|
manifest: list[str],
|
|
do_not_migrate_settings: list[str],
|
|
) -> int:
|
|
"""
|
|
Copy all the files in "manifest" to the new config directory.
|
|
"""
|
|
if old_root == new_root:
|
|
print("same as new path, exiting")
|
|
return 1
|
|
if not old_root.is_dir():
|
|
print(f"{old_root} not found - this is ok if you did not install this version")
|
|
return 0
|
|
print(f"\n{old_root} found")
|
|
print(f"Copying files from {old_root} to {new_root}\n")
|
|
|
|
for f in manifest:
|
|
old_path = old_root / f
|
|
new_path = new_root / f
|
|
copy_files_rec(old_path, new_path)
|
|
|
|
# update config yaml with new keys
|
|
|
|
with lock_and_load_config(new_root, "config.yaml") as config:
|
|
config_str: str = initial_config_file("config.yaml")
|
|
default_config: dict[str, Any] = yaml.safe_load(config_str)
|
|
flattened_keys = unflatten_properties({k: "" for k in do_not_migrate_settings})
|
|
dict_add_new_default(config, default_config, flattened_keys)
|
|
|
|
save_config(new_root, "config.yaml", config)
|
|
|
|
create_all_ssl(new_root)
|
|
|
|
return 1
|
|
|
|
|
|
def copy_cert_files(cert_path: Path, new_path: Path) -> None:
|
|
for old_path_child in cert_path.glob("*.crt"):
|
|
new_path_child = new_path / old_path_child.name
|
|
copy_files_rec(old_path_child, new_path_child)
|
|
check_and_fix_permissions_for_ssl_file(new_path_child, RESTRICT_MASK_CERT_FILE, DEFAULT_PERMISSIONS_CERT_FILE)
|
|
|
|
for old_path_child in cert_path.glob("*.key"):
|
|
new_path_child = new_path / old_path_child.name
|
|
copy_files_rec(old_path_child, new_path_child)
|
|
check_and_fix_permissions_for_ssl_file(new_path_child, RESTRICT_MASK_KEY_FILE, DEFAULT_PERMISSIONS_KEY_FILE)
|
|
|
|
|
|
def init(
|
|
create_certs: Path | None,
|
|
root_path: Path,
|
|
fix_ssl_permissions: bool = False,
|
|
testnet: bool = False,
|
|
v1_db: bool = False,
|
|
) -> int | None:
|
|
if create_certs is not None:
|
|
if root_path.exists():
|
|
if os.path.isdir(create_certs):
|
|
ca_dir: Path = root_path / "config/ssl/ca"
|
|
if ca_dir.exists():
|
|
print(f"Deleting your OLD CA in {ca_dir}")
|
|
shutil.rmtree(ca_dir)
|
|
print(f"Copying your CA from {create_certs} to {ca_dir}")
|
|
copy_cert_files(create_certs, ca_dir)
|
|
create_all_ssl(root_path)
|
|
else:
|
|
print(f"** Directory {create_certs} does not exist **")
|
|
else:
|
|
print(f"** {root_path} does not exist. Executing core init **")
|
|
# sanity check here to prevent infinite recursion
|
|
if (
|
|
chia_init(
|
|
root_path,
|
|
fix_ssl_permissions=fix_ssl_permissions,
|
|
testnet=testnet,
|
|
v1_db=v1_db,
|
|
)
|
|
== 0
|
|
and root_path.exists()
|
|
):
|
|
return init(create_certs, root_path, fix_ssl_permissions)
|
|
|
|
print(f"** {root_path} was not created. Exiting **")
|
|
return -1
|
|
else:
|
|
return chia_init(root_path, fix_ssl_permissions=fix_ssl_permissions, testnet=testnet, v1_db=v1_db)
|
|
|
|
return None
|
|
|
|
|
|
def chia_init(
|
|
root_path: Path,
|
|
*,
|
|
should_check_keys: bool = True,
|
|
fix_ssl_permissions: bool = False,
|
|
testnet: bool = False,
|
|
v1_db: bool = False,
|
|
) -> int:
|
|
"""
|
|
Standard first run initialization or migration steps. Handles config creation,
|
|
generation of SSL certs, and setting target addresses (via check_keys).
|
|
|
|
should_check_keys can be set to False to avoid blocking when accessing a passphrase
|
|
protected Keychain. When launching the daemon from the GUI, we want the GUI to
|
|
handle unlocking the keychain.
|
|
"""
|
|
chia_root = os.environ.get("CHIA_ROOT", None)
|
|
if chia_root is not None:
|
|
print(f"CHIA_ROOT is set to {chia_root}")
|
|
|
|
print(f"Chia directory {root_path}")
|
|
if root_path.is_dir() and Path(root_path / "config" / "config.yaml").exists():
|
|
# This is reached if CHIA_ROOT is set, or if user has run chia init twice
|
|
# before a new update.
|
|
if testnet:
|
|
configure(
|
|
root_path,
|
|
set_farmer_peer="",
|
|
set_node_introducer="",
|
|
set_fullnode_port="",
|
|
set_harvester_port="",
|
|
set_log_level="",
|
|
enable_upnp="",
|
|
set_outbound_peer_count="",
|
|
set_peer_count="",
|
|
testnet="true",
|
|
peer_connect_timeout="",
|
|
crawler_db_path="",
|
|
crawler_minimum_version_count=None,
|
|
seeder_domain_name="",
|
|
seeder_nameserver="",
|
|
set_solver_peer="",
|
|
set_solver_trusted_peers_only="",
|
|
)
|
|
if fix_ssl_permissions:
|
|
fix_ssl(root_path)
|
|
if should_check_keys:
|
|
check_keys(root_path)
|
|
print(f"{root_path} already exists, no migration action taken")
|
|
return -1
|
|
|
|
create_default_chia_config(root_path)
|
|
if testnet:
|
|
configure(
|
|
root_path,
|
|
set_farmer_peer="",
|
|
set_node_introducer="",
|
|
set_fullnode_port="",
|
|
set_harvester_port="",
|
|
set_log_level="",
|
|
enable_upnp="",
|
|
set_outbound_peer_count="",
|
|
set_peer_count="",
|
|
testnet="true",
|
|
peer_connect_timeout="",
|
|
crawler_db_path="",
|
|
crawler_minimum_version_count=None,
|
|
seeder_domain_name="",
|
|
seeder_nameserver="",
|
|
set_solver_peer="",
|
|
set_solver_trusted_peers_only="",
|
|
)
|
|
create_all_ssl(root_path)
|
|
if fix_ssl_permissions:
|
|
fix_ssl(root_path)
|
|
if should_check_keys:
|
|
check_keys(root_path)
|
|
|
|
config: dict[str, Any]
|
|
|
|
db_path_replaced: str
|
|
if v1_db:
|
|
with lock_and_load_config(root_path, "config.yaml") as config:
|
|
db_pattern = config["full_node"]["database_path"]
|
|
new_db_path = db_pattern.replace("_v2_", "_v1_")
|
|
config["full_node"]["database_path"] = new_db_path
|
|
db_path_replaced = new_db_path.replace("CHALLENGE", config["selected_network"])
|
|
db_path = path_from_root(root_path, db_path_replaced)
|
|
|
|
db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
with sqlite3.connect(db_path) as connection:
|
|
set_db_version(connection, 1)
|
|
|
|
save_config(root_path, "config.yaml", config)
|
|
|
|
else:
|
|
config = load_config(root_path, "config.yaml")["full_node"]
|
|
db_path_replaced = config["database_path"].replace("CHALLENGE", config["selected_network"])
|
|
db_path = path_from_root(root_path, db_path_replaced)
|
|
db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
try:
|
|
# create new v2 db file
|
|
with sqlite3.connect(db_path) as connection:
|
|
set_db_version(connection, 2)
|
|
except sqlite3.OperationalError:
|
|
# db already exists, so we're good
|
|
pass
|
|
|
|
print("")
|
|
print("To see your keys, run 'chia keys show --show-mnemonic-seed'")
|
|
|
|
return 0
|