From e34fe3539725b38bb01b3bdfe9a75c0d91d7b1b1 Mon Sep 17 00:00:00 2001 From: IzioDev <9900846+IzioDev@users.noreply.github.com> Date: Mon, 5 Jan 2026 22:32:58 +0100 Subject: [PATCH 01/11] chore(tn-12): add tn-12 support --- src/config.rs | 12 +++++++++++- src/kaspad.rs | 6 +++--- src/network.rs | 4 ++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/config.rs b/src/config.rs index 829eef5..096b21f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,8 +24,18 @@ impl Config { .with_local_interface(8989); let origin = Origin::try_new("https://github.com/aspectron/rusty-kaspa", Some("pnn-v1"))?; + let covpp_origin = + Origin::try_new("https://github.com/kaspanet/rusty-kaspa", Some("covpp"))?; + let kaspad = Network::into_iter() - .map(|network| kaspad::Config::new(origin.clone(), network)) + .map(|network| { + let selected_origin = match network { + Network::Mainnet => origin, + Network::Testnet10 => origin, + Network::Testnet12 => covpp_origin, + }; + kaspad::Config::new(selected_origin.clone(), network) + }) .collect::>(); let nginx = nginx::Config::default(); diff --git a/src/kaspad.rs b/src/kaspad.rs index 2426603..fd1b475 100644 --- a/src/kaspad.rs +++ b/src/kaspad.rs @@ -81,7 +81,7 @@ impl Config { let (grpc, wrpc_borsh, wrpc_json) = match network { Network::Mainnet => (16110, 17110, 18110), Network::Testnet10 => (16210, 17210, 18210), - Network::Testnet11 => (16310, 17310, 18310), + Network::Testnet12 => (16311, 17210, 18210), }; Self { @@ -143,9 +143,9 @@ impl From<&Config> for Vec { args.push("--testnet"); args.push("--netsuffix=10"); } - Network::Testnet11 => { + Network::Testnet12 => { args.push("--testnet"); - args.push("--netsuffix=11"); + args.push("--netsuffix=12"); } } diff --git a/src/network.rs b/src/network.rs index 0f29af8..2a7243b 100644 --- a/src/network.rs +++ b/src/network.rs @@ -32,7 +32,7 @@ pub enum Network { #[default] Mainnet, Testnet10, - Testnet11, + Testnet12, } impl Display for Network { @@ -40,7 +40,7 @@ impl Display for Network { match self { Network::Mainnet => write!(f, "mainnet"), Network::Testnet10 => write!(f, "testnet-10"), - Network::Testnet11 => write!(f, "testnet-11"), + Network::Testnet12 => write!(f, "testnet-12"), } } } From 850a0f28d34e298dbd3bcd3e94901c6303085f11 Mon Sep 17 00:00:00 2001 From: IzioDev <9900846+IzioDev@users.noreply.github.com> Date: Mon, 5 Jan 2026 23:07:26 +0100 Subject: [PATCH 02/11] chore: support testnet-12 --- .gitignore | 1 + Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/config.rs | 6 +++--- src/git.rs | 3 +++ 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 91dbade..25c5a81 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /fail +.khost-data diff --git a/Cargo.lock b/Cargo.lock index 5bbecb9..29192c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr" @@ -1465,7 +1465,7 @@ dependencies = [ [[package]] name = "khost" -version = "0.4.0" +version = "0.5.0" dependencies = [ "addr", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 555cf70..cdd923d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "khost" -version = "0.4.0" +version = "0.5.0" edition = "2021" authors = ["Kaspa developers"] license = "MIT OR Apache-2.0" diff --git a/src/config.rs b/src/config.rs index 096b21f..0574abb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -30,9 +30,9 @@ impl Config { let kaspad = Network::into_iter() .map(|network| { let selected_origin = match network { - Network::Mainnet => origin, - Network::Testnet10 => origin, - Network::Testnet12 => covpp_origin, + Network::Mainnet => origin.clone(), + Network::Testnet10 => origin.clone(), + Network::Testnet12 => covpp_origin.clone(), }; kaspad::Config::new(selected_origin.clone(), network) }) diff --git a/src/git.rs b/src/git.rs index 1f909d5..5fc5cbf 100644 --- a/src/git.rs +++ b/src/git.rs @@ -180,12 +180,14 @@ where enum Preset { PNNv1, // Delta, + Covpp, Custom, } let preset = if name == "rusty-kaspa" { cliclack::select(format!("Select git origin for '{name}':")) .item(Preset::PNNv1, "pnn-v1 (aspectron/pnn-v1)", "") + .item(Preset::Covpp, "covpp (kaspanet/covpp)", "") // .item( // Preset::Delta, // "Delta", @@ -201,6 +203,7 @@ where Preset::PNNv1 => { Origin::try_new("https://github.com/aspectron/rusty-kaspa", Some("pnn-v1"))? } + Preset::Covpp => Origin::try_new("https://github.com/kaspanet/rusty-kaspa", Some("covpp"))?, // Preset::Delta => { // Origin::try_new("https://github.com/aspectron/rusty-kaspa", Some("delta"))? // } From 291b4664ddad679642382caa0ebe47e132e2c14d Mon Sep 17 00:00:00 2001 From: demisrael Date: Sat, 2 May 2026 14:36:36 +0300 Subject: [PATCH 03/11] fix tn-12 branch + ports, drop redundant clone, remove .khost-data ignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src/kaspad.rs: tn-12 wrpc-borsh/json ports collided with tn-10 (17210/18210). Use (16312, 17312, 18312) following the netsuffix-numbered pattern. - Use the official `tn12` branch on kaspanet/rusty-kaspa (the `covpp` branch has been removed). Rename Preset::Covpp -> Preset::Tn12 in src/git.rs for consistency. - src/config.rs: drop redundant `.clone()` on selected_origin (match arms already produce owned values). - .gitignore: remove `.khost-data` — code uses `.khost` / `00-khost-dev` per folders.rs. --- .gitignore | 1 - src/config.rs | 8 ++++---- src/git.rs | 6 +++--- src/kaspad.rs | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 25c5a81..91dbade 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /target /fail -.khost-data diff --git a/src/config.rs b/src/config.rs index 0574abb..bd84e50 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,17 +24,17 @@ impl Config { .with_local_interface(8989); let origin = Origin::try_new("https://github.com/aspectron/rusty-kaspa", Some("pnn-v1"))?; - let covpp_origin = - Origin::try_new("https://github.com/kaspanet/rusty-kaspa", Some("covpp"))?; + let tn12_origin = + Origin::try_new("https://github.com/kaspanet/rusty-kaspa", Some("tn12"))?; let kaspad = Network::into_iter() .map(|network| { let selected_origin = match network { Network::Mainnet => origin.clone(), Network::Testnet10 => origin.clone(), - Network::Testnet12 => covpp_origin.clone(), + Network::Testnet12 => tn12_origin.clone(), }; - kaspad::Config::new(selected_origin.clone(), network) + kaspad::Config::new(selected_origin, network) }) .collect::>(); diff --git a/src/git.rs b/src/git.rs index 5fc5cbf..fd68a5d 100644 --- a/src/git.rs +++ b/src/git.rs @@ -180,14 +180,14 @@ where enum Preset { PNNv1, // Delta, - Covpp, + Tn12, Custom, } let preset = if name == "rusty-kaspa" { cliclack::select(format!("Select git origin for '{name}':")) .item(Preset::PNNv1, "pnn-v1 (aspectron/pnn-v1)", "") - .item(Preset::Covpp, "covpp (kaspanet/covpp)", "") + .item(Preset::Tn12, "tn12 (kaspanet/tn12)", "") // .item( // Preset::Delta, // "Delta", @@ -203,7 +203,7 @@ where Preset::PNNv1 => { Origin::try_new("https://github.com/aspectron/rusty-kaspa", Some("pnn-v1"))? } - Preset::Covpp => Origin::try_new("https://github.com/kaspanet/rusty-kaspa", Some("covpp"))?, + Preset::Tn12 => Origin::try_new("https://github.com/kaspanet/rusty-kaspa", Some("tn12"))?, // Preset::Delta => { // Origin::try_new("https://github.com/aspectron/rusty-kaspa", Some("delta"))? // } diff --git a/src/kaspad.rs b/src/kaspad.rs index fd1b475..9ca7614 100644 --- a/src/kaspad.rs +++ b/src/kaspad.rs @@ -81,7 +81,7 @@ impl Config { let (grpc, wrpc_borsh, wrpc_json) = match network { Network::Mainnet => (16110, 17110, 18110), Network::Testnet10 => (16210, 17210, 18210), - Network::Testnet12 => (16311, 17210, 18210), + Network::Testnet12 => (16312, 17312, 18312), }; Self { From b023055439ca7d4ee4cd30c4a84e5da97aee8bb2 Mon Sep 17 00:00:00 2001 From: Romain Billot Date: Tue, 5 May 2026 08:31:08 +0200 Subject: [PATCH 04/11] chore: config v3 migration * wip * wip: legacy network handling * wip * wip * wip * wip --------- Co-authored-by: IzioDev <9900846+IzioDev@users.noreply.github.com> --- src/actions/advanced.rs | 7 +- src/actions/bootstrap.rs | 2 +- src/config.rs | 99 ++++++++++++++++++++++----- src/imports.rs | 2 +- src/kaspad.rs | 142 ++++++++++++++++++++++++++------------- src/main.rs | 2 + src/network.rs | 74 ++++++++++++++++++-- 7 files changed, 258 insertions(+), 70 deletions(-) diff --git a/src/actions/advanced.rs b/src/actions/advanced.rs index 2e3fb40..40aafe0 100644 --- a/src/actions/advanced.rs +++ b/src/actions/advanced.rs @@ -84,7 +84,12 @@ impl Action for Advanced { match selector.interact() { Ok(BranchChange::Kaspad) => { let origin = git::create_origin("rusty-kaspa")?; - for config in ctx.config.kaspad.iter_mut() { + for config in ctx + .config + .kaspad + .iter_mut() + .filter(|config| config.network().is_supported()) + { config.set_origin(origin.clone()); } ctx.config.save()?; diff --git a/src/actions/bootstrap.rs b/src/actions/bootstrap.rs index d2408eb..178dec4 100644 --- a/src/actions/bootstrap.rs +++ b/src/actions/bootstrap.rs @@ -24,7 +24,7 @@ impl Action for Bootstrap { resolver::init_resolver_config(ctx).ok(); } - kaspad::select_networks(ctx)?; + kaspad::select_supported_networks(ctx)?; base::install(ctx, false)?; ctx.config.bootstrap = true; diff --git a/src/config.rs b/src/config.rs index bd84e50..c975d46 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,6 @@ use crate::imports::*; -const CONFIG_VERSION: u64 = 2; +const CONFIG_VERSION: u64 = 3; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { @@ -18,23 +18,28 @@ pub struct Config { impl Config { pub fn try_new() -> Result { - let origin = Origin::try_new("https://github.com/aspectron/kaspa-resolver", None)?; + let origin = Origin::try_new( + // TODO(izio): change before merge + "https://github.com/iziodev/kaspa-resolver", + Some("tmp/tn12"), + )?; let resolver = resolver::Config::new(origin) .with_stats() .with_local_interface(8989); - let origin = Origin::try_new("https://github.com/aspectron/rusty-kaspa", Some("pnn-v1"))?; - let tn12_origin = - Origin::try_new("https://github.com/kaspanet/rusty-kaspa", Some("tn12"))?; + let master_origin = Self::master_origin()?; + let tn12_origin = Self::tn12_origin()?; - let kaspad = Network::into_iter() + let kaspad = SupportedNetwork::iter() + .copied() .map(|network| { let selected_origin = match network { - Network::Mainnet => origin.clone(), - Network::Testnet10 => origin.clone(), - Network::Testnet12 => tn12_origin.clone(), + SupportedNetwork::Mainnet | SupportedNetwork::Testnet10 => { + master_origin.clone() + } + SupportedNetwork::Testnet12 => tn12_origin.clone(), }; - kaspad::Config::new(selected_origin, network) + kaspad::Config::new(selected_origin, network.into()) }) .collect::>(); @@ -52,6 +57,14 @@ impl Config { resolver, }) } + + fn master_origin() -> Result { + Origin::try_new("https://github.com/kaspanet/rusty-kaspa", Some("master")) + } + + fn tn12_origin() -> Result { + Origin::try_new("https://github.com/kaspanet/rusty-kaspa", Some("tn12")) + } } impl Config { @@ -61,26 +74,64 @@ impl Config { return Err(Error::custom("Config file not found")); } let mut config: Config = serde_json::from_str(&fs::read_to_string(config_path)?)?; + let last_version = config.version; let mut update = false; - // Migrate old config - if config.version == 1 { - config.kaspad.iter_mut().for_each(|config| { - if let Some(branch) = config.origin_mut().branch_mut() { - if branch == "omega" { - *branch = "pnn-v1".to_string(); - update = true; + // v1 -> v2 + if last_version <= 1 { + config.kaspad.iter_mut().for_each(|kaspad_config| { + if kaspad_config.network().is_supported() { + if let Some(branch) = kaspad_config.origin_mut().branch_mut() { + if branch == "omega" { + *branch = "pnn-v1".to_string(); + update = true; + } } } }); } + // v2 -> v3 + if last_version <= 2 { + // push tn12 + if !config + .kaspad + .iter() + .any(|config| config.network() == SupportedNetwork::Testnet12.into()) + { + config.kaspad.push(kaspad::Config::new( + Self::tn12_origin()?, + SupportedNetwork::Testnet12.into(), + )); + update = true; + } + + // update rk origin + for kaspad_config in config.kaspad.iter_mut().filter(|kaspad_config| { + kaspad_config.is_supported_network() + && matches!( + kaspad_config.network(), + Network::Supported(SupportedNetwork::Testnet10 | SupportedNetwork::Mainnet) + ) + }) { + *kaspad_config.origin_mut() = Self::master_origin()?; + update = true; + } + } + + // keep until v3 is current, gate it to <= 3 on v4 + update |= config.remove_unused_legacy_network(DeprecatedNetwork::Testnet11); + + if config.version < CONFIG_VERSION { + config.version = CONFIG_VERSION; + update = true; + } + if update { log::success(format!( "Updated kHOST config to version {}", CONFIG_VERSION ))?; - config.version = CONFIG_VERSION; config.save()?; } @@ -93,6 +144,18 @@ impl Config { Ok(()) } + /// considered unused if no systemd service and no active in cfg + fn remove_unused_legacy_network(&mut self, network: DeprecatedNetwork) -> bool { + let network = Network::from(network); + let kaspad_len = self.kaspad.len(); + self.kaspad.retain(|kaspad_config| { + kaspad_config.network() != network + || kaspad_config.is_enabled() + || systemd::is_active(kaspad_config.service_name()).unwrap_or(true) + }); + self.kaspad.len() != kaspad_len + } + pub fn reset() { let path = data_folder().join("config.json"); if let Err(err) = fs::remove_file(&path) { diff --git a/src/imports.rs b/src/imports.rs index 2d89f71..9cf17b2 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -37,7 +37,7 @@ pub use crate::fqdn; pub use crate::git::{self, Origin}; pub use crate::kaspad; pub use crate::khost; -pub use crate::network::{Interface, Network}; +pub use crate::network::{DeprecatedNetwork, Interface, Network, SupportedNetwork}; pub use crate::nginx; pub use crate::nginx::ProxyConfig; pub use crate::resolver; diff --git a/src/kaspad.rs b/src/kaspad.rs index 9ca7614..a982c10 100644 --- a/src/kaspad.rs +++ b/src/kaspad.rs @@ -1,4 +1,7 @@ use crate::imports::*; +use crate::network::DeprecatedNetwork::*; +use crate::network::Network::*; +use crate::network::SupportedNetwork::*; use nginx::prelude::*; #[derive(Clone, Debug, Serialize, Deserialize)] @@ -44,10 +47,14 @@ impl Service for Config { } fn managed(&self) -> bool { - true + self.is_supported_network() || self.is_enabled() } fn proxy_config(&self, _ctx: &Context) -> Option> { + if self.is_deprecated_network() { + return None; + } + let mut proxy_configs = Vec::new(); if let Some(iface) = self.wrpc_borsh.as_ref() { @@ -79,9 +86,10 @@ impl Service for Config { impl Config { pub fn new(origin: Origin, network: Network) -> Self { let (grpc, wrpc_borsh, wrpc_json) = match network { - Network::Mainnet => (16110, 17110, 18110), - Network::Testnet10 => (16210, 17210, 18210), - Network::Testnet12 => (16312, 17312, 18312), + Supported(Mainnet) => (16110, 17110, 18110), + Supported(Testnet10) => (16210, 17210, 18210), + Supported(Testnet12) => (16312, 17312, 18312), + Deprecated(Testnet11) => (16311, 17311, 18311), }; Self { @@ -114,6 +122,14 @@ impl Config { self.network } + pub fn is_supported_network(&self) -> bool { + self.network.is_supported() + } + + pub fn is_deprecated_network(&self) -> bool { + self.network.is_deprecated() + } + pub fn enable(&mut self) { self.enabled = true; } @@ -136,17 +152,21 @@ impl From<&Config> for Vec { let mut args = Arglist::default(); match config.network { - Network::Mainnet => { + Supported(Mainnet) => { // args.push("--connect=38.242.201.109"); } - Network::Testnet10 => { + Supported(Testnet10) => { args.push("--testnet"); args.push("--netsuffix=10"); } - Network::Testnet12 => { + Supported(Testnet12) => { args.push("--testnet"); args.push("--netsuffix=12"); } + Deprecated(Testnet11) => { + args.push("--testnet"); + args.push("--netsuffix=11"); + } } args.push("--yes"); @@ -191,9 +211,7 @@ impl From<&Config> for Vec { } pub fn unique_origins(ctx: &Context) -> HashSet { - ctx.config - .kaspad - .iter() + supported_configs(ctx) .map(|config| config.origin.clone()) .collect() } @@ -205,11 +223,49 @@ pub fn active_configs(ctx: &Context) -> impl Iterator { .filter(|config| config.is_enabled()) } -pub fn inactive_configs(ctx: &Context) -> impl Iterator { +pub fn supported_configs(ctx: &Context) -> impl Iterator { ctx.config .kaspad .iter() - .filter(|config| !config.is_enabled()) + .filter(|config| config.is_supported_network()) +} + +pub fn supported_configs_mut(ctx: &mut Context) -> impl Iterator { + ctx.config + .kaspad + .iter_mut() + .filter(|config| config.is_supported_network()) +} + +pub fn active_supported_configs(ctx: &Context) -> impl Iterator { + supported_configs(ctx).filter(|config| config.is_enabled()) +} + +pub fn inactive_supported_configs(ctx: &Context) -> impl Iterator { + supported_configs(ctx).filter(|config| !config.is_enabled()) +} + +pub fn legacy_network_service_exists() -> bool { + Network::deprecated().any(|network| { + let service_name = format!("kaspa-{network}"); + systemd::service_path(&service_name).exists() + }) +} + +pub fn has_legacy_network(ctx: &Context) -> bool { + ctx.config + .kaspad + .iter() + .any(|config| config.is_deprecated_network()) + || legacy_network_service_exists() +} + +pub fn warn_legacy_network(ctx: &Context) -> Result<()> { + if has_legacy_network(ctx) { + log::warning("testnet-11 is deprecated and no longer supported for new kHOST deployments. If kaspa-testnet-11 is installed or running, uninstall it and install/enable testnet-12 instead. testnet-11 compatibility is kept only to load legacy configs and will be removed in a future version.")?; + } + + Ok(()) } pub fn fetch(ctx: &Context) -> Result<()> { @@ -245,7 +301,7 @@ pub fn update(ctx: &Context) -> Result<()> { fetch(ctx)?; build(ctx)?; step("Restarting Kaspa p2p nodes...", || { - for config in active_configs(ctx) { + for config in active_supported_configs(ctx) { systemd::restart(config)?; } Ok(()) @@ -396,10 +452,15 @@ pub fn supports_multiple_networks(ctx: &Context, networks: usize) -> bool { } pub fn configure_networks(ctx: &mut Context, networks: Vec) -> Result<()> { - let networks = networks.into_iter().collect::>(); + let selected_networks = networks.into_iter().collect::>(); + let supported_networks = selected_networks + .iter() + .copied() + .filter(|network| network.is_supported()) + .collect::>(); let limits = [(3, 42), (2, 32)].iter(); for (nodes, limit) in limits { - if networks.len() >= *nodes && ctx.system.ram_as_gb() <= (*limit - 2) { + if supported_networks.len() >= *nodes && ctx.system.ram_as_gb() <= (*limit - 2) { log::error(format!( "Detected RAM is {}, minimum required for {} networks is {} Gb. Aborting...", as_gb(ctx.system.total_memory as f64, false, false), @@ -411,7 +472,11 @@ pub fn configure_networks(ctx: &mut Context, networks: Vec) -> Result<( } for config in ctx.config.kaspad.iter_mut() { - config.enabled = networks.contains(&config.network); + if config.is_supported_network() { + config.enabled = supported_networks.contains(&config.network); + } else if config.is_enabled() { + config.enabled = selected_networks.contains(&config.network); + } } ctx.config.save()?; @@ -425,7 +490,12 @@ pub fn reconfigure(ctx: &Context, force: bool) -> Result<()> { log::remark("Updating Kaspa p2p node configuration...")?; - for config in inactive_configs(ctx) { + for config in ctx + .config + .kaspad + .iter() + .filter(|config| !config.is_enabled()) + { let service_name = config.service_name(); if systemd::exists(config) { if systemd::is_active(config.service_name())? { @@ -442,7 +512,7 @@ pub fn reconfigure(ctx: &Context, force: bool) -> Result<()> { } } - for config in active_configs(ctx) { + for config in active_supported_configs(ctx) { let service_name = config.service_name(); step(format!("Configuring '{}'", service_name), || { if force || !systemd::exists(config) { @@ -456,7 +526,7 @@ pub fn reconfigure(ctx: &Context, force: bool) -> Result<()> { if reconfigure_systemd { step("Reloading systemd daemon...", systemd::daemon_reload)?; - for config in active_configs(ctx) { + for config in active_supported_configs(ctx) { let service_name = config.service_name(); step(format!("Brining up '{}'", service_name), || { systemd::enable(config)?; @@ -470,20 +540,6 @@ pub fn reconfigure(ctx: &Context, force: bool) -> Result<()> { Ok(()) } -pub fn stop_all(ctx: &Context) -> Result<()> { - for config in active_configs(ctx) { - systemd::stop(config)?; - } - Ok(()) -} - -pub fn start_all(ctx: &Context) -> Result<()> { - for config in active_configs(ctx) { - systemd::start(config)?; - } - Ok(()) -} - pub fn restart_all(ctx: &Context) -> Result<()> { for config in active_configs(ctx) { step( @@ -577,7 +633,7 @@ pub fn find_config_by_service_detail<'a>( .find(|config| config.service_name() == detail.name) } -pub fn select_networks(ctx: &mut Context) -> Result<()> { +pub fn select_supported_networks(ctx: &mut Context) -> Result<()> { if ctx.system.ram_as_gb() < 24 { log::warning(format!( "Detected RAM is {}, minimum required for multiple networks is 32 Gb.", @@ -585,13 +641,12 @@ pub fn select_networks(ctx: &mut Context) -> Result<()> { ))?; let mut selector = cliclack::select("Select Kaspa p2p node network to enable"); - let details = ctx - .config - .kaspad - .iter() + let details = supported_configs(ctx) .map(Service::service_detail) .collect::>(); - let selected = active_configs(ctx).next().map(Service::service_detail); + let selected = active_supported_configs(ctx) + .next() + .map(Service::service_detail); if let Some(selected) = selected { selector = selector.initial_value(selected); } @@ -599,15 +654,12 @@ pub fn select_networks(ctx: &mut Context) -> Result<()> { selector = selector.item(detail.clone(), detail, ""); } let selected = selector.interact()?; - ctx.config.kaspad.iter_mut().for_each(Config::disable); + supported_configs_mut(ctx).for_each(Config::disable); find_config_by_service_detail(ctx, &selected) .unwrap() .enable(); } else { - let details = ctx - .config - .kaspad - .iter() + let details = supported_configs(ctx) .map(Service::service_detail) .collect::>(); let enabled = details @@ -635,7 +687,7 @@ pub fn select_networks(ctx: &mut Context) -> Result<()> { } } - ctx.config.kaspad.iter_mut().for_each(Config::disable); + supported_configs_mut(ctx).for_each(Config::disable); for detail in selected.iter() { find_config_by_service_detail(ctx, detail).unwrap().enable(); } diff --git a/src/main.rs b/src/main.rs index b7024ca..432424b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,6 +62,8 @@ fn main() { sudo::init(&mut ctx); + kaspad::warn_legacy_network(&ctx).ok(); + let first_run = !ctx.config.bootstrap; let status = status::detect(&ctx); diff --git a/src/network.rs b/src/network.rs index 2a7243b..156d274 100644 --- a/src/network.rs +++ b/src/network.rs @@ -28,19 +28,85 @@ impl Interface { #[derive(Default, Describe, Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] #[serde(rename_all = "lowercase")] -pub enum Network { +pub enum SupportedNetwork { #[default] Mainnet, Testnet10, Testnet12, } +#[derive(Describe, Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] +#[serde(rename_all = "lowercase")] +pub enum DeprecatedNetwork { + Testnet11, +} + +impl Display for SupportedNetwork { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + SupportedNetwork::Mainnet => write!(f, "mainnet"), + SupportedNetwork::Testnet10 => write!(f, "testnet-10"), + SupportedNetwork::Testnet12 => write!(f, "testnet-12"), + } + } +} + +impl Display for DeprecatedNetwork { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + DeprecatedNetwork::Testnet11 => write!(f, "testnet-11"), + } + } +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] +#[serde(untagged)] +pub enum Network { + Supported(SupportedNetwork), + Deprecated(DeprecatedNetwork), +} + impl Display for Network { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Network::Mainnet => write!(f, "mainnet"), - Network::Testnet10 => write!(f, "testnet-10"), - Network::Testnet12 => write!(f, "testnet-12"), + Network::Supported(network) => write!(f, "{network}"), + Network::Deprecated(network) => write!(f, "{network}"), } } } + +impl Network { + pub fn supported() -> impl Iterator { + SupportedNetwork::iter().copied().map(Self::from) + } + + pub fn deprecated() -> impl Iterator { + DeprecatedNetwork::iter().copied().map(Self::from) + } + + pub fn is_supported(self) -> bool { + matches!(self, Network::Supported(_)) + } + + pub fn is_deprecated(self) -> bool { + matches!(self, Network::Deprecated(_)) + } +} + +impl Default for Network { + fn default() -> Self { + SupportedNetwork::default().into() + } +} + +impl From for Network { + fn from(network: SupportedNetwork) -> Self { + Self::Supported(network) + } +} + +impl From for Network { + fn from(network: DeprecatedNetwork) -> Self { + Self::Deprecated(network) + } +} From bcd34ebfc9356fc62336aeb4ceb5c8024eac291c Mon Sep 17 00:00:00 2001 From: IzioDev <9900846+IzioDev@users.noreply.github.com> Date: Tue, 5 May 2026 08:43:12 +0200 Subject: [PATCH 05/11] revert: change resolver revision back to aspectron --- src/config.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/config.rs b/src/config.rs index c975d46..0425cd8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -18,11 +18,7 @@ pub struct Config { impl Config { pub fn try_new() -> Result { - let origin = Origin::try_new( - // TODO(izio): change before merge - "https://github.com/iziodev/kaspa-resolver", - Some("tmp/tn12"), - )?; + let origin = Origin::try_new("https://github.com/aspectron/kaspa-resolver", None)?; let resolver = resolver::Config::new(origin) .with_stats() .with_local_interface(8989); From f828cffd0471dc9e3fd246bec6dc57ebd6248bc0 Mon Sep 17 00:00:00 2001 From: IzioDev <9900846+IzioDev@users.noreply.github.com> Date: Tue, 5 May 2026 23:15:42 +0200 Subject: [PATCH 06/11] clippy --- src/kaspad.rs | 2 +- src/resolver.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/kaspad.rs b/src/kaspad.rs index a982c10..63b2e80 100644 --- a/src/kaspad.rs +++ b/src/kaspad.rs @@ -419,7 +419,7 @@ pub fn version(origin: &Origin) -> Option { .and_then(|s| { s.trim() .split(' ') - .last() + .next_back() .map(|version| format!("{version}-{hash}")) }) } diff --git a/src/resolver.rs b/src/resolver.rs index 8c59b6c..dff03bc 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -249,7 +249,7 @@ pub fn version(origin: &Origin) -> Option { .and_then(|s| { s.trim() .split(' ') - .last() + .next_back() .map(|version| format!("{version}-{hash}")) }) } From 17ca8daf52b0ce3a46a92bb8ac4679618cfc7a9e Mon Sep 17 00:00:00 2001 From: IzioDev <9900846+IzioDev@users.noreply.github.com> Date: Wed, 6 May 2026 16:59:04 +0200 Subject: [PATCH 07/11] roll back origins to aspectron/* --- README.md | 6 +++++- src/config.rs | 23 ++++++++++++----------- src/git.rs | 6 ++---- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index ee503cd..76273ea 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Kaspa p2p node deployment automation tool for Linux. -kHOST was created to automate deployment of Kaspa nodes intended for use as a part of the Kaspa public RPC network as well as private network high-availability clusters. kHOST deploys Rusty-Kaspa nodes from sources, configures them to run as a `systemd` service as well as configures NGINX to act as a reverse proxy for the RPC. This tool exists to simplify and automate Kaspa node deployment as well as to standardize related system configuration. +kHOST was created to automate deployment of Kaspa nodes intended for use as a part of the Kaspa public RPC network as well as private network high-availability clusters. kHOST deploys Rusty-Kaspa nodes from sources, configures them to run as a `systemd` service as well as configures NGINX to act as a reverse proxy for the RPC. This tool exists to simplify and automate Kaspa node deployment as well as to standardize related system configuration. ## Deploying @@ -33,3 +33,7 @@ If you already have an existing user and rust installed, you can simply run `car Please note that the user needs to have root (sudo) privileges to run khost. IMPORTANT: This tool creates it's own configuration for the kaspad node, as such, any previous configurations should be disabled and removed. If kaspad was running before under the same username, the `~/.rusty-kaspa` data folders containing databases will be re-used. + +## Default Origins + +They are set to `aspectron/*` origins, manually updated from `upstream`. This is a design choice that allows high velocity update coordination and controlled versioning, in practice, it should follow upstream release cycle. diff --git a/src/config.rs b/src/config.rs index 0425cd8..91f95a1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -23,16 +23,14 @@ impl Config { .with_stats() .with_local_interface(8989); - let master_origin = Self::master_origin()?; - let tn12_origin = Self::tn12_origin()?; + let pnnv1_origin = Self::pnnv1_origin()?; + let tn12_origin = Self::pnnv1tn12_origin()?; let kaspad = SupportedNetwork::iter() .copied() .map(|network| { let selected_origin = match network { - SupportedNetwork::Mainnet | SupportedNetwork::Testnet10 => { - master_origin.clone() - } + SupportedNetwork::Mainnet | SupportedNetwork::Testnet10 => pnnv1_origin.clone(), SupportedNetwork::Testnet12 => tn12_origin.clone(), }; kaspad::Config::new(selected_origin, network.into()) @@ -54,12 +52,15 @@ impl Config { }) } - fn master_origin() -> Result { - Origin::try_new("https://github.com/kaspanet/rusty-kaspa", Some("master")) + pub fn pnnv1_origin() -> Result { + Origin::try_new("https://github.com/aspectron/rusty-kaspa", Some("pnn-v1")) } - fn tn12_origin() -> Result { - Origin::try_new("https://github.com/kaspanet/rusty-kaspa", Some("tn12")) + pub fn pnnv1tn12_origin() -> Result { + Origin::try_new( + "https://github.com/aspectron/rusty-kaspa", + Some("pnn-v1-tn12"), + ) } } @@ -96,7 +97,7 @@ impl Config { .any(|config| config.network() == SupportedNetwork::Testnet12.into()) { config.kaspad.push(kaspad::Config::new( - Self::tn12_origin()?, + Self::pnnv1tn12_origin()?, SupportedNetwork::Testnet12.into(), )); update = true; @@ -110,7 +111,7 @@ impl Config { Network::Supported(SupportedNetwork::Testnet10 | SupportedNetwork::Mainnet) ) }) { - *kaspad_config.origin_mut() = Self::master_origin()?; + *kaspad_config.origin_mut() = Self::pnnv1_origin()?; update = true; } } diff --git a/src/git.rs b/src/git.rs index fd68a5d..4fcb487 100644 --- a/src/git.rs +++ b/src/git.rs @@ -200,10 +200,8 @@ where }; let origin = match preset { - Preset::PNNv1 => { - Origin::try_new("https://github.com/aspectron/rusty-kaspa", Some("pnn-v1"))? - } - Preset::Tn12 => Origin::try_new("https://github.com/kaspanet/rusty-kaspa", Some("tn12"))?, + Preset::PNNv1 => Config::pnnv1_origin()?, + Preset::Tn12 => Config::pnnv1tn12_origin()?, // Preset::Delta => { // Origin::try_new("https://github.com/aspectron/rusty-kaspa", Some("delta"))? // } From 7789bcc477eb6b7940c52e89056d23c78c5e9082 Mon Sep 17 00:00:00 2001 From: IzioDev <9900846+IzioDev@users.noreply.github.com> Date: Wed, 6 May 2026 17:01:27 +0200 Subject: [PATCH 08/11] review: update ports --- src/kaspad.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/kaspad.rs b/src/kaspad.rs index 63b2e80..a27793c 100644 --- a/src/kaspad.rs +++ b/src/kaspad.rs @@ -88,8 +88,8 @@ impl Config { let (grpc, wrpc_borsh, wrpc_json) = match network { Supported(Mainnet) => (16110, 17110, 18110), Supported(Testnet10) => (16210, 17210, 18210), - Supported(Testnet12) => (16312, 17312, 18312), - Deprecated(Testnet11) => (16311, 17311, 18311), + Supported(Testnet12) => (16410, 17410, 18410), + Deprecated(Testnet11) => (16310, 17310, 18310), }; Self { From 8174655f0bb5eabf85de2d5fd06ce40e2b72d8f4 Mon Sep 17 00:00:00 2001 From: IzioDev <9900846+IzioDev@users.noreply.github.com> Date: Wed, 6 May 2026 17:03:31 +0200 Subject: [PATCH 09/11] docs: clarify origin section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76273ea..52e0527 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,6 @@ Please note that the user needs to have root (sudo) privileges to run khost. IMPORTANT: This tool creates it's own configuration for the kaspad node, as such, any previous configurations should be disabled and removed. If kaspad was running before under the same username, the `~/.rusty-kaspa` data folders containing databases will be re-used. -## Default Origins +## Kaspad Default Origins They are set to `aspectron/*` origins, manually updated from `upstream`. This is a design choice that allows high velocity update coordination and controlled versioning, in practice, it should follow upstream release cycle. From 13e8eec722aac35c06aab9f57564cb0d2f3b243e Mon Sep 17 00:00:00 2001 From: IzioDev <9900846+IzioDev@users.noreply.github.com> Date: Wed, 6 May 2026 19:37:40 +0200 Subject: [PATCH 10/11] ci: fix install cargo nextest --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2d23756..c6bab6f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -77,7 +77,7 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Install cargo-nextest - run: cargo install cargo-nextest + run: cargo install cargo-nextest --locked - name: Cache uses: actions/cache@v4 From 6c8cb315046a490424f32e1067b04adc3e425456 Mon Sep 17 00:00:00 2001 From: IzioDev <9900846+IzioDev@users.noreply.github.com> Date: Wed, 6 May 2026 20:21:34 +0200 Subject: [PATCH 11/11] ci: warn only if no tests --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c6bab6f..07e744d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -94,4 +94,4 @@ jobs: run: cargo build --release - name: Run cargo test regular features - run: cargo nextest run --release + run: cargo nextest run --release --no-tests=warn