diff --git a/app/igneum-app/Cargo.lock b/app/igneum-app/Cargo.lock index 7456f2d4..6340779f 100644 --- a/app/igneum-app/Cargo.lock +++ b/app/igneum-app/Cargo.lock @@ -219,7 +219,7 @@ dependencies = [ [[package]] name = "igneum-app" -version = "0.3.21" +version = "0.3.22" dependencies = [ "ed25519-dalek", "getrandom", diff --git a/app/igneum-app/Cargo.toml b/app/igneum-app/Cargo.toml index f9359cbe..8e114065 100644 --- a/app/igneum-app/Cargo.toml +++ b/app/igneum-app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "igneum-app" -version = "0.3.21" +version = "0.3.22" edition = "2021" description = "Igneum Miner engine: supervises the node, the miner and the GPU workers, and serves the dashboard on 127.0.0.1" license = "MIT" diff --git a/app/igneum-app/resources/igneum-app.rc b/app/igneum-app/resources/igneum-app.rc index bda225ed..6e29c5dc 100644 --- a/app/igneum-app/resources/igneum-app.rc +++ b/app/igneum-app/resources/igneum-app.rc @@ -6,8 +6,8 @@ 1 ICON "igneum.ico" 1 VERSIONINFO -FILEVERSION 0,3,21,0 -PRODUCTVERSION 0,3,21,0 +FILEVERSION 0,3,22,0 +PRODUCTVERSION 0,3,22,0 FILEFLAGSMASK 0x3fL FILEFLAGS 0x0L FILEOS VOS_NT_WINDOWS32 @@ -20,12 +20,12 @@ BEGIN BEGIN VALUE "CompanyName", "Igneum" VALUE "FileDescription", "Igneum Miner engine" - VALUE "FileVersion", "0.3.21" + VALUE "FileVersion", "0.3.22" VALUE "InternalName", "igneum-app" VALUE "LegalCopyright", "Igneum contributors" VALUE "OriginalFilename", "igneum-app.exe" VALUE "ProductName", "Igneum Miner" - VALUE "ProductVersion", "0.3.21" + VALUE "ProductVersion", "0.3.22" END END BLOCK "VarFileInfo" diff --git a/app/igneum-app/src/config.rs b/app/igneum-app/src/config.rs index f3235e8a..761979d8 100644 --- a/app/igneum-app/src/config.rs +++ b/app/igneum-app/src/config.rs @@ -278,6 +278,14 @@ pub struct Packaged { /// height, 4 October 2026: `{"difficulty_v2_activation_daa": N}`). Absent or empty = no override file. #[serde(default)] pub node_override_params: Option, + /// The network the package's node joins (7 October 2026, Devnet 3): a suffixed devnet (`--devnet-suffix=N`), its own + /// genesis and p2p port, its own node datadir devnet-N beside the shared devnet's devnet-v4 (kept for the way back). + /// Absent = the shared devnet. IGNEUM_APP_DEVNET_SUFFIX in the environment wins over it. + #[serde(default)] + pub node_devnet_suffix: Option, + /// The package's default peers for that network (host:port); absent = the shared devnet's list. + #[serde(default)] + pub node_peers: Option>, /// Where the key and the manifest came from, for the log header: "packaged", "file " or "none". Never /// serialised (the packaged file does not carry them; nothing sends this struct to the UI). #[serde(skip)] @@ -403,6 +411,11 @@ pub struct Runtime { impl Runtime { pub fn from_env() -> Runtime { + Self::from_env_with(None, None) + } + + /// `packaged_suffix` and `packaged_peers` are the package's network (config.rs Packaged); the environment wins. + pub fn from_env_with(packaged_suffix: Option, packaged_peers: Option<&[String]>) -> Runtime { let env = |k: &str| std::env::var(k).ok().filter(|v| !v.is_empty()); let network = env("IGNEUM_APP_NETWORK").unwrap_or_else(|| "devnet".into()); let rpc_port = env("IGNEUM_APP_RPC_PORT").and_then(|v| v.parse().ok()).unwrap_or(26610); @@ -411,11 +424,12 @@ impl Runtime { Ok(v) => v.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect(), // the public seed node first, then the build box's hand nodes (main's decision (b), 6 October 2026, 19:1x UTC: // the hands move off the project lead's Mac to igneum-build-1), then the project lead's Mac on the house LAN (devnet only) + Err(_) if packaged_peers.map(|p| !p.is_empty()).unwrap_or(false) => packaged_peers.unwrap().to_vec(), Err(_) if network == "devnet" => vec!["188.245.5.161:26611".to_string(), "188.40.146.49:26611".to_string(), "192.168.68.64:26611".to_string()], Err(_) => vec![], }; let unsynced_mining = env("IGNEUM_APP_UNSYNCED").map(|v| v == "1").unwrap_or(false); - let devnet_suffix = env("IGNEUM_APP_DEVNET_SUFFIX").and_then(|v| v.parse().ok()); + let devnet_suffix = env("IGNEUM_APP_DEVNET_SUFFIX").and_then(|v| v.parse().ok()).or(packaged_suffix); let root = crate::platform::data_root(); let node_dir = match env("IGNEUM_APP_NODE_DIR") { Some(d) => PathBuf::from(d), @@ -596,4 +610,19 @@ mod fixture_tests { Err(e) => panic!("the crate refuses the file: {e}"), } } + + #[test] + fn the_package_names_the_network_when_the_environment_is_silent() { + use super::Runtime; + // the test never sets IGNEUM_APP_DEVNET_SUFFIX or IGNEUM_APP_PEERS, so the package's values win + let peers = vec!["188.40.146.49:26631".to_string(), "188.40.146.49:26671".to_string()]; + let r = Runtime::from_env_with(Some(3), Some(&peers)); + assert_eq!(r.devnet_suffix, Some(3)); + assert_eq!(r.peers, peers); + assert!(r.node_dir.ends_with("devnet-3"), "{}", r.node_dir.display()); + // no package network: the shared devnet as before + let r = Runtime::from_env_with(None, None); + assert_eq!(r.devnet_suffix, None); + assert!(r.node_dir.ends_with("devnet-v4")); + } } diff --git a/app/igneum-app/src/engine.rs b/app/igneum-app/src/engine.rs index 4d4d0878..fa93cf93 100644 --- a/app/igneum-app/src/engine.rs +++ b/app/igneum-app/src/engine.rs @@ -802,6 +802,8 @@ pub struct Engine { fault_reports: Vec, /// MF-6: the job that took the miners hold, when, and its cap in seconds job_hold_owner: Option, + /// the one log line about the ignored override on a suffixed devnet (node_override_file) + override_ignored_logged: std::sync::atomic::AtomicBool, /// the choices that give the under-12 GB cards' miners back when the prover stops (settings.prove_instead) prove_hold_restore: Option>, job_hold_since: Option, @@ -928,6 +930,7 @@ impl Engine { node_settled: false, fault_reports: Vec::new(), job_hold_owner: None, + override_ignored_logged: std::sync::atomic::AtomicBool::new(false), prove_hold_restore: None, job_hold_since: None, job_hold_cap_s: 3600.0, @@ -2041,6 +2044,15 @@ impl Engine { if self.node_override_unusable { return None; } + // Devnet 3 and every suffixed devnet from 3 (7 October 2026): every upgrade is on from block zero in the network + // object and the node refuses --override-params-file there, so no file is written whatever the manifest or the + // package carries; the manifest's consensus.override is for the shared devnet and is ignored here, once in the log + if matches!(self.shared.runtime.devnet_suffix, Some(n) if n >= 3) { + if !self.override_ignored_logged.swap(true, std::sync::atomic::Ordering::Relaxed) { + self.shared.log(&format!("node: igneum-devnet-{} carries every parameter in its network object; the manifest's consensus override is ignored here", self.shared.runtime.devnet_suffix.unwrap_or(0))); + } + return None; + } // the signed OTA manifest's consensus override wins over the packager's pin (src/ota.rs write_override) if let Some(p) = self.ota.override_path() { return Some(p); diff --git a/app/igneum-app/src/main.rs b/app/igneum-app/src/main.rs index 1f91c5f0..12237488 100644 --- a/app/igneum-app/src/main.rs +++ b/app/igneum-app/src/main.rs @@ -114,6 +114,14 @@ fn main() { } } let packaged = config::Packaged::load(&candidates).with_env_overrides(); + // Devnet 3 (7 October 2026): the package names the network its node joins; the runtime read above knew only the + // environment, so it is read again with the package's suffix and peers (the environment still wins inside) + let runtime = if packaged.node_devnet_suffix.is_some() || packaged.node_peers.is_some() { + config::Runtime::from_env_with(packaged.node_devnet_suffix, packaged.node_peers.as_deref()) + } else { + runtime + }; + let _ = std::fs::create_dir_all(&runtime.app_dir); let settings = config::Settings::load(&runtime.app_dir.join("settings.json")); // a measurement engine runs with the installed app's choices and its own switches (config.rs for_measurement) let settings = if sweep { settings.for_measurement() } else { settings }; diff --git a/app/windows/version.h b/app/windows/version.h index bedc5307..37997f85 100644 --- a/app/windows/version.h +++ b/app/windows/version.h @@ -3,6 +3,6 @@ // packaging/windows/Igneum-Miner.iss when the app version moves. Include guards, not #pragma once: rc.exe reads it too. #ifndef IGNEUM_HOST_VERSION_H #define IGNEUM_HOST_VERSION_H -#define IGNEUM_HOST_VERSION_STR "0.3.21" -#define IGNEUM_HOST_VERSION_RC 0,3,21,0 +#define IGNEUM_HOST_VERSION_STR "0.3.22" +#define IGNEUM_HOST_VERSION_RC 0,3,22,0 #endif diff --git a/packaging/mac/app/Info.plist b/packaging/mac/app/Info.plist index 717ff627..16773040 100644 --- a/packaging/mac/app/Info.plist +++ b/packaging/mac/app/Info.plist @@ -11,7 +11,7 @@ CFBundleVersion VERSION_STAMP CFBundleShortVersionString - 0.3.21 + 0.3.22 CFBundlePackageType APPL CFBundleExecutable diff --git a/packaging/windows/Igneum-Miner.iss b/packaging/windows/Igneum-Miner.iss index cb1138b9..86ed1d99 100644 --- a/packaging/windows/Igneum-Miner.iss +++ b/packaging/windows/Igneum-Miner.iss @@ -9,7 +9,7 @@ #define ArtDir "..\..\brand\icons" #endif #ifndef AppVersion - #define AppVersion "0.3.21" + #define AppVersion "0.3.22" #endif #define AppName "Igneum Miner" #define Publisher "Igneum" @@ -85,7 +85,7 @@ Name: "{autodesktop}\Igneum Miner"; Filename: "{app}\igneum-app.exe"; Parameters ; Started as the signed-in user, not as administrator (the data lands in that user's %LOCALAPPDATA%). Filename: "{app}\igneum-app.exe"; Parameters: "--launch"; Description: "Start Igneum Miner now"; Flags: postinstall nowait skipifsilent runasoriginaluser ; The over-the-air updater (packaging/ota, src/ota.rs) runs this installer /VERYSILENT /IGNOTA=1 and the app must come back by itself. -; /IGNOTA=2 (helpers from 0.3.21 on, MF-11): the helper starts the app itself, polls the new engine and restores the kept +; /IGNOTA=2 (helpers from 0.3.22 on, MF-11): the helper starts the app itself, polls the new engine and restores the kept ; exe set when nothing answers; this entry stays silent so the two never race for the single-instance window. Filename: "{app}\igneum-app.exe"; Parameters: "--launch"; Flags: nowait runasoriginaluser; Check: OtaRelaunch @@ -97,8 +97,8 @@ Filename: "powershell.exe"; Parameters: "-NoProfile -ExecutionPolicy Bypass -Fil Type: filesandordirs; Name: "{app}" [Code] -// /IGNOTA=1: the app's own updater (before 0.3.21) started this install; relaunch the app when the files are in. -// /IGNOTA=2: the 0.3.21 helper started it and relaunches the app itself (src/ota.rs WIN_HELPER): no relaunch here. +// /IGNOTA=1: the app's own updater (before 0.3.22) started this install; relaunch the app when the files are in. +// /IGNOTA=2: the 0.3.22 helper started it and relaunches the app itself (src/ota.rs WIN_HELPER): no relaunch here. function OtaRelaunch: Boolean; begin Result := ExpandConstant('{param:IGNOTA|0}') = '1';