From d7049fdfda0da859a542f2c95c7a69378b094dd3 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 17:45:05 +0000 Subject: [PATCH 001/158] app: GPU hot-plug (re-detection every minute, WM_DEVICECHANGE on Windows), faulty cards listed with the problem code, integrated GPUs off by default, the card list in the console 5 October 2026: an RX 9070 XT went into PC 1 through a Sonnet USB4 box while the app ran and nothing noticed; the app detected cards once at start. Now src/hotplug.rs compares every enumeration with the list (key, else vendor + name when unique; a card whose tool did not answer is never called removed): a new usable card starts a worker, enabled by default like a card at start, with "New card: , mining" on the strip and in the log; a card Windows lists with a problem code (Win32_VideoController Status / ConfigManagerErrorCode) is shown as ": not usable (Code 43)" with the reboot-or-reinstall hint and no worker; a card that disappears has its worker stopped (quit, 8 s) and its row says removed for five minutes, then hides; an unchanged list touches nothing. The Windows host sends "detect" on WM_DEVICECHANGE; the engine polls every 60 s (300 s on macOS, no GPU hot-plug there). detect.rs: the Ryzen iGPU is "gfx1036" to the OpenCL worker, so the APU gfx codes count as integrated, plus the adapter row's Intel processor string and a dedicated memory under 1 GB; integrated defaults to off with "integrated GPU, off by default (2 to 3 MH/s for 30 W)" on the row, and the user's choice is kept across re-detections and restarts (settings, found by key or by vendor + name when the index moved). Console: the engine logs "cards: [, ] | ..." at start, on every change and every 10 minutes; relay/lib/parse.mjs reads it and the hot-plug events, the machines API and tools/console.mjs machines show them. Tests: hotplug.rs (added, removed, moved, errored, recovered, revived, unchanged, twins, user override kept, the console line), detect.rs (PC 1's adapter lines, the Mac, kind classification, the unusable row), notices.test.mjs (card notices), relay parse.test.mjs (cards line). cargo test -p igneum-app: 91 passed. Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/src/detect.rs | 378 +++++++++++++++++++---- app/igneum-app/src/engine.rs | 242 +++++++++++---- app/igneum-app/src/hotplug.rs | 464 +++++++++++++++++++++++++++++ app/igneum-app/src/main.rs | 3 + app/igneum-app/src/state.rs | 21 +- app/igneum-app/ui/app.css | 5 + app/igneum-app/ui/app.js | 71 +++-- app/igneum-app/ui/notices.test.mjs | 35 ++- app/windows/host.cpp | 6 + relay/api/console.mjs | 3 +- relay/lib/parse.mjs | 11 + relay/test/parse.test.mjs | 22 +- relay/ui.html | 2 +- tools/console.mjs | 3 + 14 files changed, 1133 insertions(+), 133 deletions(-) create mode 100644 app/igneum-app/src/hotplug.rs diff --git a/app/igneum-app/src/detect.rs b/app/igneum-app/src/detect.rs index 1af73c03..adbc5232 100644 --- a/app/igneum-app/src/detect.rs +++ b/app/igneum-app/src/detect.rs @@ -1,6 +1,8 @@ //! GPU detection with the real names. macOS: the Metal worker's ready line (the device Metal reports) plus the core //! count from system_profiler. Windows: nvidia-smi for NVIDIA cards, the OpenCL worker's --list for the rest -//! (AMD, Intel), and the WMI name list as a last resort when neither tool runs. +//! (AMD, Intel), the Windows adapter list (Win32_VideoController: status, problem code, memory) for the cards no +//! worker can drive and for the integrated-or-discrete call, and that list's names as a last resort when neither +//! tool runs. The engine runs this at start and again every minute (src/hotplug.rs compares the two lists). use crate::state::CardState; use std::io::Write; @@ -18,6 +20,36 @@ pub struct Bins { pub dir: std::path::PathBuf, } +/// One enumeration: the cards, the notes for the setup screen, and which tools answered. A tool that did not answer +/// (nvidia-smi timed out, the OpenCL worker crashed) says nothing about its cards: the engine keeps them rather +/// than calling them removed (src/hotplug.rs). +#[derive(Clone, Default)] +pub struct Detection { + pub cards: Vec, + pub notes: Vec, + pub nvidia_listed: bool, + pub opencl_listed: bool, + pub adapters_listed: bool, + pub metal_listed: bool, +} + +impl Detection { + /// Whether this enumeration can say that `c` is gone: the tool that lists its vendor answered. + pub fn listed(&self, c: &CardState) -> bool { + if !c.problem.is_empty() { + return self.adapters_listed; + } + match c.vendor.as_str() { + "apple" => self.metal_listed, + "nvidia" => self.nvidia_listed, + _ => self.opencl_listed || (c.device.is_empty() && self.adapters_listed), + } + } +} + +/// The hint on a card the OS reports as faulty (Windows Code 43, 12, 31 and friends). +pub const PROBLEM_HINT: &str = "reboot with the card attached; if it persists, reinstall the driver with the card attached"; + /// Runs a command with a time limit; returns stdout (and stderr appended) or None. pub fn run_timeout(cmd: &mut Command, stdin_text: Option<&str>, limit: Duration) -> Option { cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); @@ -68,14 +100,125 @@ fn card(index: usize, name: &str, vendor: &str, worker: &str, detail: &str, devi } } -/// Integrated GPUs by name: AMD APUs ("Radeon Graphics", "Vega 8"), Intel iGPUs (Iris, UHD, HD Graphics, Arc A3xx is discrete). -#[allow(dead_code)] +/// Integrated GPUs by name: AMD APUs ("Radeon Graphics", "Vega 8"), Intel iGPUs (Iris, UHD, HD Graphics, Arc A3xx is +/// discrete), and the gfx codes AMD's OpenCL runtime reports instead of a marketing name (the worker's --list prints +/// CL_DEVICE_NAME: PC 1's Ryzen iGPU is "gfx1036", 5 October 2026). pub fn looks_integrated(name: &str) -> bool { let n = name.to_ascii_lowercase(); let integrated = ["radeon(tm) graphics", "radeon graphics", "vega 8", "vega 7", "vega 6", "vega 3", "vega 11", "iris", "uhd graphics", "hd graphics", "intel(r) graphics", "intel graphics", "apu", "780m", "760m", "680m", "610m", "890m", "880m"]; - integrated.iter().any(|k| n.contains(k)) && !n.contains("arc ") + if integrated.iter().any(|k| n.contains(k)) && !n.contains("arc ") { + return true; + } + // AMD APU graphics by gfx code (approximate list from AMD's ROCm and Mesa target tables): Raven/Picasso gfx902 and + // gfx909, Renoir/Cezanne/Lucienne gfx90c, Van Gogh gfx1033, Rembrandt gfx1035, Raphael/Granite Ridge gfx1036, + // Mendocino gfx1037, Phoenix gfx1103, Strix gfx1150 to gfx1152. Discrete codes (gfx1030 and so on) are not here. + let apu = ["gfx902", "gfx909", "gfx90c", "gfx1033", "gfx1035", "gfx1036", "gfx1037", "gfx1103", "gfx1150", "gfx1151", "gfx1152"]; + let code = n.trim(); + apu.iter().any(|k| code == *k || code.starts_with(&format!("{k}:")) || code.starts_with(&format!("{k} "))) } +/// One row of Windows' adapter list (Win32_VideoController), the part this app reads. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Adapter { + pub name: String, + /// "OK", "Error", "Degraded", ... (the Status property) + pub status: String, + /// the PnP problem code (ConfigManagerErrorCode): 0 = fine, 43 = the driver stopped it, 12 = no resources, 31 = not loaded + pub code: u32, + /// AdapterRAM in MB; 0 = unknown (a faulty card reports 0, and the property caps at 4 GB on 32-bit values) + pub ram_mb: u64, + pub processor: String, + pub pnp_id: String, +} + +impl Adapter { + /// "Code 43" for a problem code, "status Error" for a bad status without one, None when the device is fine. + pub fn problem(&self) -> Option { + if self.code != 0 { + return Some(format!("Code {}", self.code)); + } + let st = self.status.trim(); + if !st.is_empty() && !st.eq_ignore_ascii_case("ok") { + return Some(format!("status {st}")); + } + None + } +} + +/// Integrated or discrete, from the name (APU and iGPU names, AMD gfx codes) and, when Windows' adapter row is +/// known, its processor string or a dedicated memory under 1 GB (a shared-memory iGPU; 0 = unknown, says nothing). +pub fn classify_kind(name: &str, adapter: Option<&Adapter>) -> &'static str { + if looks_integrated(name) { + return "integrated"; + } + if let Some(a) = adapter { + // the processor string names Intel iGPUs ("Intel(R) Iris(R) Xe Graphics Family"); AMD's reads "AMD Radeon + // Graphics Processor (0x7550)" for discrete cards too, so only the Intel markers count here + let proc_ = a.processor.to_ascii_lowercase(); + if looks_integrated(&a.name) || (["iris", "uhd graphics", "hd graphics"].iter().any(|k| proc_.contains(k)) && !proc_.contains("arc")) { + return "integrated"; + } + if a.ram_mb > 0 && a.ram_mb < 1024 { + return "integrated"; + } + } + "discrete" +} + +pub fn vendor_of(name: &str) -> &'static str { + let n = name.to_ascii_lowercase(); + if n.contains("nvidia") || n.contains("geforce") { + "nvidia" + } else if n.contains("amd") || n.contains("radeon") || n.starts_with("gfx") { + "amd" + } else if n.contains("apple") { + "apple" + } else { + "other" + } +} + +/// Parses `Get-CimInstance Win32_VideoController | Select-Object ... | ConvertTo-Json` (one object or an array). +pub fn parse_adapters(json: &str) -> Vec { + let Ok(v) = serde_json::from_str::(json.trim()) else { return Vec::new() }; + let rows: Vec = match v { + serde_json::Value::Array(a) => a, + o @ serde_json::Value::Object(_) => vec![o], + _ => Vec::new(), + }; + let s = |r: &serde_json::Value, k: &str| r.get(k).and_then(|x| x.as_str()).unwrap_or("").trim().to_string(); + let n = |r: &serde_json::Value, k: &str| r.get(k).and_then(|x| x.as_u64().or_else(|| x.as_str().and_then(|t| t.trim().parse::().ok()))).unwrap_or(0); + rows.iter() + .map(|r| Adapter { name: s(r, "Name"), status: s(r, "Status"), code: n(r, "ConfigManagerErrorCode") as u32, ram_mb: n(r, "AdapterRAM") / (1024 * 1024), processor: s(r, "VideoProcessor"), pnp_id: s(r, "PNPDeviceID") }) + .filter(|a| !a.name.is_empty()) + .collect() +} + +/// Windows' adapter list through PowerShell (about a second); None when PowerShell did not answer. +#[cfg(windows)] +pub fn adapters() -> Option> { + let script = "Get-CimInstance Win32_VideoController | Select-Object Name,Status,ConfigManagerErrorCode,AdapterRAM,VideoProcessor,PNPDeviceID | ConvertTo-Json -Compress"; + let out = run_timeout(Command::new(crate::platform::tool("powershell")).args(["-NoProfile", "-Command", script]), None, Duration::from_secs(15))?; + let start = out.find(|c| c == '[' || c == '{')?; + Some(parse_adapters(&out[start..])) +} + +#[cfg(not(windows))] +#[allow(dead_code)] +pub fn adapters() -> Option> { + None +} + +/// Windows' row for a detected card, by name (nvidia-smi and Windows agree on NVIDIA names; AMD's OpenCL runtime +/// reports gfx codes, which match nothing here and fall back to the name rules). +pub fn adapter_for<'a>(name: &str, adapters: &'a [Adapter]) -> Option<&'a Adapter> { + let n = name.trim().to_ascii_lowercase(); + adapters.iter().find(|a| a.name.trim().to_ascii_lowercase() == n) +} + +/// The row's words for an integrated GPU that is off by default (the switch turns it on; the choice is kept). +pub const INTEGRATED_REASON: &str = "integrated GPU, off by default (2 to 3 MH/s for 30 W)"; + /// The defaults the launchers use: discrete cards on (8 identities on a big card, 2 on a small one), integrated off /// (1 identity), Apple silicon on with 1. pub fn apply_defaults(c: &mut CardState) { @@ -87,7 +230,7 @@ pub fn apply_defaults(c: &mut CardState) { "integrated" => { c.enabled = false; c.identities = 1; - c.reason = "integrated: about 3 MH/s and it shares your system memory. Switch it on if you want it.".into(); + c.reason = INTEGRATED_REASON.into(); } _ => { c.enabled = true; @@ -148,19 +291,20 @@ pub fn nvidia_power_limits() -> std::collections::HashMap) -> Vec { - let mut cards = Vec::new(); +pub fn detect(bins: &Bins) -> Detection { + let mut d = Detection::default(); let Some(metal) = bins.metal.as_ref() else { - notes.push("the Metal worker (igneum-bench) is missing from the app".into()); - return cards; + d.notes.push("the Metal worker (igneum-bench) is missing from the app".into()); + return d; }; // the worker's own ready line: "ready metal Apple_M5_Max dataset-log2 28 batch 4194304 prepare 1" let out = run_timeout(Command::new(metal).arg("--serve"), Some("quit\n"), Duration::from_secs(20)).unwrap_or_default(); let ready = out.lines().find(|l| l.starts_with("ready ")); let Some(ready) = ready else { - notes.push(format!("the Metal worker did not report ready: {}", out.lines().last().unwrap_or("no output"))); - return cards; + d.notes.push(format!("the Metal worker did not report ready: {}", out.lines().last().unwrap_or("no output"))); + return d; }; + d.metal_listed = true; let fields: Vec<&str> = ready.split_whitespace().collect(); let name = fields.get(2).map(|s| s.replace('_', " ")).unwrap_or_else(|| "Apple GPU".into()); let prepare = fields.windows(2).any(|w| w[0] == "prepare" && w[1] == "1"); @@ -175,58 +319,62 @@ pub fn detect(bins: &Bins, notes: &mut Vec) -> Vec { } } } - if let Some(mem) = run_timeout(Command::new(crate::platform::tool("sysctl")).args(["-n", "hw.memsize"]), None, Duration::from_secs(3)) { - if let Ok(b) = mem.trim().parse::() { - let gb = b / (1024 * 1024 * 1024); - detail = if detail.is_empty() { format!("{gb} GB unified memory") } else { format!("{detail}, {gb} GB unified memory") }; - } + let mem = run_timeout(Command::new(crate::platform::tool("sysctl")).args(["-n", "hw.memsize"]), None, Duration::from_secs(3)).and_then(|m| m.trim().parse::().ok()); + if let Some(b) = mem { + let gb = b / (1024 * 1024 * 1024); + detail = if detail.is_empty() { format!("{gb} GB unified memory") } else { format!("{detail}, {gb} GB unified memory") }; } if !prepare { - notes.push("this Metal worker has no prepare support; the miner restarts at the hour boundary".into()); + d.notes.push("this Metal worker has no prepare support; the miner restarts at the hour boundary".into()); } let mut c = card(0, &name, "apple", "Metal", &detail, ""); c.kind = "apple".into(); c.path = "prebuilt".into(); - if let Some(mem) = run_timeout(Command::new(crate::platform::tool("sysctl")).args(["-n", "hw.memsize"]), None, Duration::from_secs(3)) { - c.vram_mb = mem.trim().parse::().map(|b| b / (1024 * 1024)).unwrap_or(0); - } + c.vram_mb = mem.map(|b| b / (1024 * 1024)).unwrap_or(0); apply_defaults(&mut c); mark_sweep_support(&mut c); - cards.push(c); - cards + d.cards.push(c); + d } #[cfg(not(target_os = "macos"))] -pub fn detect(bins: &Bins, notes: &mut Vec) -> Vec { - let mut cards: Vec = Vec::new(); +pub fn detect(bins: &Bins) -> Detection { + let mut d = Detection::default(); + // Windows' own view of every adapter: status and problem code (a Code 43 card is in no tool's list), memory and + // processor for the integrated call, names for the last resort + let adapters = adapters(); + d.adapters_listed = adapters.is_some(); + let adapters = adapters.unwrap_or_default(); // NVIDIA: nvidia-smi ships with the driver - let smi = run_timeout(Command::new(crate::platform::tool("nvidia-smi")).args(["--query-gpu=index,name,memory.total", "--format=csv,noheader"]), None, Duration::from_secs(10)); + let smi = run_timeout(Command::new(crate::platform::tool("nvidia-smi")).args(["--query-gpu=index,name,memory.total,pci.bus_id", "--format=csv,noheader"]), None, Duration::from_secs(10)); match smi { Some(out) => { + d.nvidia_listed = true; for line in out.lines() { let parts: Vec<&str> = line.split(',').map(|s| s.trim()).collect(); if parts.len() >= 2 && parts[0].chars().all(|c| c.is_ascii_digit()) && !parts[0].is_empty() { let mem_mb: u64 = parts.get(2).and_then(|m| m.split_whitespace().next()).and_then(|n| n.parse::().ok()).map(|v| v as u64).unwrap_or(0); let detail = if mem_mb > 0 { format!("{} GB", (mem_mb + 512) / 1024) } else { String::new() }; let worker_ok = bins.cuda.is_some(); - let mut c = card(cards.len(), parts[1], "nvidia", "CUDA", &detail, parts[0]); - c.kind = if looks_integrated(parts[1]) { "integrated".into() } else { "discrete".into() }; + let mut c = card(d.cards.len(), parts[1], "nvidia", "CUDA", &detail, parts[0]); + c.bus = parts.get(3).map(|b| b.to_string()).unwrap_or_default(); + c.kind = classify_kind(parts[1], adapter_for(parts[1], &adapters)).into(); c.vram_mb = mem_mb; c.path = if worker_ok { "prebuilt".into() } else { "build".into() }; if !worker_ok { c.message = "no prebuilt CUDA worker in the package; built from source on first run (needs the CUDA Toolkit and Visual Studio)".into(); } apply_defaults(&mut c); - cards.push(c); + d.cards.push(c); } } - if cards.is_empty() { - notes.push("nvidia-smi ran but listed no card".into()); + if d.cards.is_empty() { + d.notes.push("nvidia-smi ran but listed no card".into()); } let limits = nvidia_power_limits(); - for c in cards.iter_mut() { - if let Some((d, cur, lo, hi)) = limits.get(&c.device) { - c.power_default_w = *d; + for c in d.cards.iter_mut() { + if let Some((dflt, cur, lo, hi)) = limits.get(&c.device) { + c.power_default_w = *dflt; c.power_limit_w = *cur; c.power_before_w = *cur; c.power_min_w = *lo; @@ -235,12 +383,13 @@ pub fn detect(bins: &Bins, notes: &mut Vec) -> Vec { mark_sweep_support(c); } } - None => notes.push("nvidia-smi is not on this PC (no NVIDIA driver): no NVIDIA card".into()), + None => d.notes.push("nvidia-smi is not on this PC (no NVIDIA driver): no NVIDIA card".into()), } // OpenCL: the worker's own device list (AMD, Intel; NVIDIA shows there too and is skipped) if let Some(cl) = bins.opencl.as_ref() { if let Some(out) = run_timeout(Command::new(cl).arg("--list"), None, Duration::from_secs(15)) { let lines: Vec<&str> = out.lines().collect(); + d.opencl_listed = lines.iter().any(|l| l.starts_with("OpenCL devices")); for (i, line) in lines.iter().enumerate() { let t = line.trim_start_matches(|c| c == ' ' || c == '*').trim(); if !t.starts_with('[') { @@ -256,34 +405,59 @@ pub fn detect(bins: &Bins, notes: &mut Vec) -> Vec { if !is_gpu || name.contains("NVIDIA") || vendor_s.contains("NVIDIA") { continue; } - let vendor = if vendor_s.contains("Advanced Micro") || name.contains("Radeon") || name.contains("AMD") { "amd" } else { "other" }; + let vendor = if vendor_s.contains("Advanced Micro") || name.contains("Radeon") || name.contains("AMD") || name.starts_with("gfx") { "amd" } else { "other" }; let units = info.split(", ").find(|p| p.contains("compute units")).unwrap_or("").to_string(); - let mut c = card(cards.len(), name, vendor, "OpenCL", &units, idx); + let mut c = card(d.cards.len(), name, vendor, "OpenCL", &units, idx); c.device = idx.to_string(); - c.kind = if looks_integrated(name) { "integrated".into() } else { "discrete".into() }; + c.kind = classify_kind(name, adapter_for(name, &adapters)).into(); c.path = "prebuilt".into(); apply_defaults(&mut c); mark_sweep_support(&mut c); - cards.push(c); + d.cards.push(c); } } - } else if cards.is_empty() { - notes.push("the OpenCL worker is not installed; AMD and Intel cards cannot be listed".into()); + } else if d.cards.is_empty() { + d.notes.push("the OpenCL worker is not installed; AMD and Intel cards cannot be listed".into()); } - if cards.is_empty() { + if d.cards.is_empty() { // last resort: the names Windows knows, so the screen can at least say what is in the PC - if let Some(out) = run_timeout(Command::new(crate::platform::tool("powershell")).args(["-NoProfile", "-Command", "Get-CimInstance Win32_VideoController | ForEach-Object { $_.Name }"]), None, Duration::from_secs(15)) { - for n in out.lines().map(|l| l.trim()).filter(|l| !l.is_empty()) { - let vendor = if n.contains("NVIDIA") { "nvidia" } else if n.contains("AMD") || n.contains("Radeon") { "amd" } else { "other" }; - let mut c = card(cards.len(), n, vendor, if vendor == "nvidia" { "CUDA" } else { "OpenCL" }, "", "0"); - c.kind = if looks_integrated(n) { "integrated".into() } else { "unknown".into() }; - c.enabled = false; - c.reason = "seen by Windows, but no worker can drive it (no NVIDIA driver and no OpenCL worker)".into(); - cards.push(c); - } + for a in adapters.iter().filter(|a| a.problem().is_none()) { + let vendor = vendor_of(&a.name); + let mut c = card(d.cards.len(), &a.name, vendor, if vendor == "nvidia" { "CUDA" } else { "OpenCL" }, "", ""); + c.kind = if looks_integrated(&a.name) { "integrated".into() } else { "unknown".into() }; + c.enabled = false; + c.reason = "seen by Windows, but no worker can drive it (no NVIDIA driver and no OpenCL worker)".into(); + d.cards.push(c); } } - cards + // the cards Windows lists with a problem (Code 43 after an eGPU hot-plug on PC 1, 5 October 2026): shown, never driven + for a in adapters.iter() { + let Some(problem) = a.problem() else { continue }; + if d.cards.iter().any(|c| c.name.trim().eq_ignore_ascii_case(a.name.trim())) { + continue; + } + let vendor = vendor_of(&a.name); + let mut c = card(d.cards.len(), &a.name, vendor, if vendor == "nvidia" { "CUDA" } else { "OpenCL" }, "", ""); + c.bus = a.pnp_id.clone(); + c.kind = classify_kind(&a.name, Some(a)).into(); + c.vram_mb = a.ram_mb; + mark_unusable(&mut c, &problem); + d.cards.push(c); + } + d +} + +/// A listed card no worker can drive: off, no switch, the problem on the row and the hint under it. +pub fn mark_unusable(c: &mut CardState, problem: &str) { + c.problem = problem.to_string(); + c.enabled = false; + c.identities = 1; + c.state = "unusable".into(); + c.message = format!("not usable ({problem})"); + c.reason = PROBLEM_HINT.into(); + c.sweep_supported = false; + c.sweep_state = "unsupported".into(); + c.sweep_note = c.message.clone(); } /// Finds the binaries next to the engine (Windows, a plain folder) or in Contents/Resources/bin (macOS bundle). @@ -322,3 +496,105 @@ pub fn node_version(node: &Path) -> String { .and_then(|o| o.lines().next().map(|l| l.trim().to_string())) .unwrap_or_default() } + +#[cfg(test)] +mod tests { + use super::*; + + // PC 1's adapter list on the evening of 5 October 2026, after the RX 9070 XT went in through the Sonnet eGPU box + // while the app ran: "AMD Radeon RX 9070 XT | status Error | ram 0 GB", "AMD Radeon(TM) Graphics | status OK | + // ram 2 GB" (the Ryzen iGPU, gfx1036 to OpenCL), "NVIDIA GeForce RTX 5090 | status OK". + fn pc1() -> Vec { + parse_adapters(r#"[{"Name":"AMD Radeon RX 9070 XT","Status":"Error","ConfigManagerErrorCode":43,"AdapterRAM":0,"VideoProcessor":"AMD Radeon Graphics Processor (0x7550)","PNPDeviceID":"PCI\\VEN_1002&DEV_7550&SUBSYS_0E4E1002&REV_C0\\6&1A2B3C4D&0&00000008"}, + {"Name":"AMD Radeon(TM) Graphics","Status":"OK","ConfigManagerErrorCode":0,"AdapterRAM":2147483648,"VideoProcessor":"AMD Radeon Graphics Processor (0x164E)","PNPDeviceID":"PCI\\VEN_1002&DEV_164E&SUBSYS_00000000&REV_C1\\4&2E5A1B3&0&0041"}, + {"Name":"NVIDIA GeForce RTX 5090","Status":"OK","ConfigManagerErrorCode":0,"AdapterRAM":4293918720,"VideoProcessor":"NVIDIA GeForce RTX 5090","PNPDeviceID":"PCI\\VEN_10DE&DEV_2B85&SUBSYS_10621043&REV_A1\\4&1F2E3D4C&0&0019"}]"#) + } + + #[test] + fn adapters_parse_with_status_code_and_memory() { + let a = pc1(); + assert_eq!(a.len(), 3); + assert_eq!(a[0].name, "AMD Radeon RX 9070 XT"); + assert_eq!(a[0].status, "Error"); + assert_eq!(a[0].code, 43); + assert_eq!(a[0].ram_mb, 0); + assert_eq!(a[0].problem().as_deref(), Some("Code 43")); + assert_eq!(a[1].ram_mb, 2048); + assert_eq!(a[1].problem(), None); + assert_eq!(a[2].problem(), None); + // a single adapter: ConvertTo-Json gives one object, not an array + let one = parse_adapters(r#"{"Name":"NVIDIA GeForce RTX 5090","Status":"OK","ConfigManagerErrorCode":0,"AdapterRAM":4293918720,"VideoProcessor":"NVIDIA GeForce RTX 5090","PNPDeviceID":"PCI\\VEN_10DE"}"#); + assert_eq!(one.len(), 1); + assert!(parse_adapters("not json").is_empty()); + } + + #[test] + fn problem_without_a_code_is_the_status_word() { + let a = Adapter { name: "x".into(), status: "Degraded".into(), ..Default::default() }; + assert_eq!(a.problem().as_deref(), Some("status Degraded")); + let fine = Adapter { name: "x".into(), status: "OK".into(), ..Default::default() }; + assert_eq!(fine.problem(), None); + let code12 = Adapter { name: "x".into(), status: "Error".into(), code: 12, ..Default::default() }; + assert_eq!(code12.problem().as_deref(), Some("Code 12")); + } + + #[test] + fn kind_from_pc1_lines_and_the_mac() { + let a = pc1(); + // the discrete cards, with and without their adapter row + assert_eq!(classify_kind("AMD Radeon RX 9070 XT", adapter_for("AMD Radeon RX 9070 XT", &a)), "discrete"); + assert_eq!(classify_kind("NVIDIA GeForce RTX 5090", adapter_for("NVIDIA GeForce RTX 5090", &a)), "discrete"); + assert_eq!(classify_kind("NVIDIA GeForce RTX 5090", None), "discrete"); + // the Ryzen iGPU: by its Windows name, and by the gfx code the OpenCL worker prints (no adapter row matches a code) + assert_eq!(classify_kind("AMD Radeon(TM) Graphics", adapter_for("AMD Radeon(TM) Graphics", &a)), "integrated"); + assert_eq!(classify_kind("gfx1036", adapter_for("gfx1036", &a)), "integrated"); + assert_eq!(classify_kind("gfx1036", None), "integrated"); + assert_eq!(classify_kind("gfx1036:xnack-", None), "integrated"); + // a discrete gfx code stays discrete; an Arc card is discrete despite "Intel" + assert_eq!(classify_kind("gfx1201", None), "discrete"); + assert_eq!(classify_kind("gfx1030", None), "discrete"); + assert_eq!(classify_kind("Intel(R) Arc(TM) A770 Graphics", None), "discrete"); + // an Intel iGPU by its processor string; an AMD discrete card's processor string ("AMD Radeon Graphics Processor") does not count + let intel = Adapter { name: "Intel(R) Iris(R) Xe Graphics".into(), processor: "Intel(R) Iris(R) Xe Graphics Family".into(), ram_mb: 1024, ..Default::default() }; + assert_eq!(classify_kind("Intel(R) Iris(R) Xe Graphics", Some(&intel)), "integrated"); + assert_eq!(a[0].processor, "AMD Radeon Graphics Processor (0x7550)"); + // shared memory under 1 GB on the adapter row makes an unknown name integrated; 0 says nothing + let small = Adapter { name: "Some iGPU".into(), ram_mb: 512, ..Default::default() }; + assert_eq!(classify_kind("Some iGPU", Some(&small)), "integrated"); + let unknown = Adapter { name: "Some card".into(), ram_mb: 0, ..Default::default() }; + assert_eq!(classify_kind("Some card", Some(&unknown)), "discrete"); + // the Mac: detect() labels Apple silicon "apple" itself; the name rules do not call it integrated + assert!(!looks_integrated("Apple M5 Max")); + assert_eq!(vendor_of("Apple M5 Max"), "apple"); + assert_eq!(vendor_of("gfx1036"), "amd"); + assert_eq!(vendor_of("AMD Radeon RX 9070 XT"), "amd"); + assert_eq!(vendor_of("NVIDIA GeForce RTX 5090"), "nvidia"); + } + + #[test] + fn unusable_card_row() { + let mut c = card(2, "AMD Radeon RX 9070 XT", "amd", "OpenCL", "", ""); + mark_unusable(&mut c, "Code 43"); + assert!(!c.enabled); + assert_eq!(c.state, "unusable"); + assert_eq!(c.message, "not usable (Code 43)"); + assert_eq!(c.reason, PROBLEM_HINT); + assert!(!c.present()); + } + + #[test] + fn integrated_default_is_off_with_the_row_words() { + let mut c = card(1, "gfx1036", "amd", "OpenCL", "2 compute units", "1"); + c.kind = classify_kind("gfx1036", None).into(); + apply_defaults(&mut c); + assert!(!c.enabled); + assert_eq!(c.identities, 1); + assert_eq!(c.reason, INTEGRATED_REASON); + let mut big = card(0, "NVIDIA GeForce RTX 5090", "nvidia", "CUDA", "32 GB", "0"); + big.kind = "discrete".into(); + big.vram_mb = 32768; + apply_defaults(&mut big); + assert!(big.enabled); + assert_eq!(big.identities, 8); + } +} diff --git a/app/igneum-app/src/engine.rs b/app/igneum-app/src/engine.rs index 85075432..ff2b34ad 100644 --- a/app/igneum-app/src/engine.rs +++ b/app/igneum-app/src/engine.rs @@ -29,7 +29,8 @@ pub struct CardChoice { pub enum Cmd { Detect, ApplyCards(Vec), - Detected(Vec, Vec), + /// one enumeration of the cards finished (the first, or a re-detection: src/hotplug.rs) + Detected(crate::detect::Detection), Start, Pause, Resume, @@ -436,6 +437,12 @@ pub struct Engine { wrapper: bool, last_state_print: Instant, detected: bool, + /// hot-plug (src/hotplug.rs): an enumeration thread is running; one more was asked for meanwhile; when the + /// next periodic one is due; when the app log last carried the card list + detect_busy: bool, + detect_again: bool, + detect_next: Instant, + last_cards_line: Instant, clock_samples: Vec, clock_node_at: Option, clock_node_behind: f64, @@ -530,6 +537,10 @@ impl Engine { wrapper, last_state_print: now, detected: false, + detect_busy: false, + detect_again: false, + detect_next: now + Duration::from_secs(crate::hotplug::POLL_S), + last_cards_line: now, clock_samples: Vec::new(), clock_node_at: None, clock_node_behind: 0.0, @@ -635,62 +646,34 @@ impl Engine { fn command(&mut self, c: Cmd) { match c { Cmd::Detect => { - if self.detected { + // the first run, /api/detect, the host's "detect" on a device change, and the minute poll all land + // here; one enumeration at a time, and a request during one runs once more after it + if self.detect_busy { + self.detect_again = true; return; } - self.st().detecting = true; + self.detect_busy = true; + if !self.detected { + self.st().detecting = true; + } let bins = self.bins.clone(); let shared = self.shared.clone(); std::thread::spawn(move || { - let mut notes = Vec::new(); - let cards = crate::detect::detect(&bins, &mut notes); - shared.send(Cmd::Detected(cards, notes)); + let d = crate::detect::detect(&bins); + shared.send(Cmd::Detected(d)); }); } - Cmd::Detected(cards, notes) => { - self.detected = true; - let names: Vec = cards.iter().map(|c| format!("{} ({})", c.name, c.worker)).collect(); - self.shared.log(&format!("GPUs: {}", if names.is_empty() { "none usable".to_string() } else { names.join("; ") })); - for n in ¬es { - self.shared.log(&format!("detection: {n}")); + Cmd::Detected(d) => { + self.detect_busy = false; + self.detect_next = Instant::now() + Duration::from_secs(crate::hotplug::POLL_S); + if self.detected { + self.merge_detection(d); + } else { + self.first_detection(d); } - let prefs = self.shared.settings.lock().unwrap().cards.clone(); - let mut st = self.st(); - st.detecting = false; - st.detect_message = notes.join(". "); - st.mining.cards = cards; - for c in st.mining.cards.iter_mut() { - if let Some(p) = prefs.get(&c.key) { - c.enabled = p.enabled && c.kind != "unknown"; - c.identities = p.identities.clamp(1, 64); - c.reason = String::new(); - if c.vendor == "nvidia" && p.power_pct > 0 { - c.power_pct = p.power_pct.clamp(crate::sweep::MIN_PCT, 100); - } - c.pinned = p.pinned; - c.sweep_pct = p.sweep_pct; - c.sweep_eff = p.sweep_eff; - c.sweep_watts = p.sweep_watts; - c.sweep_mhs = p.sweep_mhs; - c.sweep_at = p.sweep_at as f64; - } - } - let supported: Vec = st.mining.cards.iter().filter(|c| c.sweep_supported && c.enabled).map(|c| c.key.clone()).collect(); - let unsupported: Vec = st.mining.cards.iter().filter(|c| !c.sweep_supported).map(|c| format!("{}: {}", c.name, c.sweep_note)).collect(); - drop(st); - for u in &unsupported { - self.shared.log(&format!("efficiency sweep {u}")); - } - if self.shared.runtime.sweep_only { - if supported.is_empty() { - self.sweep_say("SWEEP none reason=no_supported_card"); - self.shared.send(Cmd::Quit); - } else { - self.sweep_queue = supported; - } - } - if self.running { - self.plan_miners(); + if self.detect_again { + self.detect_again = false; + self.shared.send(Cmd::Detect); } } Cmd::ApplyCards(choices) => self.apply_cards(choices), @@ -850,7 +833,7 @@ impl Engine { let mut missing = Vec::new(); { let mut st = self.st(); - for c in st.mining.cards.iter_mut().filter(|c| c.vendor == "nvidia" && c.enabled && c.power_default_w > 0.0) { + for c in st.mining.cards.iter_mut().filter(|c| c.vendor == "nvidia" && c.enabled && c.present() && c.power_default_w > 0.0) { let want = requested_watts(c); let got = readback.get(&c.device).copied().unwrap_or(c.power_limit_w); if got > 0.0 { @@ -971,6 +954,139 @@ impl Engine { // ---- start, node ------------------------------------------------------------------------------------------ + /// The first enumeration: the list as detected, the saved choices applied, the setup screen's notes. + fn first_detection(&mut self, d: crate::detect::Detection) { + self.detected = true; + let crate::detect::Detection { cards, notes, .. } = d; + let names: Vec = cards.iter().map(|c| format!("{} ({}{})", c.name, c.worker, if c.problem.is_empty() { String::new() } else { format!(", {}", c.problem) })).collect(); + self.shared.log(&format!("GPUs: {}", if names.is_empty() { "none usable".to_string() } else { names.join("; ") })); + for n in ¬es { + self.shared.log(&format!("detection: {n}")); + } + let prefs = self.shared.settings.lock().unwrap().cards.clone(); + let mut st = self.st(); + st.detecting = false; + st.detect_message = notes.join(". "); + st.mining.cards = cards; + for c in st.mining.cards.iter_mut() { + if let Some(p) = crate::hotplug::pref_for(&prefs, c) { + crate::hotplug::apply_pref(c, p); + } + } + let supported: Vec = st.mining.cards.iter().filter(|c| c.sweep_supported && c.enabled).map(|c| c.key.clone()).collect(); + let unsupported: Vec = st.mining.cards.iter().filter(|c| !c.sweep_supported).map(|c| format!("{}: {}", c.name, c.sweep_note)).collect(); + let line = crate::hotplug::cards_line(&st.mining.cards); + drop(st); + self.shared.log(&line); + self.last_cards_line = Instant::now(); + for u in &unsupported { + self.shared.log(&format!("efficiency sweep {u}")); + } + if self.shared.runtime.sweep_only { + if supported.is_empty() { + self.sweep_say("SWEEP none reason=no_supported_card"); + self.shared.send(Cmd::Quit); + } else { + self.sweep_queue = supported; + } + } + if self.running { + self.plan_miners(); + } + } + + /// A later enumeration (src/hotplug.rs): new cards start, lost cards stop, faulty cards are marked; a list + /// that changed nothing touches nothing (no running worker ever restarts because of a re-detection). + fn merge_detection(&mut self, d: crate::detect::Detection) { + let now = crate::platform::unix_now_f(); + let old = self.st().mining.cards.clone(); + let diff = crate::hotplug::diff(&old, &d.cards, &|c| d.listed(c)); + if diff.is_quiet() { + return; + } + let prefs = self.shared.settings.lock().unwrap().cards.clone(); + let mut new_nvidia = false; + for i in diff.removed.iter().copied() { + let name = old[i].name.clone(); + self.drop_worker(i, "the card was removed"); + if let Some(c) = self.st().mining.cards.get_mut(i) { + crate::hotplug::mark_removed(c, now); + } + self.shared.event("warn", &format!("Card removed: {name}; its worker stopped")); + } + for (i, problem) in diff.errored.iter() { + let name = old[*i].name.clone(); + self.drop_worker(*i, &format!("the card reports {problem}")); + if let Some(c) = self.st().mining.cards.get_mut(*i) { + crate::detect::mark_unusable(c, problem); + c.added_at = now; + } + self.shared.event("warn", &format!("{name}: not usable ({problem}); its worker stopped. {}", crate::detect::PROBLEM_HINT)); + } + for (i, fresh) in diff.moved.iter() { + if let Some(c) = self.st().mining.cards.get_mut(*i) { + self.shared.log(&format!("{}: device {} is now {} (the running worker keeps its device; the next start uses the new one)", c.name, c.device, fresh.device)); + c.device = fresh.device.clone(); + c.key = fresh.key.clone(); + c.bus = fresh.bus.clone(); + if fresh.vram_mb > 0 { + c.vram_mb = fresh.vram_mb; + } + if !fresh.detail.is_empty() { + c.detail = fresh.detail.clone(); + } + } + } + let mut placed: Vec<(usize, CardState)> = Vec::new(); + for (i, fresh) in diff.recovered.into_iter().chain(diff.revived.into_iter()) { + placed.push((i, fresh)); + } + let mut next = self.st().mining.cards.len(); + for fresh in diff.added.into_iter() { + placed.push((next, fresh)); + next += 1; + } + for (i, mut fresh) in placed { + let pref = crate::hotplug::pref_for(&prefs, &fresh).cloned(); + crate::hotplug::settle_new(&mut fresh, i, pref.as_ref(), now); + let (kind, text) = crate::hotplug::added_words(&fresh); + new_nvidia |= fresh.vendor == "nvidia" && fresh.enabled; + let mut st = self.st(); + if i < st.mining.cards.len() { + st.mining.cards[i] = fresh; + } else { + st.mining.cards.push(fresh); + } + drop(st); + self.shared.event(kind, &text); + } + let line = crate::hotplug::cards_line(&self.st().mining.cards); + self.shared.log(&line); + self.last_cards_line = Instant::now(); + if self.running { + self.plan_miners(); + if new_nvidia { + self.apply_power_limits("new card"); + } + } + } + + /// Stops the worker on card `idx` (quit, then up to 8 s) and drops its slot; the sweep on it, if any, is aborted. + fn drop_worker(&mut self, idx: usize, why: &str) { + if self.sweep.as_ref().map(|r| r.card == idx).unwrap_or(false) { + self.sweep_abort(why); + } + let Some(pos) = self.miners.iter().position(|m| m.card == idx) else { return }; + let label = self.miners[pos].label.clone(); + if let Some(mut p) = self.miners[pos].proc.take() { + self.shared.log(&format!("stopping miner {label} ({why})")); + p.write_stdin("quit\n"); + p.stop(8); + } + self.miners.remove(pos); + self.stability.remove(&idx); + } + fn start(&mut self) { if self.running { return; @@ -1143,7 +1259,7 @@ impl Engine { let cards = self.st().mining.cards.clone(); let id8 = self.shared.runtime.id8(); for c in cards.iter() { - if !c.enabled || self.miners.iter().any(|m| m.card == c.index) { + if !c.enabled || !c.present() || self.miners.iter().any(|m| m.card == c.index) { continue; } let prefix = match c.vendor.as_str() { @@ -1307,7 +1423,7 @@ impl Engine { let mut st = self.st(); let paused = st.mining.paused; for c in st.mining.cards.iter_mut() { - if c.enabled && c.state != "faulted" { + if c.enabled && c.present() && c.state != "faulted" { c.state = if paused { "off".into() } else { "waiting".into() }; c.hash_now = 0.0; c.pid = 0; @@ -1348,6 +1464,9 @@ impl Engine { let mut st = self.st(); for ch in &choices { let Some(c) = st.mining.cards.iter_mut().find(|c| c.key == ch.key) else { continue }; + if !c.present() { + continue; // a removed or faulty card has no switch; its saved choice waits for it + } let identities = ch.identities.clamp(1, 64); let enabled = ch.enabled && c.kind != "unknown"; let power_pct = ch.power_pct.map(|p| p.clamp(crate::sweep::MIN_PCT, 100)).unwrap_or(c.power_pct); @@ -1366,7 +1485,7 @@ impl Engine { if c.enabled != enabled || c.identities != identities { c.enabled = enabled; c.identities = identities; - c.reason = String::new(); + c.reason = if !enabled && c.kind == "integrated" { crate::detect::INTEGRATED_REASON.into() } else { String::new() }; if !enabled { c.state = "off".into(); c.hash_now = 0.0; @@ -1422,7 +1541,7 @@ impl Engine { let mut what = Vec::new(); { let mut st = self.st(); - for c in st.mining.cards.iter_mut().filter(|c| c.vendor == "nvidia" && c.enabled && c.power_default_w > 0.0) { + for c in st.mining.cards.iter_mut().filter(|c| c.vendor == "nvidia" && c.enabled && c.present() && c.power_default_w > 0.0) { let pct = if c.power_pct == 0 { 80 } else { c.power_pct.clamp(crate::sweep::MIN_PCT, 100) }; c.power_pct = pct; let watts = requested_watts(c); @@ -1442,7 +1561,7 @@ impl Engine { self.shared.log(&format!("power cap ({why}): {}", cmds.join(" & "))); let line = cmds.join(" & "); let what = what.join(", "); - let want: std::collections::HashMap = self.st().mining.cards.iter().filter(|c| c.vendor == "nvidia" && c.enabled && c.power_default_w > 0.0).map(|c| (c.device.clone(), requested_watts(c))).collect(); + let want: std::collections::HashMap = self.st().mining.cards.iter().filter(|c| c.vendor == "nvidia" && c.enabled && c.present() && c.power_default_w > 0.0).map(|c| (c.device.clone(), requested_watts(c))).collect(); if self.wrapper && cfg!(windows) { // the window host has a UI context: it shows the administrator prompt and reports back on stdin self.power_via_host = Some((line.clone(), what.clone(), want, Instant::now())); @@ -1479,7 +1598,7 @@ impl Engine { .mining .cards .iter() - .filter(|c| c.vendor == "nvidia" && c.power_applied && c.power_before_w > 0.0) + .filter(|c| c.vendor == "nvidia" && c.present() && c.power_applied && c.power_before_w > 0.0) .map(|c| format!("\"{}\" -i {} -pl {}", crate::platform::tool("nvidia-smi").display(), c.device, c.power_before_w.round() as u64)) .collect(); drop(st); @@ -2019,6 +2138,16 @@ impl Engine { self.last_settings_save = now; self.shared.save_settings(); } + // hot-plug (src/hotplug.rs): enumerate again every POLL_S, and keep the card list in the log for the console + if self.detected && !self.detect_busy && !self.quitting && now >= self.detect_next { + self.detect_next = now + Duration::from_secs(crate::hotplug::POLL_S); + self.shared.send(Cmd::Detect); + } + if self.detected && now.duration_since(self.last_cards_line) >= Duration::from_secs(crate::hotplug::CARDS_LINE_S) { + self.last_cards_line = now; + let line = crate::hotplug::cards_line(&self.st().mining.cards); + self.shared.log(&line); + } } /// The over-the-air updater (src/ota.rs): checks, downloads and stages on its own threads; this tick hands it what @@ -2444,6 +2573,7 @@ impl Engine { c.eff_mhw = if c.state == "mining" && c.power_w > 1.0 && c.hash_now > 0.0 && unix - c.telemetry_at < 60.0 { c.hash_now / c.power_w } else { 0.0 }; } st.mining.hash_total = total; + crate::hotplug::age(&mut st.mining.cards, unix); let cut = unix - 3600.0; st.mining.found.retain(|t| *t > cut); if st.node.daa > 0 { diff --git a/app/igneum-app/src/hotplug.rs b/app/igneum-app/src/hotplug.rs new file mode 100644 index 00000000..2f05e672 --- /dev/null +++ b/app/igneum-app/src/hotplug.rs @@ -0,0 +1,464 @@ +//! Hot-plug: what changed between two enumerations of the cards (src/detect.rs runs one at start and one every +//! minute; the Windows host also asks for one on WM_DEVICECHANGE). Pure, so the rules are unit-tested here; the +//! engine applies the result (start a worker, stop one, mark a row). Born 5 October 2026, when an RX 9070 XT went +//! into PC 1 through an eGPU box while the app ran and nothing noticed. +//! +//! Rules: a card is the same card when its key (vendor:device:name) matches, or, failing that, when vendor and +//! name match and that pair is unique on both sides (a device index that moved because another card arrived). A +//! card missing from a list is removed only when the tool that lists its vendor answered. Removed and faulty cards +//! stay in the engine's list (the other cards' indices are the miner slots), marked, and the dashboard hides a +//! removed row after five minutes. + +use crate::config::CardPref; +use crate::state::CardState; + +/// How long a removed card's row says "removed" before it hides. +pub const REMOVED_SHOWN_S: f64 = 300.0; +/// How often the engine enumerates again, seconds (macOS has no GPU hot-plug on Apple silicon: slower there). +pub const POLL_S: u64 = if cfg!(target_os = "macos") { 300 } else { 60 }; +/// How often the app log carries the full card list, seconds (the console reads it from the log tail). +pub const CARDS_LINE_S: u64 = 600; + +#[derive(Default, Debug)] +pub struct Diff { + /// new cards (usable or with a problem), to be appended + pub added: Vec, + /// cards that were marked removed earlier and are listed again: (slot, the fresh entry) + pub revived: Vec<(usize, CardState)>, + /// slots whose card is gone + pub removed: Vec, + /// slots whose card now reports a problem: (slot, "Code 43") + pub errored: Vec<(usize, String)>, + /// slots whose card had a problem and is now driven by a tool again: (slot, the fresh entry) + pub recovered: Vec<(usize, CardState)>, + /// slots whose card is the same but its device index (or memory, bus) changed: (slot, the fresh entry) + pub moved: Vec<(usize, CardState)>, + pub unchanged: Vec, +} + +impl Diff { + /// Nothing to do: every present card is where it was. + pub fn is_quiet(&self) -> bool { + self.added.is_empty() && self.revived.is_empty() && self.removed.is_empty() && self.errored.is_empty() && self.recovered.is_empty() && self.moved.is_empty() + } +} + +fn same_identity(a: &CardState, b: &CardState) -> bool { + a.vendor == b.vendor && a.name.trim().eq_ignore_ascii_case(b.name.trim()) && (a.bus.is_empty() || b.bus.is_empty() || a.bus == b.bus) +} + +/// Compares the engine's list with a fresh enumeration. `listed(card)` says whether this enumeration's tools could +/// have seen that card (Detection::listed); a card its tool did not answer for is kept, not removed. +pub fn diff(old: &[CardState], fresh: &[CardState], listed: &dyn Fn(&CardState) -> bool) -> Diff { + let mut out = Diff::default(); + let mut used = vec![false; fresh.len()]; + let mut pair: Vec> = vec![None; old.len()]; + // exact keys first + for (i, o) in old.iter().enumerate() { + if let Some(j) = fresh.iter().enumerate().position(|(j, f)| !used[j] && f.key == o.key) { + used[j] = true; + pair[i] = Some(j); + } + } + // then vendor + name, when that pair is unique among what is still unmatched on both sides + for (i, o) in old.iter().enumerate() { + if pair[i].is_some() { + continue; + } + let cands: Vec = fresh.iter().enumerate().filter(|(j, f)| !used[*j] && same_identity(o, f)).map(|(j, _)| j).collect(); + let twins = old.iter().enumerate().filter(|(k, x)| pair[*k].is_none() && *k != i && same_identity(o, x)).count(); + if cands.len() == 1 && twins == 0 { + used[cands[0]] = true; + pair[i] = Some(cands[0]); + } + } + for (i, o) in old.iter().enumerate() { + match pair[i] { + Some(j) => { + let f = &fresh[j]; + if o.removed_at > 0.0 { + out.revived.push((i, f.clone())); + } else if o.problem.is_empty() && !f.problem.is_empty() { + out.errored.push((i, f.problem.clone())); + } else if !o.problem.is_empty() && f.problem.is_empty() { + out.recovered.push((i, f.clone())); + } else if !o.problem.is_empty() { + // still faulty: the same problem or a new code, nothing to start or stop + if o.problem != f.problem { + out.errored.push((i, f.problem.clone())); + } else { + out.unchanged.push(i); + } + } else if o.device != f.device || o.key != f.key { + out.moved.push((i, f.clone())); + } else { + out.unchanged.push(i); + } + } + None => { + if o.removed_at > 0.0 { + // already removed: stays hidden or shown as removed + out.unchanged.push(i); + } else if listed(o) { + out.removed.push(i); + } else { + out.unchanged.push(i); + } + } + } + } + for (j, f) in fresh.iter().enumerate() { + if !used[j] { + out.added.push(f.clone()); + } + } + out +} + +/// The saved choice for a card: by its key, else by vendor and name (the index moved since the choice was saved). +pub fn pref_for<'a>(prefs: &'a std::collections::HashMap, c: &CardState) -> Option<&'a CardPref> { + if let Some(p) = prefs.get(&c.key) { + return Some(p); + } + let head = format!("{}:", c.vendor); + let tail = format!(":{}", c.name); + let mut found: Vec<&CardPref> = prefs.iter().filter(|(k, _)| k.starts_with(&head) && k.ends_with(&tail)).map(|(_, p)| p).collect(); + if found.len() == 1 { found.pop() } else { None } +} + +/// Applies a saved choice to a freshly detected card (the first detection and every later one use this). +pub fn apply_pref(c: &mut CardState, p: &CardPref) { + if !c.problem.is_empty() { + return; + } + c.enabled = p.enabled && c.kind != "unknown"; + c.identities = p.identities.clamp(1, 64); + if c.enabled || c.kind != "integrated" { + c.reason = String::new(); + } + if c.vendor == "nvidia" && p.power_pct > 0 { + c.power_pct = p.power_pct.clamp(crate::sweep::MIN_PCT, 100); + } + c.pinned = p.pinned; + c.sweep_pct = p.sweep_pct; + c.sweep_eff = p.sweep_eff; + c.sweep_watts = p.sweep_watts; + c.sweep_mhs = p.sweep_mhs; + c.sweep_at = p.sweep_at as f64; +} + +/// A card a re-detection added (or brought back): the saved choice if there is one, else the detect defaults it +/// came with; its slot and the time it appeared. +pub fn settle_new(c: &mut CardState, index: usize, pref: Option<&CardPref>, now: f64) { + c.index = index; + c.added_at = now; + c.removed_at = 0.0; + c.gone = false; + if let Some(p) = pref { + apply_pref(c, p); + } + if c.problem.is_empty() { + c.state = if c.enabled { "waiting".into() } else { "off".into() }; + } +} + +/// Marks a card unplugged: no worker, the row says removed, the saved choice is untouched. +pub fn mark_removed(c: &mut CardState, now: f64) { + c.removed_at = now; + c.gone = false; + c.state = "removed".into(); + c.message = "removed".into(); + c.hash_now = 0.0; + c.pid = 0; + c.restart_in_s = 0; + c.ready = false; + c.prepared = false; +} + +/// A removed row hides after REMOVED_SHOWN_S; returns true when something changed. +pub fn age(cards: &mut [CardState], now: f64) -> bool { + let mut changed = false; + for c in cards.iter_mut() { + let gone = c.removed_at > 0.0 && now - c.removed_at >= REMOVED_SHOWN_S; + if gone != c.gone { + c.gone = gone; + changed = true; + } + } + changed +} + +/// The event line for a card that appeared: "New card: , mining" and its kind (ok | info | warn). +pub fn added_words(c: &CardState) -> (&'static str, String) { + if !c.problem.is_empty() { + ("warn", format!("New card: {}, not usable ({}); {}", c.name, c.problem, crate::detect::PROBLEM_HINT)) + } else if c.enabled { + ("ok", format!("New card: {}, mining", c.name)) + } else if c.kind == "integrated" { + ("info", format!("New card: {}, off ({})", c.name, crate::detect::INTEGRATED_REASON)) + } else { + ("info", format!("New card: {}, off (switched off in settings)", c.name)) + } +} + +/// One word for a card's state on the log line and the console: mining, waiting, off, removed, not usable (Code 43). +pub fn state_word(c: &CardState) -> String { + if c.removed_at > 0.0 { + "removed".into() + } else if !c.problem.is_empty() { + format!("not usable ({})", c.problem) + } else if !c.enabled { + "off".into() + } else { + c.state.clone() + } +} + +/// The app-log line the console reads (relay/lib/parse.mjs): `cards: [, ] | ...`, hidden rows +/// left out, `cards: none` when nothing is listed. +pub fn cards_line(cards: &[CardState]) -> String { + let parts: Vec = cards.iter().filter(|c| !c.gone).map(|c| format!("{} [{}, {}]", c.name.replace('|', "/").replace('[', "(").replace(']', ")"), c.kind, state_word(c))).collect(); + if parts.is_empty() { "cards: none".into() } else { format!("cards: {}", parts.join(" | ")) } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::detect::{classify_kind, mark_unusable, INTEGRATED_REASON}; + + fn card(name: &str, vendor: &str, device: &str) -> CardState { + let mut c = CardState { index: 0, key: format!("{vendor}:{device}:{name}"), name: name.into(), vendor: vendor.into(), worker: if vendor == "nvidia" { "CUDA".into() } else { "OpenCL".into() }, device: device.into(), enabled: true, state: "off".into(), ..Default::default() }; + c.kind = classify_kind(name, None).into(); + crate::detect::apply_defaults(&mut c); + c + } + // PC 1 at start on 5 October 2026: the 5090 on nvidia-smi index 0, the Ryzen iGPU as OpenCL device 0 (gfx1036) + fn pc1_start() -> Vec { + let mut a = card("NVIDIA GeForce RTX 5090", "nvidia", "0"); + a.bus = "00000000:01:00.0".into(); + a.state = "mining".into(); + let mut b = card("gfx1036", "amd", "0"); + b.index = 1; + vec![a, b] + } + fn all_listed(_: &CardState) -> bool { + true + } + fn fresh_pc1_with_egpu() -> Vec { + let mut v = pc1_start(); + v[0].state = "off".into(); + let mut e = card("gfx1201", "amd", "1"); + e.index = 2; + v.push(e); + v + } + + #[test] + fn unchanged_list_is_quiet() { + let old = pc1_start(); + let mut fresh = pc1_start(); + fresh[0].state = "off".into(); // runtime state in the fresh list means nothing + let d = diff(&old, &fresh, &all_listed); + assert!(d.is_quiet(), "{d:?}"); + assert_eq!(d.unchanged, vec![0, 1]); + } + + #[test] + fn a_new_usable_card_is_added_and_nothing_else_moves() { + let old = pc1_start(); + let d = diff(&old, &fresh_pc1_with_egpu(), &all_listed); + assert_eq!(d.added.len(), 1); + assert_eq!(d.added[0].name, "gfx1201"); + assert_eq!(d.added[0].kind, "discrete"); + assert!(d.added[0].enabled); + assert_eq!(d.unchanged, vec![0, 1]); + assert!(d.removed.is_empty() && d.moved.is_empty() && d.errored.is_empty()); + } + + #[test] + fn a_new_card_with_a_problem_is_added_as_unusable() { + let old = pc1_start(); + let mut fresh = pc1_start(); + let mut bad = card("AMD Radeon RX 9070 XT", "amd", ""); + mark_unusable(&mut bad, "Code 43"); + fresh.push(bad); + let d = diff(&old, &fresh, &all_listed); + assert_eq!(d.added.len(), 1); + assert_eq!(d.added[0].problem, "Code 43"); + assert!(!d.added[0].enabled); + let (kind, text) = added_words(&d.added[0]); + assert_eq!(kind, "warn"); + assert!(text.starts_with("New card: AMD Radeon RX 9070 XT, not usable (Code 43); reboot with the card attached"), "{text}"); + assert_eq!(state_word(&d.added[0]), "not usable (Code 43)"); + } + + #[test] + fn an_unplugged_card_is_removed_only_when_its_tool_answered() { + let old = fresh_pc1_with_egpu(); + let fresh = pc1_start(); + let d = diff(&old, &fresh, &all_listed); + assert_eq!(d.removed, vec![2]); + assert_eq!(d.unchanged, vec![0, 1]); + // the OpenCL worker did not answer this round: nothing is called removed + let opencl_dead = |c: &CardState| c.vendor == "nvidia"; + let d2 = diff(&old, &pc1_start().into_iter().filter(|c| c.vendor == "nvidia").collect::>(), &opencl_dead); + assert!(d2.removed.is_empty(), "{d2:?}"); + assert!(d2.is_quiet()); + } + + #[test] + fn a_card_whose_index_moved_keeps_its_slot() { + // the eGPU landed before the iGPU in the OpenCL list: the iGPU is device 1 now, the eGPU device 0 + let old = pc1_start(); + let mut fresh = pc1_start(); + fresh[1].device = "1".into(); + fresh[1].key = "amd:1:gfx1036".into(); + let mut e = card("gfx1201", "amd", "0"); + e.index = 2; + fresh.push(e); + let d = diff(&old, &fresh, &all_listed); + assert_eq!(d.moved.len(), 1); + assert_eq!(d.moved[0].0, 1); + assert_eq!(d.moved[0].1.device, "1"); + assert_eq!(d.added.len(), 1); + assert!(d.removed.is_empty()); + } + + #[test] + fn two_identical_cards_are_told_apart_by_key_and_never_swapped() { + let mut a = card("NVIDIA GeForce RTX 5090", "nvidia", "0"); + let mut b = card("NVIDIA GeForce RTX 5090", "nvidia", "1"); + b.index = 1; + a.bus = "00000000:01:00.0".into(); + b.bus = "00000000:02:00.0".into(); + let old = vec![a.clone(), b.clone()]; + // one of the twins leaves: the other keeps its slot by key; the missing one is removed, not "moved" + let d = diff(&old, &[a.clone()], &all_listed); + assert_eq!(d.unchanged, vec![0]); + assert_eq!(d.removed, vec![1]); + // both twins present, both unmatched by key (indices swapped): the vendor+name rule refuses to guess + let mut a2 = a.clone(); + a2.device = "1".into(); + a2.key = "nvidia:1:NVIDIA GeForce RTX 5090".into(); + a2.bus = "00000000:03:00.0".into(); + let mut b2 = b.clone(); + b2.device = "0".into(); + b2.key = "nvidia:0:NVIDIA GeForce RTX 5090".into(); + b2.bus = "00000000:04:00.0".into(); + let d2 = diff(&old, &[a2, b2], &all_listed); + // the keys are the identity for twins: slot 0 is nvidia index 0 whichever bus it sits on; nothing restarts + assert!(d2.is_quiet(), "{d2:?}"); + assert_eq!(d2.unchanged, vec![0, 1]); + } + + #[test] + fn a_driven_card_that_turns_faulty_is_errored_and_recovers_later() { + let old = fresh_pc1_with_egpu(); + // the eGPU is still listed by Windows, now with Code 43, and no longer by OpenCL + let mut fresh = pc1_start(); + let mut bad = card("gfx1201", "amd", ""); + mark_unusable(&mut bad, "Code 43"); + fresh.push(bad); + let d = diff(&old, &fresh, &all_listed); + assert_eq!(d.errored, vec![(2, "Code 43".to_string())]); + assert!(d.added.is_empty() && d.removed.is_empty()); + // after a reboot with the card attached it is driven again: recovered, same slot + let mut faulty = old.clone(); + mark_unusable(&mut faulty[2], "Code 43"); + faulty[2].device = String::new(); + faulty[2].key = "amd::gfx1201".into(); + let d2 = diff(&faulty, &fresh_pc1_with_egpu(), &all_listed); + assert_eq!(d2.recovered.len(), 1); + assert_eq!(d2.recovered[0].0, 2); + assert!(d2.recovered[0].1.problem.is_empty()); + // the same problem again next minute: quiet + let d3 = diff(&faulty, &fresh, &all_listed); + assert!(d3.is_quiet(), "{d3:?}"); + } + + #[test] + fn a_removed_card_that_comes_back_is_revived_in_its_slot() { + let mut old = fresh_pc1_with_egpu(); + mark_removed(&mut old[2], 1000.0); + assert_eq!(state_word(&old[2]), "removed"); + // still absent: quiet (and hidden after five minutes) + let d = diff(&old, &pc1_start(), &all_listed); + assert!(d.is_quiet(), "{d:?}"); + assert!(age(&mut old, 1000.0 + REMOVED_SHOWN_S)); + assert!(old[2].gone); + assert!(!age(&mut old, 1000.0 + REMOVED_SHOWN_S + 1.0)); + // back: revived in slot 2 + let d2 = diff(&old, &fresh_pc1_with_egpu(), &all_listed); + assert_eq!(d2.revived.len(), 1); + assert_eq!(d2.revived[0].0, 2); + assert!(d2.added.is_empty()); + let mut back = d2.revived[0].1.clone(); + settle_new(&mut back, 2, None, 2000.0); + assert_eq!(back.index, 2); + assert_eq!(back.removed_at, 0.0); + assert!(!back.gone); + assert_eq!(back.added_at, 2000.0); + assert_eq!(back.state, "waiting"); + } + + #[test] + fn the_users_choice_is_kept_on_a_new_card_and_an_integrated_one_is_off_by_default() { + let mut prefs = std::collections::HashMap::new(); + // the user switched the iGPU on earlier and set 2 identities; the setting was saved under OpenCL index 0 + prefs.insert("amd:0:gfx1036".to_string(), CardPref { enabled: true, identities: 2, ..Default::default() }); + // the iGPU comes back as device 1 (the eGPU took index 0): the choice is found by vendor and name + let mut igpu = card("gfx1036", "amd", "1"); + assert_eq!(igpu.kind, "integrated"); + assert!(!igpu.enabled); + assert_eq!(igpu.reason, INTEGRATED_REASON); + let p = pref_for(&prefs, &igpu).cloned(); + assert!(p.is_some()); + settle_new(&mut igpu, 1, p.as_ref(), 5.0); + assert!(igpu.enabled); + assert_eq!(igpu.identities, 2); + assert_eq!(igpu.reason, ""); + assert_eq!(igpu.state, "waiting"); + // the user switched it off: the row keeps the integrated words + prefs.insert("amd:1:gfx1036".to_string(), CardPref { enabled: false, identities: 1, ..Default::default() }); + let mut igpu2 = card("gfx1036", "amd", "1"); + let p2 = pref_for(&prefs, &igpu2).cloned(); + settle_new(&mut igpu2, 1, p2.as_ref(), 6.0); + assert!(!igpu2.enabled); + assert_eq!(igpu2.reason, INTEGRATED_REASON); + assert_eq!(igpu2.state, "off"); + let (kind, text) = added_words(&igpu2); + assert_eq!(kind, "info"); + assert_eq!(text, format!("New card: gfx1036, off ({INTEGRATED_REASON})")); + // no saved choice: the detect default (integrated off, discrete on) + let mut egpu = card("gfx1201", "amd", "0"); + settle_new(&mut egpu, 2, None, 7.0); + assert!(egpu.enabled); + assert_eq!(added_words(&egpu), ("ok", "New card: gfx1201, mining".to_string())); + // a pref never switches on a card with a problem + let mut bad = card("AMD Radeon RX 9070 XT", "amd", ""); + mark_unusable(&mut bad, "Code 43"); + settle_new(&mut bad, 3, Some(&CardPref { enabled: true, identities: 8, ..Default::default() }), 8.0); + assert!(!bad.enabled); + assert_eq!(bad.state, "unusable"); + // two saved choices with the same vendor and name but different indices: ambiguous, so none is used + prefs.insert("amd:0:gfx1036".to_string(), CardPref { enabled: true, identities: 3, ..Default::default() }); + let other = card("gfx1036", "amd", "7"); + assert!(pref_for(&prefs, &other).is_none()); + } + + #[test] + fn the_console_line_lists_every_shown_card_with_kind_and_state() { + let mut cards = fresh_pc1_with_egpu(); + cards[0].state = "mining".into(); + cards[2].state = "starting".into(); + let mut bad = card("AMD Radeon RX 9070 XT", "amd", ""); + mark_unusable(&mut bad, "Code 43"); + cards.push(bad); + assert_eq!(cards_line(&cards), "cards: NVIDIA GeForce RTX 5090 [discrete, mining] | gfx1036 [integrated, off] | gfx1201 [discrete, starting] | AMD Radeon RX 9070 XT [discrete, not usable (Code 43)]"); + mark_removed(&mut cards[2], 10.0); + assert!(cards_line(&cards).contains("gfx1201 [discrete, removed]")); + age(&mut cards, 10.0 + REMOVED_SHOWN_S); + assert!(!cards_line(&cards).contains("gfx1201")); + assert_eq!(cards_line(&[]), "cards: none"); + } +} diff --git a/app/igneum-app/src/main.rs b/app/igneum-app/src/main.rs index 126291f0..dc37f2bb 100644 --- a/app/igneum-app/src/main.rs +++ b/app/igneum-app/src/main.rs @@ -16,6 +16,7 @@ mod config; mod detect; mod engine; +mod hotplug; mod keys; mod platform; mod procs; @@ -135,6 +136,8 @@ fn main() { "quit" => shared.send(engine::Cmd::Quit), "pause" => shared.send(engine::Cmd::Pause), "resume" => shared.send(engine::Cmd::Resume), + // the window host saw WM_DEVICECHANGE (a card plugged in or out): enumerate now, not at the next minute + "detect" => shared.send(engine::Cmd::Detect), "elevated ok" => shared.send(engine::Cmd::ElevatedDone(Ok(()))), _ if t.starts_with("elevated fail") => shared.send(engine::Cmd::ElevatedDone(Err(t.trim_start_matches("elevated fail").trim_start_matches(':').trim().to_string()))), _ => {} diff --git a/app/igneum-app/src/state.rs b/app/igneum-app/src/state.rs index faaa8199..c5784c67 100644 --- a/app/igneum-app/src/state.rs +++ b/app/igneum-app/src/state.rs @@ -26,7 +26,7 @@ pub struct NodeState { pub override_restart_wait: String, } -#[derive(Clone, Serialize, Default)] +#[derive(Clone, Serialize, Default, Debug)] pub struct CardState { pub index: usize, pub key: String, // stable id for the saved preference: vendor:device:name @@ -40,7 +40,7 @@ pub struct CardState { pub detail: String, // memory, cores pub device: String, // the worker's --device value (Windows) pub enabled: bool, - pub state: String, // off | waiting | starting | ready | mining | restarting | failed | faulted (the watchdog gave up on it) + pub state: String, // off | waiting | starting | ready | mining | restarting | failed | faulted (the watchdog gave up on it) | unusable (the OS reports a problem) | removed (unplugged) pub hash_now: f64, // MH/s, the last interval pub hash_avg: f64, // MH/s since the start pub accepted: u64, @@ -57,6 +57,15 @@ pub struct CardState { pub pid: u32, pub last_status_age_s: f64, pub message: String, + /// the device's own problem as the OS reports it ("Code 43" on Windows); set = listed but no worker can drive it + pub problem: String, + /// PCI bus id where the tool gives one (nvidia-smi pci.bus_id); a second way to recognise a card whose index moved + pub bus: String, + /// hot-plug (src/hotplug.rs): unix s when a re-detection added this card (0 = found at start), when it lost it + /// (0 = present), and `gone` once a removed card has been shown as removed for five minutes (the row hides) + pub added_at: f64, + pub removed_at: f64, + pub gone: bool, /// `WORKER FAULT` lines seen (the miner killed and restarted its worker) and the miner's own `faults=` count pub faults: u64, /// shares that failed the miner's CPU re-check this run (`mismatched=` on the STATUS line) @@ -92,6 +101,14 @@ pub struct CardState { pub race_variants: u32, } +impl CardState { + /// Listed, driver fine, not unplugged: a worker can run on it (hot-plug keeps removed and faulty cards in the + /// list so the other cards' indices stay put). + pub fn present(&self) -> bool { + self.removed_at == 0.0 && self.problem.is_empty() + } +} + #[derive(Clone, Serialize, Default)] pub struct MiningState { pub state: String, // idle | waiting | mining | paused | stopped diff --git a/app/igneum-app/ui/app.css b/app/igneum-app/ui/app.css index 07eb252e..4dc6820a 100644 --- a/app/igneum-app/ui/app.css +++ b/app/igneum-app/ui/app.css @@ -175,6 +175,10 @@ body[data-phase="welcome"] #screen-welcome,body[data-phase="cards"] #screen-card .gpu-row .meta b{color:var(--ink-2);font-weight:500} .gpu-row .reason{font-size:var(--t-sm);color:var(--ash)} .gpu-row .msg{font-size:var(--t-sm);color:var(--ember)} +.gpu-row.removed{opacity:.5} +.gpu-row.removed .ids,.gpu-row.unusable .ids{opacity:.3;pointer-events:none} +.gpu-row.unusable .name{color:var(--ember)} +.gpu-tile .st.removed{color:var(--ash)} .ids{display:flex;align-items:center;gap:6px;flex:0 0 auto} .ids .k{font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--ash);margin-right:var(--s-1)} .ids button{width:30px;height:30px;border-radius:8px;border:1px solid var(--line-2);background:transparent;color:var(--bone);cursor:pointer;font-size:16px;line-height:1} @@ -296,6 +300,7 @@ td .sub{display:block;font-family:var(--mono);font-size:var(--t-xs);color:var(-- .feed .t{color:var(--ash);flex:0 0 auto;font-size:var(--t-xs)} .feed .k{color:var(--molten);margin-right:6px} .feed .k.error{color:var(--ember)} +.feed .k.warn{color:var(--ember)} .feed .k.block{color:var(--ember)} .feed .k.build{color:var(--ink-2)} diff --git a/app/igneum-app/ui/app.js b/app/igneum-app/ui/app.js index 3d9b9128..8964aa25 100644 --- a/app/igneum-app/ui/app.js +++ b/app/igneum-app/ui/app.js @@ -9,11 +9,12 @@ 4 update available, downloading, ready, waiting for permission, manual 5 job done 6 updated Rules: a done job goes after 5 minutes; a failed job stays until closed; "updated" goes 60 s after the new version started; the clock notice is for the setup screens (the node card carries it on the dashboard); the job - notice is for the dashboard. */ + notice is for the dashboard. A card that appeared or went (hot-plug, src/hotplug.rs) is a level 5 notice for + 5 minutes on every screen: "New card: , mining", "New card: , not usable (Code 43)", "Card removed". */ var Notices = (function () { 'use strict'; - var LEVEL = { 'update-installing': 0, 'update-urgent': 0, 'update-failed': 0, 'job-failed': 1, clock: 2, 'job-running': 3, 'update-available': 4, 'job-done': 5, 'update-installed': 6 }; - var JOB_DONE_S = 300, INSTALLED_S = 60; + var LEVEL = { 'update-installing': 0, 'update-urgent': 0, 'update-failed': 0, 'job-failed': 1, clock: 2, 'job-running': 3, 'update-available': 4, 'job-done': 5, 'card-added': 5, 'card-removed': 5, 'update-installed': 6 }; + var JOB_DONE_S = 300, INSTALLED_S = 60, CARD_S = 300; function cap(t) { return t ? t.charAt(0).toUpperCase() + t.slice(1) : ''; } function firstLine(t, max) { t = String(t || '').split('\n')[0].trim(); max = max || 160; return t.length > max ? t.slice(0, max - 1).trim() + '\u2026' : t; } function endDot(t) { t = (t || '').trim(); return !t || /[.!?]$/.test(t) ? t : t + '.'; } @@ -91,6 +92,26 @@ var Notices = (function () { return notice('clock', 'clock:' + c.severity, c.message || '', { tone: c.severity === 'block' ? 'bad' : 'warn', detail: c.hint || '', actions: [{ act: 'sync', label: c.syncing ? 'Syncing' : 'Sync clock', primary: true, disabled: !!c.syncing }] }); } + // cards = state.mining.cards; now = state.now: every card a re-detection added or lost in the last 5 minutes + // (added_at / removed_at are 0 for the cards found at start). The newest first, so pick() shows it. + function kindWord(k) { return k === 'apple' ? 'Apple silicon' : k === 'discrete' ? 'Discrete' : k === 'integrated' ? 'Integrated' : k === 'external' ? 'External' : 'Unknown'; } + function cardNotices(cards, now) { + var out = []; + (cards || []).forEach(function (c) { + if (c.removed_at > 0) { + if (now - c.removed_at <= CARD_S) out.push(notice('card-removed', 'card:removed:' + c.key + ':' + Math.floor(c.removed_at), 'Card removed: ' + c.name + '. Its worker stopped.', { tone: 'warn', at: c.removed_at })); + return; + } + if (!(c.added_at > 0) || now - c.added_at > CARD_S) return; + // a new card with a problem, or a card that was mining and now reports one (both carry added_at): the row's words + if (c.problem) out.push(notice('card-added', 'card:added:' + c.key + ':' + Math.floor(c.added_at), c.name + ': not usable (' + c.problem + '). No worker runs on it.', { tone: 'warn', detail: c.reason || '', at: c.added_at })); + else if (c.enabled) out.push(notice('card-added', 'card:added:' + c.key + ':' + Math.floor(c.added_at), 'New card: ' + c.name + ', mining.', { at: c.added_at })); + else out.push(notice('card-added', 'card:added:' + c.key + ':' + Math.floor(c.added_at), 'New card: ' + c.name + ', off (' + kindWord(c.kind).toLowerCase() + '). Settings switches it on.', { at: c.added_at })); + }); + out.sort(function (a, b) { return b.at - a.at; }); + return out; + } + // every notice the state carries for this screen; pick() chooses the one to show function gather(s, where) { where = where || {}; @@ -98,6 +119,7 @@ var Notices = (function () { if (u) out.push(u); if (where.dashboard) { var j = jobNotice(s.jobs, s.now || 0); if (j) out.push(j); } else { var c = clockNotice(s.clock); if (c) out.push(c); } + if (s.mining && s.mining.cards) out = out.concat(cardNotices(s.mining.cards, s.now || 0)); return out; } // the lowest level wins; a dismissed key is skipped, so the next one shows in its place @@ -110,7 +132,7 @@ var Notices = (function () { } return best; } - return { LEVEL: LEVEL, JOB_DONE_S: JOB_DONE_S, INSTALLED_S: INSTALLED_S, updateNotice: updateNotice, jobNotice: jobNotice, clockNotice: clockNotice, gather: gather, pick: pick, jobTitle: jobTitle, cap: cap, endDot: endDot }; + return { LEVEL: LEVEL, JOB_DONE_S: JOB_DONE_S, INSTALLED_S: INSTALLED_S, CARD_S: CARD_S, updateNotice: updateNotice, jobNotice: jobNotice, clockNotice: clockNotice, cardNotices: cardNotices, gather: gather, pick: pick, jobTitle: jobTitle, cap: cap, endDot: endDot, kindWord: kindWord }; })(); /* ---------- the update card (pure; update-card.test.mjs loads this block) ---------- @@ -328,9 +350,11 @@ if (typeof document !== 'undefined') (function () { $('btn-settings').addEventListener('click', function () { fillSettings(); $('settings').hidden = false; }); $('btn-settings-close').addEventListener('click', function () { $('settings').hidden = true; $('s-key-box').hidden = true; $('s-key').textContent = ''; }); $('settings').addEventListener('click', function (e) { if (e.target === $('settings')) $('btn-settings-close').click(); }); + var settingsCardsSig = ''; function fillSettings() { if (!state) return; $('s-address').textContent = state.address.display || 'not set'; + settingsCardsSig = shownCards(state.mining.cards).map(function (c) { return c.key + ':' + (c.problem || '') + ':' + (c.removed_at > 0 ? 'r' : ''); }).join('|'); renderCardRows($('s-cards'), state.mining.cards); state.mining.cards.forEach(function (cd) { if (cd.vendor !== 'nvidia' || !(cd.power_default_w > 0)) return; @@ -679,27 +703,31 @@ if (typeof document !== 'undefined') (function () { if (cd.vendor === 'apple') return ''; return cd.vendor === 'nvidia' ? 'NV' : cd.vendor === 'amd' ? 'AMD' : 'GPU'; } - function kindWord(k) { return k === 'apple' ? 'Apple silicon' : k === 'discrete' ? 'Discrete' : k === 'integrated' ? 'Integrated' : k === 'external' ? 'External' : 'Unknown'; } + var kindWord = Notices.kindWord; + // hot-plug (src/hotplug.rs): a removed card's row hides after five minutes (gone); a faulty one has no switch + function shownCards(cards) { return (cards || []).filter(function (c) { return !c.gone; }); } function vramText(cd) { if (!cd.vram_mb) return ''; var gb = cd.vram_mb / 1024; return (gb >= 10 ? Math.round(gb) : gb.toFixed(gb < 4 ? 1 : 0)) + ' GB' + (cd.kind === 'apple' ? ' unified' : ''); } function renderCardRows(container, cards) { container.querySelectorAll('.gpu-row').forEach(function (n) { n.remove(); }); - cards.forEach(function (cd) { + shownCards(cards).forEach(function (cd) { var d = document.createElement('div'); - d.className = 'gpu-row' + (cd.enabled ? '' : ' off'); + var removed = cd.removed_at > 0, unusable = !!cd.problem; + d.className = 'gpu-row' + (cd.enabled && !removed && !unusable ? '' : ' off') + (removed ? ' removed' : '') + (unusable ? ' unusable' : ''); d.dataset.key = cd.key; var meta = 'worker ' + esc(cd.worker) + '' + (vramText(cd) ? '' + esc(vramText(cd)) + '' : '') + (cd.detail && cd.kind !== 'apple' ? '' + esc(cd.detail) + '' : '') + (cd.detail && cd.kind === 'apple' ? '' + esc(cd.detail.split(',')[0]) + '' : '') + (cd.device ? 'device ' + esc(cd.device) + '' : ''); d.innerHTML = '
' + badgeHtml(cd) + '
' + - '
' + esc(cd.name) + ' ' + kindWord(cd.kind) + '
' + meta + '
' + - (cd.reason ? '
' + esc(cd.reason) + '
' : '') + (cd.message ? '
' + esc(cd.message) + '
' : '') + '
' + + '
' + esc(cd.name) + (unusable ? ': ' + esc(cd.message || 'not usable (' + cd.problem + ')') : removed ? ': removed' : '') + ' ' + kindWord(cd.kind) + '
' + meta + '
' + + (cd.reason ? '
' + esc(cd.reason) + '
' : '') + (cd.message && !unusable && !removed ? '
' + esc(cd.message) + '
' : '') + '
' + (cd.vendor === 'nvidia' && cd.power_default_w > 0 ? '
power cap' + (cd.power_pct || 80) + '% · ' + Math.round(cd.power_default_w * (cd.power_pct || 80) / 100) + ' W' + (cd.pinned ? 'pinned' : cd.sweep_pct ? 'sweep ' + cd.sweep_pct + '%' : '') + '
' : '') + '
identities
' + - ''; + ''; container.appendChild(d); }); } function readCardRows(container) { var out = []; container.querySelectorAll('.gpu-row').forEach(function (r) { + if (r.classList.contains('removed') || r.classList.contains('unusable')) return; var n = parseInt(r.querySelector('.ids input').value, 10); if (!(n >= 1)) n = 1; if (n > 64) n = 64; var pr = r.querySelector('.power input'), item = { key: r.dataset.key, enabled: r.querySelector('.switch input').checked, identities: n }; if (pr) item.power_pct = parseInt(pr.value, 10); @@ -726,8 +754,8 @@ if (typeof document !== 'undefined') (function () { var list = $('cards-list'), det = $('cards-detecting'); if (s.detecting) { det.hidden = false; $('detect-text').textContent = 'asking the graphics cards to report in'; $('btn-cards-next').disabled = true; $('cards-sub').textContent = 'Asking the graphics cards to report in.'; cardsRendered = ''; return; } det.hidden = true; - var cards = s.mining.cards; - var sig = cards.map(function (c) { return c.key; }).join('|'); + var cards = shownCards(s.mining.cards); + var sig = cards.map(function (c) { return c.key + ':' + (c.problem || '') + ':' + (c.removed_at > 0 ? 'r' : '') + ':' + (c.enabled ? 'on' : 'off'); }).join('|'); if (sig !== cardsRendered) { cardsRendered = sig; renderCardRows(list, cards); } if (!cards.length) { $('cards-sub').textContent = 'No GPU this app can drive was found.'; @@ -798,8 +826,9 @@ if (typeof document !== 'undefined') (function () { function renderDashboard(s) { var m = s.mining, n = s.node, p = s.program, f = s.finality; $('d-hash').textContent = m.hash_total >= 100 ? m.hash_total.toFixed(0) : m.hash_total.toFixed(1); - var mining = m.cards.filter(function (c) { return c.state === 'mining'; }).length; - $('d-hash-sub').textContent = m.state === 'paused' ? 'paused' : mining ? (mining + ' of ' + m.cards.length + ' card' + (m.cards.length === 1 ? '' : 's') + ' mining') : m.state === 'waiting' ? (n.synced ? 'worker starting' : 'waiting for the node') : m.state === 'idle' ? 'no GPU worker' : m.state; + var shown = shownCards(m.cards), present = shown.filter(function (c) { return !(c.removed_at > 0) && !c.problem; }); + var mining = present.filter(function (c) { return c.state === 'mining'; }).length; + $('d-hash-sub').textContent = m.state === 'paused' ? 'paused' : mining ? (mining + ' of ' + present.length + ' card' + (present.length === 1 ? '' : 's') + ' mining') : m.state === 'waiting' ? (n.synced ? 'worker starting' : 'waiting for the node') : m.state === 'idle' ? 'no GPU worker' : m.state; $('d-blocks').textContent = withCommas(m.accepted_total); $('d-blocks-sub').textContent = m.accepted_session + ' this run' + (m.found.length ? ' · ' + m.found.length + ' last hour' : '') + (m.rejected_session ? ' · ' + m.rejected_session + ' rejected' : ''); var nodeWord = n.state === 'synced' ? 'synced' : n.state === 'syncing' ? 'syncing' : n.state; @@ -822,22 +851,24 @@ if (typeof document !== 'undefined') (function () { $('d-fee').title = (m.fee_session || 0) + ' this run; 1 block in 100 pays the miner software\'s dev fee'; $('d-chain-blocks').textContent = compact(n.blocks); // per-card tiles (enabled cards), the rest in one muted line - var on = m.cards.filter(function (c) { return c.enabled; }), off = m.cards.filter(function (c) { return !c.enabled; }); - $('d-cards-eyebrow').textContent = on.length + ' worker' + (on.length === 1 ? '' : 's') + (m.cards.length > on.length ? ' of ' + m.cards.length + ' GPU' + (m.cards.length === 1 ? '' : 's') : ''); + // a card that was removed keeps its tile saying "removed" for five minutes (then the row goes); a faulty one is in the off line + var on = shown.filter(function (c) { return c.enabled && !c.problem; }), off = shown.filter(function (c) { return !c.enabled || c.problem; }); + var workers = on.filter(function (c) { return !(c.removed_at > 0); }).length; + $('d-cards-eyebrow').textContent = workers + ' worker' + (workers === 1 ? '' : 's') + (present.length > workers ? ' of ' + present.length + ' GPU' + (present.length === 1 ? '' : 's') : ''); var tb = $('d-cards'); - if (!on.length) tb.innerHTML = '
' + (m.cards.length ? 'No card switched on. Open settings to pick one.' : 'No GPU worker. The node runs on its own.') + '
'; + if (!on.length) tb.innerHTML = '
' + (shown.length ? 'No card switched on. Open settings to pick one.' : 'No GPU worker. The node runs on its own.') + '
'; else tb.innerHTML = on.map(function (cd) { - var cls = cd.state === 'mining' ? 'mining' : (cd.state === 'failed' || cd.state === 'restarting' || cd.state === 'faulted') ? 'bad' : ''; + var cls = cd.state === 'mining' ? 'mining' : (cd.state === 'failed' || cd.state === 'restarting' || cd.state === 'faulted') ? 'bad' : cd.state === 'removed' ? 'removed' : ''; var word = cd.state === 'restarting' ? ('restart in ' + cd.restart_in_s + ' s') : cd.state; var extra = cd.ids.length ? 'id ' + esc(cd.ids.join(' ')) + '' : ''; return '
' + esc(cd.name) + ' ' + kindWord(cd.kind) + '
' + '
' + cd.hash_now.toFixed(1) + 'MH/s
' + '
blocks ' + cd.accepted + '' + (cd.rejected ? ' / ' + cd.rejected + ' rejected' : '') + 'avg ' + cd.hash_avg.toFixed(1) + 'identities ' + cd.identities + '' + extra + (cd.faults ? 'worker faults ' + cd.faults + '' : '') + (cd.mismatched ? 're-check misses ' + cd.mismatched + '' : '') + '
' + telemetryHtml(cd) + - '' + esc(word) + (cd.prepared ? ' · next program ready' : '') + '' + (cd.message ? '
' + esc(cd.message) + '
' : '') + '
'; + '' + esc(word) + (cd.prepared ? ' · next program ready' : '') + '' + (cd.message && cd.state !== 'removed' ? '
' + esc(cd.message) + '
' : '') + ''; }).join(''); $('d-cards-off').hidden = !off.length; - $('d-cards-off').textContent = off.length ? 'off: ' + off.map(function (c) { return c.name + ' (' + kindWord(c.kind).toLowerCase() + ')'; }).join(', ') + '. Settings switches them on.' : ''; + $('d-cards-off').textContent = off.length ? 'off: ' + off.map(function (c) { return c.problem ? c.name + ': not usable (' + c.problem + ')' : c.name + ' (' + kindWord(c.kind).toLowerCase() + ')'; }).join(', ') + '. ' + (off.some(function (c) { return !c.problem; }) ? 'Settings switches them on.' : 'Reboot with the card attached; if it persists, reinstall the driver with the card attached.') : ''; // node card $('d-node-net').textContent = s.chain + (n.version ? ' · ' + n.version.replace(/^igneumd\s*/, '') : ''); $('n-blocks').textContent = withCommas(n.blocks); $('n-headers').textContent = withCommas(n.headers); $('n-peers').textContent = n.peers; diff --git a/app/igneum-app/ui/notices.test.mjs b/app/igneum-app/ui/notices.test.mjs index 251ae8f2..22cd67ab 100644 --- a/app/igneum-app/ui/notices.test.mjs +++ b/app/igneum-app/ui/notices.test.mjs @@ -11,7 +11,7 @@ const src = readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'app.js') const mod = { exports: {} }; new Function('module', src)(mod); const N = mod.exports; -const { updateNotice, jobNotice, clockNotice, gather, pick } = N; +const { updateNotice, jobNotice, clockNotice, cardNotices, gather, pick } = N; const NOW = 1_800_000_000; const upd = (over) => ({ available: true, version: '0.3.6', notes: '', checked_at: NOW - 60, error: '', status: 'ready', downloaded: true, ready: true, applying: false, progress: 1, size: 20_588_331, auto: true, wait: 'installs at the next safe moment', urgent: false, urgent_text: '', activation_height: 0, unsupported: false, min_supported: '', channel: 'devnet', published_at: '', file: '', updated_from: '', rolled_back: '', ...over }); @@ -139,3 +139,36 @@ test('clock: the engine words, Sync clock, the hint; gather() keeps it off the d assert.equal(pick(gather(st, { dashboard: false }), {}).kind, 'clock'); assert.deepEqual(gather({}, { dashboard: true }), []); }); + +// hot-plug (src/hotplug.rs): the strip says what appeared or went, for 5 minutes, on every screen +const cardOf = (over) => ({ index: 0, key: 'nvidia:0:NVIDIA GeForce RTX 5090', kind: 'discrete', name: 'NVIDIA GeForce RTX 5090', vendor: 'nvidia', enabled: true, state: 'mining', problem: '', reason: '', message: '', added_at: 0, removed_at: 0, gone: false, ...over }); +test('card notices: new card mining, new card not usable with the hint, removed card, nothing for the cards found at start', () => { + const start = cardOf(); + assert.deepEqual(cardNotices([start], NOW), []); + const added = cardOf({ key: 'amd:1:gfx1201', name: 'gfx1201', vendor: 'amd', added_at: NOW - 30, state: 'starting' }); + const [a] = cardNotices([start, added], NOW); + assert.equal(a.kind, 'card-added'); assert.equal(a.level, N.LEVEL['job-done']); assert.equal(a.text, 'New card: gfx1201, mining.'); assert.equal(a.key, 'card:added:amd:1:gfx1201:' + (NOW - 30)); + const bad = cardOf({ key: 'amd::AMD Radeon RX 9070 XT', name: 'AMD Radeon RX 9070 XT', vendor: 'amd', enabled: false, state: 'unusable', problem: 'Code 43', message: 'not usable (Code 43)', reason: 'reboot with the card attached; if it persists, reinstall the driver with the card attached', added_at: NOW - 10 }); + const [b] = cardNotices([bad], NOW); + assert.equal(b.text, 'AMD Radeon RX 9070 XT: not usable (Code 43). No worker runs on it.'); assert.equal(b.tone, 'warn'); assert.match(b.detail, /^reboot with the card attached/); + const igpu = cardOf({ key: 'amd:0:gfx1036', name: 'gfx1036', vendor: 'amd', kind: 'integrated', enabled: false, state: 'off', added_at: NOW - 5 }); + assert.equal(cardNotices([igpu], NOW)[0].text, 'New card: gfx1036, off (integrated). Settings switches it on.'); + const removed = cardOf({ key: 'amd:1:gfx1201', name: 'gfx1201', vendor: 'amd', state: 'removed', removed_at: NOW - 60 }); + const [r] = cardNotices([removed], NOW); + assert.equal(r.kind, 'card-removed'); assert.equal(r.text, 'Card removed: gfx1201. Its worker stopped.'); assert.equal(r.tone, 'warn'); + // the newest first; both go after CARD_S + const two = cardNotices([added, removed], NOW); + assert.equal(two[0].kind, 'card-added'); + assert.deepEqual(cardNotices([added, removed, bad], NOW + N.CARD_S + 1), []); +}); + +test('card notices sit under a running job and show on the setup screens too', () => { + const added = cardOf({ key: 'amd:1:gfx1201', name: 'gfx1201', vendor: 'amd', added_at: NOW - 30 }); + const withJob = s({ jobs: run(), mining: { cards: [added] } }); + assert.equal(pick(gather(withJob, { dashboard: true }), {}).kind, 'job-running'); + const quiet = s({ mining: { cards: [added] } }); + assert.equal(pick(gather(quiet, { dashboard: true }), {}).kind, 'card-added'); + assert.equal(pick(gather(quiet, {}), {}).kind, 'card-added'); + // closed: stays closed for that key; a later event on the same card has a new key + assert.equal(pick(gather(quiet, { dashboard: true }), { ['card:added:amd:1:gfx1201:' + (NOW - 30)]: true }), null); +}); diff --git a/app/windows/host.cpp b/app/windows/host.cpp index 83d08178..946809b4 100644 --- a/app/windows/host.cpp +++ b/app/windows/host.cpp @@ -16,6 +16,7 @@ #endif #include #include +#include #include #include #include @@ -324,6 +325,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { } } return 0; + case WM_DEVICECHANGE: + // a device arrived or left (an eGPU through a USB4 box, a driver coming up or crashing): the engine enumerates + // the cards now instead of at its next minute poll (src/hotplug.rs); DBT_DEVNODES_CHANGED needs no registration + if (wp == DBT_DEVNODES_CHANGED || wp == DBT_DEVICEARRIVAL || wp == DBT_DEVICEREMOVECOMPLETE) sendEngine("detect"); + return TRUE; case WM_TRAY: if (lp == WM_LBUTTONUP || lp == WM_LBUTTONDBLCLK) { ShowWindow(hwnd, SW_SHOW); SetForegroundWindow(hwnd); } else if (lp == WM_RBUTTONUP || lp == WM_CONTEXTMENU) showTrayMenu(); diff --git a/relay/api/console.mjs b/relay/api/console.mjs index 7dcb5358..832058bd 100644 --- a/relay/api/console.mjs +++ b/relay/api/console.mjs @@ -136,6 +136,7 @@ async function machines(sql) { accepted_lifetime: g.app && g.app.status ? g.app.status.accepted_total ?? null : null, faults: cards.reduce((a, c) => a + (c.faults || 0), 0), telemetry: g.app ? g.app.telemetry : [], events: g.app ? g.app.events : [], + gpus: g.app && g.app.gpus ? g.app.gpus : [], gpus_at: g.app && g.app.gpus_at ? g.app.gpus_at : null, hotplug: g.app && g.app.hotplug ? g.app.hotplug : [], labels: g.labels.sort((a, b) => (b.at > a.at ? 1 : -1)), runs: [...g.runs], }; }); @@ -145,7 +146,7 @@ async function machines(sql) { // several Mac installs (test runs): the newest is "Mac", the others keep their id let macSeen = false; for (const m of out) if (m.platform === 'mac' && m.name === 'Mac') { if (macSeen) m.name = `Mac ${m.id8}`; macSeen = true; } - for (const n of ['Mac', 'PC 1', 'PC 2', "Sam's Mac"]) if (!out.some(m => m.name === n)) out.push({ id: n, id8: Object.keys(NAMES).find(k => NAMES[k] === n) || null, platform: n.endsWith('Mac') ? 'mac' : 'win', name: n, host: null, legacy: false, last_seen: null, silent_s: null, silent: true, app_version: null, app: null, node: {}, cards: [], hash_total: 0, accepted_total: 0, faults: 0, telemetry: [], events: [], labels: [], runs: [], never: true }); + for (const n of ['Mac', 'PC 1', 'PC 2', "Sam's Mac"]) if (!out.some(m => m.name === n)) out.push({ id: n, id8: Object.keys(NAMES).find(k => NAMES[k] === n) || null, platform: n.endsWith('Mac') ? 'mac' : 'win', name: n, host: null, legacy: false, last_seen: null, silent_s: null, silent: true, app_version: null, app: null, node: {}, cards: [], hash_total: 0, accepted_total: 0, faults: 0, telemetry: [], events: [], gpus: [], gpus_at: null, hotplug: [], labels: [], runs: [], never: true }); out.sort((a, b) => order(a) - order(b) || (b.last_seen || '').localeCompare(a.last_seen || '')); return { machines: out.filter(m => NAMED.has(m.name) || (m.silent_s !== null && m.silent_s < HIDE_AFTER_S)), silent_after_s: SILENT_S, hide_after_s: HIDE_AFTER_S }; } diff --git a/relay/lib/parse.mjs b/relay/lib/parse.mjs index 5cc2602d..a8134f61 100644 --- a/relay/lib/parse.mjs +++ b/relay/lib/parse.mjs @@ -70,6 +70,12 @@ export function parseHeader(text) { const m = HEADER.exec(text || ''); return m ? { version: m[1], id8: m[2], platform: m[3], node: m[4].replace(/^igneumd[_\/]/, '') } : null; } +/// "NVIDIA GeForce RTX 5090 [discrete, mining] | gfx1036 [integrated, off] | AMD Radeon RX 9070 XT [discrete, not usable (Code 43)]" +export function parseCardsLine(text) { + if (!text || text.trim() === 'none') return []; + return text.split(' | ').map(p => { const m = /^(.*) \[([^,\]]+), ([^\]]+)\]$/.exec(p.trim()); return m ? { name: m[1], kind: m[2], state: m[3] } : { name: p.trim(), kind: 'unknown', state: '' }; }); +} + export function parseAppTail(tail) { const lines = tail.split('\n'); const a = { telemetry: [], events: [], jobs: [] }; @@ -113,6 +119,11 @@ export function parseAppTail(tail) { if (e && e[2] !== 'block') a.events.push({ at: new Date(Number(e[1]) * 1000).toISOString(), kind: e[2], text: e[3].slice(0, 200) }); } a.events = a.events.slice(-6); + // the card list (app/igneum-app/src/hotplug.rs cards_line): the newest "cards: [, ] | ..." line, + // written at start, on every hot-plug change and every 10 minutes + const cl = lastMatch(lines, /^(\d+)(?:\.\d+)? cards: (.+)$/); + if (cl) { a.gpus = parseCardsLine(cl[2]); a.gpus_at = new Date(Number(cl[1]) * 1000).toISOString(); } + a.hotplug = lines.filter(l => /^\d+(?:\.\d+)? \[(ok|warn|info)\] (New card: |Card removed: |.+: not usable \()/.test(l)).slice(-4).map(l => ({ at: stampOf(l), text: strip(l).replace(/^\[\w+\] /, '').slice(0, 200) })); // A clean stop: the app logs "quit: ..." (Quit, Stop, or an update: "[info] installing Igneum Miner ...") and uploads // once more before it exits; when no status line follows, the machine was stopped on purpose, not lost (4 October // 2026: a Mac stopped at 14:47 UTC read "silent 4h" for the whole afternoon, the same as a crash or a lost network). diff --git a/relay/test/parse.test.mjs b/relay/test/parse.test.mjs index 8c852f5d..f5aa0049 100644 --- a/relay/test/parse.test.mjs +++ b/relay/test/parse.test.mjs @@ -1,7 +1,7 @@ // node --test relay/test/parse.test.mjs relay/test/auth.test.mjs (no dependencies; CI runs both in the site job) import { test } from 'node:test'; import assert from 'node:assert/strict'; -import { parseLabel, parseMinerTail, markStale, STALE_S } from '../lib/parse.mjs'; +import { parseLabel, parseMinerTail, parseAppTail, parseCardsLine, markStale, STALE_S } from '../lib/parse.mjs'; test('labels: every vendor the app names, the app log, the node log, the legacy launchers', () => { assert.deepEqual(parseLabel('miner-nvidia-ae432dc7-1'), { id: 'ae432dc7', platform: 'win', stream: 'miner', vendor: 'nvidia', card: 1 }); @@ -84,3 +84,23 @@ test('app tail: the newest update line decides the OTA state: staged beats an ol const inst = parseAppTail(mac + '\n1791146700 [info] installing Igneum Miner 0.3.4: the miners stop, then the node, then the app opens again'); assert.deepEqual([inst.ota.state, inst.ota.version], ['installing', '0.3.4']); }); + +test('app tail: the cards line gives kind and state per card, and the hot-plug events', () => { + assert.deepEqual(parseCardsLine('NVIDIA GeForce RTX 5090 [discrete, mining] | gfx1036 [integrated, off] | AMD Radeon RX 9070 XT [discrete, not usable (Code 43)]'), + [{ name: 'NVIDIA GeForce RTX 5090', kind: 'discrete', state: 'mining' }, { name: 'gfx1036', kind: 'integrated', state: 'off' }, { name: 'AMD Radeon RX 9070 XT', kind: 'discrete', state: 'not usable (Code 43)' }]); + assert.deepEqual(parseCardsLine('none'), []); + const tail = [ + '1791140500 cards: NVIDIA GeForce RTX 5090 [discrete, mining] | gfx1036 [integrated, off]', + '1791140560 [ok] New card: gfx1201, mining', + '1791140561 cards: NVIDIA GeForce RTX 5090 [discrete, mining] | gfx1036 [integrated, off] | gfx1201 [discrete, waiting]', + '1791140800 [warn] Card removed: gfx1201; its worker stopped', + '1791140801 cards: NVIDIA GeForce RTX 5090 [discrete, mining] | gfx1036 [integrated, off] | gfx1201 [discrete, removed]', + '1791140830 status: accepted 5 blocks (1 this run, dev fee 0), 120.00 MH/s, mining | node 100 blocks, 3 peers, synced | up 1h', + ].join('\n'); + const a = parseAppTail(tail); + assert.equal(a.gpus.length, 3); + assert.equal(a.gpus[2].state, 'removed'); + assert.equal(a.gpus_at, new Date(1791140801000).toISOString()); + assert.deepEqual(a.hotplug.map(h => h.text), ['New card: gfx1201, mining', 'Card removed: gfx1201; its worker stopped']); + assert.equal(parseAppTail('1791140830 status: x | node y | up 1h').gpus, undefined); +}); diff --git a/relay/ui.html b/relay/ui.html index 6946df10..3d394493 100644 --- a/relay/ui.html +++ b/relay/ui.html @@ -287,7 +287,7 @@ input[type=text]{width:100%} ['checkpoint', n.checkpoint != null ? nf(n.checkpoint) : '?'], ['run', `${esc((n.run_id || (mc.runs && mc.runs[0]) || '').replace(/^.*-(\d{8}-\d{6})$/, '$1'))}`], ])} - ${cardTable(mc.cards)}${tele} + ${cardTable(mc.cards)}${mc.gpus && mc.gpus.length ? `
GPUs ${mc.gpus_at ? ago(mc.gpus_at) + ' ago' : ''}: ${mc.gpus.map(g => `${esc(g.name)} [${esc(g.kind)}, ${/not usable|removed/.test(g.state) ? `${esc(g.state)}` : esc(g.state)}]`).join(' | ')}
` : ''}${(mc.hotplug || []).map(h => `
hot-plug ${ago(h.at)} ago: ${esc(h.text)}
`).join('')}${tele} ${mc.app && (mc.app.ota || mc.app.power_cap || mc.app.jobs_file || (mc.app.jobs && mc.app.jobs.length) || mc.app.status) ? `
${mc.app.status ? `
${when(mc.app.status.at)}status${esc(mc.app.status.miner)} | node ${esc(mc.app.status.node)} | up ${esc(mc.app.status.up)}
` : ''} ${mc.app.ota ? `
${when(mc.app.ota.at)}ota${esc(mc.app.ota.text)}
` : ''} diff --git a/tools/console.mjs b/tools/console.mjs index 40feee60..3a4e1448 100644 --- a/tools/console.mjs +++ b/tools/console.mjs @@ -113,6 +113,9 @@ try { console.log(`${m.name.padEnd(8)} ${m.id8 || '-'} ${m.stopped ? `STOPPED (${m.stopped.reason}) ${ago(m.stopped.at)} ago,` : m.silent ? 'SILENT' : 'live'} seen ${ago(m.last_seen)} ago | app ${m.app_version || '?'} node ${m.node.version || '?'} daa ${m.node.daa ?? '?'} peers ${m.node.peers ?? '?'} ${m.node.synced ? 'synced' : 'not synced'} | ${m.hash_total.toFixed(1)} MH/s, ${m.accepted_total} accepted, ${m.faults} faults`); for (const c of m.cards) console.log(` ${c.label.padEnd(22)} ${c.stale ? 'last ' + c.hash_now.toFixed(1) + ' MH/s (stale, not in the total)' : c.hash_now.toFixed(1) + ' MH/s now'}, ${c.accepted} accepted, ${c.rejected} rejected, ${c.mismatched} mismatched, ${c.restarts} restarts, status ${ago(c.status_at)} ago${c.fault ? ' | ' + c.fault : ''}`); for (const t of m.telemetry) console.log(` ${t.name}: p95 ${t.p95_w} W, max ${t.max_w} W (cap ${t.cap_w} W), GPU ${t.max_gpu_c} C, memory ${t.max_mem_c} C`); + // the engine's own card list (kind and state per card, hot-plug included: app/igneum-app/src/hotplug.rs), as of its last "cards:" line + if (m.gpus && m.gpus.length) console.log(` GPUs (${ago(m.gpus_at)} ago): ${m.gpus.map(g => `${g.name} [${g.kind}, ${g.state}]`).join(' | ')}`); + for (const h of m.hotplug || []) console.log(` hot-plug ${ago(h.at)} ago: ${h.text}`); } } else if (cmd === 'chain') { From fe635edcfb644cbd2981aa58d519c87959f96741 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 17:53:07 +0000 Subject: [PATCH 002/158] EVM transaction relay: design doc 1.4 "Relay", rows 463 and 9 closed, bench-log entry, 3-node relay harness Fork tx-gossip e242acd0 adds the inventory relay of EVM transactions (protocol version 14) and replaces the 4-second hand-out cooldown with the hold on block-added. Here: the relay paragraph in execution-layer.md 1.4, the 10.2 table row (hand-out cooldown, now the block-added hold) and 10.3 item 9 updated, the bench-log entry with the PC 2 suites (igneum-exec 15 of 15, kaspa-p2p-flows 33 of 33), the digest check (unchanged, 9409dedac4bf...) and the 3-node fast-time run (A - B - C, generator on A at 2/s: 240 of 240 included, B 151 and C 105 over two hops, p50 1.5 s, 0 skipped copies, pools equal on all three nodes at every sample), the result JSON under docs/benchmarks/evm-relay-2026-10-05/, and tools/txgen/relay-net.mjs (the harness: three nodes in a chain on the fast-time profile, vmine on B and C paid to throwaway keys, txgen on A, inclusion counted by miner from A's executed chain). Co-Authored-By: Claude Fable 5.1 --- docs/bench-log.md | 36 +++ .../evm-relay-2026-10-05/relay-report.json | 301 ++++++++++++++++++ .../evm-relay-2026-10-05/txgen-summary.json | 269 ++++++++++++++++ docs/design/execution-layer.md | 6 +- tools/txgen/relay-net.mjs | 172 ++++++++++ 5 files changed, 782 insertions(+), 2 deletions(-) create mode 100644 docs/benchmarks/evm-relay-2026-10-05/relay-report.json create mode 100644 docs/benchmarks/evm-relay-2026-10-05/txgen-summary.json create mode 100644 tools/txgen/relay-net.mjs diff --git a/docs/bench-log.md b/docs/bench-log.md index ddab9e48..d32721bf 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -1508,3 +1508,39 @@ What is measured: one BLS12-381 aggregate signature over 16 summed G1 keys plus | on, split 90 s | v3 | 0 / 2 | none / 3 | 278 / 265 | apart | none | 3 on n0 | 2 (n0 reconnected 6 s after the heal, A's chain at about 58 DAA, inside the table) | Reading (the NEW finding, ledger C4). With the module off GHOSTDAG alone converges on the heavier chain and the losing side's records re-determine (F24 works when the chain moves). With the module on the overlay holds during the split (A, with 30% of the frozen table, locks nothing; B locks 7 and 8) and then fails at the heal in the shipped node: B's certificates for blocks off n0's chain are "kept pending until the chain decides (no lock at this index)", n0's chain never decides because GHOSTDAG keeps its heavier tip and nothing turns the certificate into a fork-choice constraint, and once n0's last lock (index 7, DAA 209) is one window old (DAA 329) the frozen table stops applying on A's chain ("no frozen table (no lock on this chain inside the window)"), A's two keys are 100% of A's own window (B's post-cut blocks are red there) and n0 locks 10, 11, 12 alone; B's certificates for 10 and 11 then log CONFLICTING on n0 (n0 log, 17:27:04 to 17:29:54 BST). A finality fork from a 96-s honest partition, no attacker, table intact at the heal; the 150-s run and the v2 control end the same way. The spec's fork choice ("GHOSTDAG among tips through all certified checkpoints", 3.5) is therefore implemented only for certificates over blocks already on the node's chain. Fix named in the ledger entry: verify an off-chain certificate against the table at its own block and let it constrain fork choice (a certificate-driven reorg), then re-determine. Raw: `scratchpad fud-a/c4-results-*.md`, node logs `c4-on90-tmp/`, `c4-v2-control-tmp/`. + +## 5 October 2026 (evening), EVM transaction relay: three nodes in a chain, every transaction sent to one end included by the other two miners (execution and networking engineer) + +Until this change the node did not relay EVM transactions to its peers, so a transaction sent to one node was only ever included by that node's own templates (this file, "5 October 2026 (afternoon), live devnet: real transactions": 3,794 transfers, all in the Mac's blocks; execution-layer ledger item 9). Fork branch `tx-gossip` (worktree `vendor/igneum-node-txgossip`, from release-0.3.6 a24ab01a, commit e242acd0), main repo branch `tx-gossip`. Design in `docs/design/execution-layer.md` 1.4 "Relay"; the hand-out cooldown of its 10.2 table is gone with it (row "Mempool hold"). + +What was built. Three p2p messages after Kaspa's own transaction relay (`protocol/flows/src/v10/txrelay/flow.rs`): an inventory of admitted hashes, a request for the unknown ones, one answer with the raw bytes (`protocol/p2p/proto/p2p.proto`, payload numbers 72 to 74). Two flows per peer (`protocol/flows/src/v10/evmrelay.rs`), a pump that announces the mempool's admitted hashes every 250 ms, a sink trait the execution layer implements (`kaspa_consensus_core::evm::EvmTxSink`, `igneum/exec/src/service.rs EvmTxRelaySink`), and the mempool's side: every admitted hash queued for gossip, executed, evicted and invalid hashes remembered (65,536) so a second announcement is not requested, a 50,000-transaction cap, and the hold on block-added in place of the 4-second cooldown (the executor subscribes to consensus `BlockAdded`; a transaction leaves the templates when any DAG block carries it and comes back if a chain block skipped it). Limits per peer in the table. + +| Limit | Value | Over it | +|---|---|---| +| Hashes announced to us, or requested from us | 2,000 per second, burst 8,192 | the surplus of the message is dropped (the sender paid as much as we did) | +| Hashes per inventory or request message | 4,096 | disconnect | +| Bytes per answer / per transaction | 4 MiB / 128 KiB | disconnect | +| Transaction failing a state-free rule (malformed, signature, chain id, type 3 or 4) | | disconnect, hash remembered | +| State-dependent refusal (nonce more than 16 ahead, fee cap under the base fee, funds, 64 queued per sender, pool full) | | dropped quietly, hash not remembered | + +Protocol version. 13 to 14. An Igneum node drops a connection on a payload it cannot decode (`protocol/p2p/src/core/router.rs route_to_flow`: prost leaves the oneof empty, the router returns "empty payload", the connection closes), so the three messages go only to peers that advertised 14 or later, exactly as the finality (12) and proof-record (13) messages did. A 14 node registers the 13 flows for a 13 peer and never announces to it. The consensus params digest does not cover the protocol version: a scratch node on the devnet profile from the shipped 0.3.6 binary (`target-036`) and from this build printed the same digest, `9409dedac4bf9f0f20a54fb169b52a75a2903364909fe9ffd6fc5cdcd9d95d38`, so a 14 node and a 13 node still peer. Rollout: during the mixed fleet a transaction reaches the 14 nodes connected to the node it was sent to, and whatever a 13 node mines carries only what its own RPC received, as today; the relay is complete when the last miner is on 14. No fresh chain, no activation height. + +Unit tests (PC 2, job build-20261005-173606, `igneum-exec` 15 of 15 in 0.01 s, `kaspa-p2p-flows` 33 of 33 in 0.19 s, 24 s for both): the pool queues an admitted hash for gossip once and answers "known" for the duplicate; wrong chain id, a signature above the curve order and truncated bytes are refused and remembered by hash, a nonce beyond the gap and a fee cap under the base fee are refused and not remembered; a transaction stays in every template until a block carries it, is held then, comes back when a chain block skips it and leaves (hash remembered) when one executes it; the wire messages round-trip through prost and the router's payload type, hash lists of the wrong length or over 4,096 are refused, the per-peer bucket grants the burst then the rate. The first PC 2 run of the suites (build-20261005-173013) failed on the signature case: a flipped low bit of `s` recovers a different signer (a funds refusal), not a fault; the test now sets `s` above the curve order. Found on the way: the `kaspa-p2p-flows` test target had not compiled since M20 added the epoch-seed headers to the pruning proof messages (`ibd/proof.rs` tests), fixed in the same commit. + +The 3-node run (`tools/txgen/relay-net.mjs`, new; this Mac, load 7 to 8 at the end of the run after the other agents' harnesses finished, every number a count or an inclusion latency, not a timing of the node). Fast-time profile (`infra/fast-time/override-60x.json`, proof of work skipped), ports 29700+, data `/tmp/igneum-txrelay`. Chain A - B - C: B dials A and C (a harness node that dials accepts no inbound, and `--connect` takes one address per flag; both found by the first two runs, which are not numbers). A mines nothing. One vmine on B and one on C at 0.5 blocks/s each, paid to throwaway keys made for the run; B's rewards funded 16 generator wallets (2 IGN each) 33 s after start. The generator (`tools/txgen/run.mjs`) sent to A's EVM RPC only, 2 transfers a second for 120 s, so every inclusion is by a block B or C built from a pool the relay fed; C is two hops from A. Result files `docs/benchmarks/evm-relay-2026-10-05/{relay-report,txgen-summary}.json`. + +| Measured, 3-node fast-time run (17:49 to 17:52 UTC) | Value | +|---|---| +| Sent to A / included / pending at the end / failures | 240 / 240 / 0 / 0 (0 nonce retries, 0 deferred, 0 throttled) | +| Included per second over the send span | 1.98 (target 2) | +| Inclusion latency p50 / p90 / p99 / max | 1,545 / 3,058 / 5,033 / 6,017 ms (mean 1,859) | +| Chain blocks in the window / executed transfers / skipped copies | 149 / 256 (240 transfers and 16 funding) / 0 | +| Included by miner B (one hop): blocks / with transactions / executed | 79 / 59 / 151 | +| Included by miner C (two hops): blocks / with transactions / executed | 70 / 42 / 105 | +| Pool depth, sampled every 5 s on A, B and C | equal on all three at 27 of 27 samples (0 to 6 pending), peak 6 | +| Sinks agree at the end | yes | +| First funding transfer, sent to A, included | 2.0 s after the send (block 37, mined by B or C) | + +Reading. Every transaction given to A was mined by B or C within 6 s, two thirds of them within 3 s, with no skipped copy: the hold on block-added kept B's and C's parallel blocks from carrying the same transfer. The afternoon run on the live devnet, through one node with the cooldown, had p50 40.7 s and p90 110.8 s with 50-s quiet stretches; here the 1.5 s p50 is one fast-time block plus the relay and the executor's lag. The pool depth matching on all three nodes at every sample is the convergence. Not measured here: a transaction flood above the per-peer rate (the bucket is unit-tested only), a 13 peer in the fleet (the digest check and the version gate are the evidence), and the hold's 30-s expiry on a block that never reaches the chain (not seen in 149 chain blocks). + +Commands: `IGNEUMD=vendor/igneum-node/target-txgossip/release/igneumd IGNEUM_MINER=vendor/igneum-node/target-txgossip/release/igneum-miner tools/lock/with-lock.sh run node tools/txgen/relay-net.mjs --rate 2 --duration 120 --wallets 16 --fund 2`; the Mac binaries from the fork worktree with `CARGO_TARGET_DIR=vendor/igneum-node/target-txgossip cargo build --release -j 4 -p kaspad -p igneum-miner --features kaspad/igneum-pow` under the build lock (an APFS clone of `target-036`, 2 min 15 s to clone, 5 min 06 s to build); the suites with `node tools/build-job.mjs run --target 1ccfe586 --node vendor/igneum-node-txgossip --targets linux --node-tests "igneum-exec kaspa-p2p-flows" --no-app`. diff --git a/docs/benchmarks/evm-relay-2026-10-05/relay-report.json b/docs/benchmarks/evm-relay-2026-10-05/relay-report.json new file mode 100644 index 00000000..c1c24c32 --- /dev/null +++ b/docs/benchmarks/evm-relay-2026-10-05/relay-report.json @@ -0,0 +1,301 @@ +{ + "tool": "tools/txgen/relay-net.mjs", + "node": "/Users/joshm/Projects/igneum/vendor/igneum-node/target-txgossip/release/igneumd", + "topology": "A - B - C (B dials A and C); generator on A; vmine on B and C", + "params": { + "rate_per_s": 2, + "duration_s": 120, + "wallets": 16, + "fund_ign": 2, + "fast_time": true + }, + "chain_blocks_in_window": 149, + "executed": 256, + "skipped": 0, + "by_miner": { + "B": { + "blocks": 79, + "blocks_with_txs": 59, + "txs_carried": 151, + "executed": 151, + "skipped": 0 + }, + "C": { + "blocks": 70, + "blocks_with_txs": 42, + "txs_carried": 105, + "executed": 105, + "skipped": 0 + } + }, + "generator": { + "sent": 240, + "included": 240, + "pending_at_end": 0, + "included_per_s": 1.975, + "latency_ms": { + "p50": 1545, + "p90": 3058, + "p99": 5033, + "max": 6017, + "mean": 1859 + }, + "blocks_with_content": 100, + "errors": {}, + "stop_reason": "duration" + }, + "pools": { + "samples": [ + { + "t": 33.5, + "A": 0, + "A_chain": 37, + "B": 0, + "B_chain": 37, + "C": 0, + "C_chain": 37 + }, + { + "t": 38.5, + "A": 6, + "A_chain": 38, + "B": 6, + "B_chain": 38, + "C": 6, + "C_chain": 38 + }, + { + "t": 43.5, + "A": 2, + "A_chain": 44, + "B": 2, + "B_chain": 44, + "C": 2, + "C_chain": 44 + }, + { + "t": 48.5, + "A": 2, + "A_chain": 50, + "B": 2, + "B_chain": 50, + "C": 2, + "C_chain": 50 + }, + { + "t": 53.5, + "A": 1, + "A_chain": 58, + "B": 1, + "B_chain": 58, + "C": 1, + "C_chain": 58 + }, + { + "t": 58.5, + "A": 2, + "A_chain": 66, + "B": 2, + "B_chain": 66, + "C": 2, + "C_chain": 66 + }, + { + "t": 63.6, + "A": 4, + "A_chain": 71, + "B": 4, + "B_chain": 71, + "C": 4, + "C_chain": 71 + }, + { + "t": 68.6, + "A": 2, + "A_chain": 77, + "B": 2, + "B_chain": 77, + "C": 2, + "C_chain": 77 + }, + { + "t": 73.6, + "A": 0, + "A_chain": 80, + "B": 0, + "B_chain": 80, + "C": 0, + "C_chain": 80 + }, + { + "t": 78.6, + "A": 3, + "A_chain": 83, + "B": 3, + "B_chain": 83, + "C": 3, + "C_chain": 83 + }, + { + "t": 83.6, + "A": 0, + "A_chain": 92, + "B": 0, + "B_chain": 92, + "C": 0, + "C_chain": 92 + }, + { + "t": 88.6, + "A": 0, + "A_chain": 96, + "B": 0, + "B_chain": 96, + "C": 0, + "C_chain": 96 + }, + { + "t": 93.6, + "A": 2, + "A_chain": 101, + "B": 2, + "B_chain": 101, + "C": 2, + "C_chain": 101 + }, + { + "t": 98.6, + "A": 1, + "A_chain": 114, + "B": 1, + "B_chain": 114, + "C": 1, + "C_chain": 114 + }, + { + "t": 103.6, + "A": 5, + "A_chain": 117, + "B": 5, + "B_chain": 117, + "C": 5, + "C_chain": 117 + }, + { + "t": 108.6, + "A": 4, + "A_chain": 121, + "B": 4, + "B_chain": 121, + "C": 4, + "C_chain": 121 + }, + { + "t": 113.6, + "A": 2, + "A_chain": 125, + "B": 2, + "B_chain": 125, + "C": 2, + "C_chain": 125 + }, + { + "t": 118.6, + "A": 1, + "A_chain": 128, + "B": 1, + "B_chain": 128, + "C": 1, + "C_chain": 128 + }, + { + "t": 123.6, + "A": 0, + "A_chain": 135, + "B": 0, + "B_chain": 135, + "C": 0, + "C_chain": 135 + }, + { + "t": 128.6, + "A": 3, + "A_chain": 140, + "B": 3, + "B_chain": 140, + "C": 3, + "C_chain": 140 + }, + { + "t": 133.6, + "A": 0, + "A_chain": 144, + "B": 0, + "B_chain": 144, + "C": 0, + "C_chain": 144 + }, + { + "t": 138.6, + "A": 1, + "A_chain": 155, + "B": 1, + "B_chain": 155, + "C": 1, + "C_chain": 155 + }, + { + "t": 143.6, + "A": 3, + "A_chain": 163, + "B": 3, + "B_chain": 163, + "C": 3, + "C_chain": 163 + }, + { + "t": 148.6, + "A": 2, + "A_chain": 167, + "B": 2, + "B_chain": 167, + "C": 2, + "C_chain": 167 + }, + { + "t": 153.6, + "A": 1, + "A_chain": 175, + "B": 1, + "B_chain": 175, + "C": 1, + "C_chain": 175 + }, + { + "t": 158.6, + "A": 0, + "A_chain": 179, + "B": 0, + "B_chain": 179, + "C": 0, + "C_chain": 179 + }, + { + "t": 163.6, + "A": 0, + "A_chain": 184, + "B": 0, + "B_chain": 184, + "C": 0, + "C_chain": 184 + } + ], + "max": { + "A": 6, + "B": 6, + "C": 6 + } + }, + "sinks_agree": true, + "wall_s": 168.8 +} diff --git a/docs/benchmarks/evm-relay-2026-10-05/txgen-summary.json b/docs/benchmarks/evm-relay-2026-10-05/txgen-summary.json new file mode 100644 index 00000000..d681ca3a --- /dev/null +++ b/docs/benchmarks/evm-relay-2026-10-05/txgen-summary.json @@ -0,0 +1,269 @@ +{ + "tool": "tools/txgen/run.mjs", + "rpc": "http://127.0.0.1:29703", + "chain_id": 4463, + "started": "2026-10-05T17:49:40.914Z", + "ended": "2026-10-05T17:51:44.556Z", + "stop_reason": "duration", + "params": { + "wallets": 16, + "rate_per_s": 2, + "duration_s": 120, + "fund_ign": "2.000000", + "cap_ign": "74.000000", + "gas": 59650, + "stale_s": 60 + }, + "fees_last": { + "base_gwei": "100.00", + "proving_base_gwei": "10000.00", + "tip_gwei": "1.00", + "node_quote_gwei": "243.86", + "fee_cap_gwei": "243.86" + }, + "counts": { + "sent": 240, + "included": 240, + "failed": 0, + "dropped": 0, + "skipped": 0, + "reverted": 0, + "nonceRetries": 0, + "deferred": 0, + "throttled": 0, + "fundingTx": 16, + "pending_at_end": 0 + }, + "throughput": { + "included_per_s": 1.975, + "send_span_s": 121.5, + "sent_per_s_target": 2 + }, + "latency_ms": { + "p50": 1545, + "p90": 3058, + "p99": 5033, + "max": 6017, + "mean": 1859 + }, + "blocks": { + "with_content": 100, + "first": 38, + "last": 176, + "max_tx_in_one": 10, + "per_block": { + "38": 8, + "39": 1, + "40": 2, + "42": 3, + "44": 5, + "45": 3, + "46": 1, + "48": 1, + "50": 2, + "51": 1, + "52": 3, + "54": 2, + "57": 3, + "58": 2, + "59": 1, + "61": 2, + "62": 2, + "63": 1, + "65": 1, + "66": 3, + "68": 4, + "70": 1, + "71": 5, + "72": 1, + "73": 2, + "74": 2, + "75": 2, + "77": 2, + "79": 10, + "80": 1, + "81": 4, + "82": 2, + "83": 3, + "84": 2, + "85": 3, + "87": 1, + "88": 2, + "89": 1, + "91": 1, + "92": 4, + "93": 1, + "94": 3, + "95": 2, + "96": 2, + "97": 1, + "98": 5, + "101": 2, + "103": 1, + "104": 1, + "105": 2, + "106": 2, + "109": 1, + "110": 1, + "112": 1, + "114": 3, + "116": 3, + "117": 8, + "119": 2, + "120": 1, + "121": 6, + "122": 1, + "123": 3, + "124": 2, + "125": 6, + "126": 1, + "127": 4, + "128": 3, + "129": 1, + "130": 4, + "132": 1, + "133": 1, + "134": 1, + "135": 2, + "138": 4, + "139": 1, + "140": 4, + "141": 6, + "142": 3, + "146": 1, + "147": 1, + "148": 3, + "151": 2, + "152": 1, + "153": 1, + "155": 4, + "157": 1, + "158": 1, + "160": 1, + "162": 1, + "163": 5, + "164": 1, + "165": 4, + "166": 1, + "167": 5, + "168": 1, + "169": 1, + "172": 3, + "174": 1, + "175": 3, + "176": 2 + } + }, + "spend_ign": { + "funding": "32.000000", + "fees_actual": "0.542976", + "fees_max_committed": "38.769773", + "value_moved_between_wallets": "0.132776", + "cap": "74.000000", + "cap_hit": false + }, + "wallets": [ + { + "index": 0, + "sent": 15, + "balance_ign": "1.921973", + "nonce": 15 + }, + { + "index": 1, + "sent": 15, + "balance_ign": "1.922278", + "nonce": 15 + }, + { + "index": 2, + "sent": 15, + "balance_ign": "1.923568", + "nonce": 15 + }, + { + "index": 3, + "sent": 15, + "balance_ign": "1.924152", + "nonce": 15 + }, + { + "index": 4, + "sent": 15, + "balance_ign": "1.920937", + "nonce": 15 + }, + { + "index": 5, + "sent": 15, + "balance_ign": "1.922478", + "nonce": 15 + }, + { + "index": 6, + "sent": 15, + "balance_ign": "1.924611", + "nonce": 15 + }, + { + "index": 7, + "sent": 15, + "balance_ign": "1.930741", + "nonce": 15 + }, + { + "index": 8, + "sent": 15, + "balance_ign": "1.923430", + "nonce": 15 + }, + { + "index": 9, + "sent": 15, + "balance_ign": "1.926285", + "nonce": 15 + }, + { + "index": 10, + "sent": 15, + "balance_ign": "1.922495", + "nonce": 15 + }, + { + "index": 11, + "sent": 15, + "balance_ign": "1.918612", + "nonce": 15 + }, + { + "index": 12, + "sent": 15, + "balance_ign": "1.921628", + "nonce": 15 + }, + { + "index": 13, + "sent": 15, + "balance_ign": "1.921379", + "nonce": 15 + }, + { + "index": 14, + "sent": 15, + "balance_ign": "1.923181", + "nonce": 15 + }, + { + "index": 15, + "sent": 15, + "balance_ign": "1.923212", + "nonce": 15 + } + ], + "funder": { + "balance_ign": "203.751501" + }, + "lost": [], + "pending_at_end": [], + "errors": {} +} \ No newline at end of file diff --git a/docs/design/execution-layer.md b/docs/design/execution-layer.md index dbcbd553..f0466d16 100644 --- a/docs/design/execution-layer.md +++ b/docs/design/execution-layer.md @@ -79,6 +79,8 @@ Worked example. Sender S has nonce 5. Miner A's block carries S:5, S:6. Miner B' Consequence for users. A transaction can be skipped in one block and execute in a later one without being re-broadcast, as long as a miner includes it again; the node's mempool re-queues a skipped transaction once (then drops it). `eth_getTransactionReceipt` returns null until the executing copy lands, as on Ethereum for a pending transaction. +Relay (implemented 5 October 2026, fork `tx-gossip`, protocol version 14). A node's mempool is no longer only what its own RPC received. Every admitted hash is announced to every relay-aware peer within 250 ms (`IgneumEvmTxInvMessage`, the inventory pattern of Kaspa's `InvTransactions`); a peer requests the hashes it does not know (`IgneumRequestEvmTxsMessage`) and the holder answers one `IgneumEvmTxsMessage` with the raw bytes it still has; the receiver runs the same admission as `eth_sendRawTransaction` (signature, chain id, nonce window of 16, fee cap at or above the execution base fee, funds, 64 queued per sender, the pgas estimate) and a transaction the node already executed is refused without re-admission, so the pools converge and the chain's own blocks carry a transaction once. Dedup is by hash: the pool answers "known" for what it holds, executed or refused as invalid in the last 65,536 hashes, and one request per hash is outstanding across all peers. Per peer, 2,000 hashes a second with a burst of 8,192 are accepted in and served out; 4,096 hashes per message and 4 MiB per answer, over which the peer is dropped. A state-free fault (malformed, bad signature, wrong chain id, a refused type) disconnects the relaying peer, since every node refuses it the same way; a state-dependent refusal (nonce beyond the window, fee cap under the base fee, funds, queue depth, the 50,000-transaction pool cap) is dropped quietly, because the peer's tip may differ. Relay is off while the node is out of sync. Code: `protocol/flows/src/v10/evmrelay.rs`, the pump in `protocol/flows/src/service.rs`, the sink in `igneum/exec/src/service.rs` (`EvmTxRelaySink`). + Alternative. Per-block nonces or sequence-independent nonces (Sui-style objects). Rejected: every wallet assumes Ethereum nonces. ### 1.5 Invalid transactions are skipped by rule @@ -460,7 +462,7 @@ Rule change, 4 October 2026 (findings F-exec-A and F-exec-B of the attack suite, | Units | 1 sompi = 1e10 wei; 1 IGN = 1e18 wei | Subsidies come from `igneum::block_subsidy` in 8-decimal sompi; the EVM is 18-decimal. The open "8 or 18 decimals" decision is unchanged; this is the fixed scaling at the bridge named there | | Rewards | 80% of every blue block's subsidy to its miner, 20% to the proving pool escrow `0x...0220`, both credited in the segment that merges the block; reds unpaid | Design 4.4, with the pool held in a keyless account until proof records exist | | Simnet | Devnet block rate and depths (1 BPS, k 18, mergeset 180, merge depth 3,600) with proof of work skipped; chain id 4463 shared with the devnet | A CPU test network of the devnet DAG shape | -| Mempool hand-out | A transaction handed to a template is not offered again for 4 s unless a chain block skipped it; a transaction skipped twice is dropped | Kaspa removes a block's transactions on block-added; the cooldown is the stand-in until the executor listens to block-added | +| Mempool hold | A transaction stays in every template until a block carrying it is added to the DAG (any block, this node's or a peer's: the executor subscribes to consensus `BlockAdded`); then it is held for 30 s or until the executor removes it (executed) or offers it again (a chain block skipped it); a transaction skipped twice is dropped | Kaspa's own rule (`mining/src/manager.rs`, `handle_new_block_transactions`). Replaced the 4-second hand-out cooldown on 5 October 2026 (fork `tx-gossip`, `pool.rs IN_BLOCK_HOLD`, `service.rs listen_block_added`): the cooldown made a sender mineable 1 s in 5 and inclusion came in 50-s bursts (bench-log, 5 October 2026 afternoon); with the hold a transaction is offered to every template until a block has it | | Reorgs | Post-segment states for the last 64 chain blocks; deeper reorgs replay from genesis | Observed depth on the test network: 1 to 3 with Poisson-paced miners. A fixed per-template hold had made the three stub miners mine in lockstep rounds, and with equal work per block the GHOSTDAG hash tie-break then kept two equal-work chains alive from genesis (flips 48 deep every few seconds); `igneum-miner --hold-ms` is exponential now | | Block tags | `pending`, `safe` and `finalized` all resolve to the executed tip | The virtual's segment is not executed eagerly and no certified checkpoint exists on this branch; the RPC does not pretend otherwise | @@ -474,7 +476,7 @@ Rule change, 4 October 2026 (findings F-exec-A and F-exec-B of the attack suite, 6. The virtual's segment is not executed eagerly (design 1.2 "about one second after inclusion"); the executor runs about one chain block behind the sink. `pending` tags resolve to the executed tip. 7. `eth_subscribe`, `debug_traceTransaction`, `trace_block`, `eth_getProof`, `eth_getUncle*`, `IgneumInfo`: not implemented. 8. The chain follower polls `get_virtual_chain_from_block` every 100 ms instead of subscribing to virtual-chain-changed notifications. -9. Mempool: no p2p relay of EVM transactions between nodes (each node's pool is what its RPC received), no eviction by age, no fee-based replacement beyond the 10% rule. +9. Mempool: p2p relay of EVM transactions implemented 5 October 2026 (section 1.4 "Relay", protocol version 14; measured on a 3-node fast-time chain A - B - C in the bench-log of that day: every transaction sent to A was included by B's and C's blocks). Still missing: eviction by age and fee-based replacement beyond the 10% rule. 10. Differential rows 1 (ethereum/tests), 3 (independent linearizer), 4 (Python oracle), 5 and 6 are not built; the harness here is the balance and receipt comparison of the acceptance criteria. ### 10.4 Merge plan with the finality branch diff --git a/tools/txgen/relay-net.mjs b/tools/txgen/relay-net.mjs new file mode 100644 index 00000000..7922cf6d --- /dev/null +++ b/tools/txgen/relay-net.mjs @@ -0,0 +1,172 @@ +#!/usr/bin/env node +// Igneum EVM transaction relay check (5 October 2026, execution-layer ledger item 9). A private 3-node network in a +// chain, A - B - C (B dials A and C; A and C listen and dial nothing, since a harness node that dials accepts no +// inbound connection; a transaction given to A reaches C in two hops, through B), on the 60x fast-time profile with +// proof of work skipped. Only B and C mine (one vmine each, half a block per second, paid to throwaway +// EVM keys made for the run); A mines nothing, so every transaction the generator sends to A can only be included +// by a block B or C built from a pool fed by the relay. The generator is tools/txgen/run.mjs against A's EVM RPC; +// B's block rewards fund it. The report counts inclusion by miner from A's own executed chain +// (igneum_getSegment: every mergeset block with its miner and transaction count, every executed transaction with +// its including block), samples the three pools every 5 s for convergence, and keeps the generator's latency. +// +// IGNEUMD= IGNEUM_MINER= tools/lock/with-lock.sh run node tools/txgen/relay-net.mjs +// [--rate 2] [--duration 120] [--wallets 16] [--fund 2] [--out ] +// +// Ports IGNEUM_HARNESS_BASE_PORT (default 29700) and up, data IGNEUM_HARNESS_TMP (default /tmp/igneum-txrelay). The +// live devnet (26610/26611, 26640/26641, 26800, 28640) and other agents' ranges are never touched. No key is printed. + +import { spawn } from 'node:child_process'; +import { mkdirSync, writeFileSync, chmodSync, readFileSync, openSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; +import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; + +process.env.IGNEUM_FAST_TIME = '1'; +process.env.IGNEUM_HARNESS_BASE_PORT ||= '29700'; +process.env.IGNEUM_HARNESS_TMP ||= '/tmp/igneum-txrelay'; +const net = await import('../harness/lib/net.mjs'); +const { Node, stopAll, log, sleep, BASE_PORT, TMP, IGNEUMD, ROOT } = net; + +const args = process.argv.slice(2); +const opt = (name, dflt) => { const i = args.indexOf(name); return i >= 0 && args[i + 1] !== undefined ? args[i + 1] : dflt; }; +const RATE = +opt('--rate', 2); +const DURATION = +opt('--duration', 120); +const WALLETS = +opt('--wallets', 16); +const FUND = +opt('--fund', 2); +const OUT = opt('--out', join(TMP, 'out')); +const MINER = process.env.IGNEUM_MINER || `${ROOT}vendor/igneum-node/target-txgossip/release/igneum-miner`; +for (const b of [IGNEUMD, MINER]) if (!existsSync(b)) { console.error(`missing ${b}`); process.exit(2); } +mkdirSync(OUT, { recursive: true }); + +const evmPort = (i) => BASE_PORT + i * 10 + 3; +const evmUrl = (i) => `http://127.0.0.1:${evmPort(i)}`; +async function eth(i, method, params = []) { + const r = await fetch(evmUrl(i), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }), signal: AbortSignal.timeout(20000) }); + const j = await r.json(); + if (j.error) throw new Error(`${method}: ${j.error.message || JSON.stringify(j.error)}`); + return j.result; +} +const ign = (wei) => (Number(BigInt(wei) / 10n ** 14n) / 1e4).toFixed(4); + +// Throwaway keys for the two miners (never printed, never reused) +const keyB = generatePrivateKey(), keyC = generatePrivateKey(); +const minerB = privateKeyToAccount(keyB), minerC = privateKeyToAccount(keyC); +const funderFile = join(TMP, 'funder.json'); +mkdirSync(TMP, { recursive: true }); +writeFileSync(funderFile, JSON.stringify({ purpose: 'devnet relay harness, throwaway key of miner B', private_key: keyB, address: minerB.address })); +chmodSync(funderFile, 0o600); + +const started = []; +function miner(bin, argv, name) { + const out = openSync(`${TMP}/${name}.log`, 'a'); + const p = spawn(bin, argv, { stdio: ['ignore', out, out] }); + started.push(p); + return p; +} +async function cleanup() { + for (const p of started.reverse()) { try { p.kill('SIGINT'); } catch { } } + await sleep(1500); + for (const p of started) { try { p.kill('SIGKILL'); } catch { } } + await stopAll(); +} +process.on('SIGINT', async () => { await cleanup(); process.exit(130); }); + +const t0 = Date.now(); +const since = () => ((Date.now() - t0) / 1000).toFixed(1); +let exitCode = 1; +try { + const A = await new Node(0, { name: 'A', extraArgs: [`--evm-rpclisten=127.0.0.1:${evmPort(0)}`] }).start(); + const C = await new Node(2, { name: 'C', extraArgs: [`--evm-rpclisten=127.0.0.1:${evmPort(2)}`] }).start(); + // one --connect per peer: the node's parser takes the flag repeatedly, not a comma list + const B = await new Node(1, { name: 'B', connect: [A.p2p], extraArgs: [`--connect=${C.p2p}`, `--evm-rpclisten=127.0.0.1:${evmPort(1)}`] }).start(); + const nodes = [A, B, C]; + await sleep(3000); + const peersOf = async (n) => (await net.peers(n)).length; + log(`peers: A ${await peersOf(A)}, B ${await peersOf(B)}, C ${await peersOf(C)} (chain A - B - C)`); + const version = (n) => n.grepLog(/protocol version/).map(l => l.replace(/^.*Registering/, 'Registering')).slice(0, 2); + log(`B log: ${version(B).join(' | ')}`); + + const secs = DURATION + 400; + miner(MINER, ['vmine', B.grpc, String(secs), '--label', 'vB', '--share', '0.5', '--bps', '1', '--evm-address', minerB.address.slice(2), '--network', 'devnet'], 'vmine-B'); + miner(MINER, ['vmine', C.grpc, String(secs), '--label', 'vC', '--share', '0.5', '--bps', '1', '--evm-address', minerC.address.slice(2), '--network', 'devnet'], 'vmine-C'); + log('miners: vmine on B and C at 0.5 blocks/s each; A mines nothing'); + + // B's rewards reach A's executed state through the relay of blocks; wait for enough to fund the wallets + const wanted = BigInt(Math.ceil(WALLETS * FUND * 1.25 + 2)) * 10n ** 18n; + for (let i = 0; i < 300; i++) { + const bal = BigInt(await eth(0, 'eth_getBalance', [minerB.address, 'latest']).catch(() => '0x0')); + if (bal >= wanted) { log(`miner B holds ${ign(bal)} IGN on A's chain at ${since()} s; funding can start`); break; } + if (i % 10 === 0) log(`waiting for miner B's rewards: ${ign(bal)} IGN of ${ign(wanted)} (t=${since()} s)`); + await sleep(1000); + } + const startBlock = Number(BigInt(await eth(0, 'eth_blockNumber'))); + + // The generator against A, with the pool sampler beside it + const samples = []; + let sampling = true; + const sampler = (async () => { + while (sampling) { + const row = { t: +since() }; + for (const [i, name] of [[0, 'A'], [1, 'B'], [2, 'C']]) { + try { const b = await eth(i, 'igneum_getBudgets'); row[name] = Number(BigInt(b.mempool)); row[`${name}_chain`] = Number(BigInt(b.chainBlocks)); } catch { row[name] = null; } + } + samples.push(row); + await sleep(5000); + } + })(); + const summaryFile = join(OUT, 'txgen-summary.json'); + const genLog = openSync(join(OUT, 'txgen.log'), 'w'); + const gen = spawn(process.execPath, [`${ROOT}tools/txgen/run.mjs`, '--rpc', evmUrl(0), '--rate', String(RATE), '--duration', String(DURATION), '--wallets', String(WALLETS), + '--fund', String(FUND), '--cap', String(WALLETS * FUND * 2 + 10), '--keys', join(TMP, 'wallets.json'), '--funder', funderFile, '--summary', summaryFile, '--stale', '60'], + { stdio: ['ignore', 'pipe', genLog], cwd: `${ROOT}tools/txgen` }); + started.push(gen); + gen.stdout.on('data', (d) => { const s = d.toString(); writeFileSync(join(OUT, 'txgen.log'), s, { flag: 'a' }); for (const l of s.split('\n')) if (/included|funded|funder|fees|summary|error|stopping/i.test(l) && l.trim()) log(`txgen: ${l.trim().slice(0, 160)}`); }); + const genExit = await new Promise((r) => gen.on('exit', (code) => r(code))); + log(`generator exited ${genExit} at ${since()} s`); + await sleep(10000); // let the last blocks execute on A + sampling = false; await sampler; + + // Inclusion by miner, from A's executed chain + const endBlock = Number(BigInt(await eth(0, 'eth_blockNumber'))); + const byMiner = new Map(); + const label = (m) => (m.toLowerCase() === minerB.address.toLowerCase() ? 'B' : m.toLowerCase() === minerC.address.toLowerCase() ? 'C' : `other:${m.slice(0, 10)}`); + const bump = (m, k, n = 1) => { const r = byMiner.get(m) || { blocks: 0, blocks_with_txs: 0, txs_carried: 0, executed: 0, skipped: 0 }; r[k] += n; byMiner.set(m, r); }; + let executed = 0, skipped = 0, chainBlocks = 0; + for (let n = startBlock + 1; n <= endBlock; n++) { + let seg; try { seg = await eth(0, 'igneum_getSegment', ['0x' + n.toString(16)]); } catch (e) { log(`segment ${n}: ${e.message}`); continue; } + chainBlocks++; + const minerOfBlock = new Map(); + for (const m of seg.mergeset || []) { + const who = label(m.miner); minerOfBlock.set(m.hash, who); + bump(who, 'blocks'); if (Number(BigInt(m.txCount)) > 0) { bump(who, 'blocks_with_txs'); bump(who, 'txs_carried', Number(BigInt(m.txCount))); } + } + for (const e of seg.executed || []) { executed++; bump(minerOfBlock.get(e.includingBlock) || 'unknown', 'executed'); } + for (const s of seg.skipped || []) { skipped++; bump(minerOfBlock.get(s.includingBlock) || 'unknown', 'skipped'); } + } + const summary = existsSync(summaryFile) ? JSON.parse(readFileSync(summaryFile, 'utf8')) : null; + const sinks = await Promise.all(nodes.map(async n => { try { return (await net.dagInfo(n)).sink; } catch { return '?'; } })); + const maxPool = (k) => Math.max(0, ...samples.map(s => s[k] ?? 0)); + const report = { + tool: 'tools/txgen/relay-net.mjs', node: IGNEUMD, topology: 'A - B - C (B dials A and C); generator on A; vmine on B and C', + params: { rate_per_s: RATE, duration_s: DURATION, wallets: WALLETS, fund_ign: FUND, fast_time: true }, + chain_blocks_in_window: chainBlocks, executed, skipped, + by_miner: Object.fromEntries([...byMiner.entries()]), + generator: summary ? { sent: summary.counts?.sent, included: summary.counts?.included, pending_at_end: summary.counts?.pending_at_end, included_per_s: summary.throughput?.included_per_s, latency_ms: summary.latency_ms, blocks_with_content: summary.blocks?.with_content, errors: summary.errors, stop_reason: summary.stop_reason } : null, + pools: { samples, max: { A: maxPool('A'), B: maxPool('B'), C: maxPool('C') } }, + sinks_agree: new Set(sinks).size === 1, + wall_s: +since(), + }; + writeFileSync(join(OUT, 'relay-report.json'), JSON.stringify(report, null, 2) + '\n'); + log('RELAY REPORT'); + log(` chain blocks ${chainBlocks}, executed ${executed}, skipped copies ${skipped}, sinks agree ${report.sinks_agree}`); + for (const [m, r] of byMiner) log(` miner ${m}: blocks ${r.blocks}, with transactions ${r.blocks_with_txs}, carried ${r.txs_carried}, executed ${r.executed}, skipped ${r.skipped}`); + if (summary) log(` generator: sent ${summary.counts?.sent}, included ${summary.counts?.included}, pending ${summary.counts?.pending_at_end}, ${summary.throughput?.included_per_s}/s, latency p50 ${summary.latency_ms?.p50} p90 ${summary.latency_ms?.p90} max ${summary.latency_ms?.max} ms`); + log(` pool max: A ${report.pools.max.A}, B ${report.pools.max.B}, C ${report.pools.max.C}`); + log(` report ${join(OUT, 'relay-report.json')}`); + const included = (byMiner.get('B')?.executed || 0) + (byMiner.get('C')?.executed || 0); + exitCode = included > 0 && (byMiner.get('B')?.executed || 0) > 0 && (byMiner.get('C')?.executed || 0) > 0 ? 0 : 1; +} catch (e) { + log(`FAILED: ${e.stack || e.message}`); +} finally { + await cleanup(); +} +process.exit(exitCode); From c091eec39d60602c921f77d021c02858ec0ddbfa Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:01:00 +0000 Subject: [PATCH 003/158] Igneum Miner 0.3.10: the six version files Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/Cargo.lock | 2 +- app/igneum-app/Cargo.toml | 2 +- app/igneum-app/resources/igneum-app.rc | 8 ++++---- app/windows/version.h | 4 ++-- packaging/mac/app/Info.plist | 2 +- packaging/windows/Igneum-Miner.iss | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/igneum-app/Cargo.lock b/app/igneum-app/Cargo.lock index 601d3542..f3a5ca19 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.9" +version = "0.3.10" dependencies = [ "ed25519-dalek", "getrandom", diff --git a/app/igneum-app/Cargo.toml b/app/igneum-app/Cargo.toml index e3304671..bf0f5163 100644 --- a/app/igneum-app/Cargo.toml +++ b/app/igneum-app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "igneum-app" -version = "0.3.9" +version = "0.3.10" 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 3881fb70..0aadc835 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,9,0 -PRODUCTVERSION 0,3,9,0 +FILEVERSION 0,3,10,0 +PRODUCTVERSION 0,3,10,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.9" + VALUE "FileVersion", "0.3.10" VALUE "InternalName", "igneum-app" VALUE "LegalCopyright", "Igneum contributors" VALUE "OriginalFilename", "igneum-app.exe" VALUE "ProductName", "Igneum Miner" - VALUE "ProductVersion", "0.3.9" + VALUE "ProductVersion", "0.3.10" END END BLOCK "VarFileInfo" diff --git a/app/windows/version.h b/app/windows/version.h index f973e572..4c841424 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.9" -#define IGNEUM_HOST_VERSION_RC 0,3,9,0 +#define IGNEUM_HOST_VERSION_STR "0.3.10" +#define IGNEUM_HOST_VERSION_RC 0,3,10,0 #endif diff --git a/packaging/mac/app/Info.plist b/packaging/mac/app/Info.plist index 68290b21..30760a88 100644 --- a/packaging/mac/app/Info.plist +++ b/packaging/mac/app/Info.plist @@ -11,7 +11,7 @@ CFBundleVersion VERSION_STAMP CFBundleShortVersionString - 0.3.9 + 0.3.10 CFBundlePackageType APPL CFBundleExecutable diff --git a/packaging/windows/Igneum-Miner.iss b/packaging/windows/Igneum-Miner.iss index 11e35926..66d906a9 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.9" + #define AppVersion "0.3.10" #endif #define AppName "Igneum Miner" #define Publisher "Igneum" From cb67729091730201e469a47fbf16a5c1ae89211b Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:02:23 +0000 Subject: [PATCH 004/158] packaged-config: the devnet override object with all four switches (finality v3 at DAA 135,200, published 17:59Z) in the packaged line; the self-test's no-override check passes an empty value itself instead of reading the shipped default Co-Authored-By: Claude Fable 5.1 --- packaging/mac/packaged-config.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/mac/packaged-config.sh b/packaging/mac/packaged-config.sh index 5f317ec6..6731e2fa 100644 --- a/packaging/mac/packaged-config.sh +++ b/packaging/mac/packaged-config.sh @@ -28,7 +28,7 @@ DL_HOST="https://dl.igneum.network" # carry the devnet's activation height here, the same N as every other devnet node, before it is cut (Mac and CI alike: # make-payload.sh sources this file). Rule and order: docs/plans/difficulty-v2-rollout-devnet.md. # Example: NODE_OVERRIDE_PARAMS='{"difficulty_v2_activation_daa": 123456}' -NODE_OVERRIDE_PARAMS='' +NODE_OVERRIDE_PARAMS='{"difficulty_v2_activation_daa":33000,"proving_v0_activation_daa":84100,"fees_v1_activation_daa":210000,"finality_v3_activation_daa":135200}' # igneum_secret_file -> the file to read: the variable when set, else .next when it # exists, else ; IGNEUM_CONFIG_DIR (tests) replaces ~/.config/igneum @@ -109,7 +109,7 @@ if [ "${BASH_SOURCE[0]}" = "$0" ]; then check "fingerprint is 8 hex" "$(igneum_fingerprint abc | grep -cE '^[0-9a-f]{8}$')" "1" check "fingerprint of abc" "$(igneum_fingerprint abc)" "ba7816bf" # 5. the written JSON: defaults pick the .next pair; the values land; nothing printed carries them - out="$(write_packaged_config "$T/a.json")" + out="$(NODE_OVERRIDE_PARAMS='' write_packaged_config "$T/a.json")" check "output names the .next key file" "$(printf '%s' "$out" | grep -c 'log-intake-key.next')" "1" check "output never carries the key" "$(printf '%s' "$out" | grep -c 'nextkeyvalue')" "0" check "output never carries the token" "$(printf '%s' "$out" | grep -c 'tok2new')" "0" From 574eacc2651aeaac6ce49a1546ba5b61c47de50f Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:05:08 +0000 Subject: [PATCH 005/158] jobrun: a refused, cancelled or timed-out administrator prompt fails the job (exit 251) The elevated launcher exited 0 after Windows cancelled an unanswered UAC prompt at 122 s (PC 1 driver job, 5 October 2026), so the job read as done. Start-Process now runs under -ErrorAction Stop in a try/catch, a missing process is exit 251, and the summary says what to do. Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/src/jobrun.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/app/igneum-app/src/jobrun.rs b/app/igneum-app/src/jobrun.rs index bc833a21..dc7a6773 100644 --- a/app/igneum-app/src/jobrun.rs +++ b/app/igneum-app/src/jobrun.rs @@ -1119,7 +1119,10 @@ fn run_script(shared: &Arc, job: &Job, sink: &Sink, jobs_dir: &Path, dat std::fs::write(&wrapper, [b"\xEF\xBB\xBF".as_slice(), w.as_bytes()].concat()).map_err(|e| e.to_string())?; let _ = std::fs::remove_file(&out_file); let inner = format!("-NoProfile -ExecutionPolicy Bypass -File \"{}\"", wrapper.display()); - let ps = format!("$p = Start-Process -FilePath powershell.exe -ArgumentList '{}' -Verb RunAs -Wait -WindowStyle Hidden -PassThru; exit $p.ExitCode", inner.replace('\'', "''")); + // A refused or unanswered UAC prompt makes Start-Process throw (`$p` stays null) and `exit $p.ExitCode` + // would exit 0: the 5 October 2026 driver job on PC 1 was reported "done" after Windows cancelled its + // prompt at 122 s. The launch failure is exit 251 and says so on stderr. + let ps = format!("try {{ $p = Start-Process -FilePath powershell.exe -ArgumentList '{}' -Verb RunAs -Wait -WindowStyle Hidden -PassThru -ErrorAction Stop }} catch {{ Write-Error ('elevated launch failed (UAC refused, cancelled or timed out): ' + $_.Exception.Message); exit 251 }}; if ($null -eq $p) {{ Write-Error 'elevated launch failed: no process'; exit 251 }}; exit $p.ExitCode", inner.replace('\'', "''")); cmd = Command::new(crate::platform::tool("powershell")); cmd.args(["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", &ps]); } else if shell == "powershell" { @@ -1145,6 +1148,7 @@ fn run_script(shared: &Arc, job: &Job, sink: &Sink, jobs_dir: &Path, dat fn finish_ran(ran: Ran, what: &str) -> Result { match ran.code { Some(0) => Ok(Done { status: "done".into(), exit: 0, summary: format!("{what} finished, exit 0"), extra: json!({}) }), + Some(251) => Ok(Done { status: "failed".into(), exit: 251, summary: format!("{what} did not start: the administrator prompt was refused, cancelled or timed out (click Yes within 2 minutes)"), extra: json!({}) }), Some(c) => Ok(Done { status: "failed".into(), exit: c as i64, summary: format!("{what} exited with code {c}"), extra: json!({}) }), None if ran.timed_out => Ok(Done { status: "timeout".into(), exit: -1, summary: format!("{what} hit the time cap and was ended"), extra: json!({}) }), None => Err(format!("{what} was ended")), @@ -1259,6 +1263,20 @@ fn collect_done(uploaded: u32, failed: u32, names: Vec, ran: Option mod tests { use super::*; + #[test] + fn a_refused_administrator_prompt_is_a_failure_not_done() { + // 5 October 2026: the elevated launcher exited 0 after Windows cancelled an unanswered UAC prompt + let d = finish_ran(Ran { code: Some(251), timed_out: false }, "script").unwrap(); + assert_eq!((d.status.as_str(), d.exit), ("failed", 251)); + assert!(d.summary.contains("administrator prompt"), "{}", d.summary); + let d = finish_ran(Ran { code: Some(0), timed_out: false }, "script").unwrap(); + assert_eq!(d.status, "done"); + // the launcher string itself: a thrown Start-Process must not fall through to `exit $p.ExitCode` + let src = include_str!("jobrun.rs"); + assert!(src.contains("-Verb RunAs -Wait -WindowStyle Hidden -PassThru -ErrorAction Stop }} catch {{")); + assert!(src.contains("if ($null -eq $p) {{ Write-Error 'elevated launch failed: no process'; exit 251 }}")); + } + #[test] fn collect_outcome_follows_the_command_exit() { let d = collect_done(0, 0, vec![], None); From 3e129eeb5cd051b9615a4ddec882efbb62a0d748 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:17:36 +0000 Subject: [PATCH 006/158] C4 fix: certificate-driven reorg written into spec 3.5, 3.2 C4, 3.10 C4 and F1/F2, 3.11.7; ledger C4 fix paragraph, F16 note (the honest-partition row for option B is gone), O-3.6 narrowed; bench-log "the C4 fix" with every harness row; c4.mjs v2 mode, WINDOW knob, forced reconnect at the heal (addPeer, nodes on --unsaferpc), adopted-lock count; two tooling classes fixed: the signer piped into head (SIGPIPE panic under pipefail, four scripts, tools/ci/signer-pipe-check.sh in CI) and the one shared build-inputs.zip (build-job.mjs names every job's zip, push-build-inputs.sh --name and pruning) Fork: vendor/igneum-node-c4 branch c4-fix on release-0.3.6 a24ab01a. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 2 ++ docs/bench-log.md | 25 +++++++++++++ docs/fud-ledger.md | 4 +++ docs/spec/03-finality.md | 11 +++--- docs/spec/06-open-items.md | 2 +- packaging/ota/publish-jobs.sh | 2 +- packaging/ota/publish-manifest.sh | 2 +- packaging/ota/test-publish-jobs.sh | 2 +- packaging/windows/push-build-inputs.sh | 16 ++++++--- packaging/windows/push-inputs.sh | 2 +- tools/build-job.mjs | 9 ++++- tools/ci/signer-pipe-check.sh | 16 +++++++++ tools/finality-attacks/c4.mjs | 50 +++++++++++++++++++++----- tools/finality-attacks/lib/net.mjs | 3 ++ 14 files changed, 123 insertions(+), 23 deletions(-) create mode 100755 tools/ci/signer-pipe-check.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d37d7dc..8fd44cf6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,8 @@ jobs: run: bash tools/ci/identity-check.sh - name: copied sources are re-stamped before a build run: bash tools/ci/copied-sources-check.sh + - name: the signer is never piped into head + run: bash tools/ci/signer-pipe-check.sh - name: pinned guest programs match their manifest and are built only by pin-guests.sh run: bash tools/ci/pinned-guests-check.sh - name: faucet unit tests (validation, the daily limits, the signed transaction; keccak, RLP and secp256k1 vectors) diff --git a/docs/bench-log.md b/docs/bench-log.md index ddab9e48..d367ccef 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -1435,6 +1435,31 @@ The fix, both sides ours. Fork (`vendor/igneum-node-txgen`, branch `txgen-export Also noted: the collect job uploads the first 256 KiB of a log file, so a tail needs `--command "powershell -NoProfile -Command Get-Content -Tail 500 logs\app-....log"`. Commands: `tools/lock/with-lock.sh run node tools/txgen/run.mjs --duration 1200 --rate 2 --wallets 16 --fund 2 --cap 40 --summary `; `node tools/txgen/proving-watch.mjs watch --interval 30 --duration 1560 --out `; `node tools/txgen/proving-watch.mjs report --summary --pc2-log `; `node tools/prove-fixtures/complete-export.mjs seq.json out.json 72803,72854`; `igneum-prove-export out.json 72803 proving/fixtures/block-72803-skipped-copies.json`. +## 5 October 2026 (night), the C4 fix: certificate-driven reorg + +Owner: the consensus engineer and cryptographer agent, worktrees `igneum-wt-c4` (branch `c4-fix`) and `vendor/igneum-node-c4` (fork branch `c4-fix` on release-0.3.6 a24ab01a). Harness `tools/finality-attacks/c4.mjs` on the fast-time 3-node network (100-ms proxied links), node built on the Mac in `vendor/igneum-node/target-c4` from the fork worktree (an APFS clone of `target-036`), suites on PC 2 through `tools/build-job.mjs`. The Mac carried two other builds and the M20 live sync throughout; every figure is a count, an index or a second from the harness clock. + +**The cause, in the code.** `processes/finality.rs`: `ingest_certificate` verified a certificate only when its block was the node's own determination at that index (`cp.hash == cert.checkpoint`); any other block went to `hold_pending`, and nothing ever tried the pending certificate against the table at its own block. `fork_choice_lock` reads `state.locks`, which only `evaluate` filled, and `evaluate` only ever ran over the node's own determination. So a certified checkpoint off the node's chain never became a lock and never constrained the sink search, whatever spec 3.5 says. Second cause, found tonight on the harness: `protocol/flows/src/v10/blockrelay/flow.rs` skips a relayed block whose blue work is under the virtual's merge-depth root ("hence we are skipping it"), and the certified chain is lighter by construction, so the node on the heavier side never received the certified chain's blocks at all: in the first runs on the fixed consensus n0 held B's certificates by gossip for the whole heal window and B's blocks never arrived (n0's log shows only its own blocks "via submit block" after the reconnect). + +**The fix.** Fork: `ingest_off_chain` (verifies against `voters_at` of the certificate's own block, Q3 and Q5 by `quorum_at` from that block's past, the lock chain by `off_lock_chain`, then LOCKED with a `FinalityLock` notification and a `VirtualStateProcessingMessage::Resolve` nudge so the sink moves without waiting for a block); `retry_pending_off_chain` on every virtual change; the lock-chain guard in `evaluate` (a determination off the chain through the node's nearest locks never locks and never aggregates); `fork_choice_lock` reports a lock beyond the depth-based finality point once; `wants_unknown_certified_block` and the relay-flow bypass of the merge-depth skip while a pending certificate names a block the node lacks (`finality_wants_blocks` through `ConsensusApi` and the session). Not gated on `finality_v3_activation_daa`: rule v2 took the same pending path. + +**Unit tests (PC 2, job build-20261005-180827, 18:09 UTC): `kaspa-consensus` 97 passed, 0 failed, 3 ignored; `kaspa-consensus-core` 101 passed.** New: `a_lighter_certified_chain_wins_and_a_heavier_uncertified_one_does_not_override_it` (main chain 77 blocks locks to 13, a 7-block side chain's index-14 certificate is adopted, the sink moves to the side tip with no new block, ten more main-chain blocks do not move it back, a second certificate at 14 over the main block is CONFLICTING and the lock stands, the side chain then locks 15), `the_certificate_driven_reorg_holds_under_rule_v2` (the same at `finality_v3_activation_daa` never), `a_chain_that_misses_an_adopted_lock_never_locks_here` (the evaluate guard and the off-lock conflict). `reorg_past_an_unlocked_checkpoint_re_determines_it_and_verifies_the_pending_certificate` rewritten for the new behaviour (pending while the block is unknown, adopted when it arrives). `kaspa-p2p-flows` lib tests do not compile on release-0.3.6 before or after this change (nine `epoch_seed_headers` errors in the pruning-proof message tests; the M20 job build-20261005-172340 hit the same nine an hour earlier). Six PC 2 jobs were lost tonight to two tooling faults, both fixed in the class: `igneum-ota-sign embedded | head -1` under `pipefail` (SIGPIPE panic, four scripts, `tools/ci/signer-pipe-check.sh`) and the one shared `build-inputs.zip` in the downloads folder (a job published while another agent's pack landed pinned that agent's sources, three times; `build-job.mjs` now names every job's zip). + +**Harness, weight against work (B four keys and 70% of the weight, A two keys and 30%; at the cut A mines 0.6 and B 0.4 blocks/s; `WINDOW` = weight window, ban and `min_daa` at fast time).** The 120-DAA window of the earlier runs turns every long split into F21's partition-longer-than-a-window shape once the p2p reconnect is added: n0 dials the proxy again on the connection manager's backoff, 84 to 114 s after the heal in every run tonight (the original sweep's 6 s was a short cut), so A's chain is 130 + 84 s = 128 DAA past the cut before any certificate can reach it, past its 120-DAA frozen table (v3) or its own two-thirds share of a sliding table (v2, 126 DAA at W 240 and s 0.3), and A locks alone first. With `WINDOW=240` and `WARM=320` the bound is 400 s (v3) or 210 s (v2) after the cut. + +| Run | Node | Rule, W, split | B locks during the split | n0 reconnected | n0 adopted off-chain | Final chain | Conflicting | Disagreeing | Verdict | +|---|---|---|---|---|---|---|---|---|---| +| on 90 s (the sweep's framing) | c4 consensus fix, no sync hook | v3, 120, 90 s | 0 (36 blue blocks for B, a new index needs 50) | 6 s | 0 | A, all three (no certificate to follow) | 0 | 0 | not the C4 shape | +| v2 90 s | same | v2, 120, 90 s | 1 (index 8) | 6 s | 0 | apart | 5 / 5 / 5 | 2 | n0 locked 10 alone at 18:32:42, B's certificate for 8 reached it at 18:32:43: F21's bound (63 DAA of A's own chain) crossed before the heal | +| on 130 s | same | v3, 120, 130 s | 1 (index 9) | 84 s | 0 | apart | 9 / 3 / 3 | 2 | n0 locked 12 alone at DAA 359, one window after lock 8 at 239, 6 s before the reconnect | +| off 150 s (control) | same | no certificate, 150 s | 0 | 96 s | 0 | A (heavier), all three; B's nodes re-determined 2 indices | 0 | 0 | PASS, as in the sweep | +| on 130 s, W 240 | same | v3, 240, 130 s | 2 (10, 11) | 114 s | 0 (certificates 13 and 14 pending, blocks unknown) | apart | 0 | 0 | the sync gap: n0 never received a B block | +| v2 130 s, W 240 | same | v2, 240, 130 s | 2 (10, 11) | 114 s | 0 | apart, n0 locked 16 alone at 293 s | 0 / 1 / 1 | 0 | the sync gap again (n0 reconnected after v2's 210-s bound) | +| v2 130 s, W 240 | c4 fix with the sync hook | v2, 240, 130 s | 1 (index 12) | 84 s | 3 (12, 13, 14 within 2 s of the first B block; 11 re-determined) | B, all three, A's split tip abandoned | 0 | 0 | PASS | +| on 130 s, W 240 | same | v3, 240, 130 s | 0 (Poisson: 52 blue blocks, the index fell just short) | 84 s | n1 1, n2 2 (B's nodes adopted A's post-heal certificates and moved before IBD) | A, all three | 0 | 0 | the mirror case; not the C4 shape | + +Reading. With the consensus fix and the sync hook, a node on the heavier chain that receives a certificate for a chain it has never seen fetches that chain, verifies the certificate at its own block, locks it, moves its sink to the lighter certified chain and re-determines its own records onto it (the v2 W 240 row: 0 conflicts, 0 disagreements, every node on B's chain, which is the spec's F1 and the design's Fork choice items 1 to 4). The same code holds under rule v3 (unit test, the mirror case in the last row); a v3 row with B certifying during the split is Poisson-limited at these rates and is the run still owed (`SPLIT=140`). The fix does not and cannot cover a partition that outlasts the bound before the certificate arrives (rows 2, 3 and 6): there the node has already locked alone and 3.11.4 keeps that lock, the late certificate is CONFLICTING for the operator. On the live devnet (W 7,200 DAA, two hours) the bound is two hours after a side's last lock, so every partition under that heals by certificate. Raw: `scratchpad c4-results-*.md`, node logs `c4-*-n0.log`. + ## 5 October 2026 (evening), FUD ledger sweep round 6 Owner: the consensus engineer and cryptographer agent, worktree `igneum-wt-fud-a` (branch `fud-a`), 15:45 to 16:40 UTC. The Mac was loaded throughout (two cargo builds, a txgen run and a fee-switch simnet by other agents; load average over 100), so every figure below is a count, an index, a byte or a number from another machine; the only millisecond figures are the browser verifier's, taken as ratios and labelled. Live reads through the Mac node's wRPC (`ws://127.0.0.1:28640`) and the log intake (Neon HTTP SQL, lines split server-side), never a restart. diff --git a/docs/fud-ledger.md b/docs/fud-ledger.md index 1348895a..2003f971 100644 --- a/docs/fud-ledger.md +++ b/docs/fud-ledger.md @@ -620,6 +620,8 @@ Evidence: design doc Finality v2, Fork choice items 1 to 4; `sim/results.md` fin Sweep (5 October 2026, evening): the module-on against module-off comparison of O-3.8, run on the fast-time harness with the live node line (`tools/finality-attacks/c4.mjs`, fork 2b6d23ef, 3 nodes, 100-ms proxied links; raw tables in `docs/bench-log.md`, "FUD ledger sweep round 6", C4). The scenario separates weight from work: side B (n1, n2, four keys) holds 70% of the weight table and side A (n0, two keys) 30% when the link is cut; from the cut A mines at 0.6 blocks/s and B at 0.4, so A's chain is the heavier one by blue work while only B can certify under rule v3 (A holds 30% of the frozen table). Module off (`min_daa` never, so no certificate can form, fork choice bare GHOSTDAG): after a 150-s split the three nodes converged on A's heavier chain within 36 s of the heal, B's nodes re-determined their two split-time checkpoints onto it (F24), 0 conflicts. Module on (rule v3 from checkpoint DAA 0), 90-s split, n0 back on the link 6 s after the heal, A's chain at about 58 DAA of its own time, well inside the 120-DAA frozen table: during the split A locked nothing and B locked indices 7 and 8 on its own blocks, as designed; after the heal n0 did not switch. Its log: B's certificates for 8 and 9 arrived and were "kept pending until the chain decides (no lock at this index)" (the F24 path), n0's chain never changed because GHOSTDAG prefers its heavier tip and nothing in the node turns a verified certificate over an off-chain block into a fork-choice constraint, and one window after n0's last lock (index 7 at DAA 209, so from DAA 329) the frozen table no longer applied on A's chain ("no frozen table (no lock on this chain inside the window)"), A's two keys were 100% of A's own window table (B's post-cut blocks are red there and earn nothing), and n0 locked 10, 11 and 12 alone; B's certificates for 10 and 11 then logged CONFLICTING on n0, and B's nodes kept their certified chain. End state: sinks apart, 2 locked indices disagreeing across the nodes, a finality fork from a 96-s honest partition with no attacker and the frozen table intact at the heal; the same shape with a 150-s split (the table expired at the heal) and under rule v2 (the control: 1 conflict, sinks apart). So the answer to the critic is sharper than conceded: the overlay is specified to override blue work (spec 3.5, "GHOSTDAG among tips through all certified checkpoints") but the shipped node applies a certificate only to a block on its own chain, holds the rest pending a reorg that GHOSTDAG alone never produces, and after one window the heavier side certifies its own chain. Two honest views never reconcile. What closes it: a verified certificate over a block the node does not have on its selected chain must verify against the weight table at THAT block (its signers' weight there) and, when valid, constrain fork choice to tips through it, forcing the reorg (a certificate-driven reorg, bounded by the finality depth), with the node's own unlocked records re-determined on the new chain (F24); until then the exchange guidance of 3.9 (a node partitioned for more than a minute treats its locks as proof of work until it has seen the network's certificates agree with its own) is the only protection, and the 3.11.7 row for this case ("a certificate over a chain the node is not on") is missing. On the live devnet the window is 7,200 DAA (two hours) and the cliff is two hours after a side's last lock; a miner who joins with more hashrate than the weight table credits is the realistic work-majority side. The trace-driven adversary of O-3.8 is still owed. Spec rows: 3.5, 3.11.4, 3.11.7; node: `processes/finality.rs` (`ingest_certificate`'s pending branch, `fork_choice_lock`). Decision owner: the project lead (gate 3; a rule change to the node's fork choice). +Fix (5 October 2026, night): the certificate-driven reorg, built, unit-tested and measured; fork branch `c4-fix` on release-0.3.6 (a24ab01a), main branch `c4-fix`. The cause in the code: `processes/finality.rs` `ingest_certificate` verified a certificate only over the node's own determination and sent every other block to `hold_pending`; `fork_choice_lock` reads `state.locks`, which only `evaluate` filled over the node's own chain; so a certified block off the chain never became a lock. A second cause only the harness showed: the block relay (`protocol/flows/src/v10/blockrelay/flow.rs`) skips a relayed block lighter than the virtual's merge-depth root, and the certified chain is the lighter one by construction, so the heavier side never even received it. The fix: `ingest_off_chain` verifies a certificate against the voter table at its own block (canonical list, aggregate BLS, 2/3 of active and of total there, the frozen table under v3, the first-month gate), checks the block lies on the chain through the node's nearest locks (else CONFLICTING, 3.11.4, no lock withdrawn), locks the index on that block and asks the virtual processor to resolve (`VirtualStateProcessingMessage::Resolve`), so the sink search keeps only tips through it, whatever the blue work and whatever the merge depth (finality outranks merge depth; the depth-based finality point still bounds it, Kaspa's pruning safety, logged once); pending certificates over blocks the node lacks are retried on every virtual change; `evaluate` locks the node's own determination only on the chain through its locks; and while a pending certificate names a block the node lacks (`finality_wants_blocks`), the relay takes the lighter block, which orphans, falls out of range and triggers IBD of the certified chain. Not gated on v3: the live devnet's rule v2 took the same pending path (unit test `the_certificate_driven_reorg_holds_under_rule_v2`). Spec 3.5 carries the rule in one paragraph, 3.2 C4 and the 3.10 rows C4 and F1/F2 the implementation. Measured (bench-log "the C4 fix", `c4.mjs` with `WINDOW=240` so a 130-s split plus the 84-s p2p reconnect stays inside the window; the sweep's 120-DAA framing crosses F21's bound before any certificate can arrive once the real reconnect time is counted): under rule v2 side B locked index 12 during the split, n0 took B's first relayed block through the hook, locked 12, 13 and 14 by certificate within 2 s, re-determined 11, and all three nodes ended on B's certified chain with 0 CONFLICTING and 0 disagreeing locked indices (was: sinks apart, 5 conflicts on each node, 2 disagreeing); the module-off control is unchanged (heavier chain, 0 conflicts); the mirror case under v3 (B certified nothing, A certified after the heal) had B's nodes adopt A's certificates and move before IBD. Unit tests on PC 2: `kaspa-consensus` 97 passed, `kaspa-consensus-core` 101 passed. Still owed: a v3 harness row with B certifying during the split (Poisson at 0.4 blocks/s over 130 s; `SPLIT=140` queued), the live-devnet partition test of O-3.6, and the trace-driven adversary of O-3.8. What the fix does not cover, by design: a partition that outlasts the bound before the certificate arrives (the side has locked alone, 3.11.4 keeps it, the late certificate is CONFLICTING for the operator), which on the devnet means over two hours. Rollout: a consensus-behaviour change in the node with no params-digest change; a mixed fleet disagrees only in the state the old node already got wrong (an old node holds the certificate pending and stays on its heavier chain while new nodes move), and converges once every node is new; ship in the next node release with every node restarted on it. + ### C5. vs Ethereum: you compare inclusion to finality "'Included in about one second, against twelve on Ethereum.' Inclusion in a DAG is not confirmation. Ethereum's twelve seconds is a slot, its finality is about thirteen minutes, and you compare your two-minute lock to that as if a two-minute lock by a pool committee were the same thing." @@ -1254,6 +1256,8 @@ Sweep (5 October 2026, evening): the two options, with their measured cost. Recommendation: Option B, which spec 3.11.4 already states and O-3.17 names; it is Kaspa's rule for a finality conflict (`vendor/rusty-kaspa/consensus/notify/src/notification.rs`, `FinalityConflict`), it is the only reading under which an exchange can credit on a lock, and its cost falls on a state that needs a 34% equivocator or a 30-day partition. What it needs: the 3.5 paragraph replaced by 3.11.4's text, `finality_conflict` and the `finality_active` clear in the node, and the forced-double-certificate devnet test of 3.11.7. Decision owner: the project lead (gate 3). +What the C4 fix changes for option B (5 October 2026, night): the honest-partition row above is gone. Before the fix a 96-s partition with the table intact put two certified chains on the network with no equivocator (C4: the work-majority side held the other side's certificates pending, then certified its own chain), and option B would have paused finality on every node of that side for an operator. With the certificate-driven reorg (spec 3.5, `ingest_off_chain`) a node that receives a valid certificate for a chain it is not on adopts it and moves, so after a heal shorter than a window there is one chain of locks and nothing to withdraw: the module-on harness ended with 0 conflicting certificates and 0 disagreeing locked indices on all three nodes, under rule v3 and under v2 (bench-log "the C4 fix"). What remains for option B is exactly the states 3.11.4 names: an equivocator at one third or more, and a partition longer than a window (both sides certify their own chain before the heal; the node then holds a lock at a higher index on the other chain, `off_lock_chain`, and the late certificate is CONFLICTING, kept for the operator, no lock withdrawn). The node still does not clear `finality_active` or expose `finality_conflict` (O-3.17). + Answer: Correct as the proposal stands. For an exchange "locked" must be irrevocable or it is a confirmation count. The alternative is Kaspa's: a verified certificate is never re-evaluated; two certificates at one index are a chain split that halts `finality_active` until an operator intervenes, and the node never reports a lock it may withdraw. Equivocation costing history and not coins (F6) means the attacker who caused the split keeps the deposit either way. Decision at gate 3; the devnet partition-and-heal test of O-3.6 measures whichever rule is chosen. Evidence: spec 3.5, 3.9. Review id R3.17. diff --git a/docs/spec/03-finality.md b/docs/spec/03-finality.md index b5c0a7a6..bbb6c88c 100644 --- a/docs/spec/03-finality.md +++ b/docs/spec/03-finality.md @@ -32,7 +32,7 @@ All in public, on the hashrate charts. 51% never reaches 2/3 while honest miners - **C1.** Checkpoint i is the selected-chain block at blue score 30 i. It is determined once the virtual's blue score reaches 30 i + d. d = 60 at 1 block per second is a placeholder (ledger F7): the gate 3 devnet records the reorg-depth distribution and sets d so that a vote split at one index is rare and self-heals at the next. d scales with block rate. Re-determination (rule of 4 October 2026, night, ledger F24): while index i is not locked, a node whose selected chain moves past C_i (a reorg deeper than d) determines index i again on its new chain; its own votes for the old block stand (a key never signs two blocks at one index) and a certificate the network formed over the new block, received meanwhile and held pending, is then verified. A locked index is never re-determined (3.11.4): fork choice keeps the chain through its block, and a certificate for another block there is a conflict (C4). A lock lands about 90 to 120 s after a transaction (Designed; simulated lock latency after the checkpoint block is median 2.5 s, p99 4.6 s at a 2-s inter-region delay, `sim/results_v2.md` A). - **C2.** A vote is a BLS signature over `(chain_id, i, hash(C_i))` under a fixed domain-separation tag. Votes gossip as their own message type. - **C3.** A lock certificate for index i is an aggregate BLS signature over one checkpoint block hash with a bitmap of signers, whose signed weight meets Q3. Every block carries the highest certificate its producer knows. A block whose selected chain does not pass through every certified checkpoint in its past is invalid (section 2.4). -- **C4.** A node holding a LOCK at index i rejects any other certificate for index i and publishes the pair as evidence (section 3.6). A certificate over a block that is not the node's own determination at an index it has not locked is not a conflict: the chain may still move to that block (C1 re-determination, ledger F24), so the node keeps it pending, bounded, until it does or the index is left behind. A certificate naming a block that cannot be index i's checkpoint on any chain (its blue score is under 30 i, or its selected parent's is not) is refused outright. +- **C4.** A node holding a LOCK at index i rejects any other certificate for index i and publishes the pair as evidence (section 3.6). A certificate over a block that is not the node's own determination at an index it has not locked is not a conflict: the node verifies it at that block and, when it is valid there, locks the index on it and moves its chain (3.5, the certificate-driven reorg, rule of 5 October 2026 night, ledger C4). A block the node does not have yet is kept pending, bounded, and tried again as the DAG arrives (ledger F24). A certificate naming a block that cannot be index i's checkpoint on any chain (its blue score is under 30 i, or its selected parent's is not) is refused outright. - **C5.** No certificate may form in the chain's first 3,600 DAA seconds (design document). See 3.8 for the proposed first-month rule. ## 3.3 Quorum @@ -114,6 +114,8 @@ The honest level is therefore the number of indices at which k actually voted an - **F4.** There is no hidden-block penalty in consensus. It was removed in review round 2 because it breaks DAG determinism and amplifies eclipse attacks. First-seen MAY break ties in a node's own block template only. - **F5.** A node started with a configured trusted certificate follows it. A node started cold selects the DAG with the most accumulated blue work, then follows certificates found in it. A private DAG that out-works the public one over the window is a public 51% event lasting weeks. +**Certificate-driven reorg (rule of 5 October 2026, night; ledger C4; design document Finality v2, Fork choice items 1 to 4).** A node that holds a valid certificate for a checkpoint block that is not on its selected chain MUST move its virtual to the heaviest tip through that block. Valid means: the block is index i's checkpoint on its own chain (C1), it lies on the chain through every lock the node holds, the certificate verifies against the canonical voter list at that block (C3), and its signers meet Q3 and, under rule v3, Q5 there, every input a function of the block's own past. The node records the lock at i on that block, replaces its own record there, and determines its unlocked records again on the new chain (C1 re-determination). Merge depth does not bound this move. Finality outranks merge depth by design (item 2 above: a certified checkpoint removes other tips from candidacy, blue work decides only among candidates), and the certified chain's blocks are valid under their own merge-depth roots. The depth-based finality point does bound it, as F1 already implies: a certified block that is not in the future of the node's depth-based finality point is beyond what any rule can follow (section 2's pruning safety) and needs the operator's trusted certificate (F5). A certificate for a chain that misses a lock the node holds, at any index, is a conflict under 3.11.4: the lock stands and the pair is reported. While a node holds locks, its own determination at a higher index locks only on the chain through them. Before this rule the node held such a certificate pending a reorg that GHOSTDAG alone never produced, and a 96-s honest partition with no attacker ended in a permanent finality fork (bench-log "FUD ledger sweep round 6", C4; the fix and its measurement under "the C4 fix", 5 October 2026 night). + What a node does when it holds two valid certificates at one index after a partition heals is not modelled and not defined (`sim/results_v2.md`, "cannot tell us"; O-3.6). C4 says it publishes the pair. The proposal for gate 3: both certificates are evidence against every key that signed both; the node re-evaluates both against Q3 with those keys' weight struck, and if exactly one still locks it follows that one; if neither or both still lock, F2 decides among the two checkpoint blocks' descendants and the index is treated as uncertified. ## 3.6 Equivocation evidence @@ -170,7 +172,7 @@ Status of this section: Implemented in `vendor/igneum-node` (reading guide in `d | C1 | Checkpoint i is the lowest selected-chain block with blue score at least 30 i (blue scores along the chain can skip values), determined when the sink's blue score reaches 30 i + d, d = 20 on devnet. Re-determination (branch `fud-consensus`, 4 October 2026 night, ledger F24): after every virtual change, every unlocked record whose block is no longer a chain ancestor of the sink is determined again on the new chain (`on_virtual_changed`, "re-determined" log line); the certificate held over the old block is dropped, the fold clock restarts, and the certificates kept pending over the new block (`pending_certificates`, at most 4 per index, indices up to 64 ahead of the next determination) are verified. A locked record is never revisited. Unit test `reorg_past_an_unlocked_checkpoint_re_determines_it_and_verifies_the_pending_certificate` (a 6-block side chain's certificate is pending with no conflict, the 15-block side chain overtakes, index 13 is re-determined and locks from it; a block with the wrong blue score is refused) and `a_locked_checkpoint_pins_the_chain_and_a_certificate_against_it_conflicts` (a side chain twice as long does not become the sink past a lock, the certificate against the lock is the one conflict) | d = 20 is below the placeholder 60; the devnet reorg-depth distribution that sets d has not been recorded. Measured in `docs/bench-log.md`, "round-4 consensus items" (reorg run) | | C2 | BLS signature over `"igneum-vote-v1/" \|\| chain_id \|\| 0 \|\| index \|\| hash(C_i)` under `IGNEUM_VOTE_V1_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_`; the chain id is the prefixed network name (`igneum-devnet`, `igneum-devnet-7`); votes are p2p message 70 and ride in the coinbase extra data of every block | | | C3 | Certificate = index, checkpoint, voter count, signer bitmap over the canonical voter list (keys above dust and not stripped, sorted by key hash), aggregate signature, aggregator key hash and sortition proof. Every template carries the certificates not yet in its past | The validity rule (a block whose selected chain misses a certified checkpoint is invalid) is NOT enforced; only fork choice (F1, F2) is | -| C4 | A certificate at an index for a block other than the one LOCKED there is kept and logged as CONFLICTING (`conflicting_certificates`); at an unlocked index it is held pending (F24 above), not logged as a conflict | Not published as evidence. The rule is now fixed by 3.11 item 4 (the node keeps the certificate it verified first, never re-evaluates it, and reports the conflict); the node does not yet clear `finality_active` or expose `finality_conflict` when the pair appears. Until 4 October 2026 night a reorg deeper than d made the node log every certificate at the moved index as CONFLICTING (ledger F24) | +| C4 | A certificate at an index for a block other than the one LOCKED there is kept and logged as CONFLICTING (`conflicting_certificates`), as is one whose block is not on the chain through the node's nearest locks at any index; at an unlocked index over a block this node holds it goes through the certificate-driven reorg (`ingest_off_chain`, 5 October 2026 night, ledger C4: verified against `voters_at` of that block, Q3 and Q5 by `quorum_at` from the block's own past, then LOCKED there, "LOCKED by certificate" log line, and the virtual processor is asked to resolve again, `VirtualStateProcessingMessage::Resolve`); over a block this node does not have it is held pending (F24 above) and tried again on every virtual change | Not published as evidence. The rule is now fixed by 3.11 item 4 (the node keeps the certificate it verified first, never re-evaluates it, and reports the conflict); the node does not yet clear `finality_active` or expose `finality_conflict` when the pair appears. Until 4 October 2026 night a reorg deeper than d made the node log every certificate at the moved index as CONFLICTING (ledger F24) | | C5, 3.8 | `min_daa` = `weight_window` (2,592,000 DAA s on mainnet, 7,200 on devnet; a unit test pins the equality). `evaluate` never locks, and `ingest_certificate` refuses a certificate from any source, while the checkpoint's DAA score is below `min_daa`; the node logs "finality not active, window filling, N of M" at every determination until the sink's DAA score reaches `min_daa` and reports the same through `getFinalityCheckpoints` (`finality_reason`, `window_filled_daa`, `window_full_daa`). Unit test `processes::finality::tests::no_certificate_while_the_window_is_filling`: one key holding 100% of the weight signs every checkpoint of a 150-block chain at a 60-DAA window; nothing certifies below DAA 60, a hand-built certificate at an early index is refused, every checkpoint from DAA 60 locks (fin-fixes, 4 October 2026) | Implemented on 3.8's recommendation ahead of the launch-month simulation (O-3.1), which is still not run; gate 3 can lower the gate but not remove it without reopening ledger F1. The sink's DAA score the report compares is the one the virtual processor last handed the manager, so a restarted node reports the window as filling until its first virtual resolution | | Q1, Q2 | Presence window 20 indices on devnet (240 mainnet). Block reading: participation counts the indices in `[i - P, i - 1]` at which a vote by the key is carried by any block, blue or red, in the past of C_i; a key whose first block in the window is younger than P x 30 DAA seconds counts the full window; every template carries up to 48 votes not already in its past, certificates and evidence first | The per-block vote bound (48) is the devnet value of O-3.3. Participation is credited for any vote by the key at the index, whatever block it names; 3.11.1 requires the vote to name the checkpoint on the crediting chain, else a key can stay in the active denominator by voting for blocks of its own and never add to a certificate (O-3.19) | | Q3 | Integer tests: `3 x signed x P >= 2 x active_num` (active_num = sum of weight x participation count) and `3 x signed >= 2 x total` (was `30 x signed >= 17 x total` until 4 October 2026; `FinalityParams::FLOOR_NUM / FLOOR_DEN` = 2/3 on branch `devnet-v4`, with `quorum_met`, `floor_met` and `locks` as pure functions), both inclusive, both at C_i; bans known at evaluation time are applied to the voter list. Unit test `floor_is_two_thirds_of_total_and_inclusive`: 4 of 6 locks, 3 of 6 does not, 67 of 100 locks, 66 does not, the total test implies the active test for every participation. Measured on the three-node, six-voter network of `docs/bench-log.md`, "finality floor 2/3" (4 October 2026): no lock on either side of a 3/3 split, the 4 side of a 4/2 split locks at exactly two thirds | | @@ -178,13 +180,13 @@ Status of this section: Implemented in `vendor/igneum-node` (reading guide in `d | Q5 | Rule v3 (same branch and switch): `frozen_table` finds the highest locked index below i whose block is an ancestor of C_i (`state.locks`, reachability), takes `voters_at` of that block with the bans known now, and drops it when `daa(C_i) >= daa(C_f) + weight_window`; `evaluate` requires `floor_met(frozen_signed, frozen.total)` of the signers (and of a held certificate's signers) on top of Q3; a locked checkpoint is never downgraded. The LOCKED log line carries the frozen fraction and the frozen lock's index; a checkpoint that passes Q3 and fails Q5 logs "held by the frozen table" at debug. Unit test `frozen_table_holds_a_side_without_the_other_keys_for_one_window`: A at 60% and B at 40% lock together; B leaves; under v2 A locks alone within 30 DAA of B's last block, under v3 not before the last lock is one window old, and then it does | The reference is the node's own highest lock on the chain (not the certificate carried in C_i's past), so a node that has not seen the newest certificate tests against the previous lock's table, which in a connected network differs by 30 s of blocks | | S1 | VRF output = SHA-256 of the voter's BLS signature over `"igneum-sortition-v1/" \|\| chain_id \|\| 0 \|\| index \|\| hash` under the sortition tag (unique per key and message, so the signature is the proof); eligible when `output x total_weight < 8 x weight x 2^64`, drawn by weight (W6, ledger F17, fin-fixes 4 October 2026): the expected number of aggregators is 8 by weight whatever the key count, a key with no weight never draws, a key holding 1/8 of total weight or more always does (so with 8 or fewer equal voters everyone is eligible). Unit test `sortition_is_by_weight_not_key_count`: 200 dust keys draw nothing, 6 real keys draw `sum min(1, 8 w / T)`, 16 equal keys draw 8.00, a key split into 10 or 200 parts draws what it drew whole | Was `output x voters < 8 x 2^64` (per key) until 4 October 2026; measured on the attack harness (`docs/bench-log.md`, "finality v2 attack harness" S2, then "finality fixes F17 and F1"). A key above 1/8 of total weight that splits itself gains seats (its single ticket was capped at 1); seats carry no reward and no power, since anyone MAY aggregate and Q3 is tested by weight | | S2 | Not implemented (sub-user sortition above 8,192 voters) | | -| F1, F2 | In `resolve_virtual` the highest locked checkpoint that is in the future of the depth-based finality point and in the past of some body tip replaces the finality point: tips outside its future are not sink candidates | A lock that no body tip passes through is logged and ignored for that resolution | +| F1, F2 | In `resolve_virtual` the highest locked checkpoint that is in the future of the depth-based finality point and in the past of some body tip replaces the finality point: tips outside its future are not sink candidates. Since 5 October 2026 night (ledger C4) a lock can be a block off the node's selected chain, adopted from a certificate (`ingest_off_chain`), so this is the certificate-driven reorg: the sink search keeps only tips through it, whatever the blue work of the old chain and whatever the merge depth; `evaluate` locks the node's own determination only on the chain through its nearest locks (`off_lock_chain`) | A lock that no body tip passes through is logged and ignored for that resolution; a lock not in the future of the depth-based finality point (a certified chain deeper than the finality depth) is logged once and cannot be followed (F5) | | F3 | Not implemented: the pruning point and `virtual_finality_point` ignore locks | Must land before any pruning network | | F5 | Not implemented (trusted certificate at start) | | | 3.6 | A second vote by one key at one index for another block is evidence, carried in blocks (`EvidenceRecord`: the two votes, the carriers with their DAA scores). Branch `fud-consensus` (4 October 2026 night, ledger F23): the ban at checkpoint C is computed from C's own past (`bans_at`): the key is stripped at C when a carrier lies in C's past and `daa(C) < daa(lowest carrier) + ban` (7,200 DAA seconds on devnet); detection over RPC or gossip only puts the evidence into this node's templates ("detected here: carried in this node's next block"). Evidence records are bounded (4,096, the oldest dropped; a record goes once its ban ended two windows below the sink or it was never carried within one ban of being seen; 16 carriers per record). Unit test `ban_is_decided_by_the_carrying_block_so_nodes_agree_on_every_voter_list`: three nodes on one chain, one takes the equivocating vote over RPC, two see it from the carrier block only; the voter list agrees on all three at every checkpoint, the key is a voter before the carrier and after the ban and nowhere in between, and the third node verifies the first two's certificates at every locked index | A carrier the reachability store no longer holds (pruned) counts as in the past of every checkpoint more than the merge depth younger than it. Until 4 October 2026 night the ban was stamped node-locally (ledger F23). Measured in `docs/bench-log.md`, "round-4 consensus items" (ban run) | | 3.9 | `getFinalityCheckpoints` reports `finality_active` (the window is full and a lock exists within the last P indices), the latest lock, and since 4 October 2026 `finality_reason` (`active`, `window filling, N of M` with N the sink's DAA score capped at `min_daa` and M `min_daa`, or `paused`) with `window_filled_daa` and `window_full_daa`; the miner prints a `FINALITY` line whenever the reason changes | `last_certified` as a DAA score is not reported; the conflict reason of 3.11 item 4 (two certificates at one index) is not reported (O-3.17), so a conflict still reads as `active` or `paused` | | 3.11 item 6 (seed source) | The devnet keys the hourly program on the header's own `daa_score` (`epoch_seed`, `docs/review/round-3-2026-10-03.md`, R3.26), not on a checkpoint block | The `seed_source` rule (section 4.3 with the uncertified fallback of 3.11 item 6) is not implemented; nothing on the devnet exercises a seed during a finality pause | -| 3.11 test table | The four-miner test network of the bench-log entry is the only measurement on a real DAG: 93 checkpoints, 0 conflicting certificates, one equivocation strip, one 12-checkpoint pause under the floor, one heal | d = 20, presence 20 indices and a 7,200-s window are devnet values; the measured pause and heal are at those values, not the mainnet ones | +| 3.11 test table | The four-miner test network of the bench-log entry is the only measurement on a real DAG: 93 checkpoints, 0 conflicting certificates, one equivocation strip, one 12-checkpoint pause under the floor, one heal; since 5 October 2026 night the fast-time harness row for the certificate-driven reorg (3.11.7) | d = 20, presence 20 indices and a 7,200-s window are devnet values; the measured pause and heal are at those values, not the mainnet ones | Node state is one persisted blob (`DatabaseStorePrefixes::IgneumFinality`), written at most once a second; votes received over RPC but not yet carried by a block are lost on restart, votes in blocks are not. @@ -280,6 +282,7 @@ Each guarantee, the scenario that tests it, and the measured result. Bench-log c | Acquired keys decay as the window moves (3.11.5) | results K, keys worth 20% and 40% bought, 30% hashrate, signing and silent, 30 days, five seeds | share follows b (1 - t/30) + 0.3 t/30 within 0.6 points at every sampled day in every seed; keys worth 20% rise to 30% on day 30 and never reach 1/3; keys worth 40% hold the veto from day 1 to day 19 or 20 (formula 20) and end at 30%; withholding its votes, the 40% buyer stalls 63,307 to 68,716 of 86,400 checkpoints in 30 days (the pause lasts until it has decayed below one third) and the 20% buyer 304 to 1,045; 0 conflicts (K, floor 2/3) | | Signing stops while mining continues, 1, 6, 24 h | results J and L1 | 34% and above: every checkpoint stalled for the whole silence; 33%: 665 to 727 of 720 in 6 h; 32%: 35 to 221; 30%: 0 to 40; first lock after resume 0 min at every weight; 0 conflicts (J, L1) | | Seeds during a pause (3.11.6) | devnet epoch boundary through a forced pause | not yet run (O-4.3 implementation) | +| A certificate over a chain the node is not on: the certificate-driven reorg (3.5) | fast-time harness `tools/finality-attacks/c4.mjs`, weight against work, 130-s split, 240-DAA window, rule v2 (the live devnet's) | the work-majority node fetched the certified chain, locked 12, 13 and 14 by certificate within 2 s of the first block, re-determined 11, and all three nodes ended on the certified chain with 0 conflicting certificates and 0 disagreeing locks; the module-off control took the heavier chain (bench-log "the C4 fix", 5 October 2026 night); the same under rule v3 is unit-tested, the harness row with the certifying side locking during the split is still owed | | Two certificates at one index: no lock withdrawn (3.11.4) | devnet with a forced double certificate | not yet run (O-3.17) | | `T` under the block reading (3.11.3) | O-3.3 re-run | not yet run (O-3.18) | | Participation credited only for the chain's checkpoint (3.11.1) | results C with an adversary voting for private blocks | not yet run (O-3.19) | diff --git a/docs/spec/06-open-items.md b/docs/spec/06-open-items.md index 860f1c56..0da101da 100644 --- a/docs/spec/06-open-items.md +++ b/docs/spec/06-open-items.md @@ -54,7 +54,7 @@ An item closes when its measurement is in `docs/bench-log.md` or its decision is | O-3.3 | Parameters of the block reading of participation (rule closed 3 October 2026, section 3.3 Q2 and 3.4.1; ledger F3): the per-block vote bound and the carriage window, and the simulation ran with the narrower cert reading | Add vote carriage in blocks and a hostile aggregator to `finality_v2.py`; re-run A, C, D and F1 under the block reading; set the per-block vote bound and confirm the carriage window of 240 indices | 3 | | O-3.4 | Certificate grace value; must be at least 3x the worst honest one-way delay (section 3.3, Q4) | Measure one-way delays on the devnet across regions; set grace | 3 | | O-3.5 | VRF construction for aggregator selection and the binomial sub-user sortition above 8,192 voters (S1, S2) | Specify (candidate: BLS-based VRF on the vote key, Algorand's binomial sampling); simulate the threshold on sampled weight | 3 | -| O-3.6 | What a node does with two valid certificates at one index after a partition heals; post-heal fork choice is unmodelled (`sim/results_v2.md`, "cannot tell us") | Adopt or replace the proposal in section 3.5; devnet partition-and-heal test | 3 | +| O-3.6 | What a node does with two valid certificates at one index after a partition heals; post-heal fork choice is unmodelled (`sim/results_v2.md`, "cannot tell us"). Narrowed 5 October 2026 night (ledger C4): post-heal fork choice for ONE certified chain is now the certificate-driven reorg of 3.5, implemented and measured on the fast-time harness (`tools/finality-attacks/c4.mjs`, bench-log "the C4 fix"); what is left is the two-certificate case, O-3.17 | Adopt or replace the proposal in section 3.5; devnet partition-and-heal test | 3 | | O-3.7 | The eclipse case is closed by the quorum floor (section 3.3.2, 3 October 2026; ledger F2): 0 conflicting locks at 1, 2 and 4 h against a 34% attacker in the model, but the model grants the attacker the eclipse for free | Devnet with a single-node eclipse recording whether conflicting locks appear, as confirmation of the rule; no rule choice remains | 3 | | O-3.8 | The simulation has no DAG: conflict counts are index collisions; red blocks, merge under the 3,600-s bound and the finality overlay's effect on GHOSTDAG's guarantees are unmodelled (ledger C4, F8) | Devnet runs with the finality module on and off; a churn and adversary simulation driven by real pool-hashrate traces from mid-cap GPU coins (design document, "Three experiments") | 3 | | O-3.9 | Model assumptions that move the numbers: perfect or instant DAA retarget (real lag of the order of an hour, approximate), uptime 97% / 99.5% is a guess, silent sets random by key not by pool or region, keys are free, VRF noise absent (`sim/results.md` and `results_v2.md`) | Re-run `finality_v2.py` with a DAA lag model, a top-pool silent set and a regional silent set; price keys through the P2P layer | 3 | diff --git a/packaging/ota/publish-jobs.sh b/packaging/ota/publish-jobs.sh index 4e26f0ed..96fd0896 100755 --- a/packaging/ota/publish-jobs.sh +++ b/packaging/ota/publish-jobs.sh @@ -128,7 +128,7 @@ if [ ! -x "$SIGNER" ]; then echo "building igneum-ota-sign" (cd "$ROOT/app/igneum-app" && nice -n 19 cargo build --release -j 4 --bin igneum-ota-sign --quiet) fi -EMBEDDED="$("$SIGNER" embedded | head -1)" +EMBEDDED="$("$SIGNER" embedded | sed -n 1p)" OURS="$(tr -d '[:space:]' < "$PUB")" if [ "$EMBEDDED" != "$OURS" ]; then echo "the public key in app/igneum-app/src/manifest.rs ($EMBEDDED) is not $PUB ($OURS); the apps would refuse this file" >&2 diff --git a/packaging/ota/publish-manifest.sh b/packaging/ota/publish-manifest.sh index 33e3786c..e0c8e54f 100755 --- a/packaging/ota/publish-manifest.sh +++ b/packaging/ota/publish-manifest.sh @@ -88,7 +88,7 @@ if [ ! -x "$SIGNER" ]; then echo "building igneum-ota-sign" (cd "$ROOT/app/igneum-app" && nice -n 19 cargo build --release -j 4 --bin igneum-ota-sign --quiet) fi -EMBEDDED="$("$SIGNER" embedded | head -1)" +EMBEDDED="$("$SIGNER" embedded | sed -n 1p)" OURS="$(tr -d '[:space:]' < "$PUB")" if [ "$EMBEDDED" != "$OURS" ]; then echo "the public key in app/igneum-app/src/manifest.rs ($EMBEDDED) is not $PUB ($OURS); the apps would refuse this manifest" >&2 diff --git a/packaging/ota/test-publish-jobs.sh b/packaging/ota/test-publish-jobs.sh index d3fbc968..e35f502f 100755 --- a/packaging/ota/test-publish-jobs.sh +++ b/packaging/ota/test-publish-jobs.sh @@ -97,7 +97,7 @@ grep -q "^inner-identical True$" "$T/inner2.txt" && grep -q "^sig-identical True expect_ok "list reads the folder" "$PUBLISH" list --dest "$D" echo "== the key" -EMB="$("$SIGNER" embedded | head -1)" +EMB="$("$SIGNER" embedded | sed -n 1p)" [ "$EMB" = "$(tr -d '[:space:]' < "$PUB")" ] && ok "the embedded key is the Mac's OTA public key" || bad "the embedded key is not $PUB" echo diff --git a/packaging/windows/push-build-inputs.sh b/packaging/windows/push-build-inputs.sh index 273f80e2..231abdce 100755 --- a/packaging/windows/push-build-inputs.sh +++ b/packaging/windows/push-build-inputs.sh @@ -7,6 +7,10 @@ # # packaging/windows/push-build-inputs.sh [--node ] # [--node-tests "igneum-miner"] [--app-tests "igneum-app"] [--no-app] [--no-deploy] [--out ] +# [--name ] the zip's name in the downloads folder (default build-inputs.zip; build-job.mjs gives +# every job its own name since 5 October 2026 night: with one shared name a job +# published while another agent's pack landed pinned THAT agent's sources, three +# times in one evening; zips older than two days are pruned here) # # What goes in (under igneum-build-inputs/): # manifest.json created_at, node {branch, commit, dirty, source}, repo {branch, commit, dirty}, app_version, @@ -32,6 +36,7 @@ APP_TESTS="${IGNEUM_BUILD_APP_TESTS:-igneum-app}" WITH_APP=1 DEPLOY=1 OUT="" +NAME="" while [ $# -gt 0 ]; do case "$1" in --node) NODE_SRC="$2"; shift 2 ;; @@ -40,6 +45,7 @@ while [ $# -gt 0 ]; do --no-app) WITH_APP=0; shift ;; --no-deploy) DEPLOY=0; shift ;; --out) OUT="$2"; shift 2 ;; + --name) NAME="$2"; shift 2 ;; *) echo "unknown argument: $1" >&2; exit 2 ;; esac done @@ -58,7 +64,9 @@ if [ -z "$OUT" ]; then TOKEN="$(tr -d '[:space:]' < "$TOKEN_FILE")" [ -n "$DLSITE" ] && [ -d "$DLSITE/dl/$TOKEN" ] || { echo "no downloads folder: set IGNEUM_DLSITE or ~/.config/igneum/dlsite-dir (it must hold dl//)" >&2; exit 1; } DEST="$DLSITE/dl/$TOKEN" - OUT="$DEST/build-inputs.zip" + OUT="$DEST/${NAME:-build-inputs.zip}" + # per-job zips older than two days (a job expires in two days) go, with their sha256 and manifest + find "$DEST" -maxdepth 1 -name 'build-inputs-*' \( -name '*.zip' -o -name '*.sha256' -o -name '*.json' \) -mtime +2 -delete 2>/dev/null || true else TOKEN="" DEST="$(cd "$(dirname "$OUT")" && pwd)" @@ -135,14 +143,14 @@ if [ "$DEPLOY" = 1 ]; then LIVE="$(mktemp)" ok=0 for try in 1 2 3 4 5 6; do - code="$(curl -s -o "$LIVE" -w '%{http_code}' "https://dl.igneum.network/dl/$TOKEN/build-inputs.sha256")" - echo "https://dl.igneum.network/dl//build-inputs.sha256 -> HTTP $code (try $try)" + code="$(curl -s -o "$LIVE" -w '%{http_code}' "https://dl.igneum.network/dl/$TOKEN/$(basename "${OUT%.zip}").sha256")" + echo "https://dl.igneum.network/dl//$(basename "${OUT%.zip}").sha256 -> HTTP $code (try $try)" if [ "$code" = 200 ] && [ "$(tr -d '[:space:]' < "$LIVE")" = "$SUM" ]; then ok=1; break; fi sleep 10 done [ "$ok" = 1 ] || { echo "the live sha256 is not reachable or is not this zip's after 6 tries; check the deploy output" >&2; rm -f "$LIVE"; exit 1; } rm -f "$LIVE" - echo "live: build-inputs.zip verified by sha256" + echo "live: $(basename "$OUT") verified by sha256" elif [ -n "$TOKEN" ]; then echo "not deployed (--no-deploy): cd $DLSITE && npx --yes vercel@latest --global-config ~/.config/igneum/vercel deploy --prod --yes" else diff --git a/packaging/windows/push-inputs.sh b/packaging/windows/push-inputs.sh index 05f515dc..b652c011 100755 --- a/packaging/windows/push-inputs.sh +++ b/packaging/windows/push-inputs.sh @@ -69,7 +69,7 @@ if [ ! -x "$SIGNER" ]; then echo "building igneum-ota-sign" (cd "$ROOT/app/igneum-app" && nice -n 19 cargo build --release -j 4 --bin igneum-ota-sign --quiet) fi -EMBEDDED="$("$SIGNER" embedded | head -1)" +EMBEDDED="$("$SIGNER" embedded | sed -n 1p)" [ "$EMBEDDED" = "$(tr -d '[:space:]' < "$PUB")" ] || { echo "the public key in app/igneum-app/src/manifest.rs ($EMBEDDED) is not $PUB; the runner would refuse this signature" >&2; exit 1; } # the manifest: what is in the zip, from where, when. IGNEUM_NODE_SRC names the worktree the exes were built from diff --git a/tools/build-job.mjs b/tools/build-job.mjs index ebe71a67..b4ae402f 100755 --- a/tools/build-job.mjs +++ b/tools/build-job.mjs @@ -208,7 +208,11 @@ function placeFor(o) { // ---- publish: pack, then the signed job --------------------------------------------------------------------------------------- function publish() { - const pack = ['packaging/windows/push-build-inputs.sh']; + // One zip per job (5 October 2026 night): with the one shared build-inputs.zip, a job published while another agent's + // pack landed in the downloads folder pinned THAT agent's sources (three C4 jobs built tx-gossip and m20-live). The + // name carries the time and this process id; push-build-inputs.sh prunes names older than two days. + const zipName = `build-inputs-${new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14)}-${process.pid}.zip`; + const pack = ['packaging/windows/push-build-inputs.sh', '--name', zipName]; if (flags.node) pack.push('--node', flags.node); if (flags['node-tests']) pack.push('--node-tests', String(flags['node-tests'])); if (flags['app-tests']) pack.push('--app-tests', String(flags['app-tests'])); @@ -226,6 +230,9 @@ function publish() { if (flags.title) add.push('--title', String(flags.title)); if (flags.id) add.push('--id', String(flags.id)); if (!flags['no-deploy']) add.push('--deploy'); + const dlsite = (process.env.IGNEUM_DLSITE || cfg('dlsite-dir')).trim(), dlToken = cfg('dl-token'); + if (dlsite && dlToken) add.push('--zip', join(dlsite, 'dl', dlToken, zipName)); + else { console.error('no ~/.config/igneum/dlsite-dir or dl-token: cannot name the job\'s zip'); process.exit(1); } console.log(`$ ${add.join(' ')}`); const a = spawnSync('bash', add, { cwd: ROOT, encoding: 'utf8' }); process.stdout.write(a.stdout || ''); process.stderr.write(a.stderr || ''); diff --git a/tools/ci/signer-pipe-check.sh b/tools/ci/signer-pipe-check.sh new file mode 100755 index 00000000..849f0b87 --- /dev/null +++ b/tools/ci/signer-pipe-check.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Fails when a script pipes a Rust binary's output into `head` (5 October 2026 night, C4 fix round: `igneum-ota-sign +# embedded | head -1` under `set -o pipefail`; the signer prints two lines, `head` closes the pipe after the first, and +# on a loaded Mac the second print lands after the close: SIGPIPE, "failed printing to stdout: Broken pipe", exit 101, +# and publish-jobs.sh died before the job was published). A Rust binary panics on a closed stdout; `sed -n 1p` reads to +# the end. The class: the signer (`$SIGNER` or `igneum-ota-sign`, the one binary here whose output is longer than the +# line a script wants) piped into head; `igneumd --version | head -1` prints one line and is left alone. +set -euo pipefail +cd "$(dirname "$0")/../.." +bad=$(grep -rnE '(\$SIGNER"?|igneum-ota-sign)[^|]*\| *head( |$)' packaging tools infra --include='*.sh' --include='*.mjs' 2>/dev/null | grep -v 'tools/ci/signer-pipe-check.sh' || true) +if [ -n "$bad" ]; then + echo "a Rust binary piped into head (SIGPIPE panics the binary; use sed -n 1p):" >&2 + echo "$bad" >&2 + exit 1 +fi +echo "signer-pipe-check: ok" diff --git a/tools/finality-attacks/c4.mjs b/tools/finality-attacks/c4.mjs index 840b4b8d..1df0d1ec 100644 --- a/tools/finality-attacks/c4.mjs +++ b/tools/finality-attacks/c4.mjs @@ -4,7 +4,9 @@ // ports 29800+, network igneum-devnet-980, data under /tmp/igneum-fin-c4; the live devnet is never touched. // // node tools/finality-attacks/c4.mjs on; node tools/finality-attacks/c4.mjs off # one mode per process +// node tools/finality-attacks/c4.mjs v2 # module on under rule v2 (the live devnet's rule) // SPLIT=150 WARM=230 HEAL=200 node tools/finality-attacks/c4.mjs on +// IGNEUMD=... IGNEUM_MINER=... node tools/finality-attacks/c4.mjs on # another node build (the C4 fix, 5 October 2026 night) // // Topology (as v3.mjs): n1 listens; n0 dials n1 through proxy P0, n2 dials n1 through proxy P2; cutting P0 isolates // n0 (side A) from n1 and n2 (side B). @@ -19,6 +21,8 @@ // overlay requires every candidate tip to pass through B's certified checkpoint. The measurement is which chain // the three nodes converge to, whether they converge at all, and what each node had to reorganise. +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); const ROOT = new URL('../../', import.meta.url).pathname; const NODE_ROOT = process.env.IGNEUM_NODE_ROOT || '/Users/joshm/Projects/igneum/'; process.env.IGNEUM_FIN_BASE_PORT ||= '29800'; @@ -33,9 +37,25 @@ const WARM = +(process.env.WARM || 230), SPLIT = +(process.env.SPLIT || 150), HE const RA = +(process.env.RA || 0.6), RB = +(process.env.RB || 0.4); // ONE mode per process: lib/net.mjs reads IGNEUM_FIN_OVERRIDE_JSON when it is imported, so the override must be in // the environment before the import (the first draft set it inside network() and ran rule v2 twice; 5 October 2026). +// `v2` (5 October 2026, night): the module on under rule v2, the live devnet's rule (no frozen table, no fold): the +// override is the fast-time file alone, as the first draft's accidental control was; the expectation is the `on` one. const MODE = process.argv.slice(2).filter(a => !a.startsWith('--'))[0] || 'on'; -if (MODE !== 'on' && MODE !== 'off') { console.error(`mode must be on or off, got ${MODE}`); process.exit(2); } -process.env.IGNEUM_FIN_OVERRIDE_JSON = JSON.stringify(MODE === 'off' ? { finality: { min_daa: 9007199254740991 } } : { finality_v3_activation_daa: 0 }); +if (!['on', 'off', 'v2'].includes(MODE)) { console.error(`mode must be on, off or v2, got ${MODE}`); process.exit(2); } +// WINDOW= (5 October 2026, night): a longer weight window than the fast-time file's 120 (ban and min_daa follow it; +// WARM must exceed it). Under rule v2 a side locks alone once its own chain holds two thirds of its own sliding window, +// (2/3 W - s W) / (1 - s) of its own DAA after the cut: 63 DAA at W 120 and s 0.3, which a 90-s split at 0.6 blocks/s +// crosses before the heal (measured: n0 locked index 10 alone one second before B's certificate for 8 reached it). At +// W 240 the bound is 126 DAA (210 s), so a 130-s split stays inside it, as any partition under an hour does on the live +// devnet's 7,200-DAA window. +const WINDOW = +(process.env.WINDOW || 0); +const windowOverride = () => { + if (!WINDOW) return {}; + const { readFileSync } = require('node:fs'); + const f = JSON.parse(readFileSync(new URL('../../infra/fast-time/override-60x.json', import.meta.url), 'utf8')).finality; + return { finality: { ...f, weight_window: WINDOW, equivocation_ban: WINDOW, min_daa: WINDOW } }; +}; +process.env.IGNEUM_FIN_OVERRIDE_JSON = JSON.stringify(MODE === 'off' ? { finality: { min_daa: 9007199254740991 } } : MODE === 'v2' ? windowOverride() : { ...windowOverride(), finality_v3_activation_daa: 0 }); +const MODULE_ON = MODE !== 'off'; const { Node, Miner, Proxy, stopAll, sleep, log, assertBinaries, TMP, IGNEUMD } = await import('./lib/net.mjs'); const { mkdirSync, writeFileSync, appendFileSync } = await import('node:fs'); @@ -110,9 +130,16 @@ async function run(mode) { log(`${name}: end of split: A sink blue score ${bsA} (${dagsEnd[0]?.blockCount} blocks), B sink blue score ${bsB} (${dagsEnd[1]?.blockCount} blocks); B locked ${bLockedDuring.length} new index(es) ${bLockedDuring.map(([i]) => i).join(',')}; A locked ${newLocks[0]}`); p0.heal(); const tHeal = Date.now(); - let reconnected = null; + let reconnected = null, addPeerErr = null; + // The heal is the link, not the session: n0 dials the proxy again on the connection manager's backoff, which reached + // 84 to 114 s after a 130-s cut (5 October 2026 night, takes 1 to 3), and A kept mining alone meanwhile, so every run + // became the partition-longer-than-a-window shape. A real heal has the other side dialling too; here the harness asks + // n0 for the connection (addPeer, not permanent) every 3 s until a peer is up, and reports the time it took. while (Date.now() - tHeal < HEAL * 1000) { - if (reconnected == null && (await peers(n0)) > 0) reconnected = Math.round((Date.now() - tHeal) / 1000); + if (reconnected == null) { + if ((await peers(n0)) > 0) reconnected = Math.round((Date.now() - tHeal) / 1000); + else await n0.rpc.call('addPeer', { peerAddress: { ip: '127.0.0.1', port: p0.port }, isPermanent: false }).catch(e => { if (!addPeerErr) { addPeerErr = String(e?.message || e); log(`addPeer ${p0.addr} failed: ${addPeerErr}`); } }); + } await sleep(3000); } for (const m of miners) await m.stop(); @@ -136,10 +163,14 @@ async function run(mode) { await stopAll(); const heavier = bsA > bsB ? 'A' : 'B'; const ended = converged ? (onB[0] && !onA[0] ? 'B' : onA[0] && !onB[0] ? 'A' : onA[0] && onB[0] ? 'both merged' : 'neither') : 'not converged'; - const pass = mode === 'on' - ? (newLocks[0] === 0 && bLockedDuring.length > 0 && converged && ended === 'B' && conflicts.every(c => c === 0) && disagree === 0) + // module on (v3 or v2): B's certified chain must win although A's is heavier, with no conflict and no disagreeing + // lock; under v2 A may lock alone during the split once the sliding table is its own (F21's bound), so A's split-time + // locks are reported, not required to be zero + const adopted = [n0, n1, n2].map(n => n.grepLog(/LOCKED by certificate/).length); + const pass = MODULE_ON + ? ((mode === 'v2' || newLocks[0] === 0) && bLockedDuring.length > 0 && converged && ended === 'B' && conflicts.every(c => c === 0) && disagree === 0) : (newLocks.every(x => x === 0) && converged && ended === heavier); - out(`\n### ${name}: warm ${WARM} s at 1 block/s (B 70% of weight, A 30%), split ${SPLIT} s with A at ${RA} and B at ${RB} blocks/s, heal window ${HEAL} s, link delay ${DELAY_MS} ms, module ${mode} (${mode === 'on' ? 'rule v3 from checkpoint DAA 0' : 'min_daa never: no certificate can form'}), node ${IGNEUMD.split('/').slice(-3).join('/')}\n`); + out(`\n### ${name}: warm ${WARM} s at 1 block/s (B 70% of weight, A 30%), split ${SPLIT} s with A at ${RA} and B at ${RB} blocks/s, heal window ${HEAL} s, link delay ${DELAY_MS} ms${WINDOW ? `, weight window ${WINDOW} DAA` : ''}, module ${mode} (${mode === 'on' ? 'rule v3 from checkpoint DAA 0' : mode === 'v2' ? 'rule v2, the live devnet rule' : 'min_daa never: no certificate can form'}), node ${IGNEUMD.split('/').slice(-3).join('/')}\n`); out('| measure | n0 (side A, work majority) | n1 (side B, weight majority) | n2 (side B) |'); out('|---|---|---|---|'); out(`| max locked index at the cut | ${beforeMax.join(' | ')} |`); @@ -149,10 +180,11 @@ async function run(mode) { out(`| sink at the end of the heal window | ${sinks.map(s => String(s).slice(0, 10)).join(' | ')} |`); out(`| A's split tip on the final chain / B's split tip on the final chain | ${onA.map((a, i) => `${a} / ${onB[i]}`).join(' | ')} |`); out(`| conflicting certificates logged | ${conflicts.join(' | ')} |`); + out(`| locks adopted from a certificate off the node's chain (the C4 fix) | ${adopted.join(' | ')} |`); out(`| re-determined lines (F24) | ${redetermined.join(' | ')} |`); out(`| reorg lines in the node log | ${reorgs.join(' | ')} |`); - out(`\nAt the end of the split: A's sink blue score ${bsA} against B's ${bsB} (the heavier chain by blue work is ${heavier}'s); B locked ${bLockedDuring.length} new checkpoint(s) during the split${bLockedDuring.length ? ' at index ' + bLockedDuring.map(([i]) => i).join(', ') : ''}. After the heal: n0 reconnected ${reconnected == null ? 'not within the heal window' : reconnected + ' s after the gate reopened'}; the three sinks ${converged ? 'agree' : 'DISAGREE'}; the network ended on ${ended}'s chain; A adopted B's split-time locks: ${bAdoptedByA}; locked indices disagreeing across the three nodes: ${disagree}. ${pass ? 'PASS' : 'FAIL'} against the expectation for module ${mode} (${mode === 'on' ? "B's certified chain wins although A's is heavier" : 'the heavier chain wins'}).`); - results.push({ name, pass, heavier, ended, converged, bsA, bsB, newLocks, bLocked: bLockedDuring.length, conflicts, disagree }); + out(`\nAt the end of the split: A's sink blue score ${bsA} against B's ${bsB} (the heavier chain by blue work is ${heavier}'s); B locked ${bLockedDuring.length} new checkpoint(s) during the split${bLockedDuring.length ? ' at index ' + bLockedDuring.map(([i]) => i).join(', ') : ''}. After the heal: n0 reconnected ${reconnected == null ? 'not within the heal window' : reconnected + ' s after the gate reopened'}; the three sinks ${converged ? 'agree' : 'DISAGREE'}; the network ended on ${ended}'s chain; A adopted B's split-time locks: ${bAdoptedByA}; locked indices disagreeing across the three nodes: ${disagree}. ${pass ? 'PASS' : 'FAIL'} against the expectation for module ${mode} (${MODULE_ON ? "B's certified chain wins although A's is heavier" : 'the heavier chain wins'}).`); + results.push({ name, pass, heavier, ended, converged, bsA, bsB, newLocks, bLocked: bLockedDuring.length, conflicts, disagree, adopted, reconnected }); } async function main() { diff --git a/tools/finality-attacks/lib/net.mjs b/tools/finality-attacks/lib/net.mjs index ac1ddc1f..88809798 100644 --- a/tools/finality-attacks/lib/net.mjs +++ b/tools/finality-attacks/lib/net.mjs @@ -72,6 +72,9 @@ export class Node { args() { const a = ['--devnet', `--devnet-suffix=${DEVNET_SUFFIX}`, '--nodnsseed', '--disable-upnp', '--nologfiles', '--enable-unsynced-mining', '--utxoindex', `--appdir=${this.dir}`, + // unsafe RPC so a scenario can ask a node to dial a peer again at a heal (addPeer; c4.mjs, 5 October 2026 night); + // every harness node listens on 127.0.0.1 only + '--unsaferpc', `--rpclisten=127.0.0.1:${this.grpcPort}`, `--rpclisten-json=127.0.0.1:${this.jsonPort}`, `--listen=127.0.0.1:${this.p2pPort}`, `--override-params-file=${overrideParams(this.override || {}, this.override ? this.name : '')}`, '--loglevel=info', '--yes']; if (this.connect.length) a.push(`--connect=${this.connect.join(',')}`); else a.push('--outpeers=0'); From bd21f2a68015350bbf577723182e7593a767ff47 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:20:44 +0000 Subject: [PATCH 007/158] app: one card row per physical GPU across OpenCL platforms, Windows names on the rows, index-free card keys; the OpenCL worker prints the PCI address PC 1 after Adrenalin 26.9.2 (5 October 2026): two AMD ICDs each listed the iGPU and the RX 9070 XT, so the 0.3.9 app showed five rows for three GPUs (gfx1036, gfx1201, gfx1036, gfx1201 plus the 5090), ran two workers on the one 9070 XT at 9 MH/s each, called the iGPU discrete, and re-enabled it when its key moved from amd:1:gfx1036 to amd:0:gfx1036. detect.rs: the Windows path is a pure assemble(Inputs) over nvidia-smi, the worker's --list and the adapter list. dedupe_platforms keeps one entry per card per vendor: the fuller platform wins (most GPUs, then the newest driver, then the first listed) and another platform's device survives only at a PCI address the winner lacks (without addresses: the same code and ordinal); the dropped entries go to the log, never to a worker. resolve_name puts the Windows adapter name on the row: by PCI bus (the adapter list now carries DEVPKEY_Device_BusNumber and Address), else by the gfx code's PCI device ids, else by the card names in a gfx table (gfx1201, 1200, 1100, 1101, 1102, 1030, 1031, 1032, 1036, 1035, 1103, 1150, 90c, approximate), else the table's words, else the code. The code stays in `code`, the key and the row's tooltip; the key is vendor:code with "#2" for a twin, no index. hotplug::pref_for reads a 0.3.9 key (vendor:index:code) when exactly one matches; the diff matches by key and PCI address, then by vendor and name or code. The iGPU is integrated and off by default; the choice survives a moved index. proto-opencl/host.c: --list prints ", pci bb:dd.f" on the info line from CL_DEVICE_TOPOLOGY_AMD or the NVIDIA bus and slot ids (needs a worker rebuild through proto-cuda/nvrtc/build-windows.sh and push-inputs; an old worker still works, by code and ordinal). Tests: detect::tests has the five-row PC 1 list as a fixture (two platforms, the three adapters with DEV_13C0, DEV_2B85 and DEV_7550) and asserts three rows named NVIDIA GeForce RTX 5090, AMD Radeon(TM) Graphics (integrated, off) and AMD Radeon RX 9070 XT (discrete, on, 8 identities), the keys, the console line, and the diff against the one-platform list before the eGPU (iGPU moved, 9070 XT added, nothing removed); plus the list parser with and without pci, dedupe with and without addresses, name resolution, twins' keys. cargo test -p igneum-app: 96 passed. Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/src/detect.rs | 499 ++++++++++++++++++++++++++++++---- app/igneum-app/src/engine.rs | 12 +- app/igneum-app/src/hotplug.rs | 94 ++++--- app/igneum-app/src/state.rs | 4 +- app/igneum-app/ui/app.js | 6 +- proto-opencl/host.c | 29 +- 6 files changed, 547 insertions(+), 97 deletions(-) diff --git a/app/igneum-app/src/detect.rs b/app/igneum-app/src/detect.rs index adbc5232..cb7c1f62 100644 --- a/app/igneum-app/src/detect.rs +++ b/app/igneum-app/src/detect.rs @@ -27,6 +27,8 @@ pub struct Bins { pub struct Detection { pub cards: Vec, pub notes: Vec, + /// duplicate OpenCL platform entries left out (one line each, for the log) + pub dropped: Vec, pub nvidia_listed: bool, pub opencl_listed: bool, pub adapters_listed: bool, @@ -88,7 +90,8 @@ pub fn run_timeout(cmd: &mut Command, stdin_text: Option<&str>, limit: Duration) fn card(index: usize, name: &str, vendor: &str, worker: &str, detail: &str, device: &str) -> CardState { CardState { index, - key: format!("{vendor}:{device}:{name}"), + key: format!("{vendor}:{name}"), + code: name.to_string(), name: name.to_string(), vendor: vendor.into(), worker: worker.into(), @@ -129,9 +132,16 @@ pub struct Adapter { pub ram_mb: u64, pub processor: String, pub pnp_id: String, + /// "01:00.0" from DEVPKEY_Device_BusNumber and DEVPKEY_Device_Address; empty when PowerShell could not read them + pub bus: String, } impl Adapter { + /// The PCI device id from the PnP id ("PCI\\VEN_1002&DEV_7550&..." gives 0x7550); 0 when there is none. + pub fn device_id(&self) -> u16 { + let up = self.pnp_id.to_ascii_uppercase(); + up.find("DEV_").and_then(|i| u16::from_str_radix(up.get(i + 4..i + 8)?, 16).ok()).unwrap_or(0) + } /// "Code 43" for a problem code, "status Error" for a bad status without one, None when the device is fine. pub fn problem(&self) -> Option { if self.code != 0 { @@ -189,7 +199,14 @@ pub fn parse_adapters(json: &str) -> Vec { let s = |r: &serde_json::Value, k: &str| r.get(k).and_then(|x| x.as_str()).unwrap_or("").trim().to_string(); let n = |r: &serde_json::Value, k: &str| r.get(k).and_then(|x| x.as_u64().or_else(|| x.as_str().and_then(|t| t.trim().parse::().ok()))).unwrap_or(0); rows.iter() - .map(|r| Adapter { name: s(r, "Name"), status: s(r, "Status"), code: n(r, "ConfigManagerErrorCode") as u32, ram_mb: n(r, "AdapterRAM") / (1024 * 1024), processor: s(r, "VideoProcessor"), pnp_id: s(r, "PNPDeviceID") }) + .map(|r| { + // DEVPKEY_Device_Address on PCI is (device << 16) | function + let bus = match (r.get("BusNumber").and_then(|x| x.as_u64()), r.get("Address").and_then(|x| x.as_u64())) { + (Some(b), Some(a)) => format!("{:02x}:{:02x}.{:x}", b & 0xff, (a >> 16) & 0xff, a & 0xffff), + _ => String::new(), + }; + Adapter { name: s(r, "Name"), status: s(r, "Status"), code: n(r, "ConfigManagerErrorCode") as u32, ram_mb: n(r, "AdapterRAM") / (1024 * 1024), processor: s(r, "VideoProcessor"), pnp_id: s(r, "PNPDeviceID"), bus } + }) .filter(|a| !a.name.is_empty()) .collect() } @@ -197,7 +214,9 @@ pub fn parse_adapters(json: &str) -> Vec { /// Windows' adapter list through PowerShell (about a second); None when PowerShell did not answer. #[cfg(windows)] pub fn adapters() -> Option> { - let script = "Get-CimInstance Win32_VideoController | Select-Object Name,Status,ConfigManagerErrorCode,AdapterRAM,VideoProcessor,PNPDeviceID | ConvertTo-Json -Compress"; + // one object per adapter, with the PCI bus number and address from the PnP properties (they name the card + // the OpenCL worker's "pci" field names); @() keeps a single adapter an array + let script = "$v = Get-CimInstance Win32_VideoController | ForEach-Object { $id = $_.PNPDeviceID; $bus = $null; $addr = $null; try { foreach ($x in (Get-PnpDeviceProperty -InstanceId $id -KeyName 'DEVPKEY_Device_BusNumber','DEVPKEY_Device_Address' -ErrorAction Stop)) { if ($x.KeyName -eq 'DEVPKEY_Device_BusNumber') { $bus = $x.Data } elseif ($x.KeyName -eq 'DEVPKEY_Device_Address') { $addr = $x.Data } } } catch {}; [pscustomobject]@{ Name = $_.Name; Status = $_.Status; ConfigManagerErrorCode = $_.ConfigManagerErrorCode; AdapterRAM = $_.AdapterRAM; VideoProcessor = $_.VideoProcessor; PNPDeviceID = $id; BusNumber = $bus; Address = $addr } }; ConvertTo-Json -InputObject @($v) -Compress"; let out = run_timeout(Command::new(crate::platform::tool("powershell")).args(["-NoProfile", "-Command", script]), None, Duration::from_secs(15))?; let start = out.find(|c| c == '[' || c == '{')?; Some(parse_adapters(&out[start..])) @@ -334,20 +353,236 @@ pub fn detect(bins: &Bins) -> Detection { apply_defaults(&mut c); mark_sweep_support(&mut c); d.cards.push(c); + assign_keys(&mut d.cards); d } -#[cfg(not(target_os = "macos"))] -pub fn detect(bins: &Bins) -> Detection { +/// AMD gfx codes the OpenCL runtime reports as the device name, with the card names Windows uses, the words for +/// the row when no adapter matches, and the PCI device ids (approximate, from AMD's public ROCm and Linux driver +/// tables; add a line when a card is seen). gfx1036 is the Ryzen desktop iGPU (PC 1: DEV_13C0, 5 October 2026). +const GFX: &[(&str, &str, &[&str], &[u16])] = &[ + ("gfx1201", "Radeon RX 9070 XT / 9070", &["Radeon RX 9070 XT", "Radeon RX 9070"], &[0x7550]), + ("gfx1200", "Radeon RX 9060 XT", &["Radeon RX 9060 XT", "Radeon RX 9060"], &[0x7590]), + ("gfx1100", "Radeon RX 7900 XTX / XT", &["Radeon RX 7900 XTX", "Radeon RX 7900 XT", "Radeon RX 7900 GRE"], &[0x744C]), + ("gfx1101", "Radeon RX 7800 XT / 7700 XT", &["Radeon RX 7800 XT", "Radeon RX 7700 XT"], &[0x747E]), + ("gfx1102", "Radeon RX 7600", &["Radeon RX 7600 XT", "Radeon RX 7600"], &[0x7480]), + ("gfx1030", "Radeon RX 6800 / 6900", &["Radeon RX 6900 XT", "Radeon RX 6950 XT", "Radeon RX 6800 XT", "Radeon RX 6800"], &[0x73BF]), + ("gfx1031", "Radeon RX 6700 XT", &["Radeon RX 6750 XT", "Radeon RX 6700 XT", "Radeon RX 6700"], &[0x73DF]), + ("gfx1032", "Radeon RX 6600", &["Radeon RX 6650 XT", "Radeon RX 6600 XT", "Radeon RX 6600"], &[0x73FF]), + ("gfx1036", "Ryzen integrated Radeon Graphics", &["Radeon(TM) Graphics", "Radeon Graphics"], &[0x164E, 0x13C0]), + ("gfx1035", "Radeon 680M (integrated)", &["Radeon 680M", "Radeon 660M"], &[0x1681]), + ("gfx1103", "Radeon 780M (integrated)", &["Radeon 780M", "Radeon 760M"], &[0x15BF, 0x15C8]), + ("gfx1150", "Radeon 890M (integrated)", &["Radeon 890M", "Radeon 880M"], &[0x150E]), + ("gfx90c", "Radeon Graphics (Renoir / Cezanne, integrated)", &["Radeon(TM) Graphics", "Radeon Graphics"], &[0x1636, 0x1638]), +]; + +fn gfx_entry(code: &str) -> Option<&'static (&'static str, &'static str, &'static [&'static str], &'static [u16])> { + let c = code.trim().to_ascii_lowercase(); + let c = c.split(|ch: char| ch == ':' || ch == ' ').next().unwrap_or(""); + GFX.iter().find(|e| e.0 == c) +} + +/// The name on the row for a device the tool knows by `code`: Windows' adapter name when one matches (by PCI bus, +/// else by the gfx code's device ids, else by the card names in the table), else the table's words, else the code. +/// `used` holds the adapters already given to another card, so two gfx1036 entries never share one. +pub fn resolve_name(code: &str, vendor: &str, bus: &str, adapters: &[Adapter], used: &mut Vec) -> (String, Option) { + let free = |i: &usize| !used.contains(i); + let fine = |a: &Adapter| a.problem().is_none(); + let vendor_ok = |a: &Adapter| vendor == "other" || vendor_of(&a.name) == vendor; + if !bus.is_empty() { + if let Some(i) = (0..adapters.len()).filter(free).find(|&i| adapters[i].bus == bus && vendor_ok(&adapters[i])) { + used.push(i); + return (adapters[i].name.clone(), Some(i)); + } + } + // the name is already a marketing name (nvidia-smi, Windows): the adapter with the same name + let same: Vec = (0..adapters.len()).filter(free).filter(|&i| adapters[i].name.trim().eq_ignore_ascii_case(code.trim())).collect(); + if same.len() == 1 { + used.push(same[0]); + return (adapters[same[0]].name.clone(), Some(same[0])); + } + let Some(entry) = gfx_entry(code) else { return (code.to_string(), None) }; + let by_id: Vec = (0..adapters.len()).filter(free).filter(|&i| fine(&adapters[i]) && entry.3.contains(&adapters[i].device_id())).collect(); + if by_id.len() == 1 { + used.push(by_id[0]); + return (adapters[by_id[0]].name.clone(), Some(by_id[0])); + } + let by_name: Vec = (0..adapters.len()).filter(free).filter(|&i| fine(&adapters[i]) && vendor_ok(&adapters[i]) && { let n = adapters[i].name.to_ascii_lowercase(); entry.2.iter().any(|m| n.contains(&m.to_ascii_lowercase())) }).collect(); + if by_name.len() == 1 { + used.push(by_name[0]); + return (adapters[by_name[0]].name.clone(), Some(by_name[0])); + } + (entry.1.to_string(), None) +} + +/// One device line pair of the OpenCL worker's --list. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct ClDevice { + pub index: String, + pub name: String, + pub platform: String, + pub platform_version: String, + pub is_gpu: bool, + pub vendor: String, + pub driver: String, + pub units: String, + /// "01:00.0" when the worker printed `pci` (workers from 5 October 2026 on), else empty + pub bus: String, + pub mem_mb: u64, +} + +impl ClDevice { + pub fn vendor_word(&self) -> &'static str { + if self.vendor.contains("NVIDIA") || self.name.contains("NVIDIA") { + "nvidia" + } else if self.vendor.contains("Advanced Micro") || self.vendor.contains("AMD") || self.name.contains("Radeon") || self.name.contains("AMD") || self.name.to_ascii_lowercase().starts_with("gfx") { + "amd" + } else { + "other" + } + } + fn platform_key(&self) -> String { + format!("{} ({}) driver {}", self.platform, self.platform_version, self.driver) + } +} + +/// Parses `igneum-worker-opencl --list`: `[idx] name | platform (version)` then `GPU, vendor V, driver D, OpenCL C +/// x.y, N compute units, M MHz[, pci bb:dd.f]` then `global N MiB, ...`. The bool says the worker printed its header. +pub fn parse_opencl_list(text: &str) -> (Vec, bool) { + let lines: Vec<&str> = text.lines().collect(); + let listed = lines.iter().any(|l| l.starts_with("OpenCL devices")); + let mut out = Vec::new(); + for (i, line) in lines.iter().enumerate() { + let t = line.trim_start_matches(|c| c == ' ' || c == '*').trim(); + if !t.starts_with('[') { + continue; + } + let Some(close) = t.find(']') else { continue }; + let rest = &t[close + 1..]; + let mut halves = rest.splitn(2, " |"); + let name = halves.next().unwrap_or("").trim().to_string(); + let plat = halves.next().unwrap_or("").trim(); + // the first " (" opens the version: AMD's version string carries brackets of its own, "OpenCL 2.1 AMD-APP (3617.0)" + let (platform, platform_version) = match plat.find(" (") { + Some(p) if plat.ends_with(')') => (plat[..p].to_string(), plat[p + 2..plat.len() - 1].to_string()), + _ => (plat.to_string(), String::new()), + }; + let info = lines.get(i + 1).map(|l| l.trim()).unwrap_or(""); + let parts: Vec<&str> = info.split(", ").collect(); + let mem_mb = lines.get(i + 2).map(|l| l.trim()).and_then(|l| l.strip_prefix("global ")).and_then(|l| l.split_whitespace().next()).and_then(|n| n.parse::().ok()).unwrap_or(0); + out.push(ClDevice { + index: t[1..close].to_string(), + name, + platform, + platform_version, + is_gpu: info.starts_with("GPU"), + vendor: parts.iter().find_map(|p| p.strip_prefix("vendor ")).unwrap_or("").trim().to_string(), + driver: parts.iter().find_map(|p| p.strip_prefix("driver ")).unwrap_or("").trim().to_string(), + units: parts.iter().find(|p| p.contains("compute units")).unwrap_or(&"").to_string(), + bus: parts.iter().find_map(|p| p.strip_prefix("pci ")).unwrap_or("").trim().to_string(), + mem_mb, + }); + } + (out, listed) +} + +fn version_tuple(s: &str) -> Vec { + s.split(|c: char| !c.is_ascii_digit()).filter(|p| !p.is_empty()).map(|p| p.parse::().unwrap_or(0)).collect() +} + +/// One entry per physical card across OpenCL platforms. Two AMD ICDs after a driver upgrade each list every AMD +/// card (PC 1, 5 October 2026: gfx1036 and gfx1201 twice, two workers on one 9070 XT). Per vendor, the fuller +/// platform wins (most GPUs, then the newest driver, then the first listed); a device on another platform is kept +/// only when the winner has no device at the same PCI address (or, without addresses, the same code and ordinal). +/// Returns the kept devices and one note per dropped duplicate. +pub fn dedupe_platforms(devs: Vec) -> (Vec, Vec) { + let gpus: Vec = devs.into_iter().filter(|d| d.is_gpu).collect(); + let mut kept: Vec = Vec::new(); + let mut dropped = Vec::new(); + let mut vendors: Vec<&'static str> = Vec::new(); + for d in &gpus { + let v = d.vendor_word(); + if !vendors.contains(&v) { + vendors.push(v); + } + } + for v in vendors { + let mine: Vec<&ClDevice> = gpus.iter().filter(|d| d.vendor_word() == v).collect(); + let mut plats: Vec = Vec::new(); + for d in &mine { + let k = d.platform_key(); + if !plats.contains(&k) { + plats.push(k); + } + } + let score = |k: &String| { + let count = mine.iter().filter(|d| &d.platform_key() == k).count(); + let driver = mine.iter().find(|d| &d.platform_key() == k).map(|d| version_tuple(&d.driver)).unwrap_or_default(); + (count, driver) + }; + let winner = plats.iter().max_by(|a, b| score(a).cmp(&score(b))).cloned().unwrap_or_default(); + let identity = |d: &ClDevice, ordinal: usize| if d.bus.is_empty() { format!("{}#{ordinal}", d.name.to_ascii_lowercase()) } else { d.bus.clone() }; + let mut have: Vec = Vec::new(); + let mut seen_codes: std::collections::HashMap = std::collections::HashMap::new(); + let mut ordinal = |d: &ClDevice| { + let n = seen_codes.entry(format!("{}|{}", d.platform_key(), d.name.to_ascii_lowercase())).or_insert(0); + *n += 1; + *n + }; + for d in mine.iter().filter(|d| d.platform_key() == winner) { + let o = ordinal(d); + have.push(identity(d, o)); + kept.push((*d).clone()); + } + for d in mine.iter().filter(|d| d.platform_key() != winner) { + let o = ordinal(d); + let id = identity(d, o); + if have.contains(&id) { + dropped.push(format!("[{}] {} on {} ({}) is the same card as the one on {}: no worker", d.index, d.name, d.platform, d.platform_version, winner)); + } else { + have.push(id); + kept.push((*d).clone()); + } + } + } + kept.sort_by_key(|d| d.index.parse::().unwrap_or(u64::MAX)); + (kept, dropped) +} + +/// Keys without an index (it moves when a card arrives): vendor:code, "#2" and up for identical cards in list order. +pub fn assign_keys(cards: &mut [CardState]) { + let mut seen: std::collections::HashMap = std::collections::HashMap::new(); + for c in cards.iter_mut() { + if c.code.is_empty() { + c.code = c.name.clone(); + } + let base = format!("{}:{}", c.vendor, c.code); + let n = seen.entry(base.clone()).or_insert(0); + *n += 1; + c.key = if *n == 1 { base } else { format!("{base}#{n}") }; + } +} + +/// What one Windows enumeration gathered; `assemble` turns it into the list (pure, so the PC 1 cases are tests). +#[derive(Default)] +pub struct Inputs { + /// `nvidia-smi --query-gpu=index,name,memory.total,pci.bus_id --format=csv,noheader`; None = nvidia-smi did not run + pub nvidia: Option, + pub nvidia_limits: std::collections::HashMap, + pub cuda_worker: bool, + /// the OpenCL worker's --list; None = no worker installed or it did not answer + pub opencl: Option, + pub opencl_installed: bool, + /// Windows' adapter list; None = PowerShell did not answer + pub adapters: Option>, +} + +pub fn assemble(inp: Inputs) -> Detection { let mut d = Detection::default(); - // Windows' own view of every adapter: status and problem code (a Code 43 card is in no tool's list), memory and - // processor for the integrated call, names for the last resort - let adapters = adapters(); - d.adapters_listed = adapters.is_some(); - let adapters = adapters.unwrap_or_default(); - // NVIDIA: nvidia-smi ships with the driver - let smi = run_timeout(Command::new(crate::platform::tool("nvidia-smi")).args(["--query-gpu=index,name,memory.total,pci.bus_id", "--format=csv,noheader"]), None, Duration::from_secs(10)); - match smi { + d.adapters_listed = inp.adapters.is_some(); + let adapters = inp.adapters.unwrap_or_default(); + let mut used: Vec = Vec::new(); + match inp.nvidia { Some(out) => { d.nvidia_listed = true; for line in out.lines() { @@ -355,13 +590,16 @@ pub fn detect(bins: &Bins) -> Detection { if parts.len() >= 2 && parts[0].chars().all(|c| c.is_ascii_digit()) && !parts[0].is_empty() { let mem_mb: u64 = parts.get(2).and_then(|m| m.split_whitespace().next()).and_then(|n| n.parse::().ok()).map(|v| v as u64).unwrap_or(0); let detail = if mem_mb > 0 { format!("{} GB", (mem_mb + 512) / 1024) } else { String::new() }; - let worker_ok = bins.cuda.is_some(); - let mut c = card(d.cards.len(), parts[1], "nvidia", "CUDA", &detail, parts[0]); - c.bus = parts.get(3).map(|b| b.to_string()).unwrap_or_default(); - c.kind = classify_kind(parts[1], adapter_for(parts[1], &adapters)).into(); + // nvidia-smi prints 00000000:01:00.0; the worker and Windows say 01:00.0 + let bus = parts.get(3).map(|b| b.trim().to_ascii_lowercase()).map(|b| b.rsplit_once(':').map(|(d, r)| format!("{}:{r}", d.rsplit(':').next().unwrap_or(d))).unwrap_or(b)).unwrap_or_default(); + let (name, adapter) = resolve_name(parts[1], "nvidia", &bus, &adapters, &mut used); + let mut c = card(d.cards.len(), &name, "nvidia", "CUDA", &detail, parts[0]); + c.code = parts[1].to_string(); + c.bus = bus; + c.kind = classify_kind(parts[1], adapter.map(|i| &adapters[i])).into(); c.vram_mb = mem_mb; - c.path = if worker_ok { "prebuilt".into() } else { "build".into() }; - if !worker_ok { + c.path = if inp.cuda_worker { "prebuilt".into() } else { "build".into() }; + if !inp.cuda_worker { c.message = "no prebuilt CUDA worker in the package; built from source on first run (needs the CUDA Toolkit and Visual Studio)".into(); } apply_defaults(&mut c); @@ -371,9 +609,8 @@ pub fn detect(bins: &Bins) -> Detection { if d.cards.is_empty() { d.notes.push("nvidia-smi ran but listed no card".into()); } - let limits = nvidia_power_limits(); for c in d.cards.iter_mut() { - if let Some((dflt, cur, lo, hi)) = limits.get(&c.device) { + if let Some((dflt, cur, lo, hi)) = inp.nvidia_limits.get(&c.device) { c.power_default_w = *dflt; c.power_limit_w = *cur; c.power_before_w = *cur; @@ -385,68 +622,71 @@ pub fn detect(bins: &Bins) -> Detection { } None => d.notes.push("nvidia-smi is not on this PC (no NVIDIA driver): no NVIDIA card".into()), } - // OpenCL: the worker's own device list (AMD, Intel; NVIDIA shows there too and is skipped) - if let Some(cl) = bins.opencl.as_ref() { - if let Some(out) = run_timeout(Command::new(cl).arg("--list"), None, Duration::from_secs(15)) { - let lines: Vec<&str> = out.lines().collect(); - d.opencl_listed = lines.iter().any(|l| l.starts_with("OpenCL devices")); - for (i, line) in lines.iter().enumerate() { - let t = line.trim_start_matches(|c| c == ' ' || c == '*').trim(); - if !t.starts_with('[') { - continue; - } - let Some(close) = t.find(']') else { continue }; - let idx = &t[1..close]; - let rest = &t[close + 1..]; - let name = rest.split(" |").next().unwrap_or("").trim(); - let info = lines.get(i + 1).map(|l| l.trim()).unwrap_or(""); - let is_gpu = info.starts_with("GPU"); - let vendor_s = info.split("vendor ").nth(1).unwrap_or("").split(", driver").next().unwrap_or("").trim(); - if !is_gpu || name.contains("NVIDIA") || vendor_s.contains("NVIDIA") { - continue; - } - let vendor = if vendor_s.contains("Advanced Micro") || name.contains("Radeon") || name.contains("AMD") || name.starts_with("gfx") { "amd" } else { "other" }; - let units = info.split(", ").find(|p| p.contains("compute units")).unwrap_or("").to_string(); - let mut c = card(d.cards.len(), name, vendor, "OpenCL", &units, idx); - c.device = idx.to_string(); - c.kind = classify_kind(name, adapter_for(name, &adapters)).into(); - c.path = "prebuilt".into(); - apply_defaults(&mut c); - mark_sweep_support(&mut c); - d.cards.push(c); - } + if let Some(out) = inp.opencl { + let (devs, listed) = parse_opencl_list(&out); + d.opencl_listed = listed; + let (kept, dropped) = dedupe_platforms(devs.into_iter().filter(|dv| dv.vendor_word() != "nvidia").collect()); + d.dropped = dropped; + for dv in kept { + let vendor = dv.vendor_word(); + let (name, adapter) = resolve_name(&dv.name, vendor, &dv.bus, &adapters, &mut used); + let mut c = card(d.cards.len(), &name, vendor, "OpenCL", &dv.units, &dv.index); + c.code = dv.name.clone(); + c.bus = dv.bus.clone(); + c.platform = format!("{} ({}), driver {}", dv.platform, dv.platform_version, dv.driver); + c.kind = classify_kind(&dv.name, adapter.map(|i| &adapters[i])).into(); + c.vram_mb = if c.kind == "integrated" { 0 } else { dv.mem_mb }; + c.path = "prebuilt".into(); + apply_defaults(&mut c); + mark_sweep_support(&mut c); + d.cards.push(c); } - } else if d.cards.is_empty() { + } else if !inp.opencl_installed && d.cards.is_empty() { d.notes.push("the OpenCL worker is not installed; AMD and Intel cards cannot be listed".into()); } if d.cards.is_empty() { // last resort: the names Windows knows, so the screen can at least say what is in the PC - for a in adapters.iter().filter(|a| a.problem().is_none()) { + for (i, a) in adapters.iter().enumerate().filter(|(_, a)| a.problem().is_none()) { let vendor = vendor_of(&a.name); let mut c = card(d.cards.len(), &a.name, vendor, if vendor == "nvidia" { "CUDA" } else { "OpenCL" }, "", ""); + c.bus = a.bus.clone(); c.kind = if looks_integrated(&a.name) { "integrated".into() } else { "unknown".into() }; c.enabled = false; c.reason = "seen by Windows, but no worker can drive it (no NVIDIA driver and no OpenCL worker)".into(); + used.push(i); d.cards.push(c); } } // the cards Windows lists with a problem (Code 43 after an eGPU hot-plug on PC 1, 5 October 2026): shown, never driven - for a in adapters.iter() { + for (i, a) in adapters.iter().enumerate() { let Some(problem) = a.problem() else { continue }; - if d.cards.iter().any(|c| c.name.trim().eq_ignore_ascii_case(a.name.trim())) { + if used.contains(&i) || d.cards.iter().any(|c| c.name.trim().eq_ignore_ascii_case(a.name.trim())) { continue; } let vendor = vendor_of(&a.name); let mut c = card(d.cards.len(), &a.name, vendor, if vendor == "nvidia" { "CUDA" } else { "OpenCL" }, "", ""); - c.bus = a.pnp_id.clone(); + c.bus = a.bus.clone(); c.kind = classify_kind(&a.name, Some(a)).into(); - c.vram_mb = a.ram_mb; mark_unusable(&mut c, &problem); d.cards.push(c); } + assign_keys(&mut d.cards); d } +#[cfg(not(target_os = "macos"))] +pub fn detect(bins: &Bins) -> Detection { + // Windows' own view of every adapter: status and problem code (a Code 43 card is in no tool's list), memory and + // processor for the integrated call, the PCI address and the names for the rows + let adapters = adapters(); + // NVIDIA: nvidia-smi ships with the driver + let nvidia = run_timeout(Command::new(crate::platform::tool("nvidia-smi")).args(["--query-gpu=index,name,memory.total,pci.bus_id", "--format=csv,noheader"]), None, Duration::from_secs(10)); + let nvidia_limits = if nvidia.is_some() { nvidia_power_limits() } else { Default::default() }; + // OpenCL: the worker's own device list (AMD, Intel; NVIDIA shows there too and is skipped) + let opencl = bins.opencl.as_ref().and_then(|cl| run_timeout(Command::new(cl).arg("--list"), None, Duration::from_secs(15))); + assemble(Inputs { nvidia, nvidia_limits, cuda_worker: bins.cuda.is_some(), opencl, opencl_installed: bins.opencl.is_some(), adapters }) +} + /// A listed card no worker can drive: off, no switch, the problem on the row and the hint under it. pub fn mark_unusable(c: &mut CardState, problem: &str) { c.problem = problem.to_string(); @@ -571,6 +811,151 @@ mod tests { assert_eq!(vendor_of("NVIDIA GeForce RTX 5090"), "nvidia"); } + // PC 1 after Adrenalin 26.9.2 and a reboot (5 October 2026, evening): all three adapters OK, the Windows names, + // DEV ids and PCI addresses as the coordinator read them (the 5090's bus 01:00.0; the two AMD cards' addresses are + // not in that reading, so this fixture leaves them empty, as an old worker's --list would) + fn pc1_rebooted() -> Vec { + parse_adapters(r#"[{"Name":"AMD Radeon(TM) Graphics","Status":"OK","ConfigManagerErrorCode":0,"AdapterRAM":2147483648,"VideoProcessor":"AMD Radeon Graphics Processor (0x13C0)","PNPDeviceID":"PCI\\VEN_1002&DEV_13C0&SUBSYS_00000000&REV_C1\\4&2E5A1B3&0&0041","BusNumber":null,"Address":null}, + {"Name":"NVIDIA GeForce RTX 5090","Status":"OK","ConfigManagerErrorCode":0,"AdapterRAM":4293918720,"VideoProcessor":"NVIDIA GeForce RTX 5090","PNPDeviceID":"PCI\\VEN_10DE&DEV_2B85&SUBSYS_10621043&REV_A1\\4&1F2E3D4C&0&0019","BusNumber":1,"Address":0}, + {"Name":"AMD Radeon RX 9070 XT","Status":"OK","ConfigManagerErrorCode":0,"AdapterRAM":4293918720,"VideoProcessor":"AMD Radeon Graphics Processor (0x7550)","PNPDeviceID":"PCI\\VEN_1002&DEV_7550&SUBSYS_0E4E1002&REV_C0\\6&1A2B3C4D&0&00000008","BusNumber":null,"Address":null}]"#) + } + // the 0.3.9 app's five rows came from this shape of --list: two AMD platforms, each listing both AMD GPUs (the old + // 32.0.21042 ICD and the new 32.0.32015 one); the driver strings are the shape AMD's runtime prints, the numbers + // are the Windows driver builds (approximate: the OpenCL CL_DRIVER_VERSION was not captured) + const PC1_LIST: &str = "OpenCL devices (4):\n\ + [0] gfx1036 | AMD Accelerated Parallel Processing (OpenCL 2.1 AMD-APP (3617.0))\n\ + GPU, vendor Advanced Micro Devices, Inc., driver 3617.0 (PAL,HSAIL), OpenCL C 2.0, 2 compute units, 2200 MHz\n\ + global 16384 MiB, max alloc 13926 MiB, local 64 KiB, max work-group 256, sub-group extension: cl_khr_subgroups (no shuffle extension), AMD wavefront width 32\n\ + [1] gfx1201 | AMD Accelerated Parallel Processing (OpenCL 2.1 AMD-APP (3617.0))\n\ + GPU, vendor Advanced Micro Devices, Inc., driver 3617.0 (PAL,HSAIL), OpenCL C 2.0, 32 compute units, 2970 MHz\n\ + global 16368 MiB, max alloc 13912 MiB, local 64 KiB, max work-group 256, sub-group extension: cl_khr_subgroups (no shuffle extension), AMD wavefront width 32\n\ + [2] gfx1036 | AMD Accelerated Parallel Processing (OpenCL 2.1 AMD-APP (3649.0))\n\ + GPU, vendor Advanced Micro Devices, Inc., driver 3649.0 (PAL,HSAIL), OpenCL C 2.0, 2 compute units, 2200 MHz\n\ + global 16384 MiB, max alloc 13926 MiB, local 64 KiB, max work-group 256, sub-group extension: cl_khr_subgroups (no shuffle extension), AMD wavefront width 32\n\ + [3] gfx1201 | AMD Accelerated Parallel Processing (OpenCL 2.1 AMD-APP (3649.0))\n\ + GPU, vendor Advanced Micro Devices, Inc., driver 3649.0 (PAL,HSAIL), OpenCL C 2.0, 32 compute units, 2970 MHz\n\ + global 16368 MiB, max alloc 13912 MiB, local 64 KiB, max work-group 256, sub-group extension: cl_khr_subgroups (no shuffle extension), AMD wavefront width 32\n"; + const PC1_SMI: &str = "0, NVIDIA GeForce RTX 5090, 32607 MiB, 00000000:01:00.0\n"; + + fn pc1_inputs(list: &str) -> Inputs { + Inputs { nvidia: Some(PC1_SMI.into()), nvidia_limits: Default::default(), cuda_worker: true, opencl: Some(list.into()), opencl_installed: true, adapters: Some(pc1_rebooted()) } + } + + #[test] + fn opencl_list_parses_both_platforms_and_the_pci_field() { + let (devs, listed) = parse_opencl_list(PC1_LIST); + assert!(listed); + assert_eq!(devs.len(), 4); + assert_eq!(devs[1].index, "1"); + assert_eq!(devs[1].name, "gfx1201"); + assert_eq!(devs[1].platform, "AMD Accelerated Parallel Processing"); + assert_eq!(devs[1].platform_version, "OpenCL 2.1 AMD-APP (3617.0)"); + assert_eq!(devs[1].driver, "3617.0 (PAL,HSAIL)"); + assert_eq!(devs[1].units, "32 compute units"); + assert_eq!(devs[1].mem_mb, 16368); + assert_eq!(devs[1].bus, ""); + assert!(devs[1].is_gpu); + assert_eq!(devs[1].vendor_word(), "amd"); + let with_pci = PC1_LIST.replace("32 compute units, 2970 MHz\n", "32 compute units, 2970 MHz, pci 05:00.0\n"); + let (devs, _) = parse_opencl_list(&with_pci); + assert_eq!(devs[1].bus, "05:00.0"); + assert_eq!(devs[3].bus, "05:00.0"); + assert!(!parse_opencl_list("").1); + } + + #[test] + fn two_amd_platforms_give_one_entry_per_card() { + // without PCI addresses: by code and ordinal, the newer driver wins + let (devs, _) = parse_opencl_list(PC1_LIST); + let (kept, dropped) = dedupe_platforms(devs); + assert_eq!(kept.iter().map(|d| d.index.as_str()).collect::>(), vec!["2", "3"]); + assert_eq!(dropped.len(), 2); + assert!(dropped[0].starts_with("[0] gfx1036 on AMD Accelerated Parallel Processing (OpenCL 2.1 AMD-APP (3617.0)) is the same card as"), "{}", dropped[0]); + // with PCI addresses: by address; a card the winner does not list (the old ICD still serving a third card) is kept + let text = PC1_LIST + .replace("2 compute units, 2200 MHz\n", "2 compute units, 2200 MHz, pci 0c:00.0\n") + .replace("32 compute units, 2970 MHz\n", "32 compute units, 2970 MHz, pci 05:00.0\n") + + " [4] gfx1100 | AMD Accelerated Parallel Processing (OpenCL 2.1 AMD-APP (3617.0))\n GPU, vendor Advanced Micro Devices, Inc., driver 3617.0 (PAL,HSAIL), OpenCL C 2.0, 96 compute units, 2500 MHz, pci 09:00.0\n global 24560 MiB\n"; + let (devs, _) = parse_opencl_list(&text); + let (kept, dropped) = dedupe_platforms(devs); + // the old platform now lists three and wins on count: its three stay, the new platform's two are duplicates + assert_eq!(kept.iter().map(|d| d.index.as_str()).collect::>(), vec!["0", "1", "4"]); + assert_eq!(dropped.len(), 2); + // two real twins on one platform keep both entries (same code, different ordinal or address) + let twins = "OpenCL devices (2):\n [0] gfx1201 | P (v)\n GPU, vendor Advanced Micro Devices, Inc., driver 1.0, OpenCL C 2.0, 32 compute units, 2970 MHz\n [1] gfx1201 | P (v)\n GPU, vendor Advanced Micro Devices, Inc., driver 1.0, OpenCL C 2.0, 32 compute units, 2970 MHz\n"; + let (kept, dropped) = dedupe_platforms(parse_opencl_list(twins).0); + assert_eq!(kept.len(), 2); + assert!(dropped.is_empty()); + } + + #[test] + fn names_come_from_windows_by_bus_then_device_id_then_the_table() { + let a = pc1_rebooted(); + let mut used = Vec::new(); + assert_eq!(resolve_name("NVIDIA GeForce RTX 5090", "nvidia", "01:00.0", &a, &mut used).0, "NVIDIA GeForce RTX 5090"); + assert_eq!(resolve_name("gfx1201", "amd", "", &a, &mut used).0, "AMD Radeon RX 9070 XT"); + assert_eq!(resolve_name("gfx1036", "amd", "", &a, &mut used).0, "AMD Radeon(TM) Graphics"); + assert_eq!(used.len(), 3); + // every adapter is taken: a second gfx1036 gets the table's words, a code the table lacks stays a code + assert_eq!(resolve_name("gfx1036", "amd", "", &a, &mut used).0, "Ryzen integrated Radeon Graphics"); + assert_eq!(resolve_name("gfx9999", "amd", "", &a, &mut used).0, "gfx9999"); + assert_eq!(resolve_name("gfx1100", "amd", "", &[], &mut Vec::new()).0, "Radeon RX 7900 XTX / XT"); + // by PCI address when both sides have one, before any table + let mut b = pc1_rebooted(); + b[2].bus = "05:00.0".into(); + let mut used = Vec::new(); + assert_eq!(resolve_name("gfx1201", "amd", "05:00.0", &b, &mut used), ("AMD Radeon RX 9070 XT".to_string(), Some(2))); + // the device id alone names a card whose adapter name the table does not know + let mut c = pc1_rebooted(); + c[2].name = "AMD Radeon RX 9070 XT OC Edition".into(); + assert_eq!(resolve_name("gfx1201", "amd", "", &c, &mut Vec::new()).0, "AMD Radeon RX 9070 XT OC Edition"); + assert_eq!(a[2].device_id(), 0x7550); + assert_eq!(a[0].device_id(), 0x13C0); + assert_eq!(a[1].bus, "01:00.0"); + } + + #[test] + fn pc1_five_rows_become_three_cards_named_properly() { + let d = assemble(pc1_inputs(PC1_LIST)); + assert!(d.nvidia_listed && d.opencl_listed && d.adapters_listed); + let rows: Vec<(String, String, String, String, bool, String)> = d.cards.iter().map(|c| (c.name.clone(), c.key.clone(), c.kind.clone(), c.device.clone(), c.enabled, c.code.clone())).collect(); + assert_eq!(rows, vec![ + ("NVIDIA GeForce RTX 5090".into(), "nvidia:NVIDIA GeForce RTX 5090".into(), "discrete".into(), "0".into(), true, "NVIDIA GeForce RTX 5090".into()), + ("AMD Radeon(TM) Graphics".into(), "amd:gfx1036".into(), "integrated".into(), "2".into(), false, "gfx1036".into()), + ("AMD Radeon RX 9070 XT".into(), "amd:gfx1201".into(), "discrete".into(), "3".into(), true, "gfx1201".into()), + ]); + assert_eq!(d.cards[0].bus, "01:00.0"); + assert_eq!(d.cards[1].reason, INTEGRATED_REASON); + assert_eq!(d.cards[1].identities, 1); + assert_eq!(d.cards[2].identities, 8, "16 GB: 8 identities"); + assert_eq!(d.cards[2].vram_mb, 16368); + assert!(d.cards[2].platform.contains("3649.0")); + assert_eq!(d.dropped.len(), 2); + assert!(d.notes.is_empty(), "{:?}", d.notes); + assert_eq!(crate::hotplug::cards_line(&d.cards), "cards: NVIDIA GeForce RTX 5090 [discrete, off] | AMD Radeon(TM) Graphics [integrated, off] | AMD Radeon RX 9070 XT [discrete, off]"); + // the same machine before the eGPU: one platform, the iGPU alone; the keys do not depend on the index + let before = "OpenCL devices (1):\n [0] gfx1036 | AMD Accelerated Parallel Processing (OpenCL 2.1 AMD-APP (3617.0))\n GPU, vendor Advanced Micro Devices, Inc., driver 3617.0 (PAL,HSAIL), OpenCL C 2.0, 2 compute units, 2200 MHz\n global 16384 MiB\n"; + let d0 = assemble(pc1_inputs(before)); + assert_eq!(d0.cards[1].key, "amd:gfx1036"); + assert_eq!(d0.cards[1].device, "0"); + // and the diff between the two lists: the iGPU moved (device 0 to 2), the 9070 XT is new, nothing is removed + let diff = crate::hotplug::diff(&d0.cards, &d.cards, &|c| d.listed(c)); + assert_eq!(diff.unchanged, vec![0]); + assert_eq!(diff.moved.len(), 1); + assert_eq!(diff.moved[0].0, 1); + assert_eq!(diff.added.len(), 1); + assert_eq!(diff.added[0].name, "AMD Radeon RX 9070 XT"); + assert!(diff.removed.is_empty()); + } + + #[test] + fn keys_number_identical_cards() { + let mut cards = vec![card(0, "NVIDIA GeForce RTX 5090", "nvidia", "CUDA", "", "0"), card(1, "NVIDIA GeForce RTX 5090", "nvidia", "CUDA", "", "1"), card(2, "gfx1201", "amd", "OpenCL", "", "2")]; + cards[2].name = "AMD Radeon RX 9070 XT".into(); + assign_keys(&mut cards); + assert_eq!(cards.iter().map(|c| c.key.as_str()).collect::>(), vec!["nvidia:NVIDIA GeForce RTX 5090", "nvidia:NVIDIA GeForce RTX 5090#2", "amd:gfx1201"]); + } + #[test] fn unusable_card_row() { let mut c = card(2, "AMD Radeon RX 9070 XT", "amd", "OpenCL", "", ""); diff --git a/app/igneum-app/src/engine.rs b/app/igneum-app/src/engine.rs index ff2b34ad..d1d9a4e6 100644 --- a/app/igneum-app/src/engine.rs +++ b/app/igneum-app/src/engine.rs @@ -957,12 +957,15 @@ impl Engine { /// The first enumeration: the list as detected, the saved choices applied, the setup screen's notes. fn first_detection(&mut self, d: crate::detect::Detection) { self.detected = true; - let crate::detect::Detection { cards, notes, .. } = d; - let names: Vec = cards.iter().map(|c| format!("{} ({}{})", c.name, c.worker, if c.problem.is_empty() { String::new() } else { format!(", {}", c.problem) })).collect(); + let crate::detect::Detection { cards, notes, dropped, .. } = d; + let names: Vec = cards.iter().map(|c| format!("{} ({}{}{})", c.name, c.worker, if c.code != c.name { format!(", {}", c.code) } else { String::new() }, if c.problem.is_empty() { String::new() } else { format!(", {}", c.problem) })).collect(); self.shared.log(&format!("GPUs: {}", if names.is_empty() { "none usable".to_string() } else { names.join("; ") })); for n in ¬es { self.shared.log(&format!("detection: {n}")); } + for n in &dropped { + self.shared.log(&format!("detection: duplicate OpenCL platform entry {n}")); + } let prefs = self.shared.settings.lock().unwrap().cards.clone(); let mut st = self.st(); st.detecting = false; @@ -1004,6 +1007,9 @@ impl Engine { if diff.is_quiet() { return; } + for n in &d.dropped { + self.shared.log(&format!("detection: duplicate OpenCL platform entry {n}")); + } let prefs = self.shared.settings.lock().unwrap().cards.clone(); let mut new_nvidia = false; for i in diff.removed.iter().copied() { @@ -1029,6 +1035,8 @@ impl Engine { c.device = fresh.device.clone(); c.key = fresh.key.clone(); c.bus = fresh.bus.clone(); + c.name = fresh.name.clone(); + c.platform = fresh.platform.clone(); if fresh.vram_mb > 0 { c.vram_mb = fresh.vram_mb; } diff --git a/app/igneum-app/src/hotplug.rs b/app/igneum-app/src/hotplug.rs index 2f05e672..aeaaf46f 100644 --- a/app/igneum-app/src/hotplug.rs +++ b/app/igneum-app/src/hotplug.rs @@ -3,8 +3,9 @@ //! engine applies the result (start a worker, stop one, mark a row). Born 5 October 2026, when an RX 9070 XT went //! into PC 1 through an eGPU box while the app ran and nothing noticed. //! -//! Rules: a card is the same card when its key (vendor:device:name) matches, or, failing that, when vendor and -//! name match and that pair is unique on both sides (a device index that moved because another card arrived). A +//! Rules: a card is the same card when its key (vendor:code, "#2" for a twin) matches and the PCI addresses do not +//! disagree, or, failing that, when vendor and name match and that pair is unique on both sides (a twin whose +//! ordinal moved because the first one left). The device index is never part of the identity: it moves. A //! card missing from a list is removed only when the tool that lists its vendor answered. Removed and faulty cards //! stay in the engine's list (the other cards' indices are the miner slots), marked, and the dashboard hides a //! removed row after five minutes. @@ -43,8 +44,12 @@ impl Diff { } } +fn bus_compat(a: &CardState, b: &CardState) -> bool { + a.bus.is_empty() || b.bus.is_empty() || a.bus == b.bus +} + fn same_identity(a: &CardState, b: &CardState) -> bool { - a.vendor == b.vendor && a.name.trim().eq_ignore_ascii_case(b.name.trim()) && (a.bus.is_empty() || b.bus.is_empty() || a.bus == b.bus) + a.vendor == b.vendor && (a.name.trim().eq_ignore_ascii_case(b.name.trim()) || (!a.code.is_empty() && a.code.eq_ignore_ascii_case(&b.code))) && bus_compat(a, b) } /// Compares the engine's list with a fresh enumeration. `listed(card)` says whether this enumeration's tools could @@ -55,7 +60,7 @@ pub fn diff(old: &[CardState], fresh: &[CardState], listed: &dyn Fn(&CardState) let mut pair: Vec> = vec![None; old.len()]; // exact keys first for (i, o) in old.iter().enumerate() { - if let Some(j) = fresh.iter().enumerate().position(|(j, f)| !used[j] && f.key == o.key) { + if let Some(j) = fresh.iter().enumerate().position(|(j, f)| !used[j] && f.key == o.key && bus_compat(o, f)) { used[j] = true; pair[i] = Some(j); } @@ -115,15 +120,26 @@ pub fn diff(old: &[CardState], fresh: &[CardState], listed: &dyn Fn(&CardState) out } -/// The saved choice for a card: by its key, else by vendor and name (the index moved since the choice was saved). +/// The saved choice for a card: by its key (vendor:code), else a key saved by an app before 0.3.11 (vendor:index:code, +/// vendor:index:name) when exactly one matches; an index that moved never changes the answer. pub fn pref_for<'a>(prefs: &'a std::collections::HashMap, c: &CardState) -> Option<&'a CardPref> { if let Some(p) = prefs.get(&c.key) { return Some(p); } + if c.key.contains('#') { + return None; // a twin's choice is its own + } let head = format!("{}:", c.vendor); - let tail = format!(":{}", c.name); - let mut found: Vec<&CardPref> = prefs.iter().filter(|(k, _)| k.starts_with(&head) && k.ends_with(&tail)).map(|(_, p)| p).collect(); - if found.len() == 1 { found.pop() } else { None } + for tail in [format!(":{}", c.code), format!(":{}", c.name)] { + if tail.len() <= 1 { + continue; + } + let found: Vec<&CardPref> = prefs.iter().filter(|(k, _)| k.starts_with(&head) && k.ends_with(&tail) && !k.contains('#')).map(|(_, p)| p).collect(); + if found.len() == 1 { + return Some(found[0]); + } + } + None } /// Applies a saved choice to a freshly detected card (the first detection and every later one use this). @@ -227,7 +243,7 @@ mod tests { use crate::detect::{classify_kind, mark_unusable, INTEGRATED_REASON}; fn card(name: &str, vendor: &str, device: &str) -> CardState { - let mut c = CardState { index: 0, key: format!("{vendor}:{device}:{name}"), name: name.into(), vendor: vendor.into(), worker: if vendor == "nvidia" { "CUDA".into() } else { "OpenCL".into() }, device: device.into(), enabled: true, state: "off".into(), ..Default::default() }; + let mut c = CardState { index: 0, key: format!("{vendor}:{name}"), code: name.into(), name: name.into(), vendor: vendor.into(), worker: if vendor == "nvidia" { "CUDA".into() } else { "OpenCL".into() }, device: device.into(), enabled: true, state: "off".into(), ..Default::default() }; c.kind = classify_kind(name, None).into(); crate::detect::apply_defaults(&mut c); c @@ -312,7 +328,6 @@ mod tests { let old = pc1_start(); let mut fresh = pc1_start(); fresh[1].device = "1".into(); - fresh[1].key = "amd:1:gfx1036".into(); let mut e = card("gfx1201", "amd", "0"); e.index = 2; fresh.push(e); @@ -329,26 +344,27 @@ mod tests { let mut a = card("NVIDIA GeForce RTX 5090", "nvidia", "0"); let mut b = card("NVIDIA GeForce RTX 5090", "nvidia", "1"); b.index = 1; - a.bus = "00000000:01:00.0".into(); - b.bus = "00000000:02:00.0".into(); + b.key = "nvidia:NVIDIA GeForce RTX 5090#2".into(); + a.bus = "01:00.0".into(); + b.bus = "02:00.0".into(); let old = vec![a.clone(), b.clone()]; - // one of the twins leaves: the other keeps its slot by key; the missing one is removed, not "moved" + // the second twin leaves: the first keeps its slot by key; the missing one is removed, not "moved" let d = diff(&old, &[a.clone()], &all_listed); assert_eq!(d.unchanged, vec![0]); assert_eq!(d.removed, vec![1]); - // both twins present, both unmatched by key (indices swapped): the vendor+name rule refuses to guess - let mut a2 = a.clone(); - a2.device = "1".into(); - a2.key = "nvidia:1:NVIDIA GeForce RTX 5090".into(); - a2.bus = "00000000:03:00.0".into(); - let mut b2 = b.clone(); - b2.device = "0".into(); - b2.key = "nvidia:0:NVIDIA GeForce RTX 5090".into(); - b2.bus = "00000000:04:00.0".into(); - let d2 = diff(&old, &[a2, b2], &all_listed); - // the keys are the identity for twins: slot 0 is nvidia index 0 whichever bus it sits on; nothing restarts - assert!(d2.is_quiet(), "{d2:?}"); - assert_eq!(d2.unchanged, vec![0, 1]); + // the FIRST twin leaves: the survivor is now index 0 with the unsuffixed key, but its bus says which card it + // is, so slot 1 is "moved" (new key and device) and slot 0 is removed; no worker is swapped between cards + let mut survivor = b.clone(); + survivor.device = "0".into(); + survivor.key = "nvidia:NVIDIA GeForce RTX 5090".into(); + let d2 = diff(&old, &[survivor], &all_listed); + assert_eq!(d2.removed, vec![0]); + assert_eq!(d2.moved.len(), 1); + assert_eq!(d2.moved[0].0, 1); + assert_eq!(d2.moved[0].1.key, "nvidia:NVIDIA GeForce RTX 5090"); + // the same two cards again, nothing changed: quiet + let d3 = diff(&old, &old, &all_listed); + assert!(d3.is_quiet(), "{d3:?}"); } #[test] @@ -366,7 +382,6 @@ mod tests { let mut faulty = old.clone(); mark_unusable(&mut faulty[2], "Code 43"); faulty[2].device = String::new(); - faulty[2].key = "amd::gfx1201".into(); let d2 = diff(&faulty, &fresh_pc1_with_egpu(), &all_listed); assert_eq!(d2.recovered.len(), 1); assert_eq!(d2.recovered[0].0, 2); @@ -406,7 +421,7 @@ mod tests { let mut prefs = std::collections::HashMap::new(); // the user switched the iGPU on earlier and set 2 identities; the setting was saved under OpenCL index 0 prefs.insert("amd:0:gfx1036".to_string(), CardPref { enabled: true, identities: 2, ..Default::default() }); - // the iGPU comes back as device 1 (the eGPU took index 0): the choice is found by vendor and name + // the iGPU comes back as device 1 (the eGPU took index 0): the 0.3.9 key still answers, by vendor and code let mut igpu = card("gfx1036", "amd", "1"); assert_eq!(igpu.kind, "integrated"); assert!(!igpu.enabled); @@ -418,8 +433,8 @@ mod tests { assert_eq!(igpu.identities, 2); assert_eq!(igpu.reason, ""); assert_eq!(igpu.state, "waiting"); - // the user switched it off: the row keeps the integrated words - prefs.insert("amd:1:gfx1036".to_string(), CardPref { enabled: false, identities: 1, ..Default::default() }); + // the user switched it off (saved under the index-free key): the row keeps the integrated words + prefs.insert("amd:gfx1036".to_string(), CardPref { enabled: false, identities: 1, ..Default::default() }); let mut igpu2 = card("gfx1036", "amd", "1"); let p2 = pref_for(&prefs, &igpu2).cloned(); settle_new(&mut igpu2, 1, p2.as_ref(), 6.0); @@ -440,10 +455,25 @@ mod tests { settle_new(&mut bad, 3, Some(&CardPref { enabled: true, identities: 8, ..Default::default() }), 8.0); assert!(!bad.enabled); assert_eq!(bad.state, "unusable"); - // two saved choices with the same vendor and name but different indices: ambiguous, so none is used - prefs.insert("amd:0:gfx1036".to_string(), CardPref { enabled: true, identities: 3, ..Default::default() }); + // old keys only, two of them for the same code (the five-row PC 1 list had amd:0:gfx1036 and amd:2:gfx1036): + // ambiguous, so the default applies; the index-free key, once saved, always wins + prefs.remove("amd:gfx1036"); + prefs.insert("amd:2:gfx1036".to_string(), CardPref { enabled: true, identities: 3, ..Default::default() }); let other = card("gfx1036", "amd", "7"); assert!(pref_for(&prefs, &other).is_none()); + prefs.insert("amd:gfx1036".to_string(), CardPref { enabled: true, identities: 4, ..Default::default() }); + assert_eq!(pref_for(&prefs, &other).map(|p| p.identities), Some(4)); + // a twin never borrows the first card's choice + let mut twin = card("gfx1201", "amd", "3"); + twin.key = "amd:gfx1201#2".into(); + prefs.insert("amd:gfx1201".to_string(), CardPref { enabled: false, identities: 1, ..Default::default() }); + assert!(pref_for(&prefs, &twin).is_none()); + // the name on the row is the Windows name while the key keeps the code: the 0.3.9 key by code still answers + let mut named = card("gfx1201", "amd", "1"); + named.name = "AMD Radeon RX 9070 XT".into(); + prefs.clear(); + prefs.insert("amd:1:gfx1201".to_string(), CardPref { enabled: false, identities: 2, ..Default::default() }); + assert_eq!(pref_for(&prefs, &named).map(|p| p.identities), Some(2)); } #[test] diff --git a/app/igneum-app/src/state.rs b/app/igneum-app/src/state.rs index c5784c67..0267c533 100644 --- a/app/igneum-app/src/state.rs +++ b/app/igneum-app/src/state.rs @@ -29,7 +29,9 @@ pub struct NodeState { #[derive(Clone, Serialize, Default, Debug)] pub struct CardState { pub index: usize, - pub key: String, // stable id for the saved preference: vendor:device:name + pub key: String, // stable id for the saved preference: vendor:code, "#2" and up for a second identical card (no index: it moves) + pub code: String, // the tool's own device name (AMD's OpenCL runtime says "gfx1201"); the key and the tooltip carry it + pub platform: String, // the OpenCL platform and driver the worker opens it through (the tooltip) pub kind: String, // apple | discrete | integrated | external | unknown pub vram_mb: u64, // 0 when unknown pub reason: String, // why it is off by default, if it is diff --git a/app/igneum-app/ui/app.js b/app/igneum-app/ui/app.js index 8964aa25..25ba1194 100644 --- a/app/igneum-app/ui/app.js +++ b/app/igneum-app/ui/app.js @@ -706,6 +706,8 @@ if (typeof document !== 'undefined') (function () { var kindWord = Notices.kindWord; // hot-plug (src/hotplug.rs): a removed card's row hides after five minutes (gone); a faulty one has no switch function shownCards(cards) { return (cards || []).filter(function (c) { return !c.gone; }); } + // the tooltip on a card's name: what the tool calls it (gfx1201), its device index, the OpenCL platform, the PCI address + function cardTitle(cd) { var t = []; if (cd.code && cd.code !== cd.name) t.push(cd.code); if (cd.device !== '') t.push(cd.worker + ' device ' + cd.device); if (cd.platform) t.push(cd.platform); if (cd.bus) t.push('bus ' + cd.bus); return t.join(' · '); } function vramText(cd) { if (!cd.vram_mb) return ''; var gb = cd.vram_mb / 1024; return (gb >= 10 ? Math.round(gb) : gb.toFixed(gb < 4 ? 1 : 0)) + ' GB' + (cd.kind === 'apple' ? ' unified' : ''); } function renderCardRows(container, cards) { container.querySelectorAll('.gpu-row').forEach(function (n) { n.remove(); }); @@ -716,7 +718,7 @@ if (typeof document !== 'undefined') (function () { d.dataset.key = cd.key; var meta = 'worker ' + esc(cd.worker) + '' + (vramText(cd) ? '' + esc(vramText(cd)) + '' : '') + (cd.detail && cd.kind !== 'apple' ? '' + esc(cd.detail) + '' : '') + (cd.detail && cd.kind === 'apple' ? '' + esc(cd.detail.split(',')[0]) + '' : '') + (cd.device ? 'device ' + esc(cd.device) + '' : ''); d.innerHTML = '
' + badgeHtml(cd) + '
' + - '
' + esc(cd.name) + (unusable ? ': ' + esc(cd.message || 'not usable (' + cd.problem + ')') : removed ? ': removed' : '') + ' ' + kindWord(cd.kind) + '
' + meta + '
' + + '
' + esc(cd.name) + (unusable ? ': ' + esc(cd.message || 'not usable (' + cd.problem + ')') : removed ? ': removed' : '') + ' ' + kindWord(cd.kind) + '
' + meta + '
' + (cd.reason ? '
' + esc(cd.reason) + '
' : '') + (cd.message && !unusable && !removed ? '
' + esc(cd.message) + '
' : '') + '
' + (cd.vendor === 'nvidia' && cd.power_default_w > 0 ? '
power cap' + (cd.power_pct || 80) + '% · ' + Math.round(cd.power_default_w * (cd.power_pct || 80) / 100) + ' W' + (cd.pinned ? 'pinned' : cd.sweep_pct ? 'sweep ' + cd.sweep_pct + '%' : '') + '
' : '') + '
identities
' + @@ -861,7 +863,7 @@ if (typeof document !== 'undefined') (function () { var cls = cd.state === 'mining' ? 'mining' : (cd.state === 'failed' || cd.state === 'restarting' || cd.state === 'faulted') ? 'bad' : cd.state === 'removed' ? 'removed' : ''; var word = cd.state === 'restarting' ? ('restart in ' + cd.restart_in_s + ' s') : cd.state; var extra = cd.ids.length ? 'id ' + esc(cd.ids.join(' ')) + '' : ''; - return '
' + esc(cd.name) + ' ' + kindWord(cd.kind) + '
' + + return '
' + esc(cd.name) + ' ' + kindWord(cd.kind) + '
' + '
' + cd.hash_now.toFixed(1) + 'MH/s
' + '
blocks ' + cd.accepted + '' + (cd.rejected ? ' / ' + cd.rejected + ' rejected' : '') + 'avg ' + cd.hash_avg.toFixed(1) + 'identities ' + cd.identities + '' + extra + (cd.faults ? 'worker faults ' + cd.faults + '' : '') + (cd.mismatched ? 're-check misses ' + cd.mismatched + '' : '') + '
' + telemetryHtml(cd) + diff --git a/proto-opencl/host.c b/proto-opencl/host.c index 457ec209..4ec8f6f6 100644 --- a/proto-opencl/host.c +++ b/proto-opencl/host.c @@ -58,6 +58,16 @@ // Vendor device attributes (cl_amd_device_attribute_query, cl_nv_device_attribute_query). #define IG_CL_DEVICE_WAVEFRONT_WIDTH_AMD 0x4043 #define IG_CL_DEVICE_WARP_SIZE_NV 0x4003 +// Where the card sits on the PCI bus, so the app can tell one physical card listed by two OpenCL platforms (two +// AMD ICDs after a driver upgrade, PC 1 on 5 October 2026) from two cards: CL_DEVICE_TOPOLOGY_AMD and the NVIDIA pair. +#define IG_CL_DEVICE_TOPOLOGY_AMD 0x4037 +#define IG_CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD 1 +#define IG_CL_DEVICE_PCI_BUS_ID_NV 0x4008 +#define IG_CL_DEVICE_PCI_SLOT_ID_NV 0x4009 +typedef union { + struct { cl_uint type; cl_uint data[5]; } raw; + struct { cl_uint type; cl_char unused[17]; cl_char bus; cl_char device; cl_char function; } pcie; +} ig_topology_amd; typedef cl_int (CL_API_CALL *ig_pfn_subgroup_info)(cl_kernel, cl_device_id, cl_uint, size_t, const void*, size_t, void*, size_t*); @@ -297,6 +307,7 @@ typedef struct { int cMajor, cMinor; // OpenCL C version int dMajor, dMinor; // device (platform profile) version cl_uint amdWavefront, nvWarp; // 0 if not reported + char pci[32]; // "01:00.0" (bus:device.function) when the vendor extension reports it, else "" } DeviceInfo; static void devStr(cl_device_id d, cl_device_info what, char* out, size_t n) { @@ -349,10 +360,19 @@ static int enumerateDevices(DeviceInfo** outList) { clGetDeviceInfo(devs[d], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(di.maxWorkGroup), &di.maxWorkGroup, NULL); if (sscanf(di.cVersion, "OpenCL C %d.%d", &di.cMajor, &di.cMinor) != 2) { di.cMajor = 1; di.cMinor = 2; } if (sscanf(di.version, "OpenCL %d.%d", &di.dMajor, &di.dMinor) != 2) { di.dMajor = 1; di.dMinor = 2; } - if (strstr(di.extensions, "cl_amd_device_attribute_query")) + if (strstr(di.extensions, "cl_amd_device_attribute_query")) { + ig_topology_amd topo; + memset(&topo, 0, sizeof(topo)); clGetDeviceInfo(devs[d], IG_CL_DEVICE_WAVEFRONT_WIDTH_AMD, sizeof(di.amdWavefront), &di.amdWavefront, NULL); - if (strstr(di.extensions, "cl_nv_device_attribute_query")) + if (clGetDeviceInfo(devs[d], IG_CL_DEVICE_TOPOLOGY_AMD, sizeof(topo), &topo, NULL) == CL_SUCCESS && topo.raw.type == IG_CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD) + snprintf(di.pci, sizeof(di.pci), "%02x:%02x.%x", (unsigned)(unsigned char)topo.pcie.bus, (unsigned)(unsigned char)topo.pcie.device, (unsigned)(unsigned char)topo.pcie.function); + } + if (strstr(di.extensions, "cl_nv_device_attribute_query")) { + cl_uint bus = 0, slot = 0; clGetDeviceInfo(devs[d], IG_CL_DEVICE_WARP_SIZE_NV, sizeof(di.nvWarp), &di.nvWarp, NULL); + if (clGetDeviceInfo(devs[d], IG_CL_DEVICE_PCI_BUS_ID_NV, sizeof(bus), &bus, NULL) == CL_SUCCESS && clGetDeviceInfo(devs[d], IG_CL_DEVICE_PCI_SLOT_ID_NV, sizeof(slot), &slot, NULL) == CL_SUCCESS) + snprintf(di.pci, sizeof(di.pci), "%02x:%02x.0", (unsigned)(bus & 0xff), (unsigned)(slot & 0xff)); + } list = (DeviceInfo*)realloc(list, sizeof(DeviceInfo) * (size_t)(n + 1)); list[n++] = di; } @@ -366,7 +386,10 @@ static void printDevice(int idx, const DeviceInfo* d, int chosen) { strstr(d->extensions, "cl_intel_subgroups") ? "cl_intel_subgroups" : strstr(d->extensions, "cl_khr_subgroups") ? "cl_khr_subgroups (no shuffle extension)" : "none"; printf("%s[%d] %s | %s (%s)\n", chosen ? "*" : " ", idx, d->name, d->platformName, d->platformVersion); - printf(" %s, vendor %s, driver %s, %s, %u compute units, %u MHz\n", typeName(d->type), d->vendor, d->driver, d->cVersion, d->computeUnits, d->clockMHz); + // the app's detect.rs reads this line: type, vendor, driver, the compute units, and the PCI address when known + printf(" %s, vendor %s, driver %s, %s, %u compute units, %u MHz", typeName(d->type), d->vendor, d->driver, d->cVersion, d->computeUnits, d->clockMHz); + if (d->pci[0]) printf(", pci %s", d->pci); + printf("\n"); printf(" global %llu MiB, max alloc %llu MiB, local %llu KiB, max work-group %llu, sub-group extension: %s", (unsigned long long)(d->globalMem >> 20), (unsigned long long)(d->maxAlloc >> 20), (unsigned long long)(d->localMem >> 10), (unsigned long long)d->maxWorkGroup, subExt); From a0bd04eab84eeaab2ecf5d6c2b6d486007cf8cec Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:26:17 +0000 Subject: [PATCH 008/158] build-linux.sh: NODE_SRC, TARGET_DIR and OUT_DIR made absolute before the cd into the node source (a relative target dir landed inside the node worktree and the copy read the stale repository-relative one; the 0.3.10 cut, twice) Co-Authored-By: Claude Fable 5.1 --- infra/cross/build-linux.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/infra/cross/build-linux.sh b/infra/cross/build-linux.sh index 003fab4c..c45fcf6f 100755 --- a/infra/cross/build-linux.sh +++ b/infra/cross/build-linux.sh @@ -17,6 +17,11 @@ REPO="$(cd "$HERE/../.." && pwd)" NODE_SRC="${NODE_SRC:-$REPO/vendor/igneum-node-v4}" TARGET_DIR="${TARGET_DIR:-$REPO/vendor/igneum-node/target-linux}" OUT_DIR="${OUT_DIR:-$HERE/out}" +# Every path absolute before the cd into NODE_SRC (5 October 2026, the 0.3.10 cut: a relative TARGET_DIR was resolved by +# cargo inside the node worktree, the copy below read the repository-relative one, and the seed's "new" binary was the +# previous build's bytes, twice). +abs() { case "$1" in /*) printf '%s' "$1" ;; *) printf '%s/%s' "$PWD" "$1" ;; esac; } +NODE_SRC="$(abs "$NODE_SRC")"; TARGET_DIR="$(abs "$TARGET_DIR")"; OUT_DIR="$(abs "$OUT_DIR")" TARGET="${TARGET:-x86_64-unknown-linux-gnu}" GLIBC="${GLIBC:-2.36}" JOBS="${JOBS:-4}" From d898d36308538dd2f8f3dd53f832ec5c842b3d30 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:28:29 +0000 Subject: [PATCH 009/158] C4 fix: the rule v3 harness row measured (140-s split, B certified 11 and 12, n0 adopted 12 by certificate 3 s after the heal, every node on the certified chain, 0 conflicting, 0 disagreeing); bench-log, ledger and spec 3.11.7 updated Co-Authored-By: Claude Fable 5.1 --- docs/bench-log.md | 3 ++- docs/fud-ledger.md | 2 +- docs/spec/03-finality.md | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/bench-log.md b/docs/bench-log.md index d367ccef..e6d054cf 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -1457,8 +1457,9 @@ Owner: the consensus engineer and cryptographer agent, worktrees `igneum-wt-c4` | v2 130 s, W 240 | same | v2, 240, 130 s | 2 (10, 11) | 114 s | 0 | apart, n0 locked 16 alone at 293 s | 0 / 1 / 1 | 0 | the sync gap again (n0 reconnected after v2's 210-s bound) | | v2 130 s, W 240 | c4 fix with the sync hook | v2, 240, 130 s | 1 (index 12) | 84 s | 3 (12, 13, 14 within 2 s of the first B block; 11 re-determined) | B, all three, A's split tip abandoned | 0 | 0 | PASS | | on 130 s, W 240 | same | v3, 240, 130 s | 0 (Poisson: 52 blue blocks, the index fell just short) | 84 s | n1 1, n2 2 (B's nodes adopted A's post-heal certificates and moved before IBD) | A, all three | 0 | 0 | the mirror case; not the C4 shape | +| on 140 s, W 240, addPeer at the heal | same | v3, 240, 140 s | 2 (11, 12, first at 12 s) | 3 s (the harness now dials through `addPeer`; the address goes as `{ip, port}`) | 1 (12 by certificate; 11 verified on the new chain) | B, all three, A's split tip abandoned | 0 | 0 | PASS | -Reading. With the consensus fix and the sync hook, a node on the heavier chain that receives a certificate for a chain it has never seen fetches that chain, verifies the certificate at its own block, locks it, moves its sink to the lighter certified chain and re-determines its own records onto it (the v2 W 240 row: 0 conflicts, 0 disagreements, every node on B's chain, which is the spec's F1 and the design's Fork choice items 1 to 4). The same code holds under rule v3 (unit test, the mirror case in the last row); a v3 row with B certifying during the split is Poisson-limited at these rates and is the run still owed (`SPLIT=140`). The fix does not and cannot cover a partition that outlasts the bound before the certificate arrives (rows 2, 3 and 6): there the node has already locked alone and 3.11.4 keeps that lock, the late certificate is CONFLICTING for the operator. On the live devnet (W 7,200 DAA, two hours) the bound is two hours after a side's last lock, so every partition under that heals by certificate. Raw: `scratchpad c4-results-*.md`, node logs `c4-*-n0.log`. +Reading. With the consensus fix and the sync hook, a node on the heavier chain that receives a certificate for a chain it has never seen fetches that chain, verifies the certificate at its own block, locks it, moves its sink to the lighter certified chain and re-determines its own records onto it (the v2 W 240 row: 0 conflicts, 0 disagreements, every node on B's chain, which is the spec's F1 and the design's Fork choice items 1 to 4). The same holds under rule v3 with the frozen table on (the last row: B certified 11 and 12 during a 140-s split, n0 reconnected 3 s after the heal once the harness dialled through `addPeer`, adopted 12 by certificate and ended on B's chain with the other two, 0 conflicts, 0 disagreements). The fix does not and cannot cover a partition that outlasts the bound before the certificate arrives (rows 2, 3 and 6): there the node has already locked alone and 3.11.4 keeps that lock, the late certificate is CONFLICTING for the operator. On the live devnet (W 7,200 DAA, two hours) the bound is two hours after a side's last lock, so every partition under that heals by certificate. Raw: `scratchpad c4-results-*.md`, node logs `c4-*-n0.log`. ## 5 October 2026 (evening), FUD ledger sweep round 6 diff --git a/docs/fud-ledger.md b/docs/fud-ledger.md index 2003f971..365cf707 100644 --- a/docs/fud-ledger.md +++ b/docs/fud-ledger.md @@ -620,7 +620,7 @@ Evidence: design doc Finality v2, Fork choice items 1 to 4; `sim/results.md` fin Sweep (5 October 2026, evening): the module-on against module-off comparison of O-3.8, run on the fast-time harness with the live node line (`tools/finality-attacks/c4.mjs`, fork 2b6d23ef, 3 nodes, 100-ms proxied links; raw tables in `docs/bench-log.md`, "FUD ledger sweep round 6", C4). The scenario separates weight from work: side B (n1, n2, four keys) holds 70% of the weight table and side A (n0, two keys) 30% when the link is cut; from the cut A mines at 0.6 blocks/s and B at 0.4, so A's chain is the heavier one by blue work while only B can certify under rule v3 (A holds 30% of the frozen table). Module off (`min_daa` never, so no certificate can form, fork choice bare GHOSTDAG): after a 150-s split the three nodes converged on A's heavier chain within 36 s of the heal, B's nodes re-determined their two split-time checkpoints onto it (F24), 0 conflicts. Module on (rule v3 from checkpoint DAA 0), 90-s split, n0 back on the link 6 s after the heal, A's chain at about 58 DAA of its own time, well inside the 120-DAA frozen table: during the split A locked nothing and B locked indices 7 and 8 on its own blocks, as designed; after the heal n0 did not switch. Its log: B's certificates for 8 and 9 arrived and were "kept pending until the chain decides (no lock at this index)" (the F24 path), n0's chain never changed because GHOSTDAG prefers its heavier tip and nothing in the node turns a verified certificate over an off-chain block into a fork-choice constraint, and one window after n0's last lock (index 7 at DAA 209, so from DAA 329) the frozen table no longer applied on A's chain ("no frozen table (no lock on this chain inside the window)"), A's two keys were 100% of A's own window table (B's post-cut blocks are red there and earn nothing), and n0 locked 10, 11 and 12 alone; B's certificates for 10 and 11 then logged CONFLICTING on n0, and B's nodes kept their certified chain. End state: sinks apart, 2 locked indices disagreeing across the nodes, a finality fork from a 96-s honest partition with no attacker and the frozen table intact at the heal; the same shape with a 150-s split (the table expired at the heal) and under rule v2 (the control: 1 conflict, sinks apart). So the answer to the critic is sharper than conceded: the overlay is specified to override blue work (spec 3.5, "GHOSTDAG among tips through all certified checkpoints") but the shipped node applies a certificate only to a block on its own chain, holds the rest pending a reorg that GHOSTDAG alone never produces, and after one window the heavier side certifies its own chain. Two honest views never reconcile. What closes it: a verified certificate over a block the node does not have on its selected chain must verify against the weight table at THAT block (its signers' weight there) and, when valid, constrain fork choice to tips through it, forcing the reorg (a certificate-driven reorg, bounded by the finality depth), with the node's own unlocked records re-determined on the new chain (F24); until then the exchange guidance of 3.9 (a node partitioned for more than a minute treats its locks as proof of work until it has seen the network's certificates agree with its own) is the only protection, and the 3.11.7 row for this case ("a certificate over a chain the node is not on") is missing. On the live devnet the window is 7,200 DAA (two hours) and the cliff is two hours after a side's last lock; a miner who joins with more hashrate than the weight table credits is the realistic work-majority side. The trace-driven adversary of O-3.8 is still owed. Spec rows: 3.5, 3.11.4, 3.11.7; node: `processes/finality.rs` (`ingest_certificate`'s pending branch, `fork_choice_lock`). Decision owner: the project lead (gate 3; a rule change to the node's fork choice). -Fix (5 October 2026, night): the certificate-driven reorg, built, unit-tested and measured; fork branch `c4-fix` on release-0.3.6 (a24ab01a), main branch `c4-fix`. The cause in the code: `processes/finality.rs` `ingest_certificate` verified a certificate only over the node's own determination and sent every other block to `hold_pending`; `fork_choice_lock` reads `state.locks`, which only `evaluate` filled over the node's own chain; so a certified block off the chain never became a lock. A second cause only the harness showed: the block relay (`protocol/flows/src/v10/blockrelay/flow.rs`) skips a relayed block lighter than the virtual's merge-depth root, and the certified chain is the lighter one by construction, so the heavier side never even received it. The fix: `ingest_off_chain` verifies a certificate against the voter table at its own block (canonical list, aggregate BLS, 2/3 of active and of total there, the frozen table under v3, the first-month gate), checks the block lies on the chain through the node's nearest locks (else CONFLICTING, 3.11.4, no lock withdrawn), locks the index on that block and asks the virtual processor to resolve (`VirtualStateProcessingMessage::Resolve`), so the sink search keeps only tips through it, whatever the blue work and whatever the merge depth (finality outranks merge depth; the depth-based finality point still bounds it, Kaspa's pruning safety, logged once); pending certificates over blocks the node lacks are retried on every virtual change; `evaluate` locks the node's own determination only on the chain through its locks; and while a pending certificate names a block the node lacks (`finality_wants_blocks`), the relay takes the lighter block, which orphans, falls out of range and triggers IBD of the certified chain. Not gated on v3: the live devnet's rule v2 took the same pending path (unit test `the_certificate_driven_reorg_holds_under_rule_v2`). Spec 3.5 carries the rule in one paragraph, 3.2 C4 and the 3.10 rows C4 and F1/F2 the implementation. Measured (bench-log "the C4 fix", `c4.mjs` with `WINDOW=240` so a 130-s split plus the 84-s p2p reconnect stays inside the window; the sweep's 120-DAA framing crosses F21's bound before any certificate can arrive once the real reconnect time is counted): under rule v2 side B locked index 12 during the split, n0 took B's first relayed block through the hook, locked 12, 13 and 14 by certificate within 2 s, re-determined 11, and all three nodes ended on B's certified chain with 0 CONFLICTING and 0 disagreeing locked indices (was: sinks apart, 5 conflicts on each node, 2 disagreeing); the module-off control is unchanged (heavier chain, 0 conflicts); the mirror case under v3 (B certified nothing, A certified after the heal) had B's nodes adopt A's certificates and move before IBD. Unit tests on PC 2: `kaspa-consensus` 97 passed, `kaspa-consensus-core` 101 passed. Still owed: a v3 harness row with B certifying during the split (Poisson at 0.4 blocks/s over 130 s; `SPLIT=140` queued), the live-devnet partition test of O-3.6, and the trace-driven adversary of O-3.8. What the fix does not cover, by design: a partition that outlasts the bound before the certificate arrives (the side has locked alone, 3.11.4 keeps it, the late certificate is CONFLICTING for the operator), which on the devnet means over two hours. Rollout: a consensus-behaviour change in the node with no params-digest change; a mixed fleet disagrees only in the state the old node already got wrong (an old node holds the certificate pending and stays on its heavier chain while new nodes move), and converges once every node is new; ship in the next node release with every node restarted on it. +Fix (5 October 2026, night): the certificate-driven reorg, built, unit-tested and measured; fork branch `c4-fix` on release-0.3.6 (a24ab01a), main branch `c4-fix`. The cause in the code: `processes/finality.rs` `ingest_certificate` verified a certificate only over the node's own determination and sent every other block to `hold_pending`; `fork_choice_lock` reads `state.locks`, which only `evaluate` filled over the node's own chain; so a certified block off the chain never became a lock. A second cause only the harness showed: the block relay (`protocol/flows/src/v10/blockrelay/flow.rs`) skips a relayed block lighter than the virtual's merge-depth root, and the certified chain is the lighter one by construction, so the heavier side never even received it. The fix: `ingest_off_chain` verifies a certificate against the voter table at its own block (canonical list, aggregate BLS, 2/3 of active and of total there, the frozen table under v3, the first-month gate), checks the block lies on the chain through the node's nearest locks (else CONFLICTING, 3.11.4, no lock withdrawn), locks the index on that block and asks the virtual processor to resolve (`VirtualStateProcessingMessage::Resolve`), so the sink search keeps only tips through it, whatever the blue work and whatever the merge depth (finality outranks merge depth; the depth-based finality point still bounds it, Kaspa's pruning safety, logged once); pending certificates over blocks the node lacks are retried on every virtual change; `evaluate` locks the node's own determination only on the chain through its locks; and while a pending certificate names a block the node lacks (`finality_wants_blocks`), the relay takes the lighter block, which orphans, falls out of range and triggers IBD of the certified chain. Not gated on v3: the live devnet's rule v2 took the same pending path (unit test `the_certificate_driven_reorg_holds_under_rule_v2`). Spec 3.5 carries the rule in one paragraph, 3.2 C4 and the 3.10 rows C4 and F1/F2 the implementation. Measured (bench-log "the C4 fix", `c4.mjs` with `WINDOW=240` so a 130-s split plus the 84-s p2p reconnect stays inside the window; the sweep's 120-DAA framing crosses F21's bound before any certificate can arrive once the real reconnect time is counted): under rule v2 side B locked index 12 during the split, n0 took B's first relayed block through the hook, locked 12, 13 and 14 by certificate within 2 s, re-determined 11, and all three nodes ended on B's certified chain with 0 CONFLICTING and 0 disagreeing locked indices (was: sinks apart, 5 conflicts on each node, 2 disagreeing); the module-off control is unchanged (heavier chain, 0 conflicts); under rule v3 (frozen table on, 140-s split, n0 reconnected 3 s after the heal) B locked 11 and 12 during the split, n0 adopted 12 by certificate and verified 11 on the new chain, all three nodes on B's chain, 0 CONFLICTING, 0 disagreeing; the mirror case (B certified nothing, A certified after the heal) had B's nodes adopt A's certificates and move before IBD. Unit tests on PC 2: `kaspa-consensus` 97 passed, `kaspa-consensus-core` 101 passed. Still owed: the live-devnet partition test of O-3.6 and the trace-driven adversary of O-3.8. What the fix does not cover, by design: a partition that outlasts the bound before the certificate arrives (the side has locked alone, 3.11.4 keeps it, the late certificate is CONFLICTING for the operator), which on the devnet means over two hours. Rollout: a consensus-behaviour change in the node with no params-digest change; a mixed fleet disagrees only in the state the old node already got wrong (an old node holds the certificate pending and stays on its heavier chain while new nodes move), and converges once every node is new; ship in the next node release with every node restarted on it. ### C5. vs Ethereum: you compare inclusion to finality "'Included in about one second, against twelve on Ethereum.' Inclusion in a DAG is not confirmation. Ethereum's twelve seconds is a slot, its finality is about thirteen minutes, and you compare your two-minute lock to that as if a two-minute lock by a pool committee were the same thing." diff --git a/docs/spec/03-finality.md b/docs/spec/03-finality.md index bbb6c88c..00bf4733 100644 --- a/docs/spec/03-finality.md +++ b/docs/spec/03-finality.md @@ -282,7 +282,7 @@ Each guarantee, the scenario that tests it, and the measured result. Bench-log c | Acquired keys decay as the window moves (3.11.5) | results K, keys worth 20% and 40% bought, 30% hashrate, signing and silent, 30 days, five seeds | share follows b (1 - t/30) + 0.3 t/30 within 0.6 points at every sampled day in every seed; keys worth 20% rise to 30% on day 30 and never reach 1/3; keys worth 40% hold the veto from day 1 to day 19 or 20 (formula 20) and end at 30%; withholding its votes, the 40% buyer stalls 63,307 to 68,716 of 86,400 checkpoints in 30 days (the pause lasts until it has decayed below one third) and the 20% buyer 304 to 1,045; 0 conflicts (K, floor 2/3) | | Signing stops while mining continues, 1, 6, 24 h | results J and L1 | 34% and above: every checkpoint stalled for the whole silence; 33%: 665 to 727 of 720 in 6 h; 32%: 35 to 221; 30%: 0 to 40; first lock after resume 0 min at every weight; 0 conflicts (J, L1) | | Seeds during a pause (3.11.6) | devnet epoch boundary through a forced pause | not yet run (O-4.3 implementation) | -| A certificate over a chain the node is not on: the certificate-driven reorg (3.5) | fast-time harness `tools/finality-attacks/c4.mjs`, weight against work, 130-s split, 240-DAA window, rule v2 (the live devnet's) | the work-majority node fetched the certified chain, locked 12, 13 and 14 by certificate within 2 s of the first block, re-determined 11, and all three nodes ended on the certified chain with 0 conflicting certificates and 0 disagreeing locks; the module-off control took the heavier chain (bench-log "the C4 fix", 5 October 2026 night); the same under rule v3 is unit-tested, the harness row with the certifying side locking during the split is still owed | +| A certificate over a chain the node is not on: the certificate-driven reorg (3.5) | fast-time harness `tools/finality-attacks/c4.mjs`, weight against work, 130-s split, 240-DAA window, rule v2 (the live devnet's) | the work-majority node fetched the certified chain, locked 12, 13 and 14 by certificate within 2 s of the first block, re-determined 11, and all three nodes ended on the certified chain with 0 conflicting certificates and 0 disagreeing locks; the same under rule v3 with a 140-s split: the certifying side locked 11 and 12 during the split, the work-majority node adopted 12 by certificate 3 s after the heal and every node ended on the certified chain, 0 conflicting, 0 disagreeing; the module-off control took the heavier chain (bench-log "the C4 fix", 5 October 2026 night) | | Two certificates at one index: no lock withdrawn (3.11.4) | devnet with a forced double certificate | not yet run (O-3.17) | | `T` under the block reading (3.11.3) | O-3.3 re-run | not yet run (O-3.18) | | Participation credited only for the chain's checkpoint (3.11.1) | results C with an adversary voting for private blocks | not yet run (O-3.19) | From 7c7c1b6b0a97d22a141fcf487f1e933cfd570c56 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:36:17 +0000 Subject: [PATCH 010/158] Windows payload inputs: node 21d4c73c (push-inputs.sh, the PC 1 build, signed, live 18:36Z) Co-Authored-By: Claude Fable 5.1 --- packaging/windows/node-source.pin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/windows/node-source.pin b/packaging/windows/node-source.pin index f2ae6445..6b34aaf1 100644 --- a/packaging/windows/node-source.pin +++ b/packaging/windows/node-source.pin @@ -1 +1 @@ -a24ab01a2e10cecf575bcea372310b871081ec64 +21d4c73c6ce32fcbd68391968e85827339a511c0 From 3839ecfca1993e8547b81d2918d894ec23358b54 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:36:44 +0000 Subject: [PATCH 011/158] site: rebuilt for the 0.3.10 branch Co-Authored-By: Claude Fable 5.1 --- site/bench.html | 24 +++++++++++++++++++++--- site/downloads.json | 12 ++++++------ site/index.html | 2 +- site/journey.json | 24 ++++++++++++------------ site/miner.html | 8 ++++---- 5 files changed, 44 insertions(+), 26 deletions(-) diff --git a/site/bench.html b/site/bench.html index dc767c18..2bca77d2 100644 --- a/site/bench.html +++ b/site/bench.html @@ -172,12 +172,12 @@ th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:
-
68 entries, newest at the bottom
+
70 entries, newest at the bottom

Engineering log

Every measurement the project has made, newest at the bottom, written by the people and agents who ran it, with the commands and hardware. Prototype numbers are not mining numbers and say so.

- +

Igneum bench log

Append-only. Every number here was measured on the machine named, on the date given.

2026-10-03 proto-metal / igneum-bench, first run

@@ -512,6 +512,14 @@ th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:

The adopted fee table (spec 05 section 5.11) reaches the devnet by fees_v1_activation_daa (docs/plans/fee-switch-devnet.md). Until today the prover's guest carried only the prototype table (B_p 30 M, S_p 7.5 M, intrinsic 200), so every shard statement after the switch would have differed from the node's plan. Now igneum-prove-core mirrors the node's fees.rs (both tables, FeeSchedule::at), the shard input carries the schedule and the block's DAA score, and the executor raises the carried base fees to the set's floors as the node does. The 328-byte public values are unchanged: the node's native veto (it recomputes every statement) is what pins the schedule a prover claims.

CheckCommandResult
The node reads the switchcargo test --release -p igneum-exec on fork 2b6d23ef (this Mac, target vendor/igneum-node/target-036, 15:32:27Z to 15:36:30Z)11 passed, 0 failed, among them the_fee_switch_meters_by_the_block_daa_score
The digest for H = 210,00020 s scratch node, override {"difficulty_v2_activation_daa":33000,"proving_v0_activation_daa":84100,"fees_v1_activation_daa":210000}ab8847da538dead1dc10e046dfaadab3c1c35928e3748810c4e050d4a886087a
One chain across the switchprivate simnet on the 2b6d23ef binaries, override {"fees_v1_activation_daa": 200}, tools/prove-fixtures/gen.mjs before and after DAA 200, one igneum_exportSegments dump with every segment's daaScoreone chain, 358 segments, DAA 0 to 1,121: block 51 (DAA 187, prototype, 11 transactions, 7,494,392 pgas, one shard at S_p 7.5 M) before the switch; blocks 351 (DAA 1,105, 11 transactions, 36,934 pgas, two shards at S_p 30,000) and 355 (DAA 1,117, 14 transactions, 69,292 pgas, three shards) after it; igneum_getBudgets went from provingGasLimit 0x1c9c380 to 0x1d4c0 and the base fees to 100 gwei and 10,000 gwei at the switch. Found on the way: a burst signed with a 21 gwei cap just before the switch never executed after it (the floor is 100 gwei), and under v1 a modexp bomb's proving charge (pgas x 10,000 gwei) crosses the signed budget unless the gas limit covers it (design 4.1); gen.mjs now sizes both from the node's budgets
The port replays both sidesigneum-prove-export on that dump: every segment's state root equals the node's, prototype below 200 and v1 at and above itreplayed 358 segments from genesis; every state root equals the node's for each of the three cuts; fixtures fees-switch-prototype, fees-v1-shards2, fees-v1-shards3; igneum-prove-host --mode native on each: MATCHES, the three tamper cases REJECTED
The pinned guest on both sidesigneum-prove-host <fixture> --mode execute on this Mac (setup 37 s, pinned: setup matches the manifest)fees-switch-prototype shard 0: 66,043,259 cycles, 44 cycles per EVM gas, 9 cycles per prototype pgas, 2.88 s; fees-v1-shards2 shard 0: 4,717,439 cycles (213 cycles per v1 pgas, 0.38 s), shard 1: 3,485,430 cycles (236 per pgas, 0.26 s); aggregator 1.4 M cycles over 2 shards; every tamper case REJECTED. Under v1 these transfer-and-modexp shards run at about a quarter of the unit (1,000 cycles per pgas): the table over-charges them, which is the safe side of design R1's calibration
The new pinproving/igneum-prove/pin-guests.shshard program id 0x2b1a81cb413236cf063077b46ed3111628f6c41036bcf6e23ee4cbbf5679ef7a (2,832,504 bytes), aggregator 0x474678f35f7545db28055d5e5bbc308231d84a5a072202087a2a8d5b09123896, pinned 16:20:38Z; the 0.3.8 ids (0x0dfade07..., 0x135e67e7...) are what every machine runs until 0.3.9

Block rate for H: DAA 111,230 at 15:23Z, 112,227 at 15:40:13Z, 0.965 blocks/s; H = 210,000 is 24 h ahead of a publish before about 19:50Z on 5 October (the runbook moves it otherwise).

+

5 October 2026 (night), the C4 fix: certificate-driven reorg

+

Owner: the consensus engineer and cryptographer agent, worktrees igneum-wt-c4 (branch c4-fix) and vendor/igneum-node-c4 (fork branch c4-fix on release-0.3.6 a24ab01a). Harness tools/finality-attacks/c4.mjs on the fast-time 3-node network (100-ms proxied links), node built on the Apple M5 Max in vendor/igneum-node/target-c4 from the fork worktree (an APFS clone of target-036), suites on PC 2 through tools/build-job.mjs. The Mac carried two other builds and the M20 live sync throughout; every figure is a count, an index or a second from the harness clock.

+

The cause, in the code. processes/finality.rs: ingest_certificate verified a certificate only when its block was the node's own determination at that index (cp.hash == cert.checkpoint); any other block went to hold_pending, and nothing ever tried the pending certificate against the table at its own block. fork_choice_lock reads state.locks, which only evaluate filled, and evaluate only ever ran over the node's own determination. So a certified checkpoint off the node's chain never became a lock and never constrained the sink search, whatever spec 3.5 says. Second cause, found tonight on the harness: protocol/flows/src/v10/blockrelay/flow.rs skips a relayed block whose blue work is under the virtual's merge-depth root ("hence we are skipping it"), and the certified chain is lighter by construction, so the node on the heavier side never received the certified chain's blocks at all: in the first runs on the fixed consensus n0 held B's certificates by gossip for the whole heal window and B's blocks never arrived (n0's log shows only its own blocks "via submit block" after the reconnect).

+

The fix. Fork: ingest_off_chain (verifies against voters_at of the certificate's own block, Q3 and Q5 by quorum_at from that block's past, the lock chain by off_lock_chain, then LOCKED with a FinalityLock notification and a VirtualStateProcessingMessage::Resolve nudge so the sink moves without waiting for a block); retry_pending_off_chain on every virtual change; the lock-chain guard in evaluate (a determination off the chain through the node's nearest locks never locks and never aggregates); fork_choice_lock reports a lock beyond the depth-based finality point once; wants_unknown_certified_block and the relay-flow bypass of the merge-depth skip while a pending certificate names a block the node lacks (finality_wants_blocks through ConsensusApi and the session). Not gated on finality_v3_activation_daa: rule v2 took the same pending path.

+

Unit tests (PC 2, job build-20261005-180827, 18:09 UTC): kaspa-consensus 97 passed, 0 failed, 3 ignored; kaspa-consensus-core 101 passed. New: a_lighter_certified_chain_wins_and_a_heavier_uncertified_one_does_not_override_it (main chain 77 blocks locks to 13, a 7-block side chain's index-14 certificate is adopted, the sink moves to the side tip with no new block, ten more main-chain blocks do not move it back, a second certificate at 14 over the main block is CONFLICTING and the lock stands, the side chain then locks 15), the_certificate_driven_reorg_holds_under_rule_v2 (the same at finality_v3_activation_daa never), a_chain_that_misses_an_adopted_lock_never_locks_here (the evaluate guard and the off-lock conflict). reorg_past_an_unlocked_checkpoint_re_determines_it_and_verifies_the_pending_certificate rewritten for the new behaviour (pending while the block is unknown, adopted when it arrives). kaspa-p2p-flows lib tests do not compile on release-0.3.6 before or after this change (nine epoch_seed_headers errors in the pruning-proof message tests; the M20 job build-20261005-172340 hit the same nine an hour earlier). Six PC 2 jobs were lost tonight to two tooling faults, both fixed in the class: igneum-ota-sign embedded | head -1 under pipefail (SIGPIPE panic, four scripts, tools/ci/signer-pipe-check.sh) and the one shared build-inputs.zip in the downloads folder (a job published while another agent's pack landed pinned that agent's sources, three times; build-job.mjs now names every job's zip).

+

Harness, weight against work (B four keys and 70% of the weight, A two keys and 30%; at the cut A mines 0.6 and B 0.4 blocks/s; WINDOW = weight window, ban and min_daa at fast time). The 120-DAA window of the earlier runs turns every long split into F21's partition-longer-than-a-window shape once the p2p reconnect is added: n0 dials the proxy again on the connection manager's backoff, 84 to 114 s after the heal in every run tonight (the original sweep's 6 s was a short cut), so A's chain is 130 + 84 s = 128 DAA past the cut before any certificate can reach it, past its 120-DAA frozen table (v3) or its own two-thirds share of a sliding table (v2, 126 DAA at W 240 and s 0.3), and A locks alone first. With WINDOW=240 and WARM=320 the bound is 400 s (v3) or 210 s (v2) after the cut.

+
RunNodeRule, W, splitB locks during the splitn0 reconnectedn0 adopted off-chainFinal chainConflictingDisagreeingVerdict
on 90 s (the sweep's framing)c4 consensus fix, no sync hookv3, 120, 90 s0 (36 blue blocks for B, a new index needs 50)6 s0A, all three (no certificate to follow)00not the C4 shape
v2 90 ssamev2, 120, 90 s1 (index 8)6 s0apart5 / 5 / 52n0 locked 10 alone at 18:32:42, B's certificate for 8 reached it at 18:32:43: F21's bound (63 DAA of A's own chain) crossed before the heal
on 130 ssamev3, 120, 130 s1 (index 9)84 s0apart9 / 3 / 32n0 locked 12 alone at DAA 359, one window after lock 8 at 239, 6 s before the reconnect
off 150 s (control)sameno certificate, 150 s096 s0A (heavier), all three; B's nodes re-determined 2 indices00PASS, as in the sweep
on 130 s, W 240samev3, 240, 130 s2 (10, 11)114 s0 (certificates 13 and 14 pending, blocks unknown)apart00the sync gap: n0 never received a B block
v2 130 s, W 240samev2, 240, 130 s2 (10, 11)114 s0apart, n0 locked 16 alone at 293 s0 / 1 / 10the sync gap again (n0 reconnected after v2's 210-s bound)
v2 130 s, W 240c4 fix with the sync hookv2, 240, 130 s1 (index 12)84 s3 (12, 13, 14 within 2 s of the first B block; 11 re-determined)B, all three, A's split tip abandoned00PASS
on 130 s, W 240samev3, 240, 130 s0 (Poisson: 52 blue blocks, the index fell just short)84 sn1 1, n2 2 (B's nodes adopted A's post-heal certificates and moved before IBD)A, all three00the mirror case; not the C4 shape
on 140 s, W 240, addPeer at the healsamev3, 240, 140 s2 (11, 12, first at 12 s)3 s (the harness now dials through addPeer; the address goes as {ip, port})1 (12 by certificate; 11 verified on the new chain)B, all three, A's split tip abandoned00PASS
+

Reading. With the consensus fix and the sync hook, a node on the heavier chain that receives a certificate for a chain it has never seen fetches that chain, verifies the certificate at its own block, locks it, moves its sink to the lighter certified chain and re-determines its own records onto it (the v2 W 240 row: 0 conflicts, 0 disagreements, every node on B's chain, which is the spec's F1 and the design's Fork choice items 1 to 4). The same holds under rule v3 with the frozen table on (the last row: B certified 11 and 12 during a 140-s split, n0 reconnected 3 s after the heal once the harness dialled through addPeer, adopted 12 by certificate and ended on B's chain with the other two, 0 conflicts, 0 disagreements). The fix does not and cannot cover a partition that outlasts the bound before the certificate arrives (rows 2, 3 and 6): there the node has already locked alone and 3.11.4 keeps that lock, the late certificate is CONFLICTING for the operator. On the live devnet (W 7,200 DAA, two hours) the bound is two hours after a side's last lock, so every partition under that heals by certificate. Raw: scratchpad c4-results-*.md, node logs c4-*-n0.log.

5 October 2026 (evening), FUD ledger sweep round 6

Owner: the consensus engineer and cryptographer agent, worktree igneum-wt-fud-a (branch fud-a), 15:45 to 16:40 UTC. The Mac was loaded throughout (two cargo builds, a txgen run and a fee-switch simnet by other agents; load average over 100), so every figure below is a count, an index, a byte or a number from another machine; the only millisecond figures are the browser verifier's, taken as ratios and labelled. Live reads through the Apple M5 Max node's wRPC (ws://127.0.0.1:28640) and the log intake (Neon HTTP SQL, lines split server-side), never a restart.

Rolled-out fixes, the live evidence (F23, F24, G12, X18, M30, M31, F25, M20, M26, M27, X21). The 0.3.5 cut at 06:33 UTC (master 2054ae3, fork 20139145) carried fud-consensus, m20-pruning and miner-reliability; every reachable app machine was on the node line by 09:32 UTC and the hand nodes and the seed restarted on it for the proving activation (docs/plans/release-0.3.6.md 8j, "live devnet: the first shards proven"). Node logs of the five app machines over the 10 hours to 15:45 UTC, one intake query (scratchpad fud-a/nodelines.mjs):

@@ -532,7 +540,17 @@ th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:

M1, M16, P14, F16: arithmetic and documents, no run. M1: the version 2 program space from the generator's draws (slot subset 48.4 bits, operation entropy 3.26 bits, about 770 bits of operations and registers per program, about 1,550 with rotation, bit and mask fields, capped by the 256-bit seed). M16: docs/analysis/m16-recompute-attacker-2026-10-05.md. P14: next_base_fee in igneum/exec/src/executor.rs:501 is the one controller; spec 05 section 5.1 now says so. F16: the two options priced from sim/results_v2.md H and M5 and the cloud F21 numbers, in the ledger entry.

C4, the overlay against GHOSTDAG, measured (tools/finality-attacks/c4.mjs, the live node line target-036 2b6d23ef, fast time, 3 nodes, 100-ms proxied links, ports 29800+; "weight against work": side B with four keys and 70% of the weight table, side A with two keys and 30%; at the cut the rates swap, A at 0.6 and B at 0.4 blocks/s for 150 s, so A builds the heavier chain while only B can certify; heal window 200 s). The first run went out with the override unapplied (the harness library reads it at import; fixed the same hour) and is kept as the rule v2 control:

RunRuleNew locks during the split A / BFirst lock A / B (s)Blue score A / B at the healSinks after the healFinal chainConflicting certificatesDisagreeing locked indices
controlv2 (the live devnet's rule), min_daa 1204 / 3133 / 9335 / 296apart (n0 on its own)none (a finality fork, F21)1 on n01
offno certificates (min_daa never)0 / 0none / none330 / 301one sink on all threeA's (the heavier)00; B's nodes re-determined 2 indices onto A's chain; n0 reconnected 36 s after the heal
on, split 150 sv3 from checkpoint DAA 00 / 2none / 39326 / 313apartnone3 on n02 (n0 reconnected 66 s after the heal, A's chain past the 120-DAA table by then)
on, split 90 sv30 / 2none / 3278 / 265apartnone3 on n02 (n0 reconnected 6 s after the heal, A's chain at about 58 DAA, inside the table)
-

Reading (the NEW finding, ledger C4). With the module off GHOSTDAG alone converges on the heavier chain and the losing side's records re-determine (F24 works when the chain moves). With the module on the overlay holds during the split (A, with 30% of the frozen table, locks nothing; B locks 7 and 8) and then fails at the heal in the shipped node: B's certificates for blocks off n0's chain are "kept pending until the chain decides (no lock at this index)", n0's chain never decides because GHOSTDAG keeps its heavier tip and nothing turns the certificate into a fork-choice constraint, and once n0's last lock (index 7, DAA 209) is one window old (DAA 329) the frozen table stops applying on A's chain ("no frozen table (no lock on this chain inside the window)"), A's two keys are 100% of A's own window (B's post-cut blocks are red there) and n0 locks 10, 11, 12 alone; B's certificates for 10 and 11 then log CONFLICTING on n0 (n0 log, 16:27:04 to 16:29:54 UTC). A finality fork from a 96-s honest partition, no attacker, table intact at the heal; the 150-s run and the v2 control end the same way. The spec's fork choice ("GHOSTDAG among tips through all certified checkpoints", 3.5) is therefore implemented only for certificates over blocks already on the node's chain. Fix named in the ledger entry: verify an off-chain certificate against the table at its own block and let it constrain fork choice (a certificate-driven reorg), then re-determine. Raw: scratchpad fud-a/c4-results-*.md, node logs c4-on90-tmp/, c4-v2-control-tmp/.

+

Reading (the NEW finding, ledger C4). With the module off GHOSTDAG alone converges on the heavier chain and the losing side's records re-determine (F24 works when the chain moves). With the module on the overlay holds during the split (A, with 30% of the frozen table, locks nothing; B locks 7 and 8) and then fails at the heal in the shipped node: B's certificates for blocks off n0's chain are "kept pending until the chain decides (no lock at this index)", n0's chain never decides because GHOSTDAG keeps its heavier tip and nothing turns the certificate into a fork-choice constraint, and once n0's last lock (index 7, DAA 209) is one window old (DAA 329) the frozen table stops applying on A's chain ("no frozen table (no lock on this chain inside the window)"), A's two keys are 100% of A's own window (B's post-cut blocks are red there) and n0 locks 10, 11, 12 alone; B's certificates for 10 and 11 then log CONFLICTING on n0 (n0 log, 16:27:04 to 16:29:54 UTC). A finality fork from a 96-s honest partition, no attacker, table intact at the heal; the 150-s run and the v2 control end the same way. The spec's fork choice ("GHOSTDAG among tips through all certified checkpoints", 3.5) is therefore implemented only for certificates over blocks already on the node's chain. Fix named in the ledger entry: verify an off-chain certificate against the table at its own block and let it constrain fork choice (a certificate-driven reorg), then re-determine. Raw: scratchpad fud-a/c4-results-*.md, node logs c4-on90-tmp/, c4-v2-control-tmp/.

+

5 October 2026 (evening), EVM transaction relay: three nodes in a chain, every transaction sent to one end included by the other two miners (execution and networking engineer)

+

Until this change the node did not relay EVM transactions to its peers, so a transaction sent to one node was only ever included by that node's own templates (this file, "5 October 2026 (afternoon), live devnet: real transactions": 3,794 transfers, all in the Apple M5 Max's blocks; execution-layer ledger item 9). Fork branch tx-gossip (worktree vendor/igneum-node-txgossip, from release-0.3.6 a24ab01a, commit e242acd0), main repo branch tx-gossip. Design in docs/design/execution-layer.md 1.4 "Relay"; the hand-out cooldown of its 10.2 table is gone with it (row "Mempool hold").

+

What was built. Three p2p messages after Kaspa's own transaction relay (protocol/flows/src/v10/txrelay/flow.rs): an inventory of admitted hashes, a request for the unknown ones, one answer with the raw bytes (protocol/p2p/proto/p2p.proto, payload numbers 72 to 74). Two flows per peer (protocol/flows/src/v10/evmrelay.rs), a pump that announces the mempool's admitted hashes every 250 ms, a sink trait the execution layer implements (kaspa_consensus_core::evm::EvmTxSink, igneum/exec/src/service.rs EvmTxRelaySink), and the mempool's side: every admitted hash queued for gossip, executed, evicted and invalid hashes remembered (65,536) so a second announcement is not requested, a 50,000-transaction cap, and the hold on block-added in place of the 4-second cooldown (the executor subscribes to consensus BlockAdded; a transaction leaves the templates when any DAG block carries it and comes back if a chain block skipped it). Limits per peer in the table.

+
LimitValueOver it
Hashes announced to us, or requested from us2,000 per second, burst 8,192the surplus of the message is dropped (the sender paid as much as we did)
Hashes per inventory or request message4,096disconnect
Bytes per answer / per transaction4 MiB / 128 KiBdisconnect
Transaction failing a state-free rule (malformed, signature, chain id, type 3 or 4)disconnect, hash remembered
State-dependent refusal (nonce more than 16 ahead, fee cap under the base fee, funds, 64 queued per sender, pool full)dropped quietly, hash not remembered
+

Protocol version. 13 to 14. An Igneum node drops a connection on a payload it cannot decode (protocol/p2p/src/core/router.rs route_to_flow: prost leaves the oneof empty, the router returns "empty payload", the connection closes), so the three messages go only to peers that advertised 14 or later, exactly as the finality (12) and proof-record (13) messages did. A 14 node registers the 13 flows for a 13 peer and never announces to it. The consensus params digest does not cover the protocol version: a scratch node on the devnet profile from the shipped 0.3.6 binary (target-036) and from this build printed the same digest, 9409dedac4bf9f0f20a54fb169b52a75a2903364909fe9ffd6fc5cdcd9d95d38, so a 14 node and a 13 node still peer. Rollout: during the mixed fleet a transaction reaches the 14 nodes connected to the node it was sent to, and whatever a 13 node mines carries only what its own RPC received, as today; the relay is complete when the last miner is on 14. No fresh chain, no activation height.

+

Unit tests (PC 2, job build-20261005-173606, igneum-exec 15 of 15 in 0.01 s, kaspa-p2p-flows 33 of 33 in 0.19 s, 24 s for both): the pool queues an admitted hash for gossip once and answers "known" for the duplicate; wrong chain id, a signature above the curve order and truncated bytes are refused and remembered by hash, a nonce beyond the gap and a fee cap under the base fee are refused and not remembered; a transaction stays in every template until a block carries it, is held then, comes back when a chain block skips it and leaves (hash remembered) when one executes it; the wire messages round-trip through prost and the router's payload type, hash lists of the wrong length or over 4,096 are refused, the per-peer bucket grants the burst then the rate. The first PC 2 run of the suites (build-20261005-173013) failed on the signature case: a flipped low bit of s recovers a different signer (a funds refusal), not a fault; the test now sets s above the curve order. Found on the way: the kaspa-p2p-flows test target had not compiled since M20 added the epoch-seed headers to the pruning proof messages (ibd/proof.rs tests), fixed in the same commit.

+

The 3-node run (tools/txgen/relay-net.mjs, new; this Mac, load 7 to 8 at the end of the run after the other agents' harnesses finished, every number a count or an inclusion latency, not a timing of the node). Fast-time profile (infra/fast-time/override-60x.json, proof of work skipped), ports 29700+, data /tmp/igneum-txrelay. Chain A - B - C: B dials A and C (a harness node that dials accepts no inbound, and --connect takes one address per flag; both found by the first two runs, which are not numbers). A mines nothing. One vmine on B and one on C at 0.5 blocks/s each, paid to throwaway keys made for the run; B's rewards funded 16 generator wallets (2 IGN each) 33 s after start. The generator (tools/txgen/run.mjs) sent to A's EVM RPC only, 2 transfers a second for 120 s, so every inclusion is by a block B or C built from a pool the relay fed; C is two hops from A. Result files docs/benchmarks/evm-relay-2026-10-05/{relay-report,txgen-summary}.json.

+
Measured, 3-node fast-time run (17:49 to 17:52 UTC)Value
Sent to A / included / pending at the end / failures240 / 240 / 0 / 0 (0 nonce retries, 0 deferred, 0 throttled)
Included per second over the send span1.98 (target 2)
Inclusion latency p50 / p90 / p99 / max1,545 / 3,058 / 5,033 / 6,017 ms (mean 1,859)
Chain blocks in the window / executed transfers / skipped copies149 / 256 (240 transfers and 16 funding) / 0
Included by miner B (one hop): blocks / with transactions / executed79 / 59 / 151
Included by miner C (two hops): blocks / with transactions / executed70 / 42 / 105
Pool depth, sampled every 5 s on A, B and Cequal on all three at 27 of 27 samples (0 to 6 pending), peak 6
Sinks agree at the endyes
First funding transfer, sent to A, included2.0 s after the send (block 37, mined by B or C)
+

Reading. Every transaction given to A was mined by B or C within 6 s, two thirds of them within 3 s, with no skipped copy: the hold on block-added kept B's and C's parallel blocks from carrying the same transfer. The afternoon run on the live devnet, through one node with the cooldown, had p50 40.7 s and p90 110.8 s with 50-s quiet stretches; here the 1.5 s p50 is one fast-time block plus the relay and the executor's lag. The pool depth matching on all three nodes at every sample is the convergence. Not measured here: a transaction flood above the per-peer rate (the bucket is unit-tested only), a 13 peer in the fleet (the digest check and the version gate are the evidence), and the hold's 30-s expiry on a block that never reaches the chain (not seen in 149 chain blocks).

+

Commands: IGNEUMD=vendor/igneum-node/target-txgossip/release/igneumd IGNEUM_MINER=vendor/igneum-node/target-txgossip/release/igneum-miner tools/lock/with-lock.sh run node tools/txgen/relay-net.mjs --rate 2 --duration 120 --wallets 16 --fund 2; the Apple M5 Max binaries from the fork worktree with CARGO_TARGET_DIR=vendor/igneum-node/target-txgossip cargo build --release -j 4 -p kaspad -p igneum-miner --features kaspad/igneum-pow under the build lock (an APFS clone of target-036, 2 min 15 s to clone, 5 min 06 s to build); the suites with node tools/build-job.mjs run --target 1ccfe586 --node vendor/igneum-node-txgossip --targets linux --node-tests "igneum-exec kaspa-p2p-flows" --no-app.

Generated from the repository at build time. Times are UTC. Machine names are model names.

diff --git a/site/downloads.json b/site/downloads.json index 6bf80dae..772ab00a 100644 --- a/site/downloads.json +++ b/site/downloads.json @@ -3,11 +3,11 @@ "files": { "miner-hive": { "alias": "/public/igneum-miner-hive.tar.gz", - "file": "igneum-hive-0.3.8.tar.gz", - "path": "/dl/public/igneum-hive-0.3.8.tar.gz", - "sha256": "cc2fcbcaeab799364f41d613de52960337f703070c742d0df35961484002f927", - "size": 24831127, - "version": "0.3.8" + "file": "igneum-hive-0.3.9.tar.gz", + "path": "/dl/public/igneum-hive-0.3.9.tar.gz", + "sha256": "7a58a30fd47c9ecb3d4aeaa0c0a464550f7e4b2b33eacf87512f1b72164c829e", + "size": 24179978, + "version": "0.3.9" }, "miner-mac": { "alias": "/public/igneum-miner-mac.dmg", @@ -34,5 +34,5 @@ "version": "0.1.4" } }, - "updated": "2026-10-05T17:39:01Z" + "updated": "2026-10-05T18:26:47Z" } diff --git a/site/index.html b/site/index.html index 2788cd4e..4d816dcf 100644 --- a/site/index.html +++ b/site/index.html @@ -662,7 +662,7 @@ pre{margin:0;font-family:var(--f-mono);font-size:13px;line-height:1.6;color:var( - + + diff --git a/app/igneum-app/ui/view.test.mjs b/app/igneum-app/ui/view.test.mjs new file mode 100644 index 00000000..2f1979e4 --- /dev/null +++ b/app/igneum-app/ui/view.test.mjs @@ -0,0 +1,151 @@ +// node --test app/igneum-app/ui/view.test.mjs (no dependencies; CI runs it in the site job) +// Loads the View block of app.js (plain browser JS: the file is run with `module` defined and no `document`, so only +// the pure blocks execute) and checks the words each page shows for a given state (miner-ui-2). +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; + +const src = readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'app.js'), 'utf8'); +const mod = { exports: {} }; +new Function('module', src)(mod); +const V = mod.exports.View; + +const card = (over) => ({ key: 'nvidia:0:RTX 5090', name: 'NVIDIA GeForce RTX 5090', vendor: 'nvidia', kind: 'discrete', worker: 'CUDA', vram_mb: 32768, enabled: true, state: 'mining', hash_now: 124.3, hash_avg: 120, accepted: 3, rejected: 0, identities: 8, ids: [], prepared: true, restart_in_s: 0, message: '', reason: '', power_w: 410.2, power_limit_w: 460, power_default_w: 575, power_pct: 80, power_applied: true, temp_gpu: 61, temp_mem: 72, telemetry_at: 1, ...over }); + +test('the six sections and their order', () => { + assert.deepEqual(V.PAGES.map((p) => p.id), ['mine', 'prove', 'rewards', 'node', 'updates', 'settings']); + assert.equal(V.page('node').title, 'Node'); + assert.equal(V.page('nonsense').id, 'mine'); +}); + +test('a GPU row: name, kind, the numbers where known, the switch', () => { + const r = V.cardRow(card()); + assert.equal(r.name, 'NVIDIA GeForce RTX 5090'); + assert.equal(r.kindWord, 'Discrete'); + assert.equal(r.integrated, false); + assert.equal(r.on, true); + assert.equal(r.word, 'mining'); + assert.equal(r.tone, 'on'); + assert.equal(r.hash, '124'); + assert.equal(r.temp, '61 °C'); + assert.equal(r.tempTone, ''); + assert.equal(r.power, '410 W'); + assert.equal(r.cap, 460); + assert.equal(r.meta, '32 GB · 3 blocks · next program ready'); + assert.equal(r.canToggle, true); +}); + +test('a GPU row: temperatures turn amber then red; unknown numbers are empty', () => { + assert.equal(V.cardRow(card({ temp_mem: 92 })).tempTone, 'warm'); + assert.equal(V.cardRow(card({ temp_mem: 96 })).tempTone, 'hot'); + assert.equal(V.cardRow(card({ temp_gpu: 93 })).tempTone, 'hot'); + const apple = V.cardRow(card({ key: 'apple::Apple M5 Max', name: 'Apple M5 Max', vendor: 'apple', kind: 'apple', worker: 'Metal', vram_mb: 131072, power_w: 0, power_default_w: 0, temp_gpu: 0, temp_mem: 0, telemetry_at: 0, hash_now: 7.4, accepted: 0, prepared: false })); + assert.equal(apple.kindWord, 'Apple silicon'); + assert.equal(apple.hash, '7.4'); + assert.equal(apple.temp, ''); + assert.equal(apple.power, ''); + assert.equal(apple.cap, 0); + assert.equal(apple.meta, '128 GB unified'); +}); + +test('an integrated GPU is shown as integrated and off, with its reason', () => { + const r = V.cardRow(card({ key: 'intel:1:UHD', name: 'Intel UHD Graphics 770', vendor: 'other', kind: 'integrated', enabled: false, state: 'off', hash_now: 0, reason: 'integrated GPU: slow and shares the machine memory', power_w: 0, temp_gpu: 0 })); + assert.equal(r.integrated, true); + assert.equal(r.kindWord, 'Integrated'); + assert.equal(r.on, false); + assert.equal(r.word, 'off'); + assert.equal(r.tone, 'off'); + assert.equal(r.hash, ''); + assert.equal(r.sub, 'integrated GPU: slow and shares the machine memory'); + assert.equal(V.cardRow(card({ kind: 'unknown' })).canToggle, false); + assert.equal(V.cardRow(card({ state: 'restarting', restart_in_s: 7 })).word, 'restart in 7 s'); + assert.equal(V.cardRow(card({ state: 'faulted' })).tone, 'bad'); + assert.equal(V.cardRow(card({ state: 'waiting' })).word, 'waiting for the node'); +}); + +test('the big button: start when paused, stop when mining, disabled with no card on', () => { + const m = (over) => ({ state: 'mining', paused: false, cards: [card()], ...over }); + assert.deepEqual(V.toggle(m(), { synced: true }, {}), { label: 'Stop mining', sub: 'mining on 1 of 1 card', cls: 'stop', disabled: false, act: 'pause' }); + assert.deepEqual(V.toggle(m({ state: 'paused', paused: true }), { synced: true }, {}), { label: 'Start mining', sub: 'paused · the node keeps running', cls: '', disabled: false, act: 'resume' }); + const none = V.toggle(m({ cards: [card({ enabled: false })] }), { synced: true }, {}); + assert.equal(none.disabled, true); + assert.equal(none.act, ''); + assert.equal(none.sub, 'switch a GPU on below first'); + assert.equal(V.toggle(m({ cards: [] }), { synced: true }, {}).sub, 'no GPU this app can drive'); + assert.equal(V.toggle(m({ state: 'waiting' }), { synced: false }, {}).sub, 'waiting for the node to sync'); + assert.equal(V.toggle(m(), { synced: true }, { quitting: true }).disabled, true); +}); + +test('the node words: synced, syncing with a percentage, failed, a blocked clock', () => { + const n = (over) => ({ state: 'synced', blocks: 135200, headers: 135200, peers: 3, daa: 140000, last_reading_age_s: 4, message: '', restart_in_s: 0, ...over }); + const ok = V.nodeWords(n(), { severity: 'none' }, ''); + assert.equal(ok.word, 'synced'); + assert.equal(ok.tone, 'ok'); + assert.match(ok.line, /every block/); + const sy = V.nodeWords(n({ state: 'syncing', blocks: 50000, headers: 100000 }), { severity: 'none' }, 'about 3 min left at 300 blocks/s'); + assert.equal(sy.word, 'syncing'); + assert.equal(sy.line, 'about 3 min left at 300 blocks/s · 50,000 of 100,000 (50%)'); + assert.equal(V.nodeWords(n({ state: 'failed', message: 'igneumd could not start' }), { severity: 'none' }, '').tone, 'bad'); + const blocked = V.nodeWords(n(), { severity: 'block', skew_s: -75 }, ''); + assert.equal(blocked.tone, 'bad'); + assert.match(blocked.line, /clock is off by 75 s/); + assert.equal(V.peersLine(n({ peers: 0 })), 'none yet: looking for the seed node'); + assert.equal(V.peersLine(n({ peers: 1 })), 'one other node this one talks to'); + assert.equal(V.heightLine(n({ state: 'syncing', blocks: 10, headers: 500 })), 'of 500 headers seen'); + assert.equal(V.heightLine(n()), 'blocks this node holds'); +}); + +test('the next consensus switch is the first height above the DAA score, in plain words', () => { + const sw = [ + { key: 'fees_v1_activation_daa', name: 'Fees v1', daa: 210000 }, + { key: 'difficulty_v2_activation_daa', name: 'Difficulty v2', daa: 33000 }, + { key: 'finality_v3_activation_daa', name: 'Finality v3', daa: 135200 } + ]; + const next = V.nextSwitch(sw, 140000); + assert.equal(next.name, 'Fees v1'); + assert.equal(next.away, 70000); + assert.equal(V.switchLine(next, 140000), 'Fees v1 at DAA 210,000: 70,000 blocks away, about 19 h 27 min at one block a second.'); + assert.equal(V.nextSwitch(sw, 20000).name, 'Difficulty v2'); + assert.equal(V.nextSwitch(sw, 300000), null); + assert.match(V.switchLine(null, 300000), /Every planned switch is behind this node/); + assert.match(V.switchLine(null, 0), /^A switch is a planned rule change/); + assert.equal(V.nextSwitch([], 5), null); +}); + +test('the prove words follow the switch, the setup, the node and the status', () => { + const pv = (over) => ({ enabled: true, available: true, setup_hint: '', backend: 'cuda', status: 'idle', message: '', current: '', verifier_mode: 'command', pool_entries: 4, pool_verified: 4, pool_failed: 0, ...over }); + assert.equal(V.proveWords(pv(), false, true).word, 'off'); + assert.equal(V.proveWords(pv({ status: 'setup', available: false, setup_hint: 'proving needs the WSL2 setup' }), true, true).word, 'needs setup'); + assert.equal(V.proveWords(pv(), true, false).word, 'waiting'); + assert.equal(V.proveWords(pv({ status: 'proving', current: 'block 59199 shard 0' }), true, true).sub, 'block 59199 shard 0'); + assert.equal(V.proveWords(pv({ status: 'submitted' }), true, true).tone, 'on'); + assert.equal(V.proveWords(pv(), true, true).word, 'idle'); + assert.equal(V.verifierWords(pv()).word, 'verifying · pool 4/4'); + assert.equal(V.verifierWords(pv()).tone, 'ok'); + assert.equal(V.verifierWords(pv({ verifier_mode: 'off', pool_entries: 0 })).tone, 'warn'); + assert.equal(V.verifierWords(pv({ verifier_mode: 'off', verifier_reason: 'the WSL2 prover is not installed' })).needsSetup, true); + assert.equal(V.verifierWords({}).word, 'not read yet'); +}); + +test('the dev-fee lines name the share and where Settings turns it off', () => { + const s = (on, fee) => ({ settings: { dev_fee: on }, mining: { fee_total: fee }, dev_fee: { on, percent: on ? 1 : 0, address: '0x1234567890abcdef1234567890abcdef12345678', line: '' } }); + assert.equal(V.devFeeLine(s(true, 12)), 'One block in 100 pays the miner software’s dev fee (12 so far). Settings turns it off.'); + assert.equal(V.devFeeLine(s(false, 0)), 'The dev fee is off. Every block pays this address.'); + assert.equal(V.devFeeText(s(true, 0)), 'Dev fee 1% (1 block in 100) to 0x123456…5678'); + assert.match(V.devFeeText(s(false, 0)), /^Dev fee off/); +}); + +test('the remote-jobs line and the helpers', () => { + const title = (j) => j.title || j.kind; + assert.equal(V.jobsNote({ allowed: false }, 1000, title), 'Off: nothing runs here until Settings allows remote jobs.'); + assert.equal(V.jobsNote({ allowed: true, url_set: false }, 1000, title), 'No jobs address in this build.'); + assert.equal(V.jobsNote({ allowed: true, url_set: true, active: true, id: 'job-3', kind: 'build', title: 'build' }, 1000, title), 'Running build (job-3).'); + assert.equal(V.jobsNote({ allowed: true, url_set: true, checked_at: 940, queued: 2 }, 1000, title), 'Nothing running; checked 1 min ago; 2 queued.'); + assert.equal(V.shortHex('0x2b1a81cb413236cf063077b46ed3111628f6c41036bcf6e23ee4cbbf5679ef7a'), '0x2b1a81…79ef7a'); + assert.equal(V.shortHex('0xabc'), '0xabc'); + assert.equal(V.withCommas(1234567), '1,234,567'); + assert.equal(V.compact(2500000), '2.50M'); + assert.equal(V.rel(90), '1 min ago'); +}); diff --git a/app/mac/IgneumMiner.swift b/app/mac/IgneumMiner.swift index 8f92680c..fbd48cf5 100644 --- a/app/mac/IgneumMiner.swift +++ b/app/mac/IgneumMiner.swift @@ -116,7 +116,7 @@ final class App: NSObject, NSApplicationDelegate, WKNavigationDelegate, WKUIDele window.titleVisibility = .hidden window.isMovableByWindowBackground = true window.backgroundColor = obsidian - window.minSize = NSSize(width: 900, height: 620) + window.minSize = NSSize(width: 900, height: 600) window.center() window.delegate = self window.isReleasedWhenClosed = false diff --git a/app/windows/host.cpp b/app/windows/host.cpp index 83d08178..a05da8a3 100644 --- a/app/windows/host.cpp +++ b/app/windows/host.cpp @@ -284,6 +284,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { case WM_SIZE: if (g_controller) { RECT rc; GetClientRect(hwnd, &rc); g_controller->put_Bounds(rc); } return 0; + case WM_GETMINMAXINFO: // the dashboard lays out from 900 x 600 up (app/igneum-app/ui); the Mac window says the same + ((MINMAXINFO*)lp)->ptMinTrackSize.x = 900; ((MINMAXINFO*)lp)->ptMinTrackSize.y = 600; + return 0; case WM_ENGINE_LINE: { if (wp == 1) { g_exited = true; diff --git a/docs/plans/miner-ui-2.md b/docs/plans/miner-ui-2.md new file mode 100644 index 00000000..e64cba3d --- /dev/null +++ b/docs/plans/miner-ui-2.md @@ -0,0 +1,117 @@ +# Igneum Miner UI 2: sections behind a rail, 5 October 2026 + +the project lead, 18:40 UTC: "can any updates be made to the UI of the miner? different sections for different features. Made +super simple for users, all settings super easy and simple and everything looking gorgeous, as close to shippable +as we can get right now." Worktree `/Users/joshm/Projects/igneum-wt-miner-ui`, branch `miner-ui-2` from master +a93199a. Ships as 0.3.11 after the project lead has seen the screenshots. Times are UTC. + +## 1. What changed + +| Where | What | +|---|---| +| `app/igneum-app/ui/index.html` | One page became a rail with six sections: Mine, Prove, Rewards, Node, Updates, Settings. The welcome, GPU and address screens and the one-time key sheet stay as they were. The settings panel, the bottom bar and the proving tile are gone; every control they held is placed on a section. | +| `app/igneum-app/ui/app.css` | Rewritten around the rail, the thin top bar, the status strip and the pages. Same tokens as the site (obsidian, graphite, ember, molten, bone, ash; Unbounded, IBM Plex Sans, IBM Plex Mono). One accent. Lays out from 900 x 600: under 1000 px the rail folds to icons, the number strips to two columns, the two-column grid to one. Focus rings on every control (`:focus-visible`, the switch tracks). | +| `app/igneum-app/ui/app.js` | New pure block `View` (the words every section shows for a state), tested by `view.test.mjs`. The Notices and UpdateCard blocks, the log drawer and the blocks canvas are unchanged. `?page=` forces a section and `?logs=1` opens the drawer (screenshots). | +| `app/igneum-app/ui/view.test.mjs` | 10 tests: the GPU row (names, kinds, integrated off, temperatures amber then red, unknown numbers blank), the big button, the node words, the next switch, the prove words, the dev-fee lines, the jobs line. Added to `.github/workflows/ci.yml`. | +| `app/igneum-app/src/state.rs`, `engine.rs` | Engine addition 1: `node.consensus_digest`, the node's own "Consensus params digest" line (`digest_from_line`, tested; a peer's digest in a WARN line is never taken). Addition 2: `node.consensus_switches`, every `*_activation_daa` in the override file the node was started with, lowest first, in plain words (`switches_of`, tested). | +| `app/igneum-app/src/prover.rs` | Engine addition 3: `proving.program_id` and `proving.aggregator_id` from `igneum-prove-host --mode id` (`ids_from_describe`, tested), read once at thread start on macOS and Linux and when the WSL2 host is found on Windows, proving on or off. | +| `app/mac/IgneumMiner.swift` | Window minimum 900 x 600 (was 900 x 620). | +| `app/windows/host.cpp` | One `WM_GETMINMAXINFO` case: minimum 900 x 600 (there was no minimum). | + +The API contract is otherwise as it was: the UI calls the same routes (`api/state`, `api/cards`, `api/pause`, +`api/resume`, `api/prove`, `api/settings`, `api/update/*`, `api/jobs/*`, `api/sweep/*`, `api/key/*`, `api/log`, +`api/open`, `api/quit`, `api/clock/sync`, `api/power/apply`, `api/detect`, `api/phase`, `api/setup`, `api/start`). + +## 2. The sections + +| Section | What it holds | Where it was | +|---|---|---| +| Mine | One big Start mining / Stop mining button (pause and resume), hash rate, blocks found, next program; one row per GPU with its real name, kind tag (Integrated shown as such and off by default, the engine's rule), on/off switch (applies at once; the other cards keep their identities and caps), hash rate, temperature and power where the card reports them; the blocks strip; Activity | the dashboard strip, the Cards card, the Pause button, the Events card | +| Prove | The proving switch with three plain sentences on what proving is; state, assigned, proven, paid (IGN when paid); the verifier's state with one line; the shard program id and the aggregator id with Copy; Set up when the WSL2 host is missing | the Proving tile and the Settings switch | +| Rewards | The address with one Copy; blocks found, this run, balance; the Save your key card (two lines, Show my key, Hide) or the "you pasted your own address" line; Use another address; the dev-fee line; the wallet page | the Settings address block | +| Node | Node state, height, peers, version, each with a plain line; the chain numbers; the consensus digest with Copy; the next switch with its height and how far away; every switch with the applied ones dimmed; finality; the clock card when the clock is off | the Node and Finality cards | +| Updates | The version, Check now, Install now when a download is ready, the auto-update switch; remote jobs: Check now, the state line, the history table, the signing key | the Settings version and remote-jobs blocks | +| Settings | Per card: power cap slider with the watt number (NVIDIA; Apple silicon says it manages its own power), identities stepper, sweep line with Sweep now / Stop / Unpin, the cap-applied or Retry line, draw and temperatures; the sweep switch; start at login; remote jobs allowed; vote on checkpoints; the machine name; the dev fee stated plainly; Copy the log and Show the log, the log and chain folders; Advanced (collapsed): the devnet trust switch, the live page | the Settings panel | +| Status strip | Unchanged (Notices): updates, remote jobs, the clock, one at a time, under the top bar | the same place | +| Rail foot | Machine name, rewards address, Logs (the drawer), Quit, version and chain | the bottom bar | + +Every control in Settings is one switch, one slider or one field with one line of help under it. Nothing was +removed: the identities stepper, the sweep buttons, the power Retry, the trust switch, the live page, the machine +rename, the key reveal, the log drawer with its chips, search, ruler and jump controls all still exist. + +Empty, loading and error states: Mine says "Asking the graphics cards to report in" then "No GPU this app can drive +was found" (red) with the detect message; the big button is disabled with a reason while no card is on, the engine +is away or the app quits; Prove says off, needs setup, waiting, proving, submitted or idle, each with a sentence; +Node says starting, syncing with a percentage and the ETA, synced, restarting, failed or stopped, and the digest +box says "not printed yet" or "the node is not running"; Updates says "Not checked yet" or "This build has no update +address"; the jobs table says "No job has run on this machine yet"; Settings says "No card yet"; a lost engine +turns the pill to "engine away" and the Mine and Node words to "no answer". + +## 3. Proof + +The engine was built from this worktree on the Mac under the build lock (`with-lock.sh build nice -n 19 cargo build +--release -j 4`, rustup's cargo 1.99; Homebrew's cargo 1.69 in PATH cannot read the lock file) and run on its own +port and data directory in devnet v4 mode (`IGNEUM_APP_BIN` = the installed 0.3.9 bundle's `Resources/bin`, +`IGNEUM_APP_DATA`, `IGNEUM_APP_LOGS`, `IGNEUM_APP_NODE_DIR` under the session scratchpad, RPC 27610, P2P 27611, +a scratch `igneum-app.json` next to the binary with the devnet override params, no update manifest) through +`with-lock.sh run`. The live Mac app's engine, node 1 and the observer were not touched; the test node peered with +the seed and node 1 and synced 123,559 blocks in about 12 minutes, then the M5 Max mined on it (22 MH/s, digest +1f4b4425..., the devnet's). The setup flow was walked in the built-in browser pane (Get started, the GPU switch, +Make me an address, the key sheet, Start mining) and every section was clicked through, the rail driven with the +arrow keys, and the window checked at 900 x 600. + +Tests: `node --test notices.test.mjs update-card.test.mjs view.test.mjs` = 20 pass, 0 fail; `cargo test --release +--bin igneum-app -- digest_comes switches_are pinned_ids` = 3 pass (the three new engine tests, run on the Mac +under the build lock; the full suites go to PC 2 with the 0.3.11 cut as usual). + +The PNGs below were taken with the installed Mac host's snapshot mode (`"Igneum Miner" --snapshot --url --size 1200x780`, a real WKWebView, Retina 2400 x 1560), in `docs/plans/miner-ui-2/`. + +| File | Shows | Placeholder or sample | +|---|---|---| +| `00-welcome.png` | The welcome screen (unchanged) | none | +| `01-setup-gpu.png` | Step 1, the M5 Max row with its switch | none | +| `02-setup-address.png` | Step 2, make an address or paste one | none | +| `03-setup-key.png` | The one-time key sheet | the key is zeros (`?screen=key` sample) | +| `04-mine.png` | Mine while mining: the big button, 23 MH/s, the GPU row, the blocks strip | temperature and power say n/a on Apple silicon (the engine has no reading for it) | +| `05-prove-off.png` | Prove with proving off, the verifier verifying, both pinned ids | none | +| `06-prove-on.png` | Prove with proving on: Proving (CPU), block 35507 shard 0 | none (the Mac CPU prover was switched on for 25 s, then off) | +| `07-rewards.png` | Rewards: the address, 343 lifetime blocks, Save your key, Use another address | the balance cell says "--, shown in the wallet, not here yet" (no API) | +| `08-node.png` | Node synced: height, 2 peers, version 2.1.0, the digest, next switch Fees v1 at 210,000, finality | none | +| `09-updates.png` | Updates: 0.3.9, Check now, auto-update, remote jobs | this build has no manifest, so "no update address" and "no jobs address" | +| `10-settings.png` | Settings: the card block, the sweep switch, this machine, dev fee, logs, Advanced collapsed | the NVIDIA power slider is not in the shot (no NVIDIA card on this Mac); its markup is in `setCardHtml` | +| `11-logs-drawer.png` | Mine with the log drawer open (chips, search, ruler, follow, close) | none | +| `12-strip-update.png` | The status strip with "Igneum Miner 0.3.7 is available", Install now, Later | `?update=available` sample | +| `13-strip-job.png` | Updates with a running remote job in the strip and the table | `?job=running` sample | +| `14-node-syncing.png` | Node while syncing: 59,602 of 123,559 (48%), the next switch from that height | none | +| `15-mine-syncing.png` | Mine while the node syncs: the button says Stop mining, waiting for the node to sync | taken before the blank marker in the GPU row became `n/a` (it shows a dash) | +| `16-narrow-900-mine.png` | Mine at 900 x 620 (the installed host's minimum; the new hosts say 600): the icon rail, two-column numbers, the power column dropped | none | +| `17-narrow-900-settings.png` | Settings at 900 x 620 | none | +| `18-narrow-900-node.png` | Node at 900 x 620 | none | + +## 4. Shippable now, placeholder, follow-ups + +Shippable now: the six sections, the rail, the strip, the drawer, every setting, the three engine fields, the +hosts' minimum size. The Windows host line is untested on Windows (one `WM_GETMINMAXINFO` case; the 0.3.11 cut +builds it on the runner as usual). The NVIDIA power slider, telemetry and sweep lines on Settings and the +temperature and power columns on Mine are rendered from the same state fields the old tiles used, but no NVIDIA +card was on this Mac, so they are unseen in these shots: the PC screenshot is the first thing to look at after +the cut. + +Placeholder: the balance cell on Rewards ("shown in the wallet, not here yet"). + +Follow-ups (engine work, not built here; the budget was three small additions): + +| Follow-up | What it needs | +|---|---| +| Balance on Rewards | the engine reads `eth_getBalance` for the rewards address from the node's EVM RPC every 30 s (`update.rs` has the curl shape) and puts `address.balance_wei` on the state | +| "Pause while I use the machine" | does not exist in the engine: an idle-input reading per platform and a pause/resume on it; the Settings switch is one line once the field exists | +| Log export as a file | today Copy the log puts the last 2,000 ring lines on the clipboard and the folder path is shown; a `/api/log/export` that writes the ring to the log folder and reveals it in Finder or Explorer needs a `platform::reveal_path` | +| Temperature and power on Apple silicon | the engine reads no telemetry for Metal; `powermetrics` needs root, so this stays blank unless a non-root source is found | +| A per-card "pause this card" without restarting the others | exists already through the row switch (only that card's worker restarts); nothing to do unless the project lead wants a timer | + +## 5. The branch + +| Commit | What | +|---|---| +| 4d35201, amended | the UI pass, the three engine additions with tests, the hosts' minimum size, this plan and the 19 screenshots (the blank marker in a GPU row is `n/a`, not a dash: copy law) | diff --git a/docs/plans/miner-ui-2/00-welcome.png b/docs/plans/miner-ui-2/00-welcome.png new file mode 100644 index 0000000000000000000000000000000000000000..ca9b69560cb76422129d39bf70cd5dd2577e557b GIT binary patch literal 324036 zcmeGEXIN9|_6H0Df?xqer6?dODk1_RO*)EzbfrrR!YDN$y(IxerGtec9ciHlB!IMl zN>Mt5P(qVVLWhKs0C_fZ&iv1-XPlY$%X2*+j@N|=;odvzUiVtR)pzK9ZB@pjXOB`* zQ8B9ByQ@n@b;Od2>M#exVc-*P_YDEyh1yM5^$t~WCl?O*#rv_b+7m4;s%yY|1}a+W zvs82kOMoAFYR>d^0>(@;?b+fmW}dyO{mdhqWR@N;m@zg`ceQ~!H4@L4*| zAFGd8rXTv_{b7!S%aT7bl)R(O`d4Tl^o=!eHsc{Kut# zLwvd#HFe>Y34PvwMmRw9*57gBk8l1T(LeD1f1LhNhyEuX|L=*%yWZdAFh6|Vk7x16 zpR|r?CW`TErOESZrAQZTC*FK)pD632i^nCzOq%HE=%B#`_W~6C1BnX*aF2!lY~VG! z++iSF+`$lzDKKIM2^bY$EhZu+Fi&ZHXWJ7hVYgUgF8@8Wi1D6yR4`OFmvu(;3EO*t z3k6S3V=i=?1Txqtb2v1G&Di03FE?%UZ+7n#9kKM-my=NXJ0!gEgofqAH9;o5qZS=n z=p3zrh)gb$v3X%*ndcI=H(%Dl*J%DB$RxYPu79~Uw%QM>HTI-Dd3LS1iam1d%$ZLU-- z)R%OdEwoDN=CGqJWfSzx8BBc&iH0ny@CAr_Zu%}O20nbNSH0cvFniiYy=Bkwp-Dx> zAwE0xUvBYtLOAYPw#M+=O+`xTh(2k*s(yFX9@m>~SaY_q&5ma3;d#TRL^(gt%OT}f zO<~BV6K}Iu=ESFb2CZ*OP#20k8nA;Oh0Ht_?JYIc7R>Qln<_x3?fELhwK27S&Eakx z%h~oJySG{r8l}*oY7d8SjH^|_ZNONcGCf~$CBk6oidLeTBQL34R43LDX)0aHhneir zNtFPTT1-ZAW*OYG8^~UXabx9s$kq|B1W~CXA#fmR!$vU3;P%?{zp`+FQKyYdET?)~ zjgoRwQ&W+yGhs;YH8;a=xUVu`-O;NjOiwi9jo$L<2o=3#EwJj~M-3Pi8jnSC9qJr9 zYJBL!Ej3GT8dXPWsg#|t1Up?^a46o|+)ns&9?#ACQcgx{W5 zw0s(9Oy{sXveV?5*%mLXp(97!{{Et7x;0BE0d9BFHoT$m8$T4hk`VL~k%4A-qMUGb z;jeuuML0uatC19hIN}!m93ef^oJb!iO~ZnWSlIyphc%6Q?P2#)FqL6t{h@aMyEJK| z)+$)A)820eF`*v-LH{eUaevnEjE<9im`R0-_Zf%7Kr!yQUBV%18pflITM(v4O>9|f z3EdsQy(Pz?ZalAl7m%n?SNkvEyFO!a5g<08)f!t4sSzRmQqFQ*| z8Bdu9e0UYkzhRE1SYV{&n5-56|3zk17mJKbAsLM0-bYT@1}Q?=8g1@CBpPk*N}d}% zBeT#f1MU=Ld1qi!i(l~T(%gVc@T7-T_cXgGn8w8wfxneV7r%ZeYLip9H72lBK5)+N zZKR~yF^R-936qlacXOX#bmiEv32Ao8mfaA%E;qKmV2Xxr$#uINzH;}^&C>73YFT@v zJynjcir>vfDkP_1fGqJu{CK#sUW|M5QMQMbs>vg-Fu;p}iF zW`%2VYmIL)aC+#NEf3Fbw?a-C3fdX4#O1ZGu|59lJ0nWkjVh-ne~4v(KgEl zR!$m)IhGL(aY#Ld6sIcOOTueriW1f>I-W@Y!U`J(*J%09$&4j-?vmos9tx=I%8GW# zD4&hlY|8d@i)uklMjCm!a&o{nAdS#`D(v#jV6>5rT~bf(+^<#5J+Wb=Y9nlXs>q}~ zABh=k)lN#?IdQ(5CUDy^q$|Yu>1BD*YTStid`=CxSnC*9DQ=qZ-rR&IE=(eG$w_W( zZBs#eAdW^4p1k`~3`!!z-B?7Tqnhyd+4tt@ahHXM>Mn!(9j4z6oQsW8Sbpc*Wa7E^ ziJphRPA7=ws(mk5F(}LJIsYk$o#T7!gf&3oepf$+HQZhM{Tv(xb&vn<{(cZcfRkJ z6CqE(KaJAUK5MZvfh@ZIORvGQkao769|0@(CFUcabl^+RzJwh)@s5gS?F>sAYw1x# zh^Kqog#e787gjduB%t%j|JL~m;q$CuRh}LrNiIFOwibxo%HHc>0lhm6=bR{ZPTRo{ zs;8pCexM|0)I-vs0!43_{Ed5r(h#(h*CKJ0%dNeKF__k-Bl#hAe|=seN(KE(><%nt zc_(V))U)>Caa;2fD&NbBmUoU!cZKZ^iM&5NK5a=?lK5P&NQU;`ioVI0nGro-z!`c67KKSXlSK1&l@&`bwzA1hIGAhxpL8VZ9e+I2$~yJ3qSS-Sg6SvGPZdir+Vz7J93R3+Su|q z?JJYo{nPsUIL;O!_cn*K>nYvzy)E+l-&6|33_6b*Yn6%{;Jybj-7|GAH}EBtwj3_v zF};88K#eTC0)M@u2Xs%`-x(Z-&a}p!@;G`Z%8x|A;fkE04ldxPFjm}RS(o;LMtxb4 zyeHNUPRKW^yS9fns#mWZ^K~F*!@5B0Z{oA!QG{%}VQ{5Z*^>_SXnB&Y@BI7VUXKCu zc*7zS48oBZ-XSr3y>6d~K*-1T7e0KO>OC1sFvfZ5)$c7i>qUTbr?TE^s~=;#&XIr5 zrD%)`FL58<&?&Q$%p^UZj@+3|f*%0dWsSJgOd`=B3TdEe#n&Ne@Nv9(@1tPuIIEg! zw@xXWLlpeF-*S}=)@vM!eyby&u)pO`sdUYY-4Z!F>VT;04F~N`B0X?hy>4@zTF2Oh zH0#NuE4|VFyHiNW&boSixy~4|*h`!sv*@jJ*AJsAFn0pX4cne*@^dNZUAZ+-Y@U<~ zBjE@eX_VTJ%$U6jvJd1*d$N>3tb!6-MNN%}GlJZ3H=lke9re46xRmQs2qjaRBv?F^nS?qPiRs%lv&yR zOyFCAnEImm`C{cu;F5jIe*3#RXuC9-Or#hd|FTumbe^(1RMWe|ZuSlaT>y7TJg6Mo zV-~BRnW^H<)EkW}hn;FNERZ`L&M>#`@7m8F=eN8f zhgj-zu_Q|^6qbdxK#De9lE4*eEYta3$HjwvJr>LrCZ3-Pd#WE3-q6JxEvx%H-g6wf zbs9u}&fAi-Sy^xXyf=TinzBzuueI=iIBF+Z5m%Qzh{RpliV~UGch+GNh`LQIq}tmN z1C3~olE&v=Ji*pAWzj409qSq2w-ZyU_ zKaynZo&6SY6tz(`vl3Q;0%<0q_SA**1#hFLqM+a}=$h}U?Ai4@4Z)-r#rXoNc=B@1 ziW(0om>EvlMNrfLn8(@GL$~sTZhqg`jvt#q$cmY}%wP^#IAgwTvrpUXFN~ufv?SlZ z)Mtuu@{nPcBivgP?wEJT&Ez9y`K{FW=Uz1TUn;JF>8Y2l$Y*`|##jI7D^))~U)`gx zB$u_EB>xnPwe@(6ApDoeplDgkpqVBw+Qd_UFu|l^5R+c!EQLeP^?RfjMppYQkWj=S zrd#LtCfV!n#hbaR)t}cWh`4y;^i+sQ-{BHGv9#r!-|2}0<0bxQhW^`Mc*3r++zO4& zyNKL~tE{~YU2*_%e#kQxSvUsTzLV`lYkLuSx+QHAn5Z?Xm%AbOLF;J2S(dGJ02i!WP@6m= zmH4T;6{vIAceEF3BgN`+3Em+{jt9pzwpmfhI#D3AQY#9jkjD|%H9vg2l9Z*eKcyfv zro(crX04f%p7TyySIb^R_d`hBbr1?UvlIuFV;~dK+7MRb5DGTB*23hC!tn8S zZ=kkNHJA=VZ1@Q*Z}cu|-M56VX4yo$h1qj-Ck6%GtnxVH$zSww^F!SxSIJ1Y=WsdU z%SJeTts(ToYgm4ppY|^uC=Q1}hLcS!^-1V0G5>-@_lF0v9L*#=Or1AbxlX? z7bMoDD~?7O6nFwqe{mXpFU9Q2XJwaU0CHnj4Qivh)sp@2d2n4v>yNmcq-Na9s9eqq z3C4L>D;Sfd+@4_lDSMpxGHfrb46<((*RHqrUOek%^x4S82}or$^+Q<>hY230Kl)lZ zg#p}z*J45D%K@n^z2vS(Mc9QM;=N^S0$TBZO{44)b2jKnFx*c1Q|A*1-tBNT@4&& zLulv}o5f3{jd>fc70(T@xk1)O-Wy%Xe;Fd5fzXy?lA3OK>CZtzbR{Pm7IQ&(rUN7P zSA$#7A}#e7z*S9bhs>9~oN}Q)WLB=pCTH~m=cWCO(U(^rpa_BW`}Meta3Qc)>Vehx{(=ztiSE;?lx8e13nsMZqc8WHN(oh%7kQTeX6L>DED!NCzw({bot?xn1o5y-* zkf@fK?`>D%^mJ&RmctRHS!m4D=LUSg@QA+;Kzbf1&$5#<{m`-@-GE-x)Q6b`dB zoL!Jfr??0|CF(h#dBA9Iss+%p#lv>o2-HSgwsJo6ZM2GBLpYmUd@&HK=3G7JJ3rBz z`iWW6!0TfyYCqCBTQX(0xtCd5nCF8%t&Y4oHf;5|WUbFv*A9zXN+3P@HKYE1rexZP zV&13l5R_m|>0c3+wfZ*tcI@frSH#K5%6dKj z*rMvl2-iTlt&su(5A95m5@pU`UN4KZabi+f3-DkR`1emv6xS?kD$$i#Ep%0)h~O%G zdbCZ7$LuTW!VbytjNVS*Vsn!*Vgw8ft{9qaB5rgkAe^Wc9t#i4)R8a zF~L;*#@`maJ~K4-2lykkcId->gHY?=#BqFGF<nkn}VJ*SDieJotc(CqI__m zh)Axj_%{>8IN`{Kwg}x4yGt$WtO6>!b;gNnhj?~_-DhUl+3XhHpB?tEhoWW1*ZK*O z&Z*-1r8&i;AQS4gp0j}~(3N0Z+Wz=BqmP0l{~qg?*Q#8pLN&Xs>Tr4N?)&!HrZN6mD89 zTf5GdT&>HK0&zt7SW_4Br9twAz6dbOTuO1G`i@}sNCEU6|v3j)JW^nc6U_C`Dhg&8}@#fo$Ww_q9VoikqXsHsLY&LS# z(t~Ot<}Kb?!Mmp^KBgi70{iOOveCqs>RoG$-F$YeQvl3tvL7L{+7Q%xN=Nqf`kN0* zkqWR}6Qu2f?0fsnk=qlcBuKVm3Ll^-4tCD?y52zgbH><7x8F35E9`Ics8bh`@dRUv zD_EvywhQKIP5P=TpJ+9{u^pW;DA9BK7xw;2qN0wDRDgJQ+!Nk}HXzO#aVNg+gMED_zf4sVzVZ_)Ji9HcS~Wss=KB8OkPYtkgf(4IqsE?g7%>nr$#HX)qe+OFz&Zx zv1dFV&GH_n)Hpw?r9;Xrch4pl)^eA}NVVs6OO ztzR>cy4#cgpfgRO9+#t{>y@iL6`f~YQ_tWnXk3!M%=2cBs{cNC)&Z>{vwQbm&X|*8 z6@+MAfnc8u6FeO(Ef4UPeSa`OB@979X%1ule&p@Kp-)OrmUc;rmi~mZ1?OqN4-Zc57jx^HIr~s;w|g z$H1>~Wq6&OziCncnuD#|s1*$J=q$xf$lbz zcJQ)hF|#MU8~DEIxdR%EtdP;KT7a#i39GzH2;I{PTxqMMbtq)XAzGaXD?!yGwWcE# z$W8Z0qsR`?F`AQzQ&vrRx0r5yZ@4JsztJHXIW*|}apI*F{~(aPb8BzzzSknfCE>F^ ztr8}n^b18{h$vMooQwPQ_KtF+s%>(L9lP`l`w~%P9Sj+=j-bmrc8Yh zKA#rwV%*~Bgho_ss6YO)1W%a>77gC6BPyysKQg}4>E*PFVSJPuD2s(^Qx%MU#;z~` z0p(fW(0odj^J9}+irv>bo66ru`GT1ZyfV!E*7HcF$C}MNM;?)Z72=!KPG=rdv)URLa%3;MEiAleQ63N z_^@_N^XW;fOhr;$1y8yY@?jnLQR@1ps9&!Do2ItH?w4)>=b81XJ&`!P<8;X76VVFG zgM=WS)bV?3E_$=$l&N~%!k5S;2~%dN94*PAOWaub0lYOIUwqyyjW&73-_q{Pg!$p*RzTbnVf!0tjNV0?(~CoHVc8)YJKGvjbz3lH zyMji+UL|I^cL#?7`NsQ;#QD4XGz$Z&R1U6dHA#lcnkNM~nr&!e64qe~DOC_aw(J6_g|db=9h_C3v*C&FFhMvTheINoorF zPo~D&1vDY9eF<}TFx%!A|Mpw#v1~rfF!QpUzpk|X20yMm8!Ro4-($BrzHGvqTjJXfZ80CC|G5El~y>VXvSI$lDorA~q)(XScx)m6R1>G6! zZE?an{#)<;HRqB?2i1)Q@e*|KWYDAy1o~cL4g1uZ?C%$u zeY)aVbL)G+A$;*o1BcVrA=OK=u(fc|Y3f4g)@Y@%mVF7zDjR<5<SI@*h6!^hmpOlq;x>llOh{UW0KaB00v=u0V<=Z@ACdpIlS@SjFV~ z``~l-xuM(f3v)W;l}X`Co%P>8GveE?38s; z87*h(4LR~xY~gRCIGsMeXRwSU;*Ev~fHNVq#JNvK0ufkFxDa-AFQ$!=xD6mf*|tDj zVC(TYg!jc{Hi0KiACL&>l1LMCIXrE@>gE13>vUAgFM|tZ%Z@}$6NMt@vmSH0#vh3f zY0!ACaq+o$GB19*r2Q$L5$?FSe2P*}0f7AT1+S`*af3#CZ9t^J*ev8JZCXqFXuz1( z++vmS6LY)E;Mg2-hQA^+oHeR>ibW>w?)^nRg`IEdUxppaI>H>50N>=0EkRx-*Z#j35$@Z38NbIoZ;G4Nk+~?2wK2+yGq{(^J4VwptsRL$%5chLEu5BH-ep6>|$*J)B#?L1< zu?71_9S<=|8_serI^6Paa+^!>%33I{kuY~@n#r!O-g#WgJ<)GEz?>&#jB|tfp|xu@JzxYc)O9md0gW%r%s& zIK(V_V+o1YR=%+S#74O&{Vg8?_e4Qvlyv@8KcHc%zetpg?xMFR5Mr`}5PqWzN6WA- zwYxLIIx@>2txYVj2J^2~&*jyP^bbUiwJ@-cK{h&?RzTx~97*s~VctH#Zu+rK6xP;( zOzAB!-kEM8REF)m&7?Q+wORQuVyuY8Q_%{rKBLvJ+tUZJXs@#+_vYuL#@wDtS~k-( zAm673zQYxI^t~`PtFp|)9|9W$r^2rBBK~bFPue<5k#TGdr$ZBDz zvvRYYDM?In)9I51J<{bfs9lV6U#k7Lh3!w%BP)A1_QrvXGj6kUZ+Q#-ltxy|ORSX@ z@XeAhmWZ{EGGRx7TvCh$g>V<0^$amh>IMMdnY+k)?uN3y#dDX+ji>G7(%70VUOzSbbw+Rn z1*AUN1za%>Q<2UPYm;*O)p605{>Y+zC9*MbK>)O)?n@pXPX%Hi27cwQ*HBiwrP9WQ zt*2Xj=2DEFmxQrr5~5XBtdGwnIuxs!I@fuyB$C_eciT=6UlFpX{fM;uYG^lL?j<

jp_!>l;I^JeS~rEo;&>^$YlKJy)MZdFNx|Pj+LE#thInLjZsurd z`IwrstVrD~Mip5;m*Q!EJpj{xTVH%XTi73JOkJ3tuO+wFF}jttfT(|;R~@Mzwsa_o z(NZ~NebEgya8L47ar=D*WOUPShQ($Nv3`3-%2W=ya#gBsj%z{N7$cAYDf)vo`n4)b z+T0g6zk9jUw(|k#&*R)~IT`^YA)U3FXa+QLDn!xXxynV$@`IdSir1J_2N3$sUo$)# zc0!FdSjBCjdS3SP#nL5E9c#%XGZ|R5=)PFml5g&UL%^#vC79%Q2?OKp*z3KwO?`dH$YZdoORNWJl4nCgom6>UN_r$JQ+Am4W5 zAV~OflzXO{7wQNY1_c&NQnMuLLE&j4P~!UNDm-Q4NXM~){s<4^c3Ii5*TFo#xT_Dn zyKR4qx^SZX0?O!OiFYH5YSyUdaM5o!*unU#JTQz*3i?+r=?)2+ofM3E3|Yzn|!6UQ2eo`D>xt`8V#nE-mi( z)8RgQdld3L>D7Qe{FO=7XC>$;nTRakwuNR-zj!lWAL-fhnaB4{$8H-0ttqJ#iuHF+ z0idWUIkLUs-W^D0u1QK8TP^El21UaAgGU38?$KItem16FDEVSBw6bfA`Z$}&6Tcs{ z*wD*dsmezA^zrG$OFvZf%r-&m7ZLJV){q;jyG~Dl9vXhb4?vw*uD&fI0Mo<{8ksLK z(G@a;lU9QjWNl4ZK87NERXJupaDtH!5`j);-`%wqPLraAEubv%$~t$z%S?gTr^m_P z-+%L@51z~(23m`!pXi#5 zM%oTK1mJ0~>hYt7d(sHswVr4g5|3F;t#D;3{cH$PekB5w3v}7GE=Kjh5REn}j_@<# zvW;76aGpr`9clB=yA1kAcCC7_?ix6U6}qi>?GmO@ZkO-_84tZw=sfH#)Wa zma0^TXSo?EdkT;;FMTZ%Xfezly3RhKwWbzH>qv zU3UoHx?3M#`pgUxIm426#--8bo+KC0Qu#E#qm(}o&0`#ks@-aTiAPl-o4#987k=!Y zXMuW>N?m922~l2qKBWExkQP0UyJCy>7U#IEK;0T6LWjImqhmUIYYb@MQ4;8}py98@P*kPyIHF%ReKDPNR)_)wfWyF=HP5 z&TxW*9q5E)QH8v8{{pmZF?tPgC7t{@9V(9dSE#S<$IvguH0JG!^;OaFb#EfUE>D@6 zIZTJ3GP;~@#l=2jgxBsA;h_F&bGQ}x?Ez&gl)=V6cA!fW@IsA9?Qlw_ftJK*C;k2= zPp^TInLgyv21E;21qu>TDTDwdh4N+);LB!RPo~=-_0Rq z+?N9baF8W zd%Z!C&=ZZQZ20k#UW4Rthe)m-qq!AdEFX(sZR+NZXfM#~@amv4!m_eM>Q-WgZ`E!g zdKF;X);JWIXMVx@I&2i2}1dTaIxa{Qkq zdF6!a7Q@^J3!=_qRt?T=RMv~uF42M&-p$CK{Bw1orb)R}^4qtKCU>Cq3!{d<$qw`u zUV5bw?T;@shU9_U@rsP_oa zdfOEDoSw_woAmh%NSj%H`~H{XKwzm>zdNIa3*sTQxUW7!XwQLG>y<}?&%r)UNMOxZ z-O?ylfa7OULmWKjg)JTZ;-p4C0D7zqMJfDz|3T}I)l8X6G+BM3ylZ#M4v4{}rRz3y z#>|hp%8lA$;^JyHI>)n2oonjeOZWp#Lb&1@6F&qe-uS{VySNpT=;E$FOztl>H!Tbh zf85$9v6bUPLwn_eB1EO}s4*b=Y22f(1R(8(D0Nq>40*>Fcs8(p{ILnCfL)^E#Rw-1 zb$xGls9gCN*;z~~rPAN}$g19oX@WelrrB^f|BQtyYQ(Wj>-SU>kim&Z_NZ`XM@pK! z0?Pew*@P{U*@Pexa(X1vlCyt&F$EWi+nH;#BS$@-FH!4A7UAUvWV2~A)YP%m2Wz#_ zfu2orHpFgtJ@lOnx!f%g)kye6w3mJfIJw31Q}{(4U=rkXjmdh6V?uA z^Y^O-;aLgUEBV`DbAZ}ZtQzp@w*J+(p1)y8!;)o;oKMIgAIt)!rjm{sM_`PJ8UYRS z$NtrV4)mmso<@a99kYD#EJ%?%Q1qOmqhEuRo^P*+sU|;a(uY?y6D-*qck>qr@NZ1D zjc@49*60mm73j@Udy59TK!!1qo+Oj7 zW+kcu&XsB@$;(fh4@DL|^Z45QRu3ZXDu#W+3+Df2{{MGI26`SVd{}lxZlhgKS+aC= zurDuCP`hW@#V(BsXqpY_3aBMo{=07mLy@hLLO>hcO(+HjnS=rk@9ujf)hv|kI{y@WG zV@zvBVq(PKOc2@XTJ~hT00z9~(7;gu6|Q5?OA_7nBOC6rhO8PuR$6TSHGd$<18f1Z z)d3x(VV6Z+k*ly8Em^=AhDc-x{@pp`D>_P6Kz2T;Gqia=KSk6&D;YR(q;boa3Djhx zI2PybUF2?PM_t%io%RI%B(QuzkJK7epfaL?WC2V4A|>$ewBU``bg91hwN`cGjak8a zKyfhnD_X>ph8EAzxaAE&%*7%mMH^nfp^EdO4t#VpnfMh?*iD>8>phUfKK)BoP)t8X zYeh@~CJVBGAtN!H&Ji)M<=ROfzV;3-h7(tTnG!FEbIX>uZ1jqc0Ek#8Lt8?+F3a2f zs^LvJKh%$wIzN{aeCw}?@8+ioXA{%}%hSBRCgK7X;&NIH zw<|c=l0W~Y4r($4^6TYf0lBJLB2Lnt6hDC|zMQ>=*j0)TUW07LO$ejAB{Svl3 zn-!7D|AIf>U06LO;g}_c+BM-9sp0>eD+lUe(&FIYdIxsZPY6_8HLG$}#yMmgl)lu- zKO9(bq|x4}6l3XlLE28&MVGWjTh zK**AEn~klwnUrMpI0aG&s+TXasH@J#AQ8!w|-y zHtW-B*|FYNSOo|SHsw?gPtg{iAzc6CSpdFt7k@Lbc;sYmS6=1O)au593_}dEi4{vF zvI7(IW7=B3bS8c{y=pT{-T1C?!e3g&|Da?yGJwhu?SAc-j@f@U1MsPPi3WhKeWn}! z|BRSKHh}a*ZRLNqy4qZks$h7WdiGcG!hcTcKr$8405MwVynBCpuD`FYJOdHy)RDR4jTP z`iJ5kpr}V<*kB)~b@`vc3`16*s7+@r@%*oZ@Mk7jZUgr}i%K~A&qV&jRa)ATT9$u! z_wFn}LEb*iXZa5%|8XjR_K6}@o8J#e@)tB|9R)z<2>k5N`}xK70MYdSBl`cC^FL1i zA1i);^FQ(UK{@^>9{&@MA35+p@%VuQKQib4FZ5`Lxw4S=C*fN9iTQ-@ObdM5+l>G1 zPFgwwu48jQXys>%7%0N}Zhj)q@O$}Z#gGD4uREOowIY=Ne1N#fO45V>@*`iKwpGrx z{mNvv$k&})nQo{cA^cyv4jl~a$4BS&N|6otAZ*d>@FB*0ul3)<-A>syvgPdOG;Bk0$W_a@|wIV*%p-Ro}NQ1-ysw+v2J}avKO* zqZ;l%N}51sI(ji2--NNW|1?91M@8B1xB@9Ix~gnnQF}*rBwFBYj7Y6V9D(%{N$2{CVRyT!Ap< zHK+Z54Nkv0e6+^$0`R;b`*AqU!>J{vGe18n2xw%bEx9sjF8buBZ?W$ucf9DW^P;9YPXv?q5yLi~+a zX7$f(dx1S>Bz&F5? z_A04=rc|5;bK$l2JA&mzs`fimc-NzJzXAUDg}cxzAsF0>)yB^~Y##t-$F2Sx>$_(u zG6iW8zA>I2Oqh4OxxY_yXml!+t2KXr>{K(}A87LW$(fec0K)pRW%$s~hjuv-|Gnid z?U}0~qAYWd+IiUlvA@Bh^bj-t_OmgsKgjlHG35XOyq;QsaWMY0wHq{n_*X3qiD!uU zhh8|UKu_?_Kh3C~c*{Q4;B zi1>W3rGt{#l*?`hcD8E9^1c(?ATTL)jH!Q)$$s_au>U!bmMCYfBW7FD*84f%IwUjU z_C1wBCBJ;5nrNfp&33bbpKh!OKLUszmRp?XM@qUc(_khdLvPIBU$uLmbry}P(q`)D zLlv{r%xt>W@3`EU8*y5ej4uH5rFBJDS-{NqjE4=0 z7D+aa|E!7u;4O`TKRC>X7xg^8TB_5~+aMTpZHXw4%R?C_+Aa~*UHL(|TKj}Mn8P2AmL*n% zyH%yA>(ssQ$ zVTXG+sgIWhIZ7H@eBmmd{E;yMitGoNvDP8!@#DeW7^8EgvX+f5ljysNwdM|e@Su_q z&DEHPj1`{d&~>J{(rO^YdTOcYHRo5ggorm?MrW5XdC4mv1*3&<*_p!*3$Y+XS5NMU zg-|MI>m2=3{fPGE`-c1j=wVmqpq=+yrTNA^ipx1(F+Tbah0}^<4e>^u1ZIdqiqitR z{d7uS=lddV=CZZLSm%`+1p@yy3vGLl4H^#h_(>@ljvJk)7kX*xjBGWcH&Q9+8qsM3 zA673vJ(n*;@JzH;i$!WXyXM`@+5Ig@OlRco3SEB=47Atv|;J(LZ zJslS2?&-_(%;e`75s@SJg^Y0Zz$H5a`i1Uk3yf1D104qTiJkD_&3E9;AS|x32>nPR zIZ|c#$-6K5KhNLJ-vKaCs1}!_`6D7B-roY@)U8BO9QX!(T)z=KCG1%p-TFuSwmI0f$T<4@XH=Xiv7UYlMqvXL zlJ_YOaw;h(e3exmeEvFU9-6TJF~3taErHkTt+0_0VN>snzy3Au9giIYioYRtUQSMb zXkT1)KmTT$uHFt?WG$w@{1sRQo#l}^aPlWG>J9}^D#hEVBlJg$pm`&ZE0-hsOEFVs zMDuoH=~oZe^Ox&8aS4oF@iw|K0(+Si5b)Dn-Sd($nM$!?bvmvleD&=!u661=W2dqQ zX7pzKXB7=De~dk(D;`*Ya@~m{^gCxzu7#qI4+!j9hUG?tS97mxmY{`>FW$|Wp3}~T z6&p&K>(#%GuW}Mf8g54n6W@(f5;Dy9#L!L>X9UBaH0S-ed69eBPI zS2~AtP}}>rJsd`&hpV({btZk7(x*3K8}wIb*FS5ypswoWy>cL==GHD!s!xXC!B^`$ z<>myb-RhwOZHJ6OdI(`f16;|~cc2+hhZUu9BFd&tE{4%pM{<}#QZG~LQfO(uHpO*5 zMIZSJ7-2JOg0ELsD(1oDF@9}$dHt7IQ`e8g1<(6>=_HHeH%1eCGs!nik;9hmHrfAW z#xEYATUO2E!XK45U5VD!(yCSqF8y0?XKAGSKIZmYZwotNo%EPjdCB3S)9h7N233VJtBZXVpM^lMk1Oy$h52*~V3h;L>-x$pxpwPkBU{ zNrcU8;`QxFuBh>$BHT+8GZ9Xt-@f3virF6lkSdS~C|KbGl5CIuxFgF9hUtJtz+f=F zMfz#-CioSb9+{awWe_B!J#$wRWucZqNd5ZhJy*}Sne~~muR2UzGc9I?Zy4~YeJK-_J+ zm>)Ia#Jnw_=uxP;D?d5W&B2OHcO#;EVpJb+pR*hIsB&{9*YwD%=})sU8o18^;DNZd z7rqNbf(3&1Wk!&0g4y-0cZc+LJYudIb?~-E?*Ym(0&|j#Usih?cGN|#c;=R_P`qi| zOg_15;H7Unkwg-%lpS2K6?hvvyu@keQY7~Irun%?C<|CSqSfPA_N3iXTtriF!Z^KztfWw3Ho?HTZ63L~W0EtO4lR$fgLAYM_PyntHAF|0OjFt(&>Ooyn! z78T&?tND_j^z7j4S!Sq}$6%S60Vzw2X?6ZvGn8zqY`$#JZnZE2tAU$YJ|ba6-GGq0 zWkuO{tHOG^Y3x8ilo?Uz-rUI@k%5SotFeAxXUq4tEI?@sbL(}{JMPY9<@->{iX6+l z@kvbs(I)h+w9ux!aN=^&nC*>%>Pq|*-{7G)!Mo?|-8BVqNNwJ7M~|?YpP(5Cdn$m^ zT*){6pTnuMEV)d6V!Ei5$F5hi%%Ks#6|+3gTRc7}(_o9RE%W?do)>2(0=M&}e{)Dd zs_5+@v?wXgAZhOGjQw~85nnOoo4UgA6)SQb{9g{r zMZiJ1AlWPS<9RoDvwXcBymliMf8t(23%Xs_jp|xOZa7Km_!}O+8I_Gt?6f+{B0>4DpS{-FeCKCB)qiT#n4`v=HLKp+taCQo zZ8mSoey_1k`Q!|$h^dh^x$|q2LpPM_sjgSvRflO-_wtn%|891kUe9@QL2=h2=6?Ud z>lbKS&i%SL!YJa4fcT^_K(G#IOMos>OSD?KNRx@%O^E@e+@;jI63CzXEn%DLzB?MU zMO1Z`T&L@R%i3KPzbhN#O(pE5Ns$<>y=m8fx19mr9euodJ5kyyYNP-unGc=dFq>4cAEW{rKEF z&!!^3DWfjfk~fu>W_)97wD?=7UGycjZMIJ9cv1sEm*m#L;NF9}!cPP`B)S9`@`%@4 z`uFJt_S_IXTCT56B7^L5-M8D|d zw;S0GRFzfC&s|Y3v`MAdYtLbHi8{N4DPucdHconq*=|W{VG}ycJGMT;e2zGp1?O)? zCl=2xp-%IrMiev9k!=C?Y^ZsVOSWirmp8UYmkRsat+P|nW}v^}W7)B7_nBN$8R&D) z&QextOIhqIqY}QQdxl&t_oM>1DSfBK@*jq+kHE^Z=KOipKf~_%e=i-)Sazj!tCWZ5 za>e~J%P}k86)gKSN%kL3hMV*0SY>d)fgAPTS-=fIatSo4P{>7iSxGgdxT8bC`EyR& zqNj5zVI`u2P8}~R%C*yXTx-G!w}lPbLir=gS+zB9W#=v|xXj@$SW8$)vq}x^;fJi_ z*=-lg`reaJXSYuoMfL9at$UZDc=x*K=v+B8)oyl{t9#;i z>kJmP>*23EwQ@nlx%)1b2|}4)FLlIoS2bs53O+sfj|7!J=(c^f0*a-Sh)>@B;+cWE zdiIxU7zEVp^7m6!=q^Eeb5Vrmw{Tw3L+jH_04>iw9yC$`V_A4navs2`2CPiIno`HFe9$6e+6QtRe7Jpx}R z@ETFy_+36meiWWhc~?yVkuc&~ZBBEzoDBo1y#4bkJn^0@nO$uB_!?LpETYT(>X)ol za&&TrH)_8RrILki9HwfEtrr6DWv2$9n_b+e^VffvQQW=1^@jH?F+2Z}7|83Vr;iAYPGw&T=OtHPryPIVeYHKR;<8rE#h~t= z(jJAq=^ekYC8_GatWy=>OELIffPEo=1p%e`Ze$B@$K$Sh;@au)E~xstar%#*QRnYN zi@o0J-P4q%azvQGp2WK?(DrXTquq0G!sENoLpuAp?-UkTlJ>XmHM(-3DMs5a+pWpf z+m%S8jAi5_9J3v{XDi`q?bdlXWu-l8m#F92C9xKWDrMpP!P@fL{5!KF!g2O|w*OgS zB#?W0+ae#vy7?cc^_!AkUV8zE`@g}*Xt8mo!pla}NZChH^2jiuR92nCP;D3T3e})+27?nu+fu(hCjB?e1p6Q`E8i+!b!E%^L(V+#AQQG0$j&X8m% zr}R3CGKK%;GL^T8_#NT|zbc{h_^5sx-&eY#Y?c98H+|Y&@$-s`TrBmTAD~WdOQlsL zQ#{2SeWP?IkZ~8x>Zm}U6?H#lxR;-IkYLaK{8k|C)Qah-xHAw2S15~>>)lIIWOEFS zau=jde@#8L6J(cYACTKM`?Y)4oxr!PsRYhc5;GDWK!XekB*jJezdw40GT_l_#Ag#! z#$*2XXdr2e7&6P-md@x-d?d2vx|e>PV}+?I zslXE*EQePT(6~6KWS?u=)_Th;P6v_`YI*eBLpS0eSq!l zb8`Np`c2#FHbKT^&lXrhJwDhDd#t6bP?82(zlfGYF{d@ z24ulg^I0xaoHh3)Pd_KM( z=MRrG>2r=P|NZdd_*g3@Ebe7J!j2TvfkVkhg_>U9pOGbsK-vDJ?5%K@WDgnWXk(2s z(^#WIbQq7dXKoHLzN1M?)T6zcq3Zuy{r{Tinps~UqAv9(ltO`l|DO53QUNA_Ku87i zFqkcn58Ve3fv-YzAI4-E>*Xwq--eD#(a0>To11EH#lxXmn5LkOkf(5djoNT!CpFcG zP3THp2r5bi$|P>l)!H{?lg_|TQ75Y=G5B&m^83B0<3caAemNJ{WuN@~A|83(aVWP+w&J_ms8OYb0Obugd_|BWwS zjH=?Gdj}fcbOCK!bO&zk0PJ|(7;mU&g({2dAbi&eVO3+YH#*e*bzQT;=Fn?DO+VR! zKSgs9D^TU@@`wK$3;#Cxoupf2$szWMjk44?k%krUhtd=kZB*ednKjz?Wy@~qqinL{)I1dRU%wYMIw{kO zA3Vge38*FC{LqAr-77|Nbb{+)+AnpOH)m9_hJQDU(Mpl_qN>KNG z80PvuU`j>zCDhoJFE2x|zq8Jf6a$8Nh-MBEl2wT8#WWk+)gv9SUzdaWh44TD=jqEu zgdf*Cu~lG6P}i-GGm&PL`;I(33e`1le%NB>f&zB6&7Ar|9w}|P1gCOcqW_fw|05jG z@F%&*!8$Hu`wt=c|Ebh!5f~h0HAOQ(6j9RoblF6>MYRj_V~pBk(g5&o<2)Ki3X_rV zqz7MTD6G(Thtdr=?<=|)c^piyONXrovYug_V_1aY<=&3R^OuL=1ixH-jZHisWb#@w z%K&XR>g@$yt|OC^x3$wHS<6UL&!rc}gx9RqVr#Zu#@sbox#DE5y3swm5qTGtPLX*? z+W74?Vh|1nCeXD>SA_i~2^jHsY_dQobMscypu__A4_|>43(VGmyMw_)!x8n|+tS1J zarj63U;@zz=C|{`_MeyHqA)k75w7RvDv2pdfy~h3BhXP^_N;j9#>1mCd<1#SaIfaYQ%PuLJp;cM{;*Swba{4|u$10oo>--D<8 zN`79qNnu-r+xJf-;^MDlbE@NJKJ^6%#L}_aYZjt|l|EHOVZ=WAg93#U-UY|r6K!iy zDFQYW$6pG0HaH+Xm4K(wzQGK24H{S|`&Q?@pUea@u`*(W6L@{RQEn~s|`6CCICpA zOm7Nj<%9g-9+)BxoZ7=~@t$Gx_d7>HX4_17P|N(A3XgWr70;MeX4|>PV-1bkXJ-%T zIUjlwT<9vVn!>Yxa7>0rr*&*BAu6yoU2VI+ICf!G>0xI@=?$dLU?r$6LZE74s?n=O zyHi5}5q3>>kq!nM)bJ05cIVwVNZ6qWEDC?`;YSjPfw@ZPaioalrJI=M$X*8BG*tA( zU4@B4C9Fxr9pZ%EC5eU-hR8%d)Ol!>oR-NT+LMW$JMQfIKKeDZ$7uev%#*!VppCEm})iI z#e;6cK|LLJW$>{6>x5&wgmw0NTPJk}^OOSiSkbk?L)4d1mZl6ajuMJVkxSGbxz4p`2S^+QuOo~aQ(^?05JqohzxsB>Zms>Jwz(HWijog=bx%irVX~#bHZQPM!t&$>ZA1up(MAQb=<&&@_XK#rR z;pO6?_NK=1(8LKgioGdb+Xy@?UUYKtFIjP_Q`jc{ zAE*AW?gaaZw+ z1xEM1I;!%uE~OtGI}hWR6;?yhgN?wkDSjM_Eh)9~wKoZ0>u?NeC&T%g!3YWNMP2iY z0vFbx>$a1Qv^Ay{24Y%+FewkI?4J(Qjz3_X3uDEO0=jX9bRD96BFQ#Cn4Kpgn^D3b zvwX;i#E(s+H%oLOq;}Xt`aBU|U)~IY>DZOqKeSo)ejTkTBb%4=BaDtb8d!>29+RVO=kFlux2jyjzQf|9}zo?Pp@3(O>~MyvR`ys6b+HHL$pQSWwSl>2K| z0Qa`GNj(F_I9PHbn{|)&?P%7pEb%6?bkhMIDqO6u{o!pBYxEknt=_{w9qR$q%l5qe z^rSI}xT_DCV@&#fRZ-N3#o6r{-ei9(& zrT^7l9nnVO(X-@=UA`XFG)f+$2RmK)B60J`;lNhRlA%UCtb%i-hS#gVG5jWF<>XIC<-CE6o)zkx#IT)a#DCzR=>ba&|VE)Wzq$S#Tc*=3Z zBF-9GLbC!u97N!Q4U5z}r2*eBtk;c2q_mA2ZCybH-2>d+jOTt9horwW|ouY_qADr20Ot9@Py`}Z(DqZ=k^W1a;Nrf=~;=wJ-7W2HRO9@gV08PiKf2~)^ zHt^kHzNGQ$N!aamkOG0WOpD4j%UUMCQ5bQ`nHU~?n>H~exu+cB&fhA)@wUa}nR&e3k2Si4M7g%ujyTd}r{f-jj%I)bO5Sl;2bmICr zB|7Ic;Tc~mGQIMdy`%Mp?Ll149-0s;|_#Vo4%|2LBy*UKhSYM_PQ?^ zVrqBz%L2#U!4SH>TbSTC)5YWznTrmdR?2^fE7#t@_dPzGR3RDvQd0OIp>p;@E&&Vq z?QE0D1lGcuOWo&khFWGZ~!HCgRE5>2hH(7KCqIwW`xHz&-whJ~`{cP*31q2C6t=sP|2HRx(UDd|ft ziGa%drnOc0$>du~3o$Y3Q~HlrqzYVfu9`+uP_O_MfwTzyn!=cMaX`=sd(Z3mQA!SE zf(=E<*S^_qTdFFP88Mf-tbtp#qBc+^LX2iE%BE(f%}#h3D+8r`=K4#9ai~9hM7y-h zI|{77TskORbae5L%yFud_$Qh6Fq339J{-)!VQOI%vL323s1)uK&6; z124Y>Ee*yBcV2@(#lD%Vz?nMFxN!L=EsBKC4!n$w)X{skQXx11H-IUa9599f-!DE_ zwa!1W+Y%=oZGTk4sy)dXMwbRfh&PZ_Kcr>N`x1(uZb)%zF;nblur@fFE)>?n4t3|B zOR-U~@}KHCYpnwG0GtpP9I+(oO%5gLOtT%_%_ekH`f_3gj!E$p&sIW(#y-8xLGmRg zJv5_-A%HeB4%z2#D-<=wa>imH;w(Q*mo3^U9D1}Ve9Rtq=#3Y|*Xes9w3D3dKjlYX z8Do*evA;N_^8IChP(qcoolJMGnxhsUslUbo>2rDm89P2*(h%n#h8V8;thbpPnzF6T zOdD*bf%Ld8mLV-p-GWn}{wW1-=Y7hpj$o|n&k0zM>kwQRS@V=GQR=}5r&RycfmmiG zwY={>N{ur8SXc>tuh13Lt4x39vso~Si5}>Pk`HTA*5Iy!>}eppn^Y}Ds0=P{QaFz} zPEEspHg=~#U#^s)k^UbydvW9H>V)5~$)X0`aS~8>4*Lolm=nFNqnA&LyHQ|*N4i5WZW@2T;;fGY^5RVo$YO6b; zRESN}7{B>2Xf7$+lNPabxDXYy3fBdF081A6oVI#OX?szKXHMj$mcFrHdFb~GtOU;9 zB!97^jO({^j&P>VUzNGY`zLw&jj0&eC=oI8pGD9AQmD07W8+Cvn6(OF#es(TlK-of z-IhvKZpzm0%yewA()h?8>qMSBDx_pI`s=YRr1$V|KYt0$mgYiFrTHZpK1eZ=)?1(Q zakQK?Wuy?&B&rs_v|cu&mx3dZ;wJG!uqi1%EVW#+E+BTPrGhPOik9xUu6jyHq4-LcR*;6hLe|z;2y}hvx$rhL;+b58uG_{;tTl=nXC4>=~ zQ89!vCoX4=MU(P-M!JI5hST>k4YZ7gv6c)@sWH?63cMJ+i9ZrU0!?FbH4Latbub;Q*fXG-=QZG&sY`jMNlE=7?tayth~DuQ(gD&vlv^UPO{q;lkj zU6B$U;sbVWZ7blp0#>x%haC20W3p!49Js;Mcq@$^i-$2Aj6njyI|@ck#ki<>miw*O z1dRmQ8xx6^ACmQ(#w6#`8knDLyTzk-l~1l zRI_{0TC-xC^BDCR6xdNe~sJMc59OB=rCW7?O14eZ*L<~f;* zFaH#sN0@tv70ay}U!OHvRueryVvy@$1!0xFu1l)8T`cqvSqd=}{l~X24|pUyk)OcM zZSRN4d{%1>!Dq=`pG8oufT7Ny-%s4lm zNakWNo))1Rn>=p(%>>Fv73&_u8v)JirA12G&j`e&&?^3W@7C%l3!NU9Pv>lb#``i9~2yWewQKL_huZMqShsb;uqr@y<8#}`XH80h70}Z zI4ZX}8n!fi{jsJH&$<=8*(bP?P?uQk!IiCOkpj>`fRm6 zGDwVc5>3}PvH1CNKlCGfG=^gvN;IxVWxYc#eNG=@GtR^nD}So3>Tkd?A?<}EDL$(; z+Ck!MEGyo(@o5PcH7Fn5ATJ$dIAPkP4iaNLMwsT+R!R$F&8%5`uq`Ois_a*jH^b|< zyUC?CldhvPO#EZ97;)jb(x25PH46X=ABsxEQz*a<1n(pcH#3CdFI+oqDbM4C&c?wW z`SJ|2g>1!oaHo9mXs%o4C^Z|uoXjg=d%ckg5~S&)=L}!5D~(>1*F$(ouHTUM-&m^l zkY=aeA@Xn!4aZpjmdBmHeupVji|wcBE+N==@-Y`giGn)*YRKW#jIK#w5vOyd87gyK z;P(#>*77e2$Gz~MJs|3F9gs}rhuP~FsG2R=E?o(4u-4>PguMN{Lb8QUJg^z~AsF{b z?=Uf6dEoIxAS+;Z_xUFyvGBbujshFTEv073ybH3US|{4#aO!pDc!V6DURD!0aBnbbwmc+@mw%<` z3z!kmp6JyGXvS<32+}gi3#DS58eXr8y*B9$6rhuWy%Xl`8KiCkeM6H5Sr}s<&h;Gj zFj4s{MU`V5E#w{K4u7PsD)$SxAqQ-ht@7rCCOdq_W@$smIX-|Iy(2w0;SOlBiZ6kk zx7i%VQC1*Tg7T9;1Yo&w&2@o~w1B<;0N})50JBRqzC0hr{cv*Vf(BqF*@e1!PBG3H zy_)pAEB6NHBMn6!T$0)`$$Qu$AzP~idNrxmqhoz$LQ2(DkaKFzlKg+a1rVv8KjKVI z^kS`N*emmHAP`8$Y>c6^(FZjT&&MBB655V_k>o*6{Hbd@QseAJ2aN!QQ0 zN26QIS>ZPg)T5K*E^w__a7w7d{HcZ#pLzy=mrcg#x-N;}tFtWL-1U;^czJ!)ZVo4m zd7=1agPC`G($L>-%s0~_ySqS4&?+;5z1dqt(XAZ#7 z{VamJk?`o8j%|$S0FTmohXneSIX?9;<9#OQAe{W<8$r8MAtK6$8j3#nAu;*4GN#*{ z?OIr|G0}UDmuBin%-FqHRZlsF6iG-=^cJ4=Tg!Nu5f9vMTemTW zduiho%uF1-ybJ#&oxU$A8rnY4{F*({M)b>)keN6&*}LMn)U^JenFGPMUQJqvr(4xs z=O?^>tP_-m2l9i4%7%B;r)Kvigz__Eo zu1>@=n((<&kaIbvYMJx29P6+7!igrasy;#A_d9lx(bhllk#yU{7^oB7CkG?vbs+BR zL~16x=*&vqz?4#$V(BRe_cZ|z`0M(IP8 z{1VRm)UB1bFl{Vh9C(!u{l*n`t2ZlQZ`D452^C4$l6{m}%;DoL0^`!?Q~(Qo{$>?6 z7Y5Z#Cv*ru6@Eu1+^hWwvt1vTS9ODouvLQTQm@L7$G6b17mPMV;7A=>xu>*Y7uvpFcQ0t^iZ#hV;@ZU}j(j5RgR z-H^AJiJuZ-P@L9mM%djr3$Hjf5GaJ)kLx|+35!NRAop8pN$nlSHFw{c#EizOl-3Zt z%~Zc2l)MRc{}s$?bcY3K5rt5-RV&ZSiuidYtnF5lW&0U%DMaLn<$#9Qk>*LyHQq+D z)m)aPNu`CMb`8;1@FetC$O|~n2w3hJ_L)#6YM6^2fTq(uo8`mU5UQkF)}sjt4%97|ey6N2hwH1Ud8ufsn}?Vk+Yq7^fVdO_%~+6GVUUm}=(`B7ZcTF;fIsuWSm@0`}TWuvE}&KofW?)|55 z`+5fDoW0!3xDP+A^q^LP8R@ydwdg^So0Pg$i5Af1G-QTWS! zuKphRtm{+^%!UNerfWu$Uj7ZGmA9W-x)56~dX|o~=o-c){_I)P8=;nqJMCz@t==p! z03;Dr_AuzJ8gl_j71P*yz%Oqo61??IRC&Qv9XlA)ISa`v9~Y6q%`9ui(_PN%)}nV%3mMSF`0XkY zMf_`Els5&HzztYw-;Z0xn9eiKP3KiqR5Q6{x0zT>jhw-pH=2H$HsvlQeARi_8e@2p z!T~kOxih9+v+FpB$nnAS<>_YR4M0IO#*1n6q^8#B&Pv!`s{18ymQ$&<0{U$g&2>`n zn580)Yikf^OpN9#QJJv5+ld>&=n&?XrESp_%UHriKZz7=bI_$^kASE-yn5ZYDtPX- zUJ?(6j4=r-bP4?_JI>n27Wm9u=O^G0T%9p?Cd+A_1MRQ-ZD=+}PtrLxo&T7a0=^S5 zrygNMewJwLe`(0EE2sTz5&}g+xGA_-Hsnm3ukC5-r_V&|f4aiR+0~ks@cj-Cr|;awKbd`k4D zFtQd19tqF5<7=8G(B{tHn<7y+e*n36v11FE+KM`000w%+Nw`JGZ6Cu3-H3;*H^@0$ z4A&gcsr^T^Va=RHNM@#zrs+dNw6p+@SuEw0ALFs@O7NPWo&QM3#|CE~D=#SqlbC*= zM06nbvf!d#k!Vp}0XIdu=L9B$Yq7%&7hb$R5&BJhoqY*z$cUei_p8w#rT)Y;{0nK3 zoqp-A?OW4LM#6n%lgz~R&%;D8OX_o5EIgoQ_^FKfVM~_ypP(xRwA321Y%CP^2y|8s zNUJ3V>Pa{@Q~XZ%L6FAkNh?Pem=CxSdEt?BAd?rtd?DJCH$20aGh!y+_d(3ky{44} ziIHk1R0r3T#y6(^O(lVc%=}jqwwySHML61Z{?z?B_VtRZjI>%0gSi^JLp0s6@>XsX zM~ws0c6$R?j!DgIvJT@Bnne;9bIQx0lTDPH=uXZqaqTXdoc@nLfMX$bIjdOP9PbNp zTu(d^N6LmZY8O~sEb0kWX#+ZSn~%`Wo1A`s#5abIJr%uee=9`@#&uYKZHhoV zO+PtolXQnSqWklaUdP3GX3`?sjp5Y_GNEHf6p}BDFaQW4pL8;uOkpLm!I9f774BDx zn1lOGG8Gb$kp9_&nq&s89t%!i>KAV?Q*6FPV8}3(Fu$cG))^XV6QK0|p5wjWwG3Us z_YlV5vAXlkhNHAld3dxS^)ELwJgy}&%_k%ns>UbS;XzD0gmk<5sS_7Y(R_pA|osJ_fq8Rnx#UPE}Ae!7Bf~xG68SY*+|-O zS>Io*dyC`R|Da48%Fvd%`HTm%P$Z7DrDra+Ui(NtwlzA$ID#@yvGO3!B~am;vm#+8 z8XQCW$9iYcGdU(bZb@!cFO{ic&LpaTPpG{%8{i0>n|IjZz+pC%s;YjMaTjRgp!M1ap)t5|o@+yQ)fMQv zge+1-M^f;px)1gd(Zm^{e_rI=o;q%R0j-&y8(mB1m0a#u=9dOcao#rnbRcS)5xfk9 zX6AW@>PBMRMDcjZ3lnjllQVU&AaCg3Y04O5kq+P;sW8f*CBSS>g5Ddu8s5u@&jyRo zq|Ya`Nh>^VRhBpj>^J}fdXwGft;)4d;DP?kv1W3;DcSDvsqnnkapgr0=VUog&&`#j zsag+x%0l+k|Hym>sG@Mrswh3?s60(5)wb_7Ifxq@9- zh50BJ@+ws8>MEUgO9h1DaX=ujBX8fXN`QgFXJ=%|GnAGI27$E#gvGGLu0(ol7Dwwy zY`fJy8zZ>_XNo%iI4g88Qo6?Pt6Q72VR>L0j%=eYnsxp^<23Z?*gPeX?$dK&9Awoi zIfDt41xIT)jdv;=mDe=La0hye+>T&bzF~w%5~jt2Z<3eaZEy~~WD`WmjFHITp=GKO z?wXOprL$p>gyFr-5x`f{S2@ z@x{Y|A4SR~GwkPY#?f$^kSLlx4Ma`703>tb8B?Bqac7X+2onX;1OM7QU_y&*V~~m0 zO^+kp-Wh(yG>SUSRiq$7e=zu@5P2mlyiP4 zr@%izj&)!Jzr4Rm*gIYi-{vnkeBUAZ*xBk7E+r<97`e~cw=t+LlyVtGv1D@`7D4N| zGwwuNN4`0D`Z{31DI|N~kX-juMf$4q9e?unv$ll>nGFHoSWAHKS;u`6OS)GVl4%pz z7wu5*G(Gp4JxohAz>5ppFgDPOxxzpjcfW;;0QJ;4n<`Zph)VP^cHuX#`&xTR7`U}F z6=!5jQ54fXJ_jyhT*1@u_NTfM!R7cA<~b!XdFpL?gq-NhM`3OKY#rUk`wsGyt?Jtt{tEMBDgfOdT@fLEc@2BoL=|3jtkaSl{U2f&dva%xaOruS{(bOfXK<1UEVvzN56)dSryajgIgmh&8cS zrE{WnGtt3De>oDi&|1%R2`F);J;%hSJsJK9QwPb9G|w8zr}O?qN*E9+nyaa@<}Xoa zj=(kPQUJJf%q4AjAk$a|MzyJ@)?SET4ncFp5IN;Hv?W`Q_w3jjPqv5j{!$bM7q~Ku&nK7-J_4X!!g}V@wf2QSEMy>DJI0W%KU}b%>-K7M*Gh>5G z1L7Q7RkA_Q;Xn1Ko9TgfMjv?tr0=OPQCRj3@x8L7OUL?G_1e-=lHm0SP2~))GNXKq z{Y+~s>qE*p-OVb833>pdEsj4^l$v^Hov+j!BC?Jb=V*OZIiEEwTs#QHTxUCtp3jbY zX2YYKHZ3!U5oSUhf)8^kLzQ~ycq04Ach-gpoQcD9g1#goydoLmyOdH|e4~eK{Qh8@ zMGKeEcB-{UV!j>k|u z@A@5;yy;-U4y&&Kj`7SdC{tpGL^0F0ECzpF^I?aIeWXi(+F}iw@nxB<39`5Ky$V~^ z__}c%5YVNBXiszU)|9F|{vvdksMHM6`kUMs{#>vPg*_HB<9zw^=$fuM@_ZqvIoffZ zsd{GLe(I)%`<)h`K{9~1w6j`NtTkWqGCD+$x?avrz}Clh!Yoz!;w5mva|DJflrOUo znEYoFlL2_u|9#xzlK={K451>s@idlhyQp|5yBGm}nmq$S^Fz8|grO&-aSIUCzOB0v zg4Q_LQEo~pu|<|TJh#z`r)iA$gEA!CeK4AUwF^756kuFzJEolU?U!^3k!sjnS5lzuUlvfsA=^P{tGef(=eq2g?yYPVD0=)CH zf!Fn41@>zN$b<`WFv{!1uSR$tuy{Tr^i%bm?H{d-63=YXv-xiRUZk*Hw#s`)@zc^l z<%47S0n+!PC1d?W<(sVXL)CKw-`ASp?PqP_3EkhL%$SFZXQ}E{gsF<@OrXrSK9KUQ z`KQgxlrABuVeL}PwQoZ9F+AyAmgcDzd@(8;p*i86`V3qeIv-R!dc7bwqVZ!krkFb} z-SB}yiZ3W#>|58y!c;(ZBri;*ezP{MEy}#ZT(kJutJ|OBAV9%HSnmC_7VKi!&FSDK z?qWt$r}#o@q_g~}{af9q)I~UPN@7AXz+~X%ppT4a9mG}Q)Q@im~im<5aur8 zSoC5KoMwXh#lCp7l!XFy6wXOHL9SQ(;G@@U2Tv!`j_djl%KcZ!`9{3l;)y2x9liE_ zE36nGHNB?Qjn?egOU6&1{e}&ybDrLLf1HhM?OYYEsC-*$xe4)3hqqTFq)ifp`&?=~ z!w4#*W-{QF{(63~VOtl#nbYfKa!C`_%FVp%Wm)rbL8hHggw@UD(?UW4>t zauu0432BS|d-4e;zuBC{KtqvSgpEMP958Ld(jHCC#?O%P9o>y|BG+d9zVzjqmYW0H zgdiEIEmO^kOk6w5CZyj}J4ql4<;^xpk|!G5oz#h!1?1jVi5>n^Kd<3mQ|dFd3U_Gq z9OP&Sv;;zaHt3Q~;~mNp`!y%tI2duNwljqsjS4$ksMI?;>BXZwFYqiskI!#wdp;06AA z*C28G96ruS6*zjgoO=EH3OZv8ooP2q4b;+{wBTQ`9o>8DU##dQwioqjo9QdO>(J69 z5Ok;b>bz$b*TDq5Jma|}yx*5Q5^nLfuUg$Agt4eKE4j>jy@&izvmH9keOLQFb$n-T zY+@h{CVk>h|3(ru7N{$Apv^@m)TF!f-fPL}MO61!dT(jRo4sR91+6nbz6+^5tpp6w z4SH%ZdsO`H3ON#)@Kf?@%ucK(o>$X6On{s=Nn5dJ`A9F75ERL-16mdPZsSDt>zGVh%= z3I;ofx4<7(oVgV%svb6C?@o4$^2^OkkJ1z9FL(iz6C5_@?}u(*NZ=fL{6WlTr%0$i z7@D`fNB7@R`CRwqcb2sjY3miB|CLJ4qEogodR|+hPH5#IHz9pHhPPT=&NV#Fl`PO# zg@^1?d5Zbf1yva?hT&0!w|3N_=%;0&e3M*X88Mv0u_aIWj?QW-QW50Ud5l&4vxJ7> z-bdp@5{i>>tT=w>pHvZAS9lSZ0`ugn*B>hHp`M3Ue@6bH2V%fn@eattW`d8xYG*@dl_(`03N!~w?le?CPEJc9(| z6*odzC!GF4{3~mlOlNM&$PL#&@{{%D%ZV@@H(G}K)2FXSMY1&eF!goJ^{$2*5fA^M zq?hUDs&`ePv{TvY#?YkeuM_Qz&Nj8cK&=!Hog9g8@EO6UO$mu&mRAYqk_^1rXNG65Ga&19lznRp zJ!!g33;8y-;@n^XAW_}s3T;0aT>X@X51wswU`X5+VFAJcF#hN!k)Pehp)4 zTcEoac&zCAzVhoHQc=og?ZE;+20IZhPzrYFr{2JLo6AD$tU}^l*t#2o38Y7ndp|ag zmo}?IZZJ8pMvQSPe<7u_B;$Z_dr+Lg?F7(6K(H@Zo(lo#9b*Nf#n2kLUcd&-Q4LKh zul49c#2R<6lAW7xY_hd5#!j@ovYO9P=1R&F+5f?7%ysnM?+pdBwgtO} z{rQ`|NC2`v{14mN5(&2-?{`cke#6+A-Z=eUv`$yJGS%f>MjquW&k=jPk8QuGR}mX{ z_I8n%+TTk6a^SPY=4WyO*lUEFr9a9*4Dv**X#43=wu+@nFKeT>rKwEcz}`K(JVBohpRp21uB4KDOn(W=BHO z-&FfGMz>11Z^FBve4cbDNw*5XbHu`Y!$wRUnDl=OP#2eBu{9thkC;m5kyH3-f!Z%nQ3w>VXpQaLsWf-ddN0+0+9dNz1DHzK3~1r;WC!j1t8;F&K8AOLfN$ zV1a-wi#Fs#)OGGj%3taL~f)9nQQY0g*$JYo?*LiH$^GlIFwc?@3JV zAdGLg#y)jJFMZivR7v}$8{>hfmN_t;C2eKZ z-4wF}qyZZWh1bT#I9`VJJ~s9O`n=)vn|pcg^DJ(xZ(41zo-)ec6OZ5PuZ!D))Nnc_-x3$&-h-8K1Xo}Ocw*eqmi@{tBu;M}C zsKvA&`zZwj4$qL*8xcoR=;sDv>!grXjHWIbr<)i!n0k0_;7NAss|_GU%RJiRkB7vi!yn@O_lehBn8WcPApXFJm5`o?koQy)m? z+$c@IBo-)gGV(eO#_^vh#dAb-V|aut2N*l;iAxy(dtkHft5iGX9z4Vl-J)-{(8gIS zqh$EA?<*n*r`fv1qm=U$JHY=!yp@zX_nMdM+lb^95y)~Z@SP~TF@8d6#7$+Mnfj62 z{YuR+pO?f#FHbHM&l`pE-93MLqp!7vd>1!Rq<-rN@;p{cyGC8ZMj>9tqAV=Hl2W%$cz2fcSB;{{rR!sJt%CqN8cJO zgyC0UMB#YYXN7LmE|E?%nv+jt#MC6E0s_h3>Y;|t3yKfuapf!M*DoQuR<$0}Dxdr2 zAexmNGrRuyZQ@EP|H6LdB7ogm+!M#HIax?438nZ@D|f#GHc4>v{RD(hd`Np#P=Vep z*aI48UY#ve++R2u<($>uDQ{8+)u$N*+XfSzQ@m}@&lr1NVA%f(Ol?lH$8!SHJS>?C zN7-aeBxRrBJ#Cn>ZoEl-q$a|Rz;jr@jrs`RY7A9Y)qX+AIkb_2^fJ8lF;qAV$YdH6 zh|D?fT`NHnU`~apH__W=8H6v^4_H@)OY@oG-xYT;F;*J=-cK^iH&6QFBY+7mblit~ zlYoG#69e#Fub@gATO5T$n^UE_&dT)M%$JF4LFTpr>~yTbRC4#DfAgoSfd5cI!O!!e zj=O`9&7adaML*pMj27H@uHBBete-Nvw!W1Xi4GEk%Py4p$g|dE8miZVe2Bcb=3@K^ z5ItOT@OIRrXjfL(!_@XgVM0|A*V3I`)9=@V%uL_gI&B%YC7U z?d3h2uL8tg0~lv^_%|s@HN;lac|N^{xj(I3+rEnK{WHw~K~{Zmf8qo7Hk1_`~?ggZYjbDFmsc}P+rqOpeN08RdB+a42W9aEH zaXIvWWtcPIwg^@zs}j>u3huPsnm{p{Y_Gk^<&EOjDb1F2$jKrO70Tp_NKxQNnw6cA zo~}#iMs)e$(7Mzi6do|JeX2=AC>8`S#+TexM3w)ZN=+y}uFpLV*!H@6uVDaoyT#Aax1xP@vp zZmqO_#&S0CPuZ7l%h-wVLW*91B{n{+e#P!eacfFguyxHYf;UIA<`|qQ6(vLTvo$)LVO1 z{M^82p*Y9%8R2`XiJ<;hO+?ho>Q% zp69@&^$Hq$`e3OXOOQ!zM-Y&eGkm~=Bv$u?kIt;f(Ndc)rKtNiMQM$WPIuxY4C5P^zkuH&rQHhc6 z?gr`ZF6pj;fjjPV&)xf+&Hi$~@&CGjHveuaN5Yc z7W2aI=~v}=7>1>Gg~&dOIQI#@vL8Ioh*1FRUO1c9FP<@Q{+y1RMkmY@L<;PnGMx32 z@R^$M08yFNxuw#Y%o-MrhK6!dA^UGOq@_mIIGW)&V8c21dxy=xo0PQtX_2Qe&3Xkq zo(dbr7U-ovD>MbKp|AVhYrOx7RA?QG4!tzydT?4haKgCg_Cn zvAo}dF^k!kgT=eXmti56$jF~DD`WJ0u4docx0?PcGJLqYy{H5kDnIrvkRU(_*FK(e z$74HapPhMm?o!-YgB@eYK`>3u;L`|UYwP36(r)5QasBymOq4}9UA$(d3C`S<B3H~At8d+{LtU|6i=hT`45FHXkqhHC=_#zKG^%U{UKVn zO>>%{D(U=b-@93a!%^(MdTR;aS7{0Sn44YBPB2iXtpItm5Qe2vMVmpo^VYP(f*9$K zB#~RZ0%DZgfAjlMh%4a?eOWP=32heAG>FW*IwXZ#m}6~<@35*6|Bxt8N`Ev~Ps>0- zsM&bE!0%vcV>j0K%mub1<|+u!no-vKi~7=^qd0!zt6K$Js`|8iKnjojmE9FtN14w= zu;XJXxa0%`hWo&FK^ZEo7cH4F=3NAxy?$Z1tuBH}kP{WrUp0>nvm)Cbl5jFyuAq-? z6T$*(Y1un=wObubb^B8$3tdeTDnhq-5PBD~Bx;Z)MB_b5C)-8h!p}_Cl0m?3{Yq5# zazp=lbluy;X|!I(uF&GIO6#pUR=_^wPt>`i3xnNg0ww3_fAjczqB2HXMiBq?yGhle zCk~ykfW~2jfOdyt+4wyHih~Uf5oKGuwW?8-Qil?(4$oO}E9^03dGyT>#dIU&Z)mgc zj2yxxQA~|K-}j4^Q2}(hK73tx(F5ixIBK2u?|ik679}%XTgq|$LyKVMJD~-`urclk z@rEfbvoB{pdHRqhm*$NKO@^~AKTJeGPB>VT|8y@1HOm8?V=C40{%g30@9{oxqV;4~ zjnE)71!>>RYi?39SsHP_Cak_$eB^wR3fAszp7!?f^EQU`DqFK32Yir^HLvxw`h1sU zxjAK-<8)EP8%WzHIC<%m6?RE870imsX>ubpA@F?cqoE8&cAVvf6h(;Jl6~!7Z&OJ; zfsOr$bM5L0OD9)Mzs6QBwTB+-;-B!HYMsH@F@Z97*SgZ@gdbPQ~-$uxLl&+X6Rgxv)@ZmKnN`a>MB>w@$CGAH)rs@n4zP zatP1=k#9&UqCb15L(6}il+p-ievI(qwWo>63QQ7#~CVOms97vGop26mX4bv9lyo; zE+fKsmmB=JmsHlv0GuJl6KKj_@S9ed8W1HrffH-d` zYSmn6MA%)hW&4Q{UvHa8CzS>nP{a;P6(URMECSE_fZ`d~+4(G&xXNWm5At8GLeZKt zqY6D-g6+SU`iz--8eZIW*t1Tb^_6`-eYvf@d_HVqx5k6X&J6F@)0Nq70b=GZ^S+sE zw7fXrt+=Sq*NQvGj*Wp*SF2z@Ffla1)@qfZwMR-5_4%3(N->*a85RKbyAgA zw@6wbUg2`ltoaes{Yxn>pD#+_FG|^bj~vup#(=n=mcqy0ZW~sr{GXnS;TN~jrHg;O z`gTh_3ww~ukS9LIY>=E|5?jKqJ{>Mo@esf``bcu-6+4Zu%yriYf~Y*gHHV>)aus6V z(`IRd0h2fsRWEs%(ixVjLS5@7gWk*W#`&f0aIQPz_i_*u*5|1f%wSQhi~LDivONDr zP0CC=e>#A=?SaN8CSE_x&BC`|&f@-~23@MnfAX@xW)Q3&l@UYd6i2?DercKd`fL2V zWrvfgio^RJ?a*cZ)Q4!l%`Aw=Wrz&mJH_p;PUs7JBVZ!v zfTW|D&^yJ0`T$V63s(CCUbwnsA#Xjqae|#~SiufsS@TxBqAnA|hgc5@BWatwY=ORD zvt~NHz?TQP(J4^&UKe%NE=duGnCq!38P*1`FMD*Dl)u6jJO6pvQCJ4t zr)L|&=sq|wU@f^wP;KX--e{El9_W) zfWnBbY;ig9j@v6{dp(lm9gt1>`(uV^l)1Vlhl??Xt$toBiv^c1bRk855Bgfa)KaKJ zEZb>GNyBuV1#S8|>EwLg8q!9M2*r;a)&Jbc^}76LwJrHjHc} zC<}_*li7?9_csa>ZwI@b6L=_|1bNmkiWTknnBjFY8$}{dv}7E2-f;lWeZTz52F((D z1W+D2ujLeSsrifhocD@33&M3*96_)(J{4%PVicNC7p-^FmqSO8a5L6^?{piV9bu~# zR1iL2%LX;9h8UJZ*uWkF;0ZLX<9X45fIh6~A0!zXXjFjMo&Khj8)0$=&3bM#18&k3 zNurdd_w$lOs1hmq7M<_(Vinsj-w|4Xr_r=mtU9WW!CPi9syprt_taPWLW(~wld!6& z(!ODd_zO8iMsAr@uTU);|PYPU5(4tnM1Pv4)IbT7{4 zeh5uCz(pBmke03a@K)X^U$!X(7%UaU#D2`#34xfjeTi-MF zF8!@f8v1Ac`tJvYO{e=^W#cDI zZP?<=gDkaJZ8zT;h&w$}(1JMhl^Rbxn@iJYw^0?Z>P|dc6czwz?;q!Pj+Gu8GCeL) zOS4FUn!W`uRI?9!)dNk#0+nqE0iV{)3fbn~DFd|O_lUM{Lyuzdi^bcBZ=+=q@*@V)i5y%&i5r3uU59Y8t@q*hw$owsGd_h>paO1Q57= zC&?d1-fKi#RH)n$nCuwObjtlrmLGQ)T6ODl8*Tg_r!ZF!AC*jR%)Y+YZ29+&C*Tps zSrqt}IC75tO#Va*VZef!(UtoRDv`ZO2(Gi8>Z2eIn&eHV&YvMlK0p$f!JES*40Wrjha1RG}dnizN?ac z@*FZBbh0a~&fOO6Jxedf9u~B^H;EbQwxh2`32(qE@*NgUzIJc%v=1yC*mYN=4^Aki z=?nab2~}{6Eo!vGCyw=Ow7{;c5Ko<+eOcR$cIsg;i|^6pyIe+?BYOAO1J=yY1G8Kf zPW5et`;>++8PwvU`q_`rAl5ArF@D@!)lfdnt+(W9wQYJ(zvEt0@jczW7&u0ZT~M~O zIll49-Ypktt39F|5mk`=34oBm+6E|0y^A~LvrC^rWL$eyku@7Q*ol|l4e$26dQ@)< z^DY|d!MaDqIx z;|`Fp2@EV?R1X`LG$SII$@Ebl_hiT4bZndw4Ge61S*$YapYx*aB@qt+n%LH>fGh}S z2%_CRAPccS((^&jd014pxIOj_hiLD-?_c=^WOl8={pG5+@@r z02(~?xIqkF2ocE?yngbOe1&X$9n$yM=bLS$k&4h4tHleAoL`ec)T6Hy}(!U zzdRzl^4tO%r}Fmmtz6@G!Uo1_xx+_kBEA@#nfxr=M}=F9`{v>yd1wvEu|x} zQSkZq*@;f5I=q(L%#hb=uOlQXNlmzt`c<_pT19P2Z5F8-vGQKhhECOt_$$ zeM~@((N|byze41(Y8|s{Qj8g?b|+M-yW+7v`X7_L;Tcm=P`E_u*2MfZl*fay9VKP( zMGuqml9hPI`8|GlPv%%WiDXi(lE)meDR;TZj=(`)fR$ozgap~Ik!pL39eKN0dLb~;?v{3%N1|~OZl(zt|ZB77R z##>h}C(b1<+EKU~`C%VaO@YFVoaMG$#AscYrAznI=9kxmOp*ZrTMsc>ullO&%=^!b zUb5h#VUOkUcvfe~)Ghb5a8yu+d)gxlt$roiIB=FF>9>)yZr5Ov+bvhD zRik%|-tC>yd5w&7N31);bBwZHG?KEfLr0=mcjh|rV;pktW8Iy%dAU1CGQ!J^qYHy- z+Bm-GE1B?8@*O%(_;fsxbU`c&{`4OyOV9Cn=o4h8_#W_JdDofa`l;h({y(yJdYxKH z(A+_mY$$g0YvPsif*v@sZd2XXHktB_vkaC+2KPN}Ic73b+Fx`$TMIB{f6a*+&k8ML zg^(i&_!r)2>uEc@qI*MC?>sreu;yoComo07C*7D8g+_pn_Mvq_l;`HyX9T{xa<6}V zcuMpD-RfG@F#`g622AL0Q8@R2OH6My%S=$^W~A{4=Gp`Cxd;bRH0e7R0nZfBq#916)!>f z8$y${)U_N=pBjf`Y+xq%sy$wW{CQ@Zv%{eN!uV{P$bqOFP?={3CKW0TP=6$%LBm9>DXs%}b@Tp`UKwYOWvRO=FXEbq ze-~qq#J0!TceP^PV1~{mA0m^k1NKNs%ScG?Bgih<3`P`rR%#CxTvymnr&vkOM0l(F z+*@3P;i+tk>Av?u$0`tx_CeN;!euT|G0UZtwOY}pjEAd#IiLrjorx9iJ0IBBcU|}N z%nL}@Wkn!NPMR|UrPz%)~ZTC^Gh+?hx?cNXK>e~xQ$q?6q2_@2t=E8thX$~=~u-|EoB zu8QxG3v5g!OL4_`ga9hI#*7|5Le{=p*fM((FvV++;gZ&8_;wzT`KIbwTif+QpV%w7hOc z3HK6|ezG?LzD6Z6mZ+=Te<#m>xYxpS+ro2&P;{bjcSLT*>TYl~xkx5#gXYhxYaQKg zE`I<%P*S1}?dQ}0zC(jLW(kcg3LR~gAcRV+Iw4oF?_xGsQAKg`V)BEAoBa1e8`ZdP zn+bBIv4TSeLUq3OKYi%Yon7%05IoO=VItb>v$ z7l(n&gr?w|&A||DK+GBu%LV1_YjAV+>pjSvZ#m@H!-E_C=}veJ0$Xg}g*i+wZ>a+~&rzLLb*>5K-o~8~Tvn7kVSTi8n(xa7ngLfFf+kRKG`&CsOaCm@h@4 zcrrFJc36{AY+OXb`xQIGc#y{_=rAmL#d8qeYwzzEU53WHFouU1nOeqtpAIY&it@(#u>v+0)ELK7B_b z>>cu=#n-(JichYhC8q+dd)M_#>z`SEbgFoB;S?Zm)8NtBQJL~(S%h4^#t(=%Ph{uw z44E?WO9@g7%V@dc7L_9*NME6ym00$>t-Q{AJh`J%Y(0)9d~Bjb({I$f8Ke-U!~8jH zcz1StG2?r;dx3vI7^K2X?ell$fu-5w#Z*3cSN*bkPa#=| zO$ZdZB+RDK5bO-sr0Mkaw}Ps%6rUYL{lz7@+NGF9?S&E?r|KO3JS2EsnM}0^p_;QY zfVy>%uZx25f?P44fhWPEBU+0Oz zZ`Fi|2eL}1YEj$oO;q0a91;b-%UHkkYxU-UhZuTu!to6SxE$jS+xe<^UE0Il*CwWE zx{MD{>8Rd}-!4jf)myw?^)ME)Mc!V_0vb~|bLK_winn|l-~LoT?QX1woS4ry>U6{K z`Wr;K>zEIA)-uODy)1jk$|MLFM%r*rZSqMPuo%klXmzM|c!p~lNq*p3kM;Fte~+Mc zw@&F$$@%}+k${Q11-kOyDK&v8eX~>brp%k!|MiwJrU3Ju7gm7$X6Moa5lAY0FiEZN z8`HFy{2PDl&x3vU!aQI1CwaJAp%Rq$TPq3WCRYjQP7XNo)vjMZ@gp@p(*Ic3!8e$6 zU^k<2{5>q72Pm4vt^uW-?CHO?tEuysUg5voqxQS>fZ;28Tmlt;pq39$G(!3CJH z^-MdlEIbZ<_n3Aj{H_XB?(4-WEb9tFdGhmSl$t|EfSkNHVJ%-GXE+7z;Asi#4BMfx zzA@}HEyY;F1a5wzFIHa{u6KgUO%GzmjKk53*30clnGasKU}e{-ZIm4myRJuUJ{VFy zYc!pc)9M4!ta4@?6H2D6bcX7FCX4Ds$Oc?(&C+$=RFJNBLPp2ayf6I6dre5u)l+l( z7?%EiX=3l!?a-OhCk;Z4o7)ECFB}a|&YQtfUxk-E}1t4)){)WqlJZ+P$Bn+CCQHS{$knCa;x)Y*cUxWystYH8@ zgKxt#nO%(|S=g}C4TLiDAU7p<6Dt4YV=9?C|(tbu0!$gQx9Y7UZ_)_9D5} zg%@6WmD&zU<-`bN4l7O4FR8CsTK!y@e3%ee+G_TzFcd!zgF!G*79A=KUg**Q=&y14 zSi0${Wjq8~a_)e%A7UGaAOTA>MeU(4PwTsr6*I2>bo#y~?FzY>T)&HjC?5M_awKI& znL`-g+xms>D2fN8w@;B@=j}o&M+hJ4!1ibOerJ;qVo{Z_Yw}Yjss-j$(}Szg2=MLc z)&(RTb0661!7IW;6zsELfPbL&72@ZxC@R+{I!aiY`?|=bM~E2vtI;MtLlvs}4pm4N zIX}D}zjXf;tHSeQJsvM>iz@~CYfM_1Kq3pA#e*1QHZ->75Wu?C+-4x!h!dMMyJBT8 zc~c<4VYp$vs>F_KOE_IzqrbB-y<8o${-j9sS^Sr5-*`ZqdTN``;w#jx;|uQ@iVrIL zFYHyX1#$K%Z#h(czx2~6ghB5@_Nj&q{^bls#_tEQgUPPGXm3+u$Z3(aeRMP&M*OAPUn zyk`W=-^U~5)Z;0>aK^)EIxrzORk616(p-n^v1s-23{jV2gZE7A`gz}Vbc^tl<)8Db zcw42n1FvZqyw0~USeqLBy!O@$VimMc7QQX<44KNY{rSjSYJGQ|gRz=&F@nv)6Wdk!- zUH&Vf4dpHZNqqAZdqR?rRVJZDgeZbNw_%TMb7J?)rHv(y-hUZLr+<$G)adux?H%bz zIX>a`X`UUS{Hx4|K!1xoLHD+3E`Xnfm|`lU*)07AL-maDN49%(bz9>t$efh#WL$pS zueafH6Ph@no?@ZWqhECDFEA{X6dS_W6_U`|a(USMklF!t^CC6i ze6Z)I0eM88;4M~?z-12K7l~aj;pd6jA`T4b{rjTl%SI2Q%lV}X1X}#K`>hW{(239_ za>-`9QkB_(i=@o)kVufAw2wXDICLa3nY4H3mnp?xKK8X_0ZWc5)1uNW^hsfU)6(AV zLAYW@GeMVDj!V@Nrn#KSp5V4Enu^fQ)u;&mdA-OodaSl8M5z0&Voztonl|0&)hxsQ zp86ugxt$H@n;PlU8TIIY8TEO>Dc>i!yedt)3R%aGUkni=XPD1962!5oFT!lQSMZAL zN0`eo5+eGcM>PCoY>q*kc%lvVfF-zj>`|{>QLHcnz)|dkf&Pr*ou|_j(M1&j%MANTh zGF`TSJR%~M6SXvK<&P{XmCV()A;_5eGz0DZCabV$O)5oD3h91}BiPNCquFM?2GsI6 zEBeV}8%R5KU650*qHUmbeGmYzWN3qhAGe6XIfMxIC))rfcFt?A;#rWI>rJuS#NLUO z>Vz?x`Oe0&q05Rlu%i6hm_w4LY9oO%!-tYCxsPLphfYL~Z8Co83&Fe>>N^jS7^q$~ z6-j*<4WwEPb986L@*Vd}584coOVmmVl4>ZWgO z7`bERn0^vnY?ELhU|`R)Xoa)=`5W<|Su|%oS6gT02uLFi$x$=r73RR)*4Tx)P>1zG zP$^B5*A(6d*ty%@RO_7xexBxmkRxE;)<@ehBR^PW4t|U%D*LTHa;;6xjOdM`f5L^4 zvxO-r4_(h^BMx7$Dbd8x2t-3P`by2nMl&ez2ao|r^6g3xN9c4Thq&=bkT-Cmkio-y zK=k~k;uODRh3|p3%WFiLLa25n-5df8{_-r4$!Gf{)(^IPaqPDew2R;3xDHIn%+6&o z$P9fH^FDq>Z{w7NVd1Xss<|=@AJpecBlu>4mWG_fQ#2#3!}_AzX~W;xb`an(H{Ato)mV_|z9gMap^hPbfr4y8&z&|ln40P=25x9< zC~WYC?10bVEk%0`na&N~!@K93ir~~+gLCf!PU4B%w}fZ~g-c5pBUwi|kB83sn_VEQ zxN!b89?v)HsE68s24UAYFMgaBHGzyDZ!h~t-`UMk1f$EJ^JE8lB+g*r z<&CE+`86>YD2H&?2GmuBlk41Ot!;qhP5(%)p_;B7?)jRpv)+=qg?+*}sx-2UDyYcp zVTkJ8dY*2zPu?$Nr#Od&OiP0n><<8-NQz@({=X0W@|a=%uHRdfnj`_~(@_ugDRIqTbW8Q#!55CeydOD|`s*fQl`j;8Y%C?V zHE&7Z7Mg6vL}!zlPA_P6(m%7`xQYqs4XUnornJ*ZJk}q8H_yowSl?TWVB|Cjo=_;a z?2WBH`}6EROOQ8x`LV|h770zaBJ6r~FQopHte%s8Oc)FX!Y3!vcauk4Cj_cciHW7USv+S{ z_+Dvw4mhiKb5>fd4<>x@5x+?}$jYqJs@l>=l%hvGSZ+ERgR%IpeOFl^qT=+1SZo>sZ|2ltAs7C8yC|4GFw{ApUL%2H~NNvGOj zm*;fvQk3uj8(OGX+ucKyfaE`YQJf7Y{!HJhA9_%SE)onEdC#6pD(ds&8Sr_aLrK6l zbHx|!BlfvFLQU3XkMP$t{lKATVVX4hnfltYhcEjKMB;qJPwHL3T(06bw8&;KzOcT6 zYgFXB{&gYiAi&OM-7WWuZJu&%@^Gm=DOq7{vuRgdyHAD%<-Hq~^+=ApFmG}T+&H5k z#5CU{BCVU!g>-!9TKV+~q{JRd4V$!p^$jeMw|tA60h|d+8YuLsK4b_;%kQ7w%yCWj()p0}M~sW^ltnA&cSj4A zcvk=vTG+GdxCC(?_i?TxmExZQt7wWk&#G2IMWU7XqX&q2C#LznxzDFm5dL>?kZB!0 z0|3bTzkL!A{q9dN6@Yg3aLdxmiY1;9sFdKGf$N-_6+O#=KPT=q?j+0mTu??&&hjC# znDEyg>)j3FWB(G{KXb`_Amw0~Pm#JR4VRGkYC`p*o|#6-y#JW6Jo;@2UUzOJ;qOI` zo_>Rya^kD)bYj}DiT=%w{f#6Cv(ZOb!|g?k41BMiTtJ{Ogak;k`PgBl zdHi@_0lP`0Y~*DsM{);*L zPU*&e#{seu-t8tBbZkptR*%=`bU(~mY!@X=5Sb%-ayp*C|LHLFbir!@%yKt$9?TU6 zWo(j;d*5~qw%}@}*3>?OD$PZIucYzIY|Gcn5HnwYA3AC8|(;);O{2?Ab4Z3KJ!k zVn5r8PrCG1$S;_mm<>Ly(=+B$0@DdV!-3>az=5C|)<3F^*XFwu{C6}1z#nL8Mz86< z$f2YWfmD|rVVwByq!-!wyoIBjzPqn?#&GV*Q9GnwW4uF(pRgq*aujAgAQN)io{P!& zo&8?)N@)Z`M}wobT39x&;0uS=;eFA%Xu+txp4L8}9wC?U4I6z)B&XzPHIZ zA++0nG57XHd+og=#1AJ5`x#LkGk{GKSY#*lGMK40*1&*x4LA0{Ox?{nWA95)UH4si zNwK`uQdUkW3&Fm0ltM4=F2QWOJ0mY}XraL-a zs!5Y*ooV5-5qu`SJsO+EOE$(zRZe7gRoDw!8z`uZzauX_7C8wxj6^FGO$mqJ9MLwW z!la#+6@701w=DTjED(aFPK^L@E-J&fx$U1IBL7FUJ(eq7g@jubHH_5n?inq^iTy;} zJ@%due8DkGN;^*PCd!5VgKke&Q6Di1Mzhh46@IIJu+`23LGianPsiU;x^+|>m59X= zav!MVxwG+5NbQ1rhb$!Y zTTAG{Y&Fk>k?M~8J_TBOoO8WCS!f7GcBe8qiTd=CP8sR`4WQ&mto(Zn%V3agHSYS9 zbc6rIw}u5@opFC%GReaQQsf7!TXJh2iwUv}Yw6b-golOX>AT%bHde(!E9kzt_xj1* zzLqE6`^`faM>DmKBF3niILdx|uS%))xV0_p!?y*;C>n|Cj3g4|pkFptQh!_xmH7@50*|wkV*b=XL5AhZhuX zZ)^UL`&5v!ydw`CGVb1t$6fXTC8gBV=xwB zCcmA%>DrECR%rV<3dwbMbug|Ce`U1ESwfRZ4up0tgO>tqux^FyS5JY3Lc24=t`hU| zy8|k`FZ^_gid?sp=atD+56;}}D|r&Jw?C2rE>253w?*U~p% zre!?;exf$YyX1dno5hr%X~+GG($QK*aE6*UQ|r6P$L=Uht{$WEjJZJ=0}GWg62{1W zkKVDC^-NfX4S>FoV(y#iiQ^_I!H?%CcK$}9-qeY^28nVEyE1ih%xM3&3HV=dAfX_x zdO#9amwkd{b>{@LvC)TE-uqx0r36rh(qjs-ne#?r*W1wPEHapEmlz)sD>=W#0tt8f`O;6#PFAD09n^5~cHU0LEmQUM7-rM7A zGa;k!+w(esa6+!#kYQz=R^&6{e&F@m1>_nmCj_2A9jd}w2{7InJ1(B5`6FCelfHZo z&Ab>GxsDpFxt?T-dLAU)EDSm7%-5th(AQs)XEUG#zaC#!{I(d5dGoy^-11PP;rK>X zFlHaC)@xyRF>!zQg{-?TvH7WKufn$*8Oc?{*4FstyDmwup>f4M#ar*~MSA4@#~VpL zI6%3xAqurtmN}z_dHwnxsbnci;hrDgg!Q1u@nhm3&ri#kvsW!~l{IcJ(SAmO?0zlw zSrckFMarQP`I9*nI{)x7O1w>dyFf+|V}CJZYMGvLxBmXav^Uqasd!eh_7WTURHviF zRIOsFxCR_5=&CPEy3)^}L;LKH8zBa;WwBXVi0(@Ju9nn`D1S$S7W^uKmDh7o zkdBZN=VVf*5*e5DGP6FXsPCb}*1|BDOPDH7ycyfDC7W`QODoI@ar5l!zylGEqJ{|e zUen-+s&2QfSGRf=?^*t$gwvR)S5g>FwouX2cUgTV%jQg$gK-|!3Fj{5Tt#nY5Gi;) zK^lZaME)lcJ8FNs{~rPEe?1^1;*Vw3*4whqK7#CD2v&Vp%IszNCFX&mzPT;HQ^oBi zi6=OYTQR6lZ&LxJp9EW#LW@I9SJSc~RtrV)1;m@rbAS4*eErXBzz`E^- zG7kqPk6Y;}%%VpA&1Kr(z2B3b?>PpYxQ}(KkT4ne>J+Tf6k*#?0n7f7WS!;1+{^7 zb1J70t)%wGVn1Apvg@}NASE({Sgb0BH%+4m-$yC7p6@fh!pP>sbdd3fg1{_a4dFPx zb0kK6#O$DXY(c>x<5*S?H}4uZqsm`sK8SvW`_zo?NjfRZ{G$_`eDdZAIvE)fgbiKj z;(F2PrOFBP?nbYo1?jU>4ziNtzbO0dng~koy^-8I77}OZU|VZRHttEo+t&RS7eNUxD_-Z)I5kGw_hxr12H{pccl>Z=C578m}L)aXX|bXT);IK9lw3CMdHqj!GrFvlB*oJ^fl)w(%>|J4i3c%&xLA zG`JhWP}+UpP#Jv@09@GRD9e_8X;$Q9k0TWYG(43bKECaehQ0MI#Wr(R(oPbe^(v#J zXWIS)G;VLDhu?f=gJjXz{tn;W(!eo#oENHd^*sw=q#J(oXTP$}ns`~P7xRuLOq>4i zYas4!?LfG7bip2K93zPblg6l)!9U&dWc5YYW$H+ zF@Z4JR31-X;ZO=yH!&Ehd-bf|txAyfUafE2cSwk-k$fv{xop>fY3m8qjc24HR<@%$ zE4mdgbQ{2GVvC_GtrezbFJovUaIrJJW7zU!hN%<|HRzgssuQxB%hGsjyhz!Wp)Qy* zQ|`DZ_w_f&Nc3&?Z2Ijy!OTrrXY?h&oUGvH85lkD(NlP~>xCzr zJ};DlPsI=vLlp#LXWmNcJvfg*%^j0_*9ol}O1)zu?QSaUvGtwf!lvD08oO4X_V!65 zp*~nuBQTRnHhk?Q(FFCV5?PmLmop^$#lfm#1+95X10vJ1i=ezVQi@Gveu7Y|VRR03 zf(nEr-}ocjBT?5QKnZu{-Q}6?C4mpddC_eY@7DYNVbtha$H3tkQ}FE z^8XaW-jx@PfxZfxzAj&nw2_Egoa(YV<_TZ>%H~uw3*f?S+^I29nT>7LojZM6aA~p= zH066omdM~6mVDyO6O;ioY$m2J8rit0Q88FR9E_KO z6*JjMwy*i2B>aYN=YZdS#+=sw_JO7RmhobmtT@zI#!Mw6!iS~mTg90R_cNZ2cRQI0 zo%cJ=4x1#?)JLQLsN5zlvG^odpF6N&)gN_c{vr~YTBh?Acq3nO)a%Q^`1+OAiiA`% zSbReE3F)`m_nHP}Iv#F)Y4QZs9I`PFdM(bPZL?JV@F^SaoU(xJPbQW(QDdaVIoW@# zzWZoFE=~NVjrkzJNZR3-K=D}WhgAv5kCe2Hs;k>cor0`?z#~J-JP(-T)UWl1Vi_)l z>OgfLltRCpSCzbFl``O-6~*WLLrgWs^>Z{yGj;7p(M4u#)%e{q<~Qth`-FPVpR4{w z?_c~8yV+2Ng^Mk8g^P1Z_dMsb*daFWeYri6eO;837*g-zh#*sYzGjnh48P`0aDp{0 z0Q00boLUwGYOLSQ6O21JGQo7{LeUER!|TmIAKm)XHdYt;@N>b6M}D$F2R>}bGTjY# zI|ssfNl)Rs0WQ8co3@9MDm&0YBWyZzI#wR>q|4ASUi92}?lKM@65}ESx=15(3p!16Qc=dQ(B|8Y7#$9oj7*BjDGd0;9TM7)bRk77>zA) z#1|c9QOUMCMUyvsi*GjBm9C0bu{{vd|G_||MJ=7Ee~R*59o-hIwqKQxB|nl_gr% z`TOE$JA@urZMMf%d>w`2yEupXHH9ztocMCxeVsa$ThlsePzWOjI9^YKfl1??mKuuJ zAqq1S^-TL#7*T9=4PzfaU`9#!x4OS@uQ^SxZzxZ#X~PjN=LfxK=JcDE*d$-Q&C$LG zJPvFfUi|Y>N|rji9yVv7v{uhW>9B;dG#cWgJcMf#xdeR&_MTohrKofi$SQ7tTdFI%mEA8TIDe8wSNoVjm>Q`&tEk&Jx+Hj$5v4plR*X5? z`pp>G?KSd}Vk01i56WH*u2pBVbCjwDaZ)ZsO5?E(30hBNiT-6KKKx4~eL&h`eL_&k zpa#Hat4jD5fTo=9R`{Fa5wSY%uTdj>aTe-qd!iYTf%DNd(hes7>>F}8y7WhJByZ{~ zGR}d{X=~vE(J&eMSDs#=6hEq%IrEJQTvzqC&{%?|ib=EY8*06!=M^6)BjZcY?tb6BbzT)vh4Hj=DG6uEObUgt%v(d8=;B3>;%3f zh=tu2{Kk@_G`X&`wH){!UKzw_mD=rX6hby2JmGU46p?dFuC(dHj#@ADKTf@j7yH^N zls`@`nK77ru42N;mXL33ebAZY(?c3aXzu8>7G3?kP(O|NLH#9AAUHF+l*q zAN&Sym+xbLD(jmGNh{c%b=~+-f0H&qy0zBP>3r6wxe{Q5@kpTD^4jY85+i;k9uvJT z{2}r1;t*j#MPt(773{;DeH^h_B43#>b|!N7eYcFD1B4;+3e_QP(R6LM^WE>XG^hC_ zD$D2A z>1-BD_pDQb&!B&Bc%w$v`6UTabj&;dETj2!>JZq;|0%>O4Ye>Hl!*_Fx*(@Dvs3t@tOJ_*hYwhgv;y#xruJK-{XI#UM)}a#fq*mqE3PKC$ zS=J{gvyMO$6wdP(7UcGI>5Zr29Ra$6t%>80=c;@n<(%pN4_#js6;~H@i3SopxNCr5 zfe_pwSb{sjT^noM-8BUF;O=gXyG!Hl?k>~$*UZDr@ICgrYxP6jbEDBTQZr=aZZ#@kE9wpmfj(pXc#?+duQhB4t###qKSZ`u*%4G1+B9TSGq-u zVlB{blaQf~L=J6MvBak4y-pWg8ku$+cU^=-VCZ2+&tJqFs>t;aPA&y6(OhgWtJCWq zEeG-Qncj;K(z}Xt^&CI9Bk{%f3M}C@lIc5#n#}ji8>2}338W?UU+K1z9eXTBb=?` zw2$i`|+w)hw22*MNGybIIloU*vWR9eFvocp&4rAR7sRcji zeW>T4F~_TamS#W5O_J`7@&S1bOAHYByv$&C@rGRJL+J-9 zwNgT`)g=(5-X_S6B4X7gf;aQIA8xbxn-*LC^%BaXsB!k?SbJd*{8(pU)$~xM62t5eXYV-6T z!sUM^Mwf5fe=&ZT&&RR#&`oyM$CM@!O*JltoMXC3Bmh_9M;e zi^t8j1zCrEgge_vBBbHA!hwFdVYg}BW_wyuvB6&d99=;Wp-yRRd(z`hnN;M*CH!%g zGOT+4r|qY8stxkzdkrrn2{o^G@4)$G=iHsaoUYyBNc>b1YAz_=VQxJKn9l_whb#t+ znSTt{f<8S!_&~SWj}OUSwjWgho25-UkD_6RNS7F@o)XZo}lKsC%tt z?4!hTfgusi) z7a&A0&JAzArFvI!H?`V}21x3?N8aQG=tx z-SVu{LtZIX9+dt=8ZJVpz1#EAc4SQSB+QYq8p9Znw%Ru@+txJQqioqK6e_a40G@+m z-TIPYmE(196h;;_oFi#PbjaJU`nm%VihKl?BzV<=RhvZD^*B-XbUchdrE$&5fH|7> zlP-(N^Z<(k`g-}l5Q(T@QgN4$(`rIjS2x_JB-Ab&V1TuL^dHU%Be?ovE9%By`-c|y zeQqotsxYTwDt69OUX!awt}5vl_s77FN8Fu~r(G7PjnpwM{a&1^YDG!JeqWO;Hq7$I ztMl*$=F_O_wACod#QD;f zZyjl^H#Z8)z$uYlUjtbYL(lvjJIM2>dbF#&Ofa0EaTtMJue+_!_G^GT$Ol~1`dw@Q z%Ls|)q6G(B{+v>-CUxuQF6`T1@X>(WWlP7ylfLy3jVAR({#qF4Ol=SPp{b_At?z__AxefrG5?9bk=| zB_|Z7wi7NkjnhsNMgJh2yeh=|uFnyj3+wG`A}CfJclA+O@S{l!qp8tY*C?(orxM-NAl_la3lF$74AI9qQK6d{xr&J30 z;2?IF$r=1;Y0sA-+)3Qc1Ge9VV%Q&P6N9#jbwC5Erw`eYF}1(Y75aoP#E`%(e2!z4 z_Lh45tubgqKf0;vRKE;VT0AF5g4T&4h!201mxtIrbtd?W!!htUdWBqxnS3+nJ8^w*4dZ%l znj4(56CLYQ$|apge3X9|dzJlUCFYsfXl3kv@rL!_f2c2rsVjPq?k!qoY4%@3Z@cm{ z<1QUi%_qU;&h6WT%l8>L-MWC?$>V z88!vw(x|!wY2DF2ei-Jkl-@)eygzq3tD8^et>-lUhTjMLD~b;|ikZX}Y&(b~SLm4j z5JE{M`+TH`w9j~3qk!NaZ5yyI+4j(bTW52u%YPr*sVMR;iznFEi>`Y)#dX^muT7MbHSDoZlmp{8{ zY`{@1^R3t+83y}}^aE)z*6Ktd7h>_Icmnjsg;>jk3pG z^?}>7NxzA9utuJoC(Q8N&<(?b*6)Kx~Q=sfR`d4Bd^LQYKVvZ zhQTDo5FQ6}U9XO*Xkj`@XD*4q9sY?C)llrD0=f#N)x+j^jk=8VC~0aAA*d<>`gsdt zGFT)@|4C?=Dn$z+m=}SqSIb@%dWMYYI9LH&#rG@0meLx@V#gac#6{Ub zjj>Qdz3~ovd#A>gSt$W01i`Afb#{|PZb~!Imz$@2>TY(i~KR{1(H-hV0JtYqO*{cB-2*7kGj(&oezRB$gT zJnN|NtTvPyqEb)uXre~)z~#d-5M!}_Q919tges`_q4g(EVJc0w&0M#c15nq!)&#kELHsafXS}SekB03f^?0V2(`lwtfB0?&1a(`;C=!Vu z^u`XSn|P}C??+{fMC#=s$J^SoB5vij7oIhwA^S)Y`F;-4cLPWlB@zVB%vo%iuF@<< z7Y0BpsImSQ()aW9a;T*i~bszeU{^#p0!wNK)-}huyBE^#pLna)zcxYkcUwbi2{l$92 z9e=;4^XlU6&`94ErTeNslV1MajCLoP&F+2Nu~Af=Nu+jOvV3~4m-}{yw}sK~wZM6C zT8NAFW(T-P4&mcBmayt0Y)`szNd=rfW#sEaiwwx{$K{@mXvxI%AYWPcvd%UYkmJ%fVa7jaVBag?*Q96cTFqO+hMdch4TW)Hg87YNMF4QGS(7Z1i z4Y6O$~e_i_u=`X7htj@^94ObUmD4?VH?i zXXT5EEgvoa?apR~HHqZ22n+q9GUmtJuef+exMSTIvRX4nPfF=^`E=|xM#H@iOhnoy z!n?sqRZP`LHAuB+8MINzLfd6lg-&K|FO}96v*qqtFtC7ryz7o-eCQ_VWJdzydzOHF z0GHL?aSe8LY{Na&m#Jz&y4J@;-Y4;X1`IyF(<&N2g)=DRBM%Lbs+7ZnCO0rgzM|@H zTZP*g@|7<4Ln*w`AN;eta6|}R*9WIWlD$RSqQf}h!PGZN0^AN2TP{a-3OC*0PA(U< z1vqv;)A6^lou)io&w3(9(kNoX3k_qV&Ayxwd-d*0GGjDd*g5oI1Xrp9Y;{2v2$F&o z*@jC~8`);IzzRGhhorH!t-une3|~v{@vGe+q5!RLi3yG+F4t?hrAamXA(HTJ+EI1E zQzgz9Gk!>{h2m{}iIc)lVH!O9Jc4Zx(+6c*vAuc~%v!KcHLVXvAIhvPa)GL|M+-t- z-O^WBFivnVPb2BgP3hV#EynHJ;^!$l);xJwUYAr9o(IWe@T2cXBWweJhgmn_BWy&; zMnLOSK8Mt<1jrYtSTZ@~2jekEU6B83BAA|voYndQJbj<~9fxPsOiIw;H|0*lB)SKl z(Nmzgxys=UppbrRnRQkE)vsj9&iHKTklq<&Q0NP)z8O_mxIkK`dF%-j$Ndo|9Gmia zs%hEs32&;#rBl21Ej7v~+uOU0jQ7Lp-xZ-ZQj!&-kH#`lx+Bo_eCHI2=TYr zXqP+Ioi(RJxV__=aq~Qgd5k?^Q#B%meQI~sX^NGQXTN-9u~Slg28;Azu%+m%cBnxG z{qiRJ%WYm;sLgv|1dZwZ4{zJ~0arY@zWzp&sbPYev(DtdwB#lkc2h>#Nn^P1YZc9VY>T#QE8C!$KX#}$qfOD+Sn}b5OkRC;c)4{_{K?WPX%N#wGkrB!kDLs{huyJC zB}X`;irDa}vzO^$AmDqcwM%A_=LQOrqE<*RrsqossncY;XIYqH_4q)*rrzl~EB!uI zFx?j&jjkC#i|844z2aYpOOl8-e2ugFzRYZ~GQ=7Zj1|b`rLq3XuT7HSkZ(a8*SvF_ z(Kt2Bd(SI2^%@X6a&>ip`eb9L!T@97tu8*k31nsO1H5#_*JfxcM^_Z#Jp;#P>Gx<+ zq^CV#-Vrkx2aBig)n*I{OV{@s0C?Tm0^lRoWG@OR#f9SCkYvBG_!oR0;VrZDp=(ZH zXX=h^A~*7Hs7lQ#N1l0u+lGeVQ!7LW`E7;Bgpc&^*Z+-#K=OiXD(so!09Uk)ub=y9 zJ&0Es?`m+cpSIHO+AtFe24BZBmr6Kvjt;=zKA4h+7q{V6ZHbPz5x{aEUF!Ll@9nZQ zrkSvd93=5BiW1rL$;cB{(rsks!5^zhXa_q5Tc8Y`Po8wnTK!7thVs$EMJ|PygeJ#4 z=JkI;SJRcL7^LeNLHif$K%F1-3AD!Tj7PFe#OE*Z+SqX|1KnA)w2yOknY8O|`(t#^n8Ta@7Qk*o}{IgqO?{d%R85Sb)fCAWt#|8oqsKO8v73 z{c7oaWLs>}yGzaxsQjU=H22GXE3Og?+AbaBqVu(@VhL%}&e=s~>O(qJu5ZdN-)cRc zFnqU)$K8fj$^7Gy*o{>K5Jq5J_6853zW^F{8)ybphDQE)K13kxX9XlpBRTdV(rB~Y zEkeLc5dTc2(^%Ixi(c;Ls>BnX%EU6iKNm-zV#>upwCBX3P+q5ZNraVW4`R6O*SYrZ z+V6kYn6f)Ro#nGno$8j=gdK7PwX*Ln6)&*vKU-VUXn#@bB`}u7-@VyEorPl(bdh7N zd)a1#{^|&*C~()x($o6kYa(GL&JnM+7|#5IT(O56=OJ}b;Sx@8P94Y1c`>B0teQnW zNEThJ8rxsHAkty{(+kCRsI^6NYjYnP51fhngWI`Ei?~E~*|D(Q-SshZV3(JL(Hp?V zry1Yp<#ne$C1$+&jVs`dp6CrK7&#HDf1Y!`0o?0%r3o?G2>1*}rT%sJQ-zWQSeS|- zytDO`l15_wjX6E74F4RO7+wG5_f3>AtXF&-`hzM`SKEGZXs?)OzrNyS)W@Ts;a&pb zUfSL;Z6?ANh$6p|2-|z1z)R>M|N_P};ALugE+hKnIBW-K=wuF0$=GdpOR7 z*@Rk)aSr>Wz<6nJ$x6lC~J6uG%WGmF(EhkZd5 zp^q!9TmBt3eh|uVDq4u>WkoRRRYe$_(q3%t2DdBl&8U*K^aHZiU=9W>fT4+L;6{0@ z^f;=4E9$4!3;t#@mfphaND7wt+qO3+*ngQGWwmNW{*8-aMF=Mba0e4ETXoPF%@}(f z4Fe5GNvEc$UopQ(utuT(F5pw^ro<(?gEB@F%^>BuH{i#3$M*gM6&7A0*-;h$h9d8s zI0EZ7C9YR4gJ4h_iziP+Ak0gcEINqE$!V&$&P^;;k4<*q;b3ay>bX2gHtl^5TJMVY zGuqR-@dGmACtwdql2<+1IyY#nBnPnNvGZiUa0F1@#E;kbN^U~~(b($dm)ZZWqwb)3 zJ*L8bdLIV#2L=H1{E7t)JR@DLM>|fvEcu^}h9yTwBm;m?b{u2R`uq<~+lLvY*tw%P zCERvg)Gfcwx>CWZ3ca0>eKm)_QtNOJqxO@S`D^|9Dto5EpWG;DWjl%nG+AJr497|l zwrz1#;%|!qBaQ;SSyf(7)ct7S_>cc3p!t0#dpWU*=EBl=BfkD1fDcH|QsPt<)v8K0 zUrxOblDSJQF;{-yJMyMjUOxx%OF)bL_6?+hr6l0=>$o}b%S2zNG{$DvX^u;RVTbDF zuv1!j%8$I+5kW%g6dO@SY~6NJzA2Q{nGPt9NU{Z#%tI?%JF#oceJK9LKS$AS0I!Pn zvWIzT*2uzozEJG~bv~7`{Dah`F=a=OG+MJv0aE-RghYA=HjxKdndonqNsaIuc+M9El-Z!`gnq6#2`D8V zb}4yxX5>6N{I1tX6?13Cw<)o*?PkUzv}bs*V*PBMY1Pj$%Os`ev-WI7zyO$U&Nl19 z3z&Ikq&)_xObjMZa0T&nmsmLk$WY{7%;;6vXJGu2o*I$FSqPk$}Xo^OscC)4rv9>0$EGT-yLJ+DqWPl2KcWIc%dG-@}@a94+_}>Uiy=yU)^f1IaY2pdNdHnWAS^5)zWu zCvTtatS|g6+AJUKo?_fe6DF@ovLn7|ByF8DY4UM~Y)yP```62_zIH{<$Me3|u6H~= z@3FTlOk*ms%1bB%+&O*aZ>=4}u?@a}xvoo?vl zx-a{Z%t3*(dyvz~HP$$$pSACc7NTg2UA$i;8g8z0uTNW7HjWrj6OP*qSYjk30HjFWmXsa zEwn?ao=W#LSz*RB$41@NZ50 z&A%DgWo(UCL+G#_46?HtL}ut8^Q8YuKh%s+GSohh5P z6DeRI)(6CM=Bbz}`ATta3G)93Q~yt-*OX)=^9YKeh#C0^3ol$Vk02b!n>Ptn}u9Yr>O#M#nMg>I zh-oi^wmwgpBuugm;{{%D!uCGkO!?oC(U7x0MBG7rzSKI&^It@u!O^z8Nh`O?ecdrM z`1=f_hVl9U8M__YZ8FPh;Evc#vtXa>%2ySno|hcc)|74~R;X3-2Q7y{{VRK^NU4~q zgusYE=~qF2;9i3E-h8S#ngLG$x%sG(Hrj($RL%HKIH8WISL5N9tOn&Ukw*h})PTX+ zfP-Jpu0Wi&g_%$x52>K z?rij#!LOh{$3%`lERx<_1Aq>bjegb+A3pixEhn4s8k0B~`>{2ApgiG{h{5jjjh)jn z$HL*UW1X{hsDKCVY6elR^&KG+G(!;`2lBg+cQs|tb0PzdLf@Dv;^Bo#=n@wv;vW=b zVh2miok=2lzt1Bm*BK4*c%p{yQZ)L4qold1f>wBqc7k*`5>Bkg2;{pqP+V+Ec%=@- zZM7CI)6)Dk#)~>FW={C(qW=y02h3dCue%jIdN=L zvRpiw(`acfOVB_pQjlqewqiuCgf?%kYm(to0F+`Gk{@S`R8QlBNMd!WlM6iFx8es) z*an>L-}*PsHqx6x^vDiWjOyC`b7Sf%7cjiYRi!apL46Hm9cT}1v|Z<@rF>%I!$BVB$j+9t5=-kE7P^~=-k8=m+raEl z!E=+|?1xa-FaKYM4zU<6LME?9c5U%aRNbv?G#K~?(_F67{tD_@j!gCM!>`zp)(t6K zPmtYve~0pRdHVkjg zGZDy*cjj6}-{5JZ@UKAu+}2J)rl8`u4IRTjw~!p~W*q0~cvYtC3>71+*L_=+x=mM; zN-Ncds6wqITZzsJ#6&Q>cSkV+Nms10b;n&dIP*Zdaw3>J8Ew$gTsKkU>lVe1RB;s9 zd~fQ;(!&{W`P)97@Tl_WeEo7*G2*~nvRngBw11z2zr`Q}fhNaMH}7T7+S6)i@OPeH zL1@#;@6{0tif20qZE)L^`>xuwCpdXB>v+={f>@0{?H2kuUXV$u;e09I+-ow5^hqy2 zuu|5IIe;*Ct`+;dD*guY9Sc^j#oSX-IVPpO`4*7@q9 z>3U3zPuN>yHhc>L9c*TSB6`!_Cmo9Pg+4qa8SVCbfNMufDb zl%_2N8E7q)X*jkfTRE5$sJ4ktT6Cj7M^Q#&)R{9$x*gI|dE_})x$0k7cIKv9LMz4t zq^DDhr(gRIcc%ts?N{k#JF0vKXFq7j9YA*LwHD6O;VFuJAtW>}la{ypHkA|(|~nuDhdKm`;MHc~Dq`B`%OClT8+EjsS7|9z{=O{3dV+nIxD zl6JN%7~YDlwD+2 z{KjZvXHVxE~goelSpaH z)8RstytdqKmMs4e$ZmOQ{?K8H2UWy5Wwc{E#+s}Tc(V2J{Qm0t_Drez5Br%5lFScR zG(*0u%HG8-I#-qwJ|T z@%gaRyRDIPoxa?+hOp){C_6OX!S+Y|rzmK{+W8InUtmv!sRh;>KgsNZx67vl+=g|w z)7BcB>6@tNQt|Gfn1|TdcF7iNyZl&ggeg zFCWJ!;F5?src=r^F@ipW;VY@dlpjk~wG$({M<|mkNn}-r^R&D+-?(ijZtu{opI>{| ziBi7K_1fz}&-~S>xX@sd;L4hVY5R@-m*<5>k}{GlvX21fkzWDzJ@@M6LF1KER(8Z7eJ~Ub!YKAA{wVQiman)W@JLk$ zBf)O{FFe5bL}rNIW0Asi~SZ2;r);0T_3sO-B)J+2+GG@*ml~RPMe9k zCIOX<4{5dwwoM(J2XB&0=rC>=w68A}s`Mdm)&L$NpG8|m7d%q(%P1niNx?4h%Up0i z?TS$a$(&C;-b@}J$3>VwwTUxaC1*UnlN`K&_m8oGd|bkh(uG40<2`rJH3_aImL1c+ zJ9vSn)r*J|{RG!OTcJIN3 zBRLRxM*-Kxz+Er<;S7Bb>XjqIEQi!fe?>;NYE@HajNOwUFB=7*tc?%HDbA_NX~O9Q znP^v%aZM3J256(n+bSt7WvUqlGaSNtpo*d@#VP=9$lITZJWPOWz12kuuyy`)ky}<9_R#{m&8>3_`olay#(| zz?t5~V7Ug}jxe33g>xR{VM_Qabn01F&*+{wK3K+td=pCO;$AafE8)H+n_AL&g`(Kx z!h2b<#D#=(%UnHoqI7;s{7mXiwpvUyM?{tMF9#E2)b zLw1{~Le|{KYA#Cxp^1iFMwXLu9l0y#WD-<3rsCMN_&5oA{J{1?MJ~wsWFC710rn+q zv4Q8swAqpDQJwMpbmhn}R2p`pHYcfS$+9N#i|qOO-%>3|`%)iWhWSQ2AVS;SDeu=H zF1~ard=~wPichHR?*$mqzvrrb7+*{7Wdal6l>1I%d4S_&kzzcC$9!!@zD6Xemv2L0GD5{pR=GP_x#X%i z>z5QK33sBUl6o|cOWmU=DAQ`4eBmKXB}ZHCSxZ{P*X%H@*5F044c ziE!I)Qd({v9y36wTfaoQ>n>y;L)-m))_3AO187y6=Y0q)XNB-kr4le)tNF{_Gk%4_ab*}4%EnAa0#oz0V>0Lih& z>Y2q##fpW}7YK&j<@ayAH5=|QWbAv75z#q!EsC;9Nu)uIt zI%RBiH*?vjI3l0HB|qM=Ca0iA43Z*ICZJaj^%{4vM>N>{J(IkzxCZa5READ15G@aV z+;*of+>|%pVnotzRwuC!!X{iq4-hB*>us89)ew~PMr6t0`~}J6p7vS3|Ca@(>$`W# z$~E_o0$fI;%iDajl8i~Kkyb77 zm2towT>cd!U-q?Vf_pgeVz)O`h&w*HWoR*5wH+8zK928Ant~*+57$9lWbsx2n7DF9{I;q{o9K zsk-)=u3DXty=0R$G1-oi02I~ML6&H=Xvl@@biG+X|IUY7CQ=VMTYKG#QM(yi)mnbk z`N;qV_$Odp-A*|J3sxNvii12H4}G@IbMZ{OIUrW7nCe*@X6pqm-ktmbJms`Rzwe+Q z_q>Ia#{;D0>AD0Ud|&mFRc`Fit2` z7bSafdnstkSo!(pa}-KudjU7H!G?$}+a@38m2KnKOqgcK;@8@xU^K$sdUnfqz)I`Q zd>_Ir$7Ugoyb{BTPwU=2T3O`pDTWNXRvoNx+x(n+)bPL98;2o;e5Bshp_(hN&i$!D z; zb6}$WJ)A;`^anl=o&a(oUvX=Bl>%3ZfBT&zmQp`@$_8Guk-pXNS zY+Cm1Q`oFwd(6CXj{hZ$XRm^fIB!1#Ws%8QTZ@N1?oCj;47iH+==FTEzyjY)`1uoF z-UV^<7wCncxW$U#*%@GBoj{=S90Jw|g`jBo-!bnrc;h3}EyrkF;o-#L!<1{&d9uzg zQ3?9)&Sx{VpJm!NMhva{-gEW2JSbBUC|q6@zn+R6zxFqR#{$3DO3X%F)xGgq}Y5hjDTy4Jq!WTk`srFxl+JoB3 zn3Z=vR18$zwrWoGeWrCU*nZDOf|x``_IcxLBI}w)I3pF9_utTwqkv31fx$2l@AJWK z`o3@K`c@d?5@fT(KX}c@+^F4F1#L^B3BlJ`a1uEhpwl%EovLW<>ZOW98f$iz@-lZu zeP$Ix9e#u2mXCB<- zHF?}Ux?@-Q_%#hI0fWz9aRIb}0gPrVc5VrZuqmBj#G;wZ#tY*r`ZQox)7OD2#ExRv z;|CBkVt|W3O&_eNc6H~Utb&kYQ@|NfDhe}=KG7=&oN}vEM46KLe>hAYg9V=xqCJp{ z+#GSn>x;G?a>+Plb3{gPWUV_bnq7J(=~x5{STud;@}Ru6J7!Q6=kBlZNq z(pRjKq^sAB@7(GBY&ye3LUWw;r%4%=jct5Ze@(>8h`$lul!Awf%Y~k_x-a^2`$MF! z5eK&mFHrQ1cCLyKqKd|WrCa%QT<-zAsIxlX7-&eSe$6LG)(HuT%Hs|8{_3CmR?S_2 zL}u>`_CEJN5gg<{v~6M@F2~b)JFlF$!WFe+FI`I@LcP&caw;H7Rr|Q#ebI~xW%N8! z!dh$YvFu;==dakxRu zy6y)%z!GXDYN$&vs6LEP7cLjKRLFib!SNLaZrG`|p7s2JOPlRB8L}c~1Z{M^CZ}yr znSedf3$}0L=_uni!&5Y>Q4AKYBw%{qDnFqdz^P)dz@-Ey(eO|vzB9=~lD3F@p^Thv zyLvvS`M{(p-$Ep5N~m9KWi;#D%|tBvf>l z946qYTss584$J32a_|ASr<&^xw`%vm1G9k=X9{uhNdN(g=fNbnu%}%2@P=@L9x+hi zjouOzBMdQ{Q(ik97`>5(AkNiHFOe26e>aY7$scsNo(eyle3jrq3kls3OLOA!S-M{noN%y)0NE{f|J29#V4hTw!tcS0Mb_sPbr3zkVW znIN-M8M+VomD&rOBod1EuMKcRW>nJCyp_{EMXNds072ZEmaYR+N??@?9l|1|11X9>q#jN!=I-0wzycg%{B$}m2m${9( z+?4IuAo79&rU5;P*1x{Pv5;`6Qc~$oHfEdq;bL~dhRD@IIvK12zCyCYj#&QP{tbu5 zBZ>LY@;Q3}`O>wojAwxWPaco_2YWr!qUQB2PPgAAKTF~--5uo@=-qw+G8NUg(1thq zoH)e1ydchu{o{@2Czg;rk~8`UDMj|FR*A6na|S8P?#Di*PU!GY0&|DKmgk!{>+?F* zxR2+&MgicDRZ8;1WJn*tmd16)$5w3TP{|b`YM)f*m!3pUJIo{QH`l2c1Wdx%2^Zgg z$FC>3HvGfdpn8@<6U~yCLUZ`Ev|BfcD$_ytvzjGkl;Lq-H37^HAj;J~_7m4dS{*3H zHJN-|os$_FIse|2LW~l^6aM@HS>=MU1+ku?TCtZsSwTTFpr#t|!iUj}j_94l84-e0 zUcw4xI)1S4%KFu^?OSXSbk_PGCX@z>S%~ z;PGMSUdreL-SXF_ogM@@&p~XqCud&kSlakiN(;Fpcnh2tYk!Ul?u=#@M^zcmuZIO+ zWpMe2^Z$cn<%D_mg2s4T;_(MTlUF$9Cx2W7omkw7HDjh$6=~88e~L*p!1OX8z&R?$1Rsk7*U2eka$)2uzn`N4{T9 zZuzIg=09suKR~BT)drt#!9w=&fY;rq@DDRT7_@s9#_$Jg0=5jT;<`k#7i+KMH^3Ri#HlTM{} zp(&3f`j&+uvZS3kJ@l>S95M_vju?K=+9#B+;{HZrhD^tkLa&ybdX2Rm+pP8l30Vma zsO;n3lz3qBTc%mAEd?@&^mw`eRCFBQoeRqF_IK<@7${3gdRYXF{*jsds~*l+{JH1{ zyQl!d06}k!ZRh7Fu7~2*K?hU6bDXoUUWZvYzZ|ZF3?c)FBG3dDrI#m>j~k7|JNKPN!|5TlwaX*} z?->sXEPJqlmXAY;R%A6r8->)X%)pLE>l;A&Km=y!80e+)W+4$UCZSTh`cb4z92)ub z9sj7Ax0hI@#9*1x$o!48`%mV53<6cWlB~Axg;KqxBCH{j%US!LdKB6=30Y7g$R(zg z3_k8H*=L~icxP+Tn{vBuq0w2DsQUXsXH{GMb9XxqFE*4}qmWY*|ljk7@%LRh* zvEEb{yatr%cITcmbyBsVX}O6_;8E{X}@6TbPjtN2DHB8D&bqB1wvE6JPJgH^E-R z5C+~QO=L3>erwws=YFXYacMZO(mIfIn*KJbU_oh@i3@Wv2(S}1a_32Ep4#%~dTzPC zTD6mxG`mv{TZk+uQ&PFA^;rn&;pM7!8yRIUN>-wDlm*C!$9~%d+h%&9*dyFGM!6uJ zz(Ic-gVN+lFVp_ZG+Y2WKLGc>=gZY3wx${0sq8BCoKAlHf>|;6X9C#vAr8gRko)ru zKJ@SM75luHm-S5m&vEeTfFAY^1?etd|8w3HN_0Ca>!V)l_HjxO@c64t>f!-l@!z_d zp1XGqCuf5o9rv~rPN%*&(P57==&7Y&jV97U!es)=A5cN>^w541&LbSEiJMx_ek)kM zLB;1TONk?8G|lj6$XOuwt3hIUXH27?L{%=sj;?cTAjMq&(ZU4vy}C>^eIWjo?bx~k zczL0^^X6SA(UPf^F8bFuS7}v@A__p5&eVCf=4CyiKH74hCNz5Qm6(v^= zJi2^yq)Hd*r3T@)VM?TG$iyH!rfesHvV`;9(u2&y3~;%-c<`^y5oGbtWTz&kDw>rq zVwGY9jinMP5sVhIF^N{HG^wi{IU`3JvV+Zhp!klpDSV^XJA*YfPU^TWA#aOL9GQ-d zrA;E<$VRm?n#;uE_6uPSKEFn!$eI4m##a?hnt@u~k3o?ZAHXT)nBobF)G3n`}=+tOY!4a#BW znjblTK8`0CASao>nR|tGy&x;UExc@$u-Es|z{6?~ko*T)s{r z5f0227vndD2T<(s)xvf!{SWsIHpg-evz&}xwFSH>Jf>B>4j_1IJ+;}nHP#jC`O2=D zLIeZqcNwRN%ZFLb${l21k;xf9$T$_stgjiOeKDUHXo2I;nv&R9SWHTa?`DL9?}8d7 zJ>~~#q4!}LL1>?Nw+DlbMehcg9#vI)Cnr+1)-AkW?-Fls1=y)0Dh`c4D+ur&|>JMPoD}fi>IfKY$)! zHDB?36@5v#r5t9oF^IoP`UO!W=t$}JhXP|Uev_kLB8Lr($5}ZZ;dGr98C}Xbp?j(h zFU5Y3HaSGGea#EkC0vW+=2ngzt;a8U7{+4itT!`OKid6c?FkGEA@(E4I8!M@&E(AW zNX^7CK1r6UhYcdS+ea(Uy;|V@;i=g&j_NDGyXO)oJiy>Uzihhwk6)Ki3>VmBvR7qn zXYGRw2(pFeT5pVNsL>4t$T=IK*I?`NczSB#Tflaovgx`8N26nbmuP|Zv&18Y{($on z{X@xph-IZ!5;J|n(p{3@b308DBMG7WT%>Fz(?jQ=Ly86EUv;U4Ko*1BzT0RbeDDzw zNBL{nrP4&11;MIwBUd@$80A~*=poZf-2Ys@`>UC~U*0Hx^`9V4GJfW&wg^jtIRvph z5cMMzOHPdwjL_$E6lZ<_X8XeOZBCidqDOd&VXxj8W^Ich(&E!DKmVpEb@9A-wk!_c z-rmNi`cCG@iRqOQ-~5^o%n)`OB9ZT&vla;^Q>tm*`;zlts{XK*vpmY;y6r0#^AzGU zd>{b5BFnVvH`)Lw#VkDdP*I^@V7SS6TAyPYuV8h)d1*wKZveO;1CGZ09(`RvcBV+> z?KO-wI}qj`=X;kADwVo(sm}((TBCZS+wuVxAk{SUx#aPbvmRT(dXO>31UQ3&8$0hx z_h#7kR#ODASq)cRl?8*MS;>c<82d?3_iK%l%ePrw7K{+pwwGo_;##Xl1AHIusRZG= z^Y`rv*o8sQ){1*d|A8quq6D8xuXiZ>-yXFG_=74?b|t%-<52cf2K%7(x;%OvukoQt zUk>AL%`kf$rwp3;%x|kJt3w!_1R~(2ukJ~osPl?w<6(c~8B^7tCt5CE`^~dh7DNbR zKb|85tVtQ-Mj`mApkMfrS(d3?a(c&45|1X_hJ7kjGYsF_NU;rQ5B zWOs&Wb5aU$1Tfjj*jeoub<84Z)8GBvb&W<*$T+BsDIcg^BdnmdpHGr&=$#wzuGk#j zz1scNvoOo)9}Ih0U=s3)Vs^CPXKQT|e_!Qw6yP0gU=L9qz0rwEX_}@^QkVWcZFPWX z_B(}C8;P`M>wu6Vgwm&93!UlrXO*`l4EYw*@_E%?N@1E6*b6=A@2Pv@wclv#{6X|S z-p@`i^f78&Zw?eMeMVe*5<9mU)0ZVd-y*)`*VE~~STIy+w^g%1xFHuT@tX^^s)u7)wn{V$ z&(yashQGZs#dBBqOKoVo&DpmkgNBom8Pl90h(X5$(M6B!Pt&6}lq{p0y&1K~Sz$oU zQmQ1U%|RVK(bR|D8}jh;i5~paGRH-fa|zU7;Mv6j^$t~VOkzqid`Rf!ozv*8B~xqz zSJ71k&QmSIuR3seG7AjKuxDo9fB><-^#f|@AaQc zhVip2OtwM=kw`6e4Up0jcV3YyOj_6}2;++JVo4*d%WYLqmBX=jvLb6^6y6N%awO<~ z(e;)=ZFXU|FvZ>7rNt@kR=h<^ad#)U1$URWcyTE1P~3|IcY?dSyM1}ynQzXVx15># zSME%HtX#g=URR2!&C>Ds-t*hYl0sKk>w6}X%z+EAM62aSEBF(X;?{IdxrY1H;jo5! z-*o zOz*XYTw8y%-_6+)yDt;+Y43{$HlB-H&~a1qLys97(j_lIl$sQ0!3`z1WXUScI*=hn zfz(st{D^Ruj78N4ExdTRW#% z>Gh>;(igz+c9S;gLCKExSua!Vez8eolFMJ>zuciVXqR&fDS482eEVaM*9&;vk)kOz z1u^A$O@JKABLf`-MAaQG%Wlogi7ZX-uIUXBce}n<1!PBj*phs0`?*B*H(~+ip$#fVB!2Lhleg3aIVJXp{Y(pX5zvNK}mG!__Y#Fy>PQx?uD8mIA8dBE-o^)1=Va|zX z3mb3B5jGoErsShOln$=c__AH$f5z`{s)%wg$B7@V;+mBmf=F)vC+zp>FXOcer0b4A8$|v>89ZMZU7U7u8yG+Pc!+f0pKdx0JSv7;1e33%DEeh23BAbi$$td& z_x@&wFE;3M+I=({%*Jy}68E+iS0{XY*q_Ce4lV-x0or0^`<1D5;&%PNqHsg_f5JbA z*-LL&>)NPvC(nTW1zzK}k4pa7kHsfkT)|tDz~*xM`b8_2m0^_ zSNNJ5%IKH}wmKL!3mXZz?n{j9!B#+VuCdCH~m4cX3g)p-}JpnW?y))_arc3zbC??2YL?_dPiJ- z{{dUgqV)|L_A@1a4e5}-kz62FcLBX@{aw-*;m>kg->EG^w_3fLy_z3IMmKj}4NBFI z7aoO9kDX8DZ=W^BR$8oQv`ejDZ&qB{3-L>;q?R7Zi47A;I8rkj0rM0c9AC0Lf$8-n z2E2rxH_cIt<~=N9<}Pma5?_?$4JqV4KVO4*rj$hLeKcSVdSl2b5f2MjYBF9P(s(CM z?@zV&4n22aD=yL`J)u@D@o24A&kFU)^F`bFzdY6Neuzan^?Rn&>msZ+^k8$QuwjFC z{Ce>!uirs)U=viioHZD)&yB>Lqcx~u?8pq;AlzIfO9I)T!(@8>lQulrdL_f zHueHuTT(ppE);o?N50Tl!DcRQT&V#nWSeiDJP@K7^uHfICD;w#;7gaWW{v;-3i4&m z)Xo9A-y}n{yEu0|N{TsZTuhR^Pr!1Dck=Vt(&=@#sa{#mV>qWN&pWUljwDmpTFbPc zb0+`AyJN4*Dw8uPTS*5v&W?`>2V^1n$$tHx2K@ALmOytfWqjBdlx ziEe|@>0w#$b@1nTnYqk!0rZbn87ZYb>o?c5B;KIQ?1O#1T+7BMTzy~(8?Rt0PYXM) zN6EZKJ&4^EHjL$PTC@BP^&opY$Pbth5C3G5wrlyudvz-}I~g$nz)VZ)B<+#*_d$0B zye1B*6BHyAY&@-(PJZimpHyPGbs3^IB?pA)GSN3l;swN0kx9>^5 z(|E`;(kB@A4yI6NpC1g$tmE{OAgjlDHVS?H3ri#E+(fA`GCPl6|-0c|7v^t_HjAU$8y1a?9AEa#&F!n;r3V5ll767wjQr<0%Xun zcA&e@22DOG^$5?cXhu``taDNyn_D)hTKF@E(JnUDnYaHTa1qBgK+YOC>m3Yh;)^0nqg%=%)3Rl5rhOSkMA zfY{yQRrMHoo|Q}bWBZo0e7K$GIa80d7(!b3=jl90{Nszlms$J=B19wk&!hff?Q$bs zJ`B#Hz4YF72XNstm?~#7*v4QMao;&e@1Xc9KfT_Sdq@+Mu)EzhE+8@V>db(uqXRJ0 zd)P4YO%)qV-a&q6KXO#upZ7b@#W@5q10}rZp|d+Eqzeyq`VP9D;wk9R#8x8-hM+7X z&xJz0W1~%921;E|r8#F<8&R{0#8>U%{`Tm1g8}hxiH~%y$+S1mWQ}22-EEYaw)pjS& zzYPG{3w7m*m z`5z;&p4u(m1bKzc`F_vP&=!0rvWcbtJIi+#vOo{Reg#M8)={di<0^FIYU*@Jk!Gvq z^n8ITQzh())_!ww3qMEWtQ@^cNWW@lcaTL&^oqKk$`(GB;TtJQ`?l;@U*rb~St94X zG^TT@Dr+KQS@RMi3MPdk*@>~nK1!lZd2*^{PB zMZN6}x-D`MfNeyh(&*Y~@U*g+3e3&@;5+qHPXLfWyu4}_8|y=kC5plZrTdfx_7+;1 zAV1ZjB?0=y3tj}riKl8}_d`S~amQm7zdsFDf8?qaikyxLdT83yR!AI}_;Ly(x9y?rsgxu)#p-aDVqM~FN<5`Pixr8ibZcb4J%Bnov3|{1hqO0SX z#qxSwZ!+jvZ(y?{t!wlNsFO3_2BuwZ*4l2Y)N=h9@kV?F(q%)HUB1BYz5E+sN2C1} z^f?dwWfZ4yPV}(?DDm=*#1wG)O#;{z^ZruGXfVNVp8Q5oYyxZL~fAZ8$Cfx$#cc;3a_vZd>m* z8h~DWvR-_5BNvM?J!46$H*^N%CSHHfAv4f%O7z`2lkiMh1TuNG*6Qdoj!`?;Fm{hap4jo zO3c?^_{JN{b$nI^8#wflG^ht07tG)R6DZ7&Uz@ne683p}j8u|~2@noxG&_%K1K{x3 zEGbcL@nO{_ZV}0Vw~6|mQz0u$o#gJW2_hGh^f9G^8^XGC#J|hCD3^r@|7MaWg1`6& zd?A2mcqR?3B=4A(3zMt&)v;K#IF7o#aa-CPqe6_jZT-6euvqSGfp9G(Je>V3SC2ln zFF(X@H$4AHZnR%}I!!siu~- zD3^LuKt#u?rNTGUE)i7km@=G@6QwwRH#Vq#(SeBCT?2L!mV@k(K(WP@VPW(%XV+si zHvLZwB#<8w!J;vW%bKy*3iSI>B`oM0MQ6yBA-JQ<3DENJ0n zhu6NaSm7-%~sEIG6@iP;$5UT z)Evl*UoxO!_O$%&M_{QM7i^p6)E-gSdTvNu#HnIGm)_hL{gJC+@D0!QM=T40pa>3B zR{S>^o$x^OfZF;LS+B!V9kVp!q1kzIzuRuI{bj!Cd`&B(=R=`t7A5bPp_a>B|LKo^#8FuLo{4?P%+LC@wHAs!9ZGnB z`G+IakWr8XoD0=IIAD6tkN8Ua&;qmk1=HhY@b@?5@k7)HS*-inw<6bMm7@o!l zqc2(^n#_4RvN%qR@VY1^@i-79un5j_7rJf)H?K!&^a)6{s!WLWx#WIHl8d8;L9#6- zXx1;F#i(B*75N(`Oeb^2_JlH-)o*v`-X8=3cQ0eY&(ei zMD}k6(nJVIvD6P^eb8(W&C1@D^n9NdBTDl?uadc5w00v`Kj ztmhK7isMFou;mQfzLiHyESLL^)l-_D%}_xL?6_DZ$qghyI3H(A|1}4%`x$}}jxLv( zBiOv|X56QVw@*{23ks(&Fg0JbG2Z#+J*nGMp!+<2_3l2mb1Z8Luzymy12>6-%Osbv zGC`XJkNfQ-`r1|9#i@&gxbI}YOH!&F3;#kUs&q3I>o<`vyVhwYl;6~!hN6NTJWu zx*TnPGrfWOZc^vQz{Y)DS+#cNo!@4<0|-k@6ONvCRG6!jzoBqxxuT2J&ZojuM|Zto*FD{iH6nIxL)^Z3TrSS` zwrVDWoBfhYJWSNhH99+5Is%o|7GpAdRjHB^ipeC!igo=Q5fy9gB56713T3rD?IT7b z+WPs?DI8$s5|6nX@>#tNbHAjOFf1Rl1BtkAON$N+2}U<($2ny9w+l|b#LgNaap### zW3^@W;RT>>G{Ra4+2ZEela$J{-I_i$k+g1=#c)rwX-ms21E7cQNXY!fOplS_VpFF# z=SG?0q@#cslN+giWU?}?jLFUz-4R;KM*20<=IW!Bsl^|2-UyZHUXWTz;I`5q>fg>P z9X=p6einD<-Z4!#t9Ywz{kBVn*z=|07>({}0Da_3FZ zG5PN(ois3*1=g9qJPayp(M~kK-A zIrNh`zoPEaiY@;Z@5`xv>KyMXu{|S;2z)HjY2O@s^?5*ixg!6ve*ZT3hgRQBTk%@qd{s$6eg)wQL(&}@Z(Btz}knicc46fLX zQ>4Y}`Z@Yo={&2dnPlm*^fxdJ?jWdu44mx@ zRoV#vqp)m+#li63>if}p*Ua1$)7zU`?2q}}^bS7~HsW~hH3F;9m~;S9*YhF=`z`Oc z4xO$nl+W-0BXo+Zj((z4Nyg^6gn|kAg*UYOT1PMh&p8|o^qa_<5=Sy_Y{YdBY*VSh za!6-nMqig+!vP3>TQO-?N?0;|YcgFMejXsV4O!b=K61eRV{HjEDBQ=p_Q{QPgQTK? z1Og{&`)#2k>e=xa5wY#?UF_q-jnK}%q#rtMyKr%w!t~BvlpGOm8auNun=6Y78n0AF z{gqU}y=10T0dsxyeq-ZwNG>-ZwrJ2VQwa2pbeapGVtJsKWid#Mmb@q%y!Yu2@Mp2f zj$*Nn@$IA}PU)+pDW&mNZBGdedf(aPYJi3unobsX?}B?bMthkt_*sMC)5|K*X(NaC?{!m%Fz@1f9V{;?7Z1zs`%Jtm@{;DUO z7%upWb{&o@Jk&Lv=JNr(dRHVAb3Bc;4Z>pbF&W4AyjlT`#TZ#8>^Ygv871NR#FwZB z$~$EF#B?aFrx^PMS<&mAa+mDvKZ3q2eCpAiKUPmhX&bNr(BXU^FLYjo)FDlh=qbxq z%DEk^;jw!CrGOh-gmR<_A!tSD<=c@O%M@lcU`Qi_E&gkGsa9vIo1D8JcP@e|;S?v%z7eqr`vnHslkpAat$8a4}P$zyp_+nQr*N*U$>!V=`yyi~iD#vC>3SXZ?e)ZT|nn3~~!mR?KAYbU&JhNm^Z}6*Pa^#RL(a zWlq;PHsf{o*4n9gS#$x#3)=OeccO^il3nPY1Pt%LO<%i#&@37?Do7UCh+`ROrmETe)(3# zplCivUV9-xMj+7nI5>uELz{@^_g<&-Uy?B~^Li@_lz&c`Ro&0u9u+i{V{-8wi~al< zu&DViYW3t}`}SQkKln^%m$xI#-fyKrp$&oE&AWT_0&0{niqCGrEzBiSoY#S?kLSfw z7EdLQ5uH|lv&y&HANAP7!Pcxs!2By$7Osa{vFQYQ`dr+&^Wp~8pCeD$*C!dd4(cdL zz+5uokRPb`;iTRB4&eeVHoA#YU)GeaJZGbr?>r;jZ@ zzF|=|lQTf2E((GsH$yYT zUmDbL!D_1Ud*%KzflHJJBUM<_B_qK=dsuZro4O4oU1_o!@Fj&aNMM%Z91n0iPV8f zaI>dW?t2anIcCBqX53WyFme0-W|J=HGXLRbOHcm(0o0|~wn4l7!Jkbz=&=iN`(ixl zS3B@(6Yms?A8(?3OovH&Rf^-9A;||ncqUI-QMcFJ!udP8%qACU+-7~Q_N)o^9PF4< zn~ zc}FliER8I;kbE)crzjI_8M3KvhQ8J_at&gc_U%NTqNp>;gOp4Z0O4gtZcND-7~O?A zoQX7z;x^27in6w9TY2Ui%)qNiqtJyi&PT)K$4zefTo@kdFvGsOgOh0Oh&O{~5*8$f zbBLD^{5!J?aK&8HZ5}B!F;C}QAEv2$1uZ2KzHG(6q)WMLaR$V3SPVXQII;K&;GEh4 z6jJF>NcnQPyhf3)w}hlza0cqyOb^Jq$|Af5(X9*D%bF+AV6YpH_1sCSWJdx6UT{kd zj-6i{mUtd{_iiWg>w`9t1>((yR;SSuv%dI`ZOjKD1y*_ISEtHiFEH9>!P3-3s{IJw z-h#b5z989Y7`7MaBafweoI{9T73nU8G>v3&CfpqG_S@@5nbrbpd{~><3G>pVb?jzG zGRKuyr9V~7FJ#%5W;52jj76d#m-~k${t^QYv?xZfP=u(@$)72ET-r0`B6Y|Db|v8o9ZE;Hv79yyFWYmv4Y`)Mzt91YF<5)Fn> zeH&_ z4q)L&)kKr=yeS4O(_x6lJ_K{GaCIyPQLXTM4?Pte_SnBf&4oHBXOn; z#lS3y{0t%iF_4tj1oR5HMn@@v>uQL^zN`G`0uR^5p+&EYfSakGbx786XC0()c!Q+& za=vy0hgiF`j!OK6#^sSA#L zkYO&7wyWZp(DD#|R36etCF&iKYo#ulEr0KG{(0ZKzdjOkb>@cA#q5krMZVtN;ZGU; zfIeXCH7+yW2d_T1A8I~woByj9mgih<4t}DDO0W9Iq zWatQ2dhAPaE;(Y@X`i_&0`dn&u&ZI5UQHgdCozBJyv&JldRc2WNlg^|YneFP@BY}u z!@D-wH&Mi8Mp2y-)4U;Kp)InHa3L@^3~h5TSQOin-d+ml;uv?5H8Gk_d^ zWTxPfBm~>`lQQX*8toF5=^|L9*TJ>tGcze|o^qoMI11Mzr`T9QePrhTn|D};(5n4I z7)nxIu5eHV`3gjC9v@cmEG$ZAGa<9tC%AmIHExHLY<2#i@Z8jx$*@{4UyzOsk|qML zDONjqf{g{>669ZCZTpK@MO3^Msn;5Y)Q)HgCEe<)qSeUJ_z%v~{IJXT;fEiw;@@eK zO6bQ`n<`V%J?@lV+&bi!RCn=K9uPaLLH9(Ckk z)!*hAWSKRU&)~Je6PgJFqD2*#+Wg9C7=j;J=jtfq-j;y=2OfyU1;k^e+%EPGv<}F= zfvx@~thdwCU53zrsq3Ga8nXb<640e01TPV|v(!w&aVqHJ{+#Sq^y|<@Si3ovDaBnd zk^f!GIX;XO=QfAGW7 z`wB!bA?hUT1VJ}DOQEUv%j1{Ey(T z-b-E}?s(|^HhwU^6`WW_sp?$fPfH5d`iJCeBym;AMfEDI)L*E**M2ce%hgGKqMAA{ zpQ;#%9qLRnl&p#UvSMda68;^2L(Xex$DjP3^t^Enn&0s7R$|yB1-3;=?*@}}07z`n zIF6%Np1pb{p_X(|w>TeZ)Cnj*m;B|Tf1EDEROTG!lPV`)=fYUvca^yNJ)M4q9fb#e z0m9~dzp{cGW$~q+zdbL)k^M*&PB_m;#8n5|-FYr(EvV92Mcw5H4UmYK182vKp1t>= z3S@t02X`yT8jFdmBD~d0yIYXNME^W0N3;0xbZO}dvS>~w3M;UZFu2$L*Q(olh|KDZ z!A?>N)sg=BFDJo%USlSeiqJaxM0O-E_=Q0YTSjzZ@cj|JuJj{2dI)1B4_Lc^eW7Z* za>*=-hv2|GY*^Y_ee2@Wl4Y7cbI~CDp!8sRBJq!=$PScs_mw;C183TkawZL}H|$K( zkU!w=*s!U9>&>LBsMu*<&ptCc??!s<&0n~Hg>HzKUVEFKrH4bzwze(?14I$COsZ)C zJjjUNTP_%n%9ZzO5r_1NJVurD)hM%5Ou!3(zWCkzifA(H!km7PX7-a>u5v!y88*z` z#pLc%OQmL?m4NDn`k~W}r7>!CATBc-kF&Qqn>)5bSV(tEyI@}Vx1R-}1De*RjW7Lg8~X_t}2qn`f_+n;)NQqkU%E+9qQpTLb$D z-mq9=T}^|T)+Nq$f)1JDQ}ql?>cROqKpTcE+Dd}L+=h`9gQ4HV%PM4Y+@+KTeqw{- z)$PpwGt>N9)(6$fAr>jTH^Bi*eR`*PNX;PnZxKz+7W64q)4~DU;^9&V_``xkG5hho zr*p>osHBmcyF5?Fi5An`mT21Gb=gG$(}=^*O8t1c*V+sNV!CQJg%s`hvc8G_rKxpf zG3B(qy*xo5SZi)T%Gg1V*Zo76GO;5L_@|a0uu;OzzM%!-J(``iz zz$^!E?pMkl-+nJPFciBkMx(b`!g@=gvT80M*-@Hbu&veF)>;8eCj$i`Y73ZfCT%1<2d zwhfeu{T%6Nm*fZT?Rsb?sB4|u8NF-VTX19gS}?plk5#(=9-C1DbY(kyQvbID${R<5 zeAeOTBVlZ=Xi-HP^08J;(UjU-2(5X*o^@QvzF|w0(C-~;2KKhqdC(}Vf#Tjkube%N z3XxuZS9p4d4=DovCEHQQYvG>yO}~(PMYvDv^?BB(eQL?-wF|X7lDyi%ZX?#9PAk{X zmeoN9AIMkw)I(Itn~ z{*q;8yCG5jtxSsA z%1~_%?n&-+rOmFi14rx~asF)Ty_J#PIimpbub zkx(5*IwR;+_kh`moTRZM3Zv3V?S;|W6XY9;t&|!|p`1j3g>?`fao*ShtE773^5y(2 z!!$!B#uSnceTW{0vf7Wz=jp!udu{gJ`)zImgNfm9jOUdyW#MXdnC%{%Lc~+DR@b(T zI?Lj}-moZ#7YGO9HnXE;mh9gVRd^=@4xcfXKbd8C{95`z#ZI@@DYfViPu6=ZDL7+XnaM@3o?up<`?Y3VxGy~O0-}lIKwMCvb_}1Cm ztdXLK^Iz{Hp07IA<~$RrtL}U`_9B`4x72#hON7Iw>^*2=`ZV{^tD2zIGx%clx(Pe0 z4avPl^2mAsdd5)d03n)F6P&s;=6g2BGixA08eS>>y|F>6b%y8DAL8gK^|<)`-5dE7 zG++Z&LAr_HkPy&AO+C1?t@?;tYvigNP?w@(>__Ks3gS z{NvJ)?Ur+fJi$rhNE>>@G8*O_TW$&uR-Uc14;shZathDQN3H>rUAaLCn57fbHNH`4 zJ}2w43rpy7Rb2{UMrm;^XYg`Qbj9a~NRO=pk~xV_VU9>xjV?_NT}y8m>J8wXNU>RaPP8_$*#8_SyT{dnDMId@bJQ#)xHDYC1zs`h0nwUjf+lu?=J; zDgBC2xISB<+;4uRQCJkKl&cm8ZIUTYSJ6uzNL`9{`DtOgdJ>P>a;Qvmk(Bg)(|`t} z6mm~k9&K8o&gqTFM+t31{q@zii8k5kt+cWm_bHX{bjkBaGM=K4VM(T!{jSM|Fzo_= z9l90+_35jYT+VUr5oQRvt3$$#a_?CPVoJ@IXGNdo>xGIWPJ;s{lJ%kMR+uR0s-mt> zQA|1{C>prAA1V;(O!4g;9uxN`1-1!nKFd4HlP;q?E;Hb@freEciQmqCanO>U- zFDC@II2(3@Og`aw-(MQ&Zm_=Z3LM%x|6qlG@VN`YTG48=jN)7VLbl53J7nU0C@lF6 zSY@^8h|#6hfEC`fhjgXvwMyFuN3u`6Xjj3VuGu}_A8W#VSU<%Qrt}LGS6UE+vuZHt z^P9B-XS1!S+f$`8WIvoIlf|ONkG_vl1M6rzbj8Zxv1A}j<#KzY&yk#|<+!R#t7a0* z?9C!roffkLtIU>C>dhW^8@7P9j$zFRXMNM z&tr5#TPI5cr6iP!86j;oCF+QTdP^ja4##2K?ZCFkS z65k%5@hXr_(=H%_uOf7biN>!ok+~t?f#$#W+Q>}9hmxbSUlut>vY=}yn z#NGloEI`$KjboZIkrRJW$PSHp z-2n|H^oow@m^qhZC`WQg**5F+T7P&GA3?h8tk{$E9HBQ(e|4av3TsUME}l8;2#)#A zcEOD>((02jW%l$40$4u!kJd=QWA?!M$QNB|Vhua&IDL?H z^=r{4!6H&`%16g%`jwWevt&N{z%LW;Id_d#%362LdS)A-{K9r0{lw9PN! zAyz;1y==uxZReyx{k2Pq*Mx+dvA4We zPEjraK|a+VDsu%A6v)MO8Dw(#EQIH*gLt7CUQNutm}2^I|H=#o6cL3E#`+u_1lv7o z6V)-PV*i^|AnbW@%2(!UNx7LfJsw2ciDaJ@ZZChh4~WuZRGZ$}65#gmNY#Mc#T>+U z31NJQW+^Eez|~-7NwFQfQGsVWQXCH(E1+9%YYxS(QUo2IJ*E$JXAc$1H^e-^ckCDD&smzR9qt^?=f#~hm{1oep%x$d+xx&w~wrR~`IUJBrNxhU3A$Tze0 zYkf#FvysB2R?}rND&EB7@I8c2=sKCe`j1Hlv?P6!|LSgUnAuv&Td7OCv5y$0a7AEd z*RZX!TV~Uch$ep^FugtOiG*(`^0+Z`)EymI!qL+XXQZN0YNnCl+xTbBBEJ~;)ViI0 z9dJTmD@J&1%W|(4*iPtT^iI<#*k&rZv8&|Ud>y36H}bpMc5IHZZ1iZiv4=%(>Rls% zoaV6lnuRAu?=^$at=w_SD+$Vy(?dPcIja%`w57 zxNYl+lBy)dAdB!cf#Fv<%XrkDMlJKE)naBiJdOEHM@s+Jm3VAP!r>0C!q&rS0d!Pr z$Gz~0TPJnS_YN>0wKWrXyVTW*{Ft*%z~II-vRnB_EvroLqzcnF;B#Qb;p@#RJ~6Xx z=KUAHOZn_nLj(CP+F702Df%s^;UMRYCAzWw(s|ty!3p0#%KH;&&!z@;1DxM7(^#)r z+RQIR3eFC#`>2DLGVW6as4P~nIdsYBPiRb=-1Z+dt{Tj|`a^cBYzj6rP83~^#I>>$2cld+@{OLXlsI3IV3#6NXX4iLLT||Az7EwNichm?RJUu0& zD*{NwS7`nt7r~zRWE+g{2-RIj3eRDSNnXo^3T+RmcW%(*>0o|5P-FhxyWZWWZ$2)) z{QkIQd%>cZDYY5DKEY%-v*ZL>Kti+3!%TwYF#D8Qvmg5M7mT}ty5M;$_t?joW3o*$ zyYoVdw# zexIN^sjP#>Ux-W!r@A#PXbV21>ZFLPK#Q9MRm9r%7Ec+DCPs_(D%U7CKDi-*cL|>hbItr|cdc&i)2oQD$J)0FvHQBO2s$lK9GbrQ%i)J#Fz8THCCm^4D`D$A}xaXl%QTHuJTNF z8n+X%DRA=WR@!fKzUAk>bvzN?S&TJ)DpMG$9{WcB(TQ~T$0PGo9MGloVvPYKJF+S_ z8$VKHU7vP}9m_ucN@`?iUhlJTc((OVymlgWoSshWf)m$#peKE`QZ!1{927305LPiB z7vA54!LPL=QbVL>%UGn2`K5iFj817RTH8AF*EnkseO{EjV9nokbnSk+%X{{sj^FKK zCuO@7pTwe?|KzavlnG9p+HYoRcqq&+Uo~BWW=ngB)o^JE5kiOH@%HT3jT~*Y5|+?( zHd4%z&E+GUK<|2Mv2~2eDjnITY7KPLI)%Ynj6?~Hg*>t9jQ9?4IdR@iS>;KZn=F^C zVadc{dZE*fRZUoWZ}Bkkg0uPXoYC+e$H=P*`MH?IK?wZTmdf`_R^s#0%r)fiv$ajWxRZVdfySSdL_Y z9AuY>--t*6@ws6y4FMDA4EuDGPxc|Zh91bA@{5+Zw)nt#F%<-Z#R3l z0!7jsRc>=N;Ma!suZ5fL)}*MSEK=^)1wz&RFp7sz^TrRvgQ|S`5^di!-hjl=^*`c$ zMj~Wg;Xh8crU^S3>?DkZj=@7=m)WR}A~!PanUEIR+MaMJ>DfaqNn%!!nX2ezjv_TX z!enR`q{&h+mmDh8?Uoy(sFsIG?0{Wr%c8?=34>+OP3pil#Ts%3Qmff3y`r+PBu3p? zC^ZJa-l1kLX8l$&PU}?gXC2!_*U2`pJHM?p5UBER-Mq{H2xDhyvj|HL@(}CT552vM zurX<|xKZA>MLbJ8K}YEsv*=`@(y5l_Z0=!^t1M`Hy?YTPq~v zGZFkVMxlwq94B_;V1xk21{}Qv_TJbkCDUfOEo0x(*yCP)IO6F62e_#?roBynD2w}# zb_@EJ`38vDQj`UQE#fHT)<_n3e-|o6a>kJEGZhAPzKdsS7P=1C>4b*Zl6&eaQHlRH zpP%@W_!mwpvDqe}FV{`B`#LEdqq=vIS`O@ZO$9GM?OA_~532z6_h+c84+!?Izv-E> zfojXi>iV(~XIIdpvEyQz?YYYp)-7XAgK=RzNo3E4Y~Td#mQd$o1?W(~)6M>o(gKCy zFO-?RCj8iWWL%P%W7LWu*il}KyUFX8Z@U4TEv?0Pwm(c(_t4E}DPzKbaTZ>!J(h&& zrVCO~9SWr@n`Lshy3_75;QnQ{*{6PEmPTFW3r%-tW%HczUFM&Z_71Bt4e2hy-5J3( zl_}-UiC)1cbwtJ-%+nc+iAY~iyx}+Xl^4QU?io?HVYDW{<#m`A+ULPT;d*wb6D?E*jvqEzFcb_HJXR&~8(9h8YAE3l}vd zO&(VT%xv?esgXNLSrtt*AEj>TLD%{hs>WvSit6C;SAFl%DkHcl$6Yi71``ysK2flCCEvzQ66k9wWlup( z@VxG7pbI%1p!?<+8=?nQ?nQ~5anW-pZ3GW>OT*2NYIcE>x3Yrgz{Yt^C})gEia7Q ztmP*T18!_YG>1Z;JCaVG#H-UQbBW(D6wCkyX!o;AZ~`mR8-Dc(RsfdSqlhz$Y-+Fd zfsuAZn>MCNAwMm2WG)OtpvTA;B8;{jaZ8O(f?Og5XelvLDOT8@cD+h&F>WWaH19H3 z<>>7d<^LK|Ix~DIa|KfGWE82jA)KfPGbF)yLI&vVVp0A@%07;Z!-iSyKW&T1~@UqO;xsjQHgW$S71u z0LFB0DF?JvGb@s^M!ic+VNAWIJJhO5Vt0V61rSMgJQD|KyIEWrHQ!GY&VJi|BS1qY zit?R)j+(V9O*n7$I1bSDcZe2XAM)i~6l13h@iab=wek~476Tq}+nSh6dc<0rN6mn^ zNOt7CA3hJD_jhY0n+mYLg1s~6Mq)Ro+omBl@C=~AxE}t(`p`w?O*YAfY+DttSqCFi zf1!|X@yGBGz~VA-?%^9{`i!&6_xbf&r(trD^~P2Qw20bw0NbUOV$m(m27=H!WFJvo zA_z?DcfRe9i8y}O7tkGJRPpvR2(HX}oa`dIj=FX6_u3Rr!Y~}om@Z49ljGQKQR@5e zXB$(GgtIm!jOj3$z5GYBxyh*{z<~#R_`$ZcysG3|EQkdA_j9UnAFEl2&r&WG!#WzJ zADJ(gOL3xh;xzbd=c_tlWN)6goQM{c%^18ltRX7whJ01Zm?ge|b}7P7A=*ju%W~@+ zB`$bxVW`Rau41e5-xGuXsO^!k-WzEa^1&eHMuj3?ZYsNG<$`gl3&VGnLFV~FbZWR_py0?ItMBQ+>IjZ%D{=ZzM$Hp%@K&#uQZ ziSl3XNM!bXFaX+=GbKLAZ6X{>8!H}hQR=$XEF4n91kU@^3%;|7`VTJ%kNXG|#u7Alc>kidAjyi(aL>?z-P!bF;S_H~H;w1uDSBVL%ou-5=zE^+)LQ zpB2x;D4#-g-@5jW8Bq5h?Q7xd;E#XCoc@UNIP~#b?B^M9>F?v}l!Z>~(?#49t4vv9 zJ)z04pz*oZxXlR{*qIDAvPy2|a^WaG^_}37JQ#7dZXg10t&y*h2Iq%uTb1nU=qo~b zP%#_Xx7<9|bKH)A;3G*kLci7OIcXsHVJef3Ea9G_6p6JRl z|0x%|psE7{@2jz4?r$)Ut~-3!JJp#MGKuEhnUkebpHWs=!>rvwc)9!Ma?dVs9e-lZslG3Ec-UDE59nL5 zzr$XDDirBNt4<)qLg2oz$X@x_2feeK&u-0MaCNrwjrFMI;apKgKa_&8yC^|W+FOC> zO6$=~WUy6wu;Q!jZRZIRHTt%zAg9lk7oWhzhc>>^CWWbQ@t3|HkNou9jCq`FnKO!4 zT5tbvkjWKPnV}NbdDlL0V4qY{p-c4p?TjTX-?VOSueTV_7;jD%& z6Z++H^Kh_Q$A(%3NYKXA>R}gHs15MX(-PzG4NNN9^8V38M>kPQv$u5kE?iI+@O1OFfh-Msj}i8r*h-vP zOHb_%i6sU;2W&pjVv4#*Hp>wrqMoklFZ;6?Q+^i(EGYc~iX>0sBGHC_F50?M%0l_h zGkkF$*M7`k1g>X!WoNBKs&x{Q_GB~Li8Y3qrQF%Y<;a0e+n-A?L_kz$2m2fjkpZbR zRXYS^_6S*U8XX&&pYF>f^?FQ=WT8Pa=9lFLHRGJ1dT z1a>M*{>=Js%l3`dtFI0!+RBM<0b1=+$6E^fC&*U$-AP9C>Xl>NYPNY-bJtBBti znFVmtLjFEZ_==r>GxaFOyR7lC0N7>Vxo)i0Ws2qQQL9o0FV5bTifkg8nM~0irVAD_ zd!AKnnfY00Ki-dfThjBGqDw!<^=3qRliHt-^BP=`=m_<|_Vj+m!`$IAkl z<2!_IsO<;c82D|~YY}cJjIKxyQiZGH4ig}(>?ESO-v>(dkq2JHK~Ll^t#D9H!=4G`oSGPw_2WE##R&|IL8f5J?~dCK zp}yM$`RbY)8Z%&VFKlQ5)>i@Z?cNh#W8 zeScJk;nHo(N_uFVX}0|ss|vrqPUDjjk#Y}S8Y?>zz1+qB(*k(I#1BhUgXYbF&s3|@ zBsim)(+t1e{&-0RTluCg{*>OUu7FhOGUzDsaw8gTJQ`xp34xA|ZANa^2qchLi3{j# z!$~_~=PvZ-1<<987LaM1%7b04fc@a~93hJPYGG^iAjG)t8azyXlI?kVGjuHe(ZN8krWJE;~ zl25+35gn*Um`y@UKUhndYLU?AqwC~HL@+sWADRti-i?j*{4Zp3IGtGjE*wlC+V}E=(-7}nzNHz%_$^2e}j*tf3@l0nb z8}Id8eG(4Q#e11^)t5TytdGz*WV9D+5o_H<%v3Gkz5>2!^MS25X$AD=y9;nfI=>@6218K$ECjEGGO2Htr5N~Gqa^r?wnUL^no?;WfkV0&xNI;|JTvfuAsLNk_(5gkQ_)VuoL<}9f9CqT8}q!&Nvr17vszpN|fWB`Pevu5=z zSCeg+Q6cJQH}|rqkBBZ`AAYFL(tc(m-7<-K`8s5tJMu`~){u?xwwIFg<1qp|>nFjT z#K$0>GZxOXg>qc~+{Pms_fgMxr7b~-jlmqIM6OZtUhq#H17X8C*zfn7fx(OSS}0jn z9O$vC;-oy2cupDBY~CHd&VO8S@pq#?WIchIrW7980_Pg3E=PJQ`2RbUhfbz4h@oVg zOkyp*|4#H936O_T@zb0uGFHJd!qIy2*$-bm3j2CI61tZg7L29vt*rVpDhnbQXcsky zCCm0v%6l9O(=Az@wn~lD;&x~uNhrH~Z%L{R=I{e9`&9$8mcYOCO6YoxM*)<#If$9G zv47W)s5C7A&<7BvMnbl=jXV1_U{K`lo?R#sDc6)Kapb(qpp;6Xb$iAvX+9xhsPaW} zrP7j#tZrq9VM+k!50AQvk#>KR$TBKHbC{AnkJm1wYY|hiu zFk&l+<4F2;8v-odneDDJp}?%HLc=^_>dz-slqcFc&lu6v7TGtuL%+HQ<+mjks8tw= zUy}tjY4o_0=H)zz1Jh!;k!8Q#p#kMWOJ*VD*iHaOW6?UDt9BQVgbXe9dUF=3ppF z-8Y(gH4Oa`C}aR6x6$^3}#-i!ng4$M-e z9`d=rimlSc_jOYPHEYkW@wA58rmlQyKdNS<*nEVFTrj$Ye`AaO03FGEphzzaq0t9; ztuZ8bBZfN{dtw*2PCINv79)atCCfDUz&N>V+|$Vz;?~DYKWdcvtH3X5?Q~5a)R{(z zdzxeOCHP)-yIj~BYde3q>fZCb0-jwmwno2~^Bsk)1aB`XKd;@mVE$g{2ii%Rox##n z@NQg@!vX7F^M|k4TTMR|{zNbTiEyXgJ}}`|Cc?{f>6&n9I#bGM`^dXZRH{{Dkw}f; z48b0q*Cw|4MfKxTU*W=`<3hDfLHH<*DB4KO&~j#OB?u3!W2yH==PtTU3=v z;u~g)^xGc0P1OGWV_!+|#rO|K&a;MvxG~^2Zg3e{H{b(HKtcmhY0h#ds;|l^~UY4Wppu=+XL1kZ9Ba*DjH6nCjy9 z5qD^Ko~f39q`sU}r`_`( zi=N;%o*kt}KYMVpaa#Q*E_3)qWzAOgUZ9Z!QI~mA>JlQ-sax{ z`+0ll25espzsf5MGbs-?>{)werRU*(#K02-Or~zIIk}sP{*dC+j4MEzuxVTBGqROX~@Tye$SM}&-erIpWhnH}`^|3Zf! zTER*bx20vf%gp~mY~eo^38RsIn}m5Pf3XnNa~8Pr{5Hg@q(}KkUGRq%$%$`@9^pNiWSp2B%ZCf~4*i z(`YqHe_m&^Cj66KOXwJUO&=uYGjhuQs)ZVRsI-f^g%PY#u}FDFOmLL^@I(d0T78vc zCiTfKp8M#ZJo)L0Z1d(d-|IGXM;E_|0(X$$wt^|5K@O5 zTr6&gJ;SqQb+kdGcZInhiM+gN>ERdH5NlZqA81K_C7WAY$LwJGvtUE@IJ=AjWMXjQ z=p7ifV-Up+I@A4STW_xxdN>fn{QLV}BV1!5{aXfXUoO(RHv50{{0e~mRLh)q)1Ps_;v04?#zH23r6pzyI_|U*{B+*7(TA0L>lW7`Z<|yoe~?>sQ?lJV`iS9t+s; zYXAFO(t=WW6)Pijrq6Y3GFAmcendWDK(H|nE-}g{UsM&mB#&{sRzZn;0(v( zf0^)rJAQ!KrdLt~U)g`{aHlbd-?&0^hwq>pa$NoP;ihyXgGSbP_coC49RyGfm`QUR z`=}=~G1nHwS_rh#Pi!&`kG2ulmDu!!PggcWnEeI_rCRWU@xvWEG2Tk!Yd$ol2(xXO ztm*K#i0dS7)16bE7d;Ic|BYlPTaVe!t^jTdno8dm^pAJXvgPU%1EJ>Z3!mP5^J^Gq zzx$t88(AY|a&1}pP)5)%+ps7Y{=8TQJOnt1*M@>7>U*u4VskHf+o0>27vv26Q9JBG ztszwb#)CQUjN|UQe)p+lu*4Kkd@h)E7&6n6uS>}N1Gt{wR|lqC7_NC>eK7!4tIZ=E zIzC?LG0(=uJ`@@XM!g3~at#@E_V|Dqc)jc9BXJJ0^6x3ay7CU%<=&*3C9gymgi}3 zLJY1m^Fwd$<%aE|&cSr>B1(+#k*S`?%z-jxjSrFw5Ad$!>Q>NJkC&JuByQzn`yT(9 zWgGgCeqCOjM0J(F@c=O5!_cxplr$+W5H7$$^JnGJxXdNqv(X`H9lf>?s%BOF&lpwj zFUX0c@nxi8^eqaw9mL?DMW-eVXb0{1^1`)`6*Am|wBGGMf0n!wNkcj;u{ACqwk_)z z+F#2`o{@S=`A-(N`;EVVxWiJkZuZ>w`zD*^hj^GE9k$lJDRu`|+k$B}UoZ7O97JV>^lWF{&mOZ-i$RS0bh&gTiyH z)d1nb(Am-612c}M5R6&-*&%)GPpIKuo*uk!pcMQH-3s2L{A~+Dj9CeLQ!37sX8-63 zYmC$%ckDKGgx&w1BB|K#S4qOqB$Cge%4PUeY=twuZE}q>y$aR0Jd3GA^)s2gM*_;0 zQ~V+#5Bauw>t~x3MzW?EhZWk_n;xt0{zZ;=*@JoiLAe7Wnmr6!={))qEMLi)mi=y# zDqN5{RWHl!#?mGEs}NZ)TuajshL*QQ7fnDSs_oDiRQWdPr!5lwL0@+OysrEap(F`o z56p*s<-RF1{NB72@>+0XUTY&TJ8CxLBqWi0vZvJ@`y)+bT(>)_m4u8w_;r#5G* zeJ9<_H|z7`oQUz4CHf@!ojHj0%|m% zj5lPaFibIogxWuy*N{h@N?wgF?CH>{x0z$?_g6-k)3TJb%L2F?9ERjGDL-v#r@Q~q zZ5Np{=>?|D@H;=wsD6)RL1Mrp7@qy*qjuaJ@ljV&xa7;5Xq7Grdspetm?iB_sL(F0 zMamvx`D_CsR>&Dfsa+3na2AtR&z3CW8-+sS4}}9-Ik5s&WV?qpp8$iL9qf?0&DgSi zhaz%+EAPWde<%17(y5b`Z{1O_z&-yqe1v_c!{c945>P0yY9(A`=-&GV_a7nNp|A8c z==dynCFx8>tUR~g`0w($*r91&69Slj&@8ybENvMN1xE_iix@ak{7sIT+;Uq_NEL)r z#>n+@erc+9c&CeW^~YKbrvURFJNKtm$(YLM@DzXjPn=e1TzM@%NBzzcPy{>EFCA%f}PFxJ7D?4+&y=v_m5-a ztcuCS6&@EheUj2)6`V0sT!#vsMVHX0mHPc*xyM=8bMQZ~N?lArq&@6I-h}amUG}er z+Cs+rt)!2HkH|d!^aUPuI<0Kq#=E7x0e2_c%8$PUA6?cbi&x8~gQdF6@5+asE_H8i zaLHdnasPq2d%=uD6pNQ?c~ugRyR$LBbZ&dY-L`dquwC;w{cvxISh^#m7A4I_N}g9? z2*r{Vilu0Tp4}mikbG}N7sycATfkcL>pAM+_Mr^ zdFh}sI5K8IcWgXm?W&_crQq?JAJju0egIyyp8o;8(rW0?LY@`mRcLilDb>MoP{>n> zf5|VH0);JEjikC`sKDW6=Vz5g6WuM=tBTW|*WpcL-S_62e<7W*`}-}IJxXbK4QU9r zlJEdZ{#CvojQfn={Dc8#jAlnO%z5UGDUth$SaH0!OnzgViys{DnL^NKuc+GzB+9-h z{Ww}<=-bblWA;3si`EfZz^~}pmQjBxs&9_{sf%Kw9FuMjnhAaVGd9d-h z5o<-a<)zrMO-o&^vZCi1_WRPQLq-Y8n1_0P56*u{z|b$H;D3fU_MQBdnQ=B&Wszf6 z3NI14<6hDhBf|)q`*4d&UaTZUe?;}xKkIG-zFKRTQ8qyxZvx+9EK6fz0rrvqAqj?*U=tDTTEDL4z;2 zWETwqGHx@w-^}kPFIb|ePg3A>Cs;qFU7zu1--f`7e7))UPj~tr`ogTN_{3DtF zp|l(@vzAe&4?IMD>=TL^W5*U8K#~MwGY5F;nQ{dlF7z zPh)`bfMDh>SwA%@px0Mp`5o~Hdugw88@a5|E|+Jp*u9f_qR*gxLI)$`*}!kF9RxQ9 zRr*`I6sD0H>+j1W;R~~wqT&2fe=*9Z+hm>p+qHsbI=ter_o=(@)&}eT6J~sonCY-M zx61e84}??oQ$Dma7KwQFP+X+&@FeSp@1>bNwiST zp|J8o=w!ZtwJYpy<~h0^C&3n1?w^yx((Nc4;>Px>6WDF|F@j%L(mV%8xPN{FCd~&S zbTfN9)sT^@4DQ@-LxszBD0;X9uKacXVgfB>DK3`5c)Df3Bxp@8WEIiR&@*7N6Fr<= z89CT3JlQL4Z>BquR=C=o6upw7S3P%l#=KN;i6fU!INfSkvNY$9ZXQF=s{R^+zT?Md zR4kNUlyVc)vA3g8&Cd?lB5q!^(d_rdrU$&i5qSKzDIQN#@-hS&(922i?0DW3<`PbL zrSOFUCl6clC~Tj0G}Q14a*Hi+=Q?gpooY>4R5z&(YLcrsK&}1Ks?^8aPonaZ_Th}= zbv)M5D7?JMw(7X|zk!x2fic5>P;u3!9Pex>>h4hBwKbU3le|USY7A`I@3**Zz#!9g zQ4z8RRYL z$NvF%Xbk19&1~?Pw|`=p*a=eyze$hw`zw9lGW_CR(%!hC9lR_|T5prNa@(M{S0Jl% zR<1hn?zY+Ps&&lpP~-ZM4}pW8u}>^K?8jS@8vYZq@Fi}Bk|`M>&Za-b!5e=<8@uSc zyE5eO0)9lE-IDY65%e4SlDk>ha6N02DU#WeU5vZ@ekb<&=EU^ZfAmFIhF6+c6+l?- zvu7)pDo&AOs1e2>OhbH_ybbc|jg?)}CP$ zU6nh|K}C1jUS?#GjADIZX9mrUCV0$hA`l+wF>*Pqh%N4#QM5@Y2>_vSMk4fPY|2q$ z!@ju{vIT=y6Y)Vi^bMFu-R=HCk!=0~AXQ=FO`Z4lYvE%cBH~^;+sy>MUKgIAax!;q zUi5^5i63FsWw?Q(8dE^#wGbrj`L0}ddAasqTrtFCzal>)c@ZuJaA%Ka^i-`kJ>_0r z9_0pZAdIjXm54>@Y>(<}-vlNTRYX}&jF(t*ao#Xepw&<=o?5M`YhpG0sUC86DZ0!# zWVR4Rj(X(j=<_D=(}&Q8Ix1p#rAO#i_b-W@qONfHuh$&K zgF%g{p1W!UDr7#OHGz#$mguGr;3H;jBp1VtA({zO+aPc zUI{HTgW^n{0if0|H{+DtS5`#{uml6m-fjuP(TM;;yH4+68Dpn&NX0?HLnWg^&w`R` z$Z83s+rHV?Ac_URiGMVDPBrHMyAT4nQy+^SH@A(zb#W2A>Tx~!S*CZ-1f8K#ksmUS zMe|SQp#%3eYH>9m|3*sRQ_cd|G^UPfAMDv~cQ&A6&xY0$ZA)TMImo4pg*J+nTh9~o z&&!91vP&>oo!~{%0&D2#r7mSKPZ;snA!;&`PMWEB*?qP`J@Y;(%>ix8Ra={ZazlQ} z_ClZCJ0^r`D(jYmKUx>~xw;cfLXq_vvcZie6bWA!e&)Si=G-G^bdFgZoYcNrSuaUY zDuzGw2%x5r?X=raeufgGe2(TkGz__ybXHB&JvPuhg@&M-ARO3=OQnZKSvA?CqF;IB z(HR9oS+%d6sa(6&wg3(d~{?TmE+fvMx#Y}bbd!Z>faZH_7O zVPJy^1Gbm~#mmb3KXg!p>yeY^>3dr>+QH{+-0S`l=@ENWhErLJcFpsS+?i7fh7y*K zG~Xj2K7WL^Dbh`0M9lw!Tbj`SDQqH!Sb4&)*gOpnxqAOJUwJHj*u>qbyx;&J@|D&< zr02<|Mv6}HavAyy(wA&bmWexOIm-bS?iZ@8xt*?*7ZYl{EolI=g8MldYOOyyezcs| z?M2q1ki1B`KXQ_(X*&oxW@IK8EB$@FRrdX#t$wc@d`(?{n6d-;EQKQ4f*wVSb5k!Q z)J3ldixGboPCK3wC41l^!ZV>P=babviU%A~uQ1OFy~bxhhTEc|Gl#4pwu^gZ+ZlSL z#4uh$gEdK+h24`l!qVDM$da(VbAk=^J@x!~Qaa}bpz(gn)yOo%o+juS;RkOJ{yTe1 z^O|Di>tAs7ScaF@V-e%YHZ05d=f9p?Tox5hb!Qb$TaHhtvh%eCbn`Ksp&E8T)B>S0 z+BFwflQZkFD4aR^B|aGM610Hz2JK+xvxVo;Q5n~z>03CxPXCvlA({x5aDBokp#_ph zxz=kOcRiZu(hq6#rY7ij zlV6i)9zaUs3*}l=ICPv{J}QD5RpajykU`9TB!c^jXK=0)J8_<@hH8NbypcjT&e(^J z{;MmfCwvhGh4|(+Sy`8?TG#kXyv*ln<2N=8bG%h6luf@+)HZV+_xMN|UU5RU_QYRA zFbDrXIzI=TEF6t$3Kbf|GD<#qU*MqT-9&>fy$ zmQmaSw<2$95#FL|uKHFCa4CpLm8TnK20?e;M4;s>UwISN!N-eM^BG&2i51udn>u~2 zveH+v^ngBjMT>)@%GOsJMC|BZN#JHw?PJxae=r`5IBQ=$NXp$tbDN z<1g${(%0-1Z489j^?c^M-!R;8EfF&u2zoq$Nw<1tt=ueXG!JUCt6E^j)3o2)nnjQm zl6h_OW%Mn~6Tpc)tNK{^KUMkacVwr6l9nyC4d`I?Yg-g@U~MrA&1o?*f*p-37Vb4R zgA(n$CiWL%r?6uhE#s|-PyMu?=F}E&e%RdnJT$~RK0Mz(2UF6X-v3Rmgwc$oA|6q< z;kn62g6c9|R08Aqj$3u`+aCF29eK4bUjWd5w4OEsqUl-1R#iPFP zVRwq7FJU0 z24M^3hEYm}^XU8G1ZY&)m$JgEjAVLr1Gy8JfdNE}g}5%|J7gSq8KtZTP6S|Np4N}O zQ?gTYW{-PI@mHAhAq7(Dk-Wv)>U|bZ+ND;G&Yz^i*Fi2ycm5SSjOvkm4JNJ5>~e_R z(*F+OToLeD3@2!R@iioW8H*3zqs=iKU6*{{Cj7FNn?8G8>Cu=eTph(fDRk81xebXI zt?)`NSlWjifmgOcw@|ZO?n2S1&$MbYMZkIb1HE$sxyK0@JU+1>5UboJ5URx0l@tjlV#JJ0^u8W-d%MnnQaYx^R(eFf$4=IdPlMae{ zBsOPK(fQ`GTI^C$wx3h*-_>%cR%Ayraz?MM`N46`<7=;7SF(fl(B4Y1L|tXWbRE)G zXEwf6aA0{9d{@Qnr4~!?z6^0Oa-S%uOW*OpPsoX! zwwyV%W50V3d@MiS-cse3_me88TiBN}a;8%s%fv^*7%Frh!wPU9=^9bh`xmG;MAP?UeU){MaR3B&uH)T^mC=VWYi zVNOHH`d6BenSs_t;mtvC(aW*O~MO%r?ki>y~Bwj`9i(ZbA z=VOCK^u87h4Nh`E`%4mHYY z%R1=5E^eO?RKdP$2)=08Y9v>i+41bA%xJ$y`eA3*YLM{6yI+WV^0>boUGUpeX!yn( zUz?1)!qv5ayM6lM6nK8o*2yAvLDFmM7GQfPaD0&0Pvph3G1pJ#2ry7uBqtxNzBxPZ z@VPf>A*Cx5BChHr3SBQu+evpo^%xoaY1^q($t{3%^z>=#Fd=NtvdZtXSm~(&q7th z((dUgYcl~<3By?7!n@W3d@z0?G=Y*zNal~<{J0lWs^I{UNf)jL zGg>!)`e|0qJvXefRobwVXY$LK7?0aN~X*`nUB0dJL|<`zI_Lx(2mX77C)JL0sGO}5$ZdMaFMaY<8{ zJ8DU{%ye=`)96~}T}M9eAF0<2SioE-=}-E}?PWUinAB8f0U+Wn5syM%#5r%^kU+EA z&>W1>?GorQF-BIvMF5na?kjzmT(^p1Y~l-4yvStYvr?uQ(Lm9Tg2(~VjXk9sgK>uO z_|>Gz$luk!kk@KQv}2kj0P`R8>IlRBe|^u#mqUnqf~m9~IezM$Ub(8KCmR>!BU24K zKmtERo|)z|tb51$Evet1D)@mfK7kF$=m^sFjMb(V*c_f8(3)?IBElTVgzK3eaUma-KuM~1B6W3NE4LHXn>v z=7QA|K*;4(f+N=G;TV_p=v0c+nrSD$62&Xo@(s0XFC~jjW?Ew}&v6y6CJuiItk}R9 z2tMt`M~~%?@p8@Z!iS%?!x1^>D)cwQ%kz90HYg?K?Ds|tTC#YcS8EhzF@~NdJ^$70 z?OsZ_&AxL^V61SXwOv|)DX{0SK|{;@p?=!rIg#zHMfY?YwyY8}Rcx^I=9w_y$s{o7 zu0Mi+At+?uDifXRB10-Qj4@aXJ$Z`}MY~+vH4X2e7ReTYv^>4g223mS<<;Yi9b}@P z;YtNyYlyL7|MLsK+9;DH8p3Bjb@wnC z=eRJ*&1J7VOyyKe*G>gs=&2JK_rR!ekEw<2!u=@gX>@^VhQVZ#lv7h5u8m!>^|_Xa#`*PRPlZtGJI?9=*PDSR$@+Wx*H(yiY{|E2{6l#p`-i=aB*)ul`T zXE0sPSf8H)f!Z{*5PW^>+vC%2{g=4?`_quZ0&bjxV_clcZhDgGDc*dIVb?F-Y zB?TLJzq|hF(_#Yo%&|ZRt}6sD=Q0{C?YTOtJiuUW#Y>+&k&4-W0E2-VYZO1M>ngZ3 zia&rD{K|cJQh2z&Bl+lf3a8=vs!X1KBL9W!5une?B^%Gw(hn#2q%OXlCja*qN9??E zU14YTpJd@a!ONFoCi3=+W_rufG92pcEAFRI2hGS;OX352f9uq)*Ez)FR8T#R5FlET z(QSSOOWZl_e7heT<&oxp&%U^OH4!-?Tu!AUb&<&zLFXaF7-lj-`MG+imt3LKquJHG zoT|rl2zOMkiZ|V^Fg4bJylcrx->KZ4arzTTBm`Yi>z*Ov3a3vnbk{NF_9hK;hjM3q z|J+Ecdr_z10C8vz-GOZTv&fI7p4az-+90*7)gKttLKl7V$Bi=55YK}rQ? z#Mrk|py>&SA)Rt%0=j47{9)4LH^D3{qBWLy_+Ma zq`ltA+sX@mo+Ho(xm9sBne1Bc`9J`c`xtpWd9=$kEcvAJJ|=!zsEb@}Xf^VAw~3Mm z#nPV&u?%ThgBm%0<&XxHww--S$j!V0(_BGxP}?eiE|X5WV0Qo`>b%*>nA17(ltUkz zaZtFp)dz0-?AZGZcVTbuDl>GZ{Yb=P!vEyL5UX7H1r14^enYADGLSw#K(*~4f2OSH z%t|0C)_5x=(S{tRoS|gsf>L8-esPmDvggJ<>x9@b^~6Jmn&CLKWTBq>G@!b^hs(^; zWqPI%yTa}}CzUaI`xX%g;$?RoHu-SXV5wRkpx9yjX5Zj6x z((B;^bk}JYz(f|w&0>lqa4g6ZaLn0u^fbYOGXf!+nd8tjGj&*LloWbMfb&D@%-Fw8 zbYK!QEEwrQ)}i|9ZRH*9EY@*d@gwmp9K5?h+!a);JSTc6nQWHPXqerc)>C0k0-2;h z8!y^|&oJ0Dw&pFVwuO_D^Dr}}d+Ks*0H^evgsnVkkGVz$NOqwQ9hv7Gc}mfcNj0w% z@~K5>?rmeQl5Xm3X0%2%@u^@f+^4j>*BAQHG|VT&YR^K+5T%}=;K9zqZ3y#6k2iNUsE4&K|~@5-z?*-~4Z>=t-XR2<9eS2G)DN;V%i|0y?jB=W{ zB944Cu2q+|pq$$+j86X|9M8J5UwLt_xLPC_D!T({YIrbhTpaQW!giR{l_&DR;}{;7 z-&Mb(mTp2e^4!LqW;=NnN0!6nA=XXeC%<{_9feT$I&*duG>`;5h(al0m10yfI=-mh zp@I^{eiOEO|LNcE#23w8RSf`!&P0OTfOJotd7{)r);jZD%4}fI{CK@~{`C5A>k;0< zaO?DK7c*zejF!!Zuf8*4Z727XPlFc?FYlP!MmaBP_GC{E^4TEm=?Z>DD!AXT=gt^L z-0!C<1XmPK`l03vjdC4xf{1LTz}p}3-43H|Yqx;=#fi+GCP7wv^jon~c#5_v&NGM| zu(Ryf)O=rxfR6Uisn|(U@%fAcmikohcvja(!G-GUKT*HW2>ArB-=h91i{AchePJ+A z{9V@SHZ95Qc+i3~oYvIzP`ZBnf4LRE0Y%kXkb4}8@PF;P(G)2meX;&V|0t`*3}>kI zYjffd4G*K5v42(ogGG&}Qe6%ibaLT{W{Byp9N>#!>6=C4-^lwtb|EI`t++rRHB#V8cxt70cg*?GFC z?@ZCt5)*41{;g#?HE!RBJwHRb= zEXcul*hm+H$XHy5vp4s=VYd8=Z4u5%nzN(-g~UKL;6fxnw=Hbn!y2=yn!3M+mS#>{{9!CQ^)Is!C^!H}&fSP_YPtetOi0FwosS01Y z)q!yTM<(Hv!cTt5k?%kA#ig5^REp~LxeW2kVKpu$pVOPR>!35-DKlTV;sk_ChpNXv zwV*ES&5dM!Qbz|=5ZFY>>R*J}cvx5>yf4)nd)94ivs6#YM>&O48AD?H~i9HShg z)z#0xHfwN(AP-?0k2)ON)RFS_HoYA7MU}zp;L?65XSqTNb9lCh05|QBSa~bgXXW_i zPr^^TdgI@U%blRYCG7O%>9EavwjhkW@*OmBNL2 z#Gf~0@PsuwQ&j8}o@iWRnn{wa5@en3PuV1iLB|?kJG>Wo=tYO~>J8PmG$<4Eom0)6 z)B#^#K@4iBp9Ns44eB;%%3kxhehWW*feywh86 zPqh#C1OmIX{ke%PocHtwFRQiYPpT|kvNOsw{UHW54a4@zx>L1C zue8R6=MjdyQZxkY%;-3-j$Go$PJUrEYrNm=@93@-3Qx~K2 zw#zkxWveQmo%#p}`TCedy9@oV;TAtu_JxbCCma7TZT(nYUgCup{&9D$*nFTxNhQ@Rh`%0}}Gt zap7^AQmMB#@@5tk{0_<$Cpr9SIy~)8(DH zJ&M}jS!NISy*{(~ea!Kusl3U|ZUzl!p6~pWuwDf-_Y6;P3K7M>X%Nv|kQRA^Tm_dJ zILHFeS{Gv+qU_7m#FOOE3N#=^}*9Wt4}g6T5-;!*6RjsJ`5y=K89fOnmfNulK36GF%m-xl`chA8MT^TS+DIZ!TAE`_2FgR8 zTO==}nPml8u#En+piDV670n-oxT}!G)x2tVVo-LBM(}A~q3kQ5pPp0NRmyLl>1}Yi zr$gphDC#5R-R7$f>GbcCya2;3(JF5=MoG@uF=3JO7H4fwN#rcHJr4`ROmv5q*d0Ez z250dd{k~&-8ch3L)IW9~K$vjWzZ^&E+$=!Nei>V++mFmE@?c*HGBWr{I%vP3c`p#{ zq(bu^_Oj#DZSP(}Yu{yj4=>|KF9!Y6Cs@>uhh*@I!iznSZjC*+h5O8NjnErjUDrYG zpONkm?hx;g?vU>o@PR__W{k_vWjduN&dl9m_3S;S8<*`_*3!B}BBal8ACyOL%(Soe zrJK&w`{5+%4=hSu1>BYET&gkq3EHiG8b3h&C}v?f+5qB}8P)-Rq(vb3*csb5z0zI=Wubz*}6BUO?98}e*d?z=~BJd76_<}5RLwmKPCDJmvD4r!YFu$1Oyud}vFE=+FXvBs%4Yo||e5!>2D zmQSU=(K@TmH899r;5dK7?_t(fNu;9F@t3;Y9~0+9+?Ngm0+%fvbx`e)oR4w;%t_cSt1W=5Xwo7Z>?YYV z(!`~o>0Ot}YJ5C`zk;~44W;>eV{bDmmXjn)?LWj|Z_t9hdkO244CLn+%o;$6yD_*~ z5)G5h`0Q`I#fH@J2G-TN^gV%3Bi`T7P`(UZ_^ZP!$kZRx#FWLmC0PqELCBR4+yD#n z;K|R^4N;&$!jHJGg&RsZQW)&Y%MbsB5Skw}iXoxdy3`0W=LYzA$wHPj%n39dfo{|8 zAA)1SkFe5JxMMlkFoZpC@1z-HX3Qca^XPVvH$4m`OQe{SuVOHFT*QrRLN@(QHT$r6 zhcXQh%rX}vQ7QXdFXp@txJ))3n5ma}gt?>R?Q-Cwrzj48h4Y4*R|zC5kZCg`E&=4~ zKvbXCrv$(U1v@;hho5pmd=yY|)sCJvM>JQ?&_k@xnyxSxkCTIf+gfzGt9$Axo~G*} z65bmd7nU%qJY0ErQaZQw-Y_SDj_s?qT#*iu)<`or<@)ZujMH_3OW_~J;xgbAw5xD2V552e7lQq*~P@HMYgvZ_P;v19dHQ za|>R3^w9LaW7|f8>YYJs55xabq@l2j$*|bTVv9FCbIomiBIRY29b==o+72EFxhesL~XJ9x= z^sIv(6j`SnDf{e46~!V-W@60b1e&bqc`L9U?^Bz&sE^Z}VW$|YNWlOh@V*_l2$p}>_&ZxY~q z6h?^~h8kZT4v5eZx$lKbVYce6y@URoi7mSn+m=e@s9!)GuKzIiAVyFJsouWg21+~A z6K~Uzf&FnU!HaSP$ZW-3K5u-4rURO0_Qr2NUY3Y9napN9K7c3d__Qi;JQwOFD(u$D zLhZK%m#tF)N4$aX0TZblN5MCi*7AR)-aW}nj?e_1WZed)f|*0=-i0%?U$#zqc-Auc zSiB$5H6;%Lim61`=7?E zFYaC9t7X8V7!s+If7AR)TcC#c@PN=K?i~`7m^6nvV}Zvuq7&`gXD6QlC=W_Z%AIIo z(EVgZ16w&?m-|{BTLC&{^y$_7XnBGvQ%N@;2vbi{26e4y+ zbdH5$oWOiL8XDD9f2P(^tSgFXuY|QB3!AZP%|ZEZ_Cw>M##WXP(~8f2uOPU}D*E}Q z)hW3ZdKmDP=yZ$$7sOK%e9)a+8R>YOp&m-!_BYFQw}!TUxr!<}6Bw;6;o780RcLw2 z{zG{g8T`|3m*9x==Ih&)5uQxs1OB@2l7wox0DhF1!D`t@%L0Y1=RbYYxSV^7kHbAL z$r^|+VQ0?=0(UH~N{{V~oOk_Mr|9Wh*`-;Uon}2z!*PXP50D`BA@XF1cuHw@NLI|qY(C>A| zY$USJCB;7yUbBCB69Qd6YZjp-dN-oo^zl6Fqgn`k=zCpzN>|V@C{h& zj9u40`60KqKE=s4fSDt%`GLeTQ5pTS&vZ|!839vbd^n1mOR*rPwvTGJ@Lb!2pPwI4-(U15I}2zF&R6jT^Cl~l zG3S?%2n|=2_3f!=K#K!?7ZJsF;%N94xE<~t-icp4-CX|Gl9oG*ACv@Se~Nd@_!p61 z7BLr3v+IXeC$z0aXLxUYR4yKgj+SEX?nu%bq*hRlavme8$YiMaZ9*RMUNwIn1&>6B zXjYCmItV1jSzIk*Eiv2PU+D7T)jYw@3CrRoc_f!pA@b`h79GV7>pCKFniAzevZVN9 zUG_W;c%3fPu(`2cG;7E9Hg>Rw-|5zx>-$jxGaT~z8aNOde=KGdN#!IC z%qE8Ha1)opKS2!U$0s>5YA7sOk##Q3J{+wz8zB(|8?6uL3inq8_hmCn9OJNn_NOUS z%O;hZaH_c(g7W3cUCay}X-?`@LVTWw*)GIu3@r0Q&n;IcDcp9U>$86FvJ50sZ1JyH z*h-gDZBrbx>~!KBom` z5KeQYsYTPs=luq5c!Q6%|FdSF+G1p%={HJn-T?d`&QLBMee-fa^q3=poqE z|Eln+Oy@>)E(tILItu9OEy(pJZ%+_q%p0rIC;&7NfJiZ?*X7Ce6Sy-7MKwEH4eraD zWDnObx(iSS#CUdMkE^+-^G$tUljHTRkyfuxb8CItMxE{icxMDN7|W_T*>@lRv`#I$m|s*w+8>#(di8_opv%^ z1*rFi!~QtOeCsB7;@%YJL0I15sb)x{^%ru;_t2ri@<*+#6%0 z9HSIvpoEVww4AaS;tbc4Hy*30g%8eNy2wI{_lpK$$Ru)9|;^g{{wZV{Y|9)D@L0yv6}s` z*zMizY*Tb1-}ZMjzVz@@31AftR)Vk!8w`~CRIJePC6(krxuTWuM>BKfh-(5vIWGEc z`iJk_=!_~VYs$slkrH_siInfZ^AA|G!{YQ=C68Z!3Xiw--Zh3?8rkyHEm0yx$$NC4 z&0Wezj}?^t@ayFOUP%)+#cm7Kc!Saik&dzze`Z44hP{e_g6QR0zJvmUvrDGyNYG(D zh*u>-l~y>Kg4ca``_Dy+?0ZAi1Nb-lhNGYhss;G(rnbA<@u7%52ZFL_7jEXEV<9ZifNHy#ed$;+fZ_S z65SNjV%5hh#v7N8fJK4a+M4p*=SGva<@H|IABGc2r5jfFlj}~ex`3B8N1CqIJm~Z6 zm#RX5*pJj-=1})--gz$c2morm7vjfT;>YjLX$V`f?n2PpTn?kDBDLQ@A)rtYqYBy8 zDM}IOpbZ8p98}I?kNS5g`fdu2JwLypHO?xaO!+lp-grDk>&_h;@lD43Gf7sQsd)0=juWQz3+|OR$t*_ zGvBCrNZUIu1~D6aATE5P18#5m@s4+FX%fZ>d}N|$WfzB7&yc~p8@>sU;teTrUyCsx zaar%;ZTw<8e|23XU1dtMT%{w2@SFY+z$b=sOJSUB60E$@*{jWpa(KFs$xh9n3i-lF zjw!n_K%t{^UCrSijSzuyhC{6&1k3*B;}r$BUVTKGy(<)U|K0hu-1*z}`ar_8)Dp_e z{2!HwPwldRvD+ymzKK6OH4&gTpu=sT*Q3V1PG1OfT}c5I*)LGQSu!XH)K^N%Op6*G za4%@k5;HowHT_3|7a2AG5kw@&la$}k7-#i<4*s>mi2>Sy#Ym89(V?jY@v|S1SvO0s zAG=8O?twpPiWTe4v)W1Kvh0K^PlRQSyV;XwTbJ;qG_$Jb%w7H+Wjf1|+RDCNJ-Ac} z5m>+M$b|SN=B}Sgth2ue&aVWfx--3TW+m2fDbT?Stq-GwKE)eJMbt#yyDdw`T;AKP zF85u;10iteGjwJ3o?~1o{y@1za*E-ltJxA_Xe)B}9gN^6$*FtTi4JDv%#sVNN|YwC zEP|6Uw#wOb*~Ulo()cG)kwviVp9RRQzD{Ib&Q^F$PRCz>b!lxMWfDnaZt7BkSJ=*I zK~Nq1r&mCf z2+1;j<&aIJluW5zq6#e0OKB*sP0DVy2Z0M4Mota!$S@lm2XT1q$n}?dO8XzoqHUss)sY z0Wwp2JRb1sRZa(+fvtoUS0sn%dsSLP__X;YJ%F4#dNldnSN)M4kq$z z*&n7Y00ki|&pS3#dg z&6K_iZQ`=Y4)py^DIt4c%<9divo@4rkR&+is!!xu=Xxjf$iKUlfdm<96k#+{<6G3Z zB;9DT_yDnl=AtKDDXHhqpzdZWQPMr3?CfJY8gPL)JqXXm8_J5p(^GMys$q71Ghxkk z|5;LWc6nC5b+p36(ENidpJ7 zrhfGj?d2Gzq)?bmRgHH{M9Ngs{^uAxSLhqf^1rF07v=aue#`J1LJx$rO~Kn;HLfO- z1!d{!Xx05%_^2^&UADMxBg&}Og1Ct(B2U7AP!rT!Gi%M&)ZRH;nE9udi#<6l%9O6L zd@6ky*w0&7O}XJaQcir?4}u@-SDDWyUx={aW$}%*w-L!ra%bhZ2zQG%JAL_Jh?u)XIZomK*;AF$$?CIuZ2p#dcbeTUDfNK6ogw* zX~%!phH*Z3@Yx>F*X}8hqF$_S>#@i_U5m=VH@HH}W)rlJSbFX1_lJMica^U`x#>wK z?ElaKK@+S{(Af~#eoot!oy+Eof2xP*Uu!f_oxw;~2p6L$uc&dp!G$1W8%-@~Kz`#o z#vdkN_V7(KgS*X(QFGi>f8JEo#o+m%x%#3?4tQ+&sj}yt9Y;bL;@ER8)(e;%wBnPTwhOqU1Im6qzVZB6Qo*_uNZXu~&!(%y zrnmMTU4byDP(iYNO=oE67ygDi;V?eNk-BtqlF4c&0y zkS3Y;g(Kf*dO_wgi`gs@^uk$5x+NwJo!G~;x&00!n17i$Dox$gp5T`k0+VP*xWs4O z6yq$FA?COm6fWm>;8pudG!I5sn9~Z%p?k?FFsL2n{a6eqo5at}ip$CGs?90}iG4L+ z7ymH7d3D-|ecR8#KMP_lihiS+B!{sf^?k8MSDISnzCf*>7#4P3GT);WmQzdk%?S6= zsy<_ulr+(tSKrjM&2S4|XQ3lHs+dZOIZ;lUz3M!36}eJx9*_SccRoQVawH5a0-7~* zuzlrwYHCZN`tfp7EsBD}mio)nCy{Bcj96(cS)+ci`rf3d`-#L1U>-!=tPZRamEBV+ z%Si}ieZ+1-WZIdc6x&2rk`+J}y_Gun25*OI$7%<#zOA!=oVn>OUPs<&-c5bP9xmaz z#PMi%xo9Cf_0{EHNr22j>v4aU#Q+^>E^FQ=7^UF$d=@ttlzHh)s<>Zb0TlYl|4}qQ z;m~}@#!%_Dx&6sO@N>JU+-}cx7iXr=*tY>d^{u+2zoK7Teo7eMNJT(wem15~K;Oz( z1@Yfy`hPKrPZE+cathH8YNY(nGj!-XTxcM8DxP#OzV?m_vr5D7-uvTT1G4?S z*emMND-OfX`7T$Ds$tmudHs|ahBr&ex|oZXOG8cfeeHLLZDvVf4aK?Rge5JZa2%+E zjnFtyy|Eod)G1<5h$jY*=&dRn#1ipd*Mg_>H#%@r6Kv~hBJxtA;Yg%`8%H;>M4A)r z|BW*oL&#y5573`d(~5V>c>3Pgb&t)ZHJWv(TI>fX2J{2u3-SYH^RC0jJ3(N@AiSX) zqC(Fk^6~P^tVJ~lj5Oz=%)OJ2B-`>}0;m$s7+te@GH#k14nNquALzZ&?#69K(Rp%+ z{8Z({?x*giULLh=kHI|`9ICOis8_bm&1Px0mog@LvG%TaltODce$mk*g60j_`1c3f zF#KyWH7^^KT?O?__q4_E8Fo>{f4l5VxN&^wH52giq*!TdoF0$1 zGH{@<`-#4ux?bnAd!ID|I~p42DfJ?>J9^h_Pa#~%4d%;VoP^$`E+c(L`<0zq6py(l zv?n5`P|fNZw<{0_hEZHVNzA6H8nc(c9sIL&fTp$p@T2z1|HW>_Otk*Q%u z;=FdbJ>ne8hkjVh#Xi$r?3?3zvAN0Iec5)jIW#ds-q!>AkN)z_VeFI<&YMma2JU}# z9}|^Q>ag5CGu1u}_taFkE-0a;Ys=9k?TL4$EPE4tGNrJqqinZLTN7$~sTl$43fIqS zV)2!d?v9CeFF(YA(ilofQ{~z`>|?xOK`L1Z?n zEG**7gPWFm70`k3eGyYj*5~965upS->iHzFy6X2tvBM%qk&-j)j-5DjC-?C>*Uk)5 z?`bj>f@k^S$`_iHLq5OkNoNs_B1TdlhRX>ugmN7Q;|2@FVRPe_x zAlEkV7@TiKjnbE>xhI6LYT#x2y=c_HuWGPVVLtT*O8)&_zQ#2Mvsc_qO`J@;=oBZ7 z#eSOT4qNCZ4Fku@cO1uzM(S1I2AnejCMm4aAt>u#G3A2>=ydz+XgmH_9zrW^u9swj zvmY2<;?b;gKN1gc9sEusC47j|V9SBbHWk7C_@P}ZM97m<M03)g82OefCW8cK-TE_j+@Opa6liznk)*H}K0}2sy=x;^ z`I!3c=(bgV>2ruwe1(v{ev*~==?9&V8(cT;^qOG>ZV6IamO}Q115eeqMZeWhk(wH| zw6|IqU!Bl)?!T+{OiN#x-+Uc1(7v~kTh2hDcN+`y95kEx&P`MTKN`Mfpzg#yX%k+qOY)HRIfv_;K46cTGxbQ)^#7GqBx1 zhbf=v_m9SbstqqBE{oi+?k6l$>2p~xw>>A<{$vZEk+ASJkExWlA3Bt@WXrUZdkRFH z6!hzyjf*nUTkV8LHn>arMoA!npoT-o)I}8<3OjD}b|)i5r7 zZU^I9pLPq81;5){zuVFtU+YNKZHL%;f$~gg$&O^m7UuF~pY170=RN?Glj@}G^{OE@ z{~>FXtu8{bD9$;xD&OIo+iK?$mc_GrYbw#>IT6v>uO9mz&yHTRlabh@5m8iEE9Pj(S43}gn$*LM%RBHF7PwM}31 zmTTQy);!6(9$RfTfk;UCEEk01Yp#ZZFYPjQ)_~dtmA;0j{j9xOfY_H(D3rfn#PC;s zD;0-z9ie&o&viQ#f09n;6t!i7H;sK-1fPVzvP&n=lQv-=0ZX_nKB5k0+n#(f6 zazbVcTr1agdh={btpAJB7AN8P)Udumy=0Jm=f`b{aO-A=xOLG;2BcBaP6oDorRx#S zh%TH8Xi3M*6Fr)b{C#v|p``@-@|X@f^=_3>a{Sg!QQ=Kx z)Izno)#{J%+O<%-V?*{hExB4RDoIPe2A-`bkhN5R8%?)7My5XeqVc&|)_pvkW6K&7 zQZ5EW+)UowTt6q=R5vawqGgDvGpS)h%fLVZFua!_(TD?B&m+9^k*lTXZvOs;i$;G^p0LJ8d(EJJMpq?ONRp0EWO7dUtJPsN9<5I4Y&0bS;DWRVwo_9ZO1J( z>k$H{sbCBcz8Y|QW*}v_lVDl#d(R}@AVzcpOJ0swL%Bdln3Mbj)mR$&2PyL75>`RkoPcQ+4_?IT1y&8!dRuY*y4?0CHEAo6J(C zRrLMxWja+?47gf>CPb;9kp0aRKPJWye3w}rQp)y{ZGD1M+i+xEVAA?%bBUfa6VHmnvtWXqQoeCsB5z~6uQF_-hdCGF`n4u)fk;HoIJVmBUnqxB{ z((z-K=cyq4XknDppt9BAQ`(C%CbWwnbZSx=UrpR-nkE3R%r?iIwZyP;$B7Ef1`BH& zOqQW!>oFs-RXD0_yf;npa7or|PW7^}lUB{Xfi2Lxt`=CPyAc6BLC=OgOj*HePpCE^ zfJj7#^Kl?P(>TJiR2>$+%D3#-s(F-PNcL>4%RrbDdfBAt;`~llaNCT9f5Iq{V_2fj z6W6vBa!}-thR}ZN7qWcPtxs!tg<9eB@t6K?ieZc`Z@m)VY<~!mliAa=q<-Udyo2!+ z^ziv&H`dHjQy_D2=vq_D>Lwp4z!)i@FzBw8*LnE8`>(5z{$QV|iOS3^^Njjw($bhD zlsW&G4^5r`=X(wQ9k)AS_;=UvHgPrYjDvELBQtJ}arplE7y3qSh=yQyL-3Vsm>8&2K z5l|3wX{t{6X}zX3?(&~OXsyR;rUmS3YMXD%#(dWAZLCJkR^3{{d^ zROjT=h*V@26ZYt{zvcX4wz${c)9uX12qw?wdnCDybl>M?3H+1S}tdr562VK>QGwN}}jh@LIxPvG+-k0))87XB**p!#t*9&|9nR8>OFT+16h z-)s~qqGWTNhq`EJ)EjSKYI{35=t-BxU;EwIP?(+LlW;)0p5MDNY#|>98ezLj{Qf|l z17mUn6gw9KbY}jD5%JF%Uhb?dFH?z=910)OvF8$TIeb=cCds{yu)sWWK#@9rvAeLI zWJj}Bw;p|`!1Z$532$t2#^vE(?E=X8vX;RhY2pPxfJxrah{gav2 zodxQxpFKpgz3#qAV%ArRKD8T^&hpGI^iqLUD>vM8zlgu}pF@0wCGhJRpZ>oR2s)u+ z$C*hSI=nqH^mk#N{;NjX6TJIc*Q#I9K5IepD#L|-U!j*f13pFe@=@kT+G9Dqr{Lu+ zZ-waDDx(eoC~l`>H0Jf?7W;?>N2aYF%=Z}AZr>;fD5#%%cc|lIBrkzV);ZC(r<;}? z_KB=}9opvYosS&o0DT|p4|63mbm1JcJoxlv9tQ&OOfQb4bG5=htd@fM=2rcxA@`Ha zk2U7D6}StOvJL7wx(h|Lb0FM97$|=2l}Mc*FA)e(5O9nFg@cYEw}>qGlVqtq6>UK{ zC>fN(a9K|{V{(kFvfsR`G$=htk)xxlgU>Ugn~RAwSf1+soVy9q4`-37;{4~{sB8U{Aq1Nt{XbhbC4QkDcW#H5sOoe&|%C_x{(U$%eq>Y8(PN;^HxDz)?2aS{Mg(R7u4IDV*-pVRQbPvltU44dr+?5e8-ADB8*! zJv}MGT`l?uUcRYu*zr>RW`aN4t;=tHqYH1Bg+Z-!S()b|!>D1|rPm`Vo1M*V^!=QO zq((yPv>@+9()j0h`_auUb^kq`ir%QhAI6ddts(VdP~XZnX-A4({IYXxpq`sHz!_w) zNHvk<@Y>eS%(j`5eiFn28(XQ@ofG7;ZT+EDO8o3re~RWR3^AMR5=eV|5q44TIr*g{A##?s~le_ugrHdaHlmO%}MWq9Q0+z=ytRj86LzaooyOO~v;X4 zzHl+1QqkaWu=2qiDpd5@n66$-oP%E5^~1;yZA7pMY`nyV0O+z3PtkCzqqJe+3#o}O zx>#3MEMF%o^x!d`se0wGyuLA?&7gTr0dbeWhd?2cAfAc?r3_PcQTP5i@tBmK0zbA^ zG(K3siaE-j1%xcbr#j(zxjEjagL$JlSo4SXz4NM1(oCmK>p9Ry8=W)Bbzod1jwUb0hF%OTAyd#OGtZ4WE!JoW_2aKvX#~a< z45uR@g)hv8YPFvJS}hhTh1k$JBNu}0sy{G?)O85>lt!X-X|IyI+p}@(Q6O zV{qJ|S1Sf@l)ib%LID5czxK$=P8Iu&TNWa>-mBld3SdiS@RZrx_YE>hsm)iF>06C+ zGDbFr7qXsAD7Qy#nM+j_m!wi}6KdBd*bczCh2qJrj=l3OeYHS(~aSDoGWA5 z{;I+(f?sZY*>NMxXda6(r0PADDnOmZWCBvNvD@=4{1UX7GCy_)oVg4|0r+se%|G6I zo8(@YfVGEeED)9#{uRWJ;Eb|l+sw}XloAjaU*4|SP&*+nf3@O>tAT;35m)&$pxsac zBC_V|3%gqKLuQ`&bmFatKs5GXJ|-cQZ`C0U*Pm?lKO5YT_iJD)$O@et?xi2(pSk`` z^m>x2uSlm>Y{kqUk{TLbh3~C;OF$g}X>78WOiOu7>{49;qvN}>(`9d=kSh$KGE`PV*Np}`kr5I-2 zM23@(J*~43cz>S%UP=!}JXY=5teurnj=(I|xQo(bJSc&9Y@2kbU(PTCG&jDaE7VtY z>Pu94n#0U>!SjXrMe!@VtmQX0z&dwYcnhCF?~9D^f^E9>O%u7H*DM2Ok(rKCr%}fD zzq?pz*Xx@KkC92WC8TgK=tWRbn-mCEZmsbEGM&$$cZMi84guMZl*#DzFYxC`>2eU^ zHW6Zqia4vbr8Bq@CysC%J?OS=4(~+s9#nZ;)Zlv<6z;pT!;mao>l1p+yR%+IaO9xt z^|=3-Ue*)pU>$yuycxriF;vr>a=#Fxbw55UtBBjNGi}eTP%Bb_j+*%!S)F2aBuG4p z^CM~Am>092%gcNTqjbxriSZGXuml#1vbqeS#kN}Tx1R0G#ns%`V$Vvudi_NJ<66X7 z&ivb{)q0J8em{M;DZ>AT3D!v^zFj*z3BH?=_6pb1S%!er5(K1noP`L3|AADoH42|r zm?WZ2PB4lZAQDk-x=Il@c-Db$CngBSw1R)^5;<9!{uc|&_c3B=YCX3nDberX$Yiv; zvSKdFc~0v12F@T2B7Pu4odK(3%oTUKe0)@gAK8-!37y%?YwpQ);*-DubHC0O>XV`C zZ30Y!lIVimUB|6na)7A`*X?_0@y>rCqU0@ki%datI>U?4bsH5OQV{y@4AYNuI6RUk zGgg`hDw01Kd{lS8?5vsY>myxb73{Fh2>5g6wPp19D6@OA&Gb)J>&ta3h9lEilk<66 zAv$OnS(#=@j^9cp_n|rcEA(Mg{ruN_RN8Yu>WP{O^H0cUOC(?ZH1_;vWnuygy^GH# zKH27a9bPR%2;eF;10wSobu287#JloFO=qEt8hDIAL(?WMD3wMlHdx(vmmTILH-U*v zpt$gm!6_ZGnN3kDusk-I|9t`fW0AM}x5wv5J*zbR-8pgXydciiA?+nQWjTDpPtNka8Z1Ua8UvH*iKMr=&I11}*O zUv+44))Gca4oo+unu#|liQ)P%#4*N9dpILDoILq>%~Uu-K@=Ri^Tkbw9`vQHcf47l zPbC%9<7-lwNFa%9i`F>&BTHy=@jz#S=jvGXQM(-MK0RT>I1In%AJb@KU@PtTh4PJ? z1RdH19+GA{MIX0((?81$?I!AB5ZRmO1^%kDtlTJI1{q*G$tN)56$&UERwfnF`CYXq z%1+?k4$4JR*4uA`|jOhjdt| zkDkIA1<%bV(wvJR{A&;zJDB{OBfjEW6vm=4J#8BKEiwp-nymMAUPv@-QYcGPvhSpBQI!w7= zG0ulszo{I)y!Kk^h`?0Vjo$dWETw6#z^K5&V@8j~`*E>-W|=jm&5-mWuQr}?b~3GF zzknNY^l}k*V>&MeSxuzx{SpkT(>-R*G6_J-9aDRHxNT8-U zrnVN&BD)P(A9WlO0I{)=m;1y1SXFiEzSOI!uk_JFu5dwA<|%>{=ztd^MrCDMIoToNRm%x z#c2bwM*}4&#A&v%XY_Y_WtoxbS+ljX6CYb7c9cfXub{3!Zwagrj9VuB=jQ#$#~Aho zjUeWP8Du%OXyS3$^6qb7IpxzMsjK}Xzbf|s^MIV@7DoB?jDasE60!0tae!g))*Oe@ zRP0rc*i2>a!ngMXNv2Z?{IJuoAYZjcTcly0h$W2#bCv{`}q z9tuTeAycxcf&`jB&QpYdE+a;TlG0%GdIFdT6aVN#U#y{njjAH}8Y>M{ea)rouZLml zLNzB)>?mtcmoqx2M>&xU{Tf31g>Y>?=joBxqY$_74uewbW~f#fzX&8;bUy+r?Tyw$ zG@><)gc0wj%>S`dm_CHl5rVRAE|2NNU3>^g_3Pf2jxg%cG{mk{$v-|k$?SZv*7%Th zEYNH0U(8XV`|1~m!&FM3HmgRVtDDw?y&8t?)r3=l{poG31V0$U+qPn2-(uFMmN2M| zP9!iHwHt&Mk)H#fQ>xT4}7Zq(A+KhpUS#0Oj#dChTml~svlVp0NIeR=V zV%(wn0;ESRCywtWixYaH^yKQzc#hb)=D)Yer!cVhwrBN(NLL5u=_gAA8aEmut()^zKAk@VOkEa}`^HEo^CjnEc!k&ht8Gx)pV=EuU|@d zu5GZ?hS9+AJ8r`C@#P_QHPeSX<)&_+t|dy?BxzXAXx9WxSnd;q5n+^nmw?jg;;s#gS7?LM8WReh;FG}Ju6 zXl89%u6qGH(9uuCiHg<*nqJkQx-T#*z^h;xd+e=*H(ida@i3d~X#9pI8l*>6OQ^z{ zMmaf^i6{?mO}rdW7VTsYjA+2Wo=cozT7Kh_ zJih$3YKK!8rtJLPq2aa+DD|_j;>(+p{`M0Ih}63jSiJd$=?5Zrh*|rdF7-7HrD*V8coZ%`1eaAnG7e-mP6=^mHY=b4-9;oOf;v*!oAu%UdNvmyC3?Wtmj)e z0ky0t8E~0IJ|at>$ke}R8pp`IcO>Mxcd%i*Mm56Bt}3QnrfMiZmh!&@^m1AACbr+2w>O2R;K~zX&+5ZJXz13%jIB}ea zoRS0H(ZT*ejzQVy^+$T5cX1GpvVzJSXfQ7y^pfUl6$PsK-YxLD7_F3; zJWO{q%xW@xg{H+?;$VgKk}Cm`Qedwr-vjUT0w3 z6QZ=B@Q^XiiPL`*%D>OXFJ7@_r*Iud9k8MwqQ1tg3CHj7Xob7?>p0Na@)s1?)!i8K z<;nEMUyVdG%(`)+s{Zi*+^-0J|Ft(Otq^W$U2&rKgkX%h5UK}p4}*%;y7+wq=|zdx z;k|w{OK)jB5ziy;U~K0XKt^4l#gr5h4K2mjH(A5FzBHt?JRVN0j{nmo`BFuRtsbr3 z(Hq;gj#RMR@}>0wEx|RJk@v;Sz<-=uz>f%u{46!e_zzH)u@Fm9j?<`U*9t(|E-R+G zqvzZfhMxEZT#p#m7|hO(0E;{F__c0_#;PZ5gS0hy=3B>t|9F+j4KsYhjw|E zhO+r?!q!G|KkD+|PpZY-Lr7f5FTCsH4gYPIzh1W5pZ|^&?Xt{cXS89UUM%GM+(Q_} z6xkXe{Qef$it1#pdczE8amUthSe%POzykHw%c6s#?2P)Dt+yK_Y;j9cOn31(cVnuQ zRCUr%3UA5H$>>K5!$P#vq)mB9nU3dq;upleZ>t@-NZPkVj+s@@J?IN9FovYeEs!yB z*x?9)ag%j|#WTewoD>)AfIk(#aab2MzCr5#4Fs(IXC>KY!T0~XKgh!nbo`b`;RgSy z7-UGPP0iRtx?I(}B8pGB%E4M%Y(vYQWry^9us<-97O~RU9ioAIG7C$nElW@8-csXX z?EU;ve)OMq>KCK#&a)cl?=p)?j9Ez@-Y#N}8@cp?k<8FZli9(!4s`fv<$P8w1 zk9AFUOs_Y=|9(~WCo@(VvFFMKzb=3M&sIzgCX zBmG-c=Y*qu_BZ58ArNOJ&snDsoUg0n;H9!N7(z#*OysXUB;9uHn^t7?7Bq=V@#JI@y}qz>tO*u1!x+&B`^x3hbpT zAHuY1*FC$i6>fo}fprTf$VY?3jPJ;gX6{H8M`{7{%+-YjI@}(Va7WJ9PZOw(DYdXu6lzjzfr(o78L!rPiudI~ z`mtxPF4FU3BpDFk*Ki}p1REZ=k_S!3o&=Majb}<~nj&3jLT{I5By183(%0ngt8>PG zClQzPECY3@WtMPAFIx1VI~tB-tS!;8$;IP1VU}C%Hx%kEHORQl%=4Vp*^av$ak9aP zh?Yos&Ake&1vZB0l@w8Yc@sm$;He>dSTNp7p~XTXluHZZF&6LhA^7>fEtiwkKh?sW zhvCRH*|NwyvnB}^N9z4eVMJVp!;Y__%`JeAo`qmOgf#hByy{-7X?Ksa@{dCerD|;A zMEM=9B7SWGyk8uA`}bWiN{KGP9uLSfYy=rCYRy_Ph6MTNPrw!rkG_;_)*yGmd(7FV z(z6^F1A?3}(IrLmcGcvdDGAKkf>VXZLbxUiDDvlv9p5y`?jsZ`jJeg@cBL+f@tGaB z)Sw!ZfZ~zW_I}N3<;FV*fi%bHZVnzLmfuq%sj_{<;=4H{NOi9Xv^Be*E)r=qkNdNF zoAPko)?z=P!D8hr7(GaoI2FicBnnkN}v|Kn2))G54=&?+u8rCKQhFC@W)_OcW_u=Bc)de{$G;9Qwz{xyaKqa zr1$YOidn>fueq$2C%pVLW0YJ6d|mA#Xlnf>ly96F%CJK^5F_b}IP+P){OW2HcnT`6 zb#b}W9R(wNhfd1{Kh4>acN6`yGcXRj%u-^}8!b7SMgC!MMT?ow)@z#@c$wuPnI zc-PL+)N8&<_!w6yhna<$o$L998At01aA`3xH{b4XX67b6(&Jud{+-*|$9jiz8Br$& zudeKG_>_vDFmp)C2%bObCOkO#RHnZkFOIgBk_bg%ZRm$Ke2ML_gGo|E@?54Ssj$AW z2hj}4f82vT;>Y}~U-#fA!(XN-=Y?8sT*Vk)LVh=OxF3LsS7E((p*LLFBR#tQ+*LdB zGcuV|u#?);LSNoWbTC-~?*lHJZ`LWTRL!CIEcQZ-Ql_%<+Z{C@%)en2yuM!}`5PXy zIyB$JljB)wAgFDoGXZ$fcOc~ab*UIf@*PinLaw6Tt%_dRa1m+{YylL`Cn%}7(p1*< zF+29A;p$3<(;q#V892=(xS*Nk>|_3BWpX*{Z8J&fpcmNe_G?mVYvz1JSO?TpVzYEJ z%~~akog^f+7UzMN^NrwiiCzI%%-P=2(P=uv*nk_#d$xa@QU6(@R~?NSArI{O!LIUcW6J4fpRE6L)WKvn=`jYNDUpXFw@<2AY+ z<^(Ie#uY^P=$rB?f@pkedsRuZlOT=RlO=pw{aBM8+aoKo!#uKwYMPk$=2N&KfzP|N zG~P1dAcmH!hLh_^pW4{;1<96Yayppx0S#({3rXJEzWB0V#k$_v4P5+1kGMGn5a-1@ zdIh5}Vg--gWnY1cQ);EMJJp15Ex&BH5nWJ8K@}6+NXTMJznUJ`_ecl5D^b#DC0)fZ zx~q_-#w-k>zZ z7%hG zLD5jzi=_2g+k?4M`_hW*K-qIV(4Ywk4KAgE35ouNPq)H{0T7-EzM3xQs zJ9UIN>C$4uk=B04Hxg=t+;N7#Ty#AqGAY0Ix;j!FWScUF2LQ|Jd(xDWY=6vqJ=vq% ztOWC<#q>4*`WfBiUVe<$sdiz>zqI#!;r)}x{xP8@vWE#?=bGi|Q-X}a5t%wK{4Tnh+xSKU1m=`%seyvXbDa1Pl7*{YVjTBHJpemtPI3W-i|WC zL%vHfi{V*7_}0=~Zj6w~(>zB9V8|Z(WG0Jq(dbXbaELVgyDq4zvbtbMzwjuq48_mn zUCuN7QCvSptIYr%&&4Or2%!*hE^2ymo-S~5!b(R`->5@Je%$=6Eqt+c_`Q z_$e_TKAb;Q!U-J6*dNXq^tp7OxeSi+`sSPFx4yWXEh+rH?$WvUHd9cqolqAEkAAtp zQ7J3eKHu6x^V)RPq*bQd)unO4(yrH=&mQIPKzACP`)ewmTZ9q;M>*mj|P5_0x3LvQy({SH~V-c0;6KIn-d#6us6;wBb=D z>5EmrG=jW;Ql=+-(>3Ss-?VRpH`nQ@*g?nhg|ByK%HCas;5SOuZs=LeVq4@PMD`!n z9?t)}A&D&hw~e2B@mZBgtGOfxHwxcz2nxw&&|}HLP#}Q+OhxV7SG3m`!-aMnVsCFt zb?4~ei{p7wee8G)Le&8@1=&g#?wo>+C4{YQ?SqVdDRxWiIAh{k<6H3x!YFPI+yr11 zq>99LRhxCE<3oh5O0{7)E4x)MmWywLw&_va=l6@+KqQYQxQ% z@6y=s9}Zu!R)TFV^U&VcidY>nMd$r>$*u@`H7cl2Vd6|VR4>n*{l7D+(Pz88q zv0h&j;DqB}7NPx}>@zkr1F4kzaE}bUSZ-?U1WU{}v+sNgLoZkWN8|FE;#reGc@g4Xu|a9{UBl{;e`;022Xft# zALSrtCx*0lzQHg18WPaL_A8169zPlD>R&|9N8qi1bm4>JA9_urIJr(c>N|yRT>Z2$*m8!{B=wWHd ztEm5xH}Zm|JoL^PNrEBZjgo}-r3(H5XcurOQ+<#5-zlE2AxsK*rEBr_ky|oR4pOv7 z|5LP2GygkxDAjO;2kq`QXW;HgnC!}XpsAi*NuM^(#hc8h=^tdc!P9I&xP4PS>qpg2tPbQ1d& z&5(hYj0Xu*&H0llZ%ci`0WF5Re|enzcNHDL9DO|MVzy@$=TE@em9+ANWV`J~I{(qrM( zHv~!`7kS$X17;f)X4~sn^zG!|M+#jFgk4(GEuf)3DziW8IlkG6!jdQVV1T(03_7aeu*ffdj1Chw} z{z>FXCFLIzRM0b&iA+KpNODT^zCSq9O0v3I4U!RxWR&~5i$?F@(b zSrj(^yl+kCO*1$@*j!~UV#pp*2;g9_J)erp8yZL6LG~h8$b9Bw>`VE zy{rd&927+A#rJqH_+vZ)XpPc@Yk%U3bsqw4^X2(yjwTn)7WaYgvv=!1`y|-1K|gOl zL?w#V3VKCJdEtzcr9q#bQ+6)tH7s%+X+hwSF?HP;5F!pu<0s4WP%&w>#q+Q=P{anVYK7X zu`P<>L<~`jVMmWMpS}9L8;%W_^Q+uPEY8OD#_c4&>$e)HFT$l z(gR4RNQ#7l3eu(YP(wFJcML5tZcS}%&V%Qk1B;N`ib6bBcZ z-c4<~Q|TCEMl<9!npGk9a}bMo)lcU>oakE6Y;l%pU8&ZZ4^QQ;s6$UW_HhL&+*eJT z-Dy4V4x>?v0|tp#i)_9r24%H8gX70|7aePE9qWqA#jr{y9eu<4v0O}6xwF#su72Vr z^0M^Vmn%_vm6p+^d@ozV{WfnHr~E?}@SIL9D6e-H@@6#{q4iL*`HR%P#(?tyH&bO$ zf&FLPPp>v@hkdYrUnD9xi5&|DKhKK%8jo20R-8ReevNGd<;Q-%+AaU#(>?fNna{AE zx0TQ|FyD3RXGz6j38Z1cy?*Jhy5L)MoUE)++f8&9235KUbS>;EY$Ik%VoxS`IQlvY z209D2-xD^CD!*0I7i>>w`(Cr~yydlRZeIG`yl|-QYc%usPvhTVs+G$`f0KuMfx!IW z`PO%d2IrNM+n0$)!oAm*{)p%;UBYf{wm(WH&M+B3V?yJ76bnPYu$CzvW)A9^fTaf; zx{=T;;8EEic!`CGvd$po;N^k607@~(r=2>*oF6db8;*pkg9~1!TeBeryU!lEZiB6& zR$d3CKv@}4G>3NlEEEwd%%M+VH`+5c85`eCGW)8~5|7iLoTr{@tFo{lAUDXs zde8`XBEqOQT0LawyJmEEUWy&7vzUU+vRErPM`hVK9LqYs0~V_lP5H2HTcmnpiOiPB z8U0ntt`pS!?r8M|+DdP3(Or4^D}pF9{|?T&xw^m6bX@i;96F@=byR(H$;7gI9|zPB_+qd1?y!s4w$=NA zSrZiSaWQeTjxEnT4JB^XZ>a;vNI>S@3q_;Lc3_*MXZXX+adwCJA+R0Cnf$&(t=EtF z7A0tGoDaP9zCHtd6X+rEFm~f9@Af8ch9If^&Ih-0Im&7ucsKMO21E>X!2pmh@6f5H ztt9<-DH}J3^lJv07~ut+3sgtGCFJlZiu$vQlyTDIu_CQ{^(RxdF*7qh3GB`r*^Ro= z-EL*6Efbnn36kI0D=r+%@=|0QxQy1gexw3bK4yumYw>(VHyUTPE!AN`Sd&-g*_v;+ zAjrsjtmkEG`Ha@Thq(ybIv(+_#4Kh6Lft+yn%bJR{C%YSR+ts2Xiu#yB8xT|hX40d z@!yZgsxH-7^2+wBXC&>A1`9+zBH)l(sImrb*JWdhHZg8F09-#Q&f1}F>)F=*Sgye>;0g_hR*w@6rI z@vpcW^3S;51j&=5TR@9^Z931`9TeQY=o)yc3bmXKzg5Z*_ zcKHfwTr^f<<*=c8XNa8bn zJ9g{XJ@4?yQg>M87P3N_HV801ocbRsVTG9&7k1ltW5adz{tn{GX!p5o`!EKuZ@@wO zbdsjZpD#>b@|fppXNCs@Q7ZL!9PJr1beDZ0JN!MdGKvI@i+AH!@^P)@rh5BkAx;$Z zYsi`nK_AWYvAS){@B=cO2{K`@SSAqWUp5Q7hoX{00p8@E2=vGzifTCw7hV z_+y*Q1oZ0kp)K-kjnS(H9Cy;^#C2cx)AL=PNVSMB`%@JOwn zO8OOz^-2pI0t)KRLyf0Z31(55bNZgU()`j@8LX4>YfgcQdRfV+TGx$Idz|iMs7q^2 z!jZ4ylYpxs^E@6Ennzga&TFL~4n(XVnc3EX4*4)-a!0OKIs_{J)uK@1J#W=Y`l``I zl$M)AG`6Fjivp$L+ML1BcI@0V4&+T#kOg0F~)&+CxwqJ_Q!@6bw76NY3Eb2A)ZM6MTH-O zO#u|v^D%{0MOEhOaTF+5zF@u}wG;gwbcyv?1Nwv*`b&zRSrlX==ho8O{x7;LA?^Ah zus+mJA|>NQtNY0ru-c=XRefGe#t@x@4zk3Mvnix1Jlws@WV~;G6;q_?jXSj_8S$+u zYCX5@B%Nare9{;_xB~4rCimBgVFyIQ`V6@w*7ou9dw&`WtTdBxh%Q0=MGlU%6C!6e zPADReY#5rDq3FqSgtaZ=0=U{|#b0gB4x@JV-eOng746{W0D=_nud;jJWxivO?s-=T z-kvEt@diR`o8Fs0U~9Hqzs&JFF9N>tX^eLqa4a_6Z*X^|3;$hJ*@h{!4C?%81l~XA z=vx8}Hu8D6JLVmQ-23@UE*q%okUhwR#>9*=o~A_akX%reS`&)8vvOPq4E8n+DV^<< z3ZHy0d?94bPIc!6m??gKf?v~C_VJR`!Jg%S^?qtsULe#sA3Vtg)peVaGdERJrQory zA*txoH7_68iU0fUg-*lh%?C*lTZ7c!Pro#a@frkFZJ(9Vq5g&Aba^ASq1HXbDbfAp z_vil$5dO0jG;re^hx!`irQJOLpAZpNtyW3b)&jEiUzo$@^h5XVT)iZ7lfraS?TAdAa@P5QB~ zy&xXSxh%Na>NXpMSFS?~+pa9$We_b%nl49X^VBltCEje7;&4H)!CoxW;LGO!aKZ*` zpEyS6gO47q$@jF`h)T9i9$f!`N%eO}yze}gp5zw|2FJwu#gPGzR;T=f(iya_aGrkf zQJ*(RFKtfP4mHCiPCl0E*dQ)%^}2kjNz{d7Q17wM6L!>I$T3KJ1Tcq$rB;L&X-}LV zJ!GTcUU0StGBOP1U#||s@*k4S29vG78Slx-fTsZwAnQYU(es<5Jl=;jPk{0L!b%|d z3P#-VNT3YS$xP89eKco+j2+{-fC85n8}uuoT2>Z$o~XjuH&I#WNr6Qs1ni!O(>7Ry zq3dxdw7zT0Pi_eD{+4SrztvV4!viYd{EZs5dL1pFC4`lFo@bER$SUZnM7m{bXDL+P zQ&|4X)3510Pq?iNRrJpZ$EB%~c_+hHBXOC@8vw8sAxh#)qthaMkp1bzBRj)Tdp$o= zG=ld5wC^6bM$wAzaq@CGp(8=*!mSvWx`;9!j5cRN(Z!^3zQRr6=#h z;pLLaeSh*IbXR{6UR(+w)a-+BHd#})U2OHk^*}*&w|^)ZkgsREVS{?rD|befLGx=! z+;*PEMN{oJF8CR&@x@eo*tGdAt&znHAs?#G>^%XL1%YGkXL%9 z3f5DvuqO}Y6M9lREhARrip-Z-FX1Hb{oGLdu!__bgkfL}Sh<8E;MCq)=_pxS-n!ehDIr zY*6%6)v6o4#mXqirgY{S>(vPdhK=91a8}(_YBE-HFlss$`=+gA98v9=SLIcepd|l2 zUV18Y(kLTthWmH=Y?De}!*}Z}vBe*Je72PY3H{F?NcNF(*|M%<6b9DyFgGR${ZGw6 z5jid&3B@p8+0*K~`}990r~SL|82BClHjVQjY`p0BjR?F?+Nrhludx;?$D4M4MM}(Y z&!h>u@^+Pw=p1>r4!(PnhuvBrC^(enKu}lb?-cBXr%ItumpS$mYJldIeU3aVMvMg_ zn3ceZzwo*USWjd@w~u4UYQ%4JSfxI3_NdNLoXygTO%W_w#PM#trXd&055%e@)$p;G zcKaf+O{E(Pd>A(yY&(^CmjQ%N5X4mRXT(!*{cef{0KpG-hoToE9Wh>sf+X@FM~CveK+Anwcgg)Rz$~O^bA^vOy2|`+OA40(XKhN+s1YA@o1)U1&=&{?RqdomUTM5>2HK|G z|N65c*HusrwZ{s3ANHFIKuMzf?fM6^g)_BPnEQQ)kcQ)si$QX+iKrxxB_QeMw$o!! z;GyDVjHdbZtmC7QS|a!lQvG!gCm)d_{>ZQstdn>){OWd;u;x9t{{obg|LjC|mgn!&XBcHx0BT@sfv!{hp7$oL(LT$;4^;_#;$qy0sxG;zCxYT6t zp)TJXk1mso(3dZ8cw8p5XR?eSeJJ4Tp8gMxs>BV|o>$@GY~PO3?(jo!T>xk#)1Rd? zc3LRqy*^CC<%)en;4eG{9@aTL_!wIQ={W2X3sO$M!JJKlWSDOd{w;aOmG}1>ux$uj zLi1o<*`tHQV{~A!RhPSKX}O2zinDJW$7FP+k-Bnq!xhnow$gODgA-}2weFL@vB5E@ zkf0^68pW&el(N>F;XpZ4I`1cVPH z!jw3R{MMzHDT1q}t(%ufX-FYD@ftOzX^p)A0{f^B6^M>(vL8R+Exk(ulEPzj> zwAO8#segsrZpTCZevR6SHx-=V&)*tS+PlgvNR)Kmpg{8|Nim!s$2&+UOm1j@oq!`# zV{cjRTVvMF%?WYLIz1D!kW6jIExl^1G|9{v>v$;W#buljU|s#u>G|$*yhb z%>N>YuP4W?VrKKPxH|gPk<;#JE&QElzWd-3NwZ`0^jvPY<^MPJ_%DBJ^d1GTs-nl| zb><`1*ee!EyB(wXXWC!8AT$fNGGLW6lcRSvQ#Lgy^E2%7KfUfb8Bn<^P}Fk95CLsh zYxK|J0Hn|K)kr$0gJNHrZxw2X1%7=JFBoDhZaEcoVjSxnFRidF=kf)Ru25vf%v(_` zG-MkmUoxY47)RsGq3n;tJJT~8lrFGN({QQYyh-y0e9}=9gi@sC-L~CCEbEP>B(>sm z)B{f?w!ZUWY-?>n)tN@i5%gUSa!Hp_{dO8i9CJ53lgA6Z>y9222)XNr(8*&^5l~)vK9}U1ctD+{oUCO zg_!O`1utL5aGuh_=+m9)K!#m}P60s~&unE~V38*Zf` z4z}+-g%}Sg1sf;!f;J!wBbmcak3LB&*GND5I=Ef*2M%zp z#&0@P{`jnq(V9_>^PRX;vh=bcO^Gwl7V15*a1a=p%MWvIbcPxzBxL)1^@qmhV~>Sc z@it$FZ1<1DKh=AG)a8_8xoh;0R*WymV{G5YemFmZ&{tMAzav{OayI$yAE81$U&Tw9Xzsx`}rU zdAAlZK<5lfR&2}!<(bWg8l{!wu10WOkgsQw)~fQNIhm6rxo}Ap%*KsI`Bn=o%7#p`c9n0?#8xwYoXLh?K4$=>g_ zBN**qGWfU${?B0{mIHfF;@XS+6WQXu@ZKFy_lh$L3~B|hjl=VUFV^6$o&X$v0eF+# zRtugD!2igp!;E-=y;@(NZElgAf4;x**7_A?!`@1lu&4qw9q84-4q^83smtWL6YlL= zIA*p#VqaJL7I_fIp)4u6-7QK2h1QEs*G1MJmV9auT>k+UwI?uPN8?eEX5;^*NqN{v znPkFodMWDY9|@GRruq@CpY$8brnBFQ>upGgkT^WW+9DpydLs%RI5rRyc*I|~;|J}F zAh);rdy&`Ck10|=Qjt0ZGO`LH6e@zN-0$<+Sg*^B^4xx9xrJW%-qTfx8fA;HZdX^V ze-s;=?UqOrI0jmN641<2Q>fh*K4OkoBR{TM&B%i|h)sR?5+8?oLVOjcWla!V_ZHvh zIUUFD56Rc+svTGD6l_m&GE=&u7^AAHqhI$d5XTNjabrHY9mTNDLB7L{;ubAP0(jI- zwey}hu<5q2a?$*r0=xRY+USHvwz!xfoN))7x7SI#Bk!h^GF!ylbKM5H_Cw4G$XMQw z&`ML|p$gg+X+{_D9exg+_qBhq^ZS}5uah@Vt()wpht8MC%cT=L?h3BHIFkDfR_x*` zhunhaal%;c120+nvDC+uQ=a-ugvG?6^2hyLf6hI`2W6k?L-8OL1V ztW#@e`Vi*?$j^b2*)o(lyNr_KZLM`P;1QZ1alJ0avO!gRzI9Hc-L14@PM%lXr$q~p zCW+6y{-RT@XK6fTgwFD;aUk7B%2s1J*x(0jWQj|!j<`Of=#`idI^p5(fe_Z=H=nCh zd7j?f#?5Ac?t?eJ`d+}QS02Hip>_l7cGR*`__xKP$5e<0?b`dc07tK(8W;e|MLs@Y z<)P9t;9OPKbm)+4{|Dg=;D@4F49?qmhS{e~)@pG%6N@<(f|137mqIt`ou%#8XYP~dI!sLV zSv)eK|HDe;HFBCaD%M6x{wGB}X+Q;B=2Xna4C$245Yi3BD0Mr|`S?Lqw~^bL!0{KA z*p@!mR^gLG8-7y|+39#Fxbc)VIJ^BLWU)w!`ax~C zNXE8jv2Gh=wAFW;U5j->@w<=ghu2BQ#n9Z$aBc{Oc+h1&MPXTR^3Cz~DPAjI+wDz{ zwb{*=i^pS}r~hIcK88YZ>qp>v+y2V~TgY-BRFCd;-!Hu1lQTK)oh#Sk75#?s{ZTRg zOW(0>!^|P)ee$VKFm=PAX;wd!GLB0~-yk&NQmF?w&B4 zBuMn)HWwYJ^QAGU{RMh{j^fjF$(gGveLi4>5!pRnenabRWAdUo0Gncr^?B!HO;)4G z+loJY8S-B~Yb(&Rj?=1Mo5b{n?Cx_63drN1xk}K-qn6Jwldf}_lY5-7l@e=}4^$^B zypysix-aU^(QfRWHNeB-qt-G-l*s2<`jXxFGb*Yx`X5SYHeRPn1Ro?T_?=rw!1mbP zvOeZW*y9;rv|pWCCg?nDewMEQ`gt|o3V?!IZpDc$@3<{R`{FC#lk?+mH!QWI-BOW) zulQfrk$qPSCvVNXij%CqOrx<6nB7IKh z4%^^`ETiNI5r9;TVNB!tHr0_0+|n+6%F*c7IJ8GzEXt3Nel+EPm|F>HR`tGAMto!Si0&qMKfNs%fqpdSJo_e10wgvzkuAw@+_dQX?E-q z;?;5+`7bMh_ImKGM3ZH6{`A*Y%RbmBpb$>FDUglHCzDQn=k;fDo0;wA6?Q}jb&=iO zfVgG`2z=8IH*huEeAA$_i|emj3~D1B8LT)kc`06*!ps zW2nm?rt_weB*T}z!N&7|H8Ae4hIG9IYHa*X5Z=Nn)enLOXZ83!F{@W%Cn7fapdEJv zY(<>G)DgsR^GLOk)XI*@+uo$6HQ1W{SU@AB4ZH*z0C4;+*yjvfiz+1-zJ-kB^St$( zvo>DX^37xw3gGAs}hnP5qt`O?V9@?9Y3(gAU z*|VT&BJB-V?e}*KiYEEprMN%>lyOfv?XwqIV2mQWVEVYS`teQMkmO@9uHRWx$7WgV zVwTSrh0(-w6LItR`O3jE&Pk+IDHG7?OFA*N$wS$f=iGkR*!nN?+i$KQ+ugK-MNDji+O5L6UeEqc~U zEuW?CE=1Bi9**_V?eUS=c7q;4x73-B35IRiwUEHdrU37Ih5UqN&>bZ>iSFf600!dsmyHxno?hx7_OtmFx~ zOv5l;{r80;9Fm!!;S&4tL3b>g_t&UN+t;WOj^ysGk*_w6%V2;AQ)!dhQIHn|K(txu#a>HUaKiDWg--HAh<>6KII>{t#t4H6@KmfF*C8iR0sL zOM^8%-`Z65`Nqje6t#re&U)Lv8M5B^?jMs=#gymMiu)?$Z{~hhVbEv<4Mu{rMgorp z)cWH+Hg2<~z7!2wgnbn`OM(G^0s%umPMMUXd&v3wzjlmp72z15m1D)Ei=w~vNPy~- zyK%TuOezE)gY++DEBPQiPmZuU0agsra7c7>x5Yp2{i%h6YmDtdx{Utn zwFp_`1cfp0=8WoB-3&%ZSGq_5H`BwSunKM=3nrimkMw)p$TAi5LF6)`R4GuDsR>(; zZA)Zyq$sNxLmfquv;J*Ian(!|8cqG?``wLB#lTE1Yl0N}O{m0HLE{8rOs!ii=>wUc zdJ>6`Vd8it;`c3_QJBGW|ATqYBhnB`-Jtf5K-2Rw0w0-13R6cCsA9-T-K)@Aj&6u) z^KGlL%DIVv^qh;(+>_iHi-_-x!@HEqw)*V_u_c{fjZJQ{*GX}cu=gd|3c2aTC_)w` zbY)45=WR(1)OYl&aoj@qF$jXmp8j-R^Vs|es7h%0r8B}MrmDXbOU*JT=QmIx>aNih z+`|qYoEZ}Bmr*ibaBO11qHkF47ShOXYvTnKvh0)c=k1+gpK}f&{+xN zn}_UtryV@iRyq+4992vBRT&HiEhME#?=%jM(A6jb4pfE`HcaivASkreD-pCen&VR> znio9QU zD(TMIziS%!HmILbgtqH4a5m{N=j8h^$p*PNcp|G35$F*>bTxww{qSCmnb&*N!e8Y@ zuz311P)Ric&xcvqaJHHs=;eO85l}?{S94X-r3b!4UkOSmD#lIwdnkrbz5Ztgy z=w`-cX5**zAVKX$KkaQjSe!D6BE>X44e{`8-W{5&pmD^^v6ilzrv57SNV^7E*ol9I z{mz*GS^R%?dyq#YWPv%L=c8=IbE$Tqa`$^nTcq{m1^fpcLG>^#n5mCJG&~|S{KAlS zq&+fay-+9)^(X1e^f$7zNqPb%r&+Nlz(`UqyOJxl(kAmCFAjn7vAW()t?Lx1l>D6q?g5(q6zv58<{2`o8 z>z4?=54Xr@$>hGrh(Q2&3hNzQ3Er^tM;p~Gdi!_4mtkd)+;Z)W2*e|0<;OKp_js=R zv@3B%a5q;|eT^Jov)=ranLvQCoJ&&;ctpOH5HkGP3)gb~N|XqMBIeDGt*80Kw0tjC zhzng!VN2DU(%kzyqn<<;@Xz1YiPUr95_DsV_ArZH;U}@%&(Td_G1fU)Vx9-B^9=BW zU?MAxUh^~kd7L_Db@heQ2*8V8?71QFfNjKY8WES9J?US+9jkjEO%A5){v`*9#l>Hb z=(ss4+V#n;^F;b@2hX_RUvCkCiAj64mH}OuUGrj<>@ov%wzwT~{9nNXN!pf-&1-Q?~u_hp}oIXb%<0cVXQdmkPk@kFOt3OeL zjIMxDnGNk)(rD|cD-rKsZz*9{LXI#XKaRKH8^pGEtw%2`EfX(iVY|lCa`9I?`?@8d zCARfYR;{Wg>bS;9SgBv~dZoP6c`dQ|-J)x9=8E4wTZFOr344KH{MBHSWt89HLjDWO z!==9M&X!xBe#f+Q&TkvvjRGqojm|I%XyPS3sM8C#!pbAv=oZY$jOXiaZ)w+Y4CsN_ zl`%gPMR}Q!H&2zoaSooS<+~Q3ZF3)Rv0^J`m z@(!y~|3d7s%tLN1_h@o6t@Jp1wR$IU8qOFPsjo z;;P!KiL-oRo9wV=n98aeo(`PaXDcIqD@43yNK2MJ%&=4GcWpWn_Y?HsyQiOIpHL>@ zUivXvwBQ**SzBb;E%tNcw65RAtfC$IufLBpGXL6Ex2}O zihIXRP%@wS-!Zplz7wL=Y7Gb{SP3iHY0d|l-rr|>@&e&uw1dE7_%Mv@GD+c^QZ*IP z#=5gg=0XSTFU-V#UF7TP9wtO0rw!+PetQ{Uf^`2{8KA5AR9Gem+!AzgzYBlj@{885 zB9pIa{XbTWu~lzWUh4@cC+I1c#7Dm<3tesKky8w%P^MRGBTE$IcVA=P{_|;@8X-c; z{qOYeNGGS)_(7xPMnt3~dbJXJo?J@W6lN|Z8NkVCy=wDsz#;GOg~j(*FTrZ1RBVBi zF7|l`U%W9kvhovqXlS?lh%O6ws+T^x`bV3;x6Kk;MfS@BAOyy#uWl1&w;9J5ToSS@ zPEY^Lr}qLLVI@H{qx65xs=ZX7l}DJ|%NZFB4-4e!%7+L4D=7?F(m(&88#$lXlzs5~ zcGujs;(0Pj{abi?5b7VPz`|SBNbZkxbyoy|&pzb)jc{qMr(b4my);fOhg#GTBr4rT z6asxV$1Wuo0$UdY3u1pB59=gptlTo^6}ZQIH&r+mQ^R@6cMj=kI1pmxeDI@W$rpe$ z^H$2mh~gTSL>4jlty4rAzP>Fs|L?m1dbMp@B2ugF8IV2C=oPRnf~<1U_`VNMJR^Bg z#`JLgj2-tQ=k)4J(}NA?NLsC>D|U9Km7ov_Dqc&jD=OM%qLT$i+1rbK?T)+78Kir^ zMUMN;cn?g`&PumcOA96hnO}4REE|LCf}Tqh7312Su7_$lr@hvE5g<{}P#j>AA4D+@ zCjsrA$&|3mc<+817)fFLfz9}gS&3V=v{bYw=Z6>9I#p>nNq!b?SmyqlZ{;hJgT`}N zDOco*N{(LN-_cEug41W~$Dm}NT^FNgRp|i3p=Jo`&-7=eHDmRVPBDon=l34-6Qn`n zf28LUB#|E5LNsDd{BYkM)N=Po)sRj~>YtN3=h(S_#Df0|T!?nHQdPtA6kZ>c)(@|^ z4k7uN-fhPlWfMm_As$JfY~8rk?F&UIH6oq(Vd159AlryWL|ZAgZP!po zcUL1+y(jc-&f4wti4MAAwd&i>tZJ+tUAA+qqdb04m|)DQqqh!+Pxu1snKvFm3;y@< z-uxG)A*)C%Vnq6CF!9E_)rc~p&UNB>W9m0L&Ywu32vo)KWP`EBItoz=wNO zuc_Mrt#NO=ZL+IXsr(06+u)c<^-bA~ueQuvVNs0*x$AYC{5l-tB&ly)r;8iDT9pd@ z(a@)vSmEoWl)f>gHW~~?)>gQ#AkFMJwfQQ`eZg?S#jPvfe&yhSH_iR=@(TPb?~3Do z?jz9DE=jcm%+F_a!jREVtFD~p2UiLeZW)M){RekH&ydR4OM|sD^L-V#pWo^VFv{)4 zzb>oW9zE%V#(Zu`Q}63_NukA@@3b5#EXEQb7kU1(=#7fLu>oUzf`DjfgfTptj*V34 zw``1aL&g)bhRBck9+ny@GFE@=s#sWHviRW;7PWtr|7B&MYj_l~Bf7Q#x2 zF@s?p{ctg;!YI7JFN(uX$ErIc?>+9WLC)BjHS(u_J2~^H8^>hDhp190f^(nsU2*qe>1GK$aKEXJYKfmq7v*uI#uis(@wu6brp}|4R=$;@{&$gS9 zT{v}%?(yU~!Xl;;f=@Et>?EvYs^Z-_b&UYYm+Hj@5?2QkS5gO2l$6qTn0T{>H{h6) zZw&_#tk~R_KY0okh6*!v*;pRtOh&s7iFSW)#)CXz6EjJ8NkuN;O|dY97^B=51du5@ zXi}&vOdDzh=+dOiT9fq}*t{p;h2iBuLXEs8$s>)?#-6wT|#WM2`j5YSd-S?6OL5}${m-TcsQ~Nb>gfU}>v!=AYiwh=wOUW?^|ww@%pz)@*VBXj9@FEQz)XA=xNh!e2|XUIWu$UGK{ar8YFbidtrUUbenQU`Fo4WIM}yUA`jD`A9Td zz3~6VrL1m!Qw56M5tBA%c?x%(-gL+p$8Uc++TWILOnAh+qKJ4I{5wih0+2-B4EKMW zu(A4oKV*)Ch8Tmhp`;uKga3Q6Bh&ML-uCm%$9g+yuyGMku;HZlnz;X4bF7fO+!J6R zo?D+Y_8y{>@Ja7S8O03feiCXHDMiNf?Huo44y~4@Rb}kJs)N+K(>Oik&G($xD%U<1 z!8RoOG>t1~Gcs6;-*X2YI=>C8+z1}Ybj`lDO)Bl4epc`xEC%VJn%=;w-b<}CyqPZ~ z%Q>v$^^{9HA8s<{x-Pclj9dF^&Xpu-Q7q7e(>-pHbM}UayXdO@iUnxg_Jo;KYbsnB zkEH^`*2Xqb5<`Y4C4s|OCJHy!$7wi2MH}3D;%5R0>;61@B!28qwqI{m#F3ur6Mgn6Ulg^8~_K_#eiKD;Th2{GSQ)iQ@{L*`=E zq^BE1UpVWbUN5k4xKnFcpE;TzNXc%--m!x;`cuDH2KJ5a!U>aqUMoQUgh6@94Pd|F zfB0K;s;Av!l76xFzv+zm+_0uPH@uGhwt^3Cs;7auk6`vBLH z$-dMJascf%ZM!KLDb5$^XO8KVw5v^>g?#}dlO>$Q*ePT=YrWa6&k67n&L))=t*U7d zI+opJ{yFV?VM`MWm%KnS&n@|*&{}b2d$wA`%A9u9=8ALaY1SXFMT7AAUaq5bKC~#m z8*=WA2o?n~`Mg3gjHzbelb|U1nD#&vSUg>r=pCX8>U5Q6+xi^Ys6IYb&aP-gZdI)k z;&(y~qZ83+lI2_^$V`GBBFHl~Ru{lj)cK{WVMR+NVOP$u{+rXC70{abr>H z^5QU6eT2=gP7u?=~Qc0UP@feqXIu63$v1qLAj$nf}ZRxG9 z%B$)vf_#Zxms#oYHGzJ)F4R=h;xo069c*=HGCv|%%5dm|f(2}I85Zu`xX{gJwKoNr z4QF?t>AOC?sl=r@XZh1QvimeCdXmBW%qvC92LHO2gc&XfWB&F@Mb}709`f4u01`%p zCZhr>X})>1&#So3f4Rh%>YiFb-G{8dyn36MqL0VPy`j~7?IO>8g@ziK;PinwwkQfr z_;KR!_VPTlNV6@z?5`xHf4t4d( z8pQgV*qH*JL2)~pPaj~?=N-ckyN>Js$q!nRBF#i?A^mR9B!n;0y3r7`!+_sP$TKRZs^|m!PY4wJ z)2L?jsa~M0m<$8FdtV5DC6sudHd*h(IkMM#eEmB)Y{4&-+`N^jv-Hxs#RT->nK8?7 zkn%ZiIJivf{X>9XgcB}Zn*pCC;VhbN1JyuJBP4TUgNL)lpNm3hN|Q{pn*N$>l64`! zV{;qiYRwkI4dfXwH0Ax~=KTJv;2F9YuY$%CeP)mjXOm_Wl)CgFo$_ADu`*pG$}!@) z*pI+K)!s>yZDV^DLTI$eofukM-c=Iaq5X7ByX*4gLl(c5mB`2nF%Tv^%pavOq*{cyFtD$O7Mtoud<%Cwqt%agO za7g?n49d(}t~Fi%e5qU)OpITSDZ4g*sc*nQ_zL|gUWMpSf3svnydLrn`Y0DptS2UI zpM5%fsU*3j!ZzZ35Gy^g#w+dH<-IC{pOdwoI@Jad;LF}uaB$X55%p@fE;^a7=~Z$tj&VjSx8RIg?JI^Im+SqC!dS${V6MXg7*7&;h2^tSI@RCg}?$9 zqe~K|me5jkP*Zv(Wy=D#8iTT!1VHNBW@z z=21n2#W}CsFc@EHbf*M&8)2u0cVbwn=1k zi1dY0vdWU^;(->}a9&Sy1DrQ!e_m3_utcq&N4$LMVqx!Q&5{CMrnVA6* zGbw?kyt$He4+aOvDcZ#@OO^e(Qw!gbB#DKl%A(prx5p7Y_}}`u2ACZUJ;DNc;bHfB zH{GeKWj?A=3VVM$&rJVm8^?cXTQ2a|;19;-Uk>i~#iPFUQ)Rhc>>#yxw$ER(SU*I* z@Su(Wo#UHN_utNoV+iEz8w8OG3aPVv;t={&z0Hvk7}tW4# zq+klGp)cxF`tK}OK?n4%7jMeQ;;K|%?B@rQK0Q@_sTs3PQy)`EV03B(eE!C4#EgEu zE0*>&y(T_*d}vex$V18WH%{N&dIH^EJ>B_i$N#l>NofoI7SAFu7}~u z7;LL35ynUI)IN*@*;rmO?L7F5tF3S9f=gp+Icu8mSdLbMQ{NrqcDyhKMkhuRbe5jD zG*7arGP7`y-$tsxqEQ#GtjesdQ(sD=UP$~n ze#vAiuYvnnSmn1tU?M(+nGbGp%-W^QUe&WLx;Nu1k=k`g%Lnv9XXq)bOs!$@>l451 z)G**$uCopVb>rLIAejDrC zxG78&>>)LO=K3@iJ|eNsMmz4D?d-W5CH)?K)}<+jT_7&XbhEa#4EmYSwO_&siM{~2 zgG`)%-WT8S1h-V1Y&wy2O?A@V&Ct8kx;i>7%D7Sw6fZnht@8ETXItW5!xC>Kp5!CP_!Lv4b1j&Sr#7ylPKwPL1XTIBU$eVRVrX2SH^t5VN!nta(XO&MM&B$&de}NK7QuTYl#Xek4-WC* zxVw9!4UM~n;O;aU5XdoA-S8{_8QGA3esZT64}dYpp7za-#)5xYv)I z21l)IGbcNMq29TCoimYC)UHr6V1RPbV#f!F*1IFsGddhhd7WR4hm4E5V!W6g#yp?X zLIBt24W`GiWo#{$kN#A3ex&}J(oGQAhpY>Z_$|ilfmY+)2$95C*7xIKPb(XC^rKi( z7K>Gno2rf8O7)Z3#Y~Svv?MEuxjy-!GP5Z?GHNU&dbDtEx;H^q-;W)vQmj2ak*UkeU=8x!PP>Y!!_y4hoLkB@q{S@sQ;s0S+rdWp~~+V3I)_#YtZ z38=&f_|FpKe?`eTf{L#{S*Z&`F(RneblznI)j-?)wY)k0m`9+^70528=f$hr6_dW= z&vc*Rab>mYbmH@4|8sTA3Fs`JD{vx1$E%pVXd!=QkPe7BBSd~`5 zN8oEy4as`-Zl@IdF6(N}q&bTiVMWv4Q03wUv9Fs$NDVUdCi+UIl*nZfRaF~BU0nS1 z$ycHRvYsgfqlRv)A|E4D9ch64uOH>Au~4r(Ita6_V+~|bn7OIflZ?!9kVAYbKS3MX z`=Yp-=kW;4>XR^6kJ1W0n)AMy*xUkV7qdRRw9U+aWAH%?zd7n<*@=3ysYOM;2EKMP z?bsBwLO|I5H4B)``4F>lSpj{BmISXU=C?N%t~iIh-pcF!l{WWT$tqR9o}|v7nB{?* zxpo)}=a!;U3l!vS_S7VEGo4(hxZ`QXxsrQUYDqJe&8#sDmMzt=OB7Bbq$Q z#^qSt%@DqfXe+m1`8CZZPzig}g7&)-GF>ygNbLY+eYS zy^J@911YR_weFT*;r|LfiG4(=(!MOAd9Q)f%*^8xL-q^Bz+-~_BcFSyE0Xl;k}!B% z00LtQycX-Rg2rzSAl8^Hl;>6;hoWldBz;*;Cf6UY09 z7_r6Pi&%S!XM%v&qc*Y6*ld}>M+4C2vZVt%Kh|d!&-DQH`>V%sS*F>3@^{U^_P4034&WdM@#MK(Y93`RIal2UpSZ-s2WytuBA>Vy7RChJj_#1<2f!xA zydT}9$beFdnTFZEQXJTUZaCHSdlKLv<&z&q@L9C%ZE6hA4{Us{<^J+pb>`pwGX#OQ)BUCiZ8EA3M?z_LocvSBlwm)efXpn#XPnlRwO*5? zUU(8uvt5M<(?bn6B0V}bEziDTiVI7!1KhIRik$|J%AOU))ifK)dhb;k5=?O`&zh-f zo$23}?$^J@HqO1Q1;QWC)hC4s9g*yA!j1H_ow!(ObJ$Y^@FGlcnxWDzB7VQp-&T41 z8SMt`5XS%0Ak*k11#eV&FiqFpj#*fN8ZO9BH`^fHAI6o4m}WxNiYdT+=wpz$Uk;Y1!Z3X^nN7=B^8Z z=$jqIX`Lx0br)y&m)fj_om3thAqZv5<#}I&;+4LKhD>~Z>Yw*nFmb1GgsqBBc!kTd zIm7EdFsfa5Vg4pCocO6aGRFL}q)OU+RAvB`(y7Z*=VS#hu?@cFV<%XmWtl@Zx8|Eu zyZo2qY-smoxtod5=5^dYxRbTuKO8R;|G}?T+QY|Zy778TISa|Yvr@VpyhfMzlKo8g6LBg^geZ+Ds{tx#>(lr?;^Z^pd7x2S5p_OECUqfAo{>&ZLTFf6y2ygIFQqopgSGE3{okgNG*m#b*=UA zdq?G?`a@~j)o(C|1WqNny4(ds1Y6mSeLIKLI7Tp=+I#6TNS1VIa@ff9#%*5tpM}NN zC|<6r`@}O!jq7D%=vC54;3OdJdjbq@hXDJ*j6ewdm&mj{HOvr$up!y}m1}y$;sBk; z0jr^Jjj*W+`6Y!et^wy-cPk3N*UY=%JWou}2+F(k@LHO@*X<%lGMUet=xScYB%4{P z9J1Y4?HmKk&ONePoH5G^*+ZRnb(sa6Q5|tdKksDX2^gZiLzMpZM{0Qrs(g{~fOf_` zlN|qh3yvTsR6n1qm=wn-U+?pm{9sY61wfB}Pc0gj%o}h$TL4*-KDQE&$EOY}0BIP! zBjJN-6^d=r)^=@d-MDikDhuI0YSBVPN{<1dafoMKQ_stao|h_Bv7T>Y3DI8e}p58JGeo$5D6K%Wek`^69_R2_h+ zBfV>;m?{0ykz{r|GomTZx8o;+l-fkOL##lOdeoU`9cc}wMCTLoe0;7}!Pkq(@jAR^ zm71|n$kZoB<=wTo*~DrAcZ=p+TCl?v^ceTxQziWOl|>{-U~m|@M5mSPB5V#=X~E)S z(S}Lwo8+slTw2U-_Xv7%!gFoRc(g!h z@r?FU;|%H{g~y8e7M8?N(dZJyoKWYRS~tp!Tw zY1&8l7=3lX97H7>^hstfHoN19uhilpBP24}zzl~a7DL5KP=h+2({RxhdTOfAC~tva z6<;K7b(#(kmXG@k5JBNF!_~kYaN(?_Sv!xblVIyRt)g8g-}XHuO`iS_?&Uuk$Bt62 zdY;3nIq0;;yhy;I8jPE4i-i4Y-^7*mPS=<-x1*Kr+P>G5rpT%-;nIX~4WG+V_JC@u zoa&&dp=X#7TZ-k&zDhO(W!)@kmAsskEDp0?I;tou3`m$R`OgpuJ}!B(VnDxijg^js zehPk6r8?f62X@XD4b8ahpdGm5z^TsjhOOPgk&nh9p)?B?Z;QLGv@g*JsGjCeZJ6?o zASSo52mj^ad6_{f-JuTf8hF(ajB#BZ8~g?~{2z7WY<@st9a-{@x`!?k7wVVC^L`Hf6xC&>VI^)*WTpx&14B z&g2%|4E6{`9C0Qh{m0cDP3vmCb_*D1*RaYk{F?15z2#13F{dvyjQ+b@bs>s)=XEEm zoA%@prbGlOMpx47&;u`sFje_~IPjwU!@tAjheFTw|8QCq{?`0`NiAJ8MZWS+1ad0o zW;Rls^?PY6!V*+N=GGMJu!jC7$#rY9G$V zTspVxf({-U`_z+=$59ff@1%KYb>4C^RcY7vTHfWCyDB+qdCld1o5;eAcMw?(jH!C0 z2E_<@B5O6PE+)G~nG$dg#U0_PreF>if^~nqWCsZhjSBB+=dZh++6)+4_AnP5PgIfu z_A3JU_N8K7F*^}Z31D~8nXs!_R&O!momYl@-q_-PeF3wY{i2N^MQ4hNVeaNoB*;o*@7}c|dYD49p#Nzdr<#T${iPOMA;N@CsHH z!67oK?{zl)+bGgkii(`M9|qXjEs%#7L+AZQurn9+-IyO4%0qRW?wMU^h>m1xZ ziv`#~@rYfFq&}b*h^7Mc7Tu!jCz=j|&^wSFgq?|h?`=!ay-rW1#N{$Fu5y&B&in{j zj2hKqcW$M~)QUT{^{&ZF4Nc*sgJsvkO!{ zG%o?oFK~J}v}z@~G7j6X^yXYQ#RdR=vaQ4t=K_S_fE58(p#o4rbn+hsQ3uDLp5%ar zjf`iCxqk?vYZVi7oFS6rlf>0~w(dQs7VXYK6M)tVz0g|0^B*`=U1zM&CP9`rNrpYf zt&Rv7qa*15=z*AI(tw_qD)qk9)Y{Oe`z zkq$;vHijkBRROID!UO%|EMa(V%>sr_nG?HkAWI;px1$?-<==agf=~r z_$jU6%LccJQ6dG2w5;GI!?h||{hzIz>4!w#AzS)s-3!zBvio&O4S z|9R@!U_nlHaTc9xHlTFB7uw?3Fi59uLG>2302xi==AA<;J;+awPkS6;qp7@9-?8n= zlG8b^LYRBffcf6u8BMU?O*Y6Tbj9+SeQo_sXdGHCpRUN9#yv9|-YW&AfiEUp{Ul40 z1dYB&hlzKcY#!Z&e6@ZkDcyJ};KD}cpgyRb5$g~h_Q5{#^{4Cs##@^A^fI2}IE|%b zS?rsFA`N?tNb?{8u|sAzCNj6?SYh@I9Sa6w)y7*o^hEcf#I}qqPE0u3|%i|Wof?C z8nNe-Smq0|GFs5&!-nOIk?W-?y55ezY58#qadt2hn*K&o_C^;#Bax6U?dZK^Z_tDJ z9?`n5fX5V7*dlQBV5-72<(XX$wJZkzSQbaVemu`u7C)8D3x9_O@xfEi@N%t@U%fNi z^W$_kzKZ{2C>e-BlOXmVNq{=p&K`ic@#A;|ArDi*g{<#gn&YR)_j=raW*E#l=Fy~$=VoI_g9 z@_^1)!&GLQi|%zLnqsd)dv@e49Ohi<^d-YgBVgTZ3!X&$s`5^Wfx|9|c%P(*NCS!p z*h=svz8rd+oMdSivf87r&Kq;0{FtIZY~eKUrM%P2N8CVA@6YP7#9#4Jj;EHgWM2z< z({gSN&{~S?V3fekx^E~md^KzbKj9mnz*QSkl==2e$A-o3=;P)R!piu!ehwXKOV6wL z#wPlP@Nt;qVrSGaiNpC#7I#gvm43%3gHC1(n)N?x!MmH`&F{e5YbExO;U83EY{I|A^i<)k}0{;ANk zenYci>Yv%*2F|CWv#srCG(}~mqt?Q4reb*mRE@$LOXdX`Uu1NXsg>So!MupxE~#d> z3rI`rxs>3T|G>sAK$FHA+w^jHnl5+7P75u?Oy63ux{Y`-*6t>(ipOmsN9kKzE&Hqc z&)JUyvYw@t!Rg*{Ep8hy_)sELSzD-3ZPH{HVw$D2gjADpk$%;>3LoXJ$Vh#WJ>b(CVlqLaj3 zU`RUbT(qe{{_Y0Hzz+sSGM&SSPOH)P-HqzdcWs$CNM9e;{}?~#G=Rvzud%Wy==c+V zCAzD)tA8wj1cHC@;P^M&d8b>VLYjyzoYtzcKkgAYz}EObxJU2)lxIIwmhU;Ab&o`U z^Zj<0mz8w0ZIL)U?Z3><8BtR3=Kj;{VE+=j0FZwyz)4O^-L|xuShZEPm$_NVo25M@ z9oNAh@#fFVlHCjjlA1eo4GGZs< z!@E(5y+bEj9_)geW;trKtega#l_+4RQU#IXlJ=0tHtAG`>|3hA_Nftw!%XZhy88V2 z)_dqEN2zTL$Dv!uNywe{d>qCO=3|TM!ySZ;(l|wNUId(XS#bEAwE>fOh(lHZER;q_ zjj6SzPARCdb%?6PME)Hdnj7SSnb+MlePL=HV&#x?W6R?AL|QWKkZZTeb_n!NMJBPh zhb>kqIgea>HymT`?uOc7n+fnY_1@HpjMD4wXtevmaww;( ztO~In*WH`6k@y#(t<3DBTUqTWtv=^k(bF8}%nM@-ZH#q8^AyUkGVP7#wK-b%{5D@Y zm|0vI_wBPPUB5zk_-Z}z0?NbJ9$tvnv8y^B1Ed@xE{_uKL>AA|aChGy8cw4<33#SE ztC!QP@OM)X-vN{XR9YnbmLm?lY@qVf$Z2`J&P-;+alGJ{3^DVzzLsrDUZ!02%ZT^E zwP@d|nB)jyv6m&6+Jz&vJdx}5pOhsfEJd&!cNtWcwz{rA@@f@otyEG$XBxmVWWk7J z0fwl|p$SiFwwQvwnQ2*678EMjmvkaK z$Hic_CO<4oJA)+VZ-iiJFLUkt5S?yyJ1gCEv%9ubF^BjjsZUnNna(FIuv&)I+Ea@S z@Q$nV95eHE+t78m++X=` z*SA)XQSFto9*q|h!l2(IcQ=jvbc)fFX^QQ z>1sdAy5T zpQ>Qz<;7U7qQ{!7Ar#g~} zFOou0I@Xe$)<+Ch4SMpPtZe1>Fp%Ie!|w!nD=ICZ08Qm5Bh0E#2|vJkDyeN1 z$*rGp5x|RtaSfFTD|lvZ4sG0WU+q_5xlg`uoI_yk=hPggyM%GdD~tePe5XDeq*FOx zeV*~%*j_H>{prlP$G(1g&KSz+>!pKjs}8~XOp<+?NP2Cym(A&Cd{x7JY_(iRY1y)? z@&r_(_%J&pzny*2g6@TZs!A2A#6#1UJI>ZDKw=evGC#}66h^WlH&TwJE-Sq&)%dIU zEyZ4#mWZ#ocI~Wken?d96LGDs^$J_2#8_!S@m>E_Vr++vf#W|2Nqi#tqwfZ zVQE{WLt5=mwoo*lvxeYG-c5>WqEJ&ejP`bCDR+(@`^3Qxvaj}V`}`e|)@1>GS%Fe^ zDhC9mezPWv+S<1kU)(WMuapxEXaK%7hMA8+1BxdcU>C z{aT;vwD%p)$tM1LRs3g4Hwa8EOC_o zP=BUqU=#*vxO3DD@3t6-yq@o~Hhi6V!r8TWxJTNyS8L%8NpeszAqzS9KIWfP=#^|1 zFnSO^@qFjAkISF2Xz06u{?CqOp+ZaGM-yYr1L~=ivqWL&hVS^Z;ivw!;pfX5Jva&^ zU*xp$r3cio*E>tgHF12}QLj$s)fxjlHfeAdSl}KC)0WhexLT47=6F$u)9$8Vn#%o*cGc?E3PmkuXz4k?LrGbFWqymYZF@Mt>M)%=u{)PamY$7BZJ;8sG~pVD z*dO$v7mLaUzR60dDIT*PH3KYOY`ywzFw|gI`07nr?Uj^2L8`*NS_?}qGD;nM0`+hH zWO;pGq=KrzKJA*_1P0Vige_2qY?&gFfOw-Y^88$HTFWejLBh6TeL(V)b-|{?a%Ry* zd-SZkf$}XMX$avNrpKSCFWwQB7;61|{(5--Key~|5KpgkKKFQl3)ag<)Nk(5HByxK z4N#mHUN?LvJzcdi`%j#AT>JnYW#kHfkEL^54Z=RNF5(iC7 zOeN>Jb9#+2f*~JkK6Of|vh-saU8@mL1%8jA6uQ!@(gBhbm=huGFx;9kp3O+(Al{PL zIxnZ&tOzpjOA>Cq2K8;3P?^vURZX&`fYHWDmftOI?@0W$SXbT&%*e0l-bT5wNG5n( z&x9)N+Fr?!?KNS~{mdMe^5?Z2aXYNiH}2=q;irB-renOdX84#ofg~ru!nokavUany zh4hWW_xtS}gMT&5yh)7fA?5h1w+zyEvg5+a`}2JubC?K&-UvW>Qu#HF&T7+G{mQDP zRl#Q3JFe^AmhxGl2?2Mue0Cw6@|ggyFoEemaGtWJ`z)NF0-*JjU#*ba>C!WNo6q-> z-l=V!1IwEv`)Y>tdeiTV(oPw5=UQEcSwH%(GG^)*z`S%fW^@R5O%51GT1K6DK>9m# zlq~)0rcaR$pR;H)I;vQ~!{!4zeTRiextcPxT6RKPvv7^)DYY^EBr(5whZI&Q(ePKK zdAZO`zx;Vh>*cqODu$U26PE)aD)Kj2ZxhAezU{_8Kzf6DwWK!V=E!@fDvgs`Ajb5t z{OE;2HHRrShWKJYRa_jlgeMv1U^JsPL(k!~dupTni~p3pE1bOINvzU z=+Zu+qPZ8QtyhP{p(a^>s|S-;HFmE5QB#rG8c&65mhV@|5MxY{u=VM7Bt^$BSFKNm z1_RJbV9Yj8U#%EKX2FdPAEI|qxzvH_80Y(n^hiszgUk#Gx$d=NEms$(1S56jX1KR7FAzmxU@<sj2c4c)0-)@0cunr}Yj-9q(gZq-uj$L;;rbPaL~tknY9CqD z-1kFw-ZZ-E*&G<>6sVnI9u#G|LR1%ukWOEX*PW%|(v3{x^(ip_tR68s02wIq&XN%` zvp~oeBn2@dj~=6Rqk`NVjeqUdPpD0&`J`{uk~cQ`&aKfA=t(4;tq|9y-0BtgvCz~^ zITcCY_pC+IG^FB)C)JpWpDbA@qfJk#fg4kt!v}9U#u^^9@ zKkofr-PTj{Ll1Jhj5$y{O_us*G$ z{?JSE8vYo4siDF#6{SnM^}eP|87ABuD)61uO`ix_h;>Cb{0Ik4I<*zMU5p;VfS69_ zx{L0Q^_C-_RQ`^kA@u)VfPbw;9SPFRhh~ymeuwaXt<1j{DYz0_7W^Vb!AglVfWjMW zk!dt$)LN||9tQBmS=ezCv+}ilJz@o6OB`Ql=GVyr&>iTa^#NBbtMgFp*f-U}KnZHm z-q`iCZL7-WXZQ&xcEu(k#5PNx#Fk=CgY22ZZeh0S*@{m1UgDr zh|S?{EOa~E6^k0zS8ZIzO|>QTSh{U~Tc+lBpiiSGj(3OaSX#S_zBxgQQ(G(T&7TKh zql?fLNV?uiR@@GULR>Xnqs|)Y$UiLSFrge>>(ZuEwHWAJkr%`=si=u!_!u;!%*J@+ z-f7H?c9P+|>nX5LP2}g+92H!~N3uZ8i&0YLKYD#3e?_-rUOsP2F!-6NN5_ggfv`Jm zG&(GXYgB$SxyxKN3r~6S{B83U?ogUoqsyRdnbC>+v{}2yTFtiF;ddfpf&a1H{{*$y zom%uI#J&5xn)t8FP$vJ@g(%aK!U5(0yU~HU|5*J;hm{c)L-i=IkR}0zVO%0dl%;Vf z*wC5@#F3YyNGx&g`@KX>ZbA)-#Jj&?#8*NGlYIz%Qhl`y>;2&!MrlJj;(T>8dh?8} zu{AR0VV&*Ui_azW6PM&|K{wv~4b759A1hxUT4*`~6)HZ{_{EDw>&~iJaD}rKeOK^h zaGR(`RHup@oh)RSb~xfl=j}-%{8w<9-( z0a^JL59StC6&jtXlth&imtk{1n%s;0A1{y=c}FH$JMJ~)XwJkXGFDzWU8PtH3s)%~ zdJX3_Hr&1EcySakAZB5UcX4(1%lfRUp{+xlD1u%jl#g=+TSWB>>a~xc|Vx!hRgMyRxXHloFJi@icc}r)Ct9gn2o=s3m zqhE-Z-v#Ns7a&g^UX*a8h^@w~!{vaHqoGNz@Jj&;2_7_g_e zvmlzr5vHd5;YUTON7XQ1`j3{xq6Mbimn8y*%uLFk2a(fzHw?e-><*h%IG6T&(lq&2 zaLFoJducm4)$I4R5WqQEWp`2)AXG9}mWS?1Oq%WS#C+}T-iG(H1@@441xF+1e(M};(;g_IEOY^lkFT1#~i+>MbvnZ zy*HMi#9y;B_gRfn_<7oe#AKCOfFHX1@wa}Z-yiR~Rd%QYiRSw52sn7|^*Kff3D`;nG!0uiLkK)=1VFXZIS6XE{ z@dd4P2r}yE&P$0Yk$xFFpSe>_50j1nDSn9P_))U$dH1Jg&C78&0(OEV#zBBYk2eU`Vz6l>MMNzls`w=3*G zG!P8+g6$8O>)(hCQ!wHu8oS1xrfiM$08%98j&tVSV z8W2_pdp1m(*`WrdCCfKm?+2V(X2BrSTC}fQ#DQ-7@`{zW_wN-Wdynq>g`vHcZ*v{* zMLQ{(i}4XuF!hY&?18Q|ew7QBgd8p;J64q|;kF0ee}Y-1nDR09u!E|y2Dn2&JK7yS|6k-9Jh`efeWoE2BzKn9yg)yECH-Ov43*7x|CG(T zMx?9A?(G1g9gc1FG_CzrL7>B#JKD~xI~vx5iJ$ZGY!0&en0NUDLqs5P>N`@+y*E3R z!Bvw}Sc4o4PL#gex^=O>3U;1YTlm-Y{ZyZ<`CEpt4T63)tXkl(a>Y?2U*U2ROEz|R z8R`tO)Y5_trExoxS#oqAjZQdqL>Bm)kw$Bbzr_J$Ix*023CT<@G>;b+%U%xc! zQ2|#!h%)!7W%(72QnVyb5J=uI0pCcVBsUf{lVwr*p^m3*GiutPyn8m9iS618c)(q?~o19~GMnJcZP+p+^0PGk!%`uJ+wZ{gj z2`s`d8sigLaw&`%U3QL`cjtM!>phreSesd%z0a&3mD;ICXuenyzwpVDHP4i+#FFW+ z)H{As6*~ww&EKCgq19^^1(AK<*0yag}BXiJhwoF;s&&sx18yAaoxi z7-8M2eXJZwY4ZE6waz&XDic;wJL=#bt$T0^9QkX~L*+y!8SL=~fXGsa{NIW~LW~M- zQbqSs2pVk zC@AzIJP?6I+qRZF!BC#?~IEOfF|n%NFQ(Trp(xzck8N251^*$`H>R791ysroy@i;FPTF z_ny|P71RyXN9l8@#ub-2jx8L>!*_W))uw<8obsga{~A4LXcK?GZ{6d^R7{vF-^psk8AE&Vh-2#qgG;}s5v2%R<7(hyp^ zl$HLQFoddxyxW{SdlZ4w0xmPli@K2W32o|?XfUu?O7K8ghrg+-vKQK)v6_i}*fd0Z zvOupppi6JiPuF4afWxyoL?BgR>*8GLR8jXco}07N-1%d;x~ApzqE&O|{Sa02BEa9o zK{v)X+6S)?kpymxby;%m5Fgg}W4Ba!I(%f{-~xwkjA8bgc@zFby3mdviF@k&5ej@R~Ff8=p45-}ymtATM}5y53LYIbd2@ zV6$R$I1?um>>xkIHy?(~%&dF`c3_|7l~#_;TqfA=+@pvQLChd)k0tJEM{8AIUU|h* z^>||9_5Jc*Prv<$(|xQAxLgI@@fif5!!CSLHSv+~?=hu;Q(ybMGmkq-CM0oFSsCL5 z0y-OIl$aBHsV!GvFLekuz1?wlkRP$4chHFKa18Tg^!-hdIdzG9fqRajM{E|Njl)qF z{$>%h{_vo>kxf*uiSM{;C>nQ*94AGVN^B36W8BwA6j;fi;T zk48P)d&qq|*0rW!sOY>n{Ik+`2ss9!jOYGvWh*Oh^2E`dQvdkUD!4d!<>-o@edJPK zKbjWXqzacQPJ#Qf-U%ymx!Ug$99J_!h?Wu{#wFd{UZ4%q%KARIqHT6lFj%{2yV;fS zt8{?fWF+O2jv{i}D02yeqV>Zh@2C>ipQNV>_*kLJ4Xw*M%KGSux$bQcR_YR*_OvZ> zj)vYO=+C$Lp8Y@RD>0;1$YsJj*yZA_7OU&s;JQ7k5xw<}BHsh)W&=PIW)h{3Lze9x(YCvQm>(ns>&6Kd$!dJlPQRRN*e{CtIb%{E+1tM z!epfvKS&eg25|n46*4Hv9~5*U^Rk?!BiBvp`71WS<_fyQZ@fk9Ep;S}ni`?kE*ZGHHIeA~?}U zp+K2Iwz__~i>xi(3`o`lqoAh`p<0rLYe|h^OV1YlWL#SlvbvoVK)BI3Rr8J+hcv^5 zopEq(n+vGv`K1_KWf$hIQ7i~WuQrI;S0jsqDF+(ICU^%FaBZ#wb?Y-Y9IZggs|4km zO^YPvhZ728TLi4j%l?A{0~_3!h+CEh5GvozAFrr=?aUpZe^qTxG$^O9-@&IfHQG^Z zn|WP7`K5CqY3+tEL0_YQWLf;*?!bRXb4C%0m)0z@e@}Z(N>yitOh&}*|1*j} z7Gz?tE&YrLNGNEX2oK~wzCopvHQN12(KEo<`b7XtBeEYWM>lT;gsACSTEhX6qy6U0 z8;=o=O~9#6El#Mz)y3)voG1ZIeROi3TUa$uE}m)f&j3aLNHVoS6TY3&D4VVJ-njxz z94nFaMQbU#Bp`5!Fn&su>1SIiWA{MC1hd4rxGC0L2Zp(2wXff6LMj{H${UIwpRaGh z89kAnXZ2K+Cx5MpS+Roy&>u@Io`D>c$Uhv!46!gWrkmdA)Z0!o64)V%B)7r>M~gq@ zb>%5FG>&d-(Q(nVdSy}k&-_kqYRe6i2}rCy>Hw7_IQ>{FjbHD95>WPkARupO0oB|n z_VT}D{4bX)6Din!zON)jUqk9^TfpI=xj_x^hgwD|W6c57Hk@8R?3gK2O*6O<*QzPX zwv2OU*EDc(Q5LRa2q|3E#l5#%AFEhGU}!|;tLDf%z-(fiWS_D?iCcPw!r6SKuNh;b zz1VIwW9XMDJmKzi>zAE3h;|*u6%Bqd$omQ ziyD2E70eC5l_yN4;g~*lZ|q|^&~idkgS_26x+FhQuKQ&rl2YasW=XS@6%YREoIpQj zHmCnEhy`?XJKXv*q4RM}EepXcX5Ep|u93hhZUtYgQS1-Z=w(@xe!LuIG;p6>f*W0> zI@uR)@l9p%+PMxG20|8jUSB_>+_|uIzc5K7|1ESMdaDt?zuw>F4jZ+GkUfo0a(vmk zsjP|t1Kx(}ACfZ60^_76DfyRw2rC5w$jF46ns=`GwbwcV>}5w!nx`^_7m4BhOFLzk z?3JW9=2$nfod@*Z{_?5+Xr1tRw)bhqD6b@#c4)yZKPVwf4mX>&mO6IV=*gt=Oa^wx zo2a4*B;`2s$jKy$K0T&TWV+AQm5Cz8Rb_pL_ZtTx7Qvg#eGkcH+GrUP~xK>^hTp= z_&D%9-VZG9nRnQ|O}z2`=Ue=wY2f~064Mw_O8IKY#*XhINbY=(R|&yM_~VuyjfCoy zb^+R$TJsN!oO^FD_00h%X>*M*UtQSWs(MjVwin*LL=&(6ZpK1&5hxBdMcGk^HDLR? zvZ~6dJ-tH*s*9W#q|AUU9T2Y~ry_<{>u}II!*_}s?s`mx;jjd<5rZVE7DT-WX=`0B zS6@BXnRj`3)*N&7fL~O3db$+s$v|7#=r|K@o*@5Jv%0CT<8BSIvm=-3JU3265eSX% zsg<<7^bGQ;cv8K-8YUlqC_}oyt>+x`?v<~HCEIp7s-0RCPWXPs zkeHEypSdlHm$w!XuK-Y1C$^$6NgsWr@+YQYm5&r2?WvYKXYyGr|6C!a31W~P$`vb7RY}s6yN|?R5szNmY*)<;{!16VYZQv# z#_Wt-G__bfzMqSK%IW~H7d=W~yo?sr_HZw3lS<$F$ucB{ji!mu558Z33_@e~MS|L|>8!%n z5*=WKsKw=yYH`TK)v)7EY-47T>Uzj; z>nCRS*d%2xn1K->`)IMRNM$pwmteqgo=!Bo`hH@1N~_&3gAwsoYr`{jE!#Z?R3HUt z&l`8oDV4R(pt>D7oVMa`;gxQ++)pESPBzH@X7@BEH(Xgi$srZg{z9|1KrRSUb&&UH z;5Lnero|rjMd7nH^!?B?BaUE3k5d^g& z$0cf|o~gci4p|7URDV-~(_;7kPw+Z0p%R;zw)*+tRcEXb(&Xq9zuc=tQqaLav@`Z? zrxhOE~49EfrQ6;H0xPs%;`4ZAJR! z*yr^SDp=U%`fzuqEGJ6>2xwz z6#UT7)(0ntbZV#5H2D=f?e=^$<&kKr%CW z@AJ}x)-K8`wlusMyh%87qB|-icExmB*-V6%h&mS(o(jOjmxJdGA>x(>?+C9^pC!)~Vqe9d%q5D1!LsHFqncKfkvRRIB57U1-6Y!J|NM<@B^V1MKJ`AMMm6 z(Z@J3I+Gd4~_QZM<2 zxkt3bpzhoKWQqU4)H4a%ZwQzru{=1@3v~zHcZG??VxNR4Tkhe*`+^VECSt@QHK0P| z{b_My)1K}%y~8;F1>&E`B=?A=rgGRX(JJAZ(2M3$9nm-$L6(FQW=iwddrp0FSMT=v zNS_HzAl&kcDl*pUzbj*f=nveesX6%_tSUJ##=rz013H%tUP@GquA+_Ug%8>-$e4Ky zsJ=6@@zqF)mpeARkwf)6D6SWga}~NLF-|1Kw;kA)))dyWB|?62ndFPt&w%7+J~&7T z2khQu-#)I!v)h#`N>T@^Cq~$gZd~paK(LOzNZ*?qx#2b4TpO z6je%Vt5qwiimFw$#VS=Zv8ferRn=BW?Gby{j7>z))?TqwB((O7nh}xY8~6RWuitf- z`}YTz3s+v}bx$PV;#EP;S z)WWf4<77*}O>GPdnW$RgMZk|O1Kb_mUosRM|4wyzYSeX?u;OZnvmI}Kr;+PCU@Yhh z$(Xt6RoU{;e()Kgk4#_DZasOgsL{)C)7K5D)2d%GTOE3_>>MT+%TVHq{}_5wtJB!W zZn{ZM;-x)qHfd{uPq!3VETG8`9^k0;4e`T%b4ydZ(p_VN!+=E#f6Z@)7H!ozK8d#) zZ%=Z1EU~QY|K6W|t4i^Uxd#F4-JgQQrO4{C$&?E)&l-oqyt&rnby?%T3KEt+B%KhC_};(3*<%LH6C=nUUj`xW=-}>07<041w z`<(_0!%s|uCvp*V!B4&;=w>%b3&94&_DO^{90?8sJQp6yuS%lG67ZK&o(*sjzYCa| zZOfW8r9zrmybJuLa7afeX<`nA535<-xuiQJUVg*3L=uwNyJSBwA322gAezfNpaQNZ zNSdAxbDzYc@YJW8Ef4gIbA6A`IAIoDjJu|1H*+0LIP2Cx6VpOS+Zv=6z|R5PU)<}G z@^{ZssKRc^I37vVyOsx|HtI6=e~N^q&&?(n`}FWNXP3E zN3$sg=b!%GTxdMQE`*u;Mk?M&`{vwU)xU-iQXOwRg~uW=W`cwc#+w+S_0}rs7bR%j z8AvVdu3D=cf3Q$2y8G@%-5h{)@&dY45g%FcuFcqOa*>d{v1{^!;10~rx=Y|{ramA} z)R_WmvTmVc{M439tqc8a78?6s_Y7{hA8N^2y!@4VJz;TkHDtQ>Qa{Z+14ogwLq*aT&sN2n+ca1&Wp!>&2A32;#K9Tpk=?v%^Wd_K@66(5@rtt79fm?ll5{a2uTX=&V zOscX@`)B2M)Mh0k-dlKbt_1Ni-nm`G?1NneiZ(RHk5x#n`j%3uZFj5kvv>RaY@YDg zlu+FJyA_ftBbjY$3U;V*UN6f;0lV)=;^N-+n=wO4UWSMPG*sNozbYjgC$s?{iUN}U5&h5ux1MIW^|l3emfrIE(IFD>lKIN zv-lk`Sy`|vw70Q}#G%b@)jBZxVgs{g-u$5_Fl@-XuEuQ;o_xCQWZ^%=LKtTbc&yy9&ZruW~k4y-aBA=>a@Zr>OL-XgF_(d*f$S;rEl`1kBe*KLDX-yeU_y z^OCDP{IuH&PFW<9H$FPz93z0!BMyd)N@bQ{qJWSv01Ya`T27M1jlP)_b4uAe}eI^&3gs^~@ie`o`KWQmW1BX;s4Z?tEfQ|m{_ z@za<%1u6cKy?fUu5MAUUsTyf=Dx1eGPDWFz#}2)>+boHwwRkUZR;v1G#mb-ee4vJb zK-~qx$SkaD5*X|@e;~#bSr4<3plh)vZSXE{2eLUDb@OkJ)MUj)>(#PCS`GbGC`^C+ z%Q@%c+;q8aD5W*9$f@d&_^8CxV)^oS-rmuLRB9T~0lBV+CF`+%r6_roImqms-%ZhF zMNPcJqX~`Z&J% zBv`dW>wY6-Lx!MeFY;zBd=?ZA9xVQr>mlws?Bp}}lxKO)|^Y(YR=WU zUCY?p9Mzi~W&LdTkt`^E764^7R5oRc2hr9p1a^+3uOXx&`cZ!zf!J%Vm>8_#G-XQ} z7)H-Eh|)|dkBC&sF;sW|N1{9d&oE|s?OcDfJmmQ{<)hb$4ueanF1#$L4|^{#3xzu^ znpbV4+J#iKDdpluzBw+^c2$0x_m{DLjg7i2#$F$crXS`p(v1OL`G}V&aKGfd@-2(W zc4xMI{L`^V9?TL~9lS=4**ww&clD<`BdlP4ZNuLzR;iDZL?YtnP`3WF zfEs=X=aR%&p~c-eVW!(tn-M0oy}$1~@qCMK1J(%Mh;!h{S^VYqb=HHrG>F|T&kgi# zruW8&5D>`OLgfnM)3Vo3^v|khphH}-*@Hg~aW&)QUT>v}oC#*$&dIYl*QK%z`HkMe z5oCN!lKGFZ%|CD%9&w8RRj)N-+T=K1DAp_}B7SWVqb4?ZZl>=3CyPdnDXFVp!j>Yt zglqyfD*cRn;aF96L_ZsUTCBH41uo!G1I#&cu6Z!Wc#e?mx)koVHgPX4K5%&#mGEGu zkWG$KOD=9t-ga(*w=`a(KK$Ky%{Zy`J7kHHL0lRi$lj$#wE6-RclQnLkI8A{#kM)N zq(qra$TdxB1GmVg_Ct`Ul_-Lcy?HtOB zm{zP;pOz^<`(Kp<%bQpF+5!Uf*$cDU46$GWV03-To4vG`{B~c2!bdFIfWmJ!xCaBw z+pM+}^P#1K`?l?1jg98gR*omzq2suXW#YU|UhK;@-r3LrTedP4$SbpH2=&u%PpOvi zJHUX?<;=L_b8)M)Je73Y7>G1D>iriTE6HJu@<^+G;L{rV5P+#fp3rRZ#`LClPS6w@ zj_d#Vk4=?*#Sej`QgMq0o~}s`q5soHJvdyuD`4k50&UKtc{lKl&Hg%YHv!<29l5mi zoPv&2HDkZaD6saWwWTqJ=eQ#$F+u$CM3s9XN!Gr}H&*;|y&{YLMuOLBgAkHKA6h){ zGs`#NiNWBU(A+ZFO0<(33a?9#GBUp}w%V;XZwJIG4m`}Z!rh#YtM!Z9Be5mE`VrSN zAnsBz>B;{@U}Lb2L#-tcsU(p&g_UP@f*ykGvK7{= zjAVqfjS0*$iTHe7j=)C2~`87IfvWkS3Hlx819)zMV_bjS;b=JZmcn zU^TG{KL+=JqD3k;1xRIFgEdrk?Ykg8b=TH_bq0b5j$=dF5s#C=hU!@hpIXh@3eJ^K zfRCtnd!^A3?VsG5g@t5m-|e!hK^b#@yDTCgx^ z+?p+paTVv5_H+HEeKJzFA&t8Jn-%AnZNDmIgQ+&YHCB{JCH<}#s4daNQSWG4HF9eD zt;M4=07+ly{?0_16@vEI+rt7ii~h>On#y_qG?q*VDXgq{!s9q@&YpRtD-Yn34KIqy z4QcCwRt!(4OYrYARgbwmSYXiIBry@o2yR4A3-9_%7@L4f*I|wslAHdQI^CW#tcJCd zF>UONOy7S*)GxkF0O?P1`gbU=B6saH^GjaCU)$eLAr)YuhV216R-=f#T6Dd2?V`s| zudiC(vHdAuCU}ugLX^mF*RJQN{bI4#@Q)CG(?Jh3cNkNwN-Podj-HU}`1rkcMp9ub+v^7GF-OZ)=dsKSa zkof!htxNfoyJ9#p-;6n@=A%OF`hd~q8T+X^{Ums$yg>ci$uu>t7N7FMpYiygv*b!f zC5thm9ICCWgev6$;sZHjdpF*fShupIw`~j&>#sP)G-v*c{~`h$F$?Qx#=o++8{ny4 zo-se|^EPXi%3$x%A0JPz|G}(TGhYwqQCWK}Hy2Yb5vibByi{p8FScPPEnk^`U!mL3 z1}gt1N7jM-j1UR)zSd({fKkHioBNpNQ5YWzOQOGGw`9OF8@o0E0}He3@%O~uAjIb# zl-4Xd@J?O{ZQ%nVyL|RoAH+(OWKWjKV5Qq)-FvpW!e~j>fafLQ5*CS22!LYlWjNrN zC0i2iYnQD3)IFbl?*_)6ohe3@ercY^xuJ8DoKmD}5gZY(Mz^4a5T$zC@IKfk62`w1 zDkSYY1W}fu6NQ0ydyu5A>$5%^-X9KOERH_t)P7o$I^FHU{|tQmAJ{~oC#&1RXDy;s z&DriZifEA-2GOk?f70&Bn(0G%+o3=BXWAywl&_?n3u7@Z?RByE#PY!oHvl~iUAWlg zc^S9i)^e&x3ZBnwM}7P8A93N&`ys0 zp5+&W*p@SDG6IROD3`4HM%bGC)yls2Nmf2iD&MK=Do^qDsNp6z))!e#g_wL{W`QP5 zu2lI52$(%>Z<=YjeNV}k@e*TquT;eBvhc=-kll&9z_3Mmmp#fN`#t}PH-HjiuDHJI zOPxdS@MC`u-2J*tjl)*>#%zCE67X@#WFLD9TnGztpZp+Ow`e`} zz}2Oso@t~|^46!u#lBlo?GaYhj=`j>vo4%xBv~r%cgQh}`Mi)=bkIG_*FVGziFjt~Iju%2lAT3Tvbk=e)4ttiyF7wMvq; zLw))rKJKBlyUwUFm7ApWuJ8h+R8u=joi%%F z*4oaq^Fc=y#}kW?l%-0QSYrHK=f=kxN*YCh`f2+Uu=d6^gw~+Rk1_xK1@@?2`TEDE z)TgRx-$JRMS8il`)>CG-@wJ-YHCttbH@f;Fn9K6dU9WPK6?iYGiYC6?^O#qo`!uxu ziWo7%lfu#Y{sm}#70{r`A)IjH+Hd`mDWxec;5lWRK4t|bmJ+iTQLXKY8Wr|@Vlc?p zW^6fpu1eRBl4PINWYMV7^C~1@Nlve3H2Q}H?EQlk}xl{c@LI4KKEgN=1IL;N8QT@D*-gniB z0~wA=F{JE{dkRju5|g~)X1=#*BMWxZ{jV=o+^Z;+C>Swc8u!RPegFL)wNq``e^7T= z)jMj`{vR>hw6@EJ$(8o^0l?=a2t5#F-3c6GdJ^J(|Iz|2{n{kK6@IRobBBZ4y5U!#l;F$=nwWCIWD)0O84a%vDo?$He3| zKF0xpnzG@$AS2k=!tzNU53C=1b;I+tTGv=vMXzIfux0OBDxqNOVdMAP7lsHINBcFV zoxyK5V6>@}`=}idqabah57BFS#W=aO}CUf40ve&=4v71%s;l z8_i!>XE$To?rphRQhehkLRLx(W0F)gGF9pnW74{e16JEX8o9PrWtcF0Jgu)NU>F?g zgnCu}Lf}g4L$52R$^cmHDO<^$f+zNM*BVHCNCssB1?F9Qovq5`XNjjX(#o%0_`;ae z^LL-620q=e3nZt$$U(jOs+pmkFL&n!EMBNq9{KXqKU23uB|3AS=&)}t)a~nZa13XD zI$lxh+hSNd&>~)VLjaNZ2*z(t?kUEPZrmHr>txxwlFvIyw~(Agirb`i!K``R9Q&PD_A#U#lnva!IzK~73ghTo ztjrp}k>#L^pVAm>Hm8fVo>||)m<_N*NXfgTgate`FCkro_-~4{k~mE8N?y+LhI6I;ERWEC1MB!{jkrf` z)I`8&_LJcMItZbS11(-Lx)xYS(Fw(+LMT8WjyLcpHr<+a;e5fadksGB{bv;INTS3o z&|kCEe}MCMpxchme){b02<~Og4RX+a@jz4me67_cm@p*lZ14GzHMnYNUadd2km52q zl=KHUkWdO27u+AQi6;FvWxY(l_4Is8o@Z)9;KD*5+f%%-CTCr=;rL6X;@2lD+RS)l3+tbHogw|;{}J%or@M6y5Py2@qFC&rb;(d}HuO}7?!dk)66@p% zn~ALAQbxNxV^)^^VmPyb{k?86wgS1qA)Petzg`B*7QL(Hq&Wu@zayHHI-4V)`D&*{ z+Hkj5^xC_42$Sg@*%5zLKqy+lEtX_qnDfvF&^Q=J|M|P@G=pwqjsF{I4lcIK&@iaz zu>9(UINUxs(i8H#8aHRKJ6=_jxyml#)5-R!W?!wOC0l9MUf+IYZRp~in}#a6Bbpxp ze8{3jb!c$5^wtWU0m6{hy~V+W93T(hWW{b$E6B*s?snWnX%H6)DO5 z6mC2&737Q6!E)K09ouzeG}^0uLiXyXFRC^XoXl#dHDQTVXX*r z^R5`Oqx2fJcAF9cP{YytY;zjS07F+;H5 zXZ)<-NtM96ZmfL@eCQbLHy0=#2wq_~wYNCNs0`%K8I?@vev*w*wlJfFOGdwadgAXY zi}jUKYLB$n?Gt~6Y5z3&Un*@I+fP@bi=&j_c0Oq@->iK|=G2Ad_C~cPk*BDJ95{W) zFpt~*cPEQqTgH@ctl4xzO zd_x24C%CUa(6W9jX;iP~1>0Lo&Co$IMb zvG0syN1Jc#U5|<&nb)er0=lv9#{@R>Iab}^`>h7{+~d>i<&%7kVg?=ev^Em>%GF;~ zNg!`KSM!Z$pPt0kV^Bc$j)F&lbG22e%E!4A5x^X9xa*^?3woq3u*uk$H-#&9m^gJs z@F*KV)(t2wDODVGLlb&(D8U!YPK~qY0z%~Q>I8&)!wvK66PLVH#nP}PGQA2?J4E9T zjhD>v~4F>4-y+<{WPjXv?n$-P(#V0B7Xkd-X^ zm2qmmqt|E;;ghg0tHfHKbOFV%Z8!bfbDh#p^5um7$TohiAf>SI2E<%$;P!WA{gD){ zozZjp-L?NVd_l^pzjnia2W|ueI@WNQx0$8mJ*rH+7=4OXR@~C}7yRDKz5Nrmkx;A? zIKBU;N0tm-SKx$(0((_CN{Hx=M*k_9$@3YqmSh4$kst~p1IS$eUCs(!f>7QdEajOd zlLrRdx~Vndm=u16+{T=F{Oh^~|8QpbV$pW2qsD|!;rCGIGlWt89Ai-5%LzjJYZYJ) z|N4h*(NFiAN8hWqGcEfrfHi3WcDbJ5;~#MmV@~3kDEiTSA%(H2SxL!W5Xh-+U^K>{ z-SOssb^(lzzJV=FfA_3#@y(Ze|J()=a>@9w)4OoZ_=>YD+R!QD%WB?`mbZEV2j97kp7K{VD1#)eq=@YS-3sFx z#xT{5^Cxh})j9I*ds<${r-68jOeN9Nm1F${w|qfU^ZE99@Rv6|32LBBNbpMbjWl!V zZkqyMo9*>l7}k{)ww8gMWgovYDXTh0a92`Rt@c-tO3>2xFkN@9bX|L!AE)N>Ulmze z-oK!jPHc48KK;-A|CTxJqAQM|D>H2!svMP_YY#khbI!an8HPwoUR{_@XBUQ^8BjeI zV8E{VaLF~T7Vx&xosQLTuk2sYf0Z;XnJhxEuDqho{}*{^!VsQ=(xBf za!I;oI4UeP$?i>>_+x)lK%;FZSc2)fmJS$`#jw=&7TuaLn#wiVSf?A^3%h`OrwYe0P2*oUvT6_C-zNf7jgR@`jT%{^;1-avM$8!C-{T6B7;Gppsk#$DmE$~VYy$B z@Zkg5`aC>FHQU^C(kW?he91pY?voyrsguCX@VCeX7*AwT4L|0TK@9#X>RWbP>cc5g zR$j91XMrfs*zbxLP7nAIcH8LW*T3lF)dv2_ksbCo@M>Y1?`ylbv@SOAq|y|sQ@f-+ zC>+U_i>EI3gv0X!ZNl1!HBu{Edu`?}lSXd>vbt_us+`zK8yu%2tlyV1j1g-vWRK5N zAU4iX@`-u!h3-@jT|4Hz@Mw&T<7p9+MVv}Vz|8tPs>fh&P&oH%=U8#La^YFHqSwfW zoH#^EL|E%Rbt7rRReg{#e{PQbFkHrPnsA1$hZ;R#_q;G`>Q9Q_PHop3Zk3&v@doru z%IHSOf{Lg@zU*&(fBIha;St^+kB8-x?hZe6@T^;*5WH={F7pajS+Wh~y3ny@truv3 zRroj$QrgTHlWH*Gv*Mw@wG;y_H#3e$clUO^%3Rgk!4)vN_{KQZHzSs_rI#Q3y!r}( zEARpKdRFklvTsl(C2P2Wr@2u0xlsGNspnTozc)7sk2nxJnWSM@NH5Z2H-d`&(s*;e ztJlGIzqMmI8E{IEscYwK{&e5*9@%eoM4W@uh?aSQg!WXeL6Y3IYl(Jbt1`&ElL=?G z%DdLsio1_dfvKoUQ%qS(J+Akp#ji=AMh4}2W6(V$^u#ZKYp+V4=FG4XY%4WzhNZ>~ zAQj)v1s#tdbPy2ijoY+~de`PHVEAkX$cn3xo6x6DWo)6P|x`JcJ!vkDC}B1qoBU4fxF-EY83a%CB1jT`<`ElUI}~?NWD4h8_RY(Kv=lP>yfN9UOYzt zqUEJoG3{R73XpBJn`rW^p=y=OY+=3X54t(1vH`)Qb3Q4;J-~Qu#W))uV-EbwUIRDd zV_qi{AhM0V#j#6FqohZG*9Hk=yA1P%sJwZ)7OH@cYWs~+ko47?UPNt06nfQ8KC~|; z{gH-Y4QEMntln#0vMt821a?kM4aXVRV(O;fI5uUJiy*G$BR^ai`yh5Ts$S-WYycY< zqoA|=T{ASDjb@!Lj8a_t%!|&KP?hnrbjf*h66GIC3ayZ=ZIq@>O@CRr;hpc_n07nG zb?iwdG@0xi4~}o^i7PD72>Hf7>9X!P8)Q?H4TOo6$YgUA$x-G|Nf0;SQmRVTLr_qFU_A5&c?- z?EM8Gx#PMq>Q|1HvWQ=^4LA?ROeaQ>7UEP{%y}7I0(qz-EFKG4604Kku$j%BF9C+c zwd)`!ztxs;dPL@-G*DTER+CWn`gZTH@$g5nO~xD3RSjEx99s?d6tn_OA-Abt#aTS% z+4&Wkk-@OywMY&bq@-nl3gi}szG!&B^iLwEY@)brp*oe)$~N#%RYr+jsCq=rT% zmeRTaNaJnB>y0k!2833I5^p#_(*M0@4J$506~<@IleX+%?31b%E~~OKWv`kG_i)X3k@t4^z1vG351*bT-lZL*XP&F6t5JBB;`dQ}r+&)L zr#`c}*2HSR)n*(!@2sv_yf^9~+lz7u^TXJ)X5V}r8>m-2Mk}>}7WP8atLqe@ak{`& zLsEw`*mKQ)!IyA#vYQ#-zt*nxSb?iYvC(_US7O=2E+yDu^=fu#|1kmi%puLH-%CU) zqJ`EDwY?EgsY0O-bdiGt;q!4a%HBD?B`XyJp7s0r^bI| z+uc1CYt0lNh_Ta>Es5LS{~bGyZIj&}pNr2~Twx|&h7kK5vCf@ZM!r~nDRk&8#E@sh zbA%m36FD4 zL>K31iw*J_ZDM`ekj0juaza>szq9sc5%Q|_2^DviD0J- zVq=I+6duo$i>z=I!@2_mw?sefArOiRQaGt%KEb&gWJnyV77T9d$@c7&$bUDt(rP+3 z2L(nO_k81fe{)X2`@|}UeUMYv*@;CE{neAOU5FWH&oI-qn0_?=sr*4PLl^fCP}eH` zk5#2V<%Z9G4lDQwg!k9VEm4BiMpnnAN1Pnr&d5HB{XAt?3NjwANz7j{Q^)b$7>2Wy zI-~Td4V!AiI4R+y#-_UC9*wp6RQq($E~7n^`hPxS$JO*}1lqy;D*VEBYc9m6C@8~u zl_t6EYE_Z&l2=A%bRhoDPLJDPvDJtOHyc0JoxuCRcOIG#hV?d2Eq?D3 zK~Lx+U#EGQNRX0l6E8f!oArV+&-UXE`U>cEaePbHWlM2vuQ z(tRT_OLtlfa%UegR{Y-aJYfbaw^Cy?4!(!Rz1Bldyp}d5RysIFjDuo(8djG}MlmOC zCOMa))?LQSLjnNL6zMfOC+NP_#?{oWf%CPm1k>aAwYwK4sD^iGra=)qt?b^Q^H zHF7yIcB8LY^OakU3EdM12NuC<35m}y0xc$edxE*nkHlddtNB2t>MiLH1Q{F?W_OCE z>n$Ywbkz->KCkK?yf859HC*S06K^#!#Egw?FqI&HTi-;J#4!F2Z_6hydX5^s{;}G{ zyyNrrl*6SM6!5c2GmT(Q(gDF@se0t;p?`$`#vqM@6$eYpoP44g550`Y^^--4=|EEZ z%(IdAA6#Ljl}WBkhLeEhYMuza8XL^BrAT_m;XMEIRqz{nxwDpn71%b7rP#t;cxwackC0QVhFH7Py*);H#OYodc* zqrelbN1*!eJvn*^z;s}bf#J%Y=QO(a3HP0$#M`amzl)CHYIJcJP%oc%F z?E%6KgJ0^4_XWN)88bC;baGE9t+&Tr(bB<6Rn@XZdyBJ5)bGZM%@CXf7mg`PcOLt} zJWM0(rw-;z+YL{-4$sdfbdW(THiK`#Mu2@;&ZFuly9aewwwcg^OEJESlEI7q(faBXQ`DmP;p#`Ykh9z(w}l62jp%Gp-nGiixrXPc`!z@S%6u&z^!5 zP5fm3PC;-;glA1Y|7*#V@y$}tt0CT!0&@G7PikI#!HX`%5#1T_2Am#{~yY4uItY*6xev2yr0SZKi3goSsvpPg|Nf{@XtPDIIlWK;=^LGLB@7`+O)6n+X zE+V3an>`YW*j*D;%@jV~82&n4Qpf^F`?D-BMsplB6Uod#BaFGtGxOPD$iKbPr}C~k zH^Kh8E#r;+m-hRgM~p;!lLX!yh&{Unj3)@6@PH-(BTRz#^rydSKr^#N*eDasc3*7O zqb4$ZWzX{!7)vc)B~K5_Z}@#hEPWgBZT_|c8dGcOn1&*&`UP#48{{ITX2rGP zH;C0=&)m8=rxn%S*J>%r_qgq~fp^;1Vs&nctKvC8FI+deqm3-s;cIVL9G8G}N5w4y zLI?J=WLdfDkWvOAZl^Pzz2ew(^QH1gdL&D%%IO!b9WbF*<8kh(pL>neDC@3~eV3Lv8qF3QmgnxX-*a_atXe>2YaWg%2kL%nzCX zCuNRUefG%Gl{SUx=xQchaCm~coAEeE&uC$e#qsAosr%v3_9T&3v$U4}Pb%Ryj+Od3 zRdfAFo3FlCML{AV?!eyr*(Rl5ri;r)k-j)pGgJ4i{!GmW2Ik+gN0QoZ*~k@dhrc)n zIP}uA`1=2kBX=wr&t*wo*K`maX^KpW=DFe%wOnCjGWxLIskr)k<(!;SM%A~b&4pCD zA3|z2R|@gxK!g%=3|@3OZq<0H+Kf9o!d99W@j6Vp)5m0)HHY~~AD4X6uE``B6JBrx zHk6xKZnWA9BsIS+%HWc?t9hruy&4cM`B6gaL2B`+fr+_aYQDv`zIK@NyqZg?(WVD` zH6B7s5DLv}Ag;?JtBL=nB7Q!3Yi#ppKSIE+7Z8-Fm|9#~st9-2&CWd*ZKSJthcfN zIyIk0f3sO0vWqrZ*0Y*ZoF3?sV>y5J$u!*Q`PQAD%$pv-aGdv`kwMMbJ)2aO?)ta( z8||AOH?{qW%^aGJ8feW?rMb!+Z%yMP78wbhHp=?3D=0TD%ilNPHgQT|P(DihQ_+`gwqiPoC5mXYyC-)krAmJ2i*YK>| z>b6(@an?iD>Db9QA)4um!&lkZ9D?bAZiX{$4i#93b8f%R>iE^kD$j$!%as_&bo{J# zv$o#cH=WlnzE@Kck*5QaXiN7=GwE`r7rhVEdM)Xj^J7z6tlB0`LbywIK`zXbsS|p5 z&+)OK9PI-R1cn=P9`-Ze&KS`(Kd$*bbGt|FveR z_aD@xxWX$E>Rn&CTg z&p^y?vvfOG0OcCY5u=w$7rVD;c>?=37*k?r6E8iH4IPs%k{CX zYRIs5m|`jy+}eb=aclbXD$D09*6jY{yV`}-#&a-?qqsW#U|mG>VFO09ZU?!HtGQIc z5zjhvVnjsLSyRy`xI+L|ww0*`3lT*-r`ykWy9>6sUKzB9yN^h3qMIu*BT|$i&Q zze6mdVFqx38H&W~kRvd|Ysq+Z^XKVT;k<+3=JK#t0#1N)Jj2OxKy*cW+B^)iep{vD zt53$pI9VY~VyCCAhc#W zD0Mv+e|2ALIyNiC+smg|-5%>9$m|d>OmfdyT1fIv{pneJQ}z(gX<|PJzvAXSZHEtw zU0Ln6bGy-bMrEHLU5_QKM?-o?6E)CbLDuGL^%3K{LLMlhd~IKqM~4FZ7IG-!s6CHe z@dsK-7^`qQ^z5cg9?Ni+u$-W;W2Y&Kw!?SueH?3a6FmgTXw+#lnX@i8@faPiA zm+G*f)yEs0hqfWpa^_7Rrq6DFi*252G#Ps>=Ag{(BH^`5eR*K4#7ae@&;+1Z6aEvM9s4OyZzIoN>z2F@0Hggq6x6I8GGDqr0C=BMgb zcG~91J|{E?V_mJUp#Y>LCP5@098r%t7DT*5(?5SRrh=nl<<^rMt&uq+pJTIbjhucG zY@k(0dUFGVk7Q1>p^O^)59=P~NiHVbqUi)rTh!AdBq!i@V)%Vj4)%*aO3#e-W1M=H zfg!togu0J8*X8>psb?*~U`N;kwJW z>ATe81L@-i>x}u2lJ&wtDHBk6Wb`$9`Kx4R-!&$gj^HxQ_og0bIfeNc4K;^izUbS} zL9a469WU#B6tzDwO!635|9FJ5(o7N8fhn5Sx9vHC?;0tObhFNB8tb-cz5V=XP(C65 zrjXK!LW5#ARd(0faq2&09EG$xKT>2ojYrIzdlTddwc}u94>X>WIqq;8egj6M*qV#` zD1t98sHEp}zRxWJuavTAq%TgetOR)>E!Ab=hvQJ2C4n7OeB8a&=Bm@L%TW>dDg|lf4z^6~j`E zgXly$1>{@*n zYQ4wVmE$2*iEj1z;VNI8SdhLWro?a1jlSv7%ISYO#~0j>qDTDA^^*!|8NNVlftJrN zI&2%jx54jEuvW^?sIABlw14X$wr+bFd+NHskCp40lZWiM(X#`PJ~NE2I?@27p0pcU zI#bk=+*-ga^1)W+SN0Wc7mmGOBrGQbhg@tj)W7)$t}~V&7vH!soZrlQsQUlNQuNBN zOYg#nOFFVg;P+wz-H9;N$FRA4>DrI?UOJtiZj>?ARrJbOuRIJsv4TTl}`x zaU=2=Klj~3WLY*GQ~@S2p-YD!_#f>#&;92L+;{X5nLCzAew7pdl1UyE#d*Y@9iy!S z25-fGIdv3+|0R#E*d`qUGEJ8Jk|||zBW=4V4=v8oaroJrS~SDt)A)GkNDcqj|EGsE zjoPLSXC|iZu7sii^IHZ=uMRnjrrnbUP>#2!|MM>YB8;|3&VNQ}&3j&MObARF-f1Lzkn^SnOM?NSndZC@VKn^3AK}AY3t&I8d|gT$F>xY zCJbc?Udseo;G+A%4%vN4w>|ir9r80{@U*(dVPZ1x(w5eT?F{HVJS2#J$7^$GO&Ou= zOR0Z$+j{jg=2J*-5e2r%Ru|;AzPMslH{@r&jLzT6f1Z4pVPXgF>+_@NBNEB~Fy3dr z!37)#X*y0q=E)I1y6d8AL!5ARo>*GDL=9+sWSoKFFv%}?XrKF{p|0rgVZy&HwELr9 zi`ssl#=R#4=F>0Fr;?IwkyVbEUS+}PdsC_kb)J`B`nDK-NJgD)K71{Lh&3&@?z!=B z@$d`%OJb$i(@sB2C*(LSimIM`cyF`cW=I{x`K_VE%m!@%Bx2$m*P*k~N0t)@ z3bSVTVefyDeDZYiW_txrRF$JDonM-ujJD|8AAH*2KCeYs3?!|HKbC48@~7@3q9v9t z9$^dl51iL>tS#=pAp8H>-UQB|Yy1Ya?r5{0ZB)UkHm0#zzcyKFGib5_m#Zt^I4?{Y z!}-jc>Gmid>gxOFf9^>5H~eUaUCPfaIa$+tWtPu-V_n;XUbqE!HSCNnW08~@TyQu~ zI%=IHh46k>as5!27)`ZoXwr=J5ji6K{rBhRKKb?TN`$vT!I?IcaI%c+EVjx^Jz z5cX>C0|Rj9?1WzkIsir{;*o1!hUPw1$o&m|Ec45@<3Yw( z9^z}QaZ1{LrLu~69Ni8^HF|Ykp$k~;w4P>zvRJEM7D8yl-6qM6E4Wk1*M`TN)>T=& z7V#GruZC$w3emAXKRI1>>e96*%}qj1F)GjMxEV_qzC=YC$;A85-=uouYW)bU-PpYp zv@Oohwl{gV7mHw+A9a@m#RW4kN#mZ>Y+n;L&@0d{k)XxF}!u_ zyyNl+sJvs>e?8!LoTweEgve4CyrHa$a{2D3E{W}>F%yQ6Q1~(H#F+UASB+;^kkt!~ z&S$T-3oC~)M_TP!`{yk#%ON%sCav?dsj}wS3h%U)F%-~ibxaWHz4mdptkP^EChXXW z)3^Wm*SCLgCr(<93ThDl^NWMuekpj=;7`o9kA6Q;WPc?;P}59ofp55*-OFjRK`BlM z$f+JD6n{;D!p*Cdy!8cU7Ib>yM+OY+a966v{%)1S$d~R!F_In@;l8eNvH7)etxH*V zO3kB@BD49y^^7=a^|2JmtNCQo=KhJ_o_BC0ObD1#WP0An-#8=N{$z9;B zHclEL)kVfR%3-+!B_LM&p+72OZypSHgT3r7uB-*_->-O$j`nG_mF_+L+4GJ~Z*s@( zgq^_ZcvU1%IBTM!VZ)H$RP!0)CR*ZwE&fEJym#vUCK_UKffwF#VlJS<$$qpN6xwo^ zYG{fPVx#(9`34vNV!ioZ;njAM=f?b!M7i&_$+T_wrT@p?TgFw@?d`&}C=wP(qm;C? zG=g+WHz?iGxd=fNL>d;2NJ%$HgF!7qy1TnsH1AmZ+55hi`#I-1@A-B<>@WP5V-vxRwUPI;f%VfcEc4;7FZHbwMTQmw!X-O-XTos5P8v~kX)TLDTG_9)pPkBH z|6ZJ~+ldeWZL#~y9Sf>MpOU6%l0qFG-$cbA5Jmat4>6_LywOpz$jh9zbaTaze}6%+ zt828r=-_C3X?-C)4Mk^C&!ue(BZn^$6z{M9IOs{?Z`XI5wi9k2xDlk0EeB;K#-|wR zTb4 zF@>9eZj}Yy<=M17M(2=I^~8=X$;H!G^KScV=7xNP&2w(X*KVJMxi((w{rE)6%k{Q+ z{e&!V+4{0BGy!k9E5RJ2wktNxS9@t=&S%N@D7Hl}cup116ui7B>8Yr3kKr zu!|FjlVlYaCszF2`Hp$VIi=5hRgChLp!IBv_~n^2d|UrTKuLi#l+7?M&BuU&5^|Bt ztaTFbKqC8zxH(Bed7o(q1!+kGmbR>PwvgEIQvMofuv-9!sl>Asp zaTAGn^<$!=( z!Qnz{z!BS`^}?fc*7s1Iep)QY#U|Yusk8rCXIwdeY1sT-8rPZJlMj=Eul&{JCwkS{TSI822tw`W}gVK4ImSh(dc*}U( z&$c!J4xhdaS@fX>+~&@B_Kf=Db(rHs;nbn>-M`!)@OCf0iuQ{bhWu9SXF;-10_-Bc zo@XTh(AZ!70kOqxl%1B-zqNhp-`h#qR1|@FnHFU;Rz}-p(QsRwM%mCbZfnAto#{H0z6)(LKh=3ii+2ixDxJsdRqFnE-UE(xm{Ru` zCs#Y0mJIqwE-jPOsMG!i-Ie?0Q5gC9gGp*sgTlS~{BB&^EeBmUnlx@0(r(o}l#5l5 zEp8%Xu&+N3Sw&wFRoT#B>2lp@cvLw%B-*LuT`)~Lr!=LVmIG9xF>yZ=P&0*fNknOI;!Qd(LKkp zp07mfdv)>LI!{>McQO6O())MiwqEU|7AqkAptQJSKwb>PiGA{N(s;V4)rLlns5h3d zJIS-lyiGBMVQb=LS`z29`@JV-Qlu5~g}Ug;z?_v0Jm>hFYAEW~_jB}c2|eIjye6%O zGAdOzmcAjg4@oh_mJSM@=NS52m3r?^WB2vOMaw{MC39F7toHji(&0Y*y>Xt;CcL-d zZnzFFS?fKV22 zw;f$OP0>-ba_*h2!pLUyfeIC^VA}*;Dx4G<=X~*v#HZUJCW}W z%w?B4W5xWtY<^_PK}~+NehRB-o9bfqoR;*tXcRn?7LgjCRiOPu!V_`o)C3)`v~rIY zniX8@&x)p}^i|7NpkUL~%zOC8L-E097%v{=RD#puUDiOd3S3XAM5H%MHa13^GdmuJ z>S>pI>E*(pm2xv5IHXMNgr2yg%LN?Qj_} zxX01$Y_*np>w(JEuPXuj20D=1+$6@)6b~U}d3=vPlu*dfHT&+bj~#~$_ZBmGUUQQx-e z7@Q#md6a6ldSEb9n;b4YshR4&d1vF+?SZH`W_>!Z!fr`$D#+I0yARZ!`TfTluOQv~S7 z2xaYGhc<_X3%lz&o8btk6ee<;Y;Uf8kvk6b>VFRz$PR^mkq8O&()`|d*>WPd(VsUH zdM59TRiG?i+{p35+4AS2OaO(*<>F-oMC;Nw{5YCqD4*8eRjr}P`!obZ%D!P?UYj=o ztIQS}nXa}I(je0*e9~!4oYR{enLA+iZHj1%G{9j;^}vDx_B2f9ELXRoHDRw}Sf}*$ zyUbY99i3{cM-AI`0>4~&{+VI_*K=RQ0nhz(C7uFlH_Uzzjc&zV$|6d2$^`H3=(|xN zw1fNUJW1{JmwEKspnP?uu z8~^K$PjBipm??CA;<1$xM%O6P<6GLgQ#__d`U~mC{6xPZZg<`M!!hzU$3jDR%Sg&5 zqAI!ScenOh6_S?be|@atrKh87O@0JYu#hiunSpZaAnRKVkjz(s5q6s#@*0MCzR>B< zB5gX^r0&a_k#4#3=ImgyR9s+fuvGsI|Au(sSxw#L@b*oqRgMw}#cJ%Xu$rO8?7Gus z8s5+{Uw=`<`EkcQEzG7c-)lq>6&+c3}Y+y@l;|MjLGz^-k z-icAoNxm5&FN{8X*|2>N_xod{d+-x=Mi_9)N{RvX-v(@y;MYTTxb?M{*6VmPE|$0u zGeyMLfXDs{7P+liY@jZDwM*U+^{bIKv0z)e)_HC5(oITh-zZ-(MQ4Ak9shgYb))CH zy!4Nt#?_5ii`CN29lcH&lDH-(C65RzC$C;(HE}725HVR{i8LM@lR6HmYmOP_VsfwA z_>UNB&b*EKUj3oZxOHOuq%*cS4gmR>YEn>Ea@&UNs*6f>pUWe--}u9h8v-6XAD4m0 ziXRk_&@9r6ti`O5gM7_9tzZdnCVbkQ_C^8#O-^W~rLo+kdC9{_Xc3 z3U8V+XqUZMbYYej(y6m+pysyTeVG;VfGii*DSL~E)db@KF5cJnx0pEXL}`+3o616V zP3qhA3srvg&qjSwbA)@TrytVsIW6xtrQ=Z&laewOHywx66nyQ`xX&ts{)@|q*|;@y zuHOVU!RI!O#&@5{+@mHKXA>MaIA1N?v^`9_t6d?eZskUhm{a@1G))!kPI81jo=2CM zb&pqYYDb7S^@iMMu!$SYUlEF;mOAk!()|qy`8!DgS+@y@&$Q@sEXeWMolUvW>Vf6R zG2IS6m3bl>U~l9&!ygd$=P21rus>(WFn(X9QtQmnv_4FG+IX^Q-1i>Z7iHk-VD$Y* zi^ZDL*TaF_Uhs1S>dM7qwzWQWKw*e?Gq|4LNi&)bSNsc0i$x%7w1C6>eck-+$=ra| z(DzrFAd*QZaAaNb4dxp=yXMG5Z*Dbd;@;eoSDZTk;=uglS!>-#-y;(9)`9WFkO!R~ zv0kt32hbotrw?|h*H`wE&Pjb<$ipXTqSxkW6)p*B7J&xk!wJMC-^Vn})A!thp(|-# zlAyjI?wNwuY>kzf#Xd(rKldiPfx+6m!uRHfGIP4|c~$x-bK5wV=jJor4Mt_ECN3-Y z3_};^N{?fv6x7Y#NNS%-mPEYyY3T9Tu^X5sovm(nsb@FI4u?)e{ulO)&8=#eYLj3a zoJ8?E$oK-m3y>7N{h&02G#FSe`e}U#U=EUVlkXyI&d+d3m-d%nkH)H$tp*FH>ST{M z#~@ZJS+Wn78ohS**W#r+3d)1@b(;(_o#}1PT2}aM6l0ZeZHLNgOgf^*a*Q7{98}o~ zEJ9=RZMIb4c8zS}@sGr@)<)A)3b$ULKHqV#fqSehZfFHLgu)l%w=b5(Qd1@53O z_Y;>mCrgMSMJ`kf4$;5O9r5Xe`ge`0moHxVQv5)U5-Fz&SuCe{k96PTcM!a;(yW(0 zgn5wP?SNl*$dBc94H8Om$#WX?`SBpf#MXM~!JtM|xgV z1Ps7@Bsvo^DUn8>J^2jFcE;|%E6*=5*xOV9GQzkH{EyJiWfol;;VbW`>6NYoaoPK9 zthBQCq1Nj|>`=@-m#Rz}O#TbAty&ihBac_p_1eb8=q2Now$zAHk|;|jnpRkN+G4`_ z^zeYq^hb~a1rB8EQ+1|nZ2l4eTVez}C)e5SsRSzVaCMS+eP2=idQ%XN=A8<`z=_`E zpOE3YVbmSZuFLh1Mkc{;i)#La@+1qba;9(s;+1JDhfBA6bs1CkY<^=Csm9r7CbfQXqhZ4x2zk311-yaa(~wc~4!mC@Zp45p8}9 z%$NCX=0l{>FEXEfAm`8n=}ixSnykTh7aU=>Zm`x*spj`&y^}X8Szn%vtr&Jx@^VF* zpNX&$u^BjT-RhXJ9=_|h)E(THm7S+v7-cpL(lME(PR*Qz-jLf#Job@EcH1UcH_lXn zW#D9)Zn!!=VeLu#F(8;Nvk;_GW)jSG5ZS_yD%?A)9i$?oU1ky&glmVLMaMTChlq$e zIjrQ_unNy;XV=dFlk|!nDi)N37Dj-*f9hgWLGiPpw~lkC8~A`O-?+w5V`nVpKd=F+ z*-R8vEk!F;BBU5`=P?Korfr@GYjmPZ-=R~&{ievWvGFn^t1SiI0ni(C*4`qt;di6A zwZMksHgg<*f(FTDO0a^@-hVrhgpY3w6}n z_z#9gW;z3dqge{=gZBuUAUV1whhKZMq$9t_#o0={s>PVv7V^q+C!pNkaQw7DCm$6Y z)KI4_!eQ3+Bug$f24HO+14=>^6;*L;hCS3C!_3;5s%@?m2U?X*?Y}i8pp+GlSq!f- z>Mwh4?4KCp>%P>B;;41mU}nQ5jx!;(-@J!?yuv3eY|xm-F4xGloz--CEEN)F)|;x& zsP=@u+7-O1VcJ1A0wi{0|}wHiC9Jk5#i}D<3lgwty8w_IPOP;ci#E&Ia-r>^(@J|JwOZ|S0VG!=O7#F(oQnS|PNZ#!{f($S zjypew`Q_xL;>YTB6@N+}uKszIYZgWDX2ps0eC9+EIj~19<;eZ-*S@-J{fxn_|Lx85 zpxflE)FPMPjSskI;+m=wML5m6;@Uz*({E6&(vrk4ndB+=IH71a5Oa)7I&IPwC5uKk1tWpa?A;yT*Et?krN5W0ELNw3ahL`^gIJ?M9Hf7g*)eLLL_Q zYoBpiAL|kCwdSjgy04OUIfEk`YjVsft5J=MTvG&y*fv6MY#@F|(BnFV7X)_mI1-o2 zbOKZ@4v$VQkGWISD1e?3eavct#WGP>S`S6cTx+FR)uafx5u`k5E8?>3zu&hb7`21e zQVW;p2SzO4xzPi4=(@VRp+Y#jOLz$n?fC+~>>ArY=w-SzxFw{tJ*kqXc5`fNPGD-T z=p{MwYSP>@y7}FNs$K#)?DwM8e7Zi}QA-puPvyH8qkpuvuNz=M_F8ifHP&l+bIdOc zzG8IAbP=X+r@(o_=rLyAn-Z-V)WFK3QyC5N0{R)-jdQ@dUm*@f^SzT&~vhC{Co{Hr}V}9Fg{$lr#F$uS=DXVI{cbe*sOYqNO zTYit*0EQ&Q{b~zwl1dP;f4;9@)ySshM_i(q{EUG!^fQ`(YIRYH5Mgn-$ykDx&u`x; zqsjBfwul7s@`#9aAznnYn7t`2b?v%-bu*hJbjhCI@$f5g==!17#LTfH8Pa<6J!!tJ z)#pCEhP>rau}ykkUE7EiEf;c{`IiZ-?FW$yR77Vhkc{c8OGArc5TVhXmuYV)qTHm> z>4O^1g6ahq-!0bJcu^*`Zfdw#jg%E`|HRz5&9c-PCsW@MHFCPYT%MV{_>PMMm>9sQ z7n3|FAEEIFsHHWApFAMQMdfD0^^s@|yXthnO{vW84G3?ahMapB>4rmmej8c#e@i!p z-61r>Vm=UD+w`5P9VfyVN{8rDYn_=^mn#qDX^VG`zu9ro_X8TVI=+F74WTA+U8=kP zRqQ#A4?co513RQr}L+U6x+ZQ}LkN%eUiP)eB(W3Ms-risl(cUi(XvA*#H~$2XT8x{+0FeNe8A5V;D$IdvgkH` zk0Ks(zWJ-E%FZcoEJ}d)d1{EztNHuYau_-;j}v*VUu)4f#1_04{@z zf0-#PvoTue6_b%VN*vlngEiC`&uN9Fq@XB8AjR6v3Qh^*zek7p>({T2`coWR>q4vv zSBn`QnN$5bXW9Mfb>57&QGtBu(AKBp{m1{PNe|S?Ll)vYTH|$g-v3#QDuQVB<)D7R z8Yw}%Abd7wgrc#f!+mL&Q+;FF)tcAx_biruxs*v`Y`#xq?gliiQFKr^>=ie<3=y!t#Ld^fG*?qiUZg2J@dYe?SCgE)$*YZpYia&EcT*I%iMN^Cp=Yh6j>M!nwt9%*kc@Xf^9x*+lXU=gyI*b)IUsm1&4pIm0h>m zj)*BBSO}ilfV?9JLO@COZeBZG0ckX_q!|$LWdO04()lVUAhfS+sJou0ol)C$aV0P} z7spcHy3aI>sd_T;9fyeZ%*#TJOtV6I?lYW|hmLO5_A_cO4jG>#*HH@L4xjhQ2UA-9 zF*_IuIVz^*o8xbL(KcJb)`SHB8`t%D3eqq-NE`KSc8Hm`f$udBBcTxsJdE98b79e*56L*uX)9`=f*1AjRAUHtw0 z-KHh(KfFddC|zE<_pfmvjr!kWHr!MFEOFvoFKhwv*riI~w9XqW)SbvN=5_+F5L@h% zI_t1!g2Lq31nJiXa^N+$@vuewjWJv@J^Ek2t;hH*EXbl>W|Fvd$Rqx7xx2nw7SKH< zGR@m4*^;=Vql1o-`gQR3vLz-PrnI+rb|w5Aan2?eDE7a_7N9Eu&2qvb)PH`tE8U{&+oBZ<=4K_WNax^IDX4 zB9`-N0Y3s!m~WLQ25ClAkv_l7a6wDTT3cID)D#-3*<)!^cHPj@+jDb27&s_I<|ad5 za9gQy3b*-YSpQvI8z$kgaZjkk_IUtlyb};OK@)pPb;#V2|ws#M}I80%K613Qv zw9U`xSRO?J8?>afgoGKlX`PLGs>>p=`Be27fD$b}kg!JHFU0LD>=Y2>p8SW@qrtQdy-`!8MD|vw(>gm#5lV)~k2#v(3J| zfmb_a*Vam(b|TVuF5`W1>K9V1SskWU&m77GM|0d}-|=aE z2Ly`gbPt`weg%N`e!aJlltOUJr z+qIu(l_TIxT#Tx;*}my|ZdBfudfIcOEFt+W{L#Z={7sQrtxDR^gmsGb5vggiy`mWx zv#$7;)85AMGi^>(XxLgzpJ4<+x+ud4Jg5JY=2-6@%=)^oo(b z+Pgo}6`B&43D*RETS<(g4@ED&Gy~*pPODn7o=>mLlr!!SXD^1$P^o&NG(sF; zOgGoNpV2M7JhnSMg~lFhh_3yyISCOS_Lt?CNTkug?B)&oMiwZm{;lrTK1Ph&{)WEi zQ|#~gE3G@f77ND$?C1&Nj%MeT&QOA_+vPTu@Umne_V8zdb{w{@=Kuu9A@`3-Q;A zRRE;A4x4{~WaijZv&q2vd$d*WA&uUlFM*un5ewM8{(1BfPa>?j)`ipr*jbp%YAb0` zbg!h(6JidNHw8TvjGamJ;eywb<@+D+xDUZwrlVM_hb!>z&_k)5x#<9w8H5Xw$@b<| zv?6V>(JnV}*pl6=b6!)~Ka39V=%-wk&DZEw-E?~`4uWRN*C2iZY+e7g0rO6Pjr{m> zd5>N(@*@#jkc6(KiSx~;VR!b-$?oZE-fTdwz5JE$V5eMDlQBN7K>s^tpkjF6eR%kt zAQ!|h1Uj0t&>rfiipGxvYhFop$m6%{tIzF9WtTt{xpGhzw}R=P4=ho%eqixRzdyFk zvYkkxX>H58gHI+H|LvKSp`qIrH^;_#2Lj3&1Gvk@(TK*F?L|*46ZAf_mgp@aMgUSk z9V-;J8S{0r%Jf`@)XfB@w#-S<`dHr_Px2)Qn$}K2G{Ylu9&++{n{C|kkSDRIL0Nb> z)5ac`hH(qYEr1YLPe6qN!=WidwmZ>vH1nR+*t+d47AJFs*o)JHoYY7mYz!Hr$-te( zG4s?n&Kh}ELwW6i4jCpOO^~D-&M))1l$`KN13|5(CEh>Z_&h1ATcMEFZClT0!~-CU ze2}w>)dIB^S&~V$vKI~q^jfyNYQ^I*462CgVUB_HoZy2MDlXa0Ba{MQ1F?k@RZ>SD zUk=h)fGt+^;unhl9go2$J+S3#Xih#M8>0`-<^()^a-ck1KJ}R-hk4JY8z0#RKXNn& z2L}$o*woZ0SPO)}wagYIaWh~3AT{miUs|@&fY1wu`ZNlvorVK5l&m*T(2lX| zua*V+Yt?aN(u5m?9=JbIH+Sq8WxnjoESI|kSo8iyag*HS+z$k{y08*|dj56q{A6z- zirtv5BbM|1;!_{Tr7i|O$A?W$OEL8x`_d1L*936CG3wXG)L^8>SxO~xxbUNDSvG6r z>!@cdCPhlJx>6HNp=Eqpx&#))KyybiRghuESA>aXLNbFuni;HrDb^p^2a5l1M3%(W zi&KbH&rmUA(kUiBj%Cz{@qz5%Cmpi^pt+HPTM{r*)KcT#SKl$E*3+Xh(UO4bl&$GX z!*j~bt)Kk4J9Gd$uixnD<;|svH+`@99OeUwFCKJm-8P%ZBb8H#A`opZZ+45$>HO?} zNozgKoXUyysu)XoU8R!-LrfpU@~>%+#@{1v1ndu)=%~o{Nl>CPyQCQeBW<-22Ow`n znU-$n*#)e4sfq&L34)O=!;F49f88C1XK2L@!rzk}7@t+XH!K87HYhRYN}%BnavKMA z<}jbxmCQZilufl|dfGzOqFQK(7HWL)dVvc0;fgl1gNlPJBl|JZ=oj%> z5ViMPKe#O}yXu?340XlJ2KXm_4fNv0tY9^oU>PoTq)bG*=6w?WK!+(Mx@AamA^V0A ze?TWk{#;mIcU!*PX{CquR|ug)Hiy-7pi>ZI|84mr=o3O2`l>*7AVUExNJWan8Iqh9 z1#ON_6h-4(3i~+HsAxPk0Ma#dDy*2&LND~VcAwmj>T%|FoqSPq)tb$*p2U7rp)*#% z{JXV)VKTo9qY3loruh?SA-W?PC5Qd;uimjB7g!TxtS z@hqp236qg+q32r%AUw;K$Js8G(x*mVp5xP4%B2c=MAcVsbeAsBZ!#u2LI_r`Cvo;+ zo$A75cN(<2KmXdhmqFD0F;&x;Sd1;E{jHz$6HLKhZEdj34plxD?7%D#r%D8ExsZ~7 zRE#fxQ7d@ztiu^e9_ep{v)V)m;vI16M1{pFg5ZN(BhIz*l|)1K!I5&DW;+=bLqVV%1pQkhi~ z{aXl0N`>GpaVizeR;6TSB33=@;O*{2A36VC7@ME9YO?Pmar=Pm>j7}k zQYdF7lHVpcLd#J~i-QZFXszo{D0@!LD8;er+ra{~QcBH-@@7nSVLuknu(~r%H;&7G zer2kRp_MbM$u>wCbk(iBR5I33##OF;}@!FR@`5C6}1foV10_O(u)6a%nmhvN+MV&_(Qe$aZ!q zgTQ+{4v0DzlmwZEtcRhlSAXVj?NTmfF<&mz_K4@QP)3Faj4{yf?{3M;+Z6n4ZI!l>57n!2lsGKD z#3v8o^u?p#r*XLU+EUQZ<<6GN!OPt$^FB}Wz3XSnXXS^v4Pu=0>Y#pW*s1InQ@V_W zHl=nq)$CJd`1CoY%tteYOd!4!rra1r6}dY6c<8$=QO=LsHEGX$uHF6(I+~J43L{`W zdlfu2q%4f~if#ADeAXBE)MJTTHwxK|TNQ5WOsR$X4FQK%K$s=?hp{&M0*7`{y&#P| zDoq!`ZY=5X&5#l(Us42lcWDusU>RBJ%=&FDU)w3OEB7AD?rGTvOww?>ltKpPPb!Ll zFVd}MA5bBYe?A5|X}_~Jx<&|+yokeZTXCN&T7qR=j`>m`%R_VaA;)7QnQd^dMF;H) zqqv_Yu;q=ps+AdFag&gpOUJ8=)4xvvpO5 zm-Sjy5@Vj52k2?B&TGl1=6#?N?$)li*&=v%&hln?Q`=*{;8uF9tzmKwV={?8iggh-sMh%@fhLPGgDrk{lRz5J(=x_4tf)l-i@4W5R`}_L`og_ z2aE4k&gFoJ18{$xXjq57vWeWbHl~pjFcOhHf$;M&ViGi`;Vwh#h#xF(xlW*GH#66%O|vivygNRmAQBU%u3|)+4z}wg^;a)b0qo=yzD35u(&1_ zn{wlT9zTRUVF$x?XN(E(nY*mzziONbMem|TB2tK`URDTl3g@p(gwWmf5^N!j0_AR* zS@&c7<24qhIMxW6dy;Z(c(xKy#@l8jTqR9{`@aLixF2orzS6F+2sS=?`O*mO20lep z-AOmd$Nlf)7uDKfB{9wuXn~ zy08O3i3XKeGT{DEAe0b(ThGg11f>h7FBg^N_rQ+*?$y#V<2J>%Gj$;1A-?E8aGa_f zd8U!0!DZU%T;sm?CBsEUdls#!K#z7yz!kdOo!DNkB=SgI+&iHLVcekJHkk~R*+1Vo z?p1UFrL>dG50NV^mLCQlG9bw%dsYjjUH!3Se&cqOcKM2@;CyjYmB*z0l43&>z*VAS zB~%E}?l65WKO_U>Afo?6i?WRr6})dq-?2cLQH*L7*!o)t)>leogZQEV0-=S#!XeS$ zqy7X0?Ers3VE^o#8f?bI_h_coF732RkS{JuECiGN6anD}S?P7e~JpD8N*fq&b*=n1yP zQ?-4m<19@i{#qmvM-%qG?EoTZ?=x;&1S%i{7iRrhXO(PchFrDWFN00l zMHSW)lD8f(t`3#Dnw;6~tBqAymXk}UX8%C*6k6{+2ndosKjxJ?cjc7>L?#WLE{~LM znfZ=^aZvt{S@Znij(Mj)`VI2J8GTby9viT$8w?pWi%ghz#x%z#2UR#sI~Q5=4JUe% zg_veYVB|h0dpTvk*QjZoqh+S~)aBFa#TvhITqBA7Er@=bVyE0{K0~Bokn=DL`kd>R ztiT96*-&c#h+Ssf$PVMwaAJ-tv)!IusEDK-)}fJj1qfKkJf+Gi_CXQ zI8k6xSXk*1yi)Z**>~znV3plzbZfCUO{p`E!M5wC*+mQs%ez~IpqIlrn;BQW(+mUG ztRq=lF)sZ@N(w9Z{DPdQUgpkC<<7D^FScyY)fo`=ymE8nu?a#m3uafTp2rQYK`wza) z>F|v@$<_7xjA2_f+_MS0WKOkR^t=!==e!$Whs%;_CK#p@WB(o zw_M#t`QtGZ+&^(Wd`2u}R{syTg#4HPl(GeufMoHJx1$9K^0o|wHiMA+!o2AZ`Rm`N z`;SkehX|%rXeBWfi7EB-`-BD>^I`?I*J(jnS6xaNf&}pYF-O3%{4Yh}DE=k)9*WEf zthxA#)Bs8SXk`l4pE>y-wU62#1 zKiBb#CYAW1Yf38eh5TaCNO^7JK=hIWJY^v>j|q(b&Fp7l_e%GUvItvhT%?Hl?{|iJ zj@fVC8FMOeo}Z3|eAy4q{vi5vYGz2n63WdptzsDwGPw_1`=JTt|Bw)6E9gG13U@{l z-ToaJ{`sl?-Zw@bp0)=&Wl=bPe7UPg1@J>4snTcTT!{A6^Rzy@Px;JeooBdN2tT~z!7?!PvM&!#_u`KeSe1{ zx9|q}_WoQ|f)6xccRpUT=_f+^B{4NBfFkm{*w9baxx@h-Aa0*VwoM$C0pzg5eE;aH z{nBL3NeuDm+ke||$p3uF1_H{UAsPyj)y@`(c0|!;dX4n-S6b6EXn*-u9_#gT0xYxX z@n#Ljt9cVdQKeV&J#WjPBogzFl|!Dl|5pTyY^5Ybjs#3oouG{j7t>FLYL_Bp{(#lJ zsS^OC7mt`UFX=eVdcd2pyW)WA<3=HiS}5{Nk+&tVy*XMiarYE99b|~$=dBntbttsue?{nmHvmt{pnVy2t2_=-c9qROCU{z zYBs8@Sd^a% ziNHmEu>b1|Ufcu7tO#$||6m;d?LP*hfa6S&w)Fq$g3U^Zk}h!J>Ho$50pk0j|90X3 z;X?np#{aN5|K{%gvsV9EtN*iSAOI-ke{AsIOy__5&;P8||9_kM&szPzwN_N<7)Uuq z;1|(`+RCh=y#oXk0VL4xUf*M}1sd^$#l1EP9+;r>YJ~UIuP03~-_2`^C_wScd3)E_ zA040jK!5mSQxP)<1YjJmux@hXosQ?{al6)F}%jQJYmcZiGjVs*N{hacF>@etP z{6jvMvkK=`_m3hHfs^c>xN%In2rz~Q^VTA?e5D5gtt>TP1(X5l-2wkzRbNN(%3xTX z^IjAgpXcC4*}+mb(;8;cjRd4rd^!_`(iWXL> z_lZi}me_&k5E>u{-f(YG3V?nwxCZ9;KGO|0!HYCjP~V>+vS$c(t`LmZ3f% zJn%@$Z-IBhn!rE2#k|6~H9XrREpG~oiNhChheZ|Hj+LoXY{GrxVwQ>s8n35vOAuTO z=x}O8|FR%h=h<=UDzi)aS0+joh0!meV!n#=S8DFZi8eBrPtBdDd63J2n_0V)D$u}h zb`aOJ*p}uKd9cFP4%E7LAxa^_{<`*CjFi5IVF-|MXqHL6?{ly){$UJIpHKk>SNVO} z;t9Zzz*5{M>FgV?LKfRor~3CZvYwJAuaE~v{MP_qj4wA_yZs__ z^`83$u>T}`yOiI1fNkKg9@gs{({yglFBmJ61=aCl?b*l&ES*)y*xLG~I{T(a2uufH zjKE8!F9w}u|3p@dy)?=o^> zgxqJ{R2L^?*0bq)Q~km@yFgmu{U=j}wohp>R&anfQ?uxBal$fh%JiR1eo%@}m#P1--2P25Cy9KydQXWB98) z`=AAQVt3Am5>UCiy`T+~(~z|O&3vrl)!Sl;dTlu#5t070O!hI4*^~r~*Pd$b#$ri$ zA01f`17TIj#w$>?mKn9+n^6sa1R5;Ra|0?(E#kwAazNRNP)n~nCqDZKWrhOE(X&gl z!s3p{WhE345&6pL$$}TZ1NPTR&i3CT@LM#+pE&tFwA|fH~H%Hu)J7h z>8o07!2%8gkU{MEDZ3D$q=p6Pt4Rk~sqO)2|I3Y-6C} zI-j{7e!JhId6PJlsMdLOK}k62>4rM6{!IHcgWz(EL(iRQ2#5EH?oCs!Z+rZp0sMS6 zED$~80yFHT59)8oKc|B2;qSawbnBF-U-YlZA!bCS26U&lhB(FR{^jod2_B4A+13HF zbf-X*R*n#=!II=*liMTh3}eyn60CKeEEqcwTsAj%#+h`|#1H-y%zkH?v|M*>-)Ndf z|L$%j?{pl{U3G%4pH4WZnU_=ihzF0&Bn=?KpB|Wl`RRrI-U#T3r}8sK!({vGsuA~| zm@_vi{ACmW^~J7|A1(H!)54Q~O`0kXQxd`{X~$)TT8rRi?5K?-15ET<7&>0rHC1|v ziGE_GcANYDYf*c$v8f5(5Cs|r_Eq`fHWiysIo-R|B z!x?5hT@YNs^>!$P7&Zo^o2qz}!jCt2ZDKl9vta;Ri(0H+PynPumN@9;PWP?}JcDnJ zF#!N4(iG+^|I#%0?ypQpT~hZJkorgbl8eh~U$7HL2$`&gI+t0uCIB(<4Yur>#J}c} zB?HAc-#jSp8kxQ_hi?n*k1q6fW_Rlf_QWsQdITS=9YNZ)TJu4N*Ya|`$5l!nivtC{ zfq8pG3$~{XD=w)X+q+v2V$fR4K7gato)*C_%RCMYIo9fO5fslog}wZ+3bHB4_EGKc zC0bZ43pMbNyyvHYN4DEwGpLW>JGG~eWmJy=1UEQvYR$B1FJa1T5KCOf=3 z(BU)mh+wR{rSn#~Ns3|}E?SoQ%~!x}TI>{tJ?jjeOhGp+>J}|Vb+f&-Wb3b&wp}oF zG}Lka3N&zs?)>CY$(Cp6NE2x~O_+iAij>*!h=4W~@6#=~$P_pGFCI6@!HUxoEuxi&h0|4 z*SSXBZ9P#lKOGt~a*^kE8NUtmC*^f^S)*n{@bLUu09!^Ta_7rw8|&#$KQshS^(7ve z;1dD23zp~{MR=`#(SGu?nU3CoVRDOxqGJJw@0%f*{lDx5#lIbaF*^3L2>k^j+uR#% zG|pkxuM#8SuJ3avNa)2@v0?&xNtu^vSahqx3ODe`V#b`7N#&|dWg>g0fcC;OsArZ_FQ6S-s2^Tq?^!7_~Z;CCZT!o}L z?@aT_L>8TGkl^!i8|7a65p|1FXwsz=7P#aSBKq}ehTXB(ZBaCK4B+vX{2u!;bC)&w z&x(CCa~&%e5wejw*DcRrPP9~SgedKP;Kd{BZiYQ;o*aT z8cIXAFILDeLbV^KWb{C55RTTZ+kmRVe}H4bI-60I=rPc1#-oN#?7AYU>pblH_uftG zPCwkUOPtZ|IA7jb=^Vn#hdIS24eu--WgmGcOO33&0rEymEF*QU?bD(Q{)4=_o#RDY z?8Djt;rAOZOB<(U-x+`JOfA}08P{(fUXEDZ6DZnS+%AG|H;QnNwjc31FP&d}d`W>` zQt+)R3O=FkHb0{X`NvLAagu}kn!CDI=BVXHebMMY+4$F403!TYzH?cci@hQv@pTl} zP48cXD<#W9uiWJGz)`5{8+~7VlVfzs&d%E&MEE!*X)xqDU!H3;^~SNBzYN0jn!3%U z7|A|{mDA(4e_$lhpg2Pr)wf6ZuZSv2^%l`QF&iy6@~?1$DuCH|cBWec(ufz7Jgdx_ zPvLl4{U-7`wMQzSH@Le z_Ln*n@E0q|Ns=oE;xS=IBCS*EX+U8`ZDb|=omr72uyn}%1m3ojzcioq5pgh~6CuNv zTZSaBY~zK;QZW6EZ+z=c0bAEc63*90fOy~JGz%=D z=C+^ydAkuD+7flqurV{%?6}5kt6Ss8QG_Gkf3v1mL>zNp>t zU?P&$XD3m&s6p!aYCPsV1MhG0Ge!6O&XVIW04MbXFMH^} zXA2u&2^aBhyB)Hx=6~!l`kSb`H+y+Nc;~72;Oh5N9F;P)n+&?yp$=@FPmX}Ef=_#h zr@H4gkqLP+r)#Ao$OU`_Jcb=s`Ow#w-3t&g{K5BX3^}ZPu)^Yz5Io~` ze!0f*Y-*dQc6?*dd`LZQ!%e~1X0$=qoX>Uh8(*Cx5bvtgYq1{)i1jx09Kt(cZ0-8+ zN&AcQ++1RBmt)ZTHm0SJg@J|@51?pCOmKRor6Nyjp-5X*tDlB!}rMD;W%~0a(EQSjl5>d6f|3CKLJF3a8`xaJFtbmG$(iH&_ z0Y!>{6qO<(pn`OQg&3Vsz)bstu zxcB~h$Newkc%EpUz4qQ~%{Av-p|@b7K7K@8^~2Nyu!c|`7OxcYm7h;pA+x7lVFN>J zsg3k6;^_G_C0=Zu233R5jhvD!9-kvwsvm2v&FVRxy`Cf~wB^k=l%Q`kDwTEPfH`!4 z)k}UQy(mAJR%>CjRtIn!OuMpTD?I7edcNLM+vdV|B1Tngii ztK%FKTVbOC_u9JUdWG+Um$s2h#7m>hX^ZISCu7yH^}*{9=WU90*}HZn9|NaRuqAh$ zCWy%87xm%+C{nn@EiWHlp@w(jRdbGWC39aie5|R-o)eEy=f5_A!q6n7KvleM^ZUNl z@Q2Xz%H)6)eKG4#H+Tr2M3?jPq=e3QQ~6P*`^50)fLBgEFTV!DfJ}DWFp@d#4=+Eb zD###a7BKSSYT)dBmXldjZ4S{Tz11;NS?F@HP<6s!2TDEJUWb*0c=A`Y43zRT3XUo%vbg6 zyKx(_9XSXH2ca06;^re!TVe+ z9~h%Bt^o3A`wpk)bAowSiqQ5%j?Wu-$+217%oa05JV2V{-ph3)$*(CI*q`!mbE!u` z^1@};q3_29MmCTX^2*u`8pd@aP%c$2HxZyZceWdJMU-fG#Kc5ptE-?!Rw}XaD35np z(*oWPuG1t(Z$$pEEu4eIR&HSLXrFYMmDwm2 ze>k+7c8I*(Y;~S8c*M(^;&C^2Kuu86t&S1#?Y({lwtD2u~`wzdY1hH&U{8ILS-4>ZXFLDurjbuCbixxB@3jMI@~meqJvg=#TiFLNj9?tCHsW&KeCq z#P}bQ%ZTZmTpw0qhfN(j=2(j$xX?|)Ye}iEt5dp?2HQ0?D4?#Hl0lhLMDcE(e5eNJ z>cQw5!=!I27{HqA2)xv~Q9Vk1aZ6Wa0p4rmHos{bIC+>e^kb(1Wu<=@KnHvW-RJV3 zCdb5M{7*L)sSAtRCF~^t9sPD!ZKEgnZZDi`5<` zM1r(nM}Co8zZ|m>S-QkWsg9k16oU<;3GmVV_Wn(fpciy9=E@2grsz@P@gS$k>!Y4|`dLTz2bTX8~CCy-FBbgs!Kpjc8<>$3AH) zT^}&ArEfa{3zJ7CRErdh&SdPTQ0ZHRPIKjE^ACJ?aXT4RiOuMcY^H{d8X)IwO15XZmJN+}{AE4*8m#RpGm9 z#|;SB?<5}+BH)ZSy92-Vq(Wor>3$=JPTfpZM1c|Fn^DZAA>dLUxkPCqP>&m;L*E5R zK+k?DMQ_t3pxY)^y`8pph(?wi2%7;PhBJn$&nxKlKU}WJu*5XMI{Cc`r{BOb+C#UZ zo9!Zg@MYRDm8tTwqfpySkIMsEesV zrELQh7JguBRki53V!_qH$l`qGTP}-|w0G zO5*9mR^rx!H3CUw~JB~a(Ysn#|Rm5JhlDlCuCjRFu2%BA#tL>@~L{|E9Y zB*EE;#;$hzO?q){PXc?VE8Elv2t-O? zY1>hg`X^YUONKg@lKRg~NgFV*_SwXG?SxBm;o6CaDN}v&Xsfu?mY$QOB^4_EL<67V zMO_%HvaQa}Z#tpn!MM&Wlqj-K)kU8~)&%ZMB|_wl1r51O8STEWUm60#FO?!kj`Ja1 zCbkQ5u=t(36YHzL#3)=+K(V{+v%W~9ZYAP1zIoU9DZeVQR(IcVMa476*EO$ZCuckG zxzD+sExdT;IkP}aN%FK0M{>W16L^hxwvv z+}b7LrkrL5HabIB9|-(I;2S%*7TC&6wg1>^oPDSQ2OG*r5c~2$h$Sc;v$|P3|1s%$ zaAAOuXlmHZnYx}l9WFZ7kFE4&$T@UFyRis~T^DOc#GjsC^ zdV$Ux&a8LZ!^~wkm^;n=#NuOXXM3zt+kx9z0T&ECUM3sJaTQ2lj*r;(<>|nM0@r2x z^9kV>0V2pB%bQFGcy90c;#4$&kLZnwn6N4)yzvu)``UAqB~_NXd%SX{Dm9J!LFV(g zTba-^lC~RO8>1JL_4do#;Vv>c1tU5X*n$gkAFc(aS*YH=ZDiS#ld4i|H?sXH-FU!l z;9I=Zgu#xp?Ly~_)ArhVGxxJzj!WNU6yXa?ruBA4Ne<1SJ8f~_G3vPK+8X#7Kvo*#$dt?c4DAnU&$!sigx}} zxRPgR^L>UeQf2Y+B`G=SF7;yrg9ojD(516dXKa+Tqwz^4R<+8_p(^|WY|Xk5)_pzw|%~Z`oW~JE!xcL+tTF5 zl$Rk#HD&zvs>_ZKQR^e?8u^*VV9WLY_KWr%h!cJ&xtbt<{GYP-;A6ohR5LXSqamj_ zSZc>pV1N+qMLiA18N2fLK5=YAWFK}Ae_hZcaQS66|{I8?09 z*cYx_*;c}Nb-3;5*vxwRTXj>1NU`t3kR<82^%3Z#QyT3y^SV^k5L|owgZowe(j8$i zmw>eDb%l+9&BKXWHolMUa?g*RzF>Jg|7-h_V!%RINtR}>wLW}7quCR}t5WZ+EJeJl z+)*sy?j~Hc`d~_|uU06c=ClecK{n0pkdv@V#;A9O8cA$iIiaQv0~VNR<1StCY+qUM zQCMHZs#QJ|-78_7U zb+AlXB((9Ud|GnyjiW&NhavbfoTTv!piKzj&_WNcgyP9t=wtuA61XxCusmIxF9DUM%jjIsWT?bgbhP&P z{j94_1)MqyiqzAU${RHEh*!09Eqk5=v0$3QJK7hfb7|=ZETsWzJtulQ-~lbunSJ zUxbMdoz+i|3-w$(y7kRe|CY3{9HbT+g78jF8Hi+Grp}geU$UKLxb9q;5-v#h6}{nJ z8wjAOqAmg$wqg}IcTYbxeSDxfuuovX<6V+@R&CXYaw*;I5!J^Fjbh`(^G8;^`@zVL z%BAo1E#D54))0?gO>y8InVM5!Dm7imvJWQ)KjmScT+F__HagSjhz+MWSg;4U(gw!h z-l$x}aqD1cxAfF9iy+rym2pCYqFwl*>@5af6j@2m=yn# z*7BvVx1>Fy0oJuLEnqT;tp9|Pb0^IUNo@yGHd9q^k5oB;`C@S-VqL;jR&hH~g2xI# zoMNQ{2Qa2LSw)`mK#w(Msl`{&;uNfuFj;DeMhO=mH0qj=%XR}8WUiz`IBX9b^jN$3 zaD6ndMAYKp7_bDW^IW1-Pk#&KjAD@6{V|XnDgfTTjBSbma3&G{AnAyU!yjcRg)PL3 zS?gn1?Zg$?T*oPq_O;kX+ckj^j(GFpjnkdNjLe9gpmOdU50x*S6|}o@G`EKts+|XP z7Arj}ZZP3v1P!LLE$dsrmzYM8PMlybSlXQ2)=LKiH%9wa-`z{elM!qn?5>w`;Gu>> z5S@3MfulpGC51$7EjYjiy1U9Sah^>dVj<0_w{bCCp{@OG>1|JKP5e~vD&HDa?eRb| zlwW$S zrhazyoJ|QkpPs~5d2#;Q2C@x;LvFmjt(#SYWx%H+Jkqt^s!J(e>}|H8yR7&vqug(9So+=Lf<(m#4ACULLtf zRCN|x4|%z6Oc6v0i73x)V@b1s!PSaay<0FLN(jjq5%2 zW9@LBYs^fii!rf1M#x1*A^SoP>KU{s047Qu4P-MZ&t1J^U8Rg@Z1;F)K*3iDChr9G z$pnF|5b8i;xmQ*lL+mBLOz~Pjy%XPeYB||^eJkjjj(7PGEFk6-ek<|b76iv^KQM%_ zcZuvHxn*3-=3>w(UZ@(>Wcls>D9*;coxFdjW;;I%jcLb)hmBV~yNjRw0pdr^v;>_t z|Cz{OiL$uj_YV{MWQDxv*u%pR!QU((TD1I_A-mxpKYlbZB$1lG5$mbndJJf?pvNj* zWBmQAV6F2tQ1fhcb|cJdJDanoUo%$8#F2m@Y6Aa(7t<)o4DVDDy^K0dKCib#s}0)n zdPoG*TComJ-E4^2*wgk<1w zTh8-6-}J)E(3Pu;gvsWVtj7BsPSj>jpSgJka_Y#yCq)58?XOcAtz!vipM243J=P!0 zels_{z8@oF^>ujn&~YP0?R;JS`dyKcdBVV|d@i7rAGIh6;FsI6NhG|gLJ>Sve)u04 z{5kEVkjK_jdx?t!`@DF8<)y6&t<8fO?{kBPKZ)gu7K-UdNa``5(l48f|s{Y<83=9m6a7NY3uSIeEQPnhjg!;*eHX zLTkJ!R}Hnc57S89e@VDWY00S~QNu|DtSXP6z3E{upQVBeCcn7&ANl|#J(Gk_b@-L3 z-98pl;vY0W9jQt?n0LxJ{MN)vw=Fs0mN@fdZK2K|@1l}ynd#V0Izp2{0zHwjVWp@% zFPY(j50Z&k>GA#Oh?E%`kU_Di@S{a$O3?hY0$Mp-G9J*_I5x<8 zRP9*u27#AZ-+B2e3)cmcqR_y&&U+yvNI4grO4wz>uxgNKjP8H5ND2@fciJ2wUB*Bq;j~gzwmka>Lcqq8nu+ zxMcHkq3hdQt9?U6$^e=pIB1K)Ux3xy6&{_buoyR1@bRTHf*vcG z7SR5rworgygH^4F+<32U!Qw&g9zEQiOV%hJbKjO4-*wEe#(Ej8_E^r@RZy%f`#<{) zz%}=&CvB%&>yH2zic5RW0|R4uIxdoF#Ajw+1I5!K3pnEr_hKq)H^p;KkB?R6aD(0X z(t?&Ay3%Bw zJ~b>yq_1;SO;{X){t&HBI?UOFIas2&-SK5I&gWLIs7kiG=K=|lCa#g)R-(Kr-BDEz zrma4WqGVgj1*=P*v0Yw)oGwWGyzb`4iIO_}O896S?_)`bG0nhQWvIznq%)it7}1s} zVP990XMhz#fA-t)(jiXz)KA^vv%M(r*RB7n=p z_16RoSIN^$R>WG2FQ3mQg>O>7ox0KD%YJ)|Q^fc6m0!9S*Q*Z)CU-$T{@+_~pI-=5 z_tW6R9G(gs>~8g1MOln!R4n~m?_F8SlqO2J0b5{f?|o(-gF7ktbMr-F1Qgnn_2#&0 zwo4%^T$Ak!d~2SJ}I37^H~zcLmhGyzk%d zK7Bmj@_KNFQ-Zcxf{cG}SCX{p6gM?Q>dq%| z5Arr0@b}enWVp#|a>f@aOTC!&K6WGW)0?ftuY{c-ZHXL@cSzAkB^xU&E#*jCt%`Hf zwKbed0{2sH$n9=MoqzYpCx~OK`v^)tg2<~DvDHVeh!zWtk`16h*Eu^^HM5QD#^JcQ zt4KK_n~*d3@Xt!bbq*itIl^SOeE(48w-pNO+0pS2>s0{QY+FqO5#yWeyeBA!W%aUIr6ztt;n`GcmqENa(LTAt?M zV#O*1VbsSkCf06)&Ha4E;G4~Q&pT6RvldqJGG)%%{-d-#C$AHs?-YBII<-=8`!2DT zny4oDi?`B3C-PtQ5LPwVGA%Clqw%RZRjzSQ9LytgN6pRCUf^FvFB1aSUs zg_$yqllCgyVx5gKSD9MzcL+l{w53leLQD!Cc8}Q|i5aJ89e#RfOOMnbK6vwAaTjIBYf#Sq6*;Di*rl+kykJ_&})AFWT}C!LMgtztW8O z<+j14a}fF?t=?Gto@}9#CfYZ2Ns)>s8(2%p(rpKw@igUJZAu`vbSDJ1hn$~Q<{qr8 zy*KJbq&e>}dF{sg2(OS>IPh(W7C3AqEG6@v@{q00VhJmTr5D?2*r=?6f2mVq zEarUlg8+cMUrBm<440vN zJnF7;GwL8iH~)nMF>9+XFvF8%N`6Z!wwkuGwyN8*WS7stAWDn6JZgb|e07+3=54*a z0w2&o8cwew1RazjJ!yDLmtl9?NUt1P*-=ip?*+!Zy~?#pg1$ecMjf)hF)76xbs7*- zMjo{Gm#Q${sKTO6+dF4Pr81vhVHPIxBeK6fLSxnnQHkOt|>GefAsj+YS_<9LoYd#)MG8PdKI{)UWU6?YK4IMor5Ru3G83wv2>EkIq}a0@+-Rr zpDyu(`aIH?E$r8Sl@~h7hT6DxgDJh-t>aTsWe;=^co=XTdnvqpGAjQU()0T#uIU5O znh=^Re`s6K_VX?*(BN6G@F*ayggVO2E26O6V0{j2_w znJuY<*GIhC`kMoTU;deM;NnFWSjURkdYG;e9&2f7nX%e~=lv|ex253=8GgO&-#>}y zfmZMXS&yFI7sL0E=&aRRo_(8veLeHb)-%B4Zs5|_+Di#tytco)i~sr}zhhflWiH?U z_?br(PFsMDH8nlm3Px0zlH$dR9r>uoPqn^Z0E!?zu&O4k>r-##tDS8#!6c){j~`zI z7IUgqCaVAHcoAhNUAiqL4QfJAG%>%!n93m%MSf9 z#D*qX3FLIU+coqY&`Pa5ZM9>^k5B)M2*(}cTj+ScUk+wjFYBY{$$~Dn96A#^?pE+NbVMCj;TdJ zxc)Mb8AP&6JGZ1zc6Ib>-PL%-E)-UY=NKDbc>+6AQUj)B-Fx)tRR{EvJlCWBa&mBD zjg0<^b>By|1PN8DCQ`lBDnprT;b2W&7I}9!lN}PHbD4G9UeEiz1;_7)fl1{)?x&_( zN3-1i*dp>m$3$(qzaBKU91_sZzszEyurFpO`X$xN1y~IWj<~$FVHe$`jay$8zF?rM z_?30PEPV84_ePqyg!xyt!S9#?4ihgILUlpuqn!ARhQ&o?`*7Y3amCMq0y zm%ie}g4NA@)qd7Z(21wW*|W9&%Pob=1yH-&MjSQT|NT!mqVNjio}2iyi46p znNw2=9aG}Rba3Kij&9K%w%mJQZH9FmeqO~W{rh4QQU3gVMk_l07t-_hNi5HMEYWEO zZ|V;hDWdC{) zimudDwnUc}X%)}M3K z46@G54>eD5dh0~B;*#PShyrR0=f&ak{JCs4WhH^V5F?1!f|_2@bJHKCK)D3Eb42@f z0?vD`hcl`4=h-fL>*>hg!}rUqdNUkxVdz&n`(j4hg{aLusjX9Z^W>7n%0|=j%G%|* z8yxX2d*GD^RmBX0l%rmmtlPW-b@@T!-sa#ns+xbXPLOeJjm(5u8t89)!)QqBtrsgr zSPy2rbE4*7YR}(!H_ABXh6@P7eoBht#1;-oVph?qVD4;pT~eu4FM2q=DQpJ~Q@R89 z9?_X7NH<10dX=^kOF&MT!~uJk+X_RHf|^Scpt@J7vcu zYQ4{G(ISc17M<6!!Xz-Y;Rck^h_TSE5c=)P7#pwNuW-VJQNkn4p2vVC%TWL*JIvv(rCp%U&i}4vb={&B zuBRz(J9-sVMF#3+-Z`}sE2>5q>koYxgXB6iOz>&&b=kT}( zpZkG?)hco}e23ZC;f=w{r=jmdsd7tJPXmXFEit^6r1I+NKu&LwYB;!{5~1j2JC)3g z3fKy}xb<@3x=sDXTH*yp3v>J9 zJ|iB)E)mcEhnC%+y*Gt$wX|E7^4%p=cho?6w!k(wg&i^WoIYi9#N@m?X6cij<;k(y zJ}1+|O$t)u_3#>As{11K;)0M%UCm8q{ZGue^hfD$SH>P?e)}M&wxdUDEOY6(G_=_b zM^O>lB)IW*(lE$g1RVBU>+L0i@ZT{S0NV~s+zn*xSPT}fy#xG{nE7*Wt$md#9`iQp zr0^wsid(&mr!~fFeXKfBwM`o*zQ9}pqaH=Pbk-Fh*6&nl!IPP*TAj!Y*@^t%02eF~ zzGV!D<%zt}wO?8J(n^4biI94@YFB9u&m{jKk#k~`Z}>&C6uQm~;G&Onh7S%X-9NLq*eJ%HY@SgpcGHlCQ$uRG;+{&! zI=qfW*k#1A{^%VBjdN3uox$7NZZba{Wxg19lMzdfO@9oQ1y$DO!E^fsWceQddBdGL zB>Hvo=V9efkL2bR9C2i!F@tm?d^k=Dt+lauI4BI#jbRa+m`Q-e=D{mbAwn;*!`G^? zkfbTJs!*wS$jIf@{9Cbl+)DV%BJ9%8YjSIZ@L-liQnlEz81|Z}m{`gtrKMpb?vd?0 zxjhzsvLKuGs>M=Bm|@1ZQ>yvn`{A+Vf_-#JLRKSHB(o{llRO>O_OpT^aohOWt1e@1 zc_%grjSdg$(;qDL0`TcU0=Q(a(y@wu2xA{gfLgi#{JNhv#hy=nCI1UV*ZO2rkjLUn zrBYNx0Prvt(5$CEdawF^+LvbiMtZUZ>-PErHyuk~tTl{%ZkxL-Odh&pE1_EJe*n`o z=93~ERd6a}O@Fp0Hvl@|!g<5}{WcjtFL$5pPMWGatkmRWh}aQic<7Lm+S9I|f$UZXl%WR^*Hc0nZI$yIgB?>%UEO4#_eu_s@oVQ%9QK3DB9{Rj#A7qnW4bL57JTjj^Be&u z#;zX*BglBYcB;1@gB1*9eybnc=&pN58%hY$6|?HOwnJZ{WK&Z;Q{d1L+K`BZEgo5^ zoKf^5*KkZ!a`ay9B})|0{41@K_XJf}BTnJUAE^dcn!JzoeVHT&6=phFw{Di7S9T9C ztuG9X0QHSlK`eDAe%HK5z|{MP&BOY-^OhL~ z2qE6^cA?sPLR&jePGujfo=4Y9e$%n;gGC2&xOeo3l^&4l9HYY;3+m{m8@N_lNP{!) zireCrPdfkrg*BDwc3Hd$4;f#7)(o+{Hn4w($R{P$6Tl#+J#%HHWd3#ohPGR zk(r_h{g|bDn|ZmkQGnpbeB$MS$7h8MFJU1*nw6-owdS4(hVTzfeOmrIizSF%)2}H; z%5SAG@!lx#L6;g-w7I4=t52I z(yggMmnZ8^+q08BlL@mrDdFThO0p{omB=7k4rA{v9^MbRp<=yDzN^UV7$2pQ;}h}p zf>dFeE+>DuSy#NEiyk*jusDmhoSE)w-%ev;Lg2lH!LliNLVOQ@oYUY_kE8zy!YYo5 zT8>`9XtKmk&GNtOK2N5|H*Q8o9Q(5p;fnO7jSES+_9#mqgBx+)k(u^~uia$7Z8nvz zaZ``2rzEh1!%|ThvbmH_ZLK$UuY9l+orOJ>@pjA`A?DsI?&!L+zR+>xk)7K@2@;Z) z{%H2GuI^AQ(ZIb?R3>YZ2U|*LV>I(iU7nW~q?<`8dN#bTJ1Nhf&w_qL)!H zGZ?a~UmbOjdq_9~NQ`24%5C`BNm;f-;@aZ|9=LuJ?V7?xFz^baE;FEYH-LT^$S-gp z)fL1&EB0=njnY=8wMJ(Y=`oPQ!!|R6)o*Ndln8$;!Fb70MAay-8pVJtUgLc8OJlW3 zEE+J7QBUs}%^;0+g*MxSbd>oq$Uvb2{jxK3N+IR0uY^Q>;7H|CDZXmq5n1kSy#%qm zHQA?!-Kxrpbl~Wnevqhl+R=S2q`B09A>>H1DC7o*6*8?&^g1yWj1CIbhR-=Dj5T!R z1V*4^!~9)74HTr)Q&!s7L{z|$Bgm4U7ChtDJ5FdnWQ?!NY>#OrNes`=V=NVR)+7~N z4wcX5C`+ZK${N^JlGNQOZ(&dU=sveYe?+!W98dy&(sR?f@P~kPYOVf&Q;Dl z@gn~fFq$Ji$7Njd{{5AmecxzO>%Q}Fv zn?~74PqH)Lm*yMS-_Ggzw8RW1-<(88savthypKJB{ zEcn;=3KI{p3F~6qog3@ho(lQ-%F^hC8zR#?xml)}ni*xzwx)1MW;)99gTIUu{zBlI zgPOI5T#j@-!e8`pk?SiX#XJNpa`uRFRZoF|vb(M(Qc2%^{pQ0S?O4#Rj8Sz4%!x!) zBXq5L34gYM!S&LmuNyxY!;X(F?Cki+ZiN6(V*$LwZq4865lax8ZJeZIxo07v@*ya{ zP`*>T!DHA`BZ}`Tb>1)>0=~QTsnwTqaPc}M(yDt9 z&iz3k#UPHc(pdS>wAG(M+b@RDk{qD9w{-vV}Sm)Wxf~c5GRcB&~jJaKDMqdtV za1vQ9!JW}}`c0Wk+o!NQVt|yAB&DRkwSb80g2~plQ@bey*lKsqv%BW;ayVa6-CUz= zQCEe$G;Ur+gF71t3)C(B$BY&ejZQ?;R**jal1tX4xyk*O%TJ_wO*hfGnupEbcc^H; z#JluCKZ9Pgu> zbe&YcZRJwvkb#<-f8bx0RaOHtTEE^nw`%+Sbw4(p_H6;Awh8uSm8HkAGjH3fkzBEN z=z=}usLm$v2WYhKz^kJlsY+L24flTY9N)ud3i%EwHa2!W1Dwp4eIwe=ZR&_l)A9ki z3W5^*D;`f2Dtb>?A(H)eFbk>sb?vy_)RI?fC2Q`yIkx93)AC4}6b%MC zDYlDrd!=F_pn;YaGxGVimwCO6VnWFU8=e8j)Pb#6vfsbp@k14ZY)j@-V4sR<##r4Q z$+~cB^K)|3)9JP+eP=!%pQ={L+^@^BSpssl*kzP;f7gpShTQYyD?*c(QO-mfSr zdtmq_o$!vc-(V>NbG>=(skIE^mZDsy%M(0iBNPoTxQh3Gy#W4}ZMaqcE!$`w*l=N& zQOWi^VZ~pbmAq=TtqBr3NFU0XwCx7l64I?`>4E^p$U=T~!9gcAYq_*%4QSlZ`)GmA z9)CO1J9C4RNHJjot46}S=sQD(Wy5fMw@=AXlfWNa!WX1x(UYo5e`aU<{9J)bX4TH9 z`qO7VI)xlizG;>EGt4*Aql?D)cD`UH6%6KHhdj=ZnR$y&stTEt(sz%TzQPH---1!&FjgaI^APWqAOl;SK^xp z3IVgw*Vov$=CNQ1z!$OTp)|#6<%1>RbQ4ZlVk4jWbO;4b`HwRlepu#K&9lfT5=iZE zOcV@K>X;h0^GY@n28}#_kbIuMSk@eqMZPGLpS!Y_e70_FP^@^=B`WKak=*XJ&Vy!q zfRa0>=Lg9?S)~BKmDSbGA>v)7zT%DScI$k?l~MP(AZ=!PHZB&6-v7usgxlG^Xtoc0 z`)HSL+zUrYe|`ld7N%jL>t=5E1`h~a7G;dp8rAt;>+hXxy_LGZ>8D@aPf{?hmKh5i zQr2YQ!C_BhJ5K*_>}=nDTP`+BxS+XhpQ`k)+one`%C|rOmORz1;CUHU4I(? z;9zQsdLYxHK;xmi%ba6Uwv#YJ@%XOC}N(TjFk^IYXwl0oqcmxpFo^Vly~-VHeUf<0D!2lKNo zQV8i-J4SGP)*e&9FQW+DQJmW}oHI(elC2?qI;_cG2!50V$AO|hp;tgZ-LHGz@k`F- z-@`s5&zClK*m2|Kjlp+)ui9e-SE9rmLe$I3TaTq3P2!#1gWi&)rn8T&<=MU&I!11b z_q=iYu4iv<1=R@tC651=#kAgTnXCIv>tynPjWTu~5sMRq)nuOM%dBIUb zwR2ng**w+2AomV_E&Q?Nh?RbN4ZDe&vdivr^QZ9yV!@+I015FNaw5ckO_=FSh(ntp zlLhVu(r@HU%?@lj$)?4|dwJYd&KraqF!Sxpt7dwd^UX=>03Zb2Yli?H@_-?YlvBLA zUKWON?)l+yyq3qikXaL4f+ZYO=N~NPsLz$?38sfK@D@QVu4%#ELi9JE%9T)(rpum9 zDwS|-r*sn}pdJ?qkg14!ZjdasX)m6nWE{)<)VkpkS}M6l-0n)P^P~!^21|Vkc*zpc zSQ;|I6~>GHvpa|90paVO#9;Nu$d>cGpKl+*LL4(0JxOz*w;N35FjWe8W&J*kp3!7q zUloavWHmMY#Ia9rSzA6}VcxXXlmd9E1NJg*MnEC3huzJjnCX_7(G9qac}<02)UR`& z!I%G13GhfLew<+?dWsb!MxOM!eBIgwmxV1L1E~TeUIH{`Wf&UTvI~W+Gg!j&u zwXwu6JNKn-A@8POz!*67V9S8mt9o;WqHeu41m#VV;p&&V+&YfgddXP!KkrTXrXyM% zpW+`s`m11Tn*s;g4RfZMF@q(!U*FD58x6?z9sxA_ScH_lNI|s{nAId9}!6d~m zyFfx%6Kpr>pjdJ;j}g{~k@uraZk*1e!3- zV2%-0n!+`Bv&VVai+{NiZ{*At?G4Hj;XlGvJcd~Yhg&0cvP7f@9;3e z8g5^##1y}@prGRiFe2#fK6>5D`{f$WdBbeY^`pzYOzuyXzNGPj&_Bd-6x(YeMGKu2p!Nww+s+~mXY z11yG5g-gt$X}|XqM<9R;KT$q_wPjg*30TANB2rFs76)9?nELbe@E>2H(1q}PUs{Qr z67|OH+jj0oz#%H%=3=Z1fQn2P2B(zrw>Kv>GAE}O)D~0aMyt=@wzBrfsyYl+ckVlX zY%!&tn?D%f26^az-rY+Udge0udJP!k1#Z6dPi3uH^2)WaaH(Q=+~Y!(sHVpaB84jX&hsZk*AoDLt(3+6cyVnpmmJ+Y&(%%fRt@DOq{t@ur;bS*AT2ylNJ!Pypg;E1bmGTIdXJ&#OZg6z);62_y?teEbr+lT_A{+JwDQm_ z^ajC|IbK!N?4~eJb==T!R7;opVf+_4zuj@f-o8}%CT4?W?Nm=1=7y2Xtb;(#C5=(n z$)wVwb;Uy<#V*V)L+^W?EOTGkx48Y7&%(&g=OwlCJF?KhujG8oBTg(&jAG!r7wV}H z*~|Bo%`lnJ_>S6w@pP{a54wjV71t{Wm^1Mc)R3)w==*QLb5$4nzy&{q2-i%%hhZ2z z+5}{@kmz8c-8=vrOxHLfBfuu4@oav}#u_in2>S^#guUOUuZ+97s`kVSlBRb1 z!@9bBb?#*Mk^vzd`Jr)dS^r%R&a2t$a;;9Btm(%+*SBt>EH68u9@$eoyY$lJ6siTK%1|UM zo%nu6X1d*AiJ{%)gp?AYU+JWl^)0L$O-tgIQgjCx* zA!T;A3Q?<`ZsJA8wz2lr)I(I!1?Fr|x*!z4OarqFr~4?Y#Cw_h+M7lAZEuii62GXrB)IE+(+5o?>%N8D6L&FYJ5^z{W`~o2&yrEttj8zI zm15I&#cV=Ic?kPPW`7DUpdfUjjXVGeHL+xr_Oj$m}WNRT=G>@VsjYzG3Z5Dw_)YKYwl1lJU0@m zn>`>oH4aI;jW_Sn@PxTNGOmXQ2Kio)-kqzr^c}WNH;$Bmu=3z2ZZ6%%_fYx& zYOl`w?bAHDxSu8P&x^I(mzHm~RDXwv?cD9ZxsrM+P41JIgynnC`jpq#(_M^PVt7k9TE94PiatTR+S(q zkMH%KJCZi4NN~-kyPD_z6IkC#C0g0?8A-15^z4A36yd|BR7<;8pD~ee^J(RsC zFVJ(hHQQ{v(20Oe_?jr|MIQkOWliS`W`M{h;>~g9=vH#9adtbgkA%s6aTwp7nsjSx z>fY8i;pj>f)38YAv4d!S>VIhN{vuTdnzi_}{p`T|@k*4&Eqisjg)=v7)3WH2zCX;4 z_>0+nmguuoG)uA*cJH*}dZ(_Mz2Ah&PV%2EJ1g7RS6=X@*rE4|UYSGmgECox6d%f) zt*iZ8o20qvt!}9~H}q2px{uwCEAyD})>)mgYbmLqV!mHpZaWpkO&@i(Ga&)FW|^Hl zuY);&)KUJ1wodm5rvVnnQdyoJ8F@=>XPB9TX$>r|Wvd%mXbhZ_iqzh`{;np)(rA)p z_6zs1H;|OSdFPiR@;x>3G*5T~Xew=ExGr$xI_9WV>UxVV>dI$i@DArWeyZp01;%S0 z%VR<(>7H)QPEz%_!v(J)!}@IX<7`=$a)Tj(+?-qHKO&YyezK@fdo?0)m0?;p|1EP^ zWM_HVS8mIS_gPU<-RRYsEWiyB9~(ZlQIi?G zqki+JE*?eRr)giyd~0p##Y|NX-ECQHX^jbifFpe-1)DFYQ~Lls=xE=!BJkwPC3C&> zmvbc#kj($a^BwkBRJcFK{rLKZR1lxJt;aRzp&gFLyN}(ab-HUPFpf)wT~Y)5YAa7- zP~cR;@>tb^_>jo--yyP9pX~7n%7SDp;Nk%a0t@4x$sU{^jeW?Swl4P(YI{8PTFZ(P zl|QX=UmSWq*J87cv>gEYQ2c5~yiD@|`;?>FakcWg|Bt=542yDI`^FVf0hNwLBS@E$ z(in7iw{%GiNQWZQ4bt7+9THLk(#pXvFoNw~@`(@IBdC-NF$Qz(&OMMnVZvt@g{9C#IqG3vW5hP#K_`W6j_?L{n2?TEO zRxneOrhN}hwU7u)-QdZfZR-cWvNzrG`0g-1mdTUHc0l~ss@rY);Ll0#Vnd$(j$oMJ z$C>Bf0*e+kC~e6Mh%?u>VjhK~6=0;Z(U1etAgQLxEUMua6yh+dp|tVN-zn-ZS$GSz zUAS#Kq&UG;K&t#Bc%X4|ECfP8lLEvP_5!RfaMg#$*H)PihQ6W56LDC4LY6JBK0aO_ zg^PzAoA>v?y8N9#L1VegBu;zCyOXHkm^Hw(-HtHP{nU7Ba@PzO7kA3Kw^91kN?!D#VUf#a*h+HIFi)?WxtwL6GzS;MI%1fq|MWyb!#|*P}mO&;4?dBJEYMc#r z9q!VM>f`<8)I0@03;LX%PVux={RtfMGF3qr?JmmtSQIW}d_XWs#urYYRv3fWhk7=8 ztWZNuA(K6V8wBafQ_3$)DToCcpMSK^H~Km*A`)j}AcZwHLVzqP(lRLc@R6uGkdyY( za(K|)p04-fBSO(x@47OJHQPZlAHd9`iVTVI3*p^)TYFk2A*tJ6^3$=$x><;HFfW}w zabDMaF9#@5iZkOqI8va>Y47Fk=;;M`Zi&ru==D^kImIz>3s=jIlODVW!1G3{D(QNl zqG;Ow5;Sgads74hLLc$DBKuRr80OLHj;&}x^n)N302+O0Ih1eg$l5Wk=0Sk&=qXNB z?Ig3F6m%FRdRx_HzU@!=CfCkP=p4X~pXXM5r~aA-0OWK#Ai$vGqd>-9$8vu>spFY_ z1-nbGa$#I!4?Th!z3e!FVF}C&7Jsm2Z{otOt~7?=!xjh=N4l5)XFusO^ET85koSf& zwCSyX|0qK_#U@=?`tAW_tOF=&wgsRjxWs@fQF!8g$l{FyUCZ{B2raIg@ zu!LFuM?$w3HnO6$TrKwBv-oZy;L~kG_SVYmR-|)fQ@s;_=_t{0pL0w$7s5yPBy!_gu$(oBGbuFf>{sjp>@S0xeC*43m+Cj*o)!T9?%%q$a&bc zP~Z?p$Elm>FUJT!FjwHtr`K4w>|d1w*V&oVh(LY86YHk^q68af57xQ?pyufD9p*pC zfXGoyC1lJGC($qtDCh^#(&W%f#j!=xDdl~(?C9v&+3xBz1kBjUuP#T9%k|wLCCon805J2q5j>Bzj7}0sznn*Ct9EaXyVI0!T1SqxmbbgYE!| z3cP(T95SQ3Ahz5Ps3}n!cPIEDSR)v)-1mf;Sq6|M%=zO5&xS!CvwynTds);LSD25F z1!Ahglkf{+WPh_U;NukofxAf;D^vXcF_pg>$$z5CBL>;QmD6sL|8cT^xyAd-r(Np6 zK9?Pg4E1E8$mS#iKIIkpHKguM|ws3|85*Ib9nCQl!8Rt-)8(D{%o-Z z_W2Z-%HwaN|A#;SIhuct<{z&4him>3X#eP%e{{`1X23rd+W&5n{mo4Ke`cY1^{7p= z5Wu5BZ@dHnkQAWDF>kJXroUoQ!3CWJ&|N1}zr%mb+=s#BvDEeKy?`KW4;%++rJq<{ zQ`6#%J{<6bXc=axrmOJ~-XH@h8X%SA96C4hqU(N9WvVTWIu&jz%_c6m)x76fzqI5| zl{2ooWRoe}oq@e}Rf)`9iEyUy6x>8o>>4O;WOa;_kN_-NuRXjR?Z%HI1+mOdPVAB? zN+;u=39fw~PaOn;&y|+bxRVV|5kMcKj$-{G7VxZF+PZRbj=cI&g#0qC%fz z9vW<^)X8vr;?lk3jseD*RJmPz_34{x!FS1`u$Q$*T5=A_hU)M_YbxOKzA8~}3b?$- zr%WMUovMVy{h)_2{{fmxwVW2uCK_sfqkHYNd(Gf{KlhSmjfjac&YDv|hLW0~k9~Xu zDWjs{kx$MkDfuqR^*WixQQ%I+wR^202ooC3TT;8oTYA&{Y0H0u-b>|b(`+@!RGR2} zpyiSf3_;)+0heNgRiqJky>QoFJ<;3y-dN1XOphm~y?LVpFN-{4REsP#O+ z&r>Njb+;e+2HJaHX|7^;I`V-VYTM|%pGYcSZOt4B+$yR<>33#AA6~mQWITv;Dn;b* zJa(`3DRL+$0M0ulIh)YUxLBX|ubOnPA9u_4>(hQv(qaIv;|NL2i3+B8 z6YS?zaS3M+1B652rGktUj(zD-{Uw+X#Bo1BUb_;JzQs`p**okeCh-!n=g^Pkus$u+s1%;a{3ZdO z#p2kL^Avbp1}BaTy*8?4W;Vyo8PmJkmSyMMcIH%^es$Mbtb+cCK#hxrm+Eoa@ZnmH z9K8xtM0j{O_9#`UK2X8=B#2;;T3j+{R0&*1Yi#U>sp;sZmuIFzyM@00KPz6q-kgz z0Dpq`egz@w7+#oW`&`Wh5lDlXoofE>E^N|FQG(L0Cc9SgUdr)q242s}{+{1_2E|Q) zqn$S8j2vpPy7w*fEG;X|E!1A)CCma4ZC1vaQ4gMz6fAY%;%GUBbilD~LwHV5CSW-l$Bx zvah>JJL!66pJ^)jwSX%0Jm4JqRi9RHH(L)7pTIhGm-g1O0qOQ5TX27!Jwv&0%Bb!O zz+6wEDajLyLBQJKdm}99`?(rMJcTxRE}9q9zzfA1O_hmU$9i#m_29+Nf|KrY#Dn}| zd3a6q;`{h1uGeBnCdDF|h7XWLjpc#?fb>xT;K+NTSfzM?*Ro!~CI2VB^>!dTDKWGn4;k`g6~N@S{N>jKCxQ6^u{T8BV@AnyV8sy#mFDA>%Ogb$LPA1imeXSiY$=0R zYak2dz76#-5qNzyBwVPxOr}wy_O!hga4oLP3RBE^;rGtSo9O4muQcQ>uMwn-UPqx^ zQ@%{N=KF$#?uiq~nhAiFpdu?4s18b&r%TN>I2oW;CS+Z!MnP`mi8J)Xa~T-*3ub^< zakF`^rNO5sg3UDH52tTLY7CGWVGOT#@gduT3*yAZuerHLo9b%j*f^2EX5|h!}bP z2eiuG3YZbHam$T1@$?iyaPVl~Onw*1IC;rws=$HR;KN^%pePn=zXAffZ-QKv+E-Ob z>cVkuBu1>~xncT&M1@D%S!7CdnOj;~ihm}&(dVT(29!9hIkM+&;ysghZpUgzy?!6* zHJK6^=kCyR6u45AX8?q$zs#);z;oPejOY68|$*QEQAc<{`Rt*b z03v!9jaNg%P#7g}P5CXySwZc3W?kA|04A=x->eLBTBd`V!{m!dG5l-N^Au#bU2m;_ zsHC5HsNbxG3&wFu6(hO+5J)khJ9(`=63+`P-OC;i2tv*dpzpr|Faa?DWQDj(D&qt4 zmx2RxIdQJb!M)%D*AJDs_zW9beAM`z`cFYfU75S9c+gy;&KnDi0~rw@Gb)}bxMX{c z1q^USC|-z&c}l&kxdHzEn9uz)ZYJw_fdC+So?+ED&l~$W;5)>+M+?o}d?=WP;6U23 zS^mV?m3$4#a#Tcv!9H|D_~-~P4*a}L&czeWbvbP0FYf!At}Wr2QZ!@+>%R$zh_ zbYskJV0_0Yu`h+uyH|ZqMnQh=@}S%4ji3GPk1 zbv-FTGK|~x9{PtWm|Pr#74UuYBav~w`LIRic72Hcq1q`!@Bdio>z2*riLdJ z&`S<)=J;oE4Yb$w$3bq{A&y13W3tz|L?x^*Y~>a0zdh;I1ks;{Oi{*iF#WIu(EVi zyRVF}YeV3F|2q6Pwl9D*LI3b?_1%^ClvSFRYq?FODQ%WrD^KmO256CL1tynYtT zYsL0|8Up@A9blh#e4-@(`)&UJ;n8?q7;o{mWYAP`xXMaCp%llJ{igpl!S0@-AcidZ zH!ffEhAePGIbykOv0CwM3KacT#6Lm<-(VvmemR4fua;(KyEwG=Z}xAxBpEx_4&EhdI2`d^&7Vk$!`4@FBR`2Jl9q71&H|ZfAjUrvEYyP z@M&OGur+xF9g8OVN z8g7{qTN>qk=chcoX8^|9)sfsnq`Twg3QGH$9}k^vDbQjp&y>a^sP5WGk)hAqUqO--#8pY zc`y-)nco~YNlX~eZrhCXdnUk=v7zg=h2N&?x|BGdCmK0s4_jP_`6aSvwJY14D~9lN z7921Q87Cx+^lTJxus6`87}vXTqcWGiN=#ha^rNimH)G#8kou-xj9<;U_IoN`l) z^yeAfseU@0{^C*nIQ~!(iHdVYL5{8H``;&yBSxl^C<+p2E{yf&P1v%}@lxmuKs-r| z?>|1#`Nn_V*MQn7EnWQbw*(M0qnpL~HygO6zzPOKt=NN_) z#_v-1#nomfI^JmeeJ!O!Yqe2vo!epp|JY>gW`YUFd^IMs{3^+#`2#BA-&AlVcz$64 zg5YT2F(dq3L)pEwAo1*u!j*VNpU*JjjAd;Kl0F+%NYwt2U7XI2@*$#2bmQgEeZVVyb2J5t;`Nqb}(J6oXsAI zoLe2VwPNyo<1(csca99yv6y4?VU5YMih32Du|`1eevI~efXl5&p~L)iteNL8l__<3 zRzI*Ns^_0vs=?&7!P6kH#9@pg;JJs2n~Vs`IUIO!EqLps#4${#e4Q zWyWve>81ZTv{+hEo#)WHg4{Qu0{uqzkjbhcv%Pg7m&)a(|0}wW(`N8;Yy#`#~S@7xTxU&prnxL3yq*|YG-@~_#$@WCph)FfN z5Ryft`QxD8AZ;>T;hVU8dBndC=IUU3&~;H->FnrC;mqyz4S58W@9fYv@x9boj^6!( zz@Z;8iW<*WG5)nVLde+S)OlOn^!IC|d)ZZTe{xk{)Y{Uu()ouO-K<4+5z)+NLm-aj ztsN(&UVNq5#Mpi~p-NogDs%SXH&y)uNKZ2;!EStWzmKC!Z@qPqqWs?92+kJkt&tw$ zCCB6udezd4FrK)pR!cr5tS@O7K0QD~9@qKIv3DxKrS>m*-XWHP&Ml`k?seR?ybMyE z+mrBX?5pVOaI%p#zgmDGJNZ-Vr9JPaM3WJOa!6;?JB$)-HT-S`2lDQjC?}PI%yZK6 z?*%K>7a7%#!77JVW*l$m%iEgR2%C?REg5dscdhVq#NjGg+&H2N&d^<FR|_<`Wj2hN%DA`8+zWvN69gkirhljuK3reIqVe!eYB3PdwP+ zW(3(Nq;KkLij+KIug6k4vz!C`JkG1s=q<(l9%V9|R&;HwC->b0<>(m~sOYZ*19OP_+%;m+fyvp!xiY#Ig~)<@0B{CpDW>NP}T|KdMGD%oi6u&nj4 zU8PqmA3$F}W5U22Ju|lJ|2miR$4|jR;yMitK>7HDUu1diu+0;oC!EG$i$FJ3rON3J z>EGm=(vu$|I7R-J=~tYI{_00zl)}%A@u%Ev?m@~K%|e}eY-I8`bI8F&>=CD)swE9u4rc5QNmzBta7toK&%C-9qjeE#O8?)O@Bz)F@iB zPyIai@hg2|^>QSOo8D1P!QZ!p3gKDu>y8n`^(#|v67%zFpdp0^{WC6gOBH6Pt!1kU zgDqU6<})RI>jlrKhraALWS@uaRrcNQz#%a##PMxiL;#V^o{Eu9f3f^0lZQ7#cJxEb z?yw5mo_CcIdt#fCthA*iT+4kMz?#Wg3etR&uTu4_9%R58!zgr8r5VOHUmYp+0Ue?=fOS4$wv3DIKTq_~$8tRx@Ms9}( z&%yYM-eq3XDz;$~IyJ5xW#`~$gBG|7_~&;%P~NP74#&&Yo}7JgISBhW@8elbN&88) zzx5Q>@VBoFf#81Jo9~gyh9L@{S{;DbD918QAGsLyViK;WWi#6zBxjPkF8Ri*#j*cq9&Fr*(`q%1osE?zOZXAJ4XR2r15;^lGpCESm1}C{>~y+D z`KQN0Rxz*QpJbzbR`2UVznqbVJ{1M=LZ<30A$ zgnA`5yu>yKxO6%m?FBGJDgNH3s{`v0N)b-v-q)Xx$5wfj!w46l{#4)VKs?1L<;L}T z?m|@NHOQvZ^+?JQ=WOv?SpzR_EQ=gxM4(f{JiFP@6TExnp5D@mf;HHWu7#jrEV>{w z9kD?#d*7JtL0U3itl_ZN4~NoaD=<}le4na5N8g!YxyKqi#r>Pr849H|*r%1{F)zXQ zG-m_xlc&-o5Qc)=KC-BY(KV_5{^y>*$<1qB@>XIFce2|Q>dly;E!do*%y@Ea4A6MU zc`Z?d+s$0z8w{*h@g4#}sQ2$j{8eDzXtDqKt{~iT>A6i%|DS$&;tk!r!2-Bh5?pZI zOaUFxsx_!LfBor$|M+rI^w#|XqA#VtY2eR~{7=6G2D|sqU^lz<&o=%KTKR`S{wxXq z9}`H5Q84V^^7LJ@5^adGgJy^o(0D~7UcjU25$Us`^IdR^)DBd40+ zaw>)lc4IckQX;`~=*QH}3nQxl{GxBmeK1DKM8vC=3qD4&nAEd#Swqy0jCxA?8_$bUa*3(Uc>px80NQUD0k}y)t+bT%&Ton~5R002SRgZN-y_Z3 zw|_q6N!`k&UAwX<1V_n2{iN9dJYkpR$;WD(HNzOb6V8Pi*|S?);my!Z7ZS-uuH%d* zv3`~Fk3f<0Fvqk#7R-I`4*@?NB*Bz$0DPk;2sEM3Y7T&KRDwKCETQC6j9PUg0OqW* z-Ex1Wjyt|J(8e;%?X(+70{|N}B-gNL%7r$Llf=G}T2w2u7n`4k=F5R6wwecIEFLfy z)LUUe=JvbZmQ}lt^P#DViHR*%>CUJ65}82ibKLZf=X;ASCORovyWv(_RH$HTGJ8}> zw<0ovkVpzpz7vtAL4Sqj2l8o5%`qc%0pXv|Ab60aU9EqI!ji z)k?CZKVH5kyh{exK8JoPIDn>#$5w42c=DXP%V|rPhps> z`|>^>{#F623^J8rWx=N+3jVVh5-0o6Lxu-iGb3EO9Z?o&(eob6q(}~$w=I+mf&FSI^)9k%~US+B^0Joq*%W-n>9=}8Ltn==EmH`D^(e%XK z8i*^S5_duur9@vL+Fwcbn6ltBf1!qSP+FEEihrYw6jcyjx8xNWCpp0Rl!gLf{mt_t z5l(Bxz0vVGRpCtehlo8fp4?;XI|AZak%LIBIb~v(OZS?8{C$75-(h9;&`HjmB^MN8 zP;VH{NI7tZ#yg)$Tyw!+HE9$-0rVN%08mS+Zr@2RIo@5!Mwg)d1h9Nc3L*euWL(^E zR8VO#NnX4_BR=QEzpp8D{?SkB8yjy+D ze|Xg(sVZa}k>qk}EtQ01rnhnYW_wxg+vK`Y{hx>~R~UNRYpyZ4vao7B^o4YpV9E}L z$&|g8f?_qzV+3VvoS{oYOaS2#AxFu3G;Xr=k6TUCOjUEvg)BXU{ZCc`TxKJ6d_n6* zJR1$wmg6>PJi8kY;Eed8ybQ4P6v@2et?Q_)SMqiD>p@U`m2=3vuKbs1G@d;&h<$tN zUHW8iINf}@H9$~w2Pu;8a^6|deS7aGI(yC&MrJhu#LH^X7=ah=cT7ONixByhet(6ypK@BN83&8F#a*7SEr za5W$csh1cnGu_WX1)1?_0NiSHmh6nWcEh{Upya3BRFa=p-KnP}RXWRXe@(a@YRx{2 zce4BpAg)Qp@piyF4Z)z*;0V<51}9dHJ}G9*Xt4XmY0*PiZO;O&f2&$l2`)J}> zYbg=*JH+jwrM3hZEd z3&2BcmZVGfF~Fl%knr&T#g zpkVPr-84}6sBTt>L(HZ!Sg%v}b@iD6)`>V!o13^f*vgN8o~vSUCwqLPP=R_0y}2Gq zP47&%7d2j^GkDr*t2Mk?0*)bNvA6nc^Xw!yDl#%7Qck^qCgqBr(5ZoKt+7V%8?rTT`7uiCZR;PVZTJ5pf)+SLIO|a*;YLLhDVI&W`&z zw<{-)TNl=&RSen_{LtOsgo(HIY?Fu?(2c`7N><#B67N@le5)dhB+-FMBM4*T;c1;z z`~-usjLTx_m#P+_m_Fg2Hl`Tpv$S;q!=8$qH_VLjE{s?()yJSh{gbgA8V#EbKO;c3 zTzQyL-hyV#mYF%YQI|HTmp}Q_Nv=alkZ=HvoJec=z{Pk`{Q(M37EF#-joYGBjl56? zya0`ifKiQ|`4}e=>&Ixj2Q|m-@cbBrHAo>*Z?uU^rPO0WGWsSJD^NF%`0Y4ixCL=jm~-v7IUy=hl?$ zIUygveSChuZ9~rZdpD=JPoPqama`XN1Z--@x7}BuUZgK>(&L=5kMD~=jITZ(1wf`j zMm|CcuTTk26`ymNYJL5@GoN?Ie7$FLHn<|aj_9Y5^ssXRgId`Lh>u#y#42iEpjV6?WIL3+t`)|bS}nTIqz1maJgX%2zbb{0{! zX(x1fZTu9uH|b02Mik>?e<^NrJ|GI$ep2|knxqbPEti7UZ;5n{siF=#d-xh|pb5lP zb#Q4P9dkNLW1-DB`mPYMn+{>;gLR3mlSw^JCQ*570%8g~OSPa0<nE15)a1hq?`vMF~zfN$yTT$_o< zGrPz3*p}C!`t?+5>g$Ur@<1}t$Xxxm9kg0$h zX(Kk_(u=42adw}aR#R_hc_Hf2S`*W~J8jd#PM2Wj68Idcx;a_jywaW|`bl*aymizC z5mnIIoZnkd8PHIJSoxeN%d#$PhJ<{4R6R$XuoxEFU$BMBMRQLmY*XB>CEQ@x2QPtm zhSBtRS!1&TM(WB&%UEMuUcc4<9Qp*ZM#Q~8i-~R1wC*pl5!xo-T#-IH8@B0E~?&GGX$DPuJ?8j4x>3GbOfd%SQZN3%Yvh~0Hybvbd+9)a~Cva+O`g2E23Dn3ekQ9vF8yG&%p3U9I9aa znDr^Ufrn-1XVRbSn}*!iSDzoHoI#`TlG%ki{lgE63SxBLeYNxMX+IV>?3$EVI$t@a z-l)>BdJ7p$?@JHHlN_!PVs6~77^kIKkdMq^6}s1@k}w>_9gOWm+jA@q%ObqH5~HQ9 z3E~AIHg6|q^NKVT&YH9j72)HL9Zb5Q5%O+7u3xAk)Ou2)cqapl8noTKl{CHK-^=U| zYLeF$steWI%A;rgl|>TBsw1H*l%-T^Y#0k12A>rZj{56;R>Eqv+kis-AW*cR3q>xKgz zo|7EuOm3arw`*S6i9-W(?z@yS0}n_`ctJVarO6I=2uFSqQD$mo$2lKQY>J3L&E6-! z`wA3lM@}e<&NAABvS=Qi!=8m@TCVO1_}O=7C%t3qIHt4B{?xwUY#$Ohk^hM#M#sid zeT5z;n+7zleduxUo#GCKIOp`6g+~Tk$z8-lPt3jxD~=F&;0f&u!O4A5f-K%xstCN{ zAC6DyAIC_$2hjOG-ZSDqSVN`jCchF^hO~x>Mlk)c%b`)y`KC02cFQ11ys5C#)@1klw`?_@0v>Yg%;i&dxK2yn zL)WND%PYsF6X)ceifh;9dA2>>+Uh%s4!Jk~C|=;Q`j8A@())AMFZ~$pl;Xi!A;SAc zs>#2w7qPKU&-{q0^x@@NqCU4=%(+gxgTmO`fG)@PJDPkm6zqBGB;MgMt1ksGi-;hI zb|<{>&O#5w8b+@QsVLg~hmNMBSO z{;aW4zmubg3;VQtyEJtw!=G~lB4wl(TuLM*?z?U|7^&WdLxi?>TE*bgVQnU}=&6KWDQ*PZN$q-WE~&dbLcG?_j_ zK|`aAU`uf4>$FipM4`OKgY{m!F;=DOos!-q=Z9^@>WVP3`16Al7mTqa4q{Fq@XtIBQ2WR;MxS&l5C8& ztG2khDRR7Jm0y78cdp#>495WjsbDF+Sa$QO@osj94VkR9dxV@;CX5;tB^0Ov?HsE3 zHsm!S4+ZcN9J90>Uzs88>%#j-ZM_Yn0o-jV0orsOE(*TAmc&x0HsQ}Tfj-MqtwALo z8)2{p6D=E%Y+V*nAPV)?7R6WypKCFQZ~2`@lWT3V1>h;i5U*vieN$eP#G(GVst5Qu z`qSnEv-4#xse7GgEOU^NfVd%1g}w&9JwC$atd^^oe2OiWDuBFrIM=*=x|sWQFNuFj zDAl!Hii-$Ws5LN-PFM~fyE~;|5iqZLvx~Otvj8YE?Kh-c4~;1<>NZ}wEO=N`bD4Je zt-r|DX>W|UIDs%nEDRF#Pr6dz`8?kET+pS(Ir}aMByjNIb*-%|&sG8}dH%t&#w%!I z>XysaP#n9;zCFX5X(A7C9Hymc=g^+Njw@GB77uQ07p1g}Vtb)|nf;nhl7Tz-%2O@{ znPmQ9DTPD#w49d^S-(3%3|(sL?fi}Mdm>tQ;>2M9_)5@3Z-L5y0r_w;NI?|97#WyW z2FS4t!7%i>-7-TwyGt;;)yUJ#3P9fT*!q8u419)cIaN`0@6o!5)hKq#Sl1c17M@GpFEC|s#xa*0kawh>q?-z5a-ea z%X7m3d5Ycfh+#?jyF)`GtBeDu2$Z}}SArn$U z304NT3Yq$co1eQyG#;G9)5dRlY|Am1$tk`@l)`$~CRH7q^@LW6ZAXGQNRIr~q`@7M z>X46a`dHLbRfW~-y7kbya!xntI_Z~EHkgK?0o=l=wMjqIwAw+cZY-w?go~{Z2#|$6 z48TTMtN$S-hs{3fnZ=|?y$*PIaVFwmwqQknrtJiXEYbOzsEY5sF&mg=A!IO7B4`!E zURxoNtxz#C=hUvhxt&TXE_|Zk={nk+hj9;|-C3o|1c?i2!fK%OMhn7K_=+1rMu4@w zpZ9DLdnKBr(;>jV#ou2zC09P_-OeP7^)%o{Hkw+s-G%k54Bg&yKik-4Zd|j)!KY`k z4I#e+t!X>paBc5;ZDU;HAR^^Kv$E1#d{o`INbXFl9{xI5Cq~=03AE+>M&E;R^)rBD z_u)907CKcGWZzE0Y1NQuJH;giJE2G8V`y2E_Wcb5|44jXE)Ol|Zl{Ay7t$wlOFm<%tstbLUbXew$IP$C zDuM3r4u_3saYbKJOs2@!Uh2s;`I6#)7Gb~W@S#TD(oNK~8j zPt!S0G@smzw{!;uXJSdMV30tWsdKYQJVrPs-=s%sEzzVqz&^%BKBUKz=BZ51RoIQu zp_)kGva?8nRD^}S`j`l@ZWy%-_MdVLVjtV)ex6o!cbMCeo1rF7Ras<(Yej2AC~F%l zTM9O6ch3cu=m;ItC<@siQ<~exc0NCsYxY^pGj##%0h1BEMu$YVO`(HvoeO)^U{ZlE zd)dK1sAon*PzFe%BTkY--?o^|HN;wy4u<8pHQD#e@wptzB;4T=>qa^p86KEqyl_l} zHiPGWreQ%Ms4o^|M~khCBUed!Xcly~_^Gp>*SVc{s5nT5@B?~lz-c^2C+A_}`rhdX zXpj6dOeR~4e0mOZb@|z$0{y%xLaAiWw=&b%xmiDTpA=Oh!@A+uM_hKx3BXjKUZ9ck zYh_Ul9qwY{+4>{aE-s$C)ckOc%I!;xFA2#HE>iHjvr%{Xp)F~i^99dL(G*`2Tfrd* zk0tK_CdV!C;u`kShTEE?i`Gio9-|7t4jK zQgmy5u)NrGeYNDZ6b62O`aaey&y1tw11ptASfah~5Rcl)r#-YYDVu&;TZRV#!W-s2 z211V_u;qPGNO{c8;QngqY>4y_XQa$Y%wEDc_#M4zbxrW3inbetO&_+pqePzUZRM|l z$Ipgt_co)FI1Y1bIqmPOk#f4IdZh4HLAZBGFNVJBS(cDC7ES)}t#&ygm3RtI?Z;R(gw`*F_< zvVit!6X{kxUrH=0^jz>p3%odcir)vQ;~KgS^BX6*>9roysea_vlHuRCAlzPc)24QQ z8{bUCw!FF8dL$wS?*@0_*C}GTGxX-x!+-=Ln-&xWJ$Oza(3LY~&7u`mH7_?S>Jhi@ z{>C6*+3h>`_%ULWtmnP&KF|*cRgn9B+%u4466)ViyRuutW;*mFtm5ooI^+575#e;D zxw*eAKBbgawvMWzGDWs=pTkKScWV}Rb_s634L3t}!p!*IN&q*?%7D%fC?{x&E!+6a zOo!{fna~|2<2M{-=7H&P38ll0A?$sNsZ8@(dx0ez4a2C&X9c0$ibpkakRgBqp;9Xw z?xH80^uaFzQ_N>92=hK& zi?>C{kjA`)AOTYLYF@3V)5H>rZD8)j>xyqH+Px$ER8_}-ZEdCZwEy-k4g36Jp^fmNy(0+J`Yn+OfXTe`*3H>h15Cy3r=kwo0?r8 zyA(zBUUg7Bu8i$6KD7(+d!RdFG9PTlL!6$x)NNL<)0ljaG4OPQ)Ij~|td4R$8weat z%yAfj*&K1_=Mk147sFz2B`7~SOoSLB%oNQb1b`U36Ps(A&(=jbLTSZcMdn0s=H21` ze0V}=6$EP8397Xn3Rq`Q=Gxb!fep{@`A>cdl)6gH#lG*A>`r3b#!sSGzZp(sX*R&; zu%BfeJ3m(mf{fVowtQorH#zKb#ce9IvxOB%H z^7oC$W3txGoLa9-_)W7O9XuF!hZK}o9aew22W@!cL8r@wJ$vL)9X}C6wVq%;m?re5 zYJfjvobZg8-%;VYFqf{_{qEO0qp2)hA9r|Z3ypSlCR7>jd?itacv;^i_h zyaKy{?F(V@CbNA4(n6;_xhyJxx8Sf?bYf+)+C;tl?acTjxhpV=Om3Jt(fPzlOu_i# zcqM!yuF_vEqwF?|%L)})Og0K1HYE#S3%Nk~AnvVYEX!AF!vPOiE4Br)es zO!YaYMFDxgL377gh5}4Nrb&?R6s;$ah0T z_Ai6CDF{e3d7f$NnOIzB8;x734r5$+hD)xMJ#_h5^kZz^%%sPA!vp$`I>4O;XQGti zM7SQv?)+`tvRhEGXOG6SmTf&A~Xa31h;RdnNCY2vS~_K0l_sU2b^KEeMS-m~ijM=Do zkSD9j+s$ASAG5%%wU=?hZ; zoNsl_kzgDA)yD#b_L?Z9o#QWB*M9E1f^yzE*+BgJe-%Og@DB&9Rf#90cv!j6ru*k> zpOuz>a7k=z;P!|+K8VKGWZJ9t4$chT&@}W9;K<4$l(TKQ4SP+glE)@A#+_#82zzss zEw(QoS^Ip_IUm&~9&?Q#vTRToiUs-NV{dqW7aliIBu0=Mh;wb8OkXu2P!dXgD52Jj zrR44eVgAX{oy#E3Hd;~~L1eq_h0CtA2?UHS(nUg~HnhX)2J>#avC5@JJ@Bo~Nk8u4 z?7vM zz|>`IM({4i^R>uwi%zYBQeuax-YT%(fNE#VqcLmq-kCicn=;qqvM#AUimZfRtinj|)np0XGYN-v|% zoONBO6oZ`i)IAGJJ%z@eFZZ$+l{=P8ane?z#2LMv1zp7A($>7sa*u2mBAj>^5#&8d z5rb1((C1u8Q4O%xj6@8UYNQ^gZT3S<9K$DO>YfiBC?8xo8MiLuo=*Stx$SlmZ-=_9!^(|g=w%ke@Gx+YXiC99{5I>dCU%BHMF5G^S58;SM zGiofm^}1c0{h{#0f0IwTgd&8$KOTk6RZ=J;s)>-Yeyhq{(N=z{p9d6}Sfx1Z3)q%| zqm-d*~>e@z%{UHc3h(kFbF&eNkqY;-tYVCLr(0dlG|v*HWQTcH=ylwqz%G@%8@9(p3kj?;L9a7RqM8<6ildB3cS9lu ztV`X^6+aX{C+*+3Nn!vl(Sc-a|DXMGaLXV2?rpbBoG&v73HiElv@bnan^dWsEEvwPDK%zn#ijfEzKD_#7VcxG?G?fu&|jf&nE=3fmzH|>bf}1b#6}D8Uor^|!)?Cu zrIxp&I5AI%MNe9Qgp1ZO$KW+Rr}d2x8{{?UlvF*#Qa_ zb@Z0YkFfT#X7urR$T4-^0XHR+U0nlB@H-t=!3 z?~LepcaHAdu_E|FL!D_*M{G#SlV}Fq2>Uvg7HjUE$cVTng=>q>if$!?-C?uCORe|k zoK}c=wI~yc*1|H27wOZ53roO@>e7I%w8JGFd7fHzFZ*`?rMVOxY)u6yIuG;6>cZ#5 z=2gG-92bymqr^4vpc0;ZFKdbv_*UU$7Bq0j!J(aoo=ZW?=D5S*7-u|dgp-NR^QEN@ zXera7+faq8voO=|%wa~)rMTerOOqGD(o2tRRqLS95CzoC;Boq*hJJq7>1d>-cn%~h z$7S75#IpPo4RSn>bnJ88pwo>2(fi3j3&4Ssr0>Hd#M|$BN?i`Q$C&CDPb2LGQVn}? zFcMDpfo7*i5i(K~5XeYwqP&kUX+y8{^Is!fYmYf?Fbaws#w8*@#Ryx8v-QYPSeuVY zDp_teZ#h858XbESWBU>pX50?vHbDeJQNpCu%CrRv`yovt)9Pdcq6gbi_xObX$l^a9NSp}E;fXw-OpC)*gw&RY{X zwDQvH;7+#83A+Ii881wW#e-=}gU(P2~bG3Ih9)UpAj_6-{HaW&A!bHPHD5 zROt4rBd3d|(3UCk)r`+dGLDn9ytHuKho#7g=pBg0+l3&Gil1#F zbuP<TX7#B9wTihW$=0D^08pxfI$D|1`mf{C*K&OjI zdxhWxEW+a)J<3y>wioj@ed%-`xVM|#bsWx+BEWub((+!+^t|T`O;|}kwF~lc&jwiBW?d_O`|oG_l@0O3Dtm}obo*l zu>N=`06<$AM$s%fS#>lZ0OXN13v=sA0S(J3JrupUFM_vbDa<0Y9Ms^RzakrnYbfS%qG)pU$>~2=z8&;eFHR0`+~w$b(DGU?!d#Um5bdpfG(;z40jZ# z-z_{!G#FV9?kvTZqD?{HBz4~tdxOmm`fvtZCeEO-J1>OC|%Ko>Z%ZBMoaqf7ogS0 z_ow2_i|{80^KGw6q;eV%*>Iq{ue3|}`)TXgoc1d@$%&c8r;E!hR5X?XZ2&(gTT!fT zHfUVO640;#c9It?zvf`|i`H9i)W!!Ko3k!mUtlh%gmgcmr0=4v*wwxv}FS;e&g zp}jXl_^ms=N61MLN~$U&BSx{WBZ*e0Z1ur&b3D>GG|CY%o6Ay9)V+Qm8U!-x0sh zrF+q688Vxmtv$>p*d`MA5Ld}G+P4Yjg?FrgA^2WCUpdbzAA0!z5%->9O|9L!up)|B zP(eky0@8~Df`sZqKza!v9T5T|O&|ejL0~B=y-PNoJqJM4vhef1x+5VP~!}Nj^>>>}Rj!|ZQ zI;J8fCN`C@!SHx3M*${T`EG}ZO{XYYblCrw#Q#=UIyrW%}Y*zf2vAda&onq zy0I``+n{pr{p!Fj4It>Af)LA*K46{lBbq{(?GI(XV3s89KMoyNpELMC->2aD_kYYB zvf{ii+wFi&jo|m}+^u!_ca-L^SaM+?Ti}pC6ag_1#zDmF=w&CuC%ZC zo+UM!byP{&pm3ubaKSIZ3rkBwd&LcSLTIb4>BJCg>4k)c!QGm;;zHDQ5zf(IQdmm8 z6~cxx?S#3<(lTbKS{6@9uTQoy!3}=5s#8JOKg~K5+Od?`v4~Ze=u@yG3SXzod$v^P z0N!e$oW)<-_wj`*TRX$Kbm#zua?gb)3tfIoN3K&aR|`BNMq!=Ni40mI7j0} z+auv^{3XSO+S#|lTdMuh58swqjf58(g79Z~rc*$UIB^~d{ag|(`~xsjl%U1_TwM8i z;YP)@Qw!e$y>o?6-(i`%v$_p*%lh#RBTZ?w9|d-IW6|d@?h#q`Rv`22i>3kMW8TN} zQpZJ4tpVelmxML)D8-I zJ!jjn1@fK6K#TCsmKM?NUxl@IKbdMju9-8c1?J>*n%_b|IcEA5NxxFs^1{|rReoqi zx`?+JI)svAyJjL-EPL|tB_rX{e?2Ez@J8Uq#b6*dfR?u(ejVd~!TYtH9P9~+%}@r< z0l9vdq`WEJHa(xud!6tE(1VTs<8PU)2Ig99()**P#`?SMcwml+sy?(F%JXC=43iSB zna1}rZu?>@_)#S2gpt~22yN29#xi5!QVB*s2UIm8rn)ou?B2{Q_WfXs&3m}`PKZ5x z0oFRI8DLZE{3S)p-v6`J-E2X0DX(ar7B=%bzeTX+~BLf;`G*K*C zf!%S~3Y;A=P_nZ>3?|sd57Ecj9^RDmo|StqnHMYxrdj@apeS|mFrqN|+VAdnuisQT z;7XVsmd}WtRE~On2-jj5UxX@uUIj31S9z}}{spmGcTy;-yl$XfL_k<#VI=^aVLuY7 z8Eu-=NEof+;9=x<=7=Kg9h2gl6w6ZD(6TMqmX{k484GZ*-8l9whF=KA{& z!yZmS*$;r|+j*Nliz;;d$=I3qN4T6Bl`Fvt(XzS4N`?K;i@zv$42aH-NSg$Dx6*mR z_xAPgCe~E95rUG3e9E(eWOS|TcZ3 zzs;N?Uo`en>^!|kP1g4#2|9mM{;L67cJw3NI{W@tdWJk5AwXh0(bw#pcc!wMQAbouep!b#0+F5K6ibzWSnfF2= zn0QW2W^iO1U6{}^6ss_k>r;=`wiTpmGbyj~3-7a9E%fya9jyS|%4S{-(uAVlqL$1l zC4t4D8i*5LbmhIvul`D3`X!M7v}*cm1BC0U1C<53PgsGuzyQDgkpBsdhmy&!boUi| zRlYW*&_DxA{|MrPRpJ$Tb`U2|4k%Oj21IdDd|?~gJe1x)3uX3FH!@!sqrgw`HeT($ z{xeQtHQ;$v9=yL4yl@|vvblzc^oVaicbGK2R*^eO0-|lT0xeJlj{IN6EGe4## zEkMAd?NQciMvjuKot0rF!wc*zN(EWuKmADC zKu2J^q*)Xlx8$pD7&j=U9VbHrMRDK*GZ}<(mUyaJFsG%c=`ezCW$5M#1z&r=OWpu) ziQET?qV_i`YF4aDoG_JEvXCs+k?yi>2>lvgMNxFf364r4~;vk3t9I>;!V)faYXCK`ZYq%dP#!4p$`%;iH*?fLZtq5>`D zO;_Bfm)PfU;k2fG^!vHS#;IzhlI-*CNOD{lovcu0{`F#v?_l8eF1zV=w4O+cmte31 z77jE`N&axu16_6)&1i)$Ty5}~E8ux2FN!MN`DJ#fed6A>bb{{PGU8Q#Lu^m03e|`8 zXurn)1y63c{3{m;NFs>^CJLsekgh2nw#*1G*8%D|{O!L0(>|?kaao?IdJK$j_w1Mu z8mqJdR{*ni6>jz59AbRIwh|(d=+h2&?bx4Je7T%uh0r=%X+Lu*PBA*?jt&_59R?t%dYQ2(#Pu>Zy8 zM|(T3auJlvHXe_Im)~cReZh*lB$n4(Z;!DMoTD!n1(Y+dve`|~5p z6l?&=nbpd1CTSGwYXh3^0o?Lu)rW*V(bgbV5$T-LAav0vz}CS8f#xg#E&x)PKVJOP z{BU~4KXJ_83g)jIysK;H!h!Je%gs@>+m^pu6ofz4&o6Ih1ExD4XScJD9S|}22Z9}* zq277e4dmCr(ObXsy#MBp|5X5M#kTW!M1f-kflmM^L8L!`TAWkz^#6XWhXTf0ruwA++{aoYftB(NL>y~%sU<4Rb?p0;ocyDsC zCWJQgNNHiqZw1Jnyk&6b!spLZ#aoe5;!v4S14(j{lXU4}Yonljx z8=bpZgMWO|UWuubu78y*RN^80chQ_N=ixf*RxnK+eoC`T47Ug)W%o_ZIW?3Hm)J}& ze5`6JFe*axi zdVtLJ$-_FHIbr>Gz{oscK9FSPFeGhMQ0Y)N>E1cgUTT{CBEq}?_)-Al;@FpYtARWn z+yR8>FeZkyiteP6RshJ_+F^U$RF^T3Dv2#_mBkN)4^@Aro? zo@NB{PMw{;?fsx%3eYQ6`RlQ-sxg?ap~(|!p%=v z$Q=KtH1V&u{p;)hr?>k1fYM)wmG-}c-z@&afcy{7d@caU)z5KLnSWOHzrWYvzBTSV zBnCT;ZuI@T=g$A=%v0I`Lizl@$oW4LrT_Yw^IsoI&Fj1enSdkye{S^u_iZo60bcWS zC%x|f_hJ9{CDktp1M6~0?UUTO|Kt1or!xB=FD=3iAn;SDw4{G@(|>!$?*qcc41skK zgGadW{x3ho?ZfsTagx{nmpjHh8(0_fJ;q_>|J8D;=RY}2TH?0Reeo|%^B-UTzkJ*B z+rYY%?=Tk!{`+vF4{SC6^aA*wtg`yg!%k(l8&m$u5dQat{+p8l;s)s8w$7%%`5*Q6 zf4$lMR_sT@KVJYg-4hSdzv89;VOJn_wg7G+2sMX1BtKKOnWLFkR6hi?>+({w6xkPW9EwLmhxL3WbEkI}%8Apk|_O1BbU^Ahv;D-L%+5td1&(rKul^&zLA5<%9I$D5I&+n~jC5Abf z>u~44+sK)@Ff6I==J4z+pyJ!F0bS$+7=#8Dl2SAd^srXg5J<$$QT#4~Q1EWj`rrSOPlV+SJ&$D|?+& zUQ;)bF9Vktj@3f+xRjs1qXSX0+$Fn*rvl z3DU;Lan1PB$&pf%FPw6Ze%xB9LGIV?ZsP7gcNsbXpu`KMt1a=loB7Q8VMQP zY8C*Wcp@Op2z2l=W_n$Y%w+0$j$OpTn;uTG1{o^b}MxSVy?@Cp_D3K}=!c^N$ zIotH#y)0?%3`{EA-f%}E5B8_(G+k?JYEVmxg(NwhwehgM^(ckgjrFw_R**}>`NrMo zBE9xcZ;MvIT`Rle4jU6q37!QWe+job>~KM0s&VCTE6?whdvZYR}g_+viV=n(xuM$c$| zZadhg)lEiy?`GV|)nEK%y>LCBwB^^YDC~KBVe5wDXU5=_28_N*7;x^#$`&;oWzt=G z$_K?G+j8**3(sRxJ0;gOdUd!Y9`_737psuj(TaY>9or8gN=P5{DPXgod_2WV0~Wps z7zPZoyE)sK6EF*ubgij-2Y!EZa~pV+w3c%m{}N{P;=^tLDHnGS|1rDCK(Jk&ZEwXL zK^S(vViTU6?@n$PvE+PT`pA+~@&j;}aV1h4xYIh%&Tq2G+Z)9jTp7AGq`keEuG0?apeX7(L3|%`S_bP9aO@fKINeLEcrt& zw=3#dft+q3U$@B}(C|66y#%Ag-;v^c)AI?ksl`0!UV~yUowX_;eR|@Y1(d@cudB;G zcCVmRyZt4hD^l)4FPsa0Ju=IGV-C7=cy2$0TT<`bSDvoOi+AJ?sz z(`4ykh5fpEzEq0w#97W1wAtAOQ^3Uuw~ls zy#)xz^%E;9J3k^vBbZ_(3>gfvQM}PrU%XmZTwZVEaPfUzfPDXL8WIG; z&3&iiXMXRZ{Fls+pou^j0NPe?^m$IrNWC|bK6c_9ug8EY&7&FAW2RNPplDzZ@lb<_ z*)#k+$<#T;`uMONPkzWrkGA!1_QHMR@0|>WI4|8axp48)wG(BWK(vbd`LuLBoK6i4 zGe?-$GXR479gvvH@2*iT5IyxF_dO9jF#AF7tp6WZjX9V_lG^zjbZ}FEk~(dznY-Ei zlbllq_?h8!JE30k1G?CN zYxq%@PO?zoP+h$NB4d81=p^f@OYO5k#6U+P5mQ4-EjhOkxVd;dTF2TVRAKswhIC3j z|K%<>eX=obsX#M2X@e@xklOPwG;T`}BwRD8mzrYvgKo+iPf8HPkcJ zU-CsS=2ihwZ$aeq^ar20Buo6b%z61ogC8$`w!;i!9Kv`!te0!CE~#&+I&uZZCMdbj zYjp#v(K8C^?)Qg{mw1OlMG*+Cw8tY(JG&Qx2M1r+u1J z91!>Vfp^BT$rio8G6%LZx5V=|8_8of!I;e4#LN>{v;5Les~wE*{gE%jx>mwB5^O_# zz3P1c8ko1^^#WT>WB1qfhR@AITNl!7fDXN&{mtSQg8_TDXV=~15bH*~xW>Hae@uZbV-_cI+&wzLlZ>~YG9{eTmk zvn6>cK$?oPqQtKvY>~o<$=sD&?C?cr8odgXGTQw5@j16n19g(gtK5MWcE5~rxheNa z;k1gJFIen>`3!k=u+ECKrqBQI1gS!QD_tM^s;A`9pVZ6I5AjpKek~iL^PFiz9b#Y7 zL&&M%?!RgY{gCvn0Gf++L6gsRhS~slk ztIK6&xYWR|Z4Ld0Ph{`Jj)faCMcGqUDHM_UIyS#OZXrHWD3}8Y`je zyh;5Ld2adL7Z%w*>Ujul8tBTPl2x>U|ImC-ijo?Z9kMLlyv(JHDViB5cPbu`K&ntj z$tARV12?cjrMQF+wqJsIG;A2}B3j`mMtBM!2b)^Fiw-pH?g!=VU?*zlqDn~Q_+IJX z{)rjiA*9apGtfK4Y=S4d-bp7WYVkE^z@a@Ke0P3E1#9I1hEmqV{^?r)naxIF+9|VH z-BCm>JCt+YcOY6Mj<-8C_pO#K&=es>j#|Pdqz%%wuD| z`_+u|RT5zCF%xO`Y@74)TT4GVxc1}@?tcr{<_neURzzXbR`2t?)8_WMYpRkG zR+8ht!JV56NY(gED-@qJx;rG7npq$!@lZ0ViQWF`C%-*rFLq%&J(-B-+BFM)RgCd( zA&P_e+jkVV1~uBvIWj}$4Bx2|wg{6A6|Nt_S}@bzE`gzD!dO+-?l(SZp9QrXaf9J} zmXUXiz4wJ1v>x<45?EKFkrSmXd^wkAS_2-oZUEX4XNEC$I{OYZ?bjqjx@NN+|k#-(mWd=Gm;&$(LF2M@152EHv11g_>Q9*5Jd{9 z1~!n6x$%I|%sF5q&IDj*i1P=)lI#_BVQqIcFKh8LiZLIB&2~-L7~b$LPwBGyqvhG^ zrY-lojdo<+(77${q_`sgE@sg3{Iw_#{0Y|fFnT{&`V)QF&vMwPf-ayPx0UP^7U?;> z5vR2JCgpz62(al~0pM+*i_v}J0E^@C?Yj0V;8hH{C%Lrro%e3I8wFhNC1%Z%&sVYZ%tq)~if zAxSLd{cQN20Tqhv*s!C^X09!4)Lf-qC+Bq2jDMCxd!d6SDA8VOoh%}W+pzEYvw2dA zZi6X;MvEkBym>=t8TfWooP9v}5*Nt0eX#reZbv%TLZ zSHnguwp>F#dAw$q4kk0psua6+kobbs(4&mwPaU9$@XAzOx%o|y;jqUO#<v^T@SQRZvMDWy+_4L|UY`3=p_fZe)mPG)*n9gLzaS zJ=g)1$W3!T_tNjDyCI@hWXt zJO7DJuGmYeac9VnqqDo5pMXQjCN(U=iW*zX6T`9_?sE1*{6gA1Ftg9&f>BpFv_=E3 z3#9a1@*9CcPS9@O@>8H?^2fB2r?+;AY8zP3CLld$yM3#iNyl@T~3$#F$R%>#2kFK*` zZ_(N^*&nm<4$N_v@m&h(y8YG<;e_WLg-NBABl@$>RQsMXE{O-l4!^>>%qYp6zh@sx zHYWuQHor=;t+tynbfvERd<}y789i9xd6p#{WlIRR&-^JXK*`Ui;fQc;^>bI_x9#|V|+t+R@9y;k}y&&G=C z%0k*tuXfN@$K5pzIfJ%)%B;!5N}8Z#(DOoVU5Z&rF?VyJhK_Iw?^Qc19&X>5*Tar` zR2p)kL%1mFdxk8kB07$}h(w^W>dgp$3UUr^r^2?v=H7_&XF{*R*1jq}aLW>u+QIF-46O*4_`26SJYg}C?eUZOcfqb3%tQ9eo&*O zS#qA!1l)+aa6D|YebG1Y;3C2H_*AE_58GPKwfQI6p4xuZ7$~4wZg@`oS%~cFC^{NC zK?o?qOHXvL(OE)Mxh~^fI18^Q=4iH*AH=M061Z%CojW0ZI<%->evxXI-5Y?!_OI1! zCGOK{!7WKA&MmcC1ZD7py1(&<-j5`nVBvlvCt@5#1Uzt@V(gNlWR6wbT2Pu^Uh;}2 zOAy*qFWCd|HtcCWur3U5dN1u-wbX85Z<#J^2v!{sG+tb=d7Z1C`&USo$MpJ^a?&bL z;*l4+eKU1M39W-2qZc0KpP1bQ*{f2_0TmOnO;$|xS97I(H*E$<*Uk|$3ST~?N})pw zX-3-yD(TR^ih)n*wI2>P@mV6h!B}rLSK5LTH{r=Eeq~(Xghm=DARuJ zqUvT|HwY1`lg>vh+AlKq96U)o7ISb-axPDGWb}gIdMp?yJJc#*Gck01oUT>tw$&VG zlev@goL~k=o&Su0Z;Jb8fh`3oY<0V-`B3jNpD5QeTqW>&2e)`WDU^g%5JAP zVFiyl&%+G6-ep7|vuK2^PjT#B($10P@Cjj(h<&+PgLq&sS)rk+r4)-c6->;36vPmA zF}>e!aIlgH&YAUnWOi3X^W|w$O)WIG%j(AM(4uYf7l&R&y~H1T?pCxVPW1bcGK71P z9FqjHPbLB=o_P%3v90iMsBQO$d5sv326rV~(;=JVN-afBg~y~n&I^;?MMYElwmbSt z4#5aPF8#uNp2gQ|>@g4C;@;%GeI1ut;T+tD3o;-~E&8^uGS#^sgB>UNA z&-X}IE2FO&Tbg3Mz{` zbSZ-HLcTE=nwSJ0dK3={igJf`WEX`d=9Vp)u{(skFFAM#TeDlm4c9;u9&DY$7ovyy z6>;1H%ohDtL8zUe7md*a$JuRv7|=>iUisuMXX*b$5ko2LN&Aw15=F8F0|Rc z2A+X`qZg2BcnQZS8l|TA2%p3458CRDr}kM;OgMIJlTBbyAwuy~ia?tm} z4nPB_%t7ZBl55{;zW5kAJvfDi3yjb=WbCp!vIgOcbg9G@>^(%#{RmR zPm;x-y)&fbV5r_A533?h-QwydXO6)#;&TKnwH4t+CG$w5!pJ2B8=Vs5{D$Gk5vfpA zuWE_+@{fSxEm=iQ9ZW*3=3b;fuS)Q|+=^0Ng(T!{O`*G!DzDD2gso4YMNh8dD z$coQI2OoO06Du^8m2La$?lCQABj(pzJ~sR>)EBtFu~muNo$QEm|H;A4 zQiXoils=>$Vc}fZqhHkd3ADGVjp7XwL0?0uorA;a#Y$=f+f)sclp!?ogmXxu$qk$C zMyZK3zEp>tx|ZUC?2-_~0KidY+*m%|JNO8EGySMB1)Q?P$qaJX4Ggi6xl)d}DY5Sl zp!r9}V@WFw;u>lF6$OpEu+w8Yl-#hH3a}ho_rHX6FZB`UaK6d3ci!qj=_b)zdPQN% zT@^sZ0>O~BbVMOP=DFs;B(%S{dE+!+6zrs#5JQl5g@)ch%{$qIFmGK-xA~q-1g#_( zxfqnUn|Ln7RqWs?`r4KkI#?G3)TN9p=I_vUAdF2#HfhNjwr*pO-Wy_^6C5AH;Y_rjtK~Fm z#nX6R?)xj6(5T`35D0&Bhj6GWTJ%dxM>Pjh!`{%pV?L;zN-J@a%F)*!(OJ^AU8fA! zpz9565!GKF%>M9g1Wa$=q3Kn?EI?y?C1vyIwF~J)@*5qV-a08l3??9}T2V2*e9j~h zI-)v*z;Bi4PqOxdFJo*QNqK+dRy?6u)=V=(W8DW0){IOb^Y;qMGH;y}?^#T$HgC2c z-knt319cy@IhHkVDTJ$dC-5?_rXCBW>Q$5O;vJ}C>0k|Gpws3r?X1&H)+p1 zlB#2OL)cwuGkhNIPpw*7?}F4V15BY_XHO(E!c!sVR!a&W;I@YL7lL^#NJe4-$tc*wvuPf~qACgd7f{K@Oin_I=m5 z9~+0yj<%4l*GiUfWiO>)AZPtsuI>SHMg6WDARV2$JmENo-|qfqPYSDX9#$|hp}^Oa z-~APB8;w$*p!_iB^RGXgXQV#Fmhj#>@tqf72!jE}TV*ct%p(|aj9d&>B9DOE6Fz-< z@%Unf>g$m}QozXW6l8C(gERGF_S_7%8oPEqJ7vev9MA2s z=jnqJt2WrD^V>TfdrshIQW(|CQQ^E~ImUXw=7T%NJI{MCraV6EEHx0Z!8lyo?oIdg zD~sIjTj1Fe8Sdqe zn+V|@n(Od4=m5|s3$GUNc>!o(bck&YZ^MOc6T-u|)g)K53x|*I$^3#ZWM4DcdVTwq zQgzMgt|1{^pc0Vq5D<=qZ1K=<0gb1|G;_H3Ls>`CecSEnwR%pg+BY{5-n%qB#%Oc% zP(0(5&iS;&_wG|P8TRcFb74C=7UJH&nokRU1Sz=79bE6a zf!nU9@2&G4_^ABhmlfeZQ-bou-Q$praMsH1_t}Tl1kYSUj5b(B=_^(4qsiHp3_rkF zkK&P^Ne&Dw8^K;~+pLxLEpuY3ye>SR8@Oon;^NsQ`M=idkibwtii(llyA>muSvdfKtS^6qz-%I7Q3CKxTmr0xR9GV#{mAhewVM+LI z6Jdxh1Dk4%>ja|JEjTTD0>0R9erT1mUihpMa36AIo$=;%V*#Fc{Kq#iv4D96E^l-~ zLVuNh#rC ziSKG-T9@x^rhBHgG2JqS%%T;oIWZ?BYbcwwVOwzfS>Y>JX*$X!j@BD^igk}gqg%1) zS$4OtQel*S8%`xQ#)QD&W1K-H-n3T9-9|}`i8Rl!0IR$hsq_>_qH6|qWfP*(vYcFW zp{6$|C1i@)ht{)aY&n?%((lisYpv=6=xDRJHkes0)z+X6g6GnHL zsZ)O3ruguW2@5+4X1>Xy2cCg-*~ZV`C)hNb2;>!c<$0=>jSRF@x~7Nvnl4zUEo^Oa zxrTAOwwIWvDM!%a_m|=co0=fJtc*Q!$OI)ytEV)>ad{~L_EV3!#WxMLuOjx-HlKR4 zD54e<7s`wZaiwNt z4xO6{a<6{h8yf_n!eJj$T+!rIpPJ|?dkSSWxQ}u(cwT_dN)4{G@VJp{F;fAS@!=LR zq^PQRp+m9*we8ZYSJ1n5 zg87md8>3JHnAk|0w#{||!~GP6Zh4R23l!R=c~2?ajK}VaOnsDNJbC`=JJsWi52^-M z9#;WB2Cc5LBbx8&zC)k3UyEpQ6lSmJJ5mX4nfFRPb2bOnP~^iOYZ;h#nk*l1{jB(s zNc=QWAxx{c$7x+=1PU_oYjWN%dMgHL{}FMzJU(PzfbNomckMHgdr{>cA-GWky?RD` z{@ucvtQul~+tV(mlW6s%ln2dF)fBB*6D8)>MYW3Eo<=d^EfMTSXa;-`+PfcUg`EB= z?~x}S`&kh9xX_+BW!RDWjv&bc7thEUozb?8~3!Jt>f zkPPb|%&Ov>G1MSJS;-jrhlF&_DJzRIM5=nFN&2lm{A-3PwnP#L3P8Y*Fg&k&ES&4uJ~r5NmX=&LhUuVe!_Bv+jNK83QtpP5K+koC-a!quvy<#w1~$mPjQB*;UuE$z@#qFq^ej zZ0_J@Q&Mq1f87Bv2j42A=ouHDmld14VLdT2DV`JC z0qNR}iyz)-tu8Ya)rFc8XZ;m_>vgwrqn?&}gX`Evxaa0gm2j(5f~8`j$k>r{)FN&g zDU=k9$+2a=%dveW3clI2>oJYB0$o8rvDLcUCBp31hFtr6`hIjG%S)(w@mpn{(m;b8 z!Lv)r`bDbf#<}t8T%vuLR}lH$P12-&)qdf7RCx_Nu?9YlqC}Y0TUXycW>xnrDFW z^FCrRg*qqSt46>=R9aV)uji3{{r<4ZGjTvo@M-rsjo^h$(wLNg9ECl&3nC9Y?xvI| z7X>YdJ2VHe^^`(58}b0 zIss%UgKJDt#1SdT1G@XA)=>(Cr!|)i9M$_$k0V3Q)9ZRyUCfV^O&&kXM>mhD7Y?xFGlV)}3F`L?BF`SLhKrqTwf?9#Pj)7&D(9Du8f| z&1%`N{^l0O;W}$F*0h{u7jM-)r{_R5rAs5rxREkwGs3cBzl(F_^wk;tWa{Cb?(z-< z0@YeoD21jxB=h;J>JQ|?^VD!|^>_ndhVM}Z;@x~Xp))o)z8GyS-6ews!%bgYU|^0j z(>ht4w3J^J$7bx>58v*W%!MyiiaQNIW`I>qOYWxy;#co zD#IixAn-!)og{nv!Iseh)N*Y!e>ZJ?)1v~)Tk=VQs3KAXR&_IG+6Vq!*{p>JF3;Da zt&vmJkR@ts@A^B&^C>IS-ZQO?G21r%)_JjYM#WB&WxYXAvWL#8NSpf6Gx+b zz4ZLmxD@PPh?vS!4wje8q^D(Aa zgEhkl57sA}R1C6%T5~`~@?H)}g`_yymLaxnye5}6x>i%F9D*Mq17C#7_jjm0Y9_u{ zkqUn?f$D96)Xy?rS=^Muh`jGz7gd1h^CwgjN#(XoXQyx{8U$ixN%^5AySpDym9r2C zbZ~l$ZIfGkgT^iYc=iMGz}L={D%j(W?0Dq^r*XCOVyFA}h18!N2_Ir)d=R_nEC#69 zO7ZY%14>C4wPf@Q@AkWJVyfXYndv~%3^YR&VP7<)I4t;7MB|KW*XHbtyl{?~HBG6p zo*Xy_tiVB6_}g&qcD)8eoNsvg+Xz~b@262xE5PscBwu?^EAog!DKFj>z?xg{4c{>4 zyc?WOONde~gbNyxaGuzbP4P0h6WjfHTFG*p)#+x9=Bq)(bo31~P_yjC{UF0m{+!y# z?K*B(-rX+kF&-MivYtK@2Yvx`jl|ly?TN+9F4@(vejTLk*3YJsAGI2#k?(zhbG&G8 zirMut7TawkbJZ*7vAro#2cb8mW6Q6LxETlbAf$_Vo~7=g=s88I34TxFI?E76o23%6 zy+${z#uRZ?jJ5NY_Js4h9R{4xwtJ`tiqea#gvPRzrj?sgyYn|kb}7XQ?Pa`gWsh2N zRLL&fqu08N!Se1Pt{TeTX!gvAcEI?-Kr*tw)V-{`9@Q>f(55#+y4@#n&(YmUGO6y7 z(=fFPv$48fRV%H&UQ(5VuF4~ol7|>rSaxCGZHSIBk+j!O8u?UpXdH+?kc@uwZxWmg z;lhB{ue954u3iucl=|FAXkl5*id9!UP~g*RwdjW&JZ?2g2kBz`<6J&M85mV}yN$*$ zWk&FgWDGa6TNR8z@AwU!BdSct811ir5F^*kQr%`t;?jSl=qCe+2-YXJLe*`~UG}w` z-flrQ+qZ|6NNy+&1RADZ=$DakYjkH$;=A|`+-O8VMe_34Z4bzJdUx2(rbAL86e)yf zu<67%Vws8*4bFiVLz0!5qH=08?_iHW2|J+BPDn2J zT+pFM^bn+}dkTiyc6psV8$&3y{dKBgwescnl+U-=%ii}n`PHgu=``nmKmMrEn>TS- zsWON&7*1@0SFB#Wa`EX+UDb@@m=h`V61kX^z&cM}0A{2&5r5s95(}7vID9`IEZe!e z9v>FMbMj5x$A`n&E@NRve#kz%$ITUzG-KXo^wg6KmZd#;Sypv;vU`yfa>}P?kPIM> zEA!)&k4&oX2%a8>0^gkg<=GK?)4eJTLjvse``#|02`}acEA0X|1I+ROG<+<2bW|FZ zi`=*<}-p95@C^`8@d^$BpnrD#Oj$-1tP<7v`KP+#fBt68I;c} z*%3Q(>-Z`hI00msc4Aq164Y-7TKbu+9pDtep7Y)Cb zxBK0dKs8`k3!0K)9Z5rhU2Hn_l>iR3#)jLwWeke=M2H&3vhMjWO&h>vPwEaALEGo!QxJ$GoyLNKIjPor@)P~f? zAp~0J>(pQM=*(kRRjGD0RYmS zl(;SvFvDO{*jPi%sck^oTD_JyVH}agdI)S5c5jieTsKfHZ3`Ytk!CLsXq}oN_~IgN z4aa24EC=xpB(V(1@pcOGRN=4a&*|^?zdXOd|8!(w+b}&;C{_S}L<#5wNbpSlhKvbG zeb77*w!aCpXEqC5^~$&2eEZNl^pv?YfF-&GhIss7%SVYcvZ` z4VGLTAo-P0_*QI6fQQ6YqIM-lt`@0hQ5X;Xj~@KR=@7tyvcpHGXvD(w@)s+Wrxbp+~U}Ft2&l6 zrOPQeZZ`DOoVb^Vi1(P1U+d0-WezH2{uT-swvP*|KE~<#Q}7(a7e?K+A(1=W%%A(b zmC3KZE4NguUrhd(SK8l+{UD`bSLzfD%;)kIXeb|DM}*1HNLhQ z|Kxk)K;UYpG~CB7Prt-w+)W!hQ9%M$wFu@ zUrR@r*q-DrD1b4QOT^SNS4_g`pw#_NR&R(~-iU?+s=rqvCm z={j6s(`v_B9`wyJG>7|>L8fqRD+@Sy0=~-wFiCmVlAjC14j`XA zsX{IL+iVFL?z1E7zF@EFcyv?}afZ8Qcr1tb_|+lHdh#OROKW}>KO|h=s7CnDn!0_=xJGovonHxd*jA$WKkA|Pa1OI20!r*99Jj8~w59F^Yg>!=S{7qIT)!g4KI3L4!t;#TzsFmQ z_ONlPpwNS;eBuUjo$+qN1PNmuwYLalEDJtcc3Ln|&yWtlLufc3BSc~KKjzcPkkg7a zC4+^s7wygo9RRXFgXU{Y#sW=;p)dxagNnKD#f|Ljd z_1e4dCU3mw1_Ts+BfG_j(@gv9_x@c;TnqOm>%H53BijLE5_*Jt%w{@ku(=mEM~Qh%^O4q#L`^ z1VXP$??{yz7?cv4KthM0G)aIcAs{92o(puwnYo|me!jK7Kc4lz_dl$VT-Pc4?7hz} zzkQC~O|0i)lJ8;_j3V&M2Hgj(r1KRd&B;Vw{l&x)_t17eh9Rb*{)fHN8-!H5m5t4I z>#e2R6v4o5S=bowTXObyOlf=$K~Z7OE0p@sO@mqL zl9~MpkPpaMW5qt37%OW!Jg4{c7Q0&fleJn*)jEw$=j;03>FT!xSfIyPLSUAbc$$Ho z=l1yh)o;_)b!8*9lm)jU^|~`HZ!JjnzqXZ;E_63m*;6KHl?K;)M$=elYZf;;VXj+s z&lc|9wa(ibaZ7J4u^Lj-+ZNhb<&$bddS_8&yNI_FS7PqYuwm2N&kHtI6y19-h_9I6 z?21Rg*IY9FCCINLcqkj9?JgjB(dDK@p^8)DoQkSw$nSGd+8A0_eSu-*hIUnsYt)hp z!?2FVrDjOh4tO$4e|?wm@PPJa=E`zrSUaU7RDO`ymHg>Y*EvkIiTpW!~+gj9X z9zB!L{GR$*CZp$gP8GY$N*lxdgvm>)d5#J3Yez`!H_o^_xKnA21mg!Pe#zL(6x~i`?guz8Jf%N+q17JA3Qo zVYz1R&l__(fj(@WDMuy(U=ge+QY#)K$f#+-4j0R{rmA9i8LnJNZQbeqYo>1dpxl`B zY}>e90lB`a;7Jm5znM0Q=+TA#d?N>5x11r$CUdV4SJF($uS#xOX&d(gfvA;649R2R zf;QV(7$woVk6*gW-KSl8VYPJh^nh86ia@)W-9mMDLHVdHPBgN+nEZJ)$wyZyYAB!| zSa^nVEnE`u1nD`CPiE5zD*kqfgij&^lulSzHijibac@`EP_Y#Ev2EQj9|N`%DWnDK zZL;;MwqB)ex-e%;wZ$QTPz}P2iVkDWtVg+UR<2URRZWtAY=(Ti~ z_wY%LhHPxO73##)KQFWPT3g8a<($rR>v_(Wr16Pv`>N?qMTB7RQs>C7%c`Qs;J9sjM>TX5l8uva;IXrx%XU;xTsfkikBI+ zCe?`yB8*bX!h6#zv0@_`jGhrAsCJ@PqL}AUH2-9oDuqxj>S-D_6;;DXDsnn`0Y^=U zmZPqju~VcvWoU!J4b}RKy||sAwqDLp%-+w^b{1h%b*jqxi{X;y4R7`}uVKe5EQ08Q z4tFda)ot(d8tRJ=NF?1H5!33+41Sc*q5@+pFIZSV9A8iH;W4S}{6+NPQunM(k#@-u zNui>v0aERhy9Mr|dZzsXP6Pg~1=B}10^8AqKkmnV8H5+Exa&<}}XdNv* z@ZPd;BS_=HWicN^_OMaWZvuVX=VZdJwsZFG4{1$KIPGx>o8QN^8Wb|fE=5?tcDRjx z4@8exJhcskk4Z7+M+K&3-VNBU^Y)xe_`E*SO>TKzPP5{3PX~&`k(dYNx6ktJh2&Kz zPnq33swbXdLSjCgtGzk^+^hv2iKK6Rx$JFV%ue6K^UO=E%KYG7s#8}vV14QGR(DL$ zN$poD>|^52invbW3B}ba9n1SmCtj^Zt1dz+LHT}zJhi&ND1w<~?)EkG0V$UAofkfH zoJnCcaoy|B*j`6LCPM*1!-fMU1X#P7C>V#P?gYRK7o3)fbV%m)2i9xCbyudHp|p#< ze6<9My*BZn(8^-PP&a^zn#qDAOh)U&EGPTdX98gd+St%lO)~sRFmDN%EMZABEUhhV zB>g>8>oa?hbn&TE=xd7zw-l|Kig+(l)=Lit2)fs2yrRItIdvM81!7Gdk_;_9dsN72 z4Pvrp?DWwqXw2FoNJV4qVzJp2eRJDGJjxTtz~G7&ax8%)uvJvh-0jErgK(Cbodq{!zqTnu(-dFdSK^K8+sZLB1$n>?YJ>FT7j%ysVm5H-HeX4mKS;V{me_rn6kR5O@4Zg57su(E_VYWDU}EvNZNPAl;PO@3xbYoJM5@Qll<)+ zuMy;w=P~NYe=YWV>JJ}$N0@&&8KzY4B?Dad{-O9zdzjU?-1HK03ZH&$;mbP6J z+2zvxRJ=)^dj0_$X-ey9P3|MN$eD$N_AkYj4j&ayRD>`U79WHpiktpQg2u3w`xf%R zvCpFC+AE|{ZCfJ^S>O2=VM=$PAwH_iZ1nX8Y*ktgR{ET9mAk ze-kyi$|5qje@>ruB>U)#upjOI*((pEb+03tz@#B@%7-I7H3^AUMDw9_pw(?A>E;1k zv>N2Kq{d(9+zDaPKIWkwR1%U&?eyl)Mt(F~eqZg_F#V7JG=z#lsVcWf-D?nI{V#>z zBw#P6jqtW)*?!R8XRbSF{wxDfqF+$I6jfwU11Bmvv%qU7+44qkTPxVW&6Yre7%QJS zTio`N=(am-wFXxwyZCl8SHXA1AjUetn+(DYZ>j;z`e=gJXe~H5!D0;XX7-|2Wev8d zZBk8XU6-68>hE|x%mRYz<$*%!Uo2-pooDXG_-Z{Cv5z)4NTm96G1)obWea9(S_Ga` zSi6MA2X1w54{hdd$$4{kr^_F)+uov(ace|KPk%~K+v^VnheKs%V)4v@UzrKy?uvEL zoUvOamSuf2T&*`Fzp{2(nBd~k#vNmTTkC?aYYuIvncybbSF#XqBK=rY4Oxg=Jy1V+1L{;i~W`L zpstyp9F>@$dX-MOzT1CMf6TK_3mb#loUSiLRvV#6*X^a}(j)P;v{-(9+B#Aa(PE&f zB=F|19aEq*&X_G4_x6dYO~U$sEse>V4Fh=yV@>q#_ATuHTH2MfZya0=Nh!&%{3t!} zD}`w|-z{T2MJ&TA-cK*A9i_+HDTxm7{PdjHXoy#DKtn-<+&V7de2P3HgZFZq>6wOq zNW|eFnfn!C(YM_zO3@qJiXgNkIt|F4MwY-GQLVKy{gpD?$j#yY_h>(G?HpRZPr9qp zk~p&_0K;OG`n+N)QZTwee$8LVEyEK1JY17!y-Qa86x$?IQbYRo%V?XAN~{#+>kQy- zCiplKB~9}z@0bzd$x%N9jKl;qBT<+m5D0F^)rp?408h3n5vhp*drjsJ`wcd+8$lr+ zS*|^Ti1UDa9(i(ObJ|a%Ha{K>yhjFq-mXvaDtWiUElKF#-08QWu$+39v77o@m%{3( ztWD->O$D*T>8IW8@m5eRE^8<<0Rc0LpPPMgpg*Uf3UNi4g z-$Gcc5n6kd}#UY5KlY=TiE6P?3nb?nNG2+)u?0q}DM-P&^I_ha zlz6dKFA3f-2L*yD%*I@yr+dp6g7pxIYiw5m6AZRe(M@^(&X|0sbxo38Mw{ovIut)V zI(gOd^H6ty7u#~rrWk3HfZB2*H%ryOIWp`TFd%QONn(v6VIQ(Dr`q>pizvfIh7fQh zFK{g;P-co$>liIa7~z4*w7lDRuH*N~b~Xd~^{LXPtxJsddt3gbM#vCfNV&f-zV1xb zSgIANmxXpu;D-%^Qo_>04G7}jS!A>g8D$npph`B6NNOqCUN17?WuEC(< zy<<=vB%L(H&D|;_#a|2T z_`FKTfQNG&Lj3!ORNVk5hOh^{*+aRA{FClBv*eL; zsNWPFzklJm->iSKb81PA9d`_L8jHHW zCx6XtA2--~8uaMW@A8IN%)L`jHB=R7P4b%icIDECGTzJ`KIdGJPfr#So|&mKXx2M?)=mn?fzb(yMX6mHdzzE25l4D+AA z0efjm<<#4-^5LmsS%81*C3Zc_JB>6?JCbX*8J|U}mQ%B2FkXQ3X7+F+RN~+!>Jvr} zh`p)tHU-W&3FQoN!5P#>4a%G{hND<2M5v;s(_cxZMcH9;gDcLXKN~uxtAmlnC~gdO z_b2(XyyxFAIJqL0+X$s(@sPKJ5~2-YLU|7R1D?!(c;H3^`O2)TYOVTENq4~JT|zzz$icw)89#GLS;}UMdG=$xri>&r7tlQODi9+?dGdUf){9 z+0vdErlCGI8`te;KlU-ohD^POn~y6^YuYc4bmc)H!~BS-7Ks%GWgH3j=1^CwK_XKOI} zVrI>jMyj2ylD9JLKWO76>-L)Dh`S7x*iBq{Nw?hQ3Jt(l;@G#C`#qF zQ5e-oM=1d}*^g4MpTs!aNS??Gz7yfE09(Yo*I(6C?h9Pk+0KE`N2Xyf%$5>XoE{v| z57fBkr^?p86*2EN9&JVSuc zXG|A4IJc+vXR;2s*Cjelvs&w;+NvaAnTbcYR@}BL(4TucF=Vs1gtaUu%Y2Q5eu+?$ zkr)?(yJa9K^dKL=-D)>gIANhzF)td4fIqJs9RDQTCxeS@fy)1DNqP;*hXeu(WY zolvVgXg6RX+eA}Q72D?sLji#NeTd)_$mOk7RA70N06ftjEOddIZ|hh@a+VI>I>vHQ`v2L>>>+SUcf-SkP4XGNlWD+T6Ei2kb zpU`vMNA4S1A~K+Ja++Zt<7aVgOM9r7BZ6KMsDE#v_8rtg(sQCOZsQ0YyL#3Qb_Jwz z;zj(yCOMW1&P+b?a-qw;`%0& z_IRc?URD?7(I(NY%WsT7+Nu3ExH(Q&fbngzO(}KeazD(-`1(7g>f{>?x1vbK_|Vhq zAD$X{Xx!Jr3(z6AfLBtvzE-SMVg;0no>IpRl31^bY>4=}u{ie!!5mz+_L@9l6>p4^ z4eaM15nH3orEED`DqXeNN)0bX%cs?bns48p=_yxCXfcZEX(=H}sO{0BaF8F*%FoKc zl0KofSGUJ=lLv&XM^L0eB1TXHE!j$3(%G8Q!6zJINQ8lmNq)2x&cPF>v{9;*hn|ct z)hWN~Qt3N@yOOnw;yJNs~(S~&P*WY$rej9IUcUnU|;#laKa|EZrO>f6hjr*G7-vm;4Gi=kT zf3x$E#mh6@Xf%33l+fl}C))*=iTt zp4r>g#kaQ+-_GmModcdDM{y6e*g+drN%eEr01{35Cgv$>i-0QJ+9GZTTxh+}(jVLq zNiqgMbwq~8DSkm5#kh(47@;IOJq)PaH?W2b_Y%ZrS(E7>G;3LQpKroPtk;D&Y%ZW=x;8MQEy=r-s@ zT$j5g^25*#&PF0zj?X5JCb-va8wZk&{fmTaEDpgHwuAMVPZ=GZ8_2v6GU+!!c6ALK zJ@Em+D@odCzbmh?@|_*qao*ZaX>KWjwbDv9-||j8!K{C1C(8lpE_&)=vVh2M&!Z5K zFFGS|?c_Z%XYa+#cn7rQ;#HP02ZkJZvts$%#Z?_krg52TV`W=i*}qWqWCzgjxdVdy zAuLxNCFcDPTgGKtPgtypAPgPvsLwj1kVguaz=>-k3B70fi^415)sn=%uStI5zh89Z zV}os7$H-Bz#@3dB$mtcrgcgTMKj&UJiwd}?1v#7Q(r-UxnJGNN4?lNQeaJcjUG^}O zBm;RqY09#i5$HF?IN6yVkxqlq7|H{`cF&x&DT|1Pl*A^VzEskCt}flCE;9F>3=E4; zJ9ftZ?k(R?eps95m-GOvWJ9%SGYI^>4p{D5aD~@g!qc%(1TJELpIb?2c;);s_<^C` zk|u|49LjIpkF6{Oz4amg;e}bJ4LEB{P9S;47@wQ5IJ{Z!XOzj0i%QM4-SAv1bY2xB zN?6mnJY14vk$7RgR^KiOO=H1Iet7Fh=A8Wyu#G;SeW~l`&ZwuSdTKt#og)8PTIMs! zcHno*Zl&o#^DZq4S~gq62!AH6!ZE6r$2*Z&J>E&yWXp7kzg+*Yf+XJ+`4Fv#-y7*l zV!`Y;#bYgpKV`lNjG|nB*FExhZ5|9aY_P{;rk7u1}?4W-b z=%&u!fmgL#=E~<1QJXQ_dPh}KQWGUqGa?01bm=`1*InYuqGC(0$1?PKD98My|I)MO3VuRD_?cd1a)*PW$He?s9rdQG z>`mf1NOEgHR1cS!*+sN|f58Hufm2k@;4kqajZSy3)JJyIOqusPUE7M>td+Pk2E6eT zCu|G@bmnTchxXGDwMs0a0n!$sLeDo_ZhKLq&Q3VVI3xMPhX44iygP+FFc5Z8!=F2B zk%c?T<9(`Qi-KBI4-fBopIZYSnnGghp8(cHPl24HQ=rzR`2~)TocmSFUx-a9Efb@* zl>G^VMf2IyHM}sa*GS!BaY)pq?8G#BRG6_r3gA!}f->P^n)NU3HPmi8uS3s;tQ(K4 zJM%XcYm@IGDRuMPO~ILzF*8);5`~Lk@vs6Y?#8kZp%frIE;M?)=)2;LEELsut~IqY z%aqXe$;3c(Rd!-Rymu6e7W;m$e~IC zXBZitoQXi++Dl>oD4A2s`zEm>?ZO3;{QBoKW88{wt<9QPqve`$#l==md_?&nAGFIoidYj`~>BU=#ev+SR?P~1P!)!;&gPo&mk~Nyqd3YNLkRYl>mC>=*VpT}zJKs$IS7XBlnH3hd8u5TP-Ov2ec%TWaGSKi) z7q*J0D?}@D0?P}!qs5Sv^2*?8e^B~it%&LC!fEX$sTVtZ}W)zpT%#K%xWla9^$j2ZTietfeO+79@g20d1IpBqw zC{FslR4V~-hKt6)2(JrsV&P7yAjqS|XtnfZ1sE^)NdmuCDRQ57E74$9rAC}u+oFaMz0dcu#+3=vTwu>URjxj@KuDUE*~)B)KX<{3w3rm zx!dYCI-vz~@+YDhc9l8uEfmq6RShjMS3d5(VqV2|&&+t%P7y z_QN`&DgGa}xx<-Kf)zsiDejCM3(D@0adSep5b@19s?jg)OY8t~H{Q} z_@*`3&JrTM8Mu%WZ_{Tw_e>Mzf0Ki~k~iW>s$?;RB|Dmti<}d538nm(sFpyrjn7c3 z!AereSi5ImehX5bX|4QT@P?ep6wE*pfRCOiKZp?Pg}Hu4^I`k6A+`3V5?H2DFiiEJ znTr{B(N(tD7E@vXK?d&thUGvlLu{nP=ezL8+j|JYZr&Ih zse2@ND!IC|RfiK-W;wu7UR8!<1WF1}@s|og#;)_~<<2J@CiCl9Z z{5x*2>(geDlTw?W+MUXJm1V82W}}1(=o2A0fK@y12@J2ZrJ0v%re%M9k zccRlqvn@_$mbkifpP#!gF#TD$39@*?{NlTx(nOHEVisc8f9rA_5~1@EoNh0}0R(d% z^+9eN#=o?ny@F`jpO1@i*RI~eW-6MaudR$&J@jmjaq$|mi(y-W<+PO5WlG;Xqp_Je zSWaOR`YIqO3v$s09Go2X$kz1UBfT}bRS2S^j;|S(=T04*rE_)q#PG_d{tQ8n*=Z!x za4CN3ZoN?Hf{)Zf^`=6YpGI=zGNEKKtwVn}Yq;WKXdhGMEyWD^$ikRrl#nmW%46LX z=Dt70=Rm_ru1ODQ(L4|uqIG3fO?}#O3&k~KP_Vb3&7_Wc?t||ZDaD!sncX;!M5xw? zEad&GrZ}ukkQCE0DGw}fXOY6K4Uj7Ky^wuzqWHOaz%|+YUY3OYZzvn9#-O&y*j$*K zEdeAX3f0{1eQP=$&{|u?C?2Mh=!s5BuwDbDv0W1qFBXT(2``1jhMT~(h(4_jol>Vk zyp#~mv2>!Xx6Pt@cQ=UzHz1~*5Ml=1uNCYV;vMpaoEyia(=a9 YH)yj$bN^e1y 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̞!^o#HMlsRnE6D6NO!Uf3>5~ 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

ErUouM8SGYjstyjdeCFN=Ud^cK7zw;J(#o`@ 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�cUfA;Gw(nbQVhD7YM{~vzs&WJZVgq; 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 K=L1{x`F{bljWn?U literal 0 HcmV?d00001 diff --git a/docs/plans/miner-ui-2/03-setup-key.png b/docs/plans/miner-ui-2/03-setup-key.png new file mode 100644 index 0000000000000000000000000000000000000000..9b99cbb9d543eb3aca0754efb40c80646f72150b GIT binary patch literal 247363 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 b1*LzE2YyCt_w3&U{JWs0rJ8vT{rCR@k6n0* literal 0 HcmV?d00001 diff --git a/docs/plans/miner-ui-2/04-mine.png b/docs/plans/miner-ui-2/04-mine.png new file mode 100644 index 0000000000000000000000000000000000000000..d4683fe23387c5b3baccdeca7c6d8fbf00e022b4 GIT binary patch literal 287577 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%0DLYIM~l_$ms)uLBGY?kjI`N&{pTrpdA&jrEGVv1 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≫{hdn6e{ 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�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 literal 0 HcmV?d00001 diff --git a/docs/plans/miner-ui-2/05-prove-off.png b/docs/plans/miner-ui-2/05-prove-off.png new file mode 100644 index 0000000000000000000000000000000000000000..df7c3945a995a1af4eb3a6de03f78a0c4fbd09e3 GIT binary patch literal 346621 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%0D*ElxPRP= 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%0Ddw z4Fs#WT>SINyf?Q(*4=hyQvtf0DanB^fn`FW$$6{h^o*VYCF|*Bp;Z9No|EW*W^CZC zM+tPyo8jA{N-mW;x6;}wq(%MLz&6cOrZD?cO~JWaW*Kay8u&I<`vbaKAca zdwb3bnUuIeS}v|{0@oYQeNoIM2qIJtdIU3R*bn`kTiLYha0iOT#m>$$dkpsmqE-f0 zVJ{A+o#zj2jx|c#R*5*F2|)JC$CL-3h9igMaCPvxKBe#dh=iN`Z<4LUd&=`o!*On+ z{!fKd!K0aSQpx={nCyd91z()e79f=%QQGtEg)qfTgTgrX=f!!<=_Di%cnXb-P+k%)3vV9Vt(O`+w%-Fu zczZv+y<%kDVu?Us_c-59pdFU_0+rym4jVEb5qU*nJ){?Io7$a-QC?FPXfR>NNy7C` z#$1c8K~j;+a0brtYLr?}XqW+e=WsK6dyt{(+0AwoV#NREys5~he}FQnxo1#o5a;Fv z!df=4CvYAapyo_Tkw%B;KePYyuKnX(8J*i@^f(wbbPRNfE!^&NwYC*5vm4CJe8>0E z$vD1gzPqB0be!y}a2SK{Zu}A*ofY$)Xp7-JF_*YJ5T|T4ESBetL5_9ZdrIIO`uW{g z{R!7n%~A(4mp!1Q*>^sWqhaE>k^gDMBO(ao83mZ6bZh7^YaDkMISpsY{0^3a2)Wgf z7}Q+`MZk5W)~HAGG9F*-cs#ho`uX*?kC@&LE^;9MMJtYDL`E5OyY?JO!}?HUtiYBx zTfJD-e44au+l;`uX|&C(c3*&>Q!`I`Tl0obqVIG4Ef5pwFluO)bX?!}u5Rf}qadQ@ zTGO9jI>5_Cst`(q~&nf@x<|G+2#!R z%iMTQO(!4HNCz^&W2=H_g}2Hc=@~!ew2W@tOa6K3y2e}J{6hT9WY@V`uQPzlu|^_J zKoUvo2KccRcbcX-j&SSNYDmyjh;RyKS5^2{#WH-gzD2kv-O1W1zE%>b#^;6Z3?YpHSA2Za0Rm9Zx_N-Q6*DepKHKwYg zR*pKwd82afYvfs!x~P?IvfZtjyB&T}uD}<~LleF_SomqGf^V+Xm3PceGp_KxCB!p` z@93-M4sK~c<@58Mq@H)5n}f(GwTiDB?G2g54BRebf{A)g%qJaZ3pALL!a^yPu5`A( zsgzfYHWAyhs^y2We)TGxU|^>d(yg7dV&ZM-e=8NSJaW49VpvO1v}FB|bBZ0bfgn>U zH=#C+DMSM`kSx`$#%_B^(T=wjaFF;qb<*cnKir}p;;+m0-(TEqiA&5CfkZpU5FmZj zGcdr3LscL1BY(=tdD-Gi%+6hI#y;4PWVbNU#uF~eDH+9d?HW~Em(p-tzVan< zxOy|tX(dEj5T8Knc&w-<3DwCljO>=J1aUUz`mnmkm?is=x?2(Pzns!H`(0hRZn%^b zorLA3&A3=2yj)7V7pmS|!nIta9n6-^Y{D?g)v{+Y_OLcFH|u&?%N3S+Wn)Yi5M{w` z>mM|H8+DbD`~}xM20nn?g3|HGdqKp(^A|nzxiznV@I}J+5NcZ(8Zw3-6V7-`aq>4M z#zTGXis`@-myK_-ESU}Mp_zy8`-=iDoJQ|Pp4L?lWVS3OiZ?&uv0m~wE2&+P3J|y2 zqn%`H@Cc!nUn$tqNOu)-X;8syVI*PQ^qq(SDmu^gI6qyvx_(*9pa8(%@-qmBv|Xn62B9*y%ec7xp2_V zr8P1i($jv6TN3)A@^V40XE@xss`}vl!*xFd<54nnatb1zIR2nGs#X*PUKu&PpgZSw)0T8qd{=ll!OfD zYXze_Tvmn4MgH+`48FE-yzTk}0%A)J#>uyz>zRUseZ~&))xaj!#pYy74 zI&ZRvv^F$o=nkn$Q%fSgPELtU&`YPc=^(;TpJ~nWdn|Y@cf=6e=m+#k;gKsoYBa$|#6zC-bZvF153pgiio zx`kYnx%x3Fk#g~gy?h#DASo+GM)`Iu?@C2_h@Rb3=p-K>xGk?He2OE-h<&O^Y&m}h z^z7qeDY3@0g^0|aO)hBp|xj|K4<`y@;C>iYJR6QRYXDx}d400R!}qNcP4Z+o4B zhHS9p!opYibjQ(^0iL6>^i7-9iC2gtwvTEA-A;C|2Xy~9xYKYDZr^s8W6dBtJs-x0 zS5}PUFOVWvD-Ur-E8BT0oIZ|#QkOB_LgoaxNB`m22KRUHE*|2u_nwOr2W&8S1>-?~ zKX|2w4;MQ!S0`e8%YUe(aOvQMleu0rO}xcREj5~a-MyT1CZ*A-pksO4GXkhkg~@~X za|UNi-{nwdXQy{^39_F*hkAb}Voo|8jI}*Te5Yytdy3oXAm<-vYLO9VZ^a>zPo>E~ zm_W6YEN3vmYNO91!25Fv$0-&x3WcTi9S%!|!5doFT>AjIfLFJdQPbRNYuJnjQ06XO zPm`Ao&!F}l!TOH#TR^Mre3!cs=BlB7S?Hxi%%H(oWu0Q=k~{oNsf9h?a3PtB+&wgZtSc zCUcLTMuk2hhvCaUiiZ0J^c|f=&gdDSX8CPe)XGp$<>o}`HJpR5srH1+6qGeZR^627 zrgmVpg2+tPuLC01%^CWTj_r{MqVws4+qCKEq8vV-Jp&pD(_ihe)6)8!^r$rTpUU-d zaQlsulupy(pKt6l2jESY85Opk6-y)W1`@GG;xVaamHL;KmMXh_j6U*OA$_ezY&&0J zR=r52^4K0I^z5ck;Kb*)4%u#uSb~5!vkGZJO9)_uGlzL z!^Ys`fyJ!Od%4ovSni6YL4rr5zcKO*C~58))}!GSqVvcXl%Ft@SSkFv6Pw(8a$0;v zEM7X;MSVmk^v>DmW6=L08%4zH_{g%itxtzq7awX|!I(SjBSgl8dOfqj9bWLJ%?I}a zRxU3#da0d&B6niF%2J&iQ$B6{JZvlnA){!rF91)|wDxV&YasiUmqkTi$fry_K)w^e z>ef|uN$Pl2o@!Ws5ocJgO#PVKYi%%7Z1e57TpXxlX3p<1uR6@(bIp*%tyZ<}<=s=pXZo|NdaoHBB$ep?Dz-xUKY}r6IQ|h{l}A zoWExVRvM^R9tmRihDnu9INy?te$uTA7e;BeIaZP!%U{uvb7B@@iT{|23Y0Ht=v-CW zk+_@}pE8I>^ZSX1wYoP=nh9Jk=nPaj$ZL>93;TFxT;ZO-Ne80esz}N}jBFKep25)M zN<&=G(o#Nj)OwU3QEnezDUZ+8pV zI!nl`mB1n7tsU*QOg;ZM-yYomZo`M(@g-Zz+#6|NUA5YsiR<9TX4va#S5&aerx(OF z5sH>#jV{$la}RJ*JmZizBMj}?Y4^%=Iu<~5O_8#55Mb; zv1&D&!(%R&{ljIpSBS($9TkUBD z$tQ}E{Z5J+Sj|l(AsvrWzqi)?sH+nSuBQu&6k)?t>C#LFg;LS_Zu>D)6=T8Z0E!gT z!x!h-TXQ{Y5OK%k$4P#LJGod-X@CDc*==T<{Q&t@;P2tV>n`i&MH%B5R8(G1@rs6h z!Q%F*?8?fJ>pOir5_rB<9s;OCZ9=5u1fKWQp4^3wR!DMoS-AIz za5lklq_!Z`8dK?CdCzu$Q7M{Q{l!}J6sJCQf5qyc<}TkVfi{Q z`bbJADJt?P9hb_(buD~~ZBNdJQ*S7@K=rWh@f$K%)9BDvmGdrWR@x>$(^sFmG?s{0 z5}P>44rz&p5Li1}AdbDT9SnWAEeB3IRwBjiO6BpbCu=^djA|wE!IYn1$JY9=%Iz+S zZe=}5%Y$#}P+k2{1NqGD?OZCAL)G9Y_Ruu{x5NxO8C-LCxVW^L6QJ5VJ!7%R-;bc9 zld*1e}&Np`@(zlb`AJzTP`p#ax z6l#DmVqH^p*nQpzZ*=JFlIF9*VkvY@zXJTjB*o0(cA+eF%UTu&V&c=8&T|p{{09-U zo4vD$4q7G?T>f0JkyNr;>DtaH={UEi<7p;TViT%t=2TJr#p2YFwc6PHdn57`>xw=( z4w4iUE7_b{pepn2icbq!!nuJ!R@A3IE)B(^GZWE&%ttOFR9#UJkp9!y@+T{CZ(AfKQ}@ou9A0u=#7KMz5`yVajW6 zhqZ;wMS1{}x~j6o3QxBEghn88O^tAu>twB~ly2*bq#8c5R`qfBIt$IL#T>TdO7L|O zkoVZ6uo_W0heF3$Me(9uBs{DjXV|j(Tvb!A@BBV< z0ae%K6?Jva$-uKX7lEjnvpJkIP@cUC>abJ2{O~eW^tGV;`jeHm$tNINudFL@7sL4F z7U@>O7KWvze(4*tkHu!jmq>~9o%Nnbt!6WXKAXchnEK@>=MflHUs#(21IySkP(ud4 z6A_~-+KcAyi+t{`a;Wt)bFT8Q@LGVY;%qFRdSw$eN|$mw;9f+Sz{Lv8H-jxQE-Z{G zHQj7&03k5ee4@svbV`bI@*PP4Hq`GP!M7hooMG^R)=L*cf)@w$j8{o~ut}*t(YQjt z>}5aWo~mhAi09s+OJBfZ2Qwg}%|DYf8r1>G2GruDi@QR24Cq)$4*`ucrNx@UJy4$zxi*|D9Y7=|Yn*XaT{g6l=C_HWR>`nVIiE&kYV9rjbRX#4v6cKT;Zht8R$NhX?x&x0< z_gML&Zt2F`>`Z^RGnPqxQ>o3&DpI}EZ&GBq`^6VhQ6^$&NaM0I6~%I59JGS84XSrj zHa1K{)_HYmp+>yO?v`+2R*NqH2t(}3h=`b2VJkM+k`^vAcO~(&XKp_$3kbHy8R8G4 z1Np69$mfX6>%pKT^wW6M8r`bNYiK;xsj@h%br|S!`6!)N+_7B2PY5WYQGf+6taDz53JaI{*nowi=}tVN|;$lOby+DTdmeL$p$)^ z5o#CK)o`P$6cNicPKXaKmBp53(;K<9p>8!4I^iSk-7X`&5AQ#ai7PQ1f8}(vp_s|F zI5@Q5BuClpS62|O>FODFRql?2xgnkXp^}4jl%!NTJkyN{fe_s@SE1T^>5BZ;lBypQ zXSv^xY5a=>{WgfxY@em|6-TSoQMN zs}>rb3hvuJJ)%=nnsc$v&#K6X3@o!BSE^@M)=y%*5M_USC40*)%)m(ii3Up>Ui!(u z`XGNC2imIfeDS_CBD&ZG3O6o^DQ=mr(P-Onm_h9s2+O>dk}4@mo2shZs?tClQm3xu zcDT5XO#lTozyPrQh)B|}zQ$npF_bX5x(teSUgO4opnLU3R&TpB8@fD0%k+=}r9(y5 zaRo4!F8Lqt=^Wp()r`z( zv2ISyb7V{$WbI<3$@U2fs|rT5RFRLFN0w00@%ElO*0`xDv5zt8`jM<7)$m{sZ%%Fn zkN5A@2~h5nOiCajH8mC`6dFh;sa3reh({+xludduyH|)|uND2(CVGVQHp1(8zUgej zhCeet|2l^}9=9kg$i;1Pn>1Q2kMOONTNWo5vm~QRzp05xnS&_ji@*nTpUTx(OnfUV zk@@!ra9B>(vR0l!n@NsG?mG_(+jt%?UJHc5-;cIj2aJnF51gBz8CjPlrJEq+AI8Sd zL~qZ&*eGoIlu=DC0p$t641}9uhq?p8OEu1}0XTbG1IhfwZ|p|>BKi;JIrz^f#9!1y zhD?LP^M|v6PLUOk;Kd0lqgu@~=97#>DDPTWWF#FYHVKKIo%|LB{a4Dze0=ITt{07W z3J1%yHZTST5Q67UEM7_SCvlDk?@Q)V;7}e8eeNo?3`QD^$%tTVjZa2<%LQ0(vIa1_n9L|_+al`EFqJ|D2msaN` z_|sR5fkQ?BtY^xIYs(#Q3Eti`yQA!3K#D;`Mk)SVbDSR*>Dx;W`*3|ze7qqM)dE@r z96gWOK9~2&nWX=87jnCuDOk8|Ri!R?OVn z&`ntab-^mc`{S!u5kcN)n$4q+`1ttJOUp3Ih&xJSWgDl4K4siS8d z=mDBbTl_~2t7V3{HBi&?SB=KmNnC{h2``mvpA~R9h&?@G91&A?2 z-Pj#!)sr{9D4a|k&j|XwN>8)WbNZEvk{l7S=9KxjM(BQ%tQ)}c06OJu+L4*LkuQh6 z1);NWahQVd{0I?|dXGpdR{;1`MTtwiOA#XZIrI9WgAdA1^~uG>Wfh1*f3Al_iRRV& zk8kDEo*1uuu0I$e%EdCsm}NrEMz4_zu+*(3f%4iTmpLA)?(OY;S0SrnBqa_*c26ro z$M#?=?J5%u?7ntMh-A`h{R3#N`LqDrjBm}Dr18hem3ri@Mmsq~74o~A58myz+dv6Qrh+4$HiQuti(XbRHqobw89sVKhSO^k3 z2Xy`Z68BnWz(cV|mwZ?HT4L{Eng)2Hu=|a(udJN1m~GIk4eqlLd~Z8OZMcVvE6QBG z;~!mRJ1)i2$yZ|W=Cs&4WQFk1*R{DNY&%F=@bFt_P0J+T6@7(ZcAD~fGrZRNka^;W z9drf0uZa@b(Ma6JrfB47-l~5iclmR9yvQccpK)9@L@uF~mA>aT8_E>P$uYt8D*Wcq9~yGYKYY zDdKeaY*Cy)-uu~sw-6y`DM(l6r%c+rb&J>tBD&6Dt{FSKROc~?%cyF!&&@S^Y7qAo zUN}`cE@9H?NKVEAt2oFn*^wd%*C6$Vet`i=#eyW)+dY0GTAh&J?yVO*ykyi1SXJC| zyGv_p=RFU~$9Tu7FKwJS-}LKFvVg&202xDNBzIi{gY}dsqLa1tZNsUmQ^&&1NprFB z?tq=+TtP2`E1x+w#AfxI`efFGR- zyu1E%So#7am$cY$^qKzVEYx(WB4(?`ASEr+Uo5~kyTzaWo5M9fn{7LLLDx-=9Xf8` zo@JmR8b*%s@qA^zXC(x4I+@h~CbE=ceFMz#nW>Jk8V{`S@9(o2M_7pXRe`pk3x-A3i3EfbHa0$)twT76&g;5j~q`D&KGMeu!s#J+j}}v zfvf#_#Nlka_RyvUTH5h;-O4r#r`)0<&yRW z8zwcvZ9zca^K}PPOkA|&dYGDdcn7y*jm2XSL3|<^`9x|c+=e9r!*}DY*AH$Aif2fT zAOqeSUrJ;iD@;H@V8y*Dm|Js!fKI?CB*hc`8L-V$%LSITc=<_~YgE{LsZWpgSOc;7 zCk_(|8YLCGj#bw;0bS7#oGBmx-l#Hi5;<3wZ#K!}mxEKi=wQ>LaqMT^zIQ2Kd<*2e z{s=BW@C=8UG�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%0AUB>Mrer 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%0DuL;JUy}7p)yoPv?PgWl&`&J!c58ZY>8#OH zH3MS^EV4Dv*MwXi4TL&$_kx0YUT-@v26#K17kk!MBNO?c{q@+_rfX{W%9h=})bC)) z{A#mKo%$}mN-S`JO}`_n*Q9~~&~53`G4Qy+Hghy@OWYXTAJ+O1oVVX*k<-YF*)5T# zFhDOC4l|Ljl+Ewu{&;QqNFa{j96*JnKy0^W!<08*CEaijlIifcAn(G%#4MCF9Ci%v zWsN|<{bGKR>`2U#9n%<^Hq2|`BasH>9zIMN>qxjBn*Me;Qz9^^xp&3-5furjwTP4; zUmBFWOP&;#`V&xHVIt8tP>o#s&Ya3rxF^E0l53<%=i~baqfCHGgUes;)FW7Ya4h^d zg>alCP~&M_aj-XWJa>(Ckp4#4k`$HhrleYM>!YegsS~w`8tfG`)(9_Wn!vSir<-t-RbF99fg(g zI(}2FP2pw6M)xmrien4~?aeKK_0h}YDmjf3p(b!m1YOXSJvdBs>Ci^iYdxV~^Y(gFN>cV5mEmh`zK%t^ZCQ`C9X@<*xKcOPRw|Nbt3 zL*J^Bo{RZYB@6w+$__3C|Eo)??t0lgy*JzBHo5R*JV=~Ta-^L^uqzK)`vT3xb~Ts& zA&Ioy?52QhY(ZCQxRql#zvX`M!RLO${*wiN<-fQ4w4(Ly4CKMXkJ&eTqjQ(8W44%a zFDxK8+X2^Q4X5R7qy*a{kb$64O9Fl~`VR4xHh?b-kA`J4Hb6wB} z?{M*ZFR*rauPmaRx?uPg_o3rHBuwRC%^ObU#!4f5E7QF)*y!obn>&B0QXBJOtr%rb zFvfOvzo(|hl|dYKxZybldqn?Hub<#odVjVijY(Jhs0skx6)O z+xCu`oPid_2)$%{U}P9)hPe}*mnxXp(@oy?dY3XSml~@^JuVcruQvnR-s5>C)s)c` z;dW(#lW)Y1omhn?(dSE`7ayVEfCU|;1wdQSf(B|g|86WXs|bIpR2muoms{0 z)z8;wh0zbKfL`!G8`MgF0?)k)2NWolihw6HV82GCmTcz8NRc`1!1u)YJEmE@KGz{b zT903I>u1D~_i(1Il-FGE7Y1hJ#I-cVbH()}01#I%fV9>oq_oN#tfRS#nkornLBES3 zhq?~wJcaPocK35;kr&RP!YByk-SnxD3)Z7dQ^d5IVTp1C>1 z*_XUFMYrYqWq8L(vdY@XeR~p~K|ME93^R|0&d(aC{;?0b`wp@4v&##wga7 zl|uT`%BDkV{Pz1`H5x3ga4b+I*^EEnFApR7nyx<+MLuR;4K!bLK!}U2G=OEg)4l#* zT*!C-wRx+a8Sq9a+o0!!On_syKp`m z$fF`Z0_Zy(t&!V~%V8Gtf{k1JYUOT;Z+uT+GlhS6J!MpjzPgBA{7gJc8St4HSod%U zOViHxf2w!d_+@#T!ItDI+EbxJtrLkuj|Q?4+6!?E3vf)Efri!MdqHgQ{R_a8(bT%1 z3*$ZCt6iQRu-INz>$QFLxqsDln({@}83vo1Lv|gn#9C{gj8DwaBObC^PQNJhWN8JN3VP?Kf#AiO}!>TC;Qzai9>Yi>LGT zOmVppt!Hw3-}0<>d23D#B*L<%{eCsMrvVN6v@g0#?=cf#+vq*CeL%9}NjjwiGWxQQ zSBs;G?M~&c8-IjD{r$%gtM9^;a__W5J9bses_{+1l4;WFXF=OLe2+LB5^X6UJlRIO zV4FEE1Lp{jodPbtvG^RIb+7ie8B8ossWI)~e`7RdIklywrP<;C3h!AQhDu3Do#SC- zA_MO`dkqM%1t6l4in1C2j-kK3yku_`()$w_sVjiI zXBDK{Ecy0Mi&{|e*8na?w!W|FB}TQ90)mPT_=)lcc(hlO8eJdLXcks1RVUGh}wQ3Gj)PdbM?L9Cwh8EdbSL>{PZcQ7~5EIK>EW6E8NXJ_MpY#1_Hm-t%m_F z(R*mz8#JRno!SZrqzkg%_Oo^+DEXc=kr1R|u+JuB_l{J{w9W|Zo@x3$+|-RWw7K05 zc=x23$(?vKXZ;JY_GrGUgvmYAe+2)^&4YTRA!?2=nQYLxO5SaeXHPELf@;AFyxDj3 zCVpA?vGJH!lu{6yf|4pkDHt;0GuRJM3hRM<<2(ZP1RU0tBW?K`Q#i2hHmb7_P&gdJ zjj05N3WX`lW?zO-#yy7BDkEg=(ILqa9Vh>0kS`L`ac-r>g?)g>K%{q^-oKkmu2$Us z^!iBQbZ24R_#|d{g~;8d!8dv}&SxeC8d2%cBO7Y2Rd^g(LN-pMKRlOxT{Z2>TN*K` zmu!T?HZ8Jyc>i#aO60VLnwswJ%1HAQ{b6S&y~Ac%yTPVWxP^^vo7ZH8%!qLBdgJ+) zvZN*Dl;W?iWC=t=4?yag3iI7$Yhli=Ne#SPwZ*fTKUtrR%C=3}j}Ef$c`)=e87;J` zCtoE>c3e3lNXh-P(PBJZNdMal=g$ebm;}4Zm}60}{qs&)twcT}qt+35dsx{;Xvg(! zgv7r->s>nEv8JrNGt0mz+9|=+sf?vcPmbg0A;El_U*=6iqt)WdVe4EhO{y>O0-7%+ z;WQAfz{Uqpnu{#~ios|-`mz6#yh{va;%ls^F`nY7(f#oWOvwaIjJ2Cgb2mFz*iE)R|F{x|adnBI3 zvJ&C*ov?*DQurPB!=73}9OCNgj}m2d=jV4^3wjkHGDtY%3*?o$c5NEJzx2`@iDOs4 z83`d`h8N4HCJe`9em>ToD#Jz0=GV7RW-K+8%A6o1^9b+`g(vaRdc5tD)oHP-q6s7O z>K{QfX~T*31{ip42p-u6Q!7qa8EUtka&Fx}u9j<9gB`E?yORR&2iTxABkm)O%Csg| zIm0Wrm@wJ9llRfD#IcTt*8bc_5Prj5V@JzJfABJ``U2OFIt+z}*R{lGYvX9wj{2CZ zy{BCLm1sfY$;UXNry!rVm-naN-m{4PRy`}Ps7?ulhW{Q74rM`S%;cfqRH+?GoxlzVP>KrQ)g*Vlf=4Bm#=+O^gE$@ANSwVo9@I)? zJLc1x`6vWmbrUF@ioW{fv0iHz`+()h+RY{Lz+wS_Zlku3XnWp>^e8)ZJv2oW$L-Pk zo;4%!wQqOTni}xQ^LoLzrcGCg4y~N&{=DtNc;g-};Z02+-|F+>vM(diHsYPG@zn^c zG;*b^(ur2|vQHd=&fRS6%_mQc;m?iUNaPXV7+`g*D+kxo$)_085I-M(KN}VBnYBDm zQSzcHEH*NyzxS+ZcCzME|ITGqMbNm^d z47sN2XmGS%PvFFXdAG${n{D%B`%fGXlsHwMXwA>xH}IV*Mg23Y=ZkQ48d5bHcy2h9 zxsO#@;k52+q<u3(X)=fI-(#Kyr4e$u}}uBS0aW^z97>sca%H&bBE(hrSkG}We z$!?VY~wAE%TqpBY6L$xRx>>~dau_hbNfsDk{{e|&U2=`Flb73I%vsl zzUmH8Eo8O$ATAwKDr>cp0;RFG$KvV;5WC$u+RgFeGI6{C#taR^(9?N-(V!-VmLSC@ zKQihJkE(gM@fdjWo$0FGG&tp^TRssA#&%_iSUSd1gm3utAQFVO(re;i?0Jd6VE{LnL2r(*R(({#5AV#6Y{Y-y`?2_)7cO;U%vZerd1pC> zQ~vQ%WF9?d*+Nun#ezj{sdn^HKp!5k0GGX zk~Bwz>TqIRs#RIM9(#hL$DAuGK^x_T&u-oPlq>)h@)&>#$)uPWKj|juj5SxT^jXOu z;&sY*47QL4g$)nM9Up$KwcXSoOm`<3Yjf>pr)ZDocTZt?Gaw7p%48@2C>UOeqVdnY zF;W8-C(YZP#1!jm2G-6c11Q$CkddRK;r?7-HtHiDymo1zPHWVQmS;#eYN0L}mp6a+rlTT`xeFsZo1(H<;E%9y_6rNJdsJk-e5i>6Yv)#~)h zvEAI#K)N{|7hD;-K{7Wc+*eQazRruxseZf8;d;R`tCi}sVSKIj2>EPrsdvNIFAl!h zQAuLnfh`$;-QL|jVI-oAjq9K8Xe3a;8b0oa3?22-56=dsGV0aN*w0@(xo8LL)-O#h zB>-j2f##l^bFRk557FwRZznYDEfuaY{a!i-WjO&O1roy z%Woy(4PwN8`*)xMy%#&R(YF1M8r61=!GJvgBHB#2z$noJr zh?WrPwMCKW+0(MGdu2W4ue}w&%huC*IucG#oJ$eJP8><57O|3~s|J)Rt51{6woVi* zvBQ~>ea>s9smD|yDLyiJ{px1QLBWpnyb;Q6+LLAde9Q`$x%#7>&}5Dxu1YPdN{qQg z`15F^VH4pj;bYy}6`@zR)FLt|y$ncqDG2~sk%S1RaWk5X=vyZGM;@`O4|Z>W`i)3O%`v)9kRyt9yeKZ7dQ( zlDd5U+k%6G7qz@c3)dzG=H6S=O=@}?K*xzauB=h1fTr|@6 z_>d1&2Tsu@Fg^^htWFp5*h9fI!J-C@&~YLCd`% zD2vCZTO}zpJ+btzt+Y`F@R3R!oms`#oD-KS9G9;d%)S_V6H>3%UrnBzrabz5-Jj~@ zOKtILlz3%9n2@h}t?_KJ*JLYJ3Hw>#^Fbr=>V!_0KN1ckTd*Wvo@d>x%~`yBubO$3 zp2I2|^h6C}+XlOYSn}XB4~23H=r8sjCUJ(0vbk{s83V!N`Vl!fdHLk@1xwBp2@;@W z9F-=tTMAUVhrb47PzPG^tH;~C*kBPrd%^dE(%?~*=}qt$b*nX@y!l3IZSj*)$4bei z33lJjp}6Rb=46ys`k?u|e&u0$+8ybxFy@2(*x&2sl^O}xRJ~(aB`RF(JD|4QemO|k zLRrASLwG?4r_yXPeH96nvR9rA$ZFq|2VqAn<)T%$sVYuaZWB1R8Z3iVn2*y10mSHi zY$Dwj%_cW?(~o+QFbI!co>qmo*Wi4)g@D2ad~lO zHHg{O{p#-5C??Qq60`UBuFlu;E^77oI^-wn9V4;f>^AK(+PLs{g6;rMdGhWDQ7mcJ zaZ8_`^2)TtNgO<)m|0_5N+Bv@OBR(NzI`YBG|KmaXM^usohi>aS4-hsx>Wa2r5&UH z1d>-iG4@Vmjkxw`ksWyCb^6Ce#&ztyFamyRbaUE?wIXC_5_{O<( z_?r?^y=$1YNGwYoDeqY$+cviFO5Z9F?i3f7Q%*51V zB}jzXj9|BO56q!$g_}+yS&JVN|9PL`wNj2Wp(}zQ-!p9tROkYiCni3Sc2=8jZ?hW5 z;`XdjaOjfk30i}$YpibvB3qTOulu+yIalC6qJegGZ6T&WW-S(5yN2kra+h8N!TsSD zdG-^omX{xMAV95~#18bP_z~F?3Q*d8{Jlk3=u?sc53Kx;sE|hQO|SRn)X>n`G$ltn*XutU%Mj{1)qMLXNBZ%bcu`9E2FNvNRdNOBnT zbY=v7F5s`b;{dz+^C1tm@#+A%>Y(YuBy+lCFr&$i+wWdKFU|x(Zye)15}%#U*dLkTO_;8E+f>PQH zAmopF!7HO%6q#dwCC%nWtv86Bq9HFAahy(GXC!Lvwg7WF4!J;PjLboobZ0&2c-qE3 zcUEjS8R|$9@G!i1wyZjQXQ74k9DwnaPdw-2(?k?I*bp{^#4J;4TWnQ~YreKX)2mPu z|19&^8noM#dR^Fv=ZXrNXR!BhL{{!(Ufp&n>1r?;6>ot@K26U+&U;s?6hIhj~ae5HcuO9Yf_j7fQv#EUA+MtLXdGd$AcfMKrk}t%>?NW!2 zDRwh@e5Z#BDTm5U9{M97ub&d~ny+gP&{+l)e?U7xX1S`ofJ8N}oeD%s0F)R=JJ)?k z<{_rHCBrZ+3B}N32^aNca^jEZNU+3(#fgqBml`9*yKEiZM#t-~LdLHqn;OVH5grg1 zyj^ZrDISMjq;TQ!Rhy2EjvBFMGB=LMc~iM&MeGG?c6l#KnFVenVc8x;Ss8e_O(eQrQVcz#UDoTIs1}g*V{8yz14Y z7@5Iv;`DfrjDW^&F||cz_)R8b3-AU^#=QUx*8rUMVk<%LHBcc)1q~vbtg$u{#ce*u z1PSWO?To2e4#!k}UWui`qOhNLEpEob^U=MzUo9;uB^525Y%x&u=*#+(#@hW)J5vp6 zVMGm|b9gN9F-?e`!8tG`@yPB1#AIAEFnEeS=twT_h>A$QvZbXvT+Z^-O{S7@Ry#ni#WwFzOFv^kujQo^9 z*%*OsV)V&`OZ;C1&jLrf2s9q5b#{eWXGp;$cuX{SFOfeAzyuTwR~e<6d}iNPR)GoNts0TC9Ix{CQiT z4)P6dd^qu}CkfSTPcr=Vc&obID>H8i-9@5VBb5E82nSBaTKq_ zMp@W^!qsAaD#+O*Q30+@5k5}FEx9r9)kCd#Tmq18Hr8}l@4+@D?$bu5*yQ->=*9FX z?rz+TfDK`K>XXll zUh9ao(i>gIgYseu;2v1mTaCk9p|IgAf~quH|SYv91S#7V$oVB|lWFFv+4A zf^ZEGK)lfCC3Fh!V~0S}8Af!5GT%FOXkTBTMAfy4mzqz5cn6V#)K*3+JY-E;efFoW z8k!8+gr~=~+{x}D{HDjR<^YDWm6&i z-D{P8@u=M<;w5XMl&5|{@StVgp-o>L&7nwd;8u8itYtwLLuaZy_1GuB#v<;~Cw+X$ zOnx9!>@^D>5jiZp_%a^15F8jdG&V%SA$aR`?m7--eTXjHa)4cQ2wd^o za*h^^4-`1QURBZxIWiW+d-wX&UFDlPO1UEWtrDU-p&7t-5>jX|W&R0BALiClY&7~_ zw1^|OYe1YIB5zr5Tj6TCtkTjpb=I$l{n&*e#2!$_A9fn5*Fda30DO7S$(0 zu$Jr9zR1B|TiCbSCBFR*oHo~5@>M4FDn<}cr%%E@?^0kl{&ppr(6*`7mQt(HOA{AG z3CHi4W&FtQa?)14wz9K`|CuJ_lv(Q{+|(6(!q~WQ0!y#J5QF|UY_7)Ie-8DiNxM^= z;rQTtTtP3(CyMi3z4z@Oxw9|K!8PH0RmF0r2AEN@IvLbk7aL1$H$lgBPsP+G+UM=+ zKO}J5TQGdF&qP^Oq2E=U{kiR!^X? zcGGB9IOwK(lwiUEqpKSh11)*vOMyCbySj zHJ|-Wx*7Kes>IVAq)J@TW|Ft2(+<}bUoU-~Lxd(TxQm@+y!QRHgPY7|Nrpf% z!6w@hWa~f|nmPk0pY`=J?rZF$@fi9SPE|R+FsC^o?yPP$~wE zEt4BltMD1+KE1g*RH(VRz%Tc>4^zTyBv@5SmBf=RlbZcy-`iSW|6+rsS`4V?_RPY6 zp^TQub1q^|_F(6}Oe)`s4`ORxaT8GY+Zj= z{FlH`n{8FfeM$6;zt_ z9!pa~KF44EcMF=}**BcAz{j}Ii~Lu-kjrG9_QPZDIMZ!Y++dME$ewDVp|DFAIH8*c zQyms5w>8g}#j2|x@~*+zF9Q4=$4OY&kz12Xmoq9B1__B%*Fe^AV^wz!JIG1fVA^(H z)?%@xGD1L_;_d2|<7DAW*!7 z6`%B?c=S*@RDez>S4ow* z=q*2VpbI!*Omsf-gJwyd(f}xQ zB7Af$gOPb?b$~^2NR)sJJni_?7V>(WE=6Y123YmOVfVE*x?G zofaGwcu)g`TJyo|$C)8eM4?UA+#~uNA_$cF(88RefGh}K#{y|{D)!)QeIVMrXEhlg zzOADYFVr0C7RAY`m-k1o`aNV-iH4#vT*3_WhXfX2EjE=EkwjKwRjAR# z>%>IwiF@w+rg5{Iqyr>hdyHP(S_VMp`OLxw+(us=zffziTBroCIr z<|>T7e39hgELg3b#OsXRp#&}8caGR_oRK8sLHmP4>__e2UTkxX_1g2s101}iyZng& z-BSk3ZRpu4zi3^n*9DUb7+uORpU#**seOt`am-rjuTNKD`j4*#h||$A09-27l(m)_iRM(dC}+x&mA|jYU!OhEMR{!GB4kp+ znR-}NSE`>+rbJ2scG`tQx&uxBa#58^xo~(EWFDj zgNx>UisVP4g8l=E>0U!GA3{h-a?{e%JXWN6d5?KyZez-3Cj0D8s4EY_9upm29xDn8 z=*DuwS@0hsWQTiiUCDP!4Girgw**Tso%vG@jOI?0?F}2t@FS9qQgNVR(FM|q08mq= zC{Viy{PhyxAF`80rDkM&4cyF8`uf6^nvT=u<>qoczcip2Bx~M$URy+AS@rY!P9JIr$=Iu#!D{&@ohaymMyi+^pZ6 zUq~|j>~B3l!Pv^CRaYwm6fZWiVPG&=(#jfbhYxK7v9!$lY4hf{G=?%vV6;$oL;mxJ zuIfU;%CuyfXe}1aP@!!tc#c3#KC9!XV{Q4_)3n`ZD2-Il=`da0J9OWNbwsWvJ^ZWX zzxw!(Pb6$`JyNSD_NyS*aER8Lq;Y&QMk0C=D~t~Mz-d@xSoyb0@p-KhWrXIyj)FeU zf1kv^t;%2PgWd+?j2vh%mQIU7OiV#RJgJOlMA`wlpA#zZ1@_1_W0%;Kc>PjrUa3SC z=yjC3-|;tlHLcnSFIL+5(C*{FSz(0f1<^gLXwFm867Vkmt6Rz#WN76Qmt#)*zpdPV z%=)h-Rn}gw$Oy-+U8N*=tBEL1oE_9G}qK>Cav{LkxZru!`JgEkB^PI z5TE{A>;Jx{z<&vai6-~%m!4EoplWlfh`7-x%QOH5x*Q5MM#W$%FLu2Wl0+mWYwzCo zH;}kA%o}s;S1l)7%^^H;fo?Z2VqeMe%|Yk|dKU;r2C@&IFn|oYRItR`i~sqB-+xg; zKEOaL+{aZEl9@#+t|R74bLJQeo&`NNeUL;CqX|Qpm?NIrrxKAsKo8epaL_j+Ra%uc zQ6K{@M)$zlEQabM21I!8AqiA3ypQ5fx5P6JLo^^P7Bke0ZgyH+x;~{5qoCG=YjRAxF$R(_!=M6g#F1 zr?*OCC-hoQY(S^kR2&K z9hI6HU+W#ax-Ozh@kRi)8#cfY*kO_}SU{~SsQb=;F4zhp zB%G8ltP(PNbvRM7Tn~dc%*3@|H^9vo=9kO z{RcA)E#Beb&+pltBN5IoqAIF_UTnc*|>pq(- z^C^F?pA7lmS_A(t7tr0NM$`m~AfCx*ViAkU_rT>+mv6L?eyUsl{sq7yIHHio#}ybk zB{b(hT3etVmk8h!ZTOIPNlbDaD#@I(c*;|qyXSW-3RqvwGq!a0K(-C{n z)ZY}9>aJF)rI!3N$qiNP`Twe=u}FB!*PF)iCl_jRuV3>PYp;EEq&Yu7w;K8o9$^I23=>@@l1iDA zWRf6`sOcfAT<(anuuurpl5H8y-bepD*j*b_=XPY;~Hc5d8R?|@6 z4fT7mZ2)t^N=AHHR&{l|BG%q{sRVt@{?e!WTYBTwF6RIX?v{40(4U^w|FFj2mB=#y z(T5&Ip&gac<=*L+;7IN|VY*ex&N0`Q)`Mwrx2{Y=Mdxk1_wH-KF%4`ydHSQCWm~(9M(p)M@PtiqWnyWRa7i|*!077k2|T! z&j#@?b@uE+ZkL1t9qNZuIFpLWxh%tE+L+Dy?@&(#O0%CGMTYeXq1{Na#E}zZsg2Ve z90c|E2;)d1biS?B`)1>sleCpI+hdo3P6ePxi3RCv9gu=im{vX(>_T*eNS{4xtLS0= zJ@X}-1E)(4RUne#V4Q+c1K2IaF+iHq^GHcayP9tI1A++{dA4$}h>7V>XRdK-P79map0OONu|TH5 z$M*9ZV#%`H5F%Tr=3%p;%#sI01U1H#_jZzA{oXN@b{~cj(?ju%Zu;t~2p^Qh>Bq~! zn4t;@qnfRVAk?1q8TVa{h?p4ca90h7mh+)v%UwvQ6gGInI)!-^?12w_vEQ3C?B6uh zx%4oMSnGT1p;nwFV)}Z0HpGJp7IiDH04s##B+hNW+ARXygAN%`>ra-h0EuxnhEW>6miR#A{PbcHss5~-cYKjs63H^uhLl}kLOsgDz&)2 zUo3xXFwLe?f9C9X>m9Zn-g)!Hz3!6U>Y{77{^G1C7>||dDYSBJDxC&bH>>+u1JBVO zW@BC9pcCYQa&K&L)GgnwT*#-Qeth$(C~EVmWsa2AiIR+!xl5u0)A7yWvd-6T$3=mF z34WH$ZEpqe&Hnqj{J(;n5k$|fZ0~%`9ESxGwnu?i_54Rrc_Qv~*seaRfEtRFN9hR3 zAaW5V8G7O|+j(j2MHhP-cGaHLCK56e*Iby|k)pJ}SExA%>|Ne#tMfHD19*$z2}Hbo zSq?$qXyf7rN-V6t-LkS*sm7a3CtHKidD)E@ubS=deCL1JfdBc)Q|Jl0JTBzX8FS>v zd~2t@>M~n6tb?nI{Jaf1i&z@MAur1eJrQ}%O0CqioWrr{VPR$sU%9}*K>hZ0JyK3i zPHp>brQw5f9P4Et=uXc2q3bq*?(PegOcz`G(B3|RleY=1n=p2!10=%OuBXwk$Rh`Y z`o}W-y&>$gt7;mSW9$hJl(qWKRiz1d22SGCD$JhTli`@#SepF0Rn~W;eymDIM+YR) zaG;Uzw1;_j^|IZ7!@X9n|KuMs#U0SdIxJIma(CQHh+{jnY~RQ)8@l36I-)J&wzc zCE8Fb5tfG#g-^A9*}0Ps&_L?7RQn>mI{7xE+e^W@w$~Tl(SgCvPuHv+21Bk z{Wfo(GaI?l;Imm+ZDE36lrfpl)sty2$U+#?D_qC6WM1YZ+aU3Ao1f(YmWUh<4i39z zqD%xXGwxzz6MEYCU7>dRx<8LEvF43PqUw&f+Dksaa61dahC=8R$YK`vwYmyW z?e&RQzb!Rz>0I~5CSvx}KPsf|@bK>#a`u9<#imEKfWU5IQvcLdxr-odYGLP-nc>*Zkrg*TQQNLR_1fjL+DV zUya|{71<_WzEGwbI#~eSy-E)i)e4|6OuevTuJsM-Y}oH<=9MT#SdUOMolMN%n^D^J zF=U!gt;uM})fd%G`|-vpGellfazds21+9R)Y%=kD=EZF3!sD1oR%ZWgNw_c zm4SynV%03`EJ{MMo{Q7iGtbAl0AG>{-b4@m~yq4=MBNR0N-*U5Gw_FC?enS7-t3yuUQ z9a8_Yl}hSwiTQv^~INSry}*6OMa(O z)3K6X`pb}`4cl@h*G!OMqP4^BHLlev?z7g$pisgocmnGnM1qTjwlJ8JJPSQd&BjNM z9>FFFGH`RN0k~@|tM%QYs0fVP@uOP}_YeSJuRfsRkFkYihF;NhSX{*=&FCQFaqYi%&?R*0YdcQJZG*ruxA20ZtCVd1{uX9# ziREoeI<#ctO3j)U5}rrabZ!-&iJYdBV_;OQKZVEJjRMP&=TMu+?DH|DrOnXcteotX zFMjX9Xr)9yhF0c+UMR!wSx#y5hL=XMNFbq=TU?ISo$YCQlL#EY-}tsg5DhkKhu3Ou z8%dWsL!vD->R@?umzVW97X#V~{gViRn$7LK3xNF|2#C>90+E!~ACcaR*`Z)AdmUoG z_6U5^5v$d05~)EQWhxh$47^Bt7mAyd=54dh(`%3eocQ@p8WSgwVi((~#1oW}pC|bK z+BRF!X1ASsE*p=p98u#{3Zibtd%!6knY^Af*5|1@mF?<|p2qJ%;U;WepB0e4ryB@vXpKd#a4gSVwqcsR> zOggNPtlh)t{xVAtBY$}o*{{FWR}TqROnz|#ASg>6x`#H*bf>HI{7)*6;mZyhPK~;x z*yrg0Kc!->@V?bGkmFcRaV7>IE8E@Mnk-Y|OJW=@>MphbXv7b0;EB&ik{p zIwA~xE1FU35T(PBO-SxItoB42nS-y>9Z#^1tFCS6E^saRoXoBzP3V?g&vdRxNlRNe zNQsS&)vYIUrRL_m9$l)tl8+biIx&QbifY*p-?DIjzwTrHg-19(<1RQBUhcCV9b-HFUTWmmQ>4TThf-H8Y}d^bliv=N%KGL4z)Vpj>tFnvI|W| z1zxC(0bZz+0A7e8<))TkVrI^QKxBajFvtF&;^Ujg0WXJzTR={R+|dWkj_E;BRrTT5$5|gg@!x z-+J`?c9*21X>gITe2(UXg}J!6oHQmjCKT%(E&J&K+x_kTsvLh=zQ6xgwh7j&zd)&} zk0LYU`mLJpGPa_)w0E}T9N`ijBAhFj#MrAuA8%Xa4{gWg$1^S%}DD`<{S*ijpX^7E==AX70K&T1?#K__+=KKUum;f#dYh^bwPtB5_W89pk7J!W`ews8#A|L{7o+$eX|K4GK*W?=xa5H;R%xDzw`@8cH185@% zc@XsbO$O-&pg>1jdVT0WRrr4!_ft}!jgK}i_U|`I*8*<3zcqbIdh$DA$Pd|iDGIbf zXknuH2ThuQOTa<*5Kf`r)<*Pz4?vrTUIxnlpotqa3?%&)&)V-FXu%Vp%~RN3$UkWE z|C{2FzPhEf`eV&T2t9$qZ81s72p^x9@592_!#EB_bGhNW!vE_5_%{)e&0WTae;eW)TL zLs}+1Jw1|{jx|{3MVrylSpFNblk~rm+?RO*>gx4xtPsY*JWjiEMw^2os;afNV=F5w za>~^*z$?2(aE8K^!tF9aP~ZT}vU_(j>Zy!Aprt-Ej4%G`mCztqsVKb(@@B-r*f{S! z`M5|;!F!`$O7cP)(i?P;pkPEzP99ov3q{M!(&us_CoSvw#s>1oru6bypu>E04@D`4 z+8^+$1zL0p;JLn1wV`-PA|iAt>F7%39Gp&#P=09!I9Onu=*u?T48v`q5E0); zL<|W;M9PCbz*}47t2ah50E~pz`DBaHl?v^P=U7<-hKFK5-DCgnW=z%|R!)AHjJvMn zJyP+-Zh3k6=ghSk%Tf)j@nr91QD8lO@%8YY2M0`)&nJv{BO>Hp?s}4wk%=1`>P7mb zhcB`9tz#r99i)VKE&>74TR@%PMK8sacIVP9;=S#8^erxqr2# zB(m(VEQ3CL7?_b{yu7nK6k-59SXORsGpz15!>Lg3<|ogwmJC2pQ8gc-BHoQXZ3hXs ztQ7+oImr0h8 zfDT2E8gr$E)JV#OaWM2l5c@cV?tZk|AM<+ z!Q!!fjygJekOu~r%e~p4NB<4bhbNV1J_*=mGbMjcZPXcDq7L{|D+T>E-_6DuBJi}B z1PD~A*aYn7;G=}!=n&cxx*<0IRH5P2=Db6)?q;lE-iP&l#-KEvHkty>XQiyP`G&WVk!S{(d~nByu6XyxP6sdvlYzp&!bn}IY>G3WT_ z1_lOlnwp^A!3lz2PE{bSzJBEU$c|IYXdb~pbiYzrhkoy{5(Wh?Q}vjPO!47FWpeIs z3@uZ^>Sz;4F|uSx?u{x+apZ<<<@i2u+oE2~mPElo|s zX=$=wcH<%N1y21`SL4*sluvGUJ^~6{>mI@(_+4rd5J<_%RWo8@V@0%W{C>Uix@yg~ z^kcS`YzeG@*<3B?t$A{HZgC(1-Kdxs<(hVe{?6rbBu;sBZPXQn_d^!(0dF0)pt7`d zC_9?!Zn8fYQO_wvh1nq6*~G$Gpp42C2C{K-{Po-SW=~1ciHT1)yMXvUAnup(J&7Nj zdN%{=Ok#-U13;usr>qRWixf2M;$l7wME!Oh{+l@FZ|oZDAG=mi=IBQDJ?=X|6f{=zZRPDF3SGg1Pk@6b^F(|8eANE{s<4}kYWMGXwH>?y{6x$l4L?3uuQn$*~qfQQe(3)F>o6g#Bk7#XWV?Nq;9 z=D(b`D=Gj=+bQuPhme5f4WuqRElmIblgw_?{I#;*h2iHvQh-#_b$)nsG}%&TSS%29 zR>`=zH9E(`{aTFwnohHl$N6!vC_v~c8OQJ%SlD_Ogl3HHo}MUV@?;8HRhC#Zz9htB6i7X+ZiGE%6a>n?cTc&x`uh(H3JS39g#t`31n=T5_Y7TF)U2$l zs>hSDQTf4%4+tER>KuWmW=l8xM|X}N=Ac9mi=bAG)!FPkw_fXu1@Pha+{iy)kH7u4 zy;R^cyteJ`oW^|=ye&7BM>seV;^Ki7nyF0M$2+d&N$)Of3S>um;vihK*16XF1tlNa2TsF0I#S=0rCI(R7F8WN^I;d zJV=Wwl>bI~a0`?&33dr{-CMw{_bPUow!O}XCzdEPlq#e|n7>_k2AttfUjMEJqL;xi z;(OeIA2G=#qsq%!HWzoNDkQR{63LY`9iuNhWK*+AY>3M{TP@v~#Vl&i>4{k3v5&9l zYZtEWVWCQ#pVuwFcjs9d5dSj<7*XmxF7J{#6_mdB=1*PnisRo1*+HA{-Z zjCWz%)!Q3+N$k>I+?b7}UYF2QLijVeN5{RxT?1rUKINA4b8@uw^s$uk?V&1V@RA?3X9tL7d1D^tXcpuePZ zeXX|^_MUMKn%&iHbj>g8t0)M?cKulr_6`864+T2inKc+lYq6M(XXcMM-W-mt>ilp5AKO1paP2MkGm| zJb3}UqpZtYYtJv)$Gb)Ypusb)R*KytYYrO=C)3On>EW!bR5^+4Ep`k$+3>dDfVbdu z@LjSRj_q^bVi##}u;L>BWgz~Gks)X}j}@v`=TN8u*vlp; z@DuWZ0_(NVu~jX1UK#TE<{|(&G|8)eX=k40e%oku_3nE4q-pM1R@MzJXsW{eSs0H? zs=mGhv45y@tH%-;s)T>u%0DXl+;Vg7;SW2(ian4zO3!y2HFMki>;PD*5eEV`uoevP zHcL6LHi{DfVXKE0s^ktV?HMan<476Io>A|tOkjDtZIb?p?Qh29KWwtVGOR8YlthkC zJcCYpW>%I;I`AN9gz<28jcr+30TZXOaK@_;{l#{%9kv`_poqCA7?(JqIbAM?&Og)b zAZi>8MKgQuizE4gQ|n7bsc2SKcF);6J+R);mQr!UP36_e;=&qkkJtkM)%5_^D#C28 zz9V5OKe^^(YI3rHxzlGx-j$>9wv}Xi{yjV@YHInN+D$h5y-`5z4fZ3l$USJJoQ^jr zRpR4^mU5phClt^FJUf@MKHX4U8nJelfaul=eZ5{9#In^}APC<>NW z={BOmVuh#TaP_TG^Wv1^EldgAPZj)yXB^UB3cg75L&1ZjS-(-?Jy@pVBGluU`ff)_^87f*emZ~Y>n9^`O zslh2UxEAq-5V@YY8IKo*Jiupr>2_rtQh$2$;LkJU1jbqNEkr|1Qk3OISDu{|DA~}z ze*d;8I*k83I8Tc(Iz8h>v2Bpn?d>sdhLj}#Hwla(r_Hew+zy+iw+U9;g0%!TKlMLQ z^94S`{y2cp_y$9`tye`#-L9QZYw65pYe!9aN1`?A4urOyWG{J7=VM=br(k2 zEM{w!d-=gPgVbl@)Tg`jA4G^Rb9gSgu0g4hGI%ecV#r$f0g3h~YtELcVbWI~?RTov z;KRPUPP3!)D?7e3|liRk2 zi&9h+(WRht6#?l=m5v}HLI9{lrF?pzAoWL!QltH*_0}t{&Top6uEz+|gZ|IB>A)+m+5%)G~SO z80~`_mqF*DH;MQuR$qU;!3HZAfR5hZ*l^dO`P6WSfM51E@*vGMGZlz6={kbC$-y@D z5axw~2kU2uBPy{sK+!#6Q0j2OuK(IBbJrav3_NlRi`JtBZ4f&QVzYsbQXF)`c22lW z{T$@Fko|-{NSTWZyYtSIwGRydEteIBRem%v#Jso9f+CxThdp|{7T1PM^DLbqn3Wng z-xw)&{NO$2z^y(nE?do`4A7Jby`Wu&Cr#`%nHEd(BuvPDgabNMl;95YzozGtZPjJ9 zl6rzu_@SzvE0R4H3cIHp?Nc~7XAuxnIb`wC;$Z?$TArcaoG;AihAP_S(%l*iN&M;^ zI9w2lPC^eo!wPDut6#Oe+gR~1a;;MKx2Yelz;uboigagZ=kECsu{!lFM0N|M)L5Qg zy;u2*jiktVA-ZmhsJ%T;LlZLH#fgK{aMT0EuK8rI$SP5sx54}+ODVT`+8lq|QIHbq zvLM(NhWM@4TJ^JHB|UD%Gl;qU1yOq6t_awtbM9A1ix7$<<$JD0Pvtoq(&Gj>jJ|TH)+Q~6y}J4^*k_-);jd+Tjpq%5fEXz3D-{VRtwk|l zI2ezI+cJwR7u&s!i?bbP?m>@&LyU#j9tP?gWaAAL4Ek`5TIU@frkD7M{%Npg>qRoS zdU41+4bFVuS2dGvE2Bx?ydr&>_lQSIOlm!S3s2)GC|kD&gFs(YTGAIzu_;F&%W7X5 zlmd$DRzkL1Sp1MLZ);j284PEZX=^D~b`qpt*S2vLe+K=PI#1A9cn{vuq$^!7-VGWbZk03u+ zmVcG}_j2lpj1z9>4Mg4J6?vKbi zNEEb7n$sfHG1%19bl0_4CkF6`o)@M@Il45py#vN2EtvcV?Z$N{M$=QC(;l}P8V#5)y?lZW%Q z8!3HB)ihM8Vt?tyrZA^sygd`T#t-pXBfe0Zg^fb8xqQiQ)w=Z1vR8W1BE##`Q0w@J z$-C@U>}9Txvi3Xe0fT^p=GkTXVHxP#ThnFr25c;77u?t7F3m>z*C-4nFUtwC3reVT z8_ubV!^84b0CA^4x(2`}tMhC!w%tFeg`}FAN%U{x?V4paX)Vn#?t~o%&D+)XCJ+?+ z0QAZ#TDQuNY*#c@Y{#bhpdv-)e4UdmTt}ldP3*zf2pVpc{Y@)ReA2v4;~nb7b%WNH z-!BYgZ+ z+z4bLz}&O;EGY3``ub&~iyzgtvx1QkjrBXg?~xk6EPKi=w&)1eyoKrmRt`W&t)}!> zB#WkWoP}Oyl7Kj2&{T*2oy7i+mQ_e?5~@nj5(@fs_$-k0?)79f~!yD>3v+L$#Rbxd*CPR z-^D(7!&iugf^6!xB)ICtZ8HuLiBp6h^S8tSWlHlwf$Gh>YZ z%H)L*=Yr(rMpld^Ssu8mqHzuU`J%?W&f8L@H>+(j_;C+6o|3tOZ z>>96gVepnX=*VLiR=c)3IWO+|JsWcB&_Vxc-@+)VartevU8}h)I{T`jp|7jP*3NY) zLX4-$HyONRP%fS-;yamJi@xFZmDYvOYeOk;Nc<+N{<7fUvs*y}bK6GsFj>Ir25sWTh((63F9w*KK$^4e zaWv4RrjjW>e0Z6HwU>R$MYQsg^4(@7qlD>1;}$#mZrw(|15x)F^P1j*N3<9CP1`Q$ zlvzesC+IHORqFMGAElcNZ@+dih-8%4xpy15K>_T_VBl0{mSSV&Y6#R=;pp%nh%CqI z8iqRNixK~Zz6V_tA9T@&stA|P2#40FQanq({q(uc8lw}4 zB{&x6f6!J{Spw!UKc^;F<;fBOW7SF2S_77TIHy$=^L{RUwaA#WzM*JVb?0Ic?#&YQ-f;Le-*CQ`Npeimc?h#s zx3-FDCV@`+OuMdbFL8y7!8iuX`-Rd^-0g&ralzi}G^b{3Z;fYHw9;9MHt9za6@Vn9 z^SK7y3ZJa5!(D!;=vSb(uo};ci_3L-kyOLWT)z})f|?$(;uMS-TDUW>ka^&3r-s?w zqVIGc*{rd|6#6&3({0u&THn%#L42F<1gRCNJSZ7WSGy*Lf?g>?>)!Mut6H{eoPB_P zk9L0ixJ1eSNH#{-Giy)P1RkfL!N0jJs52~}+Ft}7Eoq~BY;UOSMbQqJ)T5NB-{mKo zN!?b*tWG}OC?F~T^CDy0Ot<^wNB;BNMh)UKL|kb+D*OvzlcdyiyG}9O!=p$CQ zYKGpc!8%_}2ALv^i%L@~Th`|1uh;k%a-P)kH~3@bBsO?qOlY8-Vja17e9*>rBVWR1ZY1@ z=PFh?CA{eu52d;h1_^>czD=tfS!h7<-=!vf>dS3#s1$ohLIPW(^;16LuV%%X4@)m19{|U1(o324w>kH8 z8%}7mooBrDrYm)&L9V~Tnw3YZdg}RR!J?M|wgENiS1T=fw85#faqwhuGz%ffkfFQ| zy>fr9+SbwWwZdB6xB=CzTXS&#>JiRY^2C^UZato4m})6fIe?- z+<+qTb5=Y@-{iVBpyiBeO^}lPxwQ*xT*oOIBY%Dr{zJ8Wh9PU)5sAotanX>F8~hI1 zI7K`=bD#!H?i@4^J-qzk!8z5C@bKU{;OukKP7UAdl ztiF4=6KQ^I#fe$|_QLy|+_L!7M}?S=P_!Lu&$B{$*MY|yU#=LDj3W_2oWv;5U1j;a za-$H1Eknj>bRz6s6I>q&P&6$Ku|ZuzADE{AjetxoO@gHg+Xs-GhWkUa-fovdT;v8C{Y2pB59J>F8u{u z9Xg2mZWUo-2TX1+2U{n60`lMYu(Vs)R~4A4sP z3;Ujx06R`$uoBH1!i6Xf`#{Gy$O35`f&Ob-;$i`5`Nn%CeI#==9Sgk{cge!5@e7-| zvdR^QjYCp4`f>=|Fjbn@Z5+Dk>Vec;crL9KfIIp}Z_4)D$Lj-%sxVEz* zR>v1uvA+%6Y4)%(A?H_SW@40MdFOp>OLgKr1hH$6Diev4LCtAiV@;QxxAJJZV{p|g zV#X71c*QquLD*rr16(Q0zQ0UJF6%kf;JJBBHj(dw>hQN4c(u#9rBq{uyiJ=zXGHc4 zYaQx_kaYgapZ&*IFDDs4f#@=QhJ7@ec5~`j85&Ry)yrGXbZOM0-^enY@lH+r`edwufH;uSn7zZK>8Q}8y zqWnW32%?IjTDt3Y;8G*T{f2hWKNnqe^sYb=0zpU$qj`UbQlaYy!^6+PcvihALVKZ( z-(RyOXC|LldOa7PJMyU`j?-+aDbQ)u3Z|pBx-wS%!JPP|RlY+V?!46gZLA=yIZ{`E z2)fYoPLr(07~Bl~(4`iUIQ1A!Gl|0x97LkQ-k^Q>WQA;p*=TE6W}_u&@YivI4jR| zRH^R*$Q4zN6}1pzw-8D(jR`-C8V%TFb`O_^+w6)vrKy4V#;cZYruihd@%xa~nt4Br zQ1`y;j>x6i!FH5h5&*f|j#iDpy%P4ACwPby#fpUcr3^iK*G#dzjk~tUp8roN_vQROtPCr@m;cQTXwo>NIgL^B^qfu?d;Qa!k4VRb18 zh*cv+h1a+%rV{QS)uyc4+aBTy-1LWt3Xbyke9qxoTYk#LzUW@u;BlC?NU!$qhN za2mDmoJQ+AzY%1Z0U5nQus*huPKjO1uUM0C72E%@Iz*5qhL_o3cEYxk3X#&;8JkOz zsiD|yT}$)TJlou}X!It%HKCz5m&TPtz7}*fQ|nYYyfe7u>Z*Rtnz(qtyZtT85Obn$ zqhG78)P7k8CpLt~eQjcVpUrvd>q=J=Q-sFy%)Z*jj3sjyRR|KW$$^j z+I}NvXXlRl;LXUwmg|-%?{;~G7~k%O1V4mrH1f`o z?0>k|Sm=nVr|j57Nmz;9FWL*r6P=CMTRV5vC(C4T$78Mb=;{3QcH7WLLh|86$O(S& zMJA18mTq>njcR@fGGpeH># zlN*XbyPAQ9Au74IOT+r-oGkJcl?d6;4Vys`9msm`v}Uvyzk}UncrVDEW1e_6)@-#k z!>=N{#BVQ7tG={E&daAYDEoIzxoepLWS3HXHrnSDOLR2NGXv6mX(eDxE`p0!%*rkG zz>%YGB4pXqRKBoVfMDBRo182$Anv|F6A+iWd^A$o-wXtxcLhBM*Cy&aeQ35Vu#DB_ z!%oSgJA}peJgL%i^6Tf^&8%c(ZqIASoGxw zHJy@Yq$jkM@4A*rg5G!;K@?IlN`hHR3N@;9)XxN}_+sAi$jRL3yx1S)KI>n!`s#Gl zuyb~A+g}K-j@=n*apz@1CbFO!e}vi#Vt=mB^2@tZElGT?qjcty-fPz`K@OG$4)kr^ zMrt(9auO-Fdd#Qcz?cA}D$fCaC)$4ApUom=@sw}p6(c`eU%mUVJ6j-f@wYLuvTFG9 zp5~)KOuRvoS@!MC{dhS=gcPjQviC0-tNQ^cr#SNxcxIB7BnH_hxlbQLf*t7LhHD^I zvdfTcajiz^uoy4zx%}F{vmC9zh!!3Q4c`)oNwRrGWx2U4l^)BMLC)>LOFzHp65fFR zVr8Tl&sE;gJ1r2Ae;9a*Db7mG$f3|nVt$sXt!Sx?Y5dv%S~mxc#9p(}d|p3v{wpc~ zI3GO};M4}?`4r}Jg?Z2fq*Ffyy5q$rGVUx-O_%8qW@+Mf-*m0}=bvHr|4qz+@`t$}wfm=li?+|j`CQrDN z)^h8RaLT;dfx!cG^Uj3x@2`nejkX*VAZ`J})W5LrOki1AdT=$0G3;11Lf@hJrYC+8 z)>5z6x^BHWf$hr*Tj;u46d;dQ)(7Y9#JS4>vtAT6hA~Ae&<%a9BKUj>dd_zfkAvKLDG4og>%}67^5!SUjLSP&XXEHL$`8fEy!S2ukIh8G2J3Dt2v%JiO8DAMr zVsjsJ%~(mr;6v^`eOzMsL+P$!e>P;irEs$N9(6JiTW#094@i1;P>zf4hX`>^z8`^? zSvvG=P<<>t`{Tb@0Gtpad3X0K2eZCgIsLP5DktJV$-))w%~Q8sfnzfK!g8wA&vU2W zEbZ%UZH0#q9hUo=Scwx%<}c8Fa;%e}&yF0J5}tg2LJd1)8zp7n9Vf<_WuKusv^{Eh z4gFqjvUALHl5G4Ym}O;`Cb8dRv&9Am##a2ghpZ~4C~EMFX!YrDcVSEVVztv}%2$o| z&-EDRI?fw$`QxWGoOX099%ka66i?e>gyb3Vgh|uyqzP5E54?PE5!$~~ngOAwd5AS| zM=Y-Z(j{v2WhVg8pW=65xWBftG#@ESHQJXosRh+XAr}{EuAeftGQR&Ys6afj+kJ%U zW_)t;5@yEGeH=Sc$-8C6s0BZ~cWl)u%B|~5(d1Mk1e#v#Qe;aqPpHybet2zERJ-bR z=|v(l;51WSt-8EAUC<6P>&G(~NUEvxY+%C@k9E1s+l}mE`%T(i3CaTza;NR?_EH+==|hAM!kx z4W{R3XFCBqsIS}_A~8o3kVQcopaE{um1pOMZTOya6WkiSy$yE1n07jG_~PlA0k)|l zcO*~Be_teG1)!iJY#S&P8;s`k%xW1UrfqwgBxhYfrJc#EYu7TqD|NeMOLWeX#xncV z>d1$XnKCPu!tRipZvEn;jJxnSwzRvqKN1x)AJ|fohi#pXC&h$l={I+PbtP zw_?|(T<#TQKKhPC*3*^;b9A8^{$0?FhmrQ~^aH=ECy`KVH~OW`%xSJMW|DbW>HzB> z%Z+oW_iAKdDjAfi*q$r~$;M|CSh0rrhNX^Db9^6fypYYp0Zq;Px}fzyo31Nqqeq1Z zH`Jl?IqBK^#I^vvv^?+zuX{RYz0|DJv?<`kdw8{U(g*7jIt&E5G2u)qOie|VL{u8* zH|@62b8o8?2O8Pq0A_5r9Qu-M+iE>EsVnc*TYrzq`|U5!wOCyUFuNczxxlSg5euTa zS}P0We%qL_TRMDTJo=^XS<7tF`gbZl8injM$C@99v$1h;?d9lkuC+P|4cZYO#MEa1 z^`J^H;!u%Tk>Va(V)A!7*95X-FUK7ES6G9dzVSnszg$84Cc6`U|?FjHGKpX9*d7ny@Q z&+;no5P~KK->Iw_Zpg>bbmUqZJLZ@X+z3)iK9%t8b+Sa|9)6&~Aw%A=YLe(wjuBiL zUu5do{Q7xY-rL+f+cYIN1yRb^&7}PX()5^B#cuq|6cx zYYjTbY~=sSMh6~QtVvFNxYd6uLMlp{R>AUw%$fR!(dRjyYZv1tE~pG3yyI&(XI7Uj z*3$jOs0R@5-#;T}7m~g&+NYQU4B%s5?D%K6Cz-h+QcA@FAsgNYFRa$;Hj(ab7AA8e z^Z)3`4JK2o^Jiy}lOwW&>~L2z-!-dvp6=`&!dhbrZnEvhxajv+eomjxunlwR zd@IccS1^lUaszfSBgF7C*;97PTmbL)q;{DJT_a^+H{`>z{P?6u6vo3uj9>BZ%CHQ!Kul`6eQ(O%Hc zN1s-m`(6hrnHQq>*)v)+1?VE~*1`92BW$M2?imCrlQjIE-sWuWok_7U7G){?E+Rk_ zU-){h6>?fzYwS~-iagawoyYoLAknW7y_R8T0KmzCz*E`!+H0rjhR+aIo<*jn2SLc#f0Q@0D6E z3Tj(FAN0?Vl>Hjcz-Ir(L(8su<-F%n%ljkd?3E#*6M9+fk25S@1<1}yQ?%_WRWeH#_}gmi@HFQ-OW3R@WfdR*hSn8rmG5+G zWwec0q}kOLok)i&1=o0P9!6#OCz%FkF|w#6Dd`09HtGsg38?o5NORAQ`{kG*eh06} z^dS*Y^wrdiFVL$jdiW!b@o|hemv(W}K7_wF+-cy(d*!XALB*OxAlVrzxT8}+EkE!z za6*AtvJ9jXa@t;9kZ1BO+xzbPoWhbF+9=SnzrVL>3v%LfVbQUrX9TSJ)OkQTu6iji z|D&COk*1*gSu3l>o$_q|Y5j7J{VSix3G`i_Y=qPXR+%`d!`+r5w_x^j8uDm}(1#dH zo8X8CbLR{e1|*hp0{aT}gkxL_;}ChDAfA)zRf?KC3`H9>#aptn%p7U&neOWfl+Spt z)z250|LUugeo4JMhq=S|JqLM6{a^cPfb~} zxt?QYKRgLx*8J(ET!rt}I?UI>b!Rbm(ffYM zyM+|Dlb;5kvFXn)U+8&}oR$Caeaq;V*47XTYIdt52TW&vBVI}ZqD00?k<)jilPGNO z*T!WD6pm3x^_$6cyL}EcgaeP5Gu%Hg)(qX1!0Bh@uTsKUYJBkY+3Gu%W}aylKU`_} z-Rf+RC`uY77;B`s3w(ce4m$(OU{wgDnJCqQul5z$rrbD+xjNumdo`7`unyVoyCSpA z{MHprutWjRJblTTw^8FYSfit2TtwxBpGql$coMZ}JP=}#( zwHf-|=@}atKo+ntS<*rpu<+P5g-q4zLfiK*UydlA9GQFL7cGX7Lj^i&E-!9&=cK2k za5DLBJT7Jh}Go>*Nbv8xE zX12p-Z?$&K#&}E+t#1<2t#4O)2yKdMkW3M_w=+T+`K-SLmBj8z^8%!0hG2dwnMg2) z=&X4?<_^FSQeZ(nM0oJENZor=C$C&UldJO|^N4M|GCC zc&vp|6#dBl8rb?PY%tUWVR0Br>6fngmcrD#kc=qC@tQ|KJP@j0s8v}czWrI&DXE=n zS{bz;`P9ABd#!=z4MbHV2k7H&N_e{63AtjM6K#WlOiR87V zw6a6*HT&*>GM_J@Q;^5p@|ejCzeBkQ33KB%NdddDHo!aidWz9YX~OFuD=P3onkeH9 zl=v*2;%#AJ6{jJ%@Cd^CVLg6ZhKx_-6ye7Qm9&w+V7=)UR&a3UTP?P;WyI7Ty2=kr zE_zRmIs5p>o+;XzT>z}SOA%*JFS##OgmJ z@Q&}-?^4F10oUx4Ua9HpGpn4ZF)9>D(dFWnWrF>vfRTB{R7b7*TIrpuZ0R0vDdGnT z+;n$V$Dtty^b%e!pW)~!6ROHFU@J;TLajePY=ZOeHG)6Bc|tq7&OWpgpVb@*e9Me- zhYgD~Vaf;fCF5T+GEZrio37;Zt-s!6P*EyY}J<}eEcb+2!n>YXCH^(Z1o91 z=LmA1Ey1qQLXN69Z`{haz7O8$8@_EwL-tlif8FS9WGL4r_nT;@M$YmlZ__kR^lKS; zt1)^m=LWcP>z3V?Ev&ym`;iI)4*8Y7mD_|)7YDJ*ai~jh)=Gyy!G)3)Q?BtPE-}j1G z=#jX9X?G{QYUq5%oX!4RzDky-Ccj0m)PY^s-Ri@ydIy~>-S_*AC}Q!b1NWEWpNsv+ zIhp*nUi)KaWqINn$Nik6FAp7(nvUscz{DJyy}N+NUvf)##JC~uN%H%7*Mm2e?G_&zV&g`> zwOXOSUG4mEe{eD`zr{^~->te6YabrPSyVUiG2d{}&>-9MAj6m89ecl@u1MY{ z*R*T(`ds59$E3dJVp2PVGfwSOoRIizhEgZ91$2P9TUgjxB~iLTk+^T8q&VTeSum;# zl|-mo<5zV@-`aA57YE*{@Y0A2T-$wwDzC#t4#XV%SFrxyVLSNKEy}>UI{g@kpTTfr zOG4`%D}>9*>S{W(?IjHoqv|U9OV8o@x4JYv0y&rhvZljCS&p?&-fI#T(d@s3^l?w7 zI(v;eK@$!A&KRTDf+7Fq4Bku0Y6^{U>y=`MELV&~)g^a-TmrO%85?quK^v{GE4*>? zO#TnZ7=7P%=at(T?An-spG7b7e}ZASt%gNwkG7Jo6W(*SP#9gU3o=KEU{e)silfN1 zUveZv6ba5dB3C-yO&TgSN?S-K7ruUloFq<`DW_Sic}S?W*4QN~V7-nfbbf5-Tlm}$ zAsQ^?v-=cTDk9aWZZP!j`lKh0SY&a)d8UEDbpGl@sXE1S{^do_`0bj7qlIzDc>^cx ziaRyIYoI_YnSFT_d-R?1oIXegw5j!d+n{~f7EZM&61P42a#@{(vlGjh*$kz<>{W24 z$7gXCEf;c;tGjY@i;;f?N3((RA7cqpzH4BPOlH_9=%_u2I%W2sT36LY0y(5ezCD(w zh>cN^9Pz^*W+fQLnsvr&R9`v4cH9JCMSAslPhqpq-D4`t;#hTzfc3C6AK#Lqu5R8; zfgW#XJUoiZ{ObK~afAgb-Jw?N)i!7f?i)Y1ISeYS&Jo*P9yrgpwtPU{An!O*b$+Ow zq3uvJUlTG~X2%V3n1CxFK!{0lFv4N#ux>zEjxGE5-$O!tbg9W8tN z^$kCOU?5Gab-BY5xZ;3Ynx~iUsg2aSB@fRDVgLg9sF)DZca6DaLa*x7!zNU8k7@egD z9}CPmWNUzyK4Bq``Ja%)Jt`pzV>&~{lEJRiSat^|N3qJ!x-PVd`tnm>-P5>hfjbR z|6TB3$L>$_<^HW>1en8e@NY-{_n*EH@CEuWiJkxO1i#+EE7fEE*SFz6^M(F{szf2a;;WCP-&D+cmEBdq`M z+Xl9gfY6-tm?$MxYc`LRA$-8ziqiwLsl(S-MC{i-b&}+m(8Q7zf0Knpu{-$;i9YE< z@gJ5zK;=nV9}R8oYnV#=m_ofxv(^aP3C4h-XENZs6RXa-{9=>@Zd~eF)?8e4y~)jO z@O>J(OZhPxC+22T+4uuou+>1On;B?W*X=FH{+-@iJSu@@w5aCSP!P?z%Kk*{p?EOb69JhPMfoz4y`f z@t_STDS)#t?Lm9j>K}H}7gRkKZtd-&k08mGKpbm2Sg1FNgkI3JTlGpGc{!MWm+o{^ z-d%KP`Y(`&s&(MZjPBd$-kt|T(5S(=ZqK(dh_56Dw%o3C>VE=Z1TK(l>5PNs(iK#V zYd++*W~gTb`XM;%A$%{r%#+R2rwav9>l+`$E_gGkgDv+wht#QGDdHyYK(B4uJ#40ckJ>Mk4{@uCrUo%8{`Rv`1;w_yF$<2NXfBdbAF9AX zn|SWAv#L&wOJEvx?q~8p#Tl($X0xmJm(HO{r>}#&l;(*uYu6Y;eiz#m^NX{rV)>91yO+S?GBF@svnX z{VC$f{C;|YU)q;C=ZmM1wxgHPwij@a9ITy7y>qUTnD09{ykNR^}LphuNkRE=mABDZvFTe`KZQ z3?F}A#u;nJ^oyFy{CRctY()7RhI+_zKbPQJ@`1cKYZMo+y$Oqitnz7)SEn6 z7sQ$Gvj`o>gxy#f_^%;4&8~_te-FqN3iq1N6E2nh#rdbVk@@8GT@>jy%{xiXkDs%T zH)>CV1*%^g^>tFP`>f#i_qnfEBK|yG5&|}DdOS6?tC2-csT3!)RMq9}4s z^oT|T;yO{jlfFFi=VHnXJ^i`06~oWX#Z_>_qRZ+Jdx2^>X{tpK67eao@M4OE3X!Ib z;T5l6Tw>{mflGtFOygj#p07@e`>$Q;-|gM-}rS% zJv6V@q7%T!>xH&{PRG9&1V7UUTUX|Bd6@+-`=^lSU)R{9zh3d?%4iH50yCAx5d>^+ zkpBrynGVZfkAwYdASO=bt2=|39FG%`WGZp3oMA4#n~q)I5Y6)_*kX)ci|a zIh*Vm1+7aRz5CcTol?`1eIW7SbDmRT=i=gGzBBlz)$!jWky%Qv4m_na3w1}7v_crY zasZ#yVFW0d4B}E#ZD6#CV7#FO=eZ<{_HY`08}ouMVOLJAb@2WEs_{RMG}ZgpfH}-D z;qSp4lGkDRc5gqc^F9fon&m8ZUt>JR>@Sb$TmvCmfENM;rrjz z>B7HUcxeZj`&$E$`QT0R6PU7uf(qOu2!7!;_X&zmqh;(`PR_!KnOqNK!wU1&*1gnn ziHiXxh!<3p&xw4_G|U64u+evKz-Y7tLu`J;Mbu=Ofr3$~FF}+2 z)~({ro5c@jzy+FxKwL2EjCtpK#Vzoo)98NPkwTJ{Kd<-C1+ zLpE67Zc==eGh6nYa>ijTMO_CH>1IzZ0uR)=#2Q{H1xXPq57Y+ZA2W5G9AtE^Q;It@ z0t$qnsn~2ikTnxPb!Mf7(R|G?dX}@)Y^Xq_)N4DrczgH{LHj>8is&DwWm#bNGQRrQgp zj}meDez2T*A3t@SOt4ho?fVmYcz)}FTbGtE-@X5iQ8Ci9_++uy)&d7C-RW7bG8E*u zoF)$<+v=p|67nSD{&d9tTnU*TfnX{;eDHu-&uVi{Jcgb{kNoV}(8H&-C13y%ukX<;V!q#Z zP61NL(sJlntM9o@abo+o8nyp*uM6xuI{=?lJesrcmb1uoewA!gB0Icc-w0#ZpZ%u$ zYW5=deIOSZLel=uo5|@o+1}3nUX~Ix9BKjZgVGNTQvW-aKT-`_fnptlt+CZ>u)$Q6 zKhK~Vm%0FQRSc(RuZn){h|6cOIPhxEF`C@TlOeuohNy68NS7qtoCu|T`p$8)&Ve{-?7 zK40H`CGIb#HSeTw1~JKNL|By8kpfb5Q^3Mq%y#Uq+WD2f_M%2yL&!n1K}`t&zab^D zE_!QGvZEnD|Jz@i&^W=C;b(^xJ2D0CwkqO>nIOdbUQnvG;@-%7J|+n<*Cw@Erw5oA zRK;n3i&0j08QeCQ6%^WcbgIaVA9F+o7^1eeYIg8Tt`lYKw{;-1B5KIboPkS*g6cY$ zTwtD?>boP-2UD4RrNnVr>| zEZ)ScJbF|rzPBs;Hf~V;m?WoTjD!x%e>lJ-{hb)9i4(BDZ>BH+xJsI-{06z;*HJ)N zuXPhFN9(|G5eyiUf)E$BcyRwEcNQFL*3y*=L1AiKp^2j<r9DsVhu;H1`rj}Q<$AT!JMuD46yc_w2F zKU35h_AL_gSy0kixKXvSv;ta_=QTX%Q! zfFF>dzpqCd5tln=)r5?Gfrz)vdhp2>tcuTM&c<`A#Nw0Y-m@*?>p}1*YET7O1j9Z7 zk}f_dwU(a5H!DmRkc9=5!$>J2=mgUyB)P%0ugA31da|LAA^0|%MZLckMTb+z?ioS| zlXw@2xriBlO9yGwq#mL+P+$lUvpf4BKn1-ICzt+|$@_H^E<4H2UT$+~Z!hJ8ZRmlC zwlo|LWER_o;2b%Ji!EZX2?VjrEOJ@>!-MV{NN{V8&WAVX3Os~?#k#T4$=eRvg1YG} zj*5e7V#tgrm=2S<4C%dcB#$4STEzGq2>HD$xeQCy&6B6{Dom0^-uM8{+OS3Sjp%m8}Nagl#er;!nS;I88Kg=^hZ6u7k$-r199qM9W~NWxIK; zLS2U2;?F(=Q>2Zr+ztxjt?y}EPVWsj0Df^TOhV8hb?{PFC%VM{PE`E(EC}01g~-We zr1&H8L;@EsokVWC7P%|AgergjD4j*o)PX|bf?(O%qnYof&S@Z>0MLCVFTV(=BWc)! zPEp2}mATZ~D>Q4-zWQh@_QhApV!S!gI-vg)Q^dKuW$DQu@Vcq+3=?RkfWdqVcc7jxCcJT{kjmj4Yn3Rlx9`n(`v{zW zr<*v=y#6~@NoD|C3V(_lsYVoc5lUshTy!XcJJ)k6qZStfsdLIsXeYn12G|WDX%4{R zGq2EXlx$r~NlAg8^p}ixMoFN_EUYdJZKEfrCJmqb0JcIlI-?cT5}F03O&y)xF`%Du z#Lx2p6|rI#IiaD5QwF?f}bdB4+ciB{V?^{TQ zEi12u$e7EQ&}D}d?Z|(3YyFoPR{HbF5q5t0(SYKvQ@RDFBa4g>&S?3|w$FF)Ud34m=$3*!>Xn9mVQLFaq`OG?(| zG&R3d7!@Bs2e0fUc|m%m+rhQ><@_j7g`q_4Ycl=q)$x8!y$T<>=q`=wBB8miG&ZJ4 zdaALToqy+^K!4!`!=11vt|V0x8uPmHUfB&%T@P^)>j%`lhRbVDfvD&$zv(wF>j|JX;2DC>75)=RG*VCf zgYzYQy&hpI3bT~cM%@tSA79ls?@pgK8f-AoJM#Gf7;o>pbExXl&Hl!2_v^28$Ywq{ zW!#;Fkg}BLe_D;;tG*+68rv;A8O5%a-3H*dx3bP;_f@1W>yE|~v{x%Qr|ZN#mNu>@ zTo(B<^YNz0S{?rZwvqCpYPrjzlTO1}f?$C~Cg)SmyXR_eE7?!m4KE`Ux>PD)WICoI*KD{LVQ zZXViV6q(1vYWgLV>M6EL*ivsan_426F+VRBSo+Ez?wSNjj58A$SSh7k;OFRvjR3p# z7JzWpi68C9ynWo*Fn+k+hB93mu)1!QDxD9C^dj@i{5EMX!>Ci77P%}A(JVD#Rn zNy$}e)%5)#<=#oObR@ zNRsao)@P;2OF7?{NUsO=sB)Peczh%C@V8l_0SjRKaY`Qh1GWGU1d8??$Mn&|Z z_g!{dHc?`j!(F;D;Z2}?Gu_29Ki5M1O`~x@L6|=oCrJdwZ%xitO-F!)?YZNg*nSl$ zg9$&enkjl)Sxn#iB>2`S8$)of+db)y|-)Yhi* zfV9BkpACkuSOUow&-CTEJy*}C9?oaMR6y2ru$U3K`$}$r z#l3`!(wTPyJN%e9SI0(zrR6S7Iaj|oxP$-SMp0B6nYwAB+c?buWJ1LxgQ!cC)WIUT z|2A%dkg>mmu|J$G?b=%CmF1~wN+?&r(Mj$LCJEac>Llm5xc2w=eRPabQc#4%0h7ay z&}ExmE}dB&fvNo0BIZG46sBwG5^oWH^}yfW%#-nL#l;-HlSxUCm1-WNhN>qS&Y*XE z3PnLTq>-};?)^b4Yh6idCKZ!|5LJa%^V|x5XHA=EiMSbI3SK0qWKps}egYQ+%l635 z?;Tg?2#;Qc)_wq8{V?7EF59nL3dI@bD%}${OxI%DZ1C6)X6!2}Sv$D6&&s&Jb$RbQ zy1Y3*j0?v4MKPg3eI&6_jT$~q?R-M{Ky)DPaZ5z;!KyC-S8BO3bYuQP-KQ{ILd8fA zNkzF;?2T$Y_l{44dd#J{U2FZuxQ^nkZP8+_-h9Gv;EJGp80iV#RTOR2SdQC!0o21% z*vCfs!gkElhFy&c{;iFMZ?kJ>%*4X_&=E7v)mw#XKivubnFh-lJ!w$0RYp{@-<54` zbbSpesaaIsA1N2cp&1UU78J{0v`e17cE7?xrkA%xQ#_zg{4pEke}G%cQ{HCKw98pt zU0rIbFv4R84mvrUYiNbeU=V!ZS^r%3POGEHg?q~Wc!rjZcg z`0KTh=7y`C@m?Cq9BcQ&Y)65cf$}0iqa)5 zEjiLTgn)E+cMK^p49vjHZ@Jy)3(q;Zl#w?8IdUegly#0mG)_Z<)spN!K!5R9$6G8=91n<%~Cn~lcogfb#wgV;ITrtbFw5F-|J$> zlee7a6u5IdgG}?yu_I+YKB~NWal8?}bux0;vgs1e0cg0WU@MVu+}iDZ)y{Zv`Cjr` zCC`)uqTBE?-czfCX&<^Tw|st_zY09CV*TDFN$q;m_uX`rBX<+h?&urVPcHnLw|AAV zEpau13q0DCe{SKxZc*9>!E85%-S^0E3l8G&IKV0@g(oznj;ZoU79?h~j{RVKY-`Rt z!-ey)s5<9!&w~2aBwEhrElrOiLq0XPP1x-`2-~{X{hj;m;l@T@7wp4f=uCULZK=~- z&kB}u7il%c^z8dQ)bx*SNj58wp)K1(%&^1|<|r>lmzwVW_SlAMQKcr~_o8x#u)#6VpG0RmI)h%f&!J9Sw~ z4PGZLQk-zFI(0~gXJ_H*jpa+w2#(20L#&8nj)a}xM(6xj$wi+)>P)!(qupm1?A zGfEOh7<2z84^zsl&Pwn#7QGT7ub3s57@e5-00^m_rp#nl!$jsIbCi^*-;8BGQ>u^H zD+DlnV^u2k%|FRffsjVaF<@KGiWOx%I|Ng&nGsq2MDz-|uQ!0T{2qu4=UM2r5?U!# zs^i3KUC8hisEo1xWb#f>6z-mDWFFYg+G38Ub%8@s_b>by=0)r zctp5AcniV5%nLVbJEd|O;T#F7w@LUQo); zOx1gl8XznvcVZNuZ6HS?vAe!I*6sOOFlI8Vp@~69W4g{4soOn^d46&BeiW%=ye!-q zy>k7f-PT*}m_mS!=y2+5YmT2)U{i9fAt`HL!$c}-!or<_c>r3ehdn#F15I6|;hoQG zvE4eP@#SBuEv!$nrs8elow}DGd13t?pCDrzV8h}vMwKB@S*`=iJvk2vA5@+Ezrbo+-8D!oEzsyIslk#_{p-ddr;GoCw`<;rOwiv}A-F3E5U zbhL!0YcK!C%}`|+6yct%U(iW35o)3GHG~LV#KA`ThI&`R*-V$tr_y)B&htFx^5J2% z(uMvjmv;??M3s><{WhG`$JSO1v9~(saLL>Jlkv)Z5wnh-RqVm44`&t*wA{mk8=O<^w}A zm*%Zo>9t(b1N0kp?FQniO?TlDqL}(T-K2qcC2V+}XK35ZR;ulN5>_f0XaH2Xax4P; zZXP`>H)mH(0d+Q9MY(k}T8Jqp5=U4h6M+%FSid8hN3XtJ#lf|tiFxSBa8c z&64wF16YBEBh+xzVNOu{+J{V5|D=x!S{TLUXG= zl($`8;LHzWJ%2D54HO^_|mrpIE8hTvvyrT}wNLnblzD_KWb+=j`2(XI#dB27T6_Hks7K@xwO*WgeAtI3F?P<}p(D zKuG1Q4ZG;UJY(v}716MD=Z-m(BEZzRhcTbZn*_!_eroOCHlfC$zbn`N=rr%oeU%4- zr@^f{5asiPMgPSGU?Z3=u1W_|g>R_|?s~XFrE)A-puw$lD$VB`Y_ z+)EGlQS(CX0>|TaVKyauSaZOQE0QPO0b=OKkHX36Pl1V%?&(SlBM9O@wp)$U25Q8L z0FYoOq92$O{t>sAg|VXhFR7;MqP4m#`MQODvm?(Ez{i6E&G{Icf5=Rv?`$V~dl&U}EPGxc zCfDN;g$v_E0Q7&?4WHSJfwE$O1qt{@c9MO#z+ zI6H^?&nKQ&aZwVAY^alVw_o!fu;rd)rR}9H*#z!b`&5VVL?K3w3!AnmrS^Mgw;-(< zjye|rW+E;XVV$-YvKOK{I^VpPPK;9Y0S8ZT>A!9~%iO|u{C?$*NdQM2eotv1pbxT< zj)MVNvnRlxv7e0iISSbQAg>dE64_J&x`tw7yDsa!iH87Da%VWbIKg=NuQHL0_O07K zTJe&;WK7*WZ1qugBVzK2%y`5Uy=ASAwR1euVoF<|7K(TLA zt=9yq$|r4J?41N{l#A>z`jwnDUK86B1EnTZ<>Tw1HI0VwXt^EP;P1qrIXGen?MIgU z`Xw_@x?hbJp+zGjbd$A>DMNXsK*A=B>ZCo9s_$%&llz3K!BpPppWfbP zu!Vj@j_8AvFcIU(Lod+|a*;8U@s}Hw2}FqZPGL0NhXdb@0@#^i8!Se=t7TLhfjbx4Q{{3nRuj^L?|Up6UY}8y1vpHujy~&2sxL z@pSbQTzrx1u{*Iv`eGeuR-^$f$y{R(;*vkA-Dy97{LI<@r9-;d3>0xkV}U{2R_G#7 z`&QgL_l1X=Kye1fPD^?;qaeN4m z%YqSg2J4?;Rx@(^Vm$WQE%4e*f@7Pp{DzSF_7HR9BBDi2jbz?vYi;$-g@O!nvZWpI zx?+QB)~;qPTt#y?x1G&{^^t>>b&DOs>F9R7@g=cuaXk@T{8Q!2rV*Z3&$>mFK4={k z(9?g_4iRsL8sAy&7EF|#Ga-WFf?6VNDQRZtQVV_ud zKP@&Ka)8n%w1W(uC#)|Ctj+pZB}e8xT)0NDNnU^$sVXVXn_BTpDU{88%PY1Ds#)Zm z>*+czd)N&SG)w&e=1;rZ%}-U`MT>QFNd%`^Hl)QATs-=gLlgF4G(F!ax)8^YFH$mF zU&|SUdZ&xop72c>gd?I~Qtu0*N>vP5dLE4zfuj2|$HoX*>OPeF0GaKY*^v~5JIv+e zV5?4kh7qG_enF*9!ZU(n_0k4rBFfMHuo@sbo*0*q@ey~^ec{I;>J7hJ;p7&`hlko5Pxv=DR$EcCs6{%E?=?D5^trgUs#H@U zw&4`OP<35+V6Om;55;8jVlsC=9FyaIcyzbD4LkK+g(`uF{$n z*(Q%4ZuJbzytWr9r30qO8*S!yForS??2I-0JfbI`0#110j6Od~B@RZ*TeS7$nAivl zZ1CckT}2c zhEJ2?>MB+GlET1Teiml4aZe@-mRB5u!&sFXS(kk}i0Bk{G$w!VGSgO?QXQ$dbX0!*kd;>)NA6BV_f0%Gf5Wommsc?ak zc^R<{idenc_G`P;yzuGKs07$PvzqTMv}=(#{U$1bq1^X1*ng!&(%ZDF&bF?gH%D+^ z)Al%CV0yZWy%)sGNSFLPlqkffd`z==93yOr8Q!R*p~GfX|o3rJU&_EPR%@2n~~ zm`7wkhFX@;NUQ_7T<1N;$or@93v%xwIr8Z(>SLBlAA1G%l~7vM80Xp5KpfM>mrh73 zu`-n_W4^t<zUI1$+M z_0zTW#_8|oQe&T+aTpF4rYgHa8eL;y`-KN}YWH%oF~JE<>`#ZA957EYwRcL4$Nz-c z{xd&lYiv%mNLk;{CNfb;GCEe;Hmq7znjvrGZNhL8_oX;~&p+&qi3lav+~ffYoRJ@R z>ty=HQ8uC*4N9id`y+G7yXViS(>5pw>W`y8tbQC(qnaCd0I=3X0*%|A0qO8K>k_zv z9Q2~NT%pQ#h7IW7pdzjwuC7q@3>KCg*}I!32}8Jmu3jftt>E&>NPfpnKrj;LOo7Im)ssEo0QXYr0X8%hx=p_@_9sz*5r8DRq|e~d4sP52IjtAe6q~KVxSdLKW(A3)rSb-OEGv z$7ZZ&WRQoQG?2)6TRf?PInm%G%k6Uor>TpMG;Zo=D$exJQk@OMWQ`F3b+ja}fRkxw z=0=fgg$?&475`}g&~J7MgxPtz1~{zF0fT9jbYOyV;#@O z+lMzy?p;KzJ8E~Mn_6M*3E-PfQ_ehGE$?0su} zxJE1R^vj;6(fjqrrLldIb|j2zrs33xr|00yZ@q)-h6MV3NvGmBAx{$VFECx@YLe>| zcEd6yfq|)Px0Rj-YcqV~HSJJLChsNdiYZC||#1mZ+dr}W znTppCqmyOwo}TTG&8>dxV4v~Dd6|nN?1E3GP5J505mWg&-9*PN3+PaC`n6co7}r9_ zxqJ1e+utN=57r=u9R(KwWU7*|fgXr%2sxdzJ@n`B`(bsZ`Tf%N7iMmCCxi>Q&z8Yo z-aFqNxUN0z_B{#j23fO}1wW~=1%c-i$uAR>qqYX}J%QK9>p+*DS9}yE2hy~9rv+)K z_v?6@`Kl39WqqAjKgQtH?y(IT*D!7a-p(rPxjv|u%Hu-&{hXhdu2A!%E|1w#LUAa05CjwPgXKdFYHFL~ zgXq;{lh#Bfksc9uWO+${lptve*7Ex1*+o)U9PWafsir$0HM6Y3zu5miI_W00Q~kKN z)hNMCIJx+=ut2+M2&oUZxUHw-9DFBY1g1VRJP)14$T`DjcVh+9_pb`t7>O|N40=ZS z?GZ9ck57~Af$#N*^?oIPSV+SK1#27*W@LmfwQuXsAPea8B8GhSEa`~1oqsH|hx&1j zi?XN6d4u+ z!tZCz=*#w6`-ama_W$l0WW098u&TFi-om}T2W|hHNDcPrXPm8Y-4;en;Pie9VHJ-7 zq|P))yVSz1aYV1+*y#^WZ>AVZEF;Gz7Cr*dk=nIFK*H_r}_q5!d$+Tl#wSAOj<1J}GiRG^OhT;bP);6{b-B6vR1-NN54cUImnCY)u1ROD zt?TAelgyUUJvq;6z4Mk6sLQ`Mr@l(%PgP<+l%iYXw~2NO)7%-fZR#C}3%o*LE9e|d zln3!dZ0ESqc1fE-HfnjPyF4$Fo`61L zzY0HftLI$%S%)1g!C=JvCPk8E@vgWOIMKS?XP4SpSIRPR1SAk{W64_}I-ey3l z_#ya57DkWOC2#ErJkgo^DsVQJyUTh179W&p0X&g1sDZWdt7s52&)<|if35_X&)fQ9 z7V60xQ3H_ZgG$?p3b<#feI~y*Ou%SIHB@)z2OBNG4_k%E>%3oeD$69jgs6DPB{q3M ziCTmcE1wXVc)HFZQyMlSswhih*RYf&WGM8o5wH72S?`3>3_w)S0M1SqFP&F)z;)M2 zOq#L}dzYJIy%$@hyX)$U_s6!U3f*Se|LEK3Z#lo?k}mt@%(9#hz0$iaT&y{)4lsoP`(jq4dj<7oSS)0#oI1THW7_ODAyJw3>(BXV@=`Jr3%d!#C!FIL=4WEO$a~v zJLXkq{h9*@b#(Rz_OFQo^nQ*V--2EB{U!MKnc#}ed>yALZ%3a2uPyBv!5=PCS|ru5 z@r%t3ZT_#$!Sqh5A<088>z8{)SU_iX`98u^2}#0Q$t~N#SoeaWNs*^v<-3u&`A!Wa zevb;AeQ7*9miXPw!|CPvQg_*n7BVGaiRPQ7iUQnuUO$h?Z^mm6-8 zKus$Ch%wd3^!d}g{iWQAPh6S?PSv-%G1A4?bc@@NW)S)EILJxblE{hRl)F`zr@UaP z^j^?r&`CMNxqf5q#`AiyM>DY{qZxfEWe(b#*QEMShE*3%{c4M5MU3=dI(k-8e0LUue7cL?0#k-I7W(qG;j~Y~y;7r( zx~7c$JHo?&Ih1-r@@YA_zTXD0*fw>#lnK%D(%<9JrHE;49 z>6ffAvY2?lJ*sNvX z8=*o(>w2S@*A}gQtY%PFG)F93bpDg%nbbFRZB0nrfIry|tCxD}=#$Ccy5Mb0zxKvuopq zcE`MSQ_;LrQ&G~F4n*3n5v=_jY8PD|7W@UDi_>qWugIq81akd$_%ZTEeT@TH?w+y3 zgya79&PJLQU6N!zoTgDVge;}hC5GPMyK!FjnSPIO4}rj)r26x^`)c#FO;7lTZ>)w8 z6L&sjQ(ul^PBrTs(YzR&^>s#xP9II11;&rtPUEB*dnW8-7&mMyE)q}EvBRbMNfNgF z6sf4qdr+`q66BSQ>h5mOqm%I(DvhKJcYu&YLEKr{Egip=ZE}e%LQ+tc91_n_d1HC^ zhYR?kN|ga)8sGwn;(#bLa;d)V{o&V=6W0~H7CXy_@B60}Gc*|;i^&@_Jc2!spl?Vg zDn+O(ye~oNI;0MBSvLof&*>XY!7X63STq-+LawJSUO%CC#*^T1Kcu9G!Dk$)|DVIuCqJF!!} zU)B4$CQB*zR|n36qScoDJrEWTgK^AJ>PA%pothoDSj9Gp`-Mpv?lKmXCrduS*R&sY z?wRA`4u+m3)0ByIHGucM-0gm3T=Wdhi!@**nKdk6QVAD)z5h%Y|9qwOC}3CI?YR~A zLc$VK$-;~~I36CksQjE>ri@#M8q_O$ren*v_X2hy-^>7IL@I0=C(ZNE)f-#cF{fk?fi0sbgYPww~pwPE_kb2=d zDZ(ALMcCMsvI|l}Rz9-uY$WsBu_V}TmR5E`)&M^x+S))SS9AMkM39 zUF3)KPh4l>ZV8pi&$bedrN$=wtTOkZ#caHtl)lZ3C7V?W=AWJbLjZ%lDB|eFI(Os2 z$0g$VarAeGr>a^_;nQXWYYjZ@KtD)tZ7TiwzO?GX6BVU8P{zpvJL6mux3RK`^Xh)& zlJx)LMtvdn}^M5_dN5RF`@FuhaBmcjwE zjq}9c>TN&|GK@|bed+5Bt8uO)lI+}F+>EK?RK@H}l7(5^*(0=wkh_8s+4;;7fQ&Rn zf7Jh}$Y8h&;14J9iel8n<^lRfQm&CDGO!Oq$X%e9<$4SOW<h@ifsuj`hLkp4B7h2`$_@UCc0FG2nmqQ=jhWYKl!n`O9H;Ms?_isz+Q zjp=mRat|e74i`gQlB$U4xV_$6;wq}|Vp=XGPl;s#_4pl)YxUFxk73h8RdX%6>?oW2 zLA~m325F~)F2YP?Mb3&ogbRa?C*1oG{ehlBY|%rSuN9Wxt4?dmpg|9!{kZxL%%~v- zP~4Bm>HY#=-yu)>8-l|cY5Tl=v4@+HJi@12D`1_j+6d)Cpr0fUFbjQoz+8duzOoUsJkB~$kVeda7W~kltmM2K>r!_h?(Y0z zAf$e8+QIXrqX(nL_r!66v&K%LM`inr;H#7Jt=8qhZAB~g*$q_%%w_wCO1D7F2A8y3 z?S6#0ScOAz_M;fYL!s4(mb~_UNXprVxRBR5wD;Kfy0Bv@zrmL{D8^pdR0EPO)QR% zOHDF#_Q4mV+GV;sjQub#XL*Af?9Q8`kL8TD1%R16Aw4H z3IN@YnlWmnPz_zI!4f4tUc|fl#;=;bncU(2U!8(4H3UuE)A!^VG}M54qVclABiCGR z9xSs#2wUZ9fRgys3D%C^{1~#)^Z6jXCDn8|FpOWs8@j0Vo|xP0?c$EXnj@&))yzA9 zcTjnIRE6w3J<0)NY*`jM`mj|D;(4&$zElubGY`bI#06$$tdg(Fu0s+ZSp?PE)$a9S z&sa;CIg%~zT_!v`fsm}ySyFVFw0FcmIcg$_Xllea83ySEzaq%F3bDu=| zeB-{MZC73K@KM#i(lqPp$uekw+5JlSaG~2bL3`c${Yt7O=dU%+B$eDr7-z5I3w?f} zOC}2K#vO2b1x#(Vy!XPR$R>7=v)UbD6NZ0p=Kyo|4MNLM{?E9ms#e;2uG0|qo-Z%J zL@6?Ia=vG-!%ypZ#J%ILG4N~M;?=iGfF=tYd!bIq4geM9{QOFQcOY{BJd3Y)v=M?W zs}#P?bEW8IYw(E&I>LO!08oX7P9>h5Q42)3jN58(vQCjdJ3Wqxn9jJG>RZx;(8=S_ zBz$GSUj#)mT)>LvQDh%p_YQiJeIOAV1WFYj%GJJLt?|07fzUbNo?~X~ zI2mKT5f+VB*0*A4+Y+0L*Ah4v5e32&JL4W-eVEH>qqNRttlHa=4$A?h0nA6K#HG4L z@WMlDX!qPg!rnme8tnS3^({(+$w~m@$s^3i-d2KN*kIKG(PFHUa(5Mh*aYuPM}qH* zz;8(Tn?m~CrW*G%tWY}b#0Qo96o*2F{Vg13qP#Hl7eJ=MZE%jm z*JhKRyRzmzTO+wuF82C9ZbD?sV%hn;yT`L}hM{-t5eDGzWogA*H>cJ?Ltj)6Xbu{@ zpooV^KfdMod4Pa#p{Kf0IIFis7X(+^FvIkUCEWdxs%bG^mf4@0gxMo*; zzWHa-j%3gI9*PlGqU!+u#+~k~_u5-}WS7^6%JXaC9(Z;m+>Y1weWlwsq<3XkN_96O zP5kkS5BbfV>0MYon>&uLLlyJ0XvYnkc0^P4RowL@k4H*m!KJq&Yg7@Vf_;f(;;ujh zS}1pAKg2>ld4UX$t;ClfHul*f-L%T)zCaDuVVNvQw7gCSo5b$=L-T6xu<7 z-ttdnaMT117M(22)UAK8Q5Ygtjzzqy236v}sCyEW$EHG_j?c-Qky~L0z>{J9cf5OL z6dpd-Rt2K#7=S!o>;wLYMfwE{s*iFeXmUwUNkL+5dzr5H0K<>%hhrJ`zd#OcHO*hT z3T<`n&_@HcP4b6L*Qs~EmW%*KB(S+DwbUI1K$(C_g0MqJ*Lmi`kEX4&g&l`CyL5To zEV4d{ao;LmBq5W`wgUZBY{t^p?P3W8d_L8CID#Y%^LpM86Ll(0xF%$w%+wqiTN5XC zIDnpDvGHuJcf`5QlVHB$F99!iKfaBpZUVi^i#lJ?7Vj@b8c5?d?3}#zmA&C59&QCB zOw-c6pEU;(3oxYMGc@!FVS7*Lfs4~PnvHWL&z-@XKkHiL37hP60FRj@U|<$ls1As6 zh15wRN0&vkpq!Uf%>je#-9B5`aQ{j(gsv(!G<{dWe5ri(2ujcV6O~9yO?k)lLZR4( zh}{;~lmBc)rb);yQSw-3B*MXW=&D7Mf825?^**m{lba$LkmSDn31o}54;F@R9&{a| zQBttw@csGqHOw(ij`x-?g>_XKWu1rvBby1R8ffvQs~J;$RiPf=quVQePx9)TZm9Lb zR7c_Vo;EbaPjuut0E7VX<}tRbP+05A@6~5#74!Qda=%WUhg!Ez=?rg#Yr?W2KcgRL z{T#oJ*n&iETRP0|$amW@x{GgpRC*1YPLTAgbB&++;Z;T^&~HKB1R9Rk!OcRRjTy!v zpEC4E`Xr@|hJGFm0Qy91O=ebve0-k-5Xtk-6*qbullmWoPo&ohu1MxZ{DsbO7`*JZL;4v=GTfY& zqEvE%N!Yy3M=lFz#S%pT{7OGDxBa5C)H~TN!gXq2oa7diX`CwdmlDlOyQUhXG3{Cc zqf0@I{Rz-SrWhq6!I*<`=hfEqP$Hh18C#3+R1-9A~q%t*{!hxNYEQB zc3s(>N_@ZFliRp@j?isan2qFce%E&uC|!5;EO5oSCi`sUP;7f86{+EbTOf0WXZ(~G zm{id+i1nsDv_{->m!x=S`Fru(!xlBpx#VuaeQj|s^}e*@RjQEgGjq$Q+^urGrL7dx zZ(m_Or>{}xQK$%LP?}bd+^AcB3*on}<>ib7>V2O>X(4>wJ8Q}Jyf3gJ1sO@Usfn*Z zuURRyD;#_JHOM?~cX>jZtlKU(p3K6Y^NW~4Ehw}4UfMOPS4`og0e5y{gW7ZRTT1dd+PxQLj*MBG@eYsm zUP$L;f$DSj51P>q9wcZQ_6V_8RUn%LcO#NC1o5_0-%lO7AAu%bp3L*L7`676{)9X4 z%%h~+$%ZLyh)ULxt;FOA{00vwM0|gKTps;tBE-D!ti!O}G>pV(Yp0tn&-F>1C+E?{ zV=GXj8g=m;M{5UHOksrm176UK@t!v{t>GoXH@DsT5_H|5_pEfEukr`-fwPm-ox8>< zfG4sUVtUpu#-%BhSfTj6&tP9fZjc*bTB*t^$=D2 zOzf|U;l>}k8{re_L3@qQ1Q`1~JsqLrjSIp)xec~uceW%CG$|dtQQpqGl0&{A+M7iI zSfHIy+-gxHlFU`GhQfSexF5YhNg#H+Jy18j=Y>mv@o%&g{A&{xkerczHB+^``wYId3>`3=vvlN|Y zitHqKAnA2%h0bP=@>X8>6GAhs_1PW__IP!)jUdZ@F;0q`Fw0t#<~`AN9QK9eaN1s;3Mr(TmGrJWU!EFs^V^gTQsy)5$EDmq zn;Ty1sx@3Iq~IbqZqu3YT51Z9NqkMZ_L+n>1&y1JL9+e0K3T%2UCeNL7~8D>M;>SI zC0F0CsN;rC{M>Pd3H2ksAEn|E@elE3H!Lj+dfybGsocsbPK=3N^{VV8Zk`Vg`rbMf z8z%fHRWlR2AcR91Qi9N3nw*!U!@a~nGeh)NZwusme?+o>L_k{kw$P{bDnYDKVtD}C z4WmWJKWtXv)lMWZJNKr=b6BzmwLoggw13RlA6J~Ca!JoZWsgNIiBFYlm)G8&zjaKA zY~iw9R!#?Uu1Bt`nwNp9CZ9!BzHZO8Jz(A7tiwG#D$18a7dOldzv?u?D&D%WLIv|# znpd;=$}!tt@oG2-oVtY?T|&EkvNcWQ)8nP|bGD7jZ+7d$@x&TqD2SQnEYM!-tMi8& zGZFDA$_swhDh@Vt;ZEX!4)2kqpY4nH`Q23@^W^=4D#U`9q2Q;RC6y_yd@`fcyeBV}WEG77?*lXAP^!$ZuYZ zCCUdTkU2JtJ>JL0y~Qn5AFHS1Iw+lvhcpA9=!$_Spmrx8oH|k$#;aAPgDZRPardE` zsJ4FuQ%D_K3@I9j?POTjh2`|b_v9pixyQzqaPAh>JFyR9R@*ny-R##=MfbaqB7QBn zMi%xPYew~cMTUS9r!2jhp5A1~o>xyi8U5H>w<*~E%pgzwEB3!uJ*a=#54*#m>6@!) z_sI3@M-eJZqaOwHvCYQj1vD39Z;Tl{t(_-ZCphRR=7h(BVO8-u-R{^r!5yb{dzZ6e z-w!r?85XL+o0klIEvZVZ)F;V4CDMxNS0X?AH25{0@8!6v5Ex}_SFYE%7}6(DP!FBRp*rn;F_cR|T3Oetru-~~Lb>^F(g~~Pg<`9&7Z*iSq=i%&+dWvV9@$JLw#j~6OxNT#@$J;jrj9xoFj=yl6@i;1KH2Fe~VZB#fHGK*F}#VxbsJDC66Ja9r1`qx$6sl*8PTdUQ3Edu|EXyyV)QXgY&$4!k*7S^Z8| z6342P&^0n3;oY_vH`5^_aGYUHJ6pGF?Os>ia5wTMfmX9(=*Uxcz?BA^P<|%R!RS|; zs=YVB=lr4PVp*9V5mvqKvYtj|9M$gX0&&1k?tJ=Ewda=L{aL^0UaX5!T+CRj-MfkT_ScXtZxT zLLI(*nES6VRUjixqI4L0Css$Gj^|T7*FRF>F{4fuD^ZIUvEDn z33#r_;we21VfrZKKgP|e1W0KFd-OghgXMf5`+xMs{)H~! zzkkm~CxCb1ge=kX+;P}4P}6zdQ)Io>XDiVn=gXG}4+(~|)0Jn3=cK&t4P-hpOi@4Y z>G0HN-rs!2|MFHQkhB54MVBefZR>p!+f4yIFOM(j_quW~#h-o2>uJdU{51YC;QEg= z5cs`-dw($b$K7KOwfk)k|MO=2>noX8S3LR#y(>ysn$PycHtKTZ74ZsloQgvJ`yKe# zKl|&a#u}eXONSV$J7jk<1;BWF=XFPOi4k$HtyA&b1IN!wh5t{_+2vsX8tvuXhCm>U zcRK6GmvZwgCNH^d)LYX_%F}(o!!OdS(7W~TMZN#=+VKhN8e~*d;)FVTcgEMa#RO!` z&Eq8{+#AJ@N}X(qDVhAo#H7dN{+GY~x0@Wm!=|pU|1#o{NVPY$_+g1_&7T)n8J?CE z_6)E+E(5OLpj!Wn^%#RbH#GFpu5O?<4{xQ?G^c|9f0v`J{a|cNJ32m|hbMca{fgMd z(w67gQQ?OEfCyQ@I$&@@d94=zi-G<2Gyx#jT)q2r?XzbW08osz_eQUx4!&f%7pSuK zD5a?5Nz;rxaE+Kp<<$QMjQ`(m+~!jP3~Il8EQ(*CJllPLS=Gh#PH38%B+TGKaX^49 zfQ96Gvsm=?^#9{-RUvEu2=Uksyx;&06u7Hzcixo5{9d|$n*7ftBF{rTV)mIf9qUQ6 zD=`eOCo^ts_9##x5~J>3Myz)n190sDzMT2tcj4_!U75i7_6Gqn>~7_Hf|FTLC)Vpo zb0ZF33Cic&Kckxa!{Uwa-)xiX;DFH3s{pT4{Y`3ALQ7Cc&-XCvryE1jnoR8M9cl9P zfest}wkbWU5@Gbi+i$L!tR+Q`eYShRFEPV{AS@PJ{bmC2?hbn4lZECggp!4ybPw*H zefKR9Xen}y3oEPb=3Ha@`0VdkjXXFb&uUbyj!T;rU^zO&? z4OArlrQmlRD^UQm>$@~vT7sdg_q6ahdAXmlt>-+C%J;}o;cU|q^jP!HzoYrmuYeAo zoAmK{R{s5wO(geg>eZk96DIu25)VKJpG+&}hsz`$M8DpvIjf2WhkooreqxL=UeB${ z?&UN?j7wHD2YJ5_ihXbioZFKhOSsz<`on?L8BN=TO~X_FPGVa^GROEDVnji-jI7enbu=! zvGM6h>%OGd&FKQ&urRVR+Zl7ZH0uGsudsgR!6p2nz~h^u8SOmIbzYS096Vg=hNdia=2px+9|Rf<3Ax9*yj|Fo_t{JM^&5O8I`?bd&P z8+l4#a8{7H{P`vovYNT*+qd~lX(SRU{6^c*keiH*Y*~l}Iy9}z)cz3^o0Mb~{Gs;- z88|2B?5=j`0w_{#kU2rcjJLA>#1z)~d_UO!f{rZdYT0gugu>T1<&Y3b4s z12ZizFAG{0$X<_6x}h&y)}ZleDQe`Z=4$Juu(F&`>6&ku5C5HK;a}a*pB527NIm$H z7~s&l&&n!p?M2E7;V`Rp4>aHGx^<0!tw_1I$&+98P-kuT@n#1%-vmm7IYbyWT`% zsClt_{yA|lK_4@@JF!A+CSRLMoLQV*eugB11gyEVI>l!>(ue9YmHk?snKyIP zw_@djTMYlrHv4s2E4(bBY~IwsCTw7++qtJ z^M4bTc>iFq1iy%+_CYixkT9n*EK}BY=+AY0Bi)qL*pKrX81-iVzv)NZ(EL0{pyvsB zz%MBNY>O6ad2u9z;@=&AzpSZ^(y|(FyiEe^VUkR8b5w+l zfl%@%EbfMi%%6)|-^*xpKc9)a+yLTu?zVO`Q2L)8j*BsV8Wr1Kt$hNXj$Og?7jmJa zNs2u6+t0zQduuWSz==3Sc$}thO(5b$G3Nb%3?C&Q*D=D=^bP-SiMYvcISdfZm474O zBd@1m8RSv3Cw4v3ZkpES9Nsj$UCg%;p#RgB^zh*pdj$u+t9v;wsZGrj{;;$;zMLq@ zaKqDhJ=^*7e=;aM0%L<_NjjH-tM!6%feb#$#9syNgaD@_W-`2I5Uip&xb1Jp>@Pz= zx(V0ZPxIns{g%|sUXWU+0|r~5VxyvlRJ~0_{_`gQ<`YG*H}%M#li^yz9&d+;Thj^GfBMfplAj-v;l(@V%~5PkIy#o<6rF2kYU*ZtCH%K;?C&u| z0MFgUG_pF2x5leqW48EaB>cw&T6JM-GQJwMgZe++QU>9yuN*+E6M*|J*J>=CQfXxDg z;SQ+5Kv~r2J%sR_&82|pX)6d&J1r{W3TC#s1pd3UL*_2Y=8qt>je7bYTjfs-UR|y5 z#N!knb6>1nh21#1`dRl16Ob+MF>LMSD-1!zrpyVbsEBu~NUwTTO-W7i{q2ou@b_lh zO#8#lCt~yQqX1zF6SM5oU?!X6=;o{L1fPCJ|4&&K_cHd$idl@Q&2F>Hb2J* z>f`E{|GU*kJ|#e3Q!fM~1RnbeKUcB|_>33trY);CabnLeUK$ZN|H~u?FJA`KPFO)O z-K(US=XQ1|p8L(r|M{q68ayNXQw?I&A^@!%8GlOfGDe4*I*-k70KxprSd5K6mz2l> z5-%*k#5BLdqI!(~z~=JZJ5qmROI_Tb|5BRx=l{N>m*u2!qiN&#`d>HH6o=Kw|8hS8 zAUC8DbIH!my^*SHfHb6+(KHMAf_(j(Xv$ya2Fbt6Ww1ubOycPrx}^Ul!s72gF>@av zF^`&_rbUc%8wOJ_zQ*y%{;kT)4X})?F8ZTdIuZfsW}M^=;6b`rWZr*V71cc!d`y_j zARudZ`mGo{e||#U6dATXezqVG@bk=iBO@yt1n?d!pik)U>+8>%w!JF_@*(r7>WH#z zdPk{A_P==|LsyPBU);Yylii5uU;$D_VV|+d=cgRB^-m3e8qeL-FrDMIZ#_IQ%&oyf z^>2qI>&=Ej7`wKpIG-|(ju@~F7P{Mup0p)mcbp(4V74;yS2r17(bfSRAI}dD zuL6Sb%)pkk-1vW#eR&|1?cRU3QbHvZp;E-yvSufhok|8{&o+!L`!*y+sR-G#?*?NX z>kJV>7{3seA6Z?(6!lpYP{m9sBeoLi?`~>#e|- zV08C{wtWG=^J6f|czbEkg4@`WE-hg5CWpTUOPvG|E*lKXYB$YhDp}g7s0noO8Q>-& z>1T{QZS@0w4EMh4uAr5_YE+_eogyS|?DlQq*qh9sO*fBjq_pM<|iCwmOTs>F>+Od%TkN z^Ef}EcYoDSaR}hLlHXI#vq81B>ATYu`8S&N{c~RT;*OxFV8=)3pTsW$V8+U8_tgGt zitSHx8H?4{&~PW#Xk?XnqCd`7j>-%IR!d;o_Kwt%T$#tmV`7+#Zg`k|+->9I4)-Oi zq469c%L{eqK#eC0nY;n4xILf)g_0c{to%uAiQ3%!pn$7Pm^1m!9Rp#c8ejmy+#pc9 zX62!B;YwG+JxZ6>Q+>(ynIpKA?EzGvD3vjuPk11Mh`m8sMolwNo)BhoEOp$2a~dwk z%xLOp#0ZPq(w;fbbSv7>xlAoq)G&B|D-xo`V$r<4EVz?jKC_nv3@ER!CvVvwwd$lF zC;PjNt>0Fi8Ej^w-8UFF@NC!;v{}-X^gA3XTOVlfvoHQ3&S-cG7b@A;VO(zcQR-+; z@t|#*{_^)ZzFGqfsbyiV1$`bpJ-uS*W|~2i1)@ff=ow4gmOQc`vO0%D!OXe_KChkC+YSP1~U0Mq5^ zUnjdDmH3RgzWaJq=IWMWL#6P4-6z1AgvP!MxtGS?J0E%)xtUc|WJa}Dx}>BFV({0v zTBkzg;bzc#g-Q|VYK<{8kAMuU%Sp*qJ z_JY9VeDx>e6W9Xad?q?j*5|s7@=N|E00XR?M1)+{d6SGYSu;RrHGYIR`TjBUa<)f7EV7bV256J>azBoalb=wkR{zQ8uys0>;)9}R`zg>AlhxXjaqh4nx z=NUgXAGxZznL$s<_D=!E)q4zs#V^v}eoH@MojT#iRtlPsjD>jFUBC7BY)9XHQM(PH zYV?szEkAyg60%}a4vpY=3(5TBYwq3ej;zIA)N^s3p(qTA6MWd+?Gv3&^P*y@q;n~? zT2;k-O_6Qbl)T)2jK{BnsI%U6hHW6F*yUdR@bQMe+gn+~-EKrdxHq9vksHK4Y9Ci) zeP;c*ggSBJXo_ zh&0@(LYa(7?G5f=_^aKQ!;RoxZ7o?zro?KqVbQ(5=-QtQQ}qAcP5$-ib!7?(i&F>` zOCZY!7mTFSc!4G$k+L3PJU%rxq{Bk$i2ZcKtpZa=o9Opb`FJ>@^Pa-~Hi6)Gd?>lH z8H9An1Xp_f>{6WB=lA)_=e$k9_?_Qx)2wSvRm7)kH!XBJhEAg}Nj+9n`qoR=@C0Mu z)#RMI-uBb^8u#`t?;Y|JP#arpELB($YF+C{ke!6uWa!a`ti|Dw$ca@7H zGE(7-KInQ~?R9zkJX1He_Ejv_#*oR}S)GWO1lf73xV4pNN|`p8K`yG2nf%tg{aK3g zpJq7mcFVm+T&iDl?^_$~&tL(%SQ6z>vv@qJ6mKzRC&FmxA8~GE}r>$8at0~@u$mk?EUv_-s zA#rb9Ot`0Q=Lgn1MZMvfzuo5UizB%(n)11!Po@nIqDN~TlCBADkV_gQ`=T7jeDJ;1 z<vBO2ssW|6Eft)^g=8^pt8( z3f+SBk@Kx2b#$~u$uu?|?MTv4G#m)gsQ2{uzv)=7?awt#@?C#-%{|rOd*j+{46E-x zmw2IHZ9WI;+q2nykFTU%*cdQP+VAQFKurI~D%~oV%c3>8y@R*bBQP^F&tS$6kbW~o zbO6tqRnm;#acDEE!zD~NEtS{iCVxb957`KYv zvx&Io)(~go1*dkW4iT`zTr1NQpEYLV@{JIYNgiqw>o<7-m9nHJ=(*i!&0uYa#!1NF z?t8#4e)z@t)ok-d`%C$N0MULdwz!DL?v&}obGnOk}*-FK>QoYm`lt-(%>l%%kUMuCHF%_7F} zy%g~q-BazWQle=z33EdvT9pg!ezgl@k9-U(ZS}BL5Q@cDs{BP894zNd)MYQ%b*Jo> z?)Me2NIob}a^N;sPq?e_6xVJ3vxnJGdga`{RGb*qY=`uI7Jj9hg1)W`;(W+>OrVD(x*drXYdSmNUf3to3>X=yKu z$Eq0e+A&!Y+YV_5*jR=DGn^tgMqb2MJ8N@Q;`!^*rF&Yjw%_KvioF_xqgDd9{#Jqc zB$m*v!+j-c#*G?}^|Sk3N#fYiSUo)GQ%mX*x^2CG{Egi&Y+=F1&ku>VjyfxAK^$Lv zV3M7qlO!W(f=y2D9F4CZLtCt3Q&#M-2%5X|+!6p(LE|KXJ)Q%8yO@brh#w^A#uw;) z-619>rs*_VnN`0`Iw4Kz#$zK{s--O7Pl<^u$2Dbi%>Bq6hv(g;X^m6X`}Ak8xpmb# ze*Xqc^B+0`&ha7WjWlt0%BkPIb}FpmBW41xMwo4&D}T51I>w z#jEOojs;v+=*Q-cB8MhP5!`#Hbtx-x+Z(^?r!(dlaE#WdSws2_pZXlXhfX?twtnv1 zh(1xm@%shO)wSm79Y0@PulP@3s3c%H@*t>@4vo(5pAZgl7Fn(p4G*ap_Zg2T(3MBY zeQ%BX#gNU~ej6iI2LA0@tG~-3 ze|?WUoKwEXA7$Zsa~Of{<_F!*W(2jNs%}JC+JuVluUqUZmqjIsc^jS4bn5qP@mWc&L)wTX$AE(FXud)x zydc*oi>H3d@T~OjiX4CXK{-IAsV?~Em!7yUz0~g)&7Wxmd(c=EE-*a=-_kn;b60*> zbFc)k|7HXw00v%X)FVyGhk*8dGpH%ci+=xpItuCA+W``YEY^^mvjs*f?khZzJwy^J z%jJoG^3>JaJ}{RF^jPh{e5I8AvHP6MU=YLq%Cu9xIp# zg~%G&A1&xCNwM54sxX$7WLcDLi{=UAWIIH>5I11SJFfFjl^p`}=y4wl(AW8{ZX^Yb zZpvD9MunL@YNPKrw2~!6CGY~tXU7qFLE%Y=n8bE)-H`$J8(;rdOrU(H?!xOQiKVTl z!x~TA{!4;Yi#)Q2Q-_4dJKfKFUh}E3F+p|#VKsWEaOsATJ4niXr3P?{-BI<|4VU?^GfBK>4S#@xyKTTJ z#NlG=h=V@z6b8S|FsaRE**KG)sp9g|(w6xumZ!SST zvSws%rR?j#(Z1C&uk1lzxos^s=oL%pdNi02=Q=opDvs6<)%n{-hyTWw{CmMSl?+ksmbDPlxMbkvs6-H-M35w=}^%m3^Io!XBS}@W5OE!S(}HnHEtGWZ+%6o%Y>=I<0Nv-$m`vt&o2FcNWKC{o&3}>5CT+_+CXD71FF|)@w|x_ z`I{CUh~7(U<%TN6%j~N9y@cXS<8n%7l-E?!#4^-(8kc^so#pm1ZT8OJc_d0cCw)6@(cKGz-;Hk{ z^~%v+l)G+9@sDOHD$V8ZED->6!_JcA9d5~V@z9>l<8!lnQ9*hAf+C4Kt?N$DJ^MKI z8`vCjpr2xnP5%1y*|U+T2EUY) zIGaz@$!|+WMno*i_Dj_2m)jl_w_}tvI|~oz`;mU$OCfM_%F@}f)c7v%_eR8)0k54E zGo&PX*32$nPpl7K^h>QRxkZYn?G2%9<-S5;<)5r?8XXotO>g?}K-cZg2YOFynLp&{ zC^@kpp@&sj8Q=F+kk_D7-qt%So+zp_G)q+dks3n$vg3>lIk~w?t!CXpm0ktH-yGA~ zSg0#7n2-El$e&yjwT<}^C!)H)wFm-~!e0D9S8Wf$rl%t`!vHnamN9h3T_hWl9^!qC z7q1B0_9QNsw*irZ{g3TdfSm$TOyn!kfE5&(JIN4gHfCBu$j`v2-4NTMjNLfKaD9Vn zI)3~h^NVBs*N;MDzjFGL_{I;RjP&$xBh2niJ;!}{%(xKUEC1oJo}50Y8OaV?PE_Qw!<4k^ z((Leo?CmjqSmJ(Ml*4vuhFNGN`lN5D3_97sD~Su<&9#Pl697k1m|2K82lp%Bo>XUl zcyi`i=w)x5OV7wqwYM*LrlS*|lv}|H1%N$E+r`BaC?{P5T5b}tD4q^a&%N!VQaDj` zu-F0-$-^~hXDlQn)Ht<9KiO*yS*I$UK{t+SjlkAnfZF}PZ0`Slm7ty6PNqvwebe>h z&A}Y5@RhY8d`EE0i<9GJcjJ?NqSm7HGeXA6fxB?$v5^rUaeJBiORsrVUS3{{l%a-V{H58QSLE#+l@EGcqy~_3D|^0v7fwv*llK6F5jb`F>>u z#ZQ{1z0%zu1=}LGwZAQJ*Ihel*kK_gfHX(U$9eQ;NhnN2a(ti3!xNvuC^_!`;(_w* zWO=4~VLQLp@Wx6-+VKm>um^oeV@5ptEc=C9SnOkdKzW8buKB+tGMApzi5)32@7Ov= z@(VsTe{^*%{Q?`*%w7~wM@vwWBgA?UdS+KL0D#O1ZuJ{S-36l}&my3z{D89#eOrPv z>K_DC|1#Nsy_z2(wKF17-D_?Cq*raD{P?EN=L9M4dy>$?gAae>%G0b z#en2u*Ns#FfAuQL*`etpGr-Y4SZvd419Xb61O3l*<67VD5-VE0nq93}DbMiq7?@92 zY9kOk&Vi}C)KyiZxwJS`&CJq#4%V9GlUVTTMn;$qSMC}Fp1;ZsG|5p8-9Ubp< zZFb+p*jS5wp@+fm-)HET+ZX#C`|6>+%JTHfMSn`{`?Jn{4@+BGW2C27wzJD;0jnP7 z=5ogyift;m^KHfSXdu2lut`%4y*nmtm*g6YffHMSJ+=H3xKad*Br3?T%zH-)H-0g7 z&Y*K^wmsgWCA_nqkJ&3xfsskXru00s=no??5fPDDrV+D!bau-YeU;IKw{$dc$?)J{ z6+MhkY~`bkA7%F-r)gPlO1iIn-dgOB->7b{RRM%KEf#umwg+6ZHI|R9lHd!+QZDsV zYcbBa@oR*~`0?7>Yf>KAFG+^&M)CH{6co}+u3&=^K<(Ojrp}-2i6LE8r+mQacvr=@ z;E&u~Tw$8Bhdl;b#F5MZBX;qil6Tu9l`JlcQFh3SXi<^JY5is30oaPsWS=Y2D}QFj zwS1g9A#2>#J-^a|)L3?Zo}Gwx+4%Y58hrn9EV0$xdu&T$xZ;J`v*u3`5wOr&9i-9G zRKKfEfx%=IV=rt%T*9O9$C&qKT=xx$UYC~e+W>5`@9l8#jKtD#dD8hS*l`1~Y70O^ z_+mLg2LtHR#S`inoIA(x#sy{LZQB*)uZ`%AI@sdg_Iu*m@u*Fij5(0Bv>||2fcd)T zpQ}Q?UNIgAL=^|eEdV9&@X}IYZ$6jZcBS#DI=fQy(Z_U$qp3TR=<-E0=OzS-cbDv2 zWKW#CiG6GRivxXKk9_F%%Zw|kt!k$bI>DQrm$*baDHoX|-n{iaK|Harf3mc>ydbR= zKc-+!Sqi=mU{&}kChij-@443!?FQO2vaG&aL>pSl;!Hwe`!9DE5XieEF?PBHt4T2I{l&b}`hqbQh?&>7R%V)fRJnWi-kmGXz z9g7;@zb+MZ3LMHRJ)>&8A5N`Yxoj;k^^H#RGm%g$o4FdQ?^pNIA5eT1Q?i^*>2?W{ zxwk!FX3-uevc5YNI+z@zKZXXNQ{QKzjqO8w+nteFUz~!a_UF?8FfoG|Vo--VX2b=j zzFlGDyO&|vpi`AH6(ZSj`rOE)UVXn(^jZ_MDVc6nsJ;VQTToDN#75Ap={&QoCvfcI zmxNZOVAu{3>dsLY91q>4tMit7##-jbRO%PDKV3&@uMGCCV}16|9{Y6(g3?bw>*BceSxLPt7?(a_~d z#8m&(V3AqSo{_9QS-Lv-yb$~9we~OKS|Q@IG07P&@jyTjQggViFTb&+p4Xoe2;Y<} z^4_uEn_@f0ntR&^vc{ioTd5|*U+prs>3_DSEu=@eg{=A8E>a}xONxohav&*Gp zS;9QIJM`2wfuhvYzH9G|OED`#Xg2G*Z9vq(t)w1!$?WN=|cv#k;PrQ>8Aw4-6Wqu4M13-Dr0k3z z*FF0@czthgk3&wR<8)jBdK==o`t^+Z*2hEBYH^QM6FSydYfv4Ot@PYeI2@h7okqJX za)plTWk!-Uu2rrJ_2GkB$ zYr9DQz^A5zy$w5y&j4`0+E7}QI?ljj^5M2Td_q+CR=Vj8YPorF4+6SUR3PQBOD_Jj zW2r3DIYjmyT7$BdQZS_ZR1A2`>u{@o2H-rmEmoU9zl<0Hlp;F75JJz{AQkX&>KnHLF#>=kFqT?Wk%D8oKK}k_UyiDYt9_W?3P7CvRT`)uTgo6Cyf@nD(MKi=GMb$+Z-)pK2S86Y3Xt0D zIbAj8OSG$&MHg(v^>}Ftn@k1F?qK&U zD*RL6B?XX@r&NyUt=^F)h|@I)d>*i6W&HuC*Mtu;wSNfb3Bvbx)bA=0$IOhr8m4{!h;_;fkzD z5H&xaZeg8osEDnI?Z9K9Bh1K41;(!OTTVjA-G z`d#G$&=6}4f;Vx8jq=?6^37_N#jT5-&XXbUe^m$qa@--_ml^j|+tA&e; zx)gX*tqB$=!~RlEl5+E|0*WCnDee&x?T%{T8+;gk4tkpdzI!zXuP|L_`pIP%M4bxF z<}x_d_9j92(-^c||i5eWBVh0TzTeNR@=sbs+BPcl2@l8)Mb2z-tpHGudQ&L|R zR%7&l&$|F9Eb0!Zb~==FO2-)9>$l>nE1Hi@oxHYn{@Nwpr%9x9uw&u8%sHG$ zzD7pFEFSv_a#0DSL;RkHl;71(J6nv6M z-AQh*1!_%j8?r`GX-ciV7EkOgklgkzdfs?W-`}n$J51Dv;R>{e!u7EBVCLen#6qFl zGNcmlaZ5)W>Jr?!FBAO8EN}KXXThI@58&U#~IbGK+cU=O_UN1 zf)sGhNCn?OBN)U&6Y)%>|G8|4@ZQ0ktb@9-=S*ZWAt^A#W&Q`Eph{wiPvp)Nv#5PU z&iCy=zpH461usAn>kE%T1y)ve=qwtw<$d{{R5p!PZi+nhEYy)uUVE5-oY>#J_&6X3 z^5!vNl+T2IKvBn(SD=mwNyv~KXL;f9Zq5_ujWR;)OR_r-jCOZRyJc%p|FBNx6MiXT z)^u0&0fOXVJ|9?1YdNdmIbWL@nDDLWi*p^_7B& z!gX8NCnE{3U`7qbH~c(H*Hqnjh?LNj1q`I#<4<2}BTv+D&;9$EMf&6=PS1?X(Fzm86onUvSX>MI+Z%iqAt54q)WRE^rZ(9elZ;m?$ zmntwCi;uOLFx?AJAY0sVHV;yU-)^hYl zQ}8+Wko(b8aO5G?dE?5?(XxDb5cT#O(Va>#%1ME7L6GUC_x7M4`5WX&6;p86g*T%4 zx(ovxYB6^pWj27-m7fHa|45XU;2B|?NeN;-m&-9H(=T8S#Vz96`Gq5Mp4$~AN}ky3 zp(=$uQ_Gzk%ANCD|E7+LcJnZD92ySLyM?{0?5$ zEyQUv(gq#-htw!M`~l(?^%a4-cKbEg`2yIX;RP?v{v9u5KQTgI!+n5ZT=m#rSm@%S zr=6wN(G6LEhhfu4YE^F6)H)&FSJMTGsmr9_D#fFv1$<_ry1c)fuC6cZ%UeWcr40BT zZOm{?f(~m$#+P-Gp{RYD%BksJfku=V{N1QcxLS`SkUk#CPd38{-)(7c|`=B(BH&qF5&{&fK3;2mtfl^tp*5 z{pdT8rTOHW#eLcN@!F(>GPpPH24MBee|ve6_I^5$I+%WUiN!ik_TgHUw6(*U zE&J@CkvCaX)B~XVb!cAwbPQBiqrr2?Ww!+y2Y%e0sZe39o$gVpbci$ED8+AGf!L|c z!7P}iWpg;N(FqA@C8;(L7R7k*yK}aX4CP^fYM;#NjJhIIxE7;RlsqU#OyV=32>Wrl zKIrJ3IURnaa?uNyxjJq)npbTujDjFarD)U#SPNu#pC=7L02%;&W-w?GQxnp60 zAq4SFgor1Ow5X7nu3kC=UIN-) zWp510;9Yeui$@|?h(qB7RLRHfeaE))tx8HMnap3*Dg7T@hW6i}WT}hdMy6+ZSU4F4 ze`sC%-lP)9?jj113&{1;1A}2@>%i<6L$v2ZLZmQx$gSkwG$Sv3BRem{W+ix_>v81i z0#cA+WlF9A1*;(!KmT}O*(S+mS-%l;X+oWt{gnr!_7kFH(eH%OzK0TYAzbARqzgv| zkL7~Ox>HEY;v#AE7?GLUA8lYTI|};c9Y8H4v;u#02#>Te_|7?U&gR*ZC#GbyOrqU4 zPM6sP%T)9q;(g8RxVTs)4Pqsrs+yXiR|MX<4)f|6ffB57X2F^536eT49^5R?pCXDP zKYxxX9Mhni#pOPM1q3#Ku=H-{o!V!xzDhj{1WKPwO(=NEYmB^sY_xPvtuvlVe||M= zGLA2e_i5eD5s9uJ&XYxs@DG-UN^SR&>YTH*7fU~06>1_EtqyWBio2=6Ing+RxcuX%Kq~=IzUWm>^#NpkiZagnLcKVIO@( z%H!?XV1_tvxfE)-DuPM{;rXLSnfWiX;2*ZCVC_}yvE;seaRj%#e#M&3hZ8RZKK>Kc z^1n-Wl(!<}g2)RmL*W)_J)T&`yMvFiZHd1%@b<@Es~4Nd3f*J zWiXd836C9QFL+h^y|~`pb0pNd39fy5slD97H5nLso0rsf+6^ zHfGq*v_zG^N z2j(1cYZ4AjUe%l3suuIu6gM%ZnEZl*?I1(L3!p;UlsUfD5_a`+%Tac_VfYyE_*4r* zJrbz;{fvHfZp|;x7oLiGZe2H#tZa~gWp5;>l@fhj*8aNCtzSeX?X19uE#GrXUsXLf-$wmq-96$&M7l8d7P&a|mL%uyFK|9)Yk&NC*@u)0YmyMKiO zT-&`0@ zWNZSApeTl-{EM|TrF&`KReEjZ(k!m*uFVA+RG=o`=iNcbqL(t-f@n z`>l*DdW8dZ0k7xxJe6WbM%lq}nO==YSkA4o?=oauh^N?T<{Wcj?`rZGu0eZTb(3IG zIv@H25^@$_*i-k=%{G<01WA;` z(#f0?l!^phj+eH7n%F$Hu{oBtBZp>OkPZu!^QPp{TaO+62v+I>{Dv-Zf6xlExBYQX z5its_kVhN>Rl;S<=0k0e{k*U661Li%ZDXjvW?OXfi``R573b_B9t#hP3w*hmQh6F` zW23e%8H|Dx-Kw9NnwoygC@HQQKlU4hrP63fk+-S$=+XlH<2AwK_Ay7s)2(<_Ab zo9!E$@QPez`>Xw${SFD^-HTW|+-SAojNA^B`b&|Ewb3gTsc<{ICoaXljN914BBN&a zCzX1=PS`fGm&WjAM?$A#TVVAHq{O}g{yLtq^A8KD+WD|z%7pb-6&5wpup|xC)RUgr zT>kg1Ofw&)KNchUKvy|F2w-2+WyUzxzh1Gd(huHL(+soKGLZ{f=+3=+f-}9|MX_m= zvBQIQ$4jdbEW9*v-tl8of={0=Nox||UbH>lH>mx$FX>jf#IlfTH1IPiE%YO6^9!Fn z{YfQAzTF`GJweLTX~Oon^fYyc_?!V3H}`fYtmQS;1!xiwq?(8oLBsgd=@z$xgM)7g z;XX%G;W#wDKk=+iMXh__`1h8By^}1*)(6vNRor zKPu7N-h2~L`ty@0bw_#@kvnPdDhU9a9$5n_jCV<#FTuJ2oBHj8%lVVyE^wmN>nW5k z%|J_sLQ!7>&Zye2Oqyzu5wnusz`P6~BHRWbV8#TLhPP^`$gR(3w)XQn#^#z0k&@~1 zqG?7kI(fRLq!}WD&%aSJNA-F?@bs+ApP!9-foxetyTCtgG9foI0i>_QGqZ|J5?-Am zo=s=>BbWJBM|I*ole(9VY)jsO#ub*Q7%%T(+~!SOFAc*bY+5&)RxAgLmTF1@dOT4hFle zoULALB?Sh#j&2}2q<0LM`5Z~IA4dcA9?hl?SIzEU$!blM_O^|@Uj8BQQilxjn)ph^ zRJy}BX8k+6pBr1_K`v4Da1-~COt-SKQuI2?1>YT0o(6)_r&00OJT~%}q}VV)_tk!U zlJ=4h8E(9S4B^yDCkn4#Kt@t z0&lw1GIb19bTNMKwh-QQY8TOYA84?>Jk05U*?TmRtDXtsZk++*w#ZKS%5(9>ypr}x zT@WPUR$QiO;kGRhuUupqLgePE93Ab{#@G4t-JBBkeB;GX(6pxmi#%a=?2^1-HU8$UVc;2DRa+HJRtWa9U z_;Qp18?QJc1izk(C@7Zclp*HX4i=05cp^aci$<^#pa1J~CgKv*w|Mw>TF(ikOd=Am zay>Q4ZN3~cW^?;$K2w^u1@xrAdy}e13>bv3Q^=^yT1JY=T6%m%XSN!@@f^q8eft2M z>X1*5b$rYGNT3@fEGOdUiz^H}omlGC38W=<^KxY8C*x>Hc7bO3kU+a@zab|e@qH=( zUfS2!DGEVDj)5}Yd;y1BpU=8gb4c82Icbss#VEIgUW5)YX#Kqxdk{m9ijluxiNxHn8lVj?FMp@}5ReHRvgnXK_Myv^XgIIDm8$25IMi*pNk()2z zy=NA@)YTItv@r)W{P8f$pu6MdNXGn*i0Ei5WEvnF`^~ks$X+er0@Fy>sY&4HaKZWu zL~f)*h(gd0Asj8v)?my|4TMF3U7`w?fRW33X;p7So7m2gSu@~-l&HH12@rI^Z$&g~B| z0NpuE`TUD-i#2#cf<;@@AfY^PXY*ijr$RB5bsKZkrN}l&C<5mKZT2W=#$9)0-($S& z@t$Va4O0=_EP3<8(U{0c3*XR9F_J+M6lp56(Np3w1lH#|e6Jg(iW%mulz}+$ZTc?c zQZ4L+oazhf;P?fPkendpE%Q@tU#;cj8+2zfRX%`&Q~pHc$Q8Z>i+3f>LcF}Z212*w zi%+Wq4Xhv3+mF9wB@!$(DNb-PIX{mNR zP_^n`4se85AZWjp;n#qgvK+8`ncaq|82L)SUd^{jr=1WIvOB9$od@>p!oCKd#|5qJ zpBfaQwuNRO>TJYTgy6lcV1N>-zZV;K1jW7VNPYNzN#Q^@_dC=AIc#6uZm$RugYQn> zuSU-v>DBrshy$n<0+0w!-Xse}R=Vo1#~Ly7*9Z)QX@LgWI}S=MTaY>)UM_=`NZn3X+Rqt$=MYv3nhvX>=$*aDX5_SQ5>)<$%r*IPlwK*;Tg5S4T|J|aWO zdf7oKyF}2m^E>6hM0|TOGQW~$G*C3=9MBH55FN?xH#T|0Tdwv`3wfz?BXcgTs%j{( zU-(+C?7k7`Eq*HrXf3OY2q3<;)}f$XoT3I+b3kKjm=% zh6t)~ajbN@dNh17?e^UB#lC#em)RtmVg}D&H<y=LzD5jht2xryW$_nEuJ}Weq>?hnzBh2S+oUesCWCce9(Z;U%NKT5;FBqE`UG7=h6pqY*DCQGMs6H&)15X zw&>>DJ{iqf^wJ{&uXx4B92cQ)RoRNm)2(Ni^Pk~*t3eqtQSb#VhJ3DVlI| za@)758taLIjQ(1QK*T);^<(`Kd*R`J3&{SceaqIxNOaOz2$E6&h zO^QD~auurXiK2S(seBgSLKw(t6A1O_DFB?ua|E6JP-)Av+4oZEwmPMgF~0~SPf|7* z3AagEnZWR06cZFK9tA=j_Y_>_^MOEkIHgw9ere>kQar_^!b!q#+TR)n6gFw!zkk0B z0qoQVQ#*!-e*t^`9M}0*#Nh7%a3Tf=-5mbsAP)nTxrIebPmb2|{>;qGNfK7>p`Ujm z2I?Z>e?uX#_1inTWmDKz9bov~L`Xvz|8=c@d*x5VsD$rIi!N{QmOt|cWc1RbWB`1O z;s?OTnlUnE4;4=X;A1g;0DR04Zxw%+5-7&_W87Neyp#=Cbwqd0tFYm{QB4WrQX^H&bzWrZ+`a>xI(u{$J<+k_{_KC+N zpdrw!nSY{N)ybKd7A)pESnP`JL{CK1O0(pw@rm0b38Vc-hr}Ux($YT(E&dbg^%aHG zR|n6($DH`*rn!(0up2@%bZP!|hrfMA2@pKelh*710=e>^9`AJs38`+EO3~lt-8?Do zUAQTC3Pk?8{1@B%Kh^)_N=eBJQzM^?|Nq?D1Au9elJZx{(|=kG;2BbyJOusnm1pLv+T-WQpnIR+8B%3{HVE z*d@PWerw7)-BY`{mBCaeUzN?bw^d^cH?2UgQcm=FTsTW zvN>;+>!jbic3PT2#9J6RUPhX8epvbA>3mbHTeK@=cBGrB#&+^G|1nN@;YrHfH|@l`b}eVx&ZO#}*}I zcA;+JhgG!MA1?XroyYq6>;OKp8;4`(Yxw=R$0<@?I;>UEz8VUJsaeSe*2_OI>5El+{{24&%pUI(k1Hy%^$2t-&B*U zhA0XJr^=L3zoF>^HXA@<@!|ZqknWBesw4nduqCUQsXrGixprxBk@*(y@^Ohf=c;z3 z)z9XnB_b;&dHp3fii;0Oo;-ehnS#;4rp!h%4VB=wPuO=@F4d}rWVw}g)FyzNNBcrl zDdZ6qptr%Vlm^`E87>tQn2$GV@Zux4b^=GH$KtCG#yaQ1OE3g)3}^kKcqe>J7sgbt z8`@k%_TPT`KTcJ$JL&$4_W0L@>H(#*b-}yk?Et!5(DH|FOJ`^F$cO>gojU@xyp4^G zPr+c0AX>I4fCCo5x!MtY-`_iTa{24&MCtXfXJ7!( zQ$2O@^2v4mS1)Ae=R1=Y0-kqQQbx)iq;o=o^q&sIU+!=zT86gQpM@}DrKAMk;ii-JQF{7Fs7zvt?`{SFE>7mxv4AeEiM`7Q-nU`EE zO~^3&@81M`DtCcuUb26F9=b86{l_mhibHunT3S1D_D0tr0>9NB5u5>S-zXkqj2bu} zNN4*Xpp!n#2R7NUCEQECeEon@;fOxDC_FB!^=BKuYt;odHqPV2=AeYF0xGlSkT4js zvenioc2}j2tgvc3$n}rgr^wB1&F7-r$kz;yq(F29H?i(c4Ec+uz<>J-lEp|RhCg9o z(xw*F-R{URaB9QhD@I;O57$QTLNB3(JydFm+!(*52>@kH2Y?Rld^2AGQP@PavsYp% zRbDsRsOq~BbzBnD2N9c|x5;=~mXrCHSB7$PYy}8}AYQ;#;{LhcoTigKP9Wc8L3G^7 zA{cCq!qOuPY3jIncs_4*CM}L=QGbCHJ%95KRU&3-W%b^-bbeC{AbS>LGwl0WW!x|F zRCCdhx;{?Qy#T8J_1W|1&!>$sUfu$(?=UClRi6LxlPA`K#*Ix)u{o3hmuw3YOeLsm`7IVh2oqh>(Dp z%rn~DUPw2MWx84?K&ns{Y(iMs&KRevAMxH}a_#}!KxE^8;nKYu78cDvYAKYJTl%iw zka@kYq4WEYr|#Umq&B=fw@7rqpjXB2@&FCFGOS3xn>NYato1WjI8g0du(PrG3~acj zk);CanK@c#Q5Ff8Y}|Ufa#pOXv?Wky?vOFW8ehxb@E#`?+`4ru4prKIQnSUqAS)`~ z4hS{h&U047m5;i;_gS5ku(q*T(9(ITr8OP?hOuQ6uVt}1A>Z9S!lM`x^<9#X7u5@s z`Lw<57P`g3dAnt%@7Y_S4hgMd=VBmpF*$c_z77Kn3(1I&9w25129b zTlAYP9qwx__Ea(QRD8GX6Aid=3O3br6Lg11K2tNEJ0NeddTpb>B`s8XW@yWZmzG81 z!3wdn`nC{z8gA{)n>34#$`W&}E{46y+EI)4(ZM0>+5#wXXWTs$nD-bN)DiHB`BCeb z2w<$V5`EUKt*W!$T%>52k@fQ~hhB(*T@{)&C%PZ4@>$z0k6{+qcuvBDv2(zUs`&WS z+~UmAB@0v&(w*m;=o#J@{|#{|-~uTv{7)PMp9nWMWMOeJy-Gqbi?x(ss;eO%6Boz* z=FOW~rmZ)7LuIh##pP`wNy*_sw*!yL5(6V&r@^f&b1(gPprhNWo_1QpG=b~i=s3x~ zL>ZwUtZxK)_2!=k-Cv8Y-F4^O&8?}q57dbGfl@j^pmtiT|8q-L%MOO9UcTkK!EC&I z*+&}!wzRl7e5*MmvenbsvPGod0KYBeZDwg1c(7z8>+}jq!z#rCGWOCQ^CC*;-4@QsnCoV<$j|6gZTa z{XyxXDT(no3LfOMJJ~GgJbV7#`}Yw@pS`5%aliF8qb-HB%YNH9zX%@?SyU9Gv5y|B z*X~_ToVFV{YQ%HS{Q+6wY@%Pm|6}hh28pL zA;hMXZV-i`yBmX)?naRux|{zVL7#KJzvmp!dG~*D-uOX=duHx^?|toSuWMav-4yl= z72MuUjISX5nn+Ko&pVK1<(hH+7#Uo!P|Op51oooaZpV`X?XUr?o@3|u_(k?LyQF*u9@*6!iV7)qc-Ax*M7g9+VIl zL%(5;?I&Ma`1hF+>ec%)Ni-W85p$b)vSi5p&X zb91|?!Yk9WMUo=|9kI6=*^2Vj_S#%Se8|pjlnKgfPGTqF$VJ6Q9rIWvA7Bp6B zS9`ctFBLb*G^VYua%<;!`<#`oKi^CX6YIVD3#MQ8jT#_5i<0gShguZtwJP45Z)omq z7$i6Z%~#xYSs)@4@yZ&if;YCKUd+$Q(O<4SIpISGsC~b&Bmt0 zTC@AeY|KUMp5{EB3(NID9`9q%ficrq_}7om@Pr;LTHJ7Tuo)TWRuCFVDH&DOkT~}lqP@BI zN?+Y+q)2#YYqz#5+Y68%XIMt`v;3JP3@OhEAQ$nWWZR_?qs9e5oWSX>YsSkgT# zS-E6}2-QYyru$S$JzpcjZoVBI72Mc?0#+0;jt-N8LLfXXQ0-4cfacptC@n1=78a&% zivN+CZzj}2+Rr#*o z_KL{lHZM2o^nFCi`|uJ~kSr+UbEQc~_Vzj>R}vqm=jN_miyJxNPCxfgng8F-{%hLj z0ueHbilLuBOZ2?0sIUY*f5Lh3xF7pJaiB5b#GlWS*?pWlrU`C1`}y z@>o;?;i|AUyVwX<+{~}_zV1jf72`o1z%^ZBf;zXZMXGt|6(PI*OxpxUJYjvHh{Je$ZR-as!k1s5EtXJJ( z9E9+oNxy{*OFN|{*D9-nOs(8-A&(swfmG!4@8A8PJo!PWkRvI2o+!qbO+eQ#FXT2T z2gD3>HDf+1E-v0pQDFQtyo63mKuBnodLzK!|DFOjH@7wu^2Z^>Io$-==dCrbz2nsEuZu3~y*v*No7G85QuS`TW&Dc%n%e zidp=5Yt<9S=`L0`bevz`w-L~8r3n@`QFzZkD!)Tj z!jxM!x*yEeqqk6EBamf5As5#=UV7qF=!sSI>+oVeap>r{O>^`Ej&!BEfqHl%cykj+eSP7MyLs4;!gYso zb+YO*wUH~rpfY%$8&iX$vGru+5|dclJ6pV_;Q%S!!^J=Qoa$WJ+U$J*vVZEA&tm>qYzsXRhN}+ zCLh5EGP5Fl^+ECyGiK$ck|EFudS_xym@1du5DyQnB|jo4M}Jw zj4>>8{ZFW(?@K~+8=FE7D3^?pQRtRpp*}z=Xxi&2NpF^%(+2rCP>R0VmBiR4;RyN# z!O{{OWf2%Fll1+ewsX_PvYuEg}7I+vXD%up50h zy}z;WBzHgsY_|^u9vVG-_-@ppF*1JBB2%lDo=G!n&Y9GCxE|1^&YS82IrGC!gj@7< z;^7Zgvx+p2<-KC(_~-VZ4P0|ObE7SPU+!Si&zqx%BIeS=?HBDQe4g4h7u#@U8X7{09ou%zU#eyqKF)h6o=ufi@Az@w|6Om3j;5X`hkg6V zJ}-zLFp_Kh&SiX)b-qGElui17#A3jLB6yS6=99AHih?OY8*U3gxxQwXV59=khf8v!ulfvK1@$?tgrm+9{>00$my$?{-W}#s_W4DZZaT@y0bi{x&7f`KM{-^ zjKei{)y+a6o4^#S7HYvU5JR1tlvKxTRo4mdMw8vupo@2Ja9ii44vm;@r=+B`lGfN4 z%Exk#%`bpsk#87a$n36{Rqwl3iwTO}D4692{rUt;W z+mk#;M@J9=oo=(Tw#Ue>M5(K*H}TDWVr zq91fF2P@i@+D!mC8}0dda)`kq9JJ7)@b32y0A769-ZCNCfk55=x7&A} z&84vGFwTE;v+T34Fwn#ML$V0QsICrILLM!mO;20yC2&i^~u7vn@fV;M5IsH=woSvyJ7wiuS{Z&Hv# z#Nd#9PK(krPm|kb*f{m};{4-nRE6suvnZP8oe80RRKED{-&vL_g01lV*{Xm6f{(pF-GE8|5hh^2;1VegNu$fXcM?Ll3L+yMKNl=R|NMJkcU3 z<t(DUx^Z+E%>`AT0jJc;vVLNWh>B=Wa!@bQ5UT2@|pxE{_QZySu#>BwN} zfNMnkd;S$E`}ZIIHI7dMQ3K$_R8H`>-{NocQJf-hjV}q}&ox3+wD}ocac+Ro`Z%NM zcLO~L}#6!9+o=LK-lK^(BIxJyIu{YSjmHZ{hrJ;rwsM`QMK7|K}Ztio5et_gSX)jg^3$oW{Yy0l+LwU0o{u^mL5_0iL!;$lDs^)DVJQ5l#R@ed3NiWnSxj3lH1xNZNSbS(x+-$)PNFP7yK zebGkoCCBd)l|>~bkVI1}y}O?~SwtY+P@?=N~PdKZ;Be06(K z^D{$v`c%}~Iu7_I%iX{KWJ~7uB;)Pn>+uplGrBrJ!FiLNp5C?3iNa5|*7Lu(g}+c% zAU8_B{VG_A(FTwx~h!(iLIW?~u1jUg>G4nSd_= z(Ii9#XPmPlzy}{QxuC#NX*Y=?<1}$3^aU+7u}pmShDuR-Z@pP*7#U?%)p~C8@%6!) zqdL>C9F-u1?Tc#Hk|r=y&XO?ET^w_6dV$syY%+^f`ho7z(c4>F#j3ixMkY^jR!*k- z*;Qm^W42`2eYJ8f2fWhb`Ux}vW{@D3q7bWP7~ppy3CX6Y6NsP9uJCC+`Ai$`yoP`P=2_bJ*ibHRZXI7Og#@KXznp?4;zafIp4_He zXl)2$j*cNN1f-lQ=Zoh9!or-b8KmAW zwGQm;`600aO`b+-JKS7ca^m9ITy2I{JAOmI6VgL*(MAFpne>o11a>8yJUp@xh(%M- zEx_@i74UfDBJ}E{EWy+&;|o6$rc*Zi>%bryjLy#P5E7TAM&cIW4wZDvHUqO)N7Eack!RQ zboR7vb+WT|GXw}E6oy4omNrPb-0E_x3>9Q5pxyn2V8V%UUYJS3t6kg0sp7C6Wt~9E znBv&pSp5tLtBO7`5^(5k!!tMuce}*(qF`{0A>?e0T~nZk-p>6=@4A};Dha8 z74af1P5YI9&mw-hDsXsqD{(%GmH%^n?#Ck5C{?(P+oqG@!M`=N(*GVIvQm7}3MY9IXo~~U0J#_2S zY~Ek=z9k-SN-?mqUY^RFKD8khya4>i{k`6+_~im>RkVI1A<@Vgfu_#NH#HS|qd>)P zyr-KZd+kA(x<S@dxa_UUK8rR#Lp>s`A^r+u$rBkNALmq7U$ zJcmx-=`K$++VL74abFJSX)2gwtUD-;F}j0r+inx|JUx5QDqeHtoWn#r&%RzA4Goss z@*Ou}Ti*pRWWF^QK+}LOgo2-9Uqeev#>&cRg7G`^*?bhO^YihlRB0sRpXpT6)y?ef z*Yx0p8FB;FNC>>pFE=C0$GJfQ!-?MfRL#bBm88=_1>hxn)Nw&V{0)*^VlG{ROt={% zVrHk$2S?6V`LQNZ#Ht<HH!&ROq_ zF1HcHL`Pc%zKZd)*85zTyn3`W)pGXjDSQpRG>HUdH!*!DQD@e&6{TDcw`2E0lZJr# znO*T0L7Ufn!b9U}2lSo7`AUkitVPlDVDN`$!Y1l`z%uU@|n+27|!iWwMv(BmKYwRkaZCrt%De|`;AhuNpz&`4c|_)aci^u`96 zDh0iI%HHKOe&Gy~rz*g7)>Ai_U}tSu04AFZkG8Z zr#Ts@v4d~FxlAUs{7h8SHC4~4EqtbFn~)x-Zs8f(3o3$nbO>Y!FToh{9u-*GOGx9%7s=E{P=3yueq~ zt0eZ#IGYfviVibJ>eH7VQYZB+0uIr!(L(*g-Ev0OUyR-Rl1U{c!vW&Xg2_`MzKj8{ zuw@fmf2|u5Lp@`;)n7j9)DSA|28p+x_+_q``=I|q)1a4^ehzCL8~^gYaJLx1)+l_x zezi7RsNdc$nVi2SbOwL;z9i^ed!gu?rFIKhW}Jj;ljmP zhsc)*BjUNcHs|NMj8r{Wlb8*TL&9z{xd@Fivq1`^ais!7qVJjU9;7C)Lzc^q{7vD= ze)9G4g`If#_D8aQ_DE(us!uPbTX;l^S)xsZmOi{KrXeeTgwGm8g~_YZ^73I%pGsI*Sw%K9>`yM-to}W2 zS2`V}A3Z7{i=5m2kk6iX!d4%3=aE-mneyp!d!p~-pwc-(&HpR9o+G$6$F73L`j(CW zM*xM~Ek|shJR#BJMYZDFFv3ob@TZ*ySi&X!SzQ@f?_6V5OO0d2BW;h&g&zsn`sD#) z^^fC&<+oR8o+k?0?cR;H>&vd!Qxe`QDjXZTZ)y2@^r*Gcc(jZ?-qmWvEKvv=!K{Bf zSY(aVeR*#>e!D}+J4im-QwL!))fh~CWbV%ApizpJWp{|w`cWW>ClDW+D5B13kIZ0V zi%dK|99Zv?N^~C1Wl}>ZzV$v?+o7f9EAJ65juk@QEJrcza#!}0_Kc4Z@!N$Uqo}8S z6A@v-FR`j&KSl~WT>+xEapxX`WVdf$RXUZ}$AvQqGd(@v6*{isFYa{bdmkazZg$zE zMoB@Yg|+^?U$Ytgj$!aC?C%g2G`7%G0-PJZFJqo4Bb>DJCaFQi|tG>TT zZewZnTD3I?2*p(mC#S-!(kD@xx%yTnsf_n#0n@!%1U_%t^;IJ$rFlQ%F%(|SSz5#k zhy^BNsflz<;^9{1%fp%RlbH<-bbGr~TkEBy`e8OBuF@VJm7hOMBTXapPQ11GDI482WaZ zbfGi07b;|{jZW@35VZP`_N;+M~;4K+e8A1dx}TQxy`i>NWOC@d`~7SVWF@44U|v?l!YJWuaxP zz-Mo0cHU3ss-EOzlIwj0De+9H)+PBxq$@2cX~8(zl7egv$F_ULtF9_CJ#JR(5={f* z4b6k>&*-i#bTu9`UzbRc#K`qp+n|hHRPXBQavbB*g7h2_^JyG4Eo4T-*4q1xI!|qx zx<_+CzkLc8d>8>ei4r=v$@CJ5XR0PCnc{g+DH28_rRde_*_A@tdXkUc911=HRJVuC(tBYcs{3onH+uAXx+F5XB9IC< zgO-CQ*&dtc&mdE3dC^81e%3BD2XX`uvB#DW9qDEx0|a;uI^37MfoDe~aGJq()cXLR zf^?-X6C9icYAOPB3@$p{&&6HX=-=)TsF39WZMnD?&DW(<&S_LPKkeh%l|4!Bsi1ym z&V6TKlBs%z3$Sz%$A`7;(;ZRlZIFJfODzShc&(bQxHa8NlCh;Ni6`p;aOu*->qT2g zkL6Hry(jxSH2Ux2-*`-gxk+b}VA7gl*x&@0wha2$-3lDG-|o z)^@-_I>D7t|4ES&1X`K;SP#qbYP$K0a-{FXW*aiNO)t4=3dBkUG22JD)VmyImrg7d zb?6USn*{P}bTJ%_635eZvX+*JxVE-y_>_r<7L8f)hfT_P8bbO=4v9~7CORMBd zm>)H$l2fUAV|UjYkzcD(3KR<4?;oBtJx%Gk;>wcnBzKX&f`T##8MD+11$yht zJ5d%_b|_y4PzjxXhJ|@wUKF9y$NU(uOmFo&Rs3uX1gDH|tVz+{Q&WV%l5}g!#j9E` ze)$diTZ32WH_ukZ{w;e)J!2v7?E_SbbyTJihw6Mq%?B?cUOqb6&4Z|>;7GAPoTt^I zCp3`zZ<;PAEnI(E42rC?mZMV0pk*SZn6)%ScPleIXks4~dTc z7WW2taPy2kt}a}*cv#k2V}x-MqSDN2Yrf0;_scj0BNf#&pHL%u-J$jOYG~CCasg{5y$bL3Au&iz6GIV zVd{{{0$J!ZsmQ@w&n>HokW@O3#X;6X2`X*ON00cAFZ5bSGn}3R!HwO$FgQi%QT4r} zX2@pHX1|bS0mT~nxye)!=QZBhj`}jPQO!4Q`^mN^)`^eg>@|W_gpZJLek0SdwSNWh zrCCA0a>FOZ80wejJd9O2*!qsZGX-9Pk>t$hJ*a&2E&t*TSB}oCf&v=*mZ^FQ*STJ( zt0`mr$J`&<>3ph)Tg)shp<;fHZLkLqDj++`fg^2B9f?zz21|qaay5HPlrODHSlaUX zCeX6hU&|tv=Ay3cwf1!IBXL!(8~@S*7-tS67h!FU$mOIIQp!E}h}Ry8%++N~2j(Ck z-)WB*k^{VUs9naiHb{|uQ1F~ZN_9M17Mgtz=2_zY_?BU?(&E}{QUna1Huiihb%59& z{p>ZWNvU%l0gr+qhl62*|Ibie$!nzFe*EQhgwW{;$jefn6w(5^Z8{cLXNJ{<@JXhn zoqijExaCA{i)7F+&kwpD0B_W>eW)j1AqJ9x;4tr`Dy?h`rbJqc*iR-EX5j%jaUOHVi2G|oiBY}-cPtW)3lVqKjJFoq|L9sfD z#K9?I{1ty{HLM@IPo>5)dA+mK0v`Vhe99kwGgX z{_3feNIhfp63!$!zwZ>g-rmknrsQAjV)#;#0xBVoP*w*2-h|5=w{oR1Ii(Jc6Cq#_ zyfyEs_`A*gkl{_-=-QVs&}Y(vICwAAAaSgmqWB9kNkN;UHn3DhsaB(^^iMNHlNu!6 zEr{1%!NK-T%gP#Ec3Qy_@4!yOhiID8un-f!BYm6{o$9scPbna#Ep&xK@Ugbn?)$`+ ze4~`H%Lhl^N16a4X$o-}P+Ey#n^>bI<9C)WopSzkuj5#O!hJK{_8z2it@L(@9Lnw49`i(!n?g>m@!wWDKEU6RdGsi`u+8P$_=K?GlX}iKt*jkF zwerWa-{==L$T;t4QSA44LtJk2jyQ{Tz>fk;<87j&$u#LQ4O$5@6_g|~O1w6w44QJr zJmSPT>yY#yRmO!yRVe%AdY!y@!LJ}Sr(TDSF2;Pmg z^hVwJ@vaOF6I&U;>d@62*%zhuFMR3l?xqGT@}PN`{6M zaKza>)tEoi|UF|O*)(%$BThiho84^DZ%MZsgp^!D}s?2(kuxXagh zaypxxQ-%sqyvLof6OZ(QfNdkg5Wu!)ijIy}jBgB%Y()VDfMI^W z#Ad>xKyezPMnp$T+Y*sI!mofxb3q**T8cENKz}ryf4Ju6@i4wg$2eRuJ}^*W{hL&x z_BmZX4rcE(Cpb(LB&5I|NZ-qSr`mB+G~X$RRehkh>4sgEQD6L{;>6qY78Z1&LCZZG z&N>YS6HbX)?kD175@Mj2nwk~^Gz&@9v5@86Ul<5&KSizx*wKCWH31;{axeV*{nM-y z2Lf`CTOPfiKP zT3Qm)FSf$YTJc($_yBr)oT{*I09Tc9OxvXi1Lo_iE*Z&c7(q87hN+yRnDW1xx9$K38~12=i1!(A(h( z{-N3vACsQp%ne=~Uqd1)49&aa#QVQG1OXP3X2%tBj;Y(Lu?KL1dEJMYPiH)vg}BJ&rJ2b>aUMsM>cg`-B{Zmt+{Cch0^TL%cTQERf)%Tp*213 z7jXA$1N#Mw|6Kb2@DIU9^oKWmh$$1PfG$>T%=|ZW2XB<~uvYDUI5<3FCG!pcEi39* z3Uzi^KmD&S6~W8PPlR_<@bdH7uToO3L{^MxWY}25{yua4?Pb3*uAd+D@}@7KpMvgZ zwqT*{H2FB*=9nj)wGO7>Ap^H0%`WcWnccs-B~)-by+Z;%)Xk-o+BXJ~&k*vJ_V8#` zz}f%~ZG=eAs!HzW30M6Oc#dap2XKadK!tl;i|#0+bh1_MQzTCFr_ufWRi0M(c+bw@ zxsdPwf?WTTOE;+i_=(TE_&dP=(`f(Gu>F2Wu&Y#Pne^m8xsm_LXP`z(2=K`IFfLdA z2XFuDPka*q#ce_61^hq#4DffN_@JD(<@1U4Y)AV2oqxUQ-`r6WJ%ATs-47{$J=5Pq z`VW)%|NO4g4^&KqCiDK43gq8??i{K-mEb)1U+~p%HmL|kJ_);?5%xq0a8Rk+k7l)s ztqKr3rFWJBMiI&aoWp97KEXbaLFWA7;-tdeCBAyOVcqxK!2)M@Grl*fR-ZW;n9etcR zCMct%6bUST_#A({1SdajK+P8{nV2Al#ShP;VQPoda2cmO(%Z-ec_W!Nm&{Aj=OQRK{wDIaVz z4T!G~#-0@+MRPG7ozBa6d3ogq_4#d1?0^4`5g$}~vc&IoSay@&Ij=_;+T7m$7PJBA z9*mxhXe!^mUr?oX-yLIdWeHb zI=y9z9p!}N{%~*rEvvVevmBUhfR^?8_~A}cS0$mXu#w29_{&9_tQB*QQ1)4swj~tH zHle77c^e;PP)RJ`ju&vNxKzi#@fFo8rxi=;jxTa(Us~bfCoEsNT!D-ey#vorXA>v} zCykcZ#7saJl_$!@;KX5n9?=R&`ihVx417UDEcSAIOl0uUXt`5LUY;`0!ZgCF>T^am ze*fZ6%gx=tBrXPjV|6mVG+ZnP+QVNd&VpXlm?NMZSkQ>ZoNci3a3Hys2PIYv`g&53 zto^5(vprLiSUphKOdo+Sb9-$MnT?_6Eqad#@BJVwHH!TBv8%winfUwsM2S;7pHw^V z)J3;8z7bB|8U$Jv79MMeQ-{#N;Xx*Sd63-Yo4i!sd#wf^-n|=7?})@hT#uZ4EY}#e z+;H)zxUgdYIgc#+7)jE5N~;h}!;=T{qM#77w9Uaubyz&0+1yC!z3A#a5C~l1xH=u+K!3EUu%X@5OVmRyl5wcBWuqU@j`cKQ8Zf#Yc| z2o&Eu?8-S%5y|&1VW-{Q9KG^xy>M)T>6sbrp?8XrfG6GrRC}3%h4)_?6wsNY4ce5B z(UFmn0dGb|pdHOI@%kok(A)}feB}u|$imJ8ihARDTRc*IYH&Qe0?$u#G2dor`^pLW zdN-)mFkCQp)3hQu(3=I!=FL}1zbODBS+M4C-z`QKRkgaVvWr*oPz@0bc zPP5TyIiR1+>g4=b`%d3;lq`{6(4)ql3(foeR?+^+aN`tk^%b{w7m8Q4uq`eo?J zL80-;45f&ziR=M=kG6Cqa9w4OreCf-@nU*Y)n;Al&40d}Sw2yC?7GKSE(nA0{+#$w zxl^RMa)If!zJSDgreEIxebupm%&O+Q3*8dAB$YvO*q2(0^=~V3N3(57E=&cB^k>5M zVanbO#BiI?jnl}Fqje76kWAK6Y(BFAu-;anv}*CB1@uUo>?}it^!F$0-Dk*Nnd0}E z(Z-|{BFi!wares$YnOIh^uGDeszV#qbBK<=S}qJRdb{ zP~n6F9z5nzR^i5G-#E7({YeX$tgNa{FqzowVvXnG+VaZ!$6>y3oU#ychpJx6_14Z4bcAOttHHv{n?}d z=tY@6$GQ_+6CUwTwEeL;Dvx!iU1O`0?WnW_%+tme`}6&gzy*Rdhe1mOT(7Bbek6K4b6sH^ZN6DlQ zA3pT(ngeY>Q%2LHyVt+j4I0{7B{v)h_4)3=T6m1yDgP}#HD=n6n{ zPA)DN+#1KFLepox9il|!q4RR`EZe)ylt-(LWgjVzKAat^??ViMhg#}EOxJ?CN%x0W zGp#Pb+o34cj+L9NXtP`{N!ELK1;n#~j&yVrNrAscp`Hy)&(#SU*lA4_P?6^6=S>&H z1g1TM&wG^LOt6h6H|hCg-MzzA0RwaCD5d zbi}zrGQMGXsdR#*?PH%Bs=>a}rQ)+P69^7{Tvn@4u)WB~ILG8>aQ28lEd_|ApQ%6ko9l#`5{KfV3$y2KUN>Q6$3AXx{Iw03c z+%s4nE^Zan8b;YXqK^S<4j6x%*@0)5mq7z@jhEu|Jp#tRl2#mdki*?SCUHFX^Y-5| z-+gSK0Mv%`Q9D6#F_$m`)Mg@pU=}#PY-qdL*hp>Pxzy>P)!ofsqNP=tNY^>Wmrw$I z{LPaI$2_FDa}@|x!}z}r<=|i8>X86VcfdvWlNniD9QI+^B%X(X3rf@+kov4I!Zi?z zq$#vQ)l>C(xeF<0^Z1EIUAqo!J|$9CQPH;3r6&Phx-fRXaMwuhTgR~~&uOOl?sk%I zFNomm=QLbh<&=&Rc^#g(&lEb%DmuLcQasUjA3oWeUlBRGWlzOvJDT5B6@ay|oZE52 zde+a?c_vUveP7g|7k<3K!#@xM^rTZ?LMmOetAN#KyQ=}~;6zOCG`ckQ%^4tnv7Su&ol{nY;S(QnBP4&(Cp7vgX*~oXNEXF({pT2NhYgw zmKADiN;#)vV?#bf0n>O$iQk=$3Dl>TwyoivsdIfhB7) zgtTTY&@SrYdwzv=U=eBzgigNudmDK4fUZkpCN%y}eBkgnAWxsIhEE}=ZLJ{UTsJfu zj0GmuVVBavDG7s$2orespz|Fp7Aj~>eg7`e$Oo#BDDUYOATtW9dJ4iy^a)!jMs5%} z)7zF7*$qjNBaQ?`mBo(l{@Vu}IeCfVn>&0ON*%Bu3L$FbK<*AbrORI}W|;55?k@IN zZSLyn6J5qc(E*v5T8C14jxD<8cY*9IGZ27t+}^XiB`jPspz)=VzD%fp88H%HJsK4q z&49`;*ce?}zWB3NG#S@tWFnht`qa22C1bz(w*fozm=`aBwge|xKFCgN3XFaltIZ>B zz%r624q&nqxkFz@u?+V<)=ZGQr5lb_xX_`=-FsENzre)mv0pmp4@P^-r|@TbMXey( zsIY&*xhv8$SmZcLK0)w{@n$bO%97=wFeXtx!4{8K?4mIwFCCjoD=(0p;eD0PJLQ+RC8=0apQb_IwW< zsdU+(8}hPng;sBn73;F`BTX8EmTWt=zBGW{N3+Cs?0X4g8~Sw4U_wc5rO_gMJ0BW6 zALwQm)CJ?JQ@ZC26i}jto6Az+1HoSRB2Ou!qEuL$CW9^EOch*w@O?t~K2#n-OM#6K|5VC0&`5^5c@*bN zbkm}>GI3n~S#Dc>>r+GEw6n7Qig4L48Qz;UV(DCAe5rf9#3s}8U}WO$;o(DIMIv2W zv)Vg*3TXrp8Se6CWoA3v&x%g6G|YvN=!C9VT4Yim7&%y5O+*I#+W zwx*5phz$mLxVd9m*NJ?HNl9ll(E1w!6QieGu75m^h>N?aCJ7)w@67L>r~B`HK-C9f zM{Wd?ME28C!zd9@cZ& z(<2`WC%LZ=aFI8)&-8wxV4@%J=A29Y|OiXOwD9@)SXVDW&|TE5UN&62{we9u5@2DQayWLM9)|M&udfk zk4JOKk~Y_KBbh{`z_%PuYAjD1l6vgkZ7O6*F34+A5c zBV{_9Vr-|%;=a-ZofnB>uJo$q7s|QgTJAXOdzT=u%~kJ=6J81f(i|H?nki~xRuSjn zWXPOBqd?#5T4hMWk<*m#rLk{SRgjrvcsRMiYp3E)>)4n!Phj)|&h~g?!oYa;E#GLn z>NG5eE#j!FhNpwcb1klg^m6ZCBAo+nZ+lzRpfV z6GFCS*FFB~B$9(@u>p+5S*Kztl{$&^B}fD;cBkOR1TP8}!= z#sZ(1K6#DzY{Q%;)ryw@t%cGpyB#vHr~yLMX)YXK&&G{2&k@0MYxXmkp%v;8%n)h-)rgEKdH~0+pN1yDg71~^n(DKx{r#jnuJB5`g|dP=ZBOS(z&4tvtpc@BsVX zmjVR0h~ecKY#|xdo5a7Xg@&hrtVyq4-*_zF@H!C&K(0M7gUFiCUIVO ze?Mbx^>&P&+n7s%c^9?&{(Nnjjc#scUS20=2pnKcn%KQYbcRT9rse6;s6AM&94m;Z z$Zj%uUpj>wsRlV?)}E6y?9YM#6;uQn9(?(Ihrs4ZlQ+|HYm0|~|D60ieSYL1V91Sdgl{$4h4 zRWEm~hxD7Vd7m7#w)3O3+0Nkkx3|DvXgq3`gKEA9s%x04s;Zw8N)*_nD6d(pzNta$ zA%*R)XCadDzw33Zz^qkN-j1L#3n{8~Bb|9+$C9K_V?`TlYtEiV7K55mnEPONafKps zf9WIeDgKK4x%@uo!CTYi?qnBXkguw$7p3>CRYQl$*3<~ErTUud*bR^-0Bv26}N{Y@zev(*Vt2o~?m_H@aXbWwPV2N!jQvmX= z80k=F`82rqY<%gGg8tE_sopn@FE>xCRHB$T&X>@DqX!3siR&(KmxqpFatdfFDh6G^ zUYUoy#5S|AkMxSR`Sd*K>)aS)A@>j6==QCt9P>Q)0gx}2P85%!a>%tfS&SYbVsgqW zKGQNUmVP}c(OukA*@}tQGY)~^oHj~~a!&;dCO zOe+h5s8WDkw#ys5ABxP#V5}CZHfXdOad&rD1)Ai;{h}X~8ISS@l1is&HFR`f zEk4!Cj_-28$BOde{`hf`J5ochY9KeV5mn3;(##oB;0jGQeYVL>XdX;_-q7sPqY!uZ z%=%5xs&md9)bf4?90(}i-JXD-7#xTSBi2v)x_)wHM0qb@TFMhCngW#!(C3cvR|C2) z0Vyb?&kYcpmt=uZxs@4IlaOVDBock6vu~7JZwjpQ-ofTKZ=5DAd4%B1i$7%5?US!e zq4tbfko*@dFA?wJ4=2V%gNwwc)l$?XznCW4cqoD~jP*yoo_{Q(+*bqpcW0H|=1-aX zKbPN7xBWyETtiiM!TGF<>ojVg?v$w4|L&*%ea`>J4K+9ZHxK^(JlH*19K(vbDLGuX z#JwURA3sCB$-GI*oA&l~-XRJDiqW9LGs z2`Eaiv&!tZU;eatw0wu}6t$YCR!&oXI6sQ zR^zrCXTHJ1=Y4~x`Gbs_al=>0d;u)%SO3XBeRp3^RLgtKUIs)3^{{;9Nz-&ogEsk6 zOPyExC5KC`m?Zs4E_j4Tvg)!D(#eM?i5#e=D#lW5Z+6Ld7E1BBukF#N7}l>|pSx+l zFvMXuerQgz6b&$#P43Ms9J|xcYC4Y|&ZrTR|GrjkHzr5FP%~)R>HgnF;9?gDewl>Drsq8G?l!4F>DA zY-g6}l<2NiK$O-`C&e2X^kS>o4}A+n465p^$FqCuxpL2?UtRq|!&+Gq;xa>qh^Za& z{`&T0Y(x1Y#w-o1AmLxz@;}V0H}Aj%HJKxAwfwmxjcY{99jA@w!cRg9+XaP^`*YT> zE)1uH_5?np-`lRFoIbIwfr4X6?7;0Sfoz&tWr7>M?^R3WqhF>5nItoX&J< zE#KkdZhExLmHfsg79ImOE=%=b2Dc4Uh3O+VgV2uJU7h9XlPg0dHoEe&9bf*B#OU5;xyE33+0P|wuS(Dz<)PuE4bj<)ullo_R1hPSNeGUbG`=$3r;bl(|sdckQi zdUs}{&FXoyanwKs$EhNvuRKv2G*h!BSM%NdXVdJ6aHVXeFH9DAX^4x0GS|7Y*k%n@ z*@p6+q^d~1c>eujrFtn^PxXoH7cYu|QMuHXd~dRPfbgoP$pv6uM!QnG5o12UmoHBm z=bbap&mOVid@r}cCtv*T_O;%F3GhHJNVZ+R~CWzwA7bgV%hx|;w(K|COF za=DxM@9oNS1n^zHcZOdFVyc$o-M>@=+i#Zo5%u1KR=p^jkn^Mt?ndhz&HOKv!zFO) z9x3ZcMy=nSTU?hJ#@H+Qu;_bOvbS&Ja@JIt{ zErSDZv5mXvzo+j%<(;SzjKJ%#Y`?Ssc(yV?LNWfvzztN#ZUN8(BvlKcRrOt;Zf$n= z>u}Y9q%#|MZ^Q>UJvlE8rh}*#$sP5yywUxY8&E<3t4ry-Zz=4pPAQoH;7|+!R2$1K z-n35!v{ePwBJ;ONdsR}L*2)tHMuRJMwM(WJQ_jJmb73;iUzHfSpU$oiF`{Gq#o{FM zll6Yx88%v7hMN+I*pro*Je|Emo6!|Vq>Wp1OtF<8$=0B-RBPY>et3lo60>beDA{1b zC=M;Br2u&RuGClkO8@_PANA!$$>*VKCSDhs4pU13MG4_@Mc!|ZLCbNlF*s`re^UD< z-jytw0v+jB&DAGQH>l0&O;dsPXX!KHk+6pB;gPh&@ogB-w)f&w2-J8R@U1K1Y^OEeZS?y+=3)HkN7u43dQNLGZ!Vy_ z_xWpP>x70)W?m*A=7ZvIJ1QJDBMh3BPO7CYQ(Pqf|JeJ^s3zAoYXLzK5fudi0mXuJ zmEKW7=}meEX#oMHhh7dKpn&v_(t`97TIed$Yk)wgN(})by_4^bdS>2v=geC3^ZPM> zTuYZ4@;vu_m3{5KuX{VQDw17WbQN=5-k-r9{yvfy%-0A+0DI! zZBcIRpK+F`k;jBp-q5vLD~tKgxSI=6qiSgJ7GDQ^HF^Dpkz*RVGTRoVY}udTYyaMJ z&0E$I{{SVbpg4u4uBx=1PlPt?d=u>@oeLyd#0nTUhtd&p%-nPT9pzU=Bo(Bs~{6AJM6)^izQ^4jN@PEqieqQ;#%pjl1u?1da+4&zM*Qm?}11wEVT}%1?@( zV(0)n!8^*%sawT4npblHhlOj*3p5;{^*8!&snqn?>+v@%Hipa(gy3BGmFyPv{Hls& z{&RHtFQv>W|2+zG=Tlk$Qcg3;CQ~mp@h`m-eua&t!Q=RNXgO8b$vnx42&{OIh)zr6 z3OaW+`cWX6Fcm=Z6cj;TAuX`zP>)NwlV>h;qjH+ z?Br08tzQ}m>x(^Pa{r2{YBO|u{l>?^qFf=s zH}uI!q2Z$Q6CHrqK{!F8Q==Ei!%YlvbFPk8#yK<{vROxIWOm`2#Oyv{Kf#QDGN+dI z9c2H6E~ejk#~{ep<+V2eGY>{ik`S(&Y<#mcOtgiRf@Z#UPuOWu*uN!YqkA#qqwM?4 z!N!KV8zo5<3wtU|8%`|&r1R^~a0$!9`N7@Q(9Mj8Tj&%;w@2%suC@9S_3G`PWW;=K zsFQq>e=|BK)PYEWdCmq64=r$*O(<*X03<+z`R!gf1bNpAv8rD6k`C8GG|en@5uyLZ zkNUy?u#1M}a{K71LZB?$haX*e-)l_GDB(5AyAQmh#c&PN)*R0fim;YXf3dE5l$w4y zNS{C1?L9OvqIS1H=4;wY-}MPTCr@02NYO1{-=eqz^TAD0RCa!KKtQ~V*e z(ftS}Payaxu<7ybaFw}R_A-FT*BsQW@6fc!qCKapOy?d=4L1<$qXXRYDGbxkF0oCh zRn2$AM@J1*?^K{;1IpgEr0zS7ZuJ^&y2KzU^LkWouv)Q5%%2nVnz%AG#PV^@9R)9v zQod0GeV7e0HwSJ%te&q`#4_I*zb)1de?5D5U?x{dS*m8l4UTOz_B}M*!MXWHPnSL- z87q77EYA%34a^ALx7kEZ%gkDH6q5#bUbLeuy0n9@_klfC|9VINPFHWzq`r}v0E%4~ zTyGc@qYS&edj1G{VxJb^V-av1^7`D+gpPuqA_xVBwiz^&c z?lO}U;>skwFtnLOPH?gZO;dl$d-_z-z0?YWxP?zvMrW~4&GEAbovrx#8+}m8{h`g? z*C_7<5oqsyf4U!biOrg(YJYNWkd$lCIdz0aN0db+mk9>9C5*pzJHg27qefvOqcgpo zTE0<(NcuV`9wSyJYW02S4hy`tx2re4MBWmg4QSYZ(R&?(Zcr&5ctAH*(WbT_5|okz z%H9bjfj?crzjTDZdS0pXrymNOQm|X-%WybhaT%Q90OGfy0&SknMe!0~T_DZ(lYI;_ zyv(1@63d~jRbIg%q_SqebDm2xAbP6HccMF~VjcryiZ^Qw8zgEI&3zEN{m(3>K1#&Ht~FpvGsc!w$KFy=U|Qdid!NTe~Q7u zM^enAWRX~+)ZZfd<}Ap??l;x!^9rB5~j6u1*m0vGczg%0NrtABx%$s^P74KiSBPNKh|D2c`JT6PAIhR?adhVzS*OI1sU zDs5Tm90yEz^+*uKP3u~^6_!P;hwI0O$*gw#%}d;@&Daf&65h#`p5*P2RG)7qNYc-5 zN+j3kBo`-cDkp^ao)NbL^FU3BzVil+kcpa)Z?CNf8hEevq~G9R-03HAOQ9))^OqVm zAiAg|_grZQU@8uMm)GjCL`kAx+ETb5?0XVdzcGkhsBq=oThPaK_>(+CJb#Hq#94E^ zNZx{&xs=P*A%>cZW0Y5T@U^o{0VSO0!Du3?irC{i-g?w(=1>(Cnb#!vK%fveQ~Wb;`Q%`IA`S#iu8rrgR=9DXKGqVdjYS%~REkNyq7r+Pn&U-saDSyE;^wAn8>*}lCsXwfC!qAh^z zj4LqTSrHzrhdyW1gXi(RLiCpt=?M$__St^^+EiyY1R2%{tdw99FmOE{W=~9G{Wbm( zAy-Tm8zz5xtoQWY$(@yE4#U2#6PL2$4Y1flTLBqxKFt>zz>^U?ex30QHFQ$ZeNBoi}=ShKxGCCD*3jMMP}2J zC_Emq0ZkL#mE}+qDK!oBkeH3$HvOJ?N1{c$>UwwrXQhKubK+v)X0A?|S$ZUx;cwUP z`pk+Okc_|7D>qN_#)^4+!8HoBhBwC=Z|-%<{{)5x820{;-uku2RVj0ySeYD&omJv~ zv_5gWwbYP4S;u$>b-tn7Q?+>)Co=it_K#eP<{(xR6TV>bdraYf5Q`#G8&t2H&3>h5 z^WoZl$xz<T{qyU)T}9PX_ayOR zPc4NfF9<*;RKcj&RPQ|UeUnNer$^aTE>PWcaaB*TxlQ;~#IimAcHVyULnD->*0@Om zM9S^Gx#xXdVCm|Lc|D=Whj6LpI77xJZ)w{Y>?&CtyE7fm)4U&n7Y0Td3rmMS0?p9^yRT7_d;&Wcuy_j+X5F=~6e+g#+q=995rt10*fD4C3P%&51w#0 z{qVp{H5526^^ZW=YeU!Jbo$jcH+LtFnQMS zp&J9ipRA?+lv}d={vwr=l|DIKSFyrlKyhSP^7(7%^Er^LqQ7S}@(ibHm2Jr1QHYNM z#Vsqdmk?)3Av8jAb%@AumQq^*)aTHJ?cP#b6rdATiO!GXe10-@d;aqgl`REFl$=au z;RhUBUula{DAMCsdXTU6$&9#EKy-3_OruowCKPuZx!U&Dd-#SQP)k2c=C^wp0OLFR z5wJHGejGi9akNM-bjN^fb8!pv{auPc$Irf|hrVGNpVVc9(GX!QXgK8{i;xun?{=se z*0uL#eKRk-$pNsMPh6$1XP*jxpF=*O0c6n2x${l4hr4k^tQj}1vYQ8Wm)zRUT_j!1 zxJAgatT<>O{=1@Y{i0K;;OsXQJ}w^D<|@5 zwp4h;9LxSpplSJw#S2-k!&eg!gV?E}@Fn9yBp7utzXD->JDO4S$GF*x!t*4>r%3(` z6mBH=RatMZ9WpP_Gj$Qg+*NsiIvaL{Eqd|gDv2`Ga;ryf)q!7wsxEY=;hrCXhuhW9fxjbzNBGr4HZ67Gd$K4E!iw<+FMQ&j zs|b%NW(khqJyG7dahGvP&QA8pSS9<_MqJ9JY0pUcg585ODkiV^vP3}#Q=Jy=tEnFv zJaM&807P&%QhdFz_8zWbV{FW~nkj2iG@5YloiFKtiN*f0`&v@l2B+g#xka``*TO^l zBivmFe_~RwO`DA_b-G$Q*ZMrQu5$eE$q!hWLb^p%B-6WZ5c7@liIwxWkvui=KCvk} zzR_i7E6QlF-T5(G`Kt1u(=Jcr^_x@@M>Q`U^@wl%$>mZfS9O|v(k1{{W=aBtp7&9F zpnfb);{BI%=T{HhS;AmG_t490gYkVrFPIXEb+w4Z3WU22|Ozlay|BRyc zyOVpE*T(GG3=hbeT!MU1C52fKP-6LE_P8FAC7JQ+>62#GpG)rqr(+g+lI63WQf9ws zbKRJurs`bG@Z~DdDvD1Q=Fc%q(HhKZQ~S1(ffqCWCORo=ce1Gx8QSZ#xgaS%sh#FM zLYI5z!Rm%-4KH&F=&$!@*Y(P!$=UQ5+Jn|^(o)7xHPm$>To~qlf*Q6JL*0tb9|k?Q zc+1OWuieRNxk*H2kMXKVrQ!^_P}etcMsW3Ergq<&B@=pbb_jwL-<&1*_g;M0yxAwg zP07I5?nDEwDG<=r@%DMOJXB0tKB>`c6DC8SDm2+t$oYOPcd+`A?(gd=>+OH}PFNlL zT>7M^!8}rt5nUZK?ocNOkj6O=`jpoMfTcjY0>Ag=!jIp!tl&1)HiMmQW_K@kL0bz| zyU+fOJb`1uN8QfoA8W~RF~8;=J-;LOOM#x@R9^t)f+8AFAfrHUtPJ1B1Cd3_cVS^+ z$CG{9c8~@i_e}AwMN!xAx~*MLlANv{W>X0kYTCD0aVQ@ok40=o_$tIT1r)Mtb;qYG zH^-hpVcxwl^^sx+vES)1MzyoOp7Z3a?gc;^+57I~fRF|}tsdYiElTkdK9MRv z<9hg9SA^*9&z#;|XST{PH9{5Z;{lF0>L-Zx*Gih6esknID5yQ5i3Ga8_Blfeh% zO#5cs#@FrMq*mQ&CW-9u#e0U&2~F~i6zI1d?W@3AwEHA>%U3SUKCZ`$?GXlw?i4S_ zs|+0-ZQQBO?KRpAcPfXURgs~`+~_tH38BM&7oEV5-? zhk64XY#zWE%7PJw+3Kxf60`M^H9d>sN`=#)DRxq?b-V+a%$G^8SevK~Bou)gpr=ie zsd~>;b*L&^I6>Z+ORQ8K>eO}kTJx_rCL20k~0~|bQQ%F#O;m1(uTH_*-psv&W zfYT2tF3ZKvrSm%F&d?7nmO5bAtTee2*2Vo^JxBg@53TjryzFFW8supr4k2 ztpCHqSJ%Jsf}|8oJMV$|E9SDYo72Z=H(O@#q37fH=Y$O7xp|Z1^mpCY_O<7NXpLi* zdyxnN(V~>vujR=hrfqEXz{A>u<%Mj#nCYkZ-G;^J0Mi22G!J9yc*lDrW z!F0V`AtZ6S$tuH>jk6966Wf&pjj$f{4MfSx<;fKxhcuD+( z8IzaCZUa@?X4f${jOCY^{VzE5zx;Ch*ApgCtoy_^;X@_U&6t2vzeuMF%O06J)*(KV z=2NVyY{0umr_R+*rBN-*$8|CY35@$K1~P+sDRB+yn3(;i84-rHj&b|p@ay#5Ilf7W zTaW8qD}>CxQkCCS1;s}R8qUOSlex|m&7b>M z1r;QIA02h=nLbH8AD0OB2|x6X9pzy#Hdr1O!6+S=1R&%TjF!ZxGnkdXw+%MpZrjB% za4Hmkw@uT^hZoPMG%{A7jT+yTsU)v5K^r)JrEZ>%vb? zC*xhp8V&14a#LDO{K?0zawN`h7_PEChgr3wE9e2d6hVCE!u2T7tUhJ(-SR=T;szQD zmZrZcrS!b(q{Irgdn`I&tL&5oD=l)&a>CRr<*x#sQJ_;H$FfIb^6NMx`;Q-Ye5-fv z+aH~V>8Gii_S`DzCY1XC$C-=kc{>dpYP*wC>f%aCjQp)W*)Q41bo}jx;&l8 z3g(;y&t-kq!832S>g|t%Da&7fc{;Z03^SO5tn2DW*iRQ1&CS(NIPV%wZSpiG7sTbG zjC}5{XqD72Lf3??Jbd3Cc!OHzY;&02d%Axi+x#ayA5|^&^o$9q{xO)Xbb;ld>_DX0 zh%8qV&Ugu=xiNs5-)wb_`JsZz#vEjDl3YGdJ*WH#w}x*|e9+WsxhS_Bs7xD8nmRJr z8OwbO0QB#Dyd&2OYshG$2=UDPoEPs8(b}cYErayKLTUm(+8-+E5c-;2&K$9R+A`~W-xqJc)7P}Zlx zw01i{>sR9`f6jMsV0)EflFKW!RgfJ%npg_Q| z>rBs;wo7$xxc9!(t}yB-pBoVhrX&CU!~Wq_DHBR*g{Ex6;1&P?scr|)I(di{;yL3A ziW}7qt|JvhTwn53I@-u%26GhM0s#sUmsaQ60n{ZHCr3dKHx?h1+fN=GJ^tds;wKeG z0dz6iy>&C{(Brtp?8iZce&UPAw4>z~6-+)YpKDyOFQ&zIl-=Q}8C=kLYn7l$^c%n64z1N)Nswv4AqP43(w zzw%WXO`@n&AjDk1Svtn28^L*gV{>o}OQ}Jy8MHT$lJ%5O16B8#c0a{ua4Q zz4EWbj&zJIUU*NN;rkfS?5M?FoNoh{8FkwDSi;oo{9c;WS~BM&hoQ->4o24 zYmU{wH9Vo64R-uw$_b$%{^~}f;q=O6or;m~EMx7Q@e^K_y-s`9YkK5He^5+ge{d~2 zNNEl4s)%m}JXaaJin_Xg67t@0u=&yBpPB|qNq>lj|Mm_cfMTPC_HnuL%OUlKB*jZ~ zz3$q<^s(cQjzDRLv)@D@6jTn4+v(RmCr&|UKDN!xwCs_2v?7k|7eYQV-1(bZhbBJU z#l?U`C3mnT-f{Y!?N4`3B(raKTnnuM=aZ6JHLXG&x-MN_o_~HD?Kg-JH@ZLY#AyEV zFm3lGR!HFD6+Dd{F5qbJ+>Tsbs^w!GjSm(7NIr%vDOFQ{}X&{okJCXVJc3(4B zG3_ooNc?}OC@!A8qk4m*fJOWA6Y>XyeLC${#9+$AXIt;J|KqLEW@;+J@>sx5FXJ^9 zczly%&^cLr)~d$k0FHDG;_PrdVK=vsD>IX^z>e50ICRAqWeRejil%P>fkzC0Fus$` z8tan;(^c!2J_fHv3QcZqr7d7#RAwe;(3RfdlKD93oWh~2UAATcje5%`3MeX+Pv^K# zvN{paxdgf_S$}%1qFpwy4o#q=!Tzx6?f$!G79(1g`3`;mn+!&D`r-2%YA%`GLobRh z%Dx2vOz3M%g1wd$8W=*w2^e1`{j*}-|2&QCkW8iGh33wgoZdhPEV@^2^dAAef|b`y5J zyA1tr|JlEEqyP5tK(q%$%u=Qof0s!A_}~6}qL~5PHaP|5=l;V;_kZzu#DwjPr-B~+ zuRh>+I`bneC7ZbS{%^1D|HY?VJWDVMJ9UNZpP}~uQl|aKx3UMUXvZw+KYj@Rn@^GY zznAlWFX#Vm4)OojHz$bX{ENS%T6UBaAitRd2Qngstq#_VcChrCID&$&RIhUR;j0-b zVXwWY^9(zoFVXM^s#ShpKv~IMq*obB;M0Ep?5lm#vNx3tu;f2=tl3uPT+h1n9;^7GkHdU5qX=0?0giU_jLjDii^Yb>d+rX4IT<$Xln8G&82e7 zo@EVg8~J#Cr=}e*y1aCwKso^ zrj#_=YYF@%m5-00xu@DJOeJ>Rj8fg|O55VlKKDI2%X2@+8K-uWLs5yHv1}ZHWR%gM zG3x+>fvZfi677?Yt z=f3i(1Ef;3-n8xHTWr;SM$dmX(iEVKxAh87B7)`1>Vp|Gl$fw=Y*^d?%Qky*!1I{WDoCY=hE_4mNPZ8-;n={@gSMqc#asjW3+qc;3 zT(JwhE#Qj5oiI=-u(5n_`3O|nDKw>UewD##;_IIHs`ftI;3ws;0u1g7t0P5qTUb=z zTd`L&euco?Z9;5BirD+wGyU^v_rLEpQ?tIl*S2{3M};+T?YS4ljY=HEREyqlK1VsE zRa~!+$k(quwXT%dH18OZ8-whv*kjvrgcsP_Bc&FHJH4*z8gC#0D?q z-{Jh#|0B0!2=#!7mqP5HMQmPoRcl^-^s4$+XrF)9n)iG5(4`rjD3NK>JC9EJAKhv$ z6U2L=K92CEElabU<(^bHPZsEMLJ4WQCYpSt$T8B30~#W1rtm@~ihW z^Wg@sT1ayXWDS1;H$Kh^%K?!WkDE@@1WS>f%|<%7k-dV=v8%G4JFc9eMu(LBdQN48 zJ<-{kXAqTvE{|%OWM}qXoF|<((_cnV}7MMg095A z@nJ?#F1gjZubYAGJx|iH&&$o(ApIZwF`vs#S^^f>4gq`rz{}U)@^bG^7BbsHzJ^-sKMFbP0_c-TGwI5X@E`|GG77{@K%S`%MhYTme3~tLz&0r*}c{4~Wl) zZqfirgPKzyPcm#fOPt1?w)E9^mfqdbZ+mN#btqxwBG;W=I>)`Arnr~2Q>-=_RaFGD zIlxbq*3g=Cb#!#>KB@FRbggh$KH3mDA>e$9+PR8!P7}yABe%7Q_gyUge$xA=^#s0L z{7G}~d2S)xdVt{i*}ZNBl0muas@|dyf@R4;5%fLMzTrnP%GAq(Tr`Ka*-as#ql+8J zGL+Bm_mNPRPgbKAm`lcHy~6bfKIbiT$2sM^5vC}oizws;=A;)h>c z4u=qL(r;NCdLwh+DW~xvoX*rW%kGv~`#f7MpFKVR2#ZKt`@%zu&LO|>!8JIaYypU& zL~S9W_t8!T=1du147Gch*Q(YU&fCrBTXfO%`lycyN8U5<&8f66hJg6Lpt+kS#WsjA z{s4Wz>2&(D@YESStZZ+Jv*<{%VrAtP7zB6}?-m+2aidta4%7kj&MV+iMLBz-?R^hJ zGe81PhioCjjGhd>h_$Qaq1nFQM{LkIaLgNR3kl`4E%d45k^`4OX;QDwdc5X!mXqB| zq?o?!+bz8}hPf#y>uTn@x3921t_bt`;92?t5j7SRFZ%d)>a0rs-d9h27+jekzR(lg z$xzAbt13Kc^Tk6}aMGoR7f$x~2~(8pXwZ#)+Okun`Q4eDaekov3b!T4XZt_?NL-3= z!`a)P2lE%f@U1>k;1%LfOI*73mtFsdN9hy?=xrHbgQqz21=xUC%(Z8DAvF3K_`yBe zPA`H^!y(VX0)*@qN&V^*M!%~a9?KixqMD~4Ni64Ob+_V_(?mMnT*{Fqc@3}kc%%?B z1*d=TgCWCgNv0R%Ryo{MLr;%^;j@MZ*^FzRm+`b;T@)JCYPGG5d3qWIKfnp`0Q!xI1 zjokv^Mk?#Ey?j+mcYAcG3BR7%#);1sL{$#|B_$~7jEGSd8M{8c;;eV!Sjl}pPJ88p zUrFc=xz1TK7T|3k$t0dOU6LiY>B&Sb5#(-50Iok~S_!K@!wAbNkb-GEb&QK`2+JT2P^;M>oF?_p6!uy~d zXcOit`XqWP<@Kn}X)GQbZGKn2zfL3!HY)Y>c#D1NYVfwmJEJmyHAR;#-Z>M_~i znP7d)rBhCoVBH{0!$!E3j2X9dcVz*aD)s3*`V>2(K((zSHN1%HiuF_5=1}`XfAwsa zCaFy`9ZT}RJnMIj55aZZGT3>`+k1a8Y-$sE<~70W3mufV`-GJv$;+$%Vuh-41Oj4vloL#C{DtGzbeT`qc$ zPBGw8jTbKP4{i^I^4BZ_6HUG%QH&B(!`VD89n5{ZeT5JR1;f<*Fy>%Oc%(4AN-x_8 zU7lcc%62$Ehk9$om|$W9Jdri}spZn1Wk!>=S^tci`fGr}1RW4pWtl@;zV$gI!WUdY zag{`m1Z-hN??aU;8|-=D<5U45mCuj`)Uu)_nZNu=&WN;VWVoxCO<|u~f;DUcTz1~T zn>hUgO9Rm-!5_{DV3ay%bBQZ@waxRp2XVO*-Phj#L+j{fK6@#>8I#*-tbim_rK z7?4>7(O@%u!fP7g-3XjaCBAG2R$c#|#==z~v6>@*h{RMC9L7q_7Z+W^v|3X7z12R~ z_N?o^?T8|4Kdh;CX?@ad8frh}k)Tq2Q|Y7q26T~XTLS&UKdpMIdxX_ha&Lx&+8}#; z1$YfP5KnLEEHp`CojcI5TWkeJ*Bw9>A{PQ)LD^#oTTj!OL|u-zt{r`)RuRHBBBK*g zYm=UdGX;Q)ALf(caWu#Q>i>d}tNFIA;*#+aKS8ASBvy4cLNG&`E61;NoX}%%rYyU)N6stna+R~k z{ z7cV7G*Ku1qv_q1VCHBp!yK`K$C#U&~be*#AOj=iu{Z7X{=d$}H8P4WSD{wx~UexLk zl6Y1=_1+$fD0H0f1T?-3``(z!h<4>*?YZpv6nv+y3mmB06A9lMe&&sO4*;B@_mQ=7>B&7&qZ=2 z&z8yz`}{dH+IQd{c~G#F_=I~O_Nr@H(pxAatM*%Vmz=b2*70f;(*dJ3$6p@TN3bCt zX^%^;LQai@vZh<>m2Xi+y&m z#-9OAu%FTG^(NIH$G6Q&b@$z<^8|PJF+rtZ!j!z*a)OZ&Y%s|xLbw}?P1U>(tUp{6 zb!@$=oqU5@d~DD(neX0H{L4Yh75?M>#SAF7%BBn=U1Sw_x+>ZVc|3**!r(3+KuMfp ziuMS0)z57qN1v{-!+i4E=R}$%OqIHC_6Ujzc5M+SlD%UTaUK;LD-=Y7*O|zD_miB% zU&AHF(@=UBIP5x_F{8>ON5$1f`z`&4%2yv9x)eMDAGO({9x!|TY_!r#Q^vy!{27&1 zqpRQbWMhj}R3YIPl2O3E2$qD~-kUvC0KLzONuTZQ@klGP#dlL%0}RlEp{CqhDtHH1 zWwc-yDXqbcXNqC=5@>Ag2 zdfEYRI)#-K4_>A|Kwr+3AX;Je%7Gr$We!j8zEZC_Ea(-Dp8g_@bK1v@GC`wkdqAq{W? zP8aOPbo*5JFqFEXX?_>`rS ze$}D6vfpEqf~S54b3wP{<5Df$hxVW}pbCA2EqP(*x=D|zyqJ+xb98ZahuUtk&*H(0 zXs*jB>t*Q&Y|dAB$=xJF=k{V=W=O9ZpSe}|9E+lbObUgSObrEXeb+78U8=OfANc4m zie&9P{8+T}n~QhlUSdR0ZucLwKPs7+{!JFEoJUJH{&QKpj>9R;yK9u_ev1B2QGD%P zxu6tIsh;}6m-r~gh_hNQqkn+<(Wb(Y6}%)7T)+{xAbPd6uAtOqFH!ymE-)mj2RA5BZt!Zy&vJV z_VBM}hXXe*Gk94fz)I*%V6$*fIlJSV=qpOPDT0!N+flBe*Y34eP<)p4+6oA<=U^1M zj$vx8>pqBWB>RDRcN@o0KLw3mH_~_AQ1MVRj#L7zluJ6C8_O$_bm&}TF?_5Z$goXj z^&y++9{oiaHhcf&ptpJxM?*V?(aWOEVQj;yKZUVlt89MuDiMbXPcIo(K*5(}rAJ&; zsnF=lr17m|aYd9jw?T$^p;fjEOC6=mhNul}c-G;AIab#nF?k_&y666+3f#6rYFi_1 zN_45C=V+rd%jMMS!q}A5Zm*=%0_XE*{Ufg??-mh!9@`BBUrZ=7vm0lR8}c<}ee8@` zdW}W|yDAmS=?|PA;&S4%*pP^H^t8X)v#|pI&g8q|{JrsMMbY?XQERx9+k0Cq9#jau zn%S~gpIXbWes8b5*>Pv?ZOkGvKG4qrw&Z7wb#*2s;gxiDYI^#rh<8 zHR_d_1qtIzsb(;>;R|K^t6Mw#spV)$^mekc#jw*8@2D2Z9yh&URBT$>DldDo?M(?@ z$Nd(2s`tNgg8>ci^2<=ycZlW(iI0R1<+9r{u3h2H*S+nDcLbOi-2OV{TPCNij~d=L zYCa-ASNaYj`iNkaJ8ZoqAAjWqMpA1c4#p<_&fmToQeo$;UxlYn0CiI<-!106#40wZ zCy|J1x;Z6OuFm`!v30sV@?I(A9kWD{?>w_B#Oy_&t-1RZ>t`eb&e6yp;5)c%U=5-EV(q`*WHvheJy)xqw)5xy|ma zgkloc`iGx%hcoE@<0XpS65k z<91UPRLK@g;z7bfBNupaJGZLJ-UD=YNh8JZVJ*8yX48!gWjfO7oBbXH#7*44*@NVc zsv+;_Tlmzak}DWj+8&&B{m7k(eH!2y(<_P-L!Nu*TEg$+HDj{&YLS7nNj!3c$sq)q zp%0CW-14qz%aBc&l{6F@N07Z*R#LXxw*qDr5*7IX)!biUY_{Z;*4wQ>ZYI|fmkCXU zRNzMKUT-cm>GSNODra9Z%C=#l;UfcZQcfigP)nm#sMq~DjiXv+%a1L${%QWSh${v!6;}_--jJKmzLXTUrgpZ#ixU= zSZo(E0}jujVsW8-n}bD>$+0uufw7^z8J87`-S+eBI?>PugAv`!O2s~v<0U)4dY#jiYAjFv_oje7^Q=# zzsR_3DDI$Hq&c61`G0D0dSo=1xF;hbe4FFQOly@0du~$rSG?ZayA2#g1RJ&sDSqI3 zmg+WTLcRB)MLWaiK<;{cv`58x*Q3zfY*5wi+_koaS=^XBQ@O3CL|>Ta-i(u`~e(Gr-{6+W;I zR7IQUpuxIJ65tSgtu~{jAK`#GY;tM)kW(#Fy7~1-GQA8{DDgXmp&e?uQ(?Z)Tc8*F z4)a9^S>)$RNgVJcPr*TK0}HcPtCcVINpxg~v$&=xg?OXwJwNJ2l+M;iR`@q+SC5;B z?AI8wZsb(^xo(SkAMn9~GSHcmBTGRxcDm0UmbP&jx48895(KB*k=F~NYja(v$*a%1 zBAkWp2G!Y5?Z=d*zaI&$?@B`*%0^c2?4_fQCf}m8)TY1}gy~0aN z+}b^gQIU8}Ok;&eGE9d8`2!na5z9Vb%`MO-H3gUg{>Q6ahv|v=oejwbc`Zv;mkymeN41W~jGX9y|E73mr9Lk;?puh~hX6g?eL{^aq<7G-` zyN;f!)R{<|CIuX^G%sla~4dJ$~B-A;ZccUSE${p^;CZ&$o;}kgoLo85eO+4{qHrgC3~^w0`nL zm^0>KZ_prp?YAQ1rYVRi*01)>^7QfBo~&Kh#Ps??%L?bdE1 ztZRgotP%=VnTBvhHbf=F#8AVpMnAM5cI$&A(1pK9=b|b(E6x6)Dek0++0_W}%;V^I zEfXU_OIQ{dvqOMbe8v_pH>3{?g|0lk(u2!uU^8E|Fb05_#niVAC*d~qt`tqvC}s{7Wpi@&OmJl_Wj!jJZ%z|_{R2G*|DvBrr_c8MROhi$&A;xwjY7<&tqKq^JtG7V#sVx+aAkr9v@&!7QR-y z;j)xy1*VY8q$sD}{SX7M_14_FQ@GdkOF1eW7;?BWHUp4T?e&a=FxfHQPr(Tri1(-G zvtHsgc3B&*E`gYW$xUr8#nX)QM$9jb2h_w1M zty|uF5@@}I0}N^HiwNOwr;RX;7yDxKUOXz?!MN*~a)-mlzVaOp`N?xn1c>s5+mA1u;$%Xoe8Q7NS=T}$WaS#C_E@7R8-F0n`Vkcjk$ zX{Ned;65+tz`w^f#s(^3bDUF>2LbCdug(GEE-x}LDUC;MQK{`mTl&Z``&j!ZUd%cf?SC<6y@Jf zCxQ~V3pcIlql4FB#7*j9w!@5l_FIKRxa{-B)@(60=|E zutnBT<>9>i3ATS8zmVb}ogpza1c&g#f>Q3LuhYwB$vIryQ`rUkw>&m54C1emrx++# z24L4JMM)dl3srWpTj=>oK!st1V>#2>0!S$ok((ax(GnJgU+fM50_?5lH0YNiJA&-x zna~zr$1fL@%n?v_L4T{BWQ%mg5tjnvrgX?v`HcQU09Zfi)pPK}JpYgkZ}n3ZYrgh664g@{096)c(0qG~V?(T6TP>w~*@VEU)_!hlz9oe~X#fHSQP3v)rIAt6C)z#Y zIll})VMDK40fPG=pt~UDV=w7h8{|#TRMh#_Yj`j z`<&;z=lQ)K-}6l_=Zd}d+N;-p+2LPV%BPeqR9fr~W)_H#Q{Q7HLe7I$0sCc~J}b8!YeZ7O@mu8wRP_2=lgcyJ6nKl^mQyO{?6*Lkdb=<8yGZshs& zgG%{wP$JOMlTog^x)L8B4G-SCMeP{h+M{j8ZgU5vF0#g(nWLy?k#e~%hT{|qAmhd| znB`c+oVuY1#hdf@1Z7aj2BD2i7zWw*ZkVfUxB4$o-4zr5RDpNn@Kqv|($HDWEN_2r z4SqXvIjLz)e6(LT-A`3)IZWehb)dU{qCoi&!I# zULSt^F&t*Q$TL;F&jqFk=xJ^(N_7R`=|%hN9{R4^^@MlVAUAq)N-&Gbbk~H}kkyV{ zR#n~ON$2*x#6Y{0O$ThstA%jO;wX|Yi;mt9tVKm)=JSktC9#zoND&()1r8e4+eA_^VxH2?5 zI17C&F-;t*&@jq-rdl^Qy5Ds64nPPk@c6hJHDe@^uQCWqoRXAvll!0CQP)i#l4iGo z0hFyz75+?xCfM@1(e|1w2$TIaxYv-e)IQCsQCYtEr8KDtx^+!6LJNO{Yvd27fc5S1 z>JR!(RntW9tzcuEuAYpfj2n$#UygsflsG+R6T=+E@qW(bd9M0=xg3DCk(^(4$bAgY zV4vN{1780A0N6a}{Lsf{~2JV{6@Uv5J?RJM?8bk+n zcRBSZL67kDd-7&i!BBo33?jI57!}#yzv@jqn(k-Px%p zp&B0$wL;a;bFcZcluv{t2#e9gCop{RF1pq^#%^LD$I?0H&r|(8U(SC1xg?d0C zkmBH(Yt~tt>MWT5=9Wwv_jl{08(C+vMP2bfmcw=C^0|;a!KODr9Ki5bcu8E6`?ZpQ z&qGKv*giE#w8nPuQ$L&MQNig>xN*rBk>i0!@<_J76B}LjG$`^knnx=g+f$AviftM{ z?eg0D?mI2{h!PXkwX`q7WHTs`xrrtr*`W45cj#c(1$AF_@8AXvu-so>iB#wo>GVXK ze!@kS`p6D^YDs5{n&QiS$}7~v|cay_1lraQyxiU22pXiBWV@b-`UXHgc<0 zeb-d`;8?9l?c#%PbDRf*dUdgD$V=tA8Td>3e3Gj0D1kAq?{ccO1siNb2$>wxdv74D zIS(3M5!dZ32I$&%sp0v&E=R{hd-e?<;_G2$Ch3!R^o?7q7n#n0Vv!k#EOou*H4^eE z*GpU^IiOg`cyOHq==17`YfHdA7Z`2z`PS&jzv4vEY)fE*&NNtL-(=rb%axw>cmag( zc^=hj+Z0p^|3W^fC0f(+iqo7u+qLQ9eQt2j3$p(BD!KtH=%PEOvNAC~jrJGDBOq-` z4(v>MUc#7s(BRR-sP#cC?4w+N8#v1ocG5&i@?wL7p-ieB24XdD0bX$3`Qd@k`Daa` zOmD7ZcQ0)h5nsCWwv)Q!EH(*tlse3%F_K24nCkP^s3X_&@K2XPm0M9cMiNb0aFN=J zY$c1rI5MXc3^j)zUnOND78ZP^@c5|O{Tl%if-S1+#x#;CtTe@i#R214za4rKnlG9B ztx}y04exLnv#$p8SU$8#hXr*TT~pO&RVz;m1q8r|UVbjms_EFJ<+~$kAM;0%i9WYn z*=u`w->~KufTGG#wPb{!s>4g3m{2MHAK8kTlkFWj#=b?b8bZoQm`3J>DwyUa2^OWE z1g1E=HN)ml3`@!}H>o~ksysc{yg_S4Y`7Xa6Wr?VR<4wD5Hx$N^;jWfp$Wmg!g z3z|*o$2OVpafGnW3K9XT#?T=AqHTJkLo(v-j4a*RC+{6wR5h#ajp2G@MwGE6wBv+* zXRvQWDZyOTtvGn9FL5CK52#fQeC*AHok>#kb%-nM5g-M3W+vk!ai^3FmPC zqqYhNKS8Hfv^l0JB3Q_5bFMq?2lXP!^N+SV!QRQ&cF(FW*i{;v+?K5TfyXs_2?mk= zQV}W5J{0J}!Vt@Roha-MT~@eE{4cSmzV#yU1N|fwMO63qHP|?WE2L@a1S?az6ehn) zB7H^k!i83&6nSX|K5V9ptsUQr1gdYpWs>Dy7N)~x~jdF)R{S- zxdiV(*MtiLx+HD@hjBJv#AA%(x*Uw?M$k3Mok&h@%pa!nWc3+PRew@8>Y1GZ(7h{x zr;J=RS5VkVU?4FVVVSu|8vFp$=h3JtyK_^(JIKBMCxlQEzY5L7))@VMA6h!rIXLxc^ zh8`j2EG57W!|CRTuv7dn``onUH>zv$Ten3Q^RMrGGM?XT38KVUBombbVJ|*)5fqDx z8N@JxJ4yk{gHVZcy~Tg*GZL_1pjT5suQ3?SH&AA)8T$D{Hpz(@ zPx&2W+7Q;cKc~*Sma&Rxfqa9MpPrG#)=Y%DZ8@V-9#FMLY08fmQ`J?;yZX9-VN3;@ zKND?=+IHrYiO}R=!YX?jNunwD?J9u6mO^C6(G>C{`3g&1Qu@h`T;Wu74;J0ny?M}e zagi??=FFso8U~s#oteD1gY|zOE;5t= ztgISkl*2brE}9H%;R3Uiw@5ZMR&v@m239<8~tcZX#{ZQW{1lQuQI9h zP|n!@$WgBp@+i`6`*hoCYA+aOu5>iW)IAx`+@Ez;^KlJ5x;0Y5NU>rBs!^?i&PM{Q zRL2@tEuAY6%0lc5uXW@f#SCUOA&>f!-rao;RgwJYytCTyLn7SP-pX<=iWSs@@GrJV zxu3>|*04cvKRQee-R7nk&Lg8{f7+XffejED8IeEH#nER2^b&HlSL01;|8rAQlN>~~0U_|WK3m8X?$x#!`InOa^Ns%mE#O{{o)n>E z7JgiRb+S(<8Q|lNu7A2ldJmLmpL34PzHrJ*|HBXe@|b%HAi!SlPG}rfw->b$k)5p)B z2tVjld|@o};CcR;$3n6dBm<*|_DhytK8|{NwrSIL{P8xrrm? zK#?}M=$_x&GY=?T%UioRtrl=1;4!cg>9hpm2jC1{ZGJ{~-5_;fzYNRB^^R(%?&U*F zO#J0{+ay5i$~LF0^@-e+t##qg$jAtA*ndOq%k$tAc>r=3>kYFZ)~&ST!YemO3vT^t zB&|+oIxeOK0oZ}dGp~<&z6o62!7R)yg2>3h6_$%szePtC*2471%*={u;#~DBuByt) zFV(v{*nX0lKR#vbge#bvY+}Z7elrY)_0XR$POf_=LKx$H5|u77`$Jj)8A@v zU-9t#3&nn$OfWE%dr|i{KnDRA25Kj{qJn--Cl3(hgM;Vm-%R`Nww6HB0G#n<4B6)A zb>jDu-_D<~Il*0bC4QoYbg|bS5HYlH3n>jd#xLxlc`hSkN*?m@quki%h2N73UeTY@ zcDSRigDuM5!sK)z|4*ButQ;x7yI}m!o01=|n5g`W7oQidKh;otqB_pFn`1BNT}4Gw z4X@R4kD|XM#)_Nd^u&3my6ro?637)|W61$rf9V?KnXDhS#}nv->M_B{H(6z6y2!}L zR{~|!7B;jzC+9HP+l@bBX3d8`ZDviZhzKs!MKZN}LaEN@7^w2}#KgDLiJo^XZ=Kw6 zui9;2;%Zh+Vlqw=CT=}CG5YZ5M-KG=LY%NUjg|C>DOp$qYOrtHosvl_$!%Zll|WiO z=C_X44qg)b5Rf`KKDE!|-S&-T6-sJq1n-u_PbYfISl&Kmham8x*8*umGX-h@1vnAi z^mFxj`MskzS>lF<=M-OvijI!BiJcmH^0c5;%uOqw{TM>O3q$t3ld)I)7@N?VaU? zlYz5{6OcTE#HOZmwEjHFdSOq?Poe!lQa?P})L##6mbk5@k*NgF1aaRajp zExw{E)Q&pSDSu##i7P7Ww82>3gjW*98#(KBW#%fQRNI1d~($_Jgp1Z7=$hRV-7>s2;s zE@es(pISifSk{A&oiBI27Ko@%dSg2(8Fh74;%eXxNZiRDYVmo@6naym2JhWsv>jb1 zZV-%rbb7PQu5TF;fFO$l@A_eH!G6RF7cPWGL^M~}7(u0K5)(tNX5W@Sp_sc6N#`{F zqEa_{yL=Q2;(b_IS(DS#4XS(}*PODSkD`4@4zBXi3GxUl^Bbu}T);AQonL?^@c;ofUSagw)ih088RdadB~?+{pCA1jn{Y%x^d1-JJyZjosnTaEjfViSWBC zQZ{fQaeHdtEq_YaW#C|5hAaP&G`Soplwn-*eW%Q-H-LL-_cz)5$jK8r8Rtcg5|xU~+u z;tm%oKk)iRdV1`UrhIx`7ZOJTc zp6#L`9g}+T1Kg~P^Irj{&TjK1bR6%OkX*u2;0HNzf~ zH9o!cwS~P~P!yf9Pvo?m;W(39V4rcfl2`miQ(SZ&vra6?0e`FK-8 zBq8w(At|}EgT{0$nE514^tAexs@ZH*0!QCdPu!Ve&OR&qa5je1ctFd)21tyc;o(PX zA_&HeY({B4!nuGGlhpqzEhA$vwd4h;-UkjN1*&xl(#6d1Fq6E|U4A0@flB+|a)OL9 z==LaF7>N^hasTvrAmALnD?L2eLqwK5gArxn2S21=RHs{gLt1H~! zk^FL{7AvVqeD^KI9H<@2B>OdmVvDx;>?7eoD3Rcdl2Y)Q>WGciqVm(UC@8c7NRgbn z)Y)oT1Yjz0!OJ?HjGP=_&CgAztVyT=z;snFT)5DVaJE-aW}v5ky|YTCa9{50wb9RR z;PgPDgO6>v;6j&0jZbB;hc=2`1K%*v*f-CMr1S787yr`yfA&6&-rOGDw2ww%JhENiBIzulbgGI zDl)y6C`e;VtqAex8q`ONd8P8Iv+znGjot~HJ)`VJ`G(ADJXZkLB2cC5Hszdv?m_Q> z+QWt!fFak2qKuqo^*f}bHttHjy}gNSDl-)1HuhhUD%24dbDA`cA0#VIdH1eh(Ot>I z+H^MEIa!Y=>+&xkAk_r=x06#-c~a=1a3LxxNoWf3qTU^75BChy(nD0fvkYSWt{6QhlikbMz7KEEZxb5TX+8fk($yoy>s1>PYMex-%yLC^9sK_Ub6QGw`D7t6#L8R zMY>plZM`2+^y|b_Tl-1t$7_3c_d7}g?B+w;n|_c?v^p`g3*9|hcD*{}5Vw`$)ws(I z+7q#bTd!QJ@%DK#V+Yl3$73D?`&NSqRDXZ`@QLb2Z>INqh%O`RZFKR~f8v53Z9g&E zb8V;;i~V2RLk&(u!@Ed{|HUo;ea(M~_Fqo>@45M}u=%g({Qq9x3_fUnf2urd37eXl zf)*BTHn+5dx3x*EeSGOs(D^rvIp88fdwP04#aHyt&CzFMW`-h>Nbkwo_hc*(RN0EB z8U|XI|JM1q%n;PWGU?Y_gl@#j(mBVO3AaFqlvK{d^t8BX&cTF=yCSk#;?!tAx!F6` z?VP3orGl{V`yINE1qG}b+1cLe7){UgDc1fGzC3!-Pw?W04qIr)aL`f*eZM<9aVI3} z>(@JUw6v@kiZz9*vx1Vq2_p4TH}U`02QMz>FrXLy9j!b1VD}l^*RNmuUUtdXBkwDT z?0A|j0Ky$AkqU~6t)7=s3QW~Ah(8g>h9qyv8&ITe?Wb*?*oH?2B7rLgvN69B2b`#m zsFmjpp=#%8~Q$MW)$`cZhX>WMyk=F|H!7oQ=Dy(u#>QLUx5HJgh0=X2VEQ=jOB z@*wmEJgwDL>+3VGtTY4O^W2tz5@ zVuBtJ1_YvkIlT*Iq@@i7bCf;>>{gtoVqnN>TP2=vGgh4ZtoiQlU#vy+QhqWAef+2w z9?Zhdrrle-42;~@lP$ywH&~Y5i@(L}t$Hx(g7; zf(r^Zs0oZB4VLp`s=61GoEMV6a{iU0A(Zym`V?GyqYut1)Ga`a|9421Rk4B{Q&uILg?PXm;Et1oeDo3YaT z$BYXyEDVd|=|mqf{U%J~lt!O#RtskwC! zkH}4{)pUa&??4&uOw%Z*2~s&JHY+Pjr#&i6R$YD4l0Ev*d$DBYAwzF%Ouias`KOx* zo3N#dCpyP4+eHEN-&1!8y^b%~h-&AXAP1GL#j|h}hZWVvP`Ay40)i_)R zYGGuO(PDa#uiH*PPfYs9gU`6HlXU9fE&`H}7B7C9^Vzh>9D6x;8U{XI|2vkTfW`6M zP~6%~Y481`YX&sW)rMaH|2(;*q)+A56ADW7Z0f(R5;q;9PO~PK)t+u5Vj`Go2uP7> z7w+(9Zr1;rXxgR)tqPXb`^5SO$4|Z+6dla~S_DhyKt+II;hvTavM1K}PZHw3qfS6m z*bu>)TtX#>-pgWMG3P(`ya_K^5cTi{2jI)FpGS!0?xEM3ng|RA3;Z+mJOOl(6g>lj zY)!|Dzm1Shc!`PB?I}qrxn0bZVbb#ASy77^JseqLNbd3>*Vj9VNiPza3tA8pSe%9+az0hhK890&CO+oX5S6_yI!qwJ0U|w ziO$5D_aw0aMWbwV)t{v9xguJ;X;Eh935e0~@Q5!dC$TK8tf&NFK0y2Dt!uT6sMDVY zCXQJ}ND~sF(#e@p!c^=ZnGHCvV3fP%clPFpo9=l+GK&IlBtAQ%AlUQpD7;IVg5~QM zt-R57rM1Es>+ke+H^(YsQs%-7|CE9|kNor_CZ4ttsgvd$Lu>cW@=peBrV6589QRBo za_c4F=s2=kL`i`zxJgH={|YnLP(p8KH#FlK@!#zks9OnLu}OxBgozQgD&T5kyHgyC zQ~vj%3t9kA&Lni}ea_u+e8SJWCnmmXZfd&!MNy~CC5+H6RP4{sS-FyaV^USO_(3r= z(ilyQkq0fOz{JP&x|a$mC=I-kDF%o!`4As1w<)QxK!_9S0FoprLeA+}e`AV0ETFZa zZHpzkdt!tx9k!7M6kcYHq<=f9HJHuA{F~5cGx31sPW6SX1f_t$7$aUVlf1k<5O-m( zij#Q$c4IzCdQMIe@)NSJhf}ZM9kBca5u|WsiF@ zNfVqUuLYD19ar~{tlkOLAk~<*H@Z!& z`W&BxP=JVM$QZ=;DB;yZz3#GqPX_Y=$xCEjaYT`?)CfS$g-s7V}#kCm*U ze+pp;u;bXssC`Vy z#ifbPVHJdYXTZ6|`L~Z^e*zhbLQeIR=q1u18+}~7Xes{K?Vu{m<{p02Y*s*N;t8i? zN?&biZ!d092iq4qnb!HY?ek&KD&<%#(fb+xLL%ro_|h7I&_8QhM0MR?8&YufMV`t2 zcEJ*jk?97Jy#UY@RVQ>TTu7h4nnBY0*T(Rgeb-64{i~M~S|BDG*)VkwZ))L&K1Qy{lw@SUV6( z3+#mwT?K`ob}$6L)bR2Zs?)sa@w!6PDIZvL z5MGKZpagsE=h2S4U%evgb=aKN1Wg2kI!?b5%^rvg_XLO7NLxM?X`Z=on!GysiPrgK z!J&_57;6^(xaS|=^soK}Ig74$p}d*)H#Yj~TmR`kEUgvtzcJ9i{gaFqh>){C9{$Px z|MnJ8E#tTRPcQyA3+se{tPvS*Q6TQceh{w zET*5lf7!zS>zde~1K0UfCjMW4eY{VQGbCew9=+e<@Ly6n!5zdKIS2x}|NW=`@xSiI zxF>&8+F%tF7V1=*IEpm}kS7~-6VlPq1-G?bRIWDk&sHlI1I-e5V0*jKFBB9~rBX7^ zW8S_{WHzReQ$EMqm?Q#w1g`EkSWw*fn`R>LPCc{Ot3UT`8tJ%D6CLyB2}NU@CONNE z>fZLp@fvfkZ}joztqm)uTma!g*B}Mh#qt~-=j2d`scBq{C1S;LBG{{Cv%2JCRn>69 z1_J{FSL`E!*7`vH;Qq#H;rj~DnQT)-AceY;lV$uO+zPXWaM-;wtumher%wiCzn24P zC1IMFe@4MgVm|OI*z)i!KJU`n*qbkIYZC(f8b4rzyG{!hX)W<=dOSO! z*IOr)qm44NbiT!ZcDB>c-4&-koF1_q87#*C*yL-SEpFALwpU{H-|kh)E!ISQWO5 z^vgAT3II|y8!wxVDNNmNDEH;H@$BSlGXwe6M&u-nN;4HK!{qrbG{cq_k4_aFm;@9# zt7I^D0t-`s2Ry!}_Opj~1x+idvcEGNI+Pl8tDM6pqs1S#iVGfc-gKdqlbOb^_h1e2=$1~$r0LCb&N2Qq-nn0yY#cfBZV%`09+ICxTa=6kGN zR05UCpnNWqAcp{X8!WO2VSe@ZB1bV9AAcI%ibozRuFnt|nS3zRt>Llw!7|KG=Uba{ zf#IZKb9;O6W!)b2Mk(?XdGW233;5jIXSjd<5}e$}r#@HB$@_PnMV~Lu^J>n$I*Em0 z9|@jg>@ydc41K$z?Sd6S0TvuU{POPUH@HF2A_wE1@$sRg%*VDJve7f>%g)fgeHh6r zZF+NCb7yusBLKBEqGwjVIoF!32RFI7rD;2hy>U(pKoH(4IIM9LN1r;Itnh+Yll01Y z=D|*D?WL=;dYax+DW)`+NQNxDG&V)z6=O}EHy2C(|6**Mpf| zWWKh@Y@ZEV|WutS9RI_=d+RqE9>vS5L}_X;s1l53xBazw-IU$~C_Sh3|Q zgq6=y?bW`Fm%BWICuDnHXAl{xWB%F1SZGX%Vg zkqV9~^&htF&r3>;yPgE_E?u9kX_!KM7UXU2XklHPbd`)f2rg|rlqT6>N-7yC;gfU~ zW@{98W!q5&H5=hM(I&d~+~n-DS-H-f5uu??7R~|T8bc;VleyC@8ddd0R}Mo~maFuT ze&S96_>&&t%ildVBcP>7gdvTQDmNEbzab3^n@KA+-nAA0QBTLF*M4ErvhM+MiBAhw zeKr8z(MGuFWg%{3GuSg~!`tELm@_NDy_;UA)30vJI##~f7vhC9jbqcxsak`bUoG9K zc^|zVg|h$)Twkj8Ku=&p7Mbd(zxt8bpm5lAdehuQNWF8u-nw2b%a}-OGSN z4mbtlDI|=zHG5jgYd#J8vwciFu%z`0q@Lb>00*1ztPwBKfP#k1?X31?#l`!77D$_ zIiw!#w7D%dZg4-{jkgOoF5C#mKvi+6xsHUft|UoI#>PE}jkKz=d%v#1YrPD!^B&KK z9c+5V40GifAK=7WuAaASl{{P-Gj?{F`9XsTP?qt}L_|R<_#1E1LAh+Bc;c!owC(NR z_Xh7aX-l_$Aa&)rm06fH;;^<6fZF8y?wt@jT6(Fbd2V1+V5jxjSd$wbOCe`}Utf#! z*08CK3yOkkHVe!o!j4dhURzzAoTZ`Q{Gw8cqM)u5`QWQ^M!^qs@2@v~?vt=QAeD~h z6zq67?{wHc6^P}37|<||B6@y>^F(k%p5VsxKc4VxXTxk+?F&&U?|t-D9%o@?#5~{r z-B^YpSDSfeUeY1{E!CY;N1>Z2Kiazlh$BAq=(^ zrrp`5NLp+;yj;=MvwXjwG9)XfOo@MI9goxrHB!)a@mCl7#HQ-&p*X%h78+vWumb1h z3QiQj$Dl98ui^fFeI#$pT_oyERzu=hvz)SpJKV5C?wlH)vV~YXnooPn=jrk!Jye*> ze>n}>Pm@Cvs2VYihu@5FD0*KucU;^kl~A=xp0~7UXrQv(vk+;upI*1BQX?Yr=2|ar zB;T5Sy2x8T8$P54T4WziP7qCc-pC0LxwD2hbbB^|$3$@KO}8uGR{f9raOz8h6-!aA z?X^W7DS4ejeHF>#%siUbK}f&5jZNuYxmY)e138@mz}*4yqwR!WsJh{B>>eYyW?roT z-n>=Os?XHEkIyL_;pCOjlYZTnF4qXo6Y4NrN#RlKv*>v9R>}SHRy}fkx z!{)|*YvV~Z6oDvTuqn;m}ALPZqrGQA>eV~ zm^Q0+=>xoG&R2Y*x$zLrvE5Qr)=^{)S1)ZIu^ZL3LCoQ5l@@7fdv)jixC@oZlWHV4 zdy1TzuC&9P<)%V1S2NLnH-JLt~B!9HR1l3i~OmS39M-q0*N>`=V3o~O>Mp?3gl#jv8DF41f(*0 zxoR>jGeg5XR9-ikjO@eB5B3HGiY-er{JP=7Fy8n&1uR10@1Y;1g19s&m}qXgW*eCY z$tf2v_HKm|O^%c{wlrMx_jzJ=iUn{9w?a+*-Vm{r&iN7mVV%Rev?0^iCTGgFJ+ni0 zFU7-V)MZ>}P-i;U`lB^#p&Tz&>;oka2)ksoY2s6L1c|COTcONk^RJiEttk;|1U-r# zv_){O;elYpaNCE4aBE1rApYpxIxl{z%Ec0^>Mx3$HrBreU303Re1q*6)@JuCB1SSQ$#eF5T{z?@gI9y8%Y9500PwqG7uk7`cUiZDy0obC<+*JAX`)#a z5p+xjB#=U`c?mFh;9R;$x7{+Ngp+R&Kk={|)(cftTvm_U>Z+-sxjT9)pO9$fP*NJ_ zdu$?R)h@e!dO68Fi|}K~|W6W!zB0>1@?>NvIC?Sf8lY& zTEC)TQ?X5$b+S2}@7UYHgSNKE3usbT33RO>8id~in>Oj@*5D1BouJrQrhPbH_l2VFge0Xj0ZwL-nRqi*w&JP) zT``Kd-6@%R^N7H-Q0Qc3L2?wxSFZL{WUvCJ!Z0y zAsOGHgLF|y*DM|29YyNkv?rHOrZQT!O1ceP3^r5jZ?Q;aB#%|Rw$-d1PItJK$}A-y z5H%k{Ao>C$R(Q69_Z;@6iWu zJ;?3m2tDs=I(4WNz*pP|I1Qfa)gPyH9$s(T`pS%cgAUZT7Y$e#|p0z&R2wf>RA!4}8XH_G5WOPS8Y>#p%pglT`9d zd$;Z_+)cipf~3{yj=gK*4)0vP$dcWHM?Un4eJ>>2LR+L&LG|3zbP?3<=g>gyQ^&d( zT?n;^5??&JAp89L*4W6|JnNoB+liW&PqU=po&oC_@!!;ZTZw(t7O{9 z3U!bUjlm#dZ!#sW$EL9TbwVU@#_fz;WuE0nM4jW|Q!E2LKi2p1u8my&$i^u}wPfwq z(PteZPBUTlZ8o}jOeJmBLNMN0?X5qg=WMZDS$5kVcJplmnw?m?VMCtKXdb|h?3=}0 zIsnHo0B6RM7||PvufYVQ;zmvhW=zw*cIdw1TY~Yfu89X1}qK*gZ5vr!tnW7rO`o1A*KX6U zI;UYz6Wc#!yQXhFfqw@th9a#Rf0_^T8N>aVsT((2a~7NOb72NdjdoxvYqN>q(iL)o z^=>$|C4y@9qDqBRgq^QK6Ps(2$*9TJbBMdut z$et6LA`M%Ye)go9FFX)wDb^kQqLpDi;-FmCxrbjzwOBU3Y54{xWqiD<-MUv{(#cEG?5-l@_Vq7yOUlWsGafA$2QL2*iDzv!x*UEuZ0`4H z{TERm;pbg-8!@E7Zj<1a3fs^SD#_WIm*Tfls>XgLQ2p4!QSPK`4otDcU>J;UU!bbq$ikr2gdYnmL{ZaGP#1o{``tk=)+Q=ag`Y*Mvie8ma%$7` z)LsT`kDf3#Q$StF&sXsLQL`#N7=n_lIzSE}f28z2I~duLlSt^HXY*xKEmLSoD+NwFmPYa6ih2f!Ns1T*n2R&fn{U>{0z1%U+UTrt=FQUZBDV}G z-h124jr_s4xxqEzMd4zNBo8zB!gChq&@KUvYqh8afK!Yap-U|8NXyO^f9U*IA{XK6}7yHQJ$oB`chep030>R+Xh~V_GrEX>axw zCjfPscU{Y<)xG-wEu0cWm zDq07}v9$)Z#582A?rPrY1f%LtjpRQQtK1@BU%CuJWWqQJlKYcf23?8e#GkGhKkr35 zig4JciSp#j{PQN5?}rgyL;}GqG|t+^VT!pLcdJY0>3EG{u`w17ng)nu%J1x#SZ}g{oh12`AGtlX!1 zKM^(8ORZxfW%l0xh{hbZewrP9PGj1(?1Q6r)rXt=zHOgmxWpq}Xae}>%(E!!4^dfc zdD3qV?`l72tk7UYuIKLsaFz(Pne0f#?wkBb6pnKw$a1=4gBH7njpPpYYE@%xQ5>pL zOOB~}q;kv7`26diitA@a9T%6X2OV-@+MC1LTv=msTtcW_3~a5lC*hjp6cDU2upi1e z?v*OQ8@A|oMrcPvU*kk{_&lFai*4LZO1d-lW{tdjKfqClmz%>t3;sZa{D(uv1KsnF z_(rA1qR*s0Um)NqS{90?9a^WMC%^=q$1)kEw23DtmZUvldy)^rz zLWL2F{(XgCfm@qe%OU{|!spfn8uN*^*okgVU3Ke)_YA^3RDOd>e%pX zeOSH=kJx%kmKlM%ryOrwQW&z>gdvd60R|U+#+#cKM@pX?=-7IoU;PxtOVYj^C@s8w z8u&g>(@9^NBihOk<5!Zs)TOR6^>BG>OvU8ZXAM_MEE0pFPqdsF_{^GY@ZEm!sE^hlXV?c#*B_(A|q~ddva^?Z8k9Bj#_km(s6|Mo(mp)B~Zi z>Gm~lRFzdnsQb4DIuq?(e{`G;iJvf=OoZ~j%xEP>Mr+#`yS1}`aMxt(01c?x9@KXOz+gM~g;#d!thqfGfxG4{(FO;}t>~+@7 z8?Qhe5PSCSb&DKsMsq!yn5BHvzqec%4b<+#jdX@8sb&JV1zlut`1q7HEo_(`m zwFqG7_Uodc;=Ipe5fja0pkxnp9QiVv{pzSBijlGyPL2Jh-Oi{j<*ud9HGju3va4W} zjeZi8@`wxbs2hJp5rJ9YlHE73@IrVG-?lov&8^B!Bj4k?SS4Q(eZ`%xy?Rp`6&>6r z{9ahLBB#eyv4=lbr5Xv&9owGX(j(9%jwh_==BF!*N{e%9%9q<6m*)Q{Lg7eHiPCp% zOx)Q&pMLSisd@GpYvD|`jY}&&@_kRr86ycThb#gBwN=$G;neg#)^L)Y4X zRoQ$T4wTfynljQAL7KV1nO(BNYiE1wYO0C-;dh2*quKAd?z)kMX>o6w91ExSWkhc2 zVr$uR*3Yz;PiqnqIHf4hyXA~XYJGA!j0x#@YvM@ZGVn#pOKFH&;*&mh1ULD8v(Z$3 z+~s|dTNddwXT(cIDvx*;STj!pZ_-y_CCf!E7w>6hujaRQ4WNh?$7Bv*Gz*q`dU~zh zsqs%;4|9~tWfgLis2JMk>Dj!cHLo%6Djvk_M6_}2KZSi#p0w6Vk@MZ$h+Tbs+R&aH z4`lVM9X#U!T6jZz(Jjtljd5>~)+PQWPu-TPx&!*MmB@s8JFROEP)bQH{QC=5Y+?v! z>t#JniB+tJ+>n%ilQ zh;kdZu6x2}Uw^Og!}r@v!coz2U&1F zC{OQ71-2Xv79QGG=Uncw#H%%2w2|hV|EXbniR=Q`>;}ku%_)1{AJ_@j?{1vqMGhR~ zIkug75-T%8$Mj1jfK6fNVOX83D(w~x5|;E0wx-cJxX!*UuV-!*-5@!i49l(Aox5Q_ zHF&K}!m-;alXP(&_4*smDQ#8VacyIhp|^>TOGzkmFac2519CjU$+v#Y5zd)395}OwpnuOjTP8uFCLx8n(tB5 zdU}~RC6NE%1(;^&)T?}%@2!ex5S^4KhVf1{?&Ifsid z|JX4+K~iMnrcD1hqPnmeCN zDq0aYQK~%;Emy?Bu_s@wDi}KHGs;aq-)%Zn+CmAT`&v6 zJdY$+$*K_5mY|EuS6&f>Ir}TNkH5(&pYe&l^>|S|C%2Vz>Pj+g&h+kbnXLnKeYa(2 znrsXfu^Zo_hvkH2?U~{0>u+SSyKn`FsJ+XD{F_Kq)wK4$5}*_vHb+c!wN^Ey;Dv1U zT&lKXPfV*03?qLnJ!Df|OWjlhT=m6wrVl59P2T9ftn+LELypxWA-y7m@|P!)a;`f} zGoMWhAIv^0FSn7~z{YcnW^JgxWUQCX_IUmSj}+ZNacn?fJ!h#4Aqx5V(@en!Q}MjP zeuE|>ug%ZA()Q{-+huMXs>-pRl4CQHwem9c@t<{QJ|N7iHYI< zd;stAM~2)QjomnxfQ1Uwfn|LcO-tpW<5=;oI%mgq`6Yx8l!k<)4>s|c*=Qzwwf%#F zsX=-_qvB}e%qoWCa&cPmY^a>neex{#`D}DnH$v0C9mNhBeXHTqqE7lFnJ)`J@Chsk zH%8X=%=OgJs0a^bltwmZH}(ZItjF(*@qS@oh&@CqsLB3(bLr5(p`qa*`cZsqOUt9> zkx_P|xdPh{+_z~O_xUT;Su}>W#>|c~k8gEzyGO@dhL|uK>*sh$W|yTQ!UO*>=knUx z6jCxWT5oPrO$Dne3Kj-t@m3rhx|BSb7+Y1DH449dk%NgYm$%@(xbl)J&GB zjA$d;m@Erp9=w_C8ovXZxD4AYrqUp4$*6br-9{)A@<3J93MuPN16~6SXK8`{qS7(W zRyB^Lp-huu9E!5m8tdVq9Ae@du0P(0z1X52SkM~vvbBg*r=y`!Ehu<$*s#)^rujyb z^ZULh0WyPZeKK53YMmSNVBhwto-f>peCD~cI zRWCLNoVnnf&P0Do3~~2PRepnJ!Ia}{vFIaruUl1(7Wd2~P5fhq4hfZK3aaAOKf*w| zuV3A@aG`j536UuLy@G=K#@T3+Xwv9m1eh8^t0}eHVp3YHu9s~oPR?s-dCU z5X`(=wj+FYqnJjT89PZHs&mLqWo0L-)PAUW>5SNLrm3DAG1ZN6k=-28V;Qsx91$84j^(kztzZwAyItfgqo#I; zr_d%FNh~$lX>!P{aYsr#afAKryVcdzlt-ex54pIcZLBH3u>9PyC%4h^N*)Ub{NUASBYlL$#<@bMUQ})L}2Mx1$@g`^rq{LdN1Wi_%Qd5Yl!%*sIn1nuuRV z$z-VQXhx|T@oUb{0pzi~tQ#tzD3#Tzo|g2@crnKRVehSj;%KA2L4p%3fdqF5ga88s zf=hq|fb;K72syA1B`Fu1!r18nokz2Duc{r2AdYqx5vrYJ&arlQ7*M*nA1s@tZ;4VdF;^b_>O`ZgXWu z(ucD8jS%ZC^e%(~l|l``EFMJe9nfxRe{bBI9z-o8M|B+=XXd64NUbZ8BbJ1jv5bx&n}og{_~sOv zNYu@sD@3J`(tPD|zd2jPds{G8sQ&G+=~ASt?+mCAM>ajG&j$JN1?quu)GPsD4)bk$ z!@7c7VN#?@M}nmo<5Z>cje!iea^uNn8tg&7jT~As*fGiP@~Px{^%m4pOI6o5>*wOJ zDsxS$OEP-Ayu6cCIfS|-`%Y(B)pZYp)P7u6wysm9VrX5>uY$yA%giQq%e9Lg+Lwd6 zbJz_AEJqK1<^vN73wEzLOzvKm*qraYdjfOxTX>wQ&T)lX=q_HN{MZuUZffGgUJiTxpH^6;J4*=40 z$a?Y6Fdn*n$FYp^eN)?TrJY9-$m3p;@y$t6ovxqx;GRoD0}q^d>Uq0+qa3*xzcT#P z)o71rthUT79?b)aD*Q-wKe8s=i|!IJtlW^a6KOua^YS5vYpcRrHLWkNX#)jWOcGLy zZX!{cv>lhPPmbGNUQO@moNJo^RqPr7a`I3(pEmCn$WcEejhXcS8iLvuwV96D?Zr^I zZ3*%24Fj}-g~WE;NE?|1&n`wW^tfu1+Klp&7(4#%?bT;*ix z&Lv@FDJ#A2l@kZV|I?b|lptsgiRiV1t zOt;m~W(I85HwTUFL&5Z}=1xY1o-eM5v~0Wx5+ww+e;iTa&t;ZtOvE;6F_twmYr6Vk zH18;{c{uKp{Unu5okrl0qw8}aOnLULuG#;Rp|{NF-~oM*_ZI8=zMge0AWgncnVxN> zOq%tVNeWn|DgLZ<&h>7hXnjmQNQXljd)DmzL;1QNh7Bs*^XgvHeg(s;pp7#}0_k&> zdX&3*EDDgU(vzs#P&HoK`PFA6g_XVBS-S{Rp|MH)QxF zjg9d?jr(&5`&_g!k*29b^OZA6@m+J&F2fsN3bP!*%rlO)5 zi|{s-V_HUBRm(YT7cXSKdb;)Dv*MkA{Ww$6l<>(nvGw&4*0@v=d^1h?QR0adPdN4U zTDnW2kheQd`1Apg`H{|35*WryV-JtNmz4#iVxPIG5}@fPr?y*}Fhy)XGjMHoV(&wZn#l}9VozgvzJ=BpF5z?Iuy z6{?kfZa{K(`1jmks1pJzT&IhPq6?rZz4ITE}*TuQ$_-Rz@;$iyZKRv!F zlNSgF-)l1m(B}+TUE>@^O{gGUNl7Ci?Kgx6cijTRtWeE+UP=HF@wFKuqfVCozd%GbFS2){_6{W`8@U4eRsk`F^2^2^nLLf_S^U zcvPW*XYA~1*iyyJc)T@6fsX7?)H0P@SpRs~Br=|;;r?81-06Kg|0}cdn*W5D{#k*M zb)c4Ty2_+K+wH{m#+4V6s#x`og7Kt81I+6r0d*oO>a*s17;Cujb-|p_mGRUMb5f(j zM(1j<7VZfw&5GvmqPN>KyptWOsR{w>|u3^()f`VI{N83$Xo5 zh}*#Ai$3RoB6|~j$%@oM<{UJ3gdP|hC%xtJuZv#KVNOk(VTjj&euiv6bEf*o6*pDz|kv&!9= zmsJ)dj&*ogO)1eg+y=^|u!|WF!u#O;WwY1&1I%lD5prm&R*e%eBDc}Mi%4^=fcPYy zFD&!GvJFMUrWt+lh_~bZIp>#()lU+-lkXy)edH_}-fp0{UDPh0^kk=apkmHC?aZVH z?bZ0XvDtmP2yp_)cd^NT+XDfnMnDHEYa=l67X|&v+6QJ0?Mi5TtyU5x*HtJiCyib+ zao>bS@e`0Lx;aA)#Uqmd@l%-et1S^Xx7t7Cu{WzJju`cBS*z;&hIdheh7&CzzmoWE zk{>`ro3Z`fGTJya!cgm9i6%j9iMR}4yfY2qI{{@1w>`#MzHZJVKx)E#=L3w3!aqYR<{Lst9ipwpnjg0yQ2el;(Ue0lqEt*G#W9A~(u z7tAQ`)x?K;P=2(g?ECReW8=KQtm6J3D}4FNT@4`-Aod#6EC@k zwXvZB_2}Qe?iKTSE$TN)Iy(9&+Xb3XogV`Tna)6WCWx zYTR88N{MLkn}d0g%TNvWFp!(1teo;qAiv$$L%rq61g2*2T7~UY6+^F8bcoocZc;q5 z421ZGH-O@#1Wbl|1mjs9inm`x3cmriXdbIs$B{dd-)q14Tn@ITi_5AUl^%k}+(xFI z$5^U+@{Na+7o@YlsHsu$A6BHFuT}^r!@gdVTUkP;rxs;m{BZf2f98t?IVU3&sI9N6 zMW9-SY#ip)A9yK9i1-bWQ83t6=iy zW(d0)IRtbn)VY5Lv8f-ELA$R7X5=|DP9+(4^+q!dymhrf;cqWLtlgAwU8rw#yNORT zR!NVYcMhs-{8RR}U^NK`45ZW915aRq*bqr`^L|bQkx0}s?3$&g78UjpZKAX15L{tv zW9O%ikn8BFg7_)Xy6#TD3Z(nUzG4mtm1DAOnhEYEab;@{ZDg0ZTk+k{fY7=Zz=#@O zlq`~$k0{0=4GK?{v&JlzegZ=TUM^w(!+_BhH0zRnkmJBF*n@|5_Xtn)9zr8|;#})wbY*DFIN2|OvsnOqwm9rT(vVOorb&3JS7dRb_sIrH8nUk7 zjl8x_LJCYiXWjMRdMp13=Vk< zwlLJ%8Wdf#)X61km*A?dMkQw0%Z2Nm+ecPSUifwt;?S6qJru{d2{0G(n!UFfVdO9m zNXX3X?E$1aK|iOIcrXRpoR@MLVtPN-;MLt&RPK38NjfwHR2V-`2cchLirFlNx!x*^ zr`piX(Ev~f$p9cF(&1?B0L%t}UW5{m5%GB+F5FV$TqSMk#sCxI)cpkOo#vTMk0wk~ zcfE*y|`F|v9g^!5PE=+#?ulheyU2jqs($kau+l+-+O>yMNn+hAmn+=e7 z2cgyEyx`=U;NKqiuc%!Hx6udtifZHlgff{q4my0CP^Xw(T+G_Y=J}cNMe|HkcEdxi%Vnw(QD*diZztD@yj zo4Q{qlyq?!lLR44&ZdEnU#TXJ>=-?HRd;%Tru7hEyWN4*|7k-uy%s=CfM~^#I{J^S z9l^){MGl+YH6QJy#-eLz4KRW4DSQIifF;8G%<{4C&E4zwPb^dG%|!zHbW9fPh9etFo)bzeGc^#4qkeeeN6rXn)#gz7$QiUd*Y&Z<~< zJBbN0s4R|%p9it1t!a=aJzwDOctd$8Kzb{r0ZkWgFf}eDwT<7hJi?Zpg}gKG&(C@| zNbr_^WRr=@{_VdpD(C$Qee57cN&gX6_W6Q0{5+Vi_fj37)1h-ayIaTO00g_369>E- zzA8%DyUFnV%dGN!L5{i!ub4NtG_#DJt&(?Zt(b$+K-NqPsL3usL;21s4~LDoGPa|b z$?FK4thTt#JI`9tr%^B+uN!gKfz;z{ZS|p(MX&pT=k^rvzdYCeky@;-aeUN<`;**d zYn`XD)(f7eoL?B9qC+I~xkBvSoFm|GF?^)en$*Ka_{}5^2z9|mvhm>SOhWg=lLpG; z6;qB|wJpfVs4%LQ-2}vNNYI)4a{D#!R`XpZUTOCB z)HuDGmrO|#2=eblzL6-nYNezj{m4&;q2bm^qH4JUoGKmEpcH=Z`lgxmAE^G(R}rbU z1ZZ);;f!xP>?zuxDF)Spf=yHvW5aG2Jt%u&Hc+OA1TJMSNi-|0n?v%G4B}5?hapO` z^%lZ2UxTehVA1ZfRyII|%;~m1VK-U8d|nBqnJLOCmRq>3#O6ENXgOKWuvz`8o42VcMUsS8>R)&Zw;>j=uDFHEcKR*s&i5dNShvi0II1y zd3*@noK8g2N%y+*#+vyT@-=%E0rWIs+I*)!`opL9uVQB!5g5uou=dvFz|uoWs>%6a zZXj>!WF0#ZgYl>0kEg*IfSxWB(9n<0IVQh*C#)Q;OkZOdU0JXwz%e!i2+_G}o}!?j zjMiV`i~_KR*?e7Q?Zz8?tqKLl&UC%8V{V?d*D2FkvZNL}CtOw0=KZ!v50X>ymQ0!PFxBE zrpyF6#QXE9uvG6g1#FL*8dXL+*q0eypG22g&e#J6A^r3lnY|wy>+5|0m|NB4z@k~j z`NUK`S%3hfVYr^gaZV>SdhfSi1)JOvRv^5iVAU?`-+>O_KPNxRMC!p~?*KN%{QDBM zb^L7)VqiNYVa^2rTWzO^wEMM0h;C*U)OvsR^XZ{^!1Wy;O9p~QHXopClyfWl+wbY) z_~KK{g`L46_J4S%sX72#q*N)Xl+y|*^$F)}>26X*)Vpa@O~H#Q>dWrF;)V?`lEX0t z??ZW#1;@O9Tjny-Mlh52=Tdk_ztw&H=D*Gqpv!$<2AF>P5@)6{ZU6F8|80wNMSPTq zf5t5iBPU$=$jfJie085OP?+`SA92>d>-&HGX9OK!k9^e=`X}z6pGN#fDG`QQH6SdebduVm{4e3>AC&7q{g+g;z#sdl8Mpf{T&sck+w0(?{UhiD zu>F2~%-VG_kLeFDAnPdu-`%8km2r4Ip5;(uR@VP|TK}h01uW^rfaWz%c40-y{;y5t z6DDA@?5SG=26fPSZTr{hY!1#BoG|M)(WUi%)c>psSm5Px_-uu@XPr1^3Zxmf=W^2v z)azA@2NPHTsf?!3%@qe?@#o76?mA;n2#IVr%D0!p>P%r;S#Uiwa zm+X5GmQ*C&r_aw7M4q6%Aa|G?jkjBfw>$VEJHP0Wzu=L-0lnUon+Pe+S>Tfb!ATTk07O@22gy`^oC?p{&bUi~UBG%UP2{uPf&XUW@%om2COVgX|RgCwNqU zy5cs9fB1ST?~SMyAdeLIKRoX7eNsPQC?Mp2zz=Krk8}C^jQ%@c zUkf~Ws`pX|n+WlrkMZ9*=%3&5!F%=oNdcZL4{GB7_yd1RJwkr%t$f9)hyFjkR>1E| z{X#$~k>Xq?viyH=)~)!d2rA2C-qintkNW@Lk@J9D+cVG@8`K$?aGG`BmRo3ByD%=F#$21&hG$16z=a892=d#_;~ z)^c6fPcRIrn;T+L!Y+423x~>-B<~9`O)7{@`At`zmoVOD&WU%WD{1Kh4FgmzS%(=y z%zqSW)%-fnvDmrD^!2guKCVDxtyh3ApTM4DKwMxCQsjl{&+MzY6&ia}?H zGSJA#{NdhfV$H&SK_2y(e>{R(?2K}y#OwBAO`tnU8Z`H?Dts@d?K1DAY=(eADAJNg1!7C9TByIH zM`@&af@e$DeAEA2&!Hmu#V_9hv%DwZ z*QYNCgn*ly*L1al`OajpeuKg1QYlMj#CH+sRgjq==n-?DrQasL;{v@+YU=8&07|d* z7jXdyQ>)>^MPrx*DegOrl@6OY`=}2;5ikt#awN}5H+4A1xc4O4!-ZR0wWNlE({OdM z^j~Lk)+@C2dfKOD39OQf1N)!8;nnYdHj*C^%_Hd&g8C@~#MBlOd6f$<-LH=HowgJ< z4tDSvKn*?fSf4P2w}5%_&UW*4=R3fgOHDg*ZywYi3LUByIq%3rZlbC!t|}4@gkVF$ z4-Mw9s=^!Ke?4ukq8{HPlA-yA*)3~gdj$|*7lu&^7rjGL&V~}^>2}PCZW926DPMb+CdGf+{ zU%Y^U-2v#LidV$Hg0)AL0i&$z*MdaF{we=S+C{^h;t7r{n|_OmGXZqUD@_oehW# z@wZ24@+2ALgGmSL-|-IkStaZ5oVQtj2hhi_w))RZhf`Yuo-1}n0-vB0U(ZWrH7f3T z!+Z1+Wyxv2e zZ*VKW*+`7${l0W7jjQ1o{s*=JD0|*ku&U6_Ap)DPV3P#W zjqruWTi1G<#9Ux->24RWg(}AW3TR(ke6S+>|+v$M3C;iUXPb>&WdAQ8G zXA{s4zgZ7*^F#Rt(yrT~fc9lDL2w;vECm)o-5)FXAOB3?qGgN!@5BLt%d^pu- zq3Cc-2dtKCKh_{}?~Bo=iG+%E);dc?J776B`AP`y0O$ssv(V^~3yfa3u=348x;@)2 z?0W!q%?Vs#cxsi+B7Ms438!KAv!vLPC<>=FfU){~c*k93=f}_GCJztSpzOU=ECM+Z z_Imtw@1RUh_>vNTj#T5#G&@x1w7UbZ(r*j=0{ag4b1rn)1i6pky4eG^X<`Y+_y(;L zt-cr|oq0&`GTbn>MFLbEnABvaU=`}`5F{GTPIekxV)I&+$ zjo#H;UXP}94MRTY<^WBh_+xGr#21IShL+Uw`#_`_eGI*(_HCAJ^E!VJ*s9k*919b)rvg8A1h~z6xM?Pm`cOC#EjHNP_;ym<+tm4{OrY<^xeL2hN7cY8Z199e}URj7n zl??xe7vAZ6yDl+5G~AI>RVG#N>cHKXgqD%Z92Y*v*Q)$32TuhSKyALq%~^{Fv)MoR zbcrf6jp{f4?F9fny@c4G&#VM4HY+<;+jYttFIn{_nCTu2yiGof8+U|}L9WL5&B2ju zt0dJn+m{@w6dZT0jl!VoI9Tv!!r72CtO%hojH{&U^Mj60BA(uvJnDE+d z6u!^^FuGqC>62C_F;-$9cI4ANgHW8@09|WC<#*Wyz77hAZG)Z2#H{jkTx!7{O;XUs zUJFTAC)TZ}!tckb>i2f50lPhV4jI^r{_)=*UltF#-V^$6NDx5 z*nNJJya>M4&N)&9hQT=sbdYP=wWIdWl$(eJ7+N>EAipm2PJ_tkj zXH^c*jkTw!4!DA6sX_!a=g0Egz*Bu5)(QXQbumNpkl(yWgf0j_YJ0O4*EN#f<+^=q z9{4_)htqP})@e1Ad#5E^p#Ekjzw%idATI%hBNkUV%hEqH#ggWLop9JLQ3b#;^by9W zSP!O4#WpiD#t`wJOlfn=Vfrk6d-j0*7MV*yT%YazT&+V8F{jN+ZE^yi6L6xgqF&eQ zvAHf>W{HLit-ho?en6ylM+^&^pbZ!JQ%ObTjnrVErB6k5J*jEqE=ok0e|u2RdQNdz zqQGUl^!|KQI)`>>MX`6fU6vWPAvhhbMEL_UQzSXAz1kTXtXzE6rWCcqQ4Wq1a4Qo! z5nl=Z4vb6=%O%ut6^Ts)tlCtCUM)1VJF8PV9s~FxH#!BGMIbkv^QWzT2JSL`&M!1* zb==mCPVHvBeml1@JKWR$lI7xZqX)vx#d_Utk#sNRW z|A=lL^bKOSvsPn7EFnD)dAO_FfBs;N0Qsfm2m-BOm$ zAhnFAcxv@)k>JUZK8Hn(7aA z&STb98y(CpOkXhdSID^wkY+DZF|Yn&DP4uu^&_QW&GxT{Pzo==WTx5^H)rD5!Qxj` zR6LzyMS%^KMt#PR2r&rk45oei#iKFQQgIVAebUDGfk8E8fF7QP5WMtpu1R@x-&?&i ziXtk(I&l@3qj7bOK@ybMl(}tEBtU;3r^*2eD8O93Sk~?;6|oD&52V|%ltfFs7?W{H zY}lB+keXgM4@7gQf?jp7o^{f=g*y@UD*;|97Ta?BxikMIs+(U7a$npnXcoV5o2go2 zb!olkiv5ldBYXRMBj=Jw z0Y4^PB7AZR$YVdtOWoY1aBhnWnpHUM5)_YJ1u)b?6@kz$dibGZX&Iy>b8nu#%YT;_mQaTR_l*;Hed7OFPXPQNDMB{qvfg@Tw- z9r~z9sOa86{thGkawh}101OGPn9(L<^uJ_p)!;Tja^Vg<*Y9Xew@EmRD+mzkx3dz* zb??hYG_?fGG%y-dIL#snnp&6eLL#_J`bf8ZUG?7LCFqoy5&Er8>j!qI$`lzazi1WK z3tH?pXmmdx4bGPrx;E5k^42yD^%*9n&!_r;M2#rZY@qkfa#|rt_((g#u9WFuU5X>| z#7`Ua2~&oOmeL0enQMuVlf&thAfEkOBKzt~j6f+GmJ;;Rp0Sbu5kB}4<|;LLyB^Jq zu62o6BcMFuqwIF6GE4m6Nrz?sk^Gze$#M|o4y|m23Dur&kAZCfK|y{I$@+vJW@QWw znV>HQHr5Xhcf?pA-#ZPXyB7V;q}CLwNtuQCd%d&rKC*aDi_W+zJxy0&hJ5aS@%eY&H_wpM-kt(>OAMmsT@)+3_+ zrV?q_*!!3#JSW1vi1c>^%yBm2B8v<^o4^uQb25B~1}%8oJ`F*ufz8P0df^oFm27Y5 z8zMl#1YSG1P48mqW|Nwmkp*PRC_-0NewT$@901kcWNYV^gx<;XeQj8km6I@{6Du-= z8lBJ>e1Do^QJ9v~hD#yvtBSGt)zF;#Zed*tx>f#bLLdV@-yS9qTVgWTWn`Wc8M5jP zSWM!9cNB#}L6#l5I+Ce%=&5o(K5RlERvskP;UEKG>N@Mhs#Qqv2L${?Vi^MtW48Uo z%c^6J`I1~hnOn&@%JYhED@39p>?JK`U12hz`enzAuwy?w0%oBwGPeM015mwX34`ff zB{%>@#IlM946nI48(ZSiokOp$V{*h*9^fNedWRZ`tCTPBEK;D+&dy`R>00DuDo6c4pn;hkW z6B#MNRF3|qyg^it*bDp%s}qvF+KR>!O5}j-A{_MLF^d2f&@9l!?l;@nviYS4)Mwr;=*T8io&h~7 z>|4J?PhWnah8}m4no0QRM?6WR+#B`t+H=MWr6$sQzxx7}3@q}zMsSM(TVdn<2l$z8 zhb)0fuJ>l(R)IPRpvbtVc1ckFhoa#h^hj`z}&4|7M8J+ zj&wFAJnl1XWei$Kwftl4lZxUTD1!MrzDq$uRP7vatxP$kF?t)3a>q89Xi_GS!7qXI zLvozyU8WGnwdh2BZ4~zTa+MWq{1AM}&qDW@LcmkzxgAW zBIM<+0X0}`YNUABaIaooPgWWHNR6m=ysEcoe0}}#I>}Iwq^mH;h&aJC)$W-Ft#N-` zno6+{#PJ1Zu;{1d7BtFHMBh(L<@eE7VsR9A6d7qy-StS5KwQc+Zp!yHlp3F9LnBNX zs9&S*t4Su-xV`Cq$brk&+ub?oTio<{Jk~yUx456kX`x2a(FigBKK6MA?VyoW@&_V3 zEXyt|eYoYSNgZLe4%>dpV2U@ z-v&E#$X7p<8~E0yfe?}vFc9^0bmvyag~m6ytMT*xTQ)} zR#(sEKJV?T*)N~wD7HJvn+{HtlN`rbseaJV)^FMHKwlEUC*UlKcX&FSg)(r)lljDg z{S%=>GEPD-pYz_;80uZ){p%x_=lD6=?fM48kI2+tuagf5Bxy3sOvkA8Z}our7kT{t zCuFL7lL7Jkm+8NbqQOUF!ep#y*dzePF-{IX)gJd?wic~n<1aysT92OoRM$u?>}n~|=B+ZmL2Q6P7fw_TYSb1Rk-`=BNdoWE8I&7hAi8xv zr7Ml!6nRM$VvvR}?p)l*5AS@BQg0K(tj-*+q*_B-(d~n)wU;GuWL=gSZ*~21wZ*rF zMoxJhq}>h5t(Y!JGdGb1@RNq=ByOT%m%VApO?~u0efxaLsH{!lqXR>iuo|COVE_K& z*^lrChclpfEouD8>*~1nBT_fr4Ku^GwGtv&P3@W>>_(xL%u)8wLF7%F<6@N#`+T=k zN(S`0+kuC#%VxOKYF?uf0c+p+{sOU8{@FKQA90bf=?z|HAu$+rZxL@Ut@Yu2@T&Vm z*8Tk@6z%B|eUCakWY<{eM5uyJ;Lh>rIl||IpQv&0r*G|6mp)(k!H3S7djyg)H1Vu6 z7%N(+$yG>ORZl3qDlP?|*bt4_(j2b+kVQUh;NTa#dU~jRg2idm;t61GX84&mX_vTO zWax*ltX4L1GvML7#h9>+RvDL1&2-)$gZU3?IQDT9+3r_`!Re{kNO#;JBZdawtUS!e zB)Hp%oo)Dy(H(}kz*!!4yC`GU(}JDr(`Wb>mx+sT`nEjNDp3!gx-L#a-H?x6f$qmo ztt4NZ?pIql*871+hSS;_0)~q;zG1HV7dP=i{M}zv_uE)LK7f+vOegHgp)i|IWtNhZ zpuxYMJr3cTIUviA+ESY7C+z%gQY4gUk-{dYmbVJ4aWVz+d*UuY3Y-PgzqC75!t?60 zbYo`zy>a?A2Xo-au-=8d%Q)DA3$m^q=~QKP%c%c7aY}C}bN?lzt!TO#pOY}`n*Hpf zKB#sLK8pf|WiW72>I-4`N&#SxABP$0&=Jl$B{=vyOek1z%3{t@>W>^crh`);3Mn%? zj?eHPvo(JVODyl7*g!+dVGR6}u&EV#Tku3`O2HmHn_V24cMQLL=%M3*5 zXz^J{xG7xpt zSNT2zQ%S!M6NQ59DUH90}5m>=%ON+AwE0GQMwgmR_Aq#AQPk`G_~*zFldO_4@wNiiphXrE%*Gq5h(T8wI!gM>~#Q?#I1{^pyZS5P1d9 zyM!6}b-(agdTDBS`Z9IDnfc7XI#|u)jg$UwA$Jh;Xu-MvS;u~=P3wvYnDp-rD1D(l zE3ZVDcr5WE+?C{KCg@-+JN#P8;%NsGGbk_=WsDhqwt9CdJnRCLZ5Lr>DXE9TH(Tjg zX;)PBRYR?D|Ez!B0im`ip})-)vjFzwJ9sT->qVI`1i z7ERS`|N4;hj9_C2E0~OOi?G@E8PTIfqBU?@bs=8iuGg)mV}SjWw8iDv{By=+?l zWSI(7h{Iojoacu*K*3G^2teufIH+adG_(&vpvM9%%U_Vpz+^LMX*gunfff-y&MZVS zHNEX|5A~?nJG&CkNJPjC5uLA3UM45WJ*}tYwiZ}4!{$SqKQ#Wm*t&DJJ(h-#Z7c8J z*vhHbam=x8^YMs|ncNqCdp0J24#LS1=nOMrw$-x@F_@n$)J#bmVD82~CFTJ{qv{r3 zEuue|-2pWK2L<{%SGoqkaPOgE4S7x`1@ziHRf%Z7l0;OdGzwh1!y}wSxQ^P7Dl(aiW~e; zJr23wJRv<6=lf%c935U`=g{K;I!|lwYLF5dP@OkYDvnz&Z>&D9jyw6iCRr}arQSZ^ z@JEu#%;!1jps|4OLEB?B7QCo8S82g5|E(r^>Y`#)w7XR>ry;n}a;oGJ9kRR+Xq0{{ z&#)ciPk6^cqz?bNx$)BJ5GVjEh6Rut{gBTG4hKA70!mOYQXxBz^~@P-KO3DBWlVOY zy>NdL8^mfR@Vaf2Zb=GZ);?{Zg>#GPaJG_3AvnpCuv)NkJ3UxeUv)cpCWN?k?$@T> zD#JyYcH2&XrSlp&=ez!d^D20VXGf_Zk;U1;Jkvh9;|RsGjQLlItHYe7on!8pK7yuC z7B|e^X;r>al6K_cZe8fm2VkGIdfnwn?$ry@NRX!8h7VoxH3@FOdUVMq*WHEZWv+dw zZ0ic%xm!~k-b;uv6PG zAS0c4$k?^K!W^{Q@Jsi9_qw>Ys!o7Kcmf;*D>y3zTWL2A8R8CVC8xuU*)>z)Cpfth4-^)fL;BL za25JCa9-<5Y^KAGb@*vOMduO)YO5yLAgW5Tvx_pB4gzZ*c(o|iRF;eDx59EDIsf7e zJGtjJ;j;@$M>+jwt#~L2%E#J_WJw*_H;1$*aTyKkF`A#dvcJBq07iaBFGlHD0#pmv z_h`dpxlxwy!5O*ZtS57j=*wZ#A3gUu;&b7NCM87jQAyq`u)U%a7K~|p9{aU#Q#M7T z{iG8w6NSj;!eA0ZFDV%1uLh0#pKUZ*!CU8;)*Q$N1ngEm$SbKy6owKQ{0vKe#=2vH zek_{$rl^Je{VX(3))JBG+G}sVI{ItO~+|)I=HbFOd}ORIp;LT)^^aL-4q+UUGX`?zFzP%R8{M zREUc#C||8g5n29z(_gtCx+7n7_zdTHbD6Asf0EcIa*H?9u1%&R*|VB6jv$GX1pOY> zWX`)!D>}W}?Bf_9eINQML5~Ypl1BaCs2)}gd{iP;)2 z-<)-Yl5Wa+H^puca=uB!|DjC+_?P57Vt3KTdqt^`>kT|E_STca?+bB7+DljqC>xodKP|Svom?UfB34l(l1#NTyqEgMdZD{0Xy+v6%N}f<+N9ao%o! z_OM*MP#1ci9FxuHzBw2|%uzxAZcUI~Raa1B6Z@J)73-^Bst8MwT`6e?YY#qX-Q@bj z=qaR|o99QWQg)bm`?sHU1@Ac~$K>UuF{SX#o}eijM+DWo9B$mT+J&w^#fLu%Degld zG(0na8mUklXSOWD_|Kq*P=&1KwEP<(yqJvQs=uSMz5@JJiq1DD)o0-e37cy7j0?8=1}_PtzZ6iXRFArlop!B6AORll0f>Y)2Dq+*;KC{4TKt zN1y0<#n46J^&r=f9t|1d(w1gw{}nfC99S#ovdWV6i%y8k@$-`XHc6S4i;)hUVda~z zAOlorebMo0z3SHD@Ky%xK}@#dNkm8M-X zZ{WjeemC890AJ=i-oC+ zk};Gul?GkTEC}bVn5?GDulEAYM(MPqgRC^DKyf9V2>AStRPX>1s(v8lJ)dak8YE}y zEKQisRZ@4OImfoP9OD{zei$U}3AcWA4&$;0xykR~z;1>qT_&}`G@FpA41sD0B--4A zcXZ8@S$Y!$Fg#TxB&2}Yp=beoTZpxs?-^mocq{480F-h5nvq#EvqwNjlI^4owGjW# zC>|iZY3hn1sMEKX2e1@Co->4G-W0OHK8LclX`iA`Uo6AvEN`|%tmy=g~e|>&bPa|1YP#c)3aPbPaeZN{&!Zk`JrN#+( zRGzYjVLuJJ?#~>oG82}Ct}fJ9HP*uOm6^2Qh>0=OYJR2rJyFrKj8kX+;!J0>rMZo= ziB;}MEdE|co&dB|>#O<8p%C5iuf_B;il)SlcvoCO)El2w!CH?EE&Qyt!5t8fl_j&K zu^j0t;-eQo`N21hMfQ)*BjUXieuUhHQLHw>);szrpb=mWaFZyngkWMy-Kedv0784z zmyCPSvz*}OSv`P9k5%(rGmvAnbeBq! zwg5mA3AdQPbpN301JXt$C}U;NvteLjj+KJjOE8Z{<~)MW9H0C*-bPz|~Zw0#ws8z)pnkrCF=T1uh)Qa3oVU|FFT1AyT3M}f$_4}hZf`aTrzz1DaTtdG z&jFztc^biwOGK^PK(QWFZa!0%Cp&Tzbj(WWHfk$W3j3Oeuhr%YtSi*XMh}-lM}*VO zMMw(xGcfU`c;Zyvw5v4C?qW}!B_o5X#c)xQL5Tfz`!rdOt0F!1TO=IN&yEuMP!RD)DYeCb00oX=HMCpLo@g+(-qL0)B8 zsBCTwtI}l1O}WjCQsd!jWHzhtV+k10a*i8>3lhBc=62j-+jo2sU0d&>O8$z+a{4G_ z>1@lc^l75M=t8q%E)mMkTXoR1BZz5rfcke%Mg9Q=l#xlSz(+|Kf%=J$?-r3}2W)K)BoY$(6j@m9_5pH3EzHeFO$#>3 zuO^HqzHU+U^KmuaI(z6F6%k1gxm|UkPjv#u$3KccQL!Rh!p3U695H}PVaQQs5Xi=D zwMw|5!!96^_(VS+h`l@Pf~|%ga6O?FgTA@U#@sM)fb2rWHurQ)p`{tI2}QS&Kn2jL zoIFG^UEIi2>=1VO`EaX$aK<t7~A$U1JR-Gx(mf>aK*F(+OjBr_PwGIxClr;Cw(wVjh!z3#hxNc1_wu(Czs%Fs) zheB30fvVu#{mFs>Qe7O~=-7*9Un+Kg7hg{Q*-c4|!iNOGlq+c1X?@)PLEKx1RoQj@ zqJWDA>25&~=@5`Er3ECVL%O@WL`p%Bk`M`{7D#tDf^?@K(%rG=vYzMr_IbZ^_WtKw zmlrM(*1G3?&pF4K;}>HcG7e4UcX@hZNb_p8(oR1LzVmkSV_)N8x^v8lD!+5*_)vud zfjJ_PfSPtH$#M%-#`5N96L1dn{YHko55l>P|)6H zh46!bWpgwrf#spa7iyLH5m3`C-R3>aDtMDdpSr2~T};of+l!)OmmL7>Z!4PkXP@Ve zA>zI$7|2MpfG_j}b)WepI3t$l;$%mKt>N^&=RyGT2=F^DX2%KBeUM1ku^j?*?pB!z zm2;k47R5&kmK$R%X&LfnEZ8A3_cmX=dw3Alc7=;vX+2pGk{*gd^4=HVn(NEoL4A7A z13`cXTR&I~3CDQOo%4H*ONNX_*C)~ zrBg#31?9f?Yu=_Sa2lKYT#?qK&2)5qvEvnIqY_j%Dy2U)h3PzaGFWjbN{n-hi@Y0^ z^`0B{6G`Dfc6_AGbieCoJ(YCB;^ea*MrqQOL5GZLz2~=OJIk#aHLwT8JwgaclDr9d z?aO7Rz1uo5RGyyjILO_ZuNe!wJ|!yM-VZ};^5D}2|!RYh-|+WiTV>AF(kNdfwcx$FT}QK7bS&%W1A|JN&lO;@P~Nq*7gJJAZFwb946=Z8P^ z{65Jpn~)=&j`0jMH3E!JN{nfPrE=yyFIk3SkgzTYQ!evJ;$4o%DkBfC$+Gn&yb zU<&QJnl$@PMBi`cfI-ty3+QKGV}b~Y`gigV<`Sx{yyABLS!p^J4{}%SdAoqjPkgx49h`OuEO=Z)N%|Bgn<=h=sWMjwE=(aPL9YgHB6wV-+nPTH<9dVpt-1%gRG+H>J{I;O< z9ic^}Rb!5{&}X%-g_UBLi4D9rldCermb1r_CyZjP*M@!IzqVDw6oBA zp@`ie$IS;gP&lOGy;0oQcAyL{v!@V9^|Y3#5FQoR?Z2!6SZulI%e;}0Ts}{O=D;({ zA4A!hhW-X)i>+ZtABev^mg?l8LJA^5DG+l)LK5aYZO(v*>udJis`O3-4ben(y|&6d zn{lVQ_v=*<_WapNAiGGn$rlvZD^7w#`PcKrh>-k(-yr(U;0GiYK)Y14 z7zl)$2J>;9<)xiEus2ZF(DTR^wnYfS;h302%Y^?%^iAOzM!WxN`MBON>C89nn^9OQ z4xQ&&L9VR@9Rnv-g*l1t-zMkFnDaT&wG;Kx({+fopxN^mr9;}q{^EbpjG(C|;QHJi zMF3W0IC~Kzq9b!z5uS3o5qqMuA%Pm?!Sa08BvfKsu39*b@7wZv>~|>ia70D%V~TB! z4EmFotNg)uTX*OYUSqwSqQQ98W8!$& zD4kTnt(gk8AiYRC9<9Bb_sFZieg5tOtF`WBwl1!8p#LvK)9`9B3{z_we*NLY91K!# zg?|wlx;lkAFjo0kyJEok9#Lp)fW0C3u@!M-_!7BCG0VzcI{NRN%i#c2C7LU&S7e8x zI93C!J0%j`&N@Y|7cHzBAEBeXbVVmucu}1RHOBlLGW#n)X}V{P*crO^f3H~V)cKbB zaK|%cylC)<>DVO>?m4-Mqs2cb|sA>VB zYHP?BjV&c#ZaSs6vD!&wgv^codmyGDdtM6Y#=3cgEYc56lk_aoEDgr%{Wd#9v~^_h&Kd60a_*|g$KE0 zMQd3DaIrrLmZzw1-fGA!+Rc#{2zA}~e7pv?zxMWdgO3c=jru3ar7$wmkc{y4Z{2Zd zc(M#n$YXY1WMQUe&(*kC`D~I0e}dwnQY`8q-R~^fz>M$!2i2~l>I1Jh0zw>6<#l%b zoJi5c5kVnTh&`x-^FZNpFJM9Q+2kY7On|G23AkRm_RX_sCs=IVP1Fe@z2NI;w^jA) z6U52A^hb3a36#M%?4%pIW10Hi_j_zbJ24g494jC#vw93v2y?gYH1Zz&2vKr7TaKB3 zAT$7EQPWmZ(`#At)NPTE(w=h3tX_fi!@?$FP0rvoy=?j&R6SM`k)j)!)gN@ToXhYm zIH0x=dk*rs8aBfQxBAL{t`}_Fj)tZ95m%ahSuzX;y*#t=$w&EW`4#-)+7+r62P|U~ zx@*hEf?{2K2dikYGKq8q+x; z0V<7c8~cfO(&^2==FN8O!ttZnMdZf){o7i`UW`YJeS&L4x2v)x7p;lB=7;EE^(`Gn zr96iMuqa0+S+{aU1RZ?Av2aOaAg%ten+E%d*J_g4q47t*-CHSh_V~Iy%du%|w=MzP zIZoDd4G0}xTZ*el&l=~?y5f9AV$XHuUBo#;?lLxvEfbor!Kx{efc(Os6+U z@Lz7lKj!Cn^Rp(=0||scKXC~UraBKR@NO8}IcOnbqT-VUJSCy3bAX>)8O z{w)QByV4o9sG&(>|A#%0Pu3*%|s(&sYPyygM_&+jjtaf)hBnRhz; zg67wV?P&);$f!K9o#6{vCWA?fT(_pOd2H@)FAvNb$xqWMUBZq!mlkcH8AftApKq+&@aohVCP!I- z@L{R$(iyc3~?gboq)qtR(?kHN;Zg%+kqmX9#SYHs0FjFS3e{qHhg*e7krnmk#8wLwomgpmY?xHBx^rUlid%yy!asEZw9h zlRoT-__a$32blIJTgipprg&jj9c*1@uAC#`wG>>tD~0-~_FmA-@UBkEi;x;1Mi>QB zTvc%99x+SzStTC>h_!YU375K>s%jpnG_^Yx1C8!Fka(yGc^)c75c%QsvWk3BF!-0E zwgfRJP)CYi=yW0IlSG(fx$}>q3`Ozu>IqC2?q2k_-bwAx8tw#G-c_1i>7c6ah)TJ4 zs9`z!sSv1#xBT9n>sHzdjh31uG3-uG;B5=#B6_Q^c}O78z2XZHAIA)r%6mcHr{P!q z;=QJEP#_Qb>h5CDBMPbLn4q{$lbIzWUOw*ZgEubfrdkfWChxTzE436keK5{kC$#hu+N-!1Bqf7-H2vlJ*79diThCPyOQU9xzr_y&rUqhrt zQFLCo5b_{r+<5F7^w0I(v(VAia4cjUI{Xz&el1vPMzHv0U((Ie{8?}>6f%IZ_b0`4 z;i);0uYPj+{-xbDi17EiZ&EY>;z+GiAfaou1czGiB;u|WPk(NfvVm|Hy?0;BW6+_# zYEp!-j_{)g-hlb5;&(orK9a<>NUD{jyo5tN?p}BL;9t>3bP;ug)f@XCpN0VRcu_Fj z)c|>(ZDfsW?pH|gpk}um8MWM9Xx7SFYP`9!+nLCf@YoG`0*kGpN7x2_)t-FAL|{@s zM}dBk4W-rnJEmW|;yne}M%V9bocF(Pblwm!n0%OM^}+owi5wE(vWHI){r-mSpMs-O z2&jp^DhP5`z@yeMh5UKA;LZg+rEQ``kz;8m*8Bb4({R|*RRe244HPfcz~O!rJn0dD zZjS(4pj&ElymoWqN87V7=YO5TXP>nePk{Dzlj7@$E=;a$3B>2?K>P!&IMRCzHjQ{& zId;T`bNW?53|9qk-_v67&Z{BIso!r%fC{#B#HsoRNRrHQi$QTVd(5)b>%^8~4rm{H z8A^(QB1h$|u4_`w+WUwRpiUbF)~6#KQ6iF#xyo4`TCHf&B<6sc$M3$I0TU97Z|Qql zh>U)aF+RFFu`1_D0J*ZqkMZV%+P;XLmrdNg1h1-WU3carsLoJAwB;c-``$RWpi+Z%$iHPmp#;2Dr6)eFxD06Q; z+gi*nOkc83tz&U@)U}ydam4F%%DBwLsDy2VnRX3lkHG*Q^jms(AdiB*veTJza%K;ht z!(|_^Nx59V^+Y~u1CMV6C_OnF9#FM)vt6GP2*Na@J-xj^=owa;Q~iTsdwDF5Ucc?5 z+}k3(TKn+(OiTy+)3)W{SSD?v-V)#}dD=Qf;~nPdx4Y0==07r&E!S>&XZ(1pcN9i~ zX=V31W-j-=swvp+DC|_|)EVOI=D%;N-xMh^;R7U2KxmdyN?FNz*jfGT^QvCTjP~d0YNys*mc1xTCQO3Vi2 z>RgM$w%w?21Gs1Cl5Sbpw))ViUoEV|MTFMB@cr08eI7gSb5gjcc5ai( zNmkitjaLaavi0LtQ2y*&^ZO8%+6YUi+Zgptt#vwBDqQ=8iMd@&$;hVO*tD_PPMfOa zCXr{2J#$#PeX@MaA%F62vAeT;R7$Y(TbLC5X@x?FDx zxH26P-?{t6{4?C zRBeS(@vNUw_7SzOtjFY@3CQXXUkdm5%+xX3P92uF7u`%1>3XrTxs07*8nb^BBK`N$ zZ@~q*m*Ls4+wX5e@A!9T0XIucw%7{_Vj<5+&!bU|`jDEJWg6J{=aU2U8}0F6(dX%3 zE)i+Diwg%*4*|AEAy|_MG&&1L8Z*4bpt13gAxHYG`*PGd1EgCXo|*Z>tn2jFC;dj> z)$ixmFhCzx*yVj)L$j<~gbc*b8oPMg5n`7di|1$G(|fa;tSW71N9#}K@~K}vPlm-9 zwS0AtOS8n7qpLh+4GY7VAY_25(=t3seN%b^dR{(5HF)HL=THZ*6e0M!Ig&*Lm`qUE z=BtLVxL(b6&Biygb--lSWC`8}LcaCzhLMPLTj;&33FDX_h%z3$K#dPO036LY4RmKU z<%0!HOc=cU&8CFNwcLbgHSLVt!vLsgR%w6YIsn`M2qRn{kw+ljs|D-^Wl+@CR~fkC zg&8vR!JHOu@<;46Dgi^eV7u|gw_fzM<4CR8oIa4>X;9``HIp8I&^=qXd()@wd_%7a zhrSo|ti;G98BTpf(DvMH4&c6XcxzTmqd19O1NcqiNyJ>Hk>^1AFa&`@m3BdK-80bc zox=A+q5T`+)c9@k*_*vD^o@x}Ou=}zwOc%hrtF$!0lP-5xBP6@W#ZV%9)tbmUVnam zQH^Z<_4!8H;EJ2YK&!7o^TtvHn>tLJ=Ca5ekS_L$@CT;Y7dk{J6wVdI<~#s#<^y=k z(lr47^oL=k?cRoiri;RI)s|fa-^3weX?2*}E#%uz0UR|(-fySe0fYa3N)}624dYJw zys6^Dt*+t;chh|*5?oSh1|ttZVezN95(dJNoUhLi)@=Rj$|Eu6BLPRS9nl)x6A(OcEtUNnik@dNc!XcVSRPlW<(RilD# zGKHVtchsvRTxlO#fV}pu*hq^q2$3pmb-RHK*5$2aFPCx}y1z3!* z#Yhy3Ii&G9yuYSQk!^uMfmYUX)3;JV4+Xty($y$PMtFLI%j^jg3Th@*kb>WU@P&Mb zyTj=&j;C#oWzYnt9mW`=al<{dap>u)adcD8O~h{dmQ`3g>cH1%k;6d=Pd%&@#lUm* z$<2d*ABRPK0ilOUMCZr--&xDIC^*+naX180S@uliLb-{L;q%zkEf8Apl)U%5N4`Aa zVn5p8RRT5sT*8ci3Pf`?|6_^?o8g=&nE#AmK&sz!`IpU@M75^G?A^RpW7Ar4vCNkS zEhx!V8SuT_yff@#==x%0y@9~$736!!T3YN6U+#+MAx|~FC*8DfhfvbHluU#_GC}b* zyyFOC)dRza%qQhQTX@j={s%+4_mVX$3_!Kfm@zy$Hp zqEHNE7{j#!1F14GP8sdYSWeXsErz1BPGMNkocIP&T17KOiyO;EV`y-33lXC9JiYsU3mGKxp$c z?s~8f(r_nNca^;;|+;uwAQmlk@|94Jt&v-wRDYuWbOiy9L`pwx7@ z^!_1k?58}8K_QPvjQdk~#{+#0kBU#fKaHvV;1KKb#!6pQGjUujh*hO>#xa6$TkIlK z7-5Rx10y#_WgszvqUGe1C3c68jdXR_`3le63ej=QP_lgiY?ik0op6>2UK?fG zpR4CaO(SY3FjzVWnCxtD`-|eQKQqVBtvf~cQxz0TcDE5)XD1#h_EpJ!LQ!~yHK4pB z)*9ibj5T10-MyG^{KpI6aibVH0Chk8)U&!7;*ehP9LCw#@Aoj(pR!1QUVX~io@wo3 zj=LQp(se73j0XQj1LOIUIaV{S#Y8{e`w;u-NRr|g1ckHowl_kSev>@5rZ)$DNuNJL z_d_AFQT}o%<^s0xv_!QneA-mw71o_L)7I=YT#-Vic~KXuPKRs4o1Wt_dJ{nUMPf}`dJ9e%p;a)s?7uZ&D8fc6WgU4t)xB5ZH< z=@%qh)fTT5-%1-jf4@n2m-2A{d3?2@>(s;3(>Bz9g%n1fomy7(-5Z2s8tA1t_~$&C(Krn?1#F_u8Mx9GG*OpH6+F zIM{Exg=KD|3X|g1%hdKQ;|-G^`WoQu9Z%bx6uFFmDW6jD0=1cdJjw~!@thU@n1|l# z*Wh#9jpCh?UPd>(g<|1P6P%n8LSsJ8T&8mgRE7Hu{W?&LL++MVYg;24Y;hsY!K?Ac zYkk=l2p0Zk42o&0gWj+=%gaso4Y_KEHegK@ZV1_&d^+H~*8^#J$-n%sQyOGWq zxVNRBL94nGI$2VCEf70xTYgFlOJDL6PUVexBsVlQvw5#1>r+c22PnO%zUPu8c_-Wx z*JWDd=B$qI<8p=XmFH0#p5of^b0A`VO@Bnwht`aXqG`aPx?ju}X{1AX;I z%%eH*cCfzL7jN49&#XsPR-_Eiq^ge*FJCpSOj#9=#*`>k0~Oy0gE>>f)vxp9 zP3s=c6h>^9mqXITi2lbHkeN1?4VjUU7hGjg?|1GZ`H&v6WT>H$E@gOp*=$`j*E$S< ziiY#d>UU0kQ$Q)AItt#+L`B!q={qLX;>l3Bom@jbf%3#O&3}+Kx-m54!8xLD5a0VK zu9%Vbt8hGqvQTt$RW3xRPEYpfyTaQFEk1k58Yq~er=Q-xZyA2U$9sg<$(#jLm%z9P zMwb3wRl@jL`Mj6aVY<;TJ$H1Q;2ko1Q3eetYZ|CDU~9Z}h=|aQbJC<5&;Gqixo$gB zWq*mabHI!cn7jM6vuP&qV>6DAo3-QF?2d0Pubvp6)l^yTQzr~}tjr~t+HuakGHHFo z!sgMtw0`MSj!u7Jc8v3bHHHw94>qa3m2w~$2MHDz9g`aCnG>`M!UdObF!=joFCi^j zn8&pt2}Lp)T35-YUXEA!C^Y)q*iF}kS9NSzU0fy1E7Z}x;=l^^M(b}}t;_IkW%@5w z0+bQf)bYclKg+f$6pkJjEco&!8A5qsV@`A=EeoLX$qRx6u``Cgb7Ft1hQ)r#+cNw2_XWS6X1F~F-GK?&xS*zp&5Z0QX3cvQsV@D5#fw zUkXr?Q9#a`;02!b3$pZ;Cw<~?W zEIV8B-R`?lzTEY@6=2xyWRgbTMJW`xp;rP6XF0#a znR{2c-wixk@&L7x5!zJOMZK2;(EA+Xn3U!vO3p>}w&h&M@54{Dq{Z9rS*aA1O<8x~&k14E^ z@VL5I(QX_li$3czAyF;;0#vw2ZZdkhVsga7l-zo!6&xaC*Nih7fAC_z0j0Bi-$>zn zzi4=%%X`;qP>_N}du27eWqoD)w$9)j$65c~)&t1QV6o^8d!4 zmKXNrYgAtkwm`qwx8JHgFqf@-mm3FGbG=iXultY%P_1~70CvfOwjeFfp-J3s#1o8M z>ypoZEHmcuUwGOV}HgT3m-mh6=q@3@J zncY$Kr~LrRAb8zHR=&-F*r)9ep4#fJcY{G9d!ldguwy$$SxTZ$Hw6T77Ibl)(Ju!j zVr4ACCofWSAGQ;zNtMoeAM~fe880inrDK~OSuV0jb*3O7mDzbz)ZjnQT6U|c>-~ot zA>WkFktH`y4>rAYFy+KGTjgg;SOqQfB@UD*3*4a$9ZauGJHPK$r z%e&G%6Yl2kk2Id}(VW@y;q-~z++u;a1x1#1nhFpczT4umK)yO|J)dRuXx<6CadW90 z2gIf%;SP!w1ml-sRK-e|+`ayGCE|dX*A*UjP`s7;@O8J83yDB8D_XeOQ8kdZJTnj*j zc=h1pt^8o6mf*U}8DLRO2NM%N{J2 zdgy@C02$_NjWZp3&cBvEEsE{lnS&YzX-0U*yUY3^5Zd&bs4B*}Qf-lyc`SByY1`GS zDWnNLqoRqV{Y=y4@5wHo!Sj)m$w86dc8L|7vR^`$-mt+!`fQ$#Zjb9sMSu2_2-0-E zd70XTV&Tv-2}8r!6EO9TZDI$0O}o3^V&t(X=+V!vkBa>YW9~|6htIEabg>#nTk#eY zldML|%|%X+ABSbC7f!*PtcI5v>sm{qLQ<{v!(tl$T{iy$5XUhO!aCaDlP{JAR#$at z#l$Piz^5;Sda2NNNn-at;KcTQN$YQN{v8yy0I@r%q5r{0x*gC_^1k|Ky%cVlanjdE z#`AK3tXtc{9odXVxL^R4P^N!+6l8hLuozM~*@AA!cqv2Ka+0Xi_7$$Q3$3}Xr>AFx zPJb|%duX5h4myagrOOkyHeK6|1onx2jRWqiB}P%Z?J*Z= zw@CD8C)Vy$CfCS5gR{CgwgFzD7M7b6C7up&;zFlE*a%f@no!aHloJ`Wpxx%6DifBK z7wu7}Of#*iuTGRVy4l^{1M(`c@VWoM@tZUYa}`WBl#GJb!(pjB({qFgLC|+!3>kUf z51dgmDSZx=5ixHATzd~f*YL$&Jh8p7vz?N>&y7^-V-|Ka-30>QF?Tc$Wo%{3W{=l+ zof&7iwqHmH=73FaO&e36jfXykOXdf}7F=FC%7=~<`S~aA$>;*^=pIK$ICiB=yUB95sYc z3y`EwZZk(|Wz_V~OxqW|+Z1M2;~}!)qBHv|J7%5X`CvL-SH0P)vLS1Eu9EvGN^WL{ zZhI&pbn@*vwtmuDxBrai58th=ONsZ+gY<-!;^A80x;hqA>plW=axm^u4No zcdSwt`kI8x)I93ZD(gjlF=ZiuXw0@^KtZMBZB+;0v#~L6(&+J7BDV;0hOgW_iT0T+ z$y#4~bmMhn$IRU_Qb^WF7QD;^g$vhh2b!v50bS?D5SrlJ90jlY>Zbtg5N5G2b^h^G zaqzjGY(x~_zW&Woqsvs6oem8&Ohy&@sOFteif&4n1;&|j`Q3j=eLaw(C&S;~7^w>4Qq6n_Lj~_V9w$MG?7pA}doOFJa&)pP`{r=46 z)nMA0a5{J|33fx^IqCw{Dl`AsdnqD03Ch6Wwoo(I_(>S7ZTiV~yPu zI2fuA#}u7Yp|j}kk1{0F2WStYEg|UT3o~uuc;)n@jTM|f_p6ay+yyQVmaxJDuM=fSlia*;8fsWywDC2ecQ5ds z*;-5{re>9Wz!=<1U{2YIX*YOMvBKF^m|=G2==W0&wiXd+2l`7G$tfbGCzPV?~%~Sj8o=;kW8ou_|yi|K{xkRm~^!~sAU%*!5(A*s~h$+ zz871bPK2w-lFRn5D1?7+LO-{nJYR(J{CTri}{nG<2`B2x`jR7-F$KUF2}t`<9z*=u0vN5M})A& z&4+Ueg=;WcP~95<1*hsu`?>G+rH7}65Cku?eIUJ&j;*t>GfsM$ZsXo~gFn^!^_7~` z?cTk6*C*HWGAlyb^bVlPHSziCi`v-Tn~9N-$x9cH@5#I?#LXZ+t_y2`TKfpS;%B^> zkbow#9@c0;q1VP93B>U_4_g*lB!%rbJoDQ@1Q5~2%YC`Rc&sZL^N;33-|9>ULAu^1 zH1B}t=YBW9h#nk60oVi7-;VO7*556sFAZNl%{|+qJ0rXPZcItt&90uSEwhR^ZeJUk z4f0~GxQp&MhSARh!ujC&Em368J~G5JY>2ec=ej4z3N$eK(>o$O@S>+^rbp{)bEXA5 zhZ7e-+ji{NGye-V{`D1+9=icSd%^#!*Pm@CzyJZjieK4U%FtJ|>MX~L-T_Bd!;EEM zp*lG5=talP>j>ROZDrqH2moEK5JWmT_y)V+J_Bg0$?g)X#0n1yJxU=(ox*AD25Bd- z4^%A=jmghKvpfSa=&_5dSG5S}+X$XBqz?oy{uEAO27?;>PvKO$C)OepF}W4db82S9 z<|=AhaS6UyhAw|}LWir4c2H9JacR-|q$PHPG@%n(#p0o*nQZF=Y=qM6zsb~F5sSO< zZLb0PmzGudneLklK3kc&Fh7)EvZGWq-ZN7G5uXzx@QIxvN$Icjc1iL=0>HVrD+r@y zq~O^>hK3e4&00vCz>{a_jttved%WGAoVLV)4MgxHD4&jY=Jowbt)Ip3Y7L&q2nFR( zl0QPh6uxJsq5I%|zeRU1&rub$LgCju5+%jo2}2n+z!50$sa<->G`hP;`&2z7Om6P8 z32yGSEF#|mz3sENmw@k$<%`?2)2N@n zVb?~sf+kfWF3rS9t7Fq+@`jymOZc2~P?F?%MhLAXyD5{Bz(_URv0XDzjqs!63Sc=q{4eljf%cI^GItbDO}&D*z_ZuD zc%U$A)>*OxjCJJstEPme8%#9~tO3JUe;T8;+R3axeeQ8}r4Y>-o~JOJ6m$s20QmC< z9}7l4Qq*Fn`#s07!5I4dvh%d=$6Wv7`Vej)1>|4p~{&jO(=wc9mvQItYc+8LV(gmcy+88oZBjc0|P zOX2fYH=ljTPAG_tjBZQioDrdyu)Bl&Yy{?9O0SUo#!(Bh3&Kw($nsM>qsf^2VAWHL zfj%m5Dyb%%f#E;^^3q*HZ{Y8}j7*OApe(*FZ;- z&peq`EV_F*gU zn^`GJ4@n{5vS|?*zP|`;ZVn4?P7A$PiS`86l4CqMyQmJHRXEblCn0ER3I;VH?SGmV zg*z0vIq>`VQN?brvGls$>x9+E`968o@sw3L601XBHl>?_aSFd|%T{9zC;@JZwc@xA zWnCw6n!FT#+JA}EDKKl_ih1rXg`@Z6&dwxfxQbuW9{Y|>PmP?e`T4uUdbzo_y>g&^ z93@RTKQ6R9aM;YS_{Ix4RI(FayE^=FA2Vpxt>T1rh4W>)Os(I^>`9*2PQ8XgDj%al z7n-41BkGmpp;a`cP%N4rERwp0lLfOGM!`d`Qz~Ww&s--x^rUI>&{9t;he}F^%~6Sx>wt>oQ*myjxg`nWls*^%gAbY zygi#Ge0^#xBm&o8p13T-yRBn$o;=yX|Ff9!?#rF4@e*UHQDmyRVk$kqD@?ShO@+7^ zj)Wh=|8O-yiq?WUg6Y=|Ba8j|(=H7cS3jt#rl(hg=sH%cUu@q;=({YP`aR9-3Cxlf z4Si0N&I`Ehc%zHf9^wQ6y-;xFLWKf=!Tazy&GY$P3x;G5fu_b?3zG-wF-61_pI%>w zZAQ>T%I*0^#d=_Lz8-)~sdO^$*BHo-82pfw$2#u;JGb4Oju^@kAWihyTP$}d@IOgh z6Yy|Mouhey_&5>hQHLX(1VW5meSu)g(k+Jk;roZc@cWnUs0B_1X!U~J=90Eh9m`ZT zKLpRde2G^015Rq68k2g28-Q|_%N7)@#1l z6-Or~M9$no=)^O6y$jF{Sr3oh1$f26E6aCb)E|%pv@5KYO-xLZCpGkavfVxzRh|Z6 z3Fl>me*-d`p<}nLsUIAkQep(c^vX0dh&QiH!5uk_cF_J_GMV=+4|{I}go9RkKO-EH zWJi1QmSW$dD&d;VeqJ+62Ac^PO-Ee^Z@B%5*R)|m!nLY^F6H9UuIvF)hNrM3A1&oH z#GrSfIY^}?q1TTem+fu3*v@j*(VUl)^ITXc?l-q@=p$5v)*+?A-CI9~bG=N;WP4Lj z!`gkwlh771QB>}1l{gVQJfaAxyTu}N#*R%NB6@fIWha%N&NvaawB$Qv_@3g%Ypw6f z!#gb6JXSRQG!mhC`q%q!_0BhU*W1u^)ibUTZD|xEL<1-K&3ti00*hFWp1<-^PBUTR zHKH;hb*UiNr~sVlvMk0uieOHff^eqbuz9Z?A)lF~szpcLvNe_BwC${Ecod!|DLnmn z2m)E}$_hJmhbMBP(To?Sh)^7fY^bIlmq|$Tj$GtXc^$4TvaFX4o|rR9=Fx@MeQ%+A zaz%izKV538O@tIe^UKits_wihqULhHx4wO_9#BR;(K6ok#zuK8g6eTgN-csA z*(1Gev;uaBk~tAM`-aB?kS2#!{XI+Jo z@K`5ml|C4oV(U1B{q{ zor$&p2l5VdF0%&1Wt~upl)6`o(&AXwEu02Tq9G-tz#rNfVtwM%VTI{Ja~t-e4wI zk&1;65~X1Tig6e;biwVMI7lr^lUBv`IS(FpS6(yGtLS^I5Fj?Q?#5-6?@|O0Q)_+( zC$Yxzg2qP|c)~3oo(~}*xNw>FDS;~D_}-&b!zRoYmCt4a$K3euqz{h4vLDilpkBL` ziFYX$fx|sP9Ssu`1>VEXXl|*qNMO0pLdO@wBMu(g4ZB3#x(|MHzbtsx#y73TZBK>(N^%gt%0h+w}_ z$ZKN01iY4T(D(&ojB+@3ucAR?78Ufbi!W40{IF9496o|2!)HrDKsZZxj^JBak^vKGF^Lt4eNssTA~wrNi*Sct>*K}6ye2Qa zK#l$*jyM00lB&BCp^R2LZ5Bzm74* zPsI0007`Z9Of&FawyLkbt+n5H=@>pHm|D!CF3o*7nRA1n?Yjhz~2cOYXthi^`z5%wn|)ppiJ={=<8!e%I8r!G&z~T0=R#Kgf|Xl zO5{Pb%Nf;ZExOn*0+7(;si%m^ulx~r0F7e^?{dOqS&~kGUIHca8?Jj3j>BL=$wF#qX6^ z^(fs+U=o}9tttsX!11TX2!8M$%^HG;SBt%(*^qHEnN~f~ziM0IRJj-SeJ2CpEBwkycSt5je~xx0c3%ZVR}dI zaWjjZlFy_raM25n4|r_FG>1(~2&I}PzoCxvwOr%uR7qX7Kj`WN9cPxbB6P=$ys(XE z^v}54Y71h#4)btb^*);_Wb6_!f6po~#eB1qEhn_y!vE2o;VaQD%~uf|;W8cyl%zsWmRF~2fGK_hOvOy|GRxc~N= z92GoKT2ZZk>4b<7_}-%@7YhToz|>CZnp26|Y(;~$!OS^j_jjs*Fy-NHd3fYy=?z>iTWN1G|tnNHTxuHd}p*G5-rL3nSK|6QBHWEI#|ifLRcyg-OMv04I~m(s=}E z3W}Bi*0)Vcp~&S3qp<-t>Qiv~d~OfU?9pOmF`tW;^w6h>|7# z8G$bk=Oh?Vf5bQWrSX1&rdW%+@}_LkH=Z2GxULKAc$=AWrFa~BfVR2;t>~&68gd>U zAMuHZ@t-=5KyQGA)uG;1Nb{?)c>uE^51_#()~@VWaZ@(f`cXFe)QNH znwW$f85HS!`gFfA5_Oc`Do`j!<8M>lfI*x=>zu9WFd29EU;=Y06O)Hg+yN(~-hK_E_`>;5!)Y4kjDTZO z^K21i<*%^`35J~Sb3S}X@-NV>tjj#b{{9-RDP4s15sHV@Llq zTMihb%YZ-gw97krL7G>566v`kEe84?CqD)KjB`dlu>Pk}1eg-Nc=3WWsd%c=j;*@d z>-ol56iCGbOzn`#B>y%}MtK5X&pYBJS+6nT^|(Lz)5$A*@P-*5LA!Iy`!egWPx^Bq z|K*jrE4+_?!t1Z?g3`Ucy{{<*N>l1Ft+D_93^h08P)~V>M0bZqB5S1#7akI*3oh6| zM7#{^_YY~siy9fl#xGR`DPG`{klY0vmbZt880ljtF4+lFMY+dFD%yhOF&2f|)GUQ@ z)kwxhM%va=N9W@w@b!dRau!h+suvT%VN!|9W7SS?r#{~n*dC|}D-?V|WFCIdG7roy zcPVk&!rtW2fY}63AT8JXYIoOucy{4XVN`?mu$2tHFRe!mx5%Bu*&`jm6KDNs%GCYhO95G)_3rg6^!VXwl z(+rQ0d$qo9+5KgX_?3+kBl!NW)g!urH|W)w1^gSg@1mole<&!RyXVTu%-n~D5mDxQ zM?Rql)F}`j)tXBVqk)ZQ&^bv@O^s1ajbQaae%Ll(N=qC(ummNul;3YSNRRmby(Q<^ zL~%LJ(lTDbrlw(RdSmf%fq%5y8n&%ZS7aOzb8h4^XH35b?cGs?;Qi)lwbtud4nJ6M z{JyHwJFjAgx(h12PFy#dAH}n&L2v(ay=3@8D>`&iB&j=AwvljP6rGp}oU|0v=^S(z%( zlP^1DGpgih&Z6h^0vwL^;)HiTC@3qMUl!PQ<0kRir2&3>C*^d9Bm~>M7U8-6vyq~vr~k5^{O~DIJr&BgKD06GoK*R{Dgx+n2EDpQMlhTdKb|CHXSXXn zwKHb=%J!UrAvbA(NnjXO@%Zrm{mjfVC7%*YGGqAAIpom2(vHd$apsZccl|f0bztlN z{3T2kFF5{f<=X;H8utm*CbZ_D$8o=RUutf|Lv3^>b!M#oj$zBc9}OEWbr%E|7gr&| z(b4r;v0x%vX{&_Op9LF$jm>w@lkMX(=;2)H!&^`zJ)_^2X<2VWUbIjTmBQ^p_N$;|)##zKXZAm?US>?8-qmvs{Mc&vhkQh;(b=uT@ev|&;^hQDDyCmM*V7EpHvGNSORp#6Z)Z=p#Hmw@00dEC@R4pV!3yIiBMPa$`wMNT2V!p5lev-85Y=j&rDWg@4o z6cV~c4MVw#`b_U^N*@Z>$eWtxM3O%oVr~Es<0R;4)~V(*Y3uA9IT`I*lGjY`NeT3e zT0AyWYYt=$PvjK`UfIr;M!wTSFTaqrvld8RnklxU!o`dG) z53U@qu{~MKy3nQ?Gex?=XsKIWrfFwqfFs$=YVQZ9NQq?%r6c*{L352&9UA^U+}U-e~nob4e=z@up(DXzn~^ zaoQ_b{o96D3fskp>4RgzuP*CM4LVM)y5NCne;>d;m`jd}qxJORKKA+Y)x+eX=Ms=E(3()XKNb zqH?;d=6fI~Vy7gS3jVolnhYV_uNMCoVP75(_1eF0mm-xDQI_f;*@{7S+OsAh%NS)D zOO`BS9ZQPJR)maw-;I4RmZZqO8#~zPs_@SBAiQDb3cxJrO8kqWtTH zYxWA6ij!(-E82iccDk1K5H74YO9Y}ZadOrWnYA}Vm3h#jsdV}M%z60{r+P4iH3d0s z4eS%t9pjN4ms0kyNVlTiW!zR4u4*}cVQx}tfIi-3;_X_CXuAtNB%>;y=5>t68ebZL z?MezV56jFDCyCD~YWyJ(3)l74x_J`N6`Y@_>>**wUJ+t;W!gaFdNP^|(R$$I^ z$5+-MAda>Q!$EmTGC9`Rg{*9qgfSpe9FJ*0ydEvC8*>;NH6sAc3XT43tF8?$ zPAHif-20DUtzNJ7Sy-M^?s(Z`a0!JdR!{Mka1&p8%rk1HlZfmsp#KX8IXQSVMeCNOIgg&_@>M%|@!edyYMcTZhgb4Hswn``5+pPlI+O5@_yvj34@;afR}v zyjnIK)H&8wzGz4i>;&XLRrDwhC;;6bB^y$%ay)Xc19PM}od)egIq>irOR_%z%q@ES z6L40 z=&~JCrL1tV9BfJPB<>_^>@vl)ec7f6CkWu?1d3fdl>pBl-Y@?Zw7tFMDcsaqImbG8 z4^53I^Q90f;yruJo2#5A;zk z+vc=fj?Rhh4%WV#kgS^DFdYdK0z~&Rr{uvEj1q+O(M)I_hmHrjv}Cn}cTz)le5)*n z9Pxdv8zXr`wi)Ky@4>p%F*l>J(c@ot-|9MoAv8~GL20e#YHu0>>Shoj3^OB4cFyzz zhD@!=z3yp9Mh;@t6wAk#Y2U~l`N`JY<6vi&r#sBdqpQW*t&QQ_wa-e4*!JVwyD6J9 zm29}2M{ZE_h!UVO{YvLn#J^g~eh@?26t}7YlCRrs@`oPl1do#bvKPr`FBFVXRe{NU zOH48wm#Fj7g`%t)T!nVhKu<9V1Lk(U2F<9TnO98pQR6udZIbtvYP`bE8a&p(W;)dq z-TJ1*u2}92LCKgl~G}tmx)< zj|HYdQ~x}Q`TTia9fKFIi43VO_~HF7LPZDB!`feX))PtS5sz`~dSJE*mRLSB_14D= zWX;Y1QMiiV6w?l1i9u1$l1@;o-bA{IoEBeidi*jY;jC@Da(q zNKezCd31@Cq0#VZT$SNi&vVZ_-Q|rc1?+tH;*-&j3f2BDSg&2f>=C(z0=w2TAEKj; zq0)4H*Ka6-O=e5X-lnjyAY_PL$?9}f>k!iN-TK#()@nnC-2>$6HAHFk+*c z(t`~7GlRtKdT^5TY;sxL32BSTp7k-48z>bV65MXG37|3WpN$F71VO%5?|Ys!g64 z+`__sn`0||-=}AXlZr(;y+VU+D63|NjszOWfR9Wb-clN(9~E&qBxioND{kx$Tp@;( zr@K?^u{i*@sntP5Lmp~TeyZf#B#srq88N-63E8hh``ufU4IJykBgx1-yi*D^wdCI>=t z*BE@!j1R6vqO)q4#8x~5E=`Z}YzS==}K4#br{_;6;XcGbz zZBU7m8E{+$Rwu`!>d)54Cab805*4-ljmwce{)SUiv|d1nHLN4tU^1uNM7*W0(D%^kG1 zx9SMF9QRU5Mi8!kV(KVQ!ZD5A*)(Jeq-2`jL7z);5hpevwp*EeZA`YCnblT8moL{n zPF{wy87*e^I4^A%<>3(!h0^MY>@ZMP3D#SBlQ}%}@P|B|%L0@6TP))z#%$4>1!g;6$aTi3=qMrRSO> zt_YThLILCAtBFUKibAwFdsBYm$QjEQ1kZ;JV$vTYUcS7gsC)Dzk^QhokDgJKZCuPn zsP^|yWX3Ke4q%H%0Z*xNG%GaJQf_XJa-QLQ9}XKMYYf=dY5GEhca3cZDZ1A-{B$Vu z7Zot0RU}~6?q#dpE!11woDy`NQFnkyz1th6nt;h9x)0`=v2}L=qid%3W_K<3-v#E( zi{JJg-l)tLc1i5B>?RN2c8RFfBDdzr(QjfnhXB_G|aSfOHb>$<~r=&gV~m z;BD`{qx)PmwhWbBoc5jW?UtinlUdzFDXXLDxHhJTSK%Y|7dX9QJj$1Vzd5{uAiMVc z@-&FV;b7Ox6Q6c$j*XcZY0Qm3!xd(X8p$w~iDjvH7D+bjyG`Qgj1da@Vtnx^IfR2W za6Q+=xu4AuH=$ zUVrr4(d^b!-qUAUPF(&Ag*AZJz|=M^LKBds{fWvbiKhAvf-cLWv8#%~a6LAQ~f! z&d&j*28+f?wHgfUOeH-@vmf^r_U&xg6w>97x{Zc=MWGDrQZ2{N=skvOc@6#`l*8bY z@@j8_srDRkg zmKT5*1qB6i%USYxakJzDDOdNA<^BTbk@AKW*f_g-b>wL5CX)~CZoL2%`r0>^c!{)m zIbq}FB4mgL?9S$1{a^3%G`$c}@*4XuFz%nSm4g2iljeF&^29{aXPY;J`c>YZwOHeZ z+no+s2}kg}%sA-PCN&aOf2MN?S4Yhu89%9IfG4aCRR1=GH_FZ8BoDS<*n@@{^3Zpy zs2DHFz|+MQ32pas=xf zO)tatcx5g*>4pS*8N=9 zO=VHYKZbg!-`h;Kb6r8y3)E-{javtx<1OKsX+JHyd9-St?o7|*^dNhhO;0?+#eE5f z2}4ABp;&VZQn~YpbtTrT6P&lbilbEt+64gRvv0YcoNFT`s$!PYz~R1Or_~c>3peai zk<_Db62uMUH&Zd}btSZ%WS81IcV9P*p~LiaJgx7vtVz>HKAux|MTtkHnp0jFaEW0w z8$*tEni*a{HTxEg8y>9^^YM2(aM9n>#ath3+QA_ z0`9;Uyb1KRIZN(1NZO8^Ak!Q7_h~L6<`yjR*L3Or>jmyHzX=Y$?lgCZ;ybo|`JuM< zGm89;n5$R5ucu$_V|cGXBI{Ky`BjUZ;v#2w*_9OK1-Tq%wW8%MZjlAN95P)?uFq{mWSEDI+@;in1bnQyBg#AP`@9um;+3$9QG>$d|F9raH#37X zIVO?c(F0(ol_Au>q?wz6XMV6GyQEExAFC2oq@sTzzn#*n;#*F;DrdgcN_fOs{s?_V zpX}Bh$EEVwKHYC)2p5MX+`x7S)7qM)9Q^?FbRQ-LzY$T<2OWAimTzbadc|u#Z;mW; z=pOY1niLc`i9WD!{y;p-(AJS~7Ri~BaYZ74dO_1$SD$su3ri(l+dOxyH8u6l zjNx=0qkR5L36|(aw!pLBpViDtILcI3(ON8Qy@{}+OObT?8OVOlRDL#X`(U6c2D^>U zusgiuzHfDnmb{%}Q_>_}GcNL(rSkEfLr4m@{CbhHsbDxix*2?Lev5zt zud~&%G(>^ei;4NrxVRrLJdsRdzCmU}U3t__hssJyIb203WRFv^zIbb8YOOBnqOz!a zE^Mq6%lVjvB-4?16;D!lAKt5~CbRk{Q&}1wQhMRWLqh2sX}QL>Vxq@E4SUK#@SwCV z+F{HIn^)&{(xQ_rWLE%mA^gdA*(Y=Q9^opw$s)-aWV#leAX#nt;t+g+;a^%Mia?1k;?Y3MD_P|S$Xui4P`hc$}XI3yeckW6n~3d3R0aOB;L|{ z39Ck?9gtt7@vAar=`Tu8%T5N!T5o2G4*GIR^?0(>33o|FQ5euSbva7k0E^zjO3c8{ zZw^sA3x#?>!ZwzD6CAeQCXH4*6@ln=UTaMMjhq(_&WsW*EI1&#J~x#yUw7Rf8i5G^l=Z?oFvp5q~dPm+dr7{C0`{Fx4x3d=Vk?ai$BOuKj?7 z`2i31zpmHNP|ki9xjQOV8T}S$jD}1!9iVrya?*t2Zj-(3RW-7E%20eBq0nC#yJ#OJ z<60Qwxy$PGG6!_?@ZbU(jk7E)1KBswsMO`0qP8q1mp-gBr{s@UOfBx;_jcA}r=7SS z;tGfES_XOS6LCBW7C^}$;*#%vDxHQm_C9|)cAghX0( zdVdCj*fO;&(ZwRHO#=Rh9bwa{T7HX*Ar@axjUw^Tmf`>DNP_Clp-Wq$L@iT>G#x2n`$tsRv3l{%XYWfzzIKy}43yUb>oi*T7slCTd zaAKL(9^>wDwtoEj^>*jtJ(Q$nbA%v|!(%-?6o3uusGyY_UCM>-&Z;w~)SXB9%!oWl0>;^_)x{nw zwX|un2R1+i&)DkPfriPK6H13#ToVRDH)1**wB0_>UTjX3b6 zEC1nlUPY%8{H)i#+{Mmo-Msm72Rk!wYfca+NiX4LzXJtbOG!tjX2r0}q^h<~w)8Y` zw7cHAN|gP=!oslIL!v|We%=Khb>AJ|E}~E1|2xyHpXeul(pqC$+OJ1_BmL#p_cPvS zD;xYGjwlVsxW+Y%fXI*M{;i@L+`PQa-#>`4au-|0NQ}DFxn)#qnYG36f&Gmo6?M(m zqOxSPr|NDqgz^apKo~WEfvc75r(q3SK{kaHg5>^2ckXi$-&S8Q_oocQpJd%j8+t!T4>h1ji(hfP3E$392{t_>(Xa)B+jG6@Wzt!DPbxKuSOMgw6xrcD%zWEhRYNolpi>FW#Pr^Vwr%p@G#yF_t$HfY&M&j@fC_ilMmMAM5;t}T;afBgEBa^z!QxR^9l}|L< zgQh@AC}Lbs-^ulLw`p-8{*waF2%N#ZE6FDrfYJGFRjwR5C%%>}zPKgC+9>I zwh#=%z1|AAiDmcOTw_Bh(5cO$x>a1+6f(&@cAXne_H@@c10UG5#%Tx%X%KJAuHeRP_hs?lzj^U`b}4DFMEvszfRQAu{o7OK?n_fM2X|Q{eB73dh=*x znE~SxE#kS{*q3_%PtNq>7nJAa&rY|vZL<^?VH+4C*g+Nx<|^w62EJ$|Wo3_?Mr>~} z|Ap521oFo)Pf3ghzZK3QGc%KCB`G1{v)iXn-5JC^=>4K#bLKoCLTYSoo+MAJN$||$ zWMwsNqhwg>Qjn8n+_>oKzycfk(OeE2t3EQ?FT5`bJ!8p*qJj77=q>1>(Xvv|F?UFG zQ1w<1OGn0twikK)!#I2YlRuIMb|DFmupvE0 zZ~7i3B_*kr-mcn&d`{-ht5why?{%G67K{A!$)qsAVmuJxKdXAwHrq7lq}Lm4GwIp=8lyb-*W$_;XdZ+!4b2+pPuF2KL{vwuaifQ%{Bu{+MIq1nh(bsF|OvJu3=oE*`JnI>f$j3 z#XAT{|)KTTm7^ZQQ{vJJizbxXfNTr7%oAt!Y zRWGvt{ayc_pMU+!5Hi;My-s83&+=^W&1aDBb8KE!DBZp5H!unK{-qye;Sxg?;KTIg zDf_+ttM*g)djfw-VSEK}DA`BfaQ}QC=Ia!|$@wGT~tet|8 zwE=Yf;Z03SWBgB5|F;kIVLpBA1fJ`bKfQfvl_il4G-5C6S;3F*48YT2*>itW2^cAM zc1SWRre`Q}Vcrg7TgC>ux)(vGv{FUU_YaXzf$KIEV5;yOQOzy~-?%B|q!hNeXbtRw zX!zYXt~NF{(tW5(i2A20vQ|t0=5Jj^_naGB?niz}dNxpQl@1sYDp^@s3yYpNZ&p@r zdPeF-~rex2`F!zW;DZ zD*7oHi>Hv0KM8vE?ALF9QzHRMx(2$nkARrc`4ne6c@M@+T3SPb!{60Xq`UzkQ zUftDw{o6lRa5%0aLwH=i&VrGSma!Z6&e1JW6MwRm1Cn7uMd#9di}a6eR7U>+u1BpI6f7%+Y~d zU1jGM5*np~opwBX#bSYhnjHB#QSr&QZ+9L&er%MgKFWE#H7)3$g9H-F6a{$21=Vvs zhM*u_VD>PT(C2(!wesbJ2-x@VKvngF=l*U}Q+W2`K;@->Zv81cpHH98?GS7|=b!WP z^6D&Yf3r3539srE8p&DA-5MdmS!G-jq8#VBSpctHUKr5QN?0tk(CO+jT0Ajsp+)LX zpFVB7MtN+TUx6&5=4DREQFilIcz?{?FMDe0F_@n^ia_V(Jpe;BM@1n#ZOo?H`uv}? z0cKQ>6?{(;Z)pN5^zFNMCFR|aM+|5~!;`uN6mC=*7yi2Ae|_=k6Si9{Xh6m80rCxg zWMFOWxNh+rEiiB?zTiAuU_<_dtC$mL!H)0s0dLVm zUNFl>im3b~(sI3`diHE0RWqG_Jw%_O5G;a(40wVNc+*_i{3?adX@FZf6@GDA{A6P4 zuY7blA6Qsu7>VD{{rx0$WKmB{PW~xd6*kZShIx=Ov=odoANVOxm&SNL1?wbPDQfAQN8I~8X~L3nkIwnHKFSAq%ToO#~9ng%3>=Zs|D-JPhji@=;*$Nu9*|He{ zd|;0E93xmsr#S_T4YbuoQ8BS{wGUfp*=vc}k+N?n)}P+R=h;V(R_RAv5Vd@@=h&2L zzw(?gZRRLnb%tIkBX%vQDeJX!hSnyP#Fb_4bS&71~`%OMKSu3~ewx2vav}UYl-K z)$zj_IF!u!^5D*#KhOVHe`GJ~Fmq3vMzCF%+5q?T+;3-B-{|W~;${M%)Q_@mLhQ=M z`JuCZ_%sw(#!Y@v{J}mc*7DM&FVhk=2j#>X=dC^Y5?{2v{dx`;+5j%Q58JoK409RZ z<(yPmhm5x(h{`9?Rckp+9wn~L+OE3;EYqD0@eyKHxTpnbgv$Hbc-I!C=MUFYkGn^+ zz#M(_SB};^s6HH!<0nN#WMtfTo%1>j{WxXfe%A1`9TUh*LB6MdW~Q6UT->Q)y#%9o z=6%^K`LyWFOl~!U2Q!%rT*_jTjTj=yGRIswn z_fckk=;~Y;{VIe?)C-V1_AV%>fL9s~{MrJ+G+f!zGRweo$F$9n{ga4k;k=vO5ql1F zEZA0Ks(+rFj~`WWGydz>TU)6^&Wq;et9M)Rz1jW-Zacjq_B}TwS3kyoKW|%&&q*IS z9em+>3oFj&9*4UU#N`2HRGZ}CBgxHe&8adbo+r8~c?H!1P_u~M96gp8uhJp6l6#c{ zOJI9>t88(fhQXt&vwB1O4ONE`hr@>ntbI#VCFZ!70E4miBSs=Lw;RR|2U!nCI1er{ zwadV4DyC0LB8I7UHiwp5WvXTmXREwkL>QBi>wpTxKxsDchRR3Mfq$y6gaV2@a_!#UUbB*x7L}du_@xLd zbnI2b{lLDL)fM7f+ZbS*~X8Y zZ=-}i^yJjq{`T=Fh2MYtWG<39sJw)h`dH8uH$%Wrs>@ss@O<)Id#DH=BD39LY@=mj z+UvLqS2w6|j8duCP>QmjCKkF8*+g|~E&%Rnz*JFtO+HK5XjO=#pDcq*(TIl7$Ysxi zoeSBRPbdZ9T%CBjmc2P9IFm0egPq?!PSLK7xI`vqp*=%GuSr3;p%c*)Y)f2rt_N{) zc){@c7gjhKxjrZq^{hKj6I}J-!VNM${90-W+RZ1ZH!({6IYp34j75un)6L0%cZM z%WOD2&b80vdm(=O>~BY|{#bF5h>@6+F2>VWKOBo#j$oH|mhA45PL-_N{2Y<1pKHjs zIhfprpWtqe`BCRIKii7HSWADL&yEl8%|guMmOF}b2Zmw>_CuCt>4l-C!>pyWeNUh_ zEXg97TWy9g?HZc`;NDRUOaE}>5&b)@*9ri@!*6Hi3*e3bp3sFB)l}~pxyN*>NWN2E=kck0aj*hi8+2y<2YXL2lelKXL7Vjit`zUlge*GoGoz(H~od7 zCES>RW0us~cSHLjL5U{XZn%=EX}HRb>D!$uI*6SGJoRP*{hZeoGCV3%=gk4+aj;wk zT42(!>SB_(WC&S~xNHA_#nFNFdC2@GNcH{q?^ggNvUs<^aO)l8N` zA>+>oJ{k0zo`~xP_3<3rogwav7e}d#1|~~j1X@FGZth1=DAc`lXuIqMV8rsreu{|T z0ke;YI{}_{`hLH{`(I&(jX!_RpWA+7d$M%`RgjrzKln=1WWH;<&;geyM15ZGz;6B0 zrAyPR_c-5!Tv0T~;xN-l%h^`^GOkaLjwWj=rl@`6+&QN*xAUThtGsaqOP$SA&dwwA zrDZnGlWDDMUjopLASe^WZNI(i6Mu$cX#lKy-^6^85Jl4^d%WUd*Ve<6Qt)nNED|1`?7lR8X)=OwV(-o3GGyof)ru z*_9!$S>?B=C`0G5JI|s<7J(pl+0=zMkvLV*Mdm<7FbzB03kdHm(SvNyWv0hVROR>R zb_CEFHw4qM*yt34S#SR6G$;*C7J9N(Son^hv-u`Fpg~U>IS)ARkLgaF$aaRB{R9#IibW6O zKevR)oyG}M4Db7oeSggogD-Uf4|7U>Yrprnz;RF-UnxHr%+7h9lWEhuEs{hWOdYK7 z5)yW1GzUwu5@d&lX{9fFo(yD{)=N{V{z~N~Xs{g%tTk^{lRQ7n+dN#J#Pc^tpzY0K z-J0HZ9nK}kzHlGk9I^@3F?1Wuk{*ov$`43$CZFvmjeeE_KI4B4Zy5ym`RSJYx}>OX zswki{gV~vK89!Zr^zbvL`PSk}r@?RU69qLIt=vhf1?p6W@T1kr_6~TnGPY|8HI7Te z10Kx>(YDo@m>|ID4JW#$B!e($Fw?TrC9pLWrrx;hxuq{T@#gYWj6>Uz`Ye5v81<8N z`d6Fe&FeD}`V6Gy!;6f=Ug#)xCMH3z^4YDmXxQPSKHGk-w{O`~^|f^{WEmt*mwqZi z*R1wv3sfT@vk79%Qr*^bopWu<8Nc)KhBz0h&WixbhW%KT*9U)2N@DyqKu5Z_$Ho$bD%<+q5ld8Tc{t|GRh686I{l2bu#&(pCGgc&X9 zgV_ew2TR^vw7GD&j;?78l2BhykIETRqW--$z&#k9+A{ld5vJ2 zn__*AVwz09!%M|?eR{KXqG>fheE6`|3_CC=EG&!}c@9u>dRCDqHf2BDLX+ zerqnis=wcyRmR-gT*uvQ*)o!r!{c$B`vJFm4;t4l7tbG!dG54OX3|ByHiH-bE^S-& zCr+j6W?tEXn{K&%Nff|EFT(ey93MP*fQZ;~8c)_O;+C)ZoRdcm2hlHdGUWW^Ud)XH zm-3C!6VrKzq}LvD?iCkC(DAyZ6>c@d_N4ZaK?vwV*3#uiO$V)LVbgjezVgJD^=k1D zqFy-wE^_zCirjx*N9BZD{wN#Tjl1vvuScit1Dr5CL8^}alb2V2AZsa~-ihl@@LD@y(lKx|J?RbD0wybYS7U5Fil;M{R-iLAr{1V$kHF6JG1B6uS9FiLO`D zbPLa%IkTwyI|9&Q?xSOfyd@MXqXoGE(qmSe8-PTrRH`@TWyTDx0@RaIip`lUAECxqfIz3yYVa8d^%iUT3f z9bQmaC{jMNt8cm+sOvOX+}(r{GoER%AEl0Lb{-S7?|TZ0T_41Q2br``-fMwJ7o($; zT0|7f=AElAQEK@nCnwJA(B5I|RgdT9LT|&~q*I+A%9i@o<4Fqv3PmBiucwjIz>f+@ zLd6tEAEEsJx>`qHP8~mjiB&|4iuUn0FL#jGvf@LC2hiq1(=l39M(r+8vrZK@3x!uB zNoCac2_sk?&+VrLmBhZG8Hc+BC4gM3bZCRQ;nA4YW&mvf)Jrx;ya78LB?sOC>i_hg zFtJPx)~nlSTQ1BBZz8QUark|XzMRwS?1E6N;|D-JNCE4i3Nh9gk-O6*J!o=YbIbSl zHrl1Rpt=LgT=RTpl!e9p{5VmG^z?@5GNTTNJ`-{|%a zk{O+U*AaJ)g+*Al0v@$=y5#(=R*$pv-2H9o^{7`(vy=5p>d0oN3|pG+Uq~I-HkE;9 zEe#BBewrdt__G-EZo_-o-B!mAY|3Ejr2tf0aNzrJgg@? zq>nV%KfRFa&H7t;RCdffBHam(i{^}MBxfHk2T+H4f-(SfRH6|9UmLzP@fOKP63>hG z45X;(SDHkDIT~n1v?qKk*c-r%`v^k8=d@E%v7O0q=MgK0tpDD+{L{qztfGPyY;5uX z9bhfLR>w)(gyQ!Qvn>c%72l{}7I(sgq}0zaG2weynVETTYE@sNszHI0RIB|573!up z{S(#1`~Q*?|LdImT{r*ss~sgV!AtqT=v}wQGx>t6iSYceq~=U(6!%b$5c7$Eci$Eo z^plK}xG#&|Ix{g5vstyiMEl=3*#GZ;0gieS1#ZLzySE3&d}mEpKaxiSpvS*q^Z5Wu zkWp^qQ{`bTeaL#$8*gDy=^A@p3*Ovy@M=9P%DkA6}gIa)dR0zkh^A zbytUmo@s0z;^o)QivXL3XjP*v?&Co%nN7T)y!l(t{ClAN&v#?qO#yrfiz+yW`_dTt zcV<}3$M&L40{+(@=fD3#0e^~%QX79Gc#-<&%TyEwuTiT@+DU{UARrK0g72e7M(aCJ zN^<1x%oW=H_b2>%ytPNx9EujWJm@vZYjGfSmrcJ?%LjllI}R4TpPE;N`^PrsMxlUV zAy`mw9pFEDpeL((tfl4poTR0r645M-?QbEO)ne?wY2n-rg5S1Y{=90=0WbaczVNr& zrUOB}NG8DW<&s?*A&yqHU>3o}Ijf;*PLp<$BA;SX=@+I}tmgx0UFEY&V@-{`K^O{j z(#WQ!FIqd-kl(9fcDeBR&T))u;BTM%--prqK?2Ax#kQOmpFaIj=;dXqKj^es@|Jqz zLRZr4AKW*n2WjC&=4XFh2&!ZS5KyK|0JXKKU8+iLCA?HZM#g}KVNqDPX1iFWdN0&y znEIEd@qam=kN}|YR_)LNg72c|Awk84A(y(Y9#3GTMK1u zYc-9+XF2~xhpS}K2}<(Rb1ys86|{e1oC&$dzJ9B31aM322=YfNZmQjD$6qv$63lQS zXgSN&fsOlgx(-xWQc}`po%Oe7=RX1p_~YO^1q+J;AXd|@diPboT?K&&A2E}s#~Y6Lm9Js#eHN390g>NbSqz0}896?Jp-*2F%>4_#1;SmHFaDLY_$ zpsaj78o9jB3hcv0Fg*0Bi)t+)@;Ue{GGC0jnCJY24A_)}iK zrcGq?)e*@2t=+ch-gNn_VZ_TXHzko1nSGvdCcR7-VQnNmvz8d{fq|hG`zTgcdxDrw z)BB5*xpL#u-@!;=>chSG(AczRLpq*zGw=d?c6Uv>lj&Ojjh}| zaNOA_5^j++681Q7g)4*Ytu1qfru@?*TXOmE@~TmfZl}@#33clqBg zy0>6LyfhH#!b8^H{c~Ks$0*!F26|Ep3vU9H`%e*(uH#>n-iL;MNgJgimXMRnI&ZMX z#wok0X}r`N@6hZSf6kfv*N`260bHj$qNQ+2J|ZQQagGOZvi=p99HG^lEB)6SzIUt3 zEp9S8#EOGnQ)K#`RW4V~!eI6rnz>;_GlNYx*10)UBiXO{BVT0Wv}PKpWUOL$bfMUz z*2cJ=@iAuxleg3Ao!Lg$**Mb~oyU)xj>c$tnfgE(aY++>kQo&piBVtLDRQqDhToDu zdHc?t#;InoZ3W)C`*;bv)h^<)q5vQKe!6a|OIhe+QG2WjwZZE~JX($yC>12vO9C1+ z)g(3Y%GPLf%PqsFW~LiKREcZ}bS!EaEWuBg;KuADFtkEdQNirMEn+TqcJjf1ewIhdD@rAdB!Ey#W-qgn_`{xDFlW}$5`_jL9BWH2O95kW}H*?uA2`&=PtvTOWk9DB02p4|qREheDpF8i#F2Q((wR!DW@_ zQwqSbaq0u@;UpETinvKy8#|Pp$YEz;v5g^&XLogpg!Nj@7oKgq#n`%?I?%9q6dB(l zV;sH2)}IhWUVLe6OA(&&m}fpV+hX+Zp=nzK&^S@T1aqFJzoX*+Xv19fFlA+*O$6V) zdGkIc1*O2I-h7>bT%E$LZLT~y3cX>FM1=<3My>mEbJky(mHVNA!JiH@ZO(%|0}F_y zBPhay)XK89vpB!DOgi9Uy8!H8rUf*|K5N|Lcv|=;*v(uS$R-M6(mY&yY__~@0-=4? z{pNCVa47b0<-Gf2NL_bE=cMUG)NFIvYI-w>geX_G;q@BYC5MRa1U^!92QsJS(Tjo? zZI#I+Vs==q#U5RQ zJ|8Y?wDvT<7aA6-Y8Q?mWSA~R!Wx$k>_UbXy!O|Zr@vozkFvn`*}i|l8z+P_a4s@{ zSiCcEt|J6*6%W~b`~jMZ>EpRJ=VrZ-=3{CObfty-@h*djp9>k&y-d_+e#3RaVO_am z|H@YR55hQe0Pc!}G6K4TTHg?LzCR8^E6l0LVl)q_%l9(C^JjCJzj3 zTu7qA9K9l>hxsoqh9b8eo=^iB2)2{}+tF1d*8{t!d`QdT)G#EE#HigBk_N%l$-p=F zV0ViId}LaNA)W`{F6=yOlYw{I=t5(kvfr5bbUMw}id{&EFT5enWiTgw(v`F}H85+~ zrKm}B@`m2Tq}9t0kQMmATDVz7+9?jXy@xZYFD8Sch@bQ)lO1zjz9OuS8*R?spZk7T-wY6oheXR<;5phcm3E9yS?8^(Hn-3%M zvCBKyKhCl@EfJ?HcSM{=ac0Qe>>aEp8r=Oos74Gf1UA8)7v4$Vp(~u*1hrxz%KM}{{)M+vQ_IuGdlRl=JyyRNq)SV~QD-Zs2P4S$ zNdrYDqaoyqQng|Umcy&Yts;X?qW~SdUdO79^G5D&h}XqRIfdGcSW?vXp~uXTMMfb} zORZ>z5)lpL)<`0<#|ayyMQh1<#cy%?8Fve;fxa7XIy!(}F+g zGh7)|5b9;=)rMkBy z!D{Q}WKP~;F+C^ZihhI@yIERD&DsgJrD%OoL^-n#C9Xv~IXBP<5rNjYeDBFT+^XVg zk5M5Y@#JYiEX%eu?lt8~z-9%`vz(ud5NwI-9OKSc9V*=>?G&0#U&6$n&%+KH8qi!A z!LbSf%Y(vnMJ>I{tW0`-w^4LN9YaV0A8FRldOZu8^A!CdVz ztGHu6YadtT4ovV`-QKUt>{k)McBW&gOIcaD#Wy>ceMH_JQq1u?GGKSM!~Fq+W2d`v zRm1b(&}ghaVtJHru=|yn)WYcgu?Nb{3Ho1VJ~MV%n>+iuot!IAn0@v0DzDL9E((>BsKSQ`sP5cDf^-wqtL)gk1jpV#I&02Dsa9N*=R`MA`IFk zo;6{npRSuPn(EWflTejJESkG+RWV**VTiMD4V|`j2UhKZHM=$P-|!&2Hrv#Q9@0RxEG=?IG6|c1Moqxu?-}#j=W_EnR4^Wk6SqcI?1goI@(*ln-Y36EwONl ze8_z(P(?f#le-sX%DkWd1bFzg+9@BRRy!Mx1}?YytMnz9zNZj&Jv*Nblnh~PZvtju zqZV%X&m(RZi=mrVnxWXY#f7P6-bVN3_WWSupxG=fD-+O^@aRW)o}268>&?*|x+8yW z&cAN(jy%Nk`mFL%BW2ciy&>?U@m&UI=4c!0Pa6LQhWOMU{}{pi0Dz=K?$G-W)YQ(t z%xB|ou>a@2UrpG04@|(eKDyZs4Bx34uY|Xs#LI~f1H_6e;=l`9W1?W8PqC|#T2}V` zI8_TKuI_P3ZmjJccV#kdeyLB3zsf+LKhA-nhzxP+R_G}f-sNgy;ojz!SA;FIKyh`V zhM9Qw>D_J{Y-6KYipK7kz;@{aRB;P;!or)&Zc1S2VnSRHQyYQDU9V*o61W;FsE?7F zl)Bb_ytuGgy)EX7th=K`GMl4mq@=^_kgt7r5bhJ#<=9gy&?AcbK{+`v`@B!bF|)0c=7vS^3{T{ zts&lxLNuUnhm=4e*}bCbvI2l!<*R)+uYz1_kRg%XTMW^1rJFq^FFGt5Zpub#%k%rk zH}fbiBxq3VwypqMn^0hQXd*cN^iFdBlfRwLP61jeyVR>__xrCq+?yf<+!&)HF}OWo z9jOmJeOIsj$`@7K8wBGS!2!9_kqwGngF9H(9%Gn>##8PSTyt6T;n6jBDe_TcF@ir2 z@~KdYNVSU3;Pp&uYHCh(`X3q>-pkdm>7;sUVf6T>M`e-i`mFGK&>~4r9?-Y!+gG<@O zJey+)E=yTd#=;Kh&<0FL_t1pe1ee(B>rClLRH9OV*(wUOWU5`&U`|wi=;J@VB)?bR zBaz@GU%m$>4PZX{Y1M0Q-7`D-3vG_=k5|oKyVXA6U`Ru`sQoA+Awh&y^4X_ASwyj< z`oStjz7fbARarvv7)YRQD;ln(*ezL`t!CFHFq9|qNl4(V2ZRkop3MhVRwDbvb%2z=>#}GL z{UbzyZzMCAVt@vDAZ*_gxgUfvB~%dAWV>x@2Aaxx8w0)eTPeVr$7MvJAp=`K%=4f~ zzqO#lTcGuQ8VJ~;vIxaY-D0`@jT1$IBcAM07Ss{{e(A|!RKVEKv|2I`S_%e%t&vxs z(EiF_;IJ~#&B-Q@N?UGTtcv{0zCrcKb4K&ZBif(+nDy=Pd3ao8yrFAVOM%EE!dT6i z_5b7SEugCI+U-$6P(lPmLXeh_4gqN-MFb4kY+%zZ4bt5WBGQct2piaRcSYA zuDkXd-}jw!&;R}3bH~`jp=0>#wbt{j`OIg|qMq(`j^-It_gGBNHk)mXr>P33yB>skp4cWPGq%h1Gxck$I6Qv_>uMGe}o+@eXS zXOdFcd(_}|=A8!*sB0t6g|p18ug@x_65F0=#@?zOTe`$I?kc+4#)qgyjU7GO*LeN< zaTqCQ?9BBpXBL_pxy91AUt38w$ik^Tz|j$5??#E1{Ck@^i$K49SEZ*=OZX*Z>eHtu zIjo7TgXD(e%wU5+jtd z!=Kv}AY5vSEUbUOIj#lTV-@l7Q53F{l8^k^O&2R~-0 zLZ}f~ve`@a10aZjL8Bf`!-;0Z&nr13#=Mzk!WNrJv&VbPF6P&5myUJ$QGN z08s!^^L~{UkSa$=zvmmR^l!e=)?U+MU;{$ISPx}ElX~k7Zi>GTz`Et*Bd-}q{DL>O$|>aRS9R(E$7GUT`o@dX&3g|X z%#E}KQMt5Oru-+rg+QW72hAfR8-Oa$G~c`_IgEK17#517$OW;ref_yc`M8quFUB)! zJNn}S9V~>80I6y6y1kPrp^0@fPKc8eIf+Xp;*2<@XeS|*$WX*0@E&Ho%Pai1(sF^~ zY*4$x(n@1p&w(m+=sBo3GG1TK(DQh7$YqYYkN;5-{d>Fnj3Wbo_tW2E=NEdH^TK*TW>kzBV9H}&3i5~+>#p}3Dub1U=fCfXp zuP^#~0~ZpfsVFb!79$F|Q=|ur)X>v6xPK2^f>%D3@WNXw13J22WeWjBJOPjgpe>6l zy%fVy`)0h-ob!Bd-w5EpKBE{8|AB*mlUbpkWw0}u#K-RHMzwV?CRkvB#905jioF-> z@6&rrbm2aHYSVOf?efiZ1n3b;vW{L!bZrv}gX1$%E2il~CXK(}lO>EsKtSz)=~E97 zn3AeKE&DIh3X)I>>N;ZLrDW=Q|E({hgw+{{>81LL_(-*XmKU z!Jix1Kxx-<#FtX;hEE(ZD1RAUjtTwi=Kc*Z{P8ylUfkcnIjWzJS19>ENN!*m(f z0zx@RmgA^Y$&9ZU8|K}Bp_4~x@n_S^-;?gYpNQrFs72${8_0(JakX)T_mN&9m~M4= ztcZ22C-vSQiepIVkhKr_Kiv&@mV6XV;jpVj=Z}S`;7vwJ$>4^Xp`l^^q_`uFPux93 z(c^P+^KXubzuJTTGc5c&K-}>8K?FAV2aylh|6s71wxWY>MdR(ypAE(l=(Uj&L%5^J zPaY9`mn6A_|EGug<8&G2IAFqPM6sR!NjYJ`eDNZ*&$GRwqq6(bAcw&8G1_@jNl|g! zyW10g1eO2(YW{UoKn)qJ2-^ui^-(K=4?o*0RaIJIV&cI}soNnDil(jiHiQ6+B?I|U z`>m_xDxd5>7LUNvKmpEI_fuc}&jrR;f<|0?po~sXaM;z76oWkr8_T~$Lik&Cob-k{ z>!;(uihm8lzvIGx8DdWw!K?4H?gxtm>XvsDGEgAz8fb0-An*OgHGLzD@ftu_bY(X? zVy&-W{vS{BZ$!o!)sRuC&-PbC1_9Q_RILI~DKte#WPLX9%aD132ENe`bGe&{|A7Ge z@ALfixBpgtE<;Gj$Rq;ksOO-$=n?(rFlm|gGMlhb5%f$zgAdmhcEwvQ|AM@a`H$cB z2FRj7Q;|%B!=IxF3*G190`{1>D;|h>D*$f?2981lg0fIX8iEUS*Mi@CpOKNM7x3Rw zvHy~q{PFHoGyIBJOa5*^FRPPKigc85mH@$*K~(fD5Mh4AWKyZ3wrzdd@e>`x_azM* z-0w?vwq|qaZnhC1$pTb?sY^rtUoy`B^HWGFf>8KMSikJAhBOUyatRPAIZ}m&G2%=K zAg^Ehfc@u>0u6Rn*@{)QVjw?=&e+M;~|X5yC8(7vOb_$ zM12Z3P+nRZ)ny|Og(i&Eq+OFIBmk9bHrqaDkkih&GP8@}^wEu6pa}Upd16I45Z& zP;e_&+`B&qJebIMTw|4-onYVQfbd1oF^Gt?bYuORe&0CqW>dcw>q)$~ag7k@xB_K+ zjjW&T?te}6e_ze?Gq^xn8N>S-2D5% z6EBd%!6MgQBDHQ|FQpK43({lXxyS5xgYZ#S*LVI3>Yc=9d&`-H#A>GAM2h}!(G~CF zPVWaXr8i`<2=0BvqGJY6xAgFJKe|=Dd1(@;b4;lY*B@`y9~P{P)#m3z@|*(l3J-^` z!@RMA!B2vL=;^nQJ#TI4Xwd)HzgGrW&%IR*lHM@U{r@<44C7H#p2a+iGKRFo|MHj7 z0enpIJDz-1lKx*F39gh;f(`C^tW7oTk2mn=d-(tOr$huT4H_iE>_uwu|Br)>h`?Q! zgyf455}^Gre?H_=66kR9p-)1D|H~u6m3+Dx!Ce~>sk!{GKQRqDU!X(*xjp0m^}4ys z7{HCi9C*+hL_QSM2hD&Ve_PFS4((R5CqVw4Mw$obwG z0t_`)I4d$#0v)}5D+fEY@K-Lmc)*<4>0*rH{xbVZz*^`9G6a^O2Js3S$j9XXlpPgg z7!`YS(mme+R6%3a1X_FaINNffV)bM@(R@+SH7oi=0T?%v^q~~v7NCFF+l9ni-spWR5|5*?e@Ei0UL>PVakB1W4A+BY4 z55;3WOJq1caCfBuS0{sH-mr7fB)1(3dvVxbC>0dBd^BNQ$>f8z7OSRRctE6|cJW== zQ@`PCjn#g;{#eEJ)D{OW?|~hSl_fo%d|m*0@|=;Co67*$kMPTSF>gu*zqb(!`R$FC zlWbmC-4PmpwG-sVS}^I3+5X@-9%lFL0gvHx#(cA(>?C~g3$A@Fis3MC4Cruvy^7{P z4q~4hJ{slkl7D`dOXeLA8Ttj(O4gafd{`lHE)@A2G&#N<;r93@hJg@~$_0D)uaFv} z9>{XVgjG*4C-X%*Xk-B8!_K`CgHgns@nTo>(psI_5F;-rcBMexGam!)B`-j6C?ZqP{RDCB)aCWtf6QvS&Se=!D8cGa)P;<1c^f-DO-L`B1ccbt=VBWj!y`au zodnE6NT*ztQ>sODQj&!5-`!30OIGFFE!5k;u_Z3IR?+Jpv-w)6u+)+aVG*U|)R*;|N z@LTyuV#I*i=lp#*j3u@S$X{7#R9abr!3Qw|t{ZMi)rTXR1-aHhHE*fyRW%Ebajy&H z{__-2MNI^@h`k!dpE=VUHPjofcd4)SfX0ai2L;JO%5PjJ6HvY=i<@*k-m)yv!=o0P z95L(u^nGdYogBZ9rd4%&KbozK{n$72{>nTuaXud^h$op723#m0gzCW%nBVm%Hx8g{ zk2G&FV-mZ)Y~(Khs*)whnYIThH?ZdqzP{=7j{OPjHu-rO5#3#f*m^cWcQ^aK&_0&PJG?ai42x)4kZb`>1e+Q;;Eqij4o~-xoSApbN#?AmnjK0}dh=9S zOJTE5On>}?Bti=*?Ix@ z+#KO)57cQog^(i!6NYVS!C#>F-)F{|um`z=^!6Rc@#K@MB~CBfAf9wEpF+y3fMGkt z@A-OHbnDS*Z=!Vqm8h$RlPOB!`t)E(S#;e>$9T|83L^_k<>(|NPtIXN2ds@ta280V zeQx*}kDOqSE;L1jSviC8;44t~*4!NiBfIx=l$b_;9y5oNb=|Hkt8?R4&3f-m{7GsS z4L0Q*yCao$5v&-nf{qfwz*t#I`Hij7jntt&-U^G0`!&}2;JAbw`bH7*sXImG6YNvS zKfOBeR#kX>&~7SKN|L}aXd5)D?PTIkLf0NXMa+l*C7N4Eswz4PaZh&!)Qo7csoz;4Y(P21m$15HqEe3M_a z787~H@@0a!m#5$^@p-j2@Z|oSMx3s`94$xnIPZj|A4bJ=_Ye+mI=ntHqW_Pj_8L$rLMiljBRe@uyV`-Rro6ntcK+SFdpfoQP|1j#FK^{Sq~ z8!L$&D>iUrbe(CqeCc=Vuy1ckC*kt^I1vQ9v3A4h7oD-#zJTWFWHcXS`72*-<_?v} zsD_X(_Z_52k4WW9AjF6hM&(Am^&3b+EUSO|tuF)QRxL}isQMpUHq{VuC@1k-zAK2c ziNp#3;)V&lJ)p66tOhYUjnI=q$>{F`YobL%>Nr{sb)Z{o@ordX%)X$qudNto&iXT8 z8yc1bC>Lpqie^qd8|a(K$$tI0BP}*}E3LMvaCv90ReO59mnog?d)q*z&-3j;11^OxSV0*qbvean1}y0y8Im)mV-@yw5pt1gp{Ix*?(H^QQ%8itI!1!KwOsGz5M7;Vv8q9mU+U_>KSnISsyZWBIpq_v&$a%J_<*e3+9rAZ) zV%ky#E-Z#7<`B@9qca>qwCcSS?m>i42p3xxI731Ip%>Uo|}XoTi1+ z!a?Tn-T86FKdvqk*aRRSNYnbA#lqIKjEk~(1Y#*q*fq)!S%EJ54WzmtGWBwZZBAeM zKM;0=6IY(>Qvj!H7hvWtQv99gdaIXzJ{L4rjfKA95w4E_iz5)zi%(if_v&vHKH?AC z76;C-iXbQDF@ri&aqIh6UtgWmle+Up-=x5zWK^mfIao(>+O#Eb8+16+mFK{5JMC`Q zQ|6?L{m{S{Ui$R55Ia)d6@w~vW2Qfs$mV)DG9>a^`6!THl5%93s zHD2hzo7O+QyFZkE_3LEa85}Ut4xAy-lp31mcVm>r%mUua`)hLONWVK=M^@^ndq?2> ziv0n4O5n^}wZdr*JGGp_7P(CN+$0tNC| z;-Ht@ynQzT$h=Kz(#%*`)gSV+`r8u%dc1i;4x?hyVhCM)?Np+&*R-eSm1nf^#h1kM z3nRMmWfh;KOcnE^A%IJU>7ZDa!MyC2hZ$ZBUn*oe$Gkm>USt$YF?$zeUQ14i38DvZj6H{FjAb$*=Fz>0~b>dE&Aq&p^Zsb3o8k!Yy`Pk zyE>4$tgh^;8-FP3`iRvGEL{bM_-lJWYg#?*6*Nd!&lBX^*`SjyO*Kht+V_G&eRa=6 zaOsl}zfK!Fv~jG_tI>o#x13m~{M~Rj%xTIYt!bes1a<*?|MMwpL(BXy*j`u`@X&0v ztlgW(+mj|BeoxD1GfQJXtg5E|6MyBoM3oILDT8Yp8$%*9uz4L=amc9M<<+H8z+ zuV3-7u#gO5L0$H4vq=*E=6&rWrcB*>c6Y>K9t#1}HajEEy4|LR^?usx+GFA>xS+;y zbAFTBT!t>VV0j?jB&^x>)+R14{3|UP&MmfUo2>&Gr-#k73wO{+FNtvNf+GLBcF+A(EN14|%lp6>g_VGcagTqEaqr@UH{OcyY{E8SdNJ*?Z zu6psK0$bkdP|nzuA0`{RWWZMi@m+YqVYz*8cPwiGu~?+u&(J0@bdRTG1gE;MBbyF= zOMK~GGwta2JJ7B=zhAvbudX7^*U7PD^%A|DfVxS!`w^hy+GBYUw$j!-U58VTS5H&V zG^Umn4N|A-QuIh*x%>I7Q|arp=~TlBI#v}pw#zn#vh9~TE)654MEcynPaa&IZ;ze( zR;|X23>EjFA`eler`d8dtyG}KF0n|Jxa3Ru#^1yFIhEIRepna#1LDWp_fMvMwTe4r zQv{i}syjpC&8KZvW3&~*+FN3&FW4a0xn1Uf9dfx@J`1wLUg%jHdYt4T13*X)Ero|+ z{{$#0LD8$4=xGeGL$k6nW^Wm1tke|fwcg5n4JGhej1J=Mv(cpST+XYr5* zr7Qq>KoedWf;|8#7~<$gCk4pB-yd#_wc^~fW0y8yKqp~KqHh>)^wME0k`$N?7;SHS z)qey69K8KLn?pOXF7X>ulL8B1#Uxm2+qo)y^(sec%*koetG5wP3IhuDRk$~Jz}kUV z$hjJI#XkXD<{VCghwqRqrQQfTo2?*XeO0CY9vyu62SFTZ6H(7GcZWgDFNGz}T@jfZ zN(H4Ekg#=a-`fK?%Sbn|F9}nVBqBQBnvwDxs|5-*xg&D++aOUexMa*}v5U}LaT|DB z@#2f@cnh~f)3G)#Mm{720SL%+tKr;=mH(Ny-Swqcf4E*G{g2b(BG0Ar+gqgPkL+sY zzkZBN*YEWR-Z3RTi%quPLwhkKH`wb@h)>&PgANCBo6HJ3YsZN&GLPaE$9c3hdUH<1{YuKvu@#|8bFF{!}&-D!{y@R24It6V?{${v5je0 zmK}TLNNw|itNM$*9>~c;ByS^^ILzx3&q{5?WW3=Fv^6@nbo_L@yRV}LIPTs%*!h;e3S=SWO^`=|@C7wf|56BfMHl61W&+s*fWH3uwH1wW)s) z71|Ius~>Zkeh#1IYxCk5FBHR2$`eynRh0Q>v^68#+kQU~N^8}`axYW6o`=-7`8`|J zt(zqBlb&$X6Dg2DY7q7Y;;omqsd3!J6^A0|o+4GYpbg6K$5rioUa+VdIp-HoV7j`#+@c zm+n^~utLJpfLJe>;8{ufx7auW*gpziVI8GZeM?xnwV(Gkr&s2)eJ82NRzt04*w;Jo zoa$K&b=x5W?8oxYhl&uUhfO!**?&Jj&2$K+$XvImWJ!-u5d&obKZI^r4NhW7H zi{Ib=XbT6sUW8Ql%Dan%`=+F}*K=N~%T;?@PUA+UA;^{MS!MB6@N+zKeq55eZE?fO zMCvSmT(k4Mgwl)5XMTYYuI59FrEcOYsc)4-CCbShmNGh?E`b78CZiP+HX_bD zy5@=aXv~GLZJFd6@dCRGO%NAPQG*BT2_YjP%xgD(;b@L!lsZ(9|Kb#~=ve5|s$(^1 zZswr-dKgI{u4Ac&k3KUqtQK{a7x&N5C*235kVlUrnQd{yzUehrhqVsJi|Xj7zWjnSZsYMN!Q75EQEF1o zRgvK5$Pm3tq@q)o%k}*e0nFIQbf&(ZB0Xi5~^~@*T((J`E1 zS56n3+rFS%1Dmm}4o<-CLF-H66R36ch-IxY5&ROOB`xN-k-#*KMamNGL)3c0t}AFR z^g3)p5clY=emf>I2=TeV_s;%T&JN&*7&T$x3waqD?Zg$;8Wc6Eb=p6qj+IGy=`jxw z21v?%0nx&(P-447)=`JGUt?t!V=bkKUo9Fh>_tAW)tLU&w}zDo1QSDwv}3=h5Z?YSb!*E_B}ct$ABeqOdZ_=}qj55knX^c~1ysgn+XZD_+>n(4&}wlTm~ z`PEgCyznjgsB(YCREit^6Q z`#l4AMxyzIMp?O%dmQa${X&G0U;_E4+08ZlmKw&2eeF)2Q4a+z1oqXr?K%l9RHb*0 zSxIP$YSvhkCEhS%J$oWsy)~JIAs$TB5kX;MGvk*pNRt2Hrn?C@Z}0H*;&;vmYzBA- z7Ss6wsnacBQf1k=4m^5bBc6MNj_V%*$Ygp_`H3FjXV z2-TF4YM+19EL>G{vRDmX0u13$`qo52e#v;baxn)fp#?z#=@b(>8(g2ZNk4_=Vhq>^ zH^&x;f1!-J5^1TRWQ`=w*I_ctM+jAW;E_MENe&U8a{0QN->X;E-_9~qX7+aTVW4wl zMRV3bje|K6l8MbHpT6@W0<7uqy8&Ahjb7=A;$CUZ_h#Bch&DyU9oFut_ zK0xVIBRfFy3w?b71pGFc=$>@&B2Q3xO+xQ|bBj+sRpeXDMkIyXHRjUywVmqd3Ta;( zz4KbV2P%*6CuI46Ozl6^9)oGTjUM{rK~>j4oAc>>*b*UkJzfbNf*7j^Vzy2|+Rp=pC%c|VjGH%(?JyKjSobcS-K#3&z- zsQgUE;2TvF+t-h*VdsRu{o3#7U9QDxT5%U>R#t>{0kD$8N$L8q@k+q0q1lxtw9Qo+ z^hVa(4i2$t9#s?b%*J8s-6W}OX~~ko#q&ClgXsdCxF4v${5qm!GZ5Wry43s5Vwp~% zjP-M?@p>Q@V)d9y5p}Sv+ zNur&K;X2W38b@YBuZ?8_2E?x?k1YKS!O~IYJN5={Qrz!#x@R;}zITY!u`ck?-yOol zsEQIjf_|A;(htW|gG>%EH@}}eASNzifwl>ff(d;_elMpm^sNwv5=&}cF5~|tb`wN3 z6&uL$T4@nDFXCE`rz8;;=w@b+=>cm*{$v1RlF-+}MLc)>es>-#COs*~{Tci@rTq)M z&4;tf^I2{?`bVNGjI7b3sy4c+ef{(NqV7owAvOOEg?lK&0@l%IFfNxpLsp)qdyvk^ z)Q^U5E$8W;*jtqlB+CSVV$rx10Nc7k=O+c1UQEIMCLgva@Cc?0MXA55&xs_fE@0uP>Zx@-E zov$&af9lhNZqXlXPIP+KRQGv4-qAsQPU1IO@T#S|GsgbY{Zsa;zj&P}!<_C@0ff5r z_(iF4ACGnYVO%}rYE{{b=X%}iQuE76x6aKIm4ouTdJMGPNw9BNr7V5{=^hQ1YcWSx zf;!Ve+Vb3G98=I>h;^GDz4Fmk6-129> z8mHgMY1z=YdYI}&MQlh*OZUrCPE>sUI1=r0wbxU`Em(FptE^|bPRbKZS>3yl59v#qv+*Yj3ef_Zc* zm|-0nA&qN9XI`T%7=d*-jeqW$xj^SM}H_SeD=Y-kzEF#1-T@ z8GJh$j%*6yYRJ?2oZCVyDdABsiD3CN!SlICi+DHuVgddNn8+B7C??Jb-Ng+AADkb^ zV&_-tj{bTm<~|CnYCfBm%XI}fqye1o0qnD0bsM!!qkSu$Kmstx8FxL2TADB}_5?mM zOVY}yx*W&=O2STq6EA4r6-Pa_U+FK0gg>7s&~){>)+v!-E>BS6C!g z{+{~rGJz{|vhXf5w>4YFg>K!;%9@(5L6MB$>>uOh}g0rz0TR}?ojc>Chg zSIlRKHj2}MO=IotmD7&U6C}<#+5iTG_VqU-g4_V?POG|IMte~2PX`$5 z+3OUdUQ3dbmdK=~{?y%QsdO=;1*}THtJwzn{nN;S&&vdm>NA7cnqzasT zqVwO@RTl=APA+Z=Zo~dD|D|c|bq#Ze$FSLL?Gp zN1VGz8Jsq|h+p;@xAAPaXfperv#K~iwzSo}~zwK33(=uvzLY3LInu20W#fpLa zI0aKoS7r0|f@7Kj{TcputWIAPcCc?!QUsM7i7 zyqcQ{>!ZtS3HSpAlpaoue_o}$9COaJg@;cmL`JN2t`H1D3&St6ywJbusrUVEK>P_T z>VVAOQKvlj9-A>$g1Mu3hmB=ZRMDF|V5&xV)S{{@-%8fnEfuC=%a-pvnc1};UJ;ab zE78d|qv|o6`CE81Wb%8mbI+K>nYCt{iSeHSb@_Ei)nw)VfS^8ruCj#)A&X|3P=z0t z^_Ed=x6jqqGoDAyqWA;r$Rca5gJ7_A@Fc z_xA3M+UFvOD!`)Y1q8ABfO;m$m_39+llM@8;2whlNl?enerF5`j=lCWSL3k8a)MX}){uVZyOXM5q`w#JT7cfZLbvzDZQ(l9TDfI-V(sb3q z?LvHF;`zPIi>)cQ(c9%Hf*w+1S%Tz~dT#rf+~}mXsb==$(ggU&o!$84FYGSnuA>OO zifh_6T`N!>jne8`N=mf$X^ZXgmlztLAy0I2wj1$o0aVtZf2`t!QlYqT|HTAw?t{Fw+9e%?Y)O84R`# zF<^&2l1CT@=pIn zJxwtP5NkWUy^JqS4`tU!ixo%5eH*M_brm?(S$cA?B7QyA@Tz^=b!`E?g{Do{c7<`U zK&!Sxvn7|fylD->#LJV`X9&6nStw%Cb6t`snB9I z!w+)|lB@T@5aX-jS`^?7TeQkd5#s|+D@Dp)Ue|;#)3-snHXtF~e(s85UGrj>QUP*c z&GvbR;$bsAU8yc~GX(!sRO#Uvu4D|tJ0PR+Vu>^HDkOIhbBj|O$h2=SeOaCo#Hcx@rpfU zdt(HjC`JT~m&_b13oG~ulVXw~`{3!qc_r>g;O2Oi&{jzWl9ab|7)I^y;ZdS~9rZRY_WlK3YCOhp5eOqGBLfhk>0_W@MID3y3 z^{+FkoUm%^g`{8vHrnA$e34mOA*+Hp;(ZNLle_cd$AGUiTsq)U-vhzv*F4qYv@wN>Fd%cyvd^9gk?1%1Y> z-hFGo-DSw*z}oGrua`at;#ZyGs_KXH6-!M%ejA$Q7qiL2$D1tq)=9qHofpY77abZ- z`psXmBvCHzo72fvA{YrzR!>7&vL*L6$4cXg4-r+=M~tok_mk+~PJg*>=k$!GmmQ<+ zxGR4D5{=f+Ub(r|9%f!6f;h z@xZsCLY?h;)7r~?{aE~Z<(ib-V`CI|L(00Ka}*p06bTf{GiyeiY1y#NWgzC8Qh&gHG%2f$_XL~wJg@sFuLB+pMkS>ru)xwI~CSqvZa2zB# z&SRN)wTC+u+0&_`^s%DN0n?x>WW#whbeO8GN69}tfhB$C)A|-*zglWg<=GdoJ2oBO zFU%5h!{wBVd{`X!{zUqbx6ys7-=(kOww>gSewB}|P%$+E!t{jcI?_awIW(q%XW>`d zOlR=3-+pYLcu0$1k=F?nFn0vEJ-Er%K>u{^D?Q0mTs$n_Dker6-R4x)OrL7(Xr_fY z%nf?EyPe%1YiRb2=ZG7EOYdi4+e}$cR)y5dwj{ZTa+up06}B%CHTs-T%6LWAbOsj& z6kF{jlvY!ToXq<1zZ>*yw{~8WOqZ38{bIc!7KC~Js4<|5k#4-@jpjGp#EXTH^_h^0 zR~oNDb}rUe{KTVe1|q-Q;O_8kV<8~Cv&!MHl%-53$1FeBbx#rl>9YLxdb=<85 zWRml8kx9H5X@gOl?pad?;{)J(w-Hsz&7MuHR+sPQ=SP;Yj!_Ta#M;%eB+FFew2jVf z*Ajc+*&dtKJy%t4G}5PePWiRwLQKiScZQ+YleqlHaZOMZEdkCi!tV7m87;tEmC1zw30bWsRdXO4SRr}=Yo4%v}C#}N-JcWE{%DC^>O?f z*)_;e^CO1~%Y@?J>8#M6QeW9vGvB5PygoxVR-iMGj9<`hUMvjwZ31(j2^T}aem>f? zGf{j*7PbB9J33R=AR_3Sp4p_>az1nP@6(m2@)6ekdb5q`3?~;zhY3s!IAv7Mvrc2k z5%f%)ZTZv^P435S1Nwe}D!JFxP=yh#(OD0tp)g0f?gRPpy(IHO;I+o8&-SJklaOTA zh4+EeG*sH>bfiGjS7vN`B~7?j+sTC{Jb}Br<%Q`yhA^Qspq7G7XT9t>X(wR7S**JP zEoUgLwn{~;Dl&JkywUhx#(=fX`8uoe`?$?)cB}F3$aqH)RYy_Q zyt>e&^SxmKMe1*lEmgamdpn0IIT`bxST3y*UMvPomvJ6KRCVyuq#Cx(#+c;ek7}f+ z5(fwq_v0BKeKSeAV5P@{h!B1@h<&o=(i}-Sv1z{z3_wk^Rd7ZM5)9}QoJO7C%@BW? zQAEkdt$Yg-`E|JqkQ2#o4-KY74cCWgllD-~A>+*Vu`ipjiG2$Zd#X1{ZT&Nd?~ttPxbI?` zXFpVs1u$iAHx90D<)mGh2R(izM;IyTmND=5lkU9mhF}Rk`d5_aLRLb>kd~Gdoi4Pq zvi+#WF&knbU|VZma1&{Iiaw%XQ$%#g#iw(ISin*iRHpi(H@#2x%2o6cHGB9YVK=&kK6}Ie7n{F4l{P;6C4#(%YnGjX!=vq{3nqQ z1MZj}8fipTZOe;h`n0eQq4zSMSd4bnOrU(Si03dn*&Hrs{qQ$VX<)dxU<4pa%Kz*- z%Gkr&d4o4HVk558zqg6Gi4d~d@EEXK>eZ{c&Ke0U9d3gPM| zUfETN>NREXlv`-tg99m3wP=Ghtzk4&-zQgE2m=EAIb9Y8j_jKP?;{e-RuF@zG3eZi{Ln(3+-s@TE zvlI!oj5S5-=_%HLIM<6R&l$T5SWjC_tz-7|zPP`~pr%=2j-0Ndwt`~l6UP4lk_tqS z89;V)wepD^e+mfyrPdR(q4?Jasr3Q1;AANN|1YZTsoE%`4&N!jTT3C)PMKMXmYEcS3v5pxbmwXbdG*X{T8q)POrYDrr*okOVnIR&Mh$WSx$Kf zH0Vxj+eFED6#ss^Q7kW5r%Fal##zS@&&sv(N)7%)HzdN=_b|bI-=5Y|(!+*WI{A$k z+fR2u)m(z!EBbJnc%VlsuT!Mc=(9RqU%TXoFj(r2b)>RGMju7I!ULU>1memTedn)DIbeALzENcF>}835Cb~4=@h<=I0?*KIvibr zw5pNLW)oL?A@MB=&(0!Q;>D(3!HM#uBq58L-2Pd3@c8-?HeSK@r57Q@I2G@&JYMmB zC3vDFcXuU$SN!(PEgL4K6w~d3-z&tY#=QHFH+%S4x{1m6Hvq5frOCD!&2OvEJnQ6( zU$*}o?6FebE5sLtHgPlxitgHi_f`JfJxVA(YT8c)^z34cFOj1Dn}3 zBi$_pb(?P3_Y1CkQW(zxd_f&98kOW}m zY&-%NC?%=4Um{*18_~s*$H2B?E0LfeCa@xa!79|9Jptu-Ly%a(@PoDcYb(_N(5y6m z`is4R-qgW<9W;jYEq;&bTN}>rR^{xwkkY|Tp2>chxDoOuQZqivD%u}CM>#!?f&3lF z+Wu%5V)kDMW=r6mf}I0kE(w4Id~ZY~k{4F6ccQ!&)p5Wi-5kKFzosd5hl8nwTtmzJYuI0KW zRofD`?!nb`SklumaoRy?;1f|jml;H5SKI9YT({~SJz1QZCVp00i=ThE_e?86;qwy^ zfF8El!1f8m*mW9uy_oGNGGFa9F!Ss=-4;9hO)rd;!&VKt^WdF9U`VfxLetH0U0ofU zYvJv>w<>6mH)2&OpVMtDadA=E;PL&cpZj4&!O5tA#0!s9TBD?jv z)!#SR0Q+D`H$xG@#c$G|(&izMpw_TmxJfuGi|G{ZHlUl^TXU;t#OaR$tWP)Xk81n) z@N?%hHO%&^*tfW`fA`R~Gwq;Ak1M+H+dFT}ptIFD112tL>d!EwXWM9K&_+Uj_6sTe zc@iS;CLsH~IWGV)+zY_vtoy;SaRKT-J8+eU2;AQnubTGS0inSV)t6UF(hY6W<4$ez zUAL^9GOjFUl&k^v7@E<9VMoweKzwD_!)vGZJorp~+8AiHuY0!{4SS9sB#Iv_uUyEdWW!lIn5G<}=d zADtejdtE_U-x>To0a!wDToCk$@Z|wo+6~K;)7>bq4ckj8Hdc(&@q3MXSC|6uQC)R! zOr0vsR$wRRTMMsR)45vGfy+GZj}>qL!w9!kY`R~MdgBtEEna@`YGLqHT+i=Actfe% zF~@bL-2~V}rIv5C2`+Hp;2J_*eTn7+0mXB&2EiK7wG{c~lF5iW(GZlzP_3Ezj~4CKMbw{HY4IswrLbP;U%j`iaM>qOOS!BATa1lz zKbN@m{y6sJP0wZHW%|9(Q8YK$WF-)bu(;EGsh6#=SXLTuU|92ksY-oo*d&hW`KVQZ zY;WpHs#6-J6aY1%m>za7YdlYWMcn~9|NVLwqQtF={C=gc6QHwE_3G)d^zz`%Bg^(n z)1@XGB4yB*X*#&J`cr<#xS_)@xjI|eOkd-p@#`y(p8~F9=}1Wx{_kJkVwiesdP{dc ziN0V{rBnNaw3M_0eO_(1qm< zfc)-DONtkViQc@GVm0-p`bpKt`KYFrsPi^DZ2av>Za12psP?5K&fT`OTcV!H8de+*6G7vLu%y+(dwb(4S_F$44 zVvWIE*F2!V2HO2Ts=G^%wxf>=%($Dg_tC&y=K|#Q)D1U#ka1N9a|4(rUyXsQLGhYG z4FEcjydmzR_6wDRI85V}+qzSt#n$B}dbi_0v0W68K7{3>0+r}xdKq=t_$h{8s<2bB zW#Fqh0IT;KKGWP&144Wg$}MY&At^TWX4mow9@W2t;ZTcC$*wQ*116FCHfKxmCe9u! zFD6J5Z#Ljz2!HVaSwPA3BjcB#3!G@Vu+~#g1{Q2+7x7jn&P8Ad%0wNLyA>BxaAR^s^ZV>72?pEpUknV;-qjX3QLb|(Qz`nqnhxz}8K?X~vT z{Xq{76$j%P&vW0`eO>4IJ7FR^Z3uOE-0dkU$;0r={5`p3f$4FCUjb_P$JG zz6CRv4{C4Q4WDL`kW#+fJI0j7(bE;hZ1Wem>wlO7Ll5!P;DAW<0mFW7vx+j1#bEetYB0;IdZv^^z+pxE8mz z)Yg3I4|z#%yozS4Ce36_C?jcHx$;WH%S=(aE?zI$Y0F}axEhW32(>dI-vrf!e;tRy zT7}_i@zqyxP#xlX{KKTeoEg999vq5yBSMZ$+)0Nx@H5DzC~+?xk>gu00?odU_-m*w zRB%yhx(sJ$+v3(>q#|CQ2IBCXzyA^;uI5aOWCiI^jgynuv*iKyl%)Kx2VH>1f)!cH zGr?rG;wuY$m1<79BkyZX=O;Sz!5hEfa>>P}g$Ud$@=58nC>_CzA@_TedK#Lkj`NqQ z6YRL-qsCd?5GJVGf^~TtZ|jt(*LE7FhT6JvbonlJjHZg`^b7&0{k-vFkf0+=YHJJx zS#FFP_=PW4|5lBbmHLYlLC)?CL;eh*j03da3mzVp4g-G=`kB`j^;Rc6Ha~+=$0hq?Zr5W$5zPvyB z+bU}q(OtjGyx5=nJ#F*a=zmDp4tqx)B7_7~4OjEYT$R46PMEnV6?DZHuXR*A7&C2p zHtLzepY|H+(kizt^ zEnlB3EZOMxWh6qv2)rO2nSL^D6&HIV!a)*c*}m61w4?au< zrkfrvQCw!`aiszuSEfy#i zF-JelrjK6PORABK%cfIJytBQakSg}%CS$na>Ui{j;x&v$244J68Tg85e@W@6zanAw z$T4WOR~U`(($)FncH&(kTLjq9m#s^Xt-qTnE-UjcL6S^n~R63Aj^@1jgRBbJpZNwl?8yQX1KNv+XdZ|AFq!T4r=gm1`3w+H{twBH%=f5F3#e8XeQZ{2zA*2#~|tSuhx8 zG(7g+*-Er{{8^%eOLDi29*C<^5t7}ze@s&H+C&}ZJG_6sGfndL!%K7Kb+V(58I2Y% z95bLAnppa^TYL9Ytx=fTYxtF96=DBSJ=f#Cb)J$Bp>K=)C|*v!@WEv476Ji&be2i2 zpv5Dlaa9UsVEnlnZ@azW7tG)>m6sF>?}4?HT~2p}&Q7MFn_Zt7*qV*y8lFE4Th7N> z^IZGcdGjqIpBocR167{T{(rTVPe40+Z0xjqotvyi5k_d(rv zT|SlH-g`V50G#IBaQ+F0jfZKk@{sCtOh^&^WKLkY$*j6tBtJ?rEgK|50-s9Sw7P2#%BT?^M zH{4)^pUwE66@0SDh(*x*gDn1Ei(onqGLSx3TU?cW$E#9|X49sU4td?$?&7PCBCRG3 zy8xoDs!;a2cwv-f0$W@I+%hA4g0j?(C05V6Hfv>q_9X)SG_cHpG3t>~Aigj4J!R%u z7E4hYa$(r*auL_6nt|i^`?3#K&W&*VOVtE;pr|`lyA_;gubxZyVaf7Q{neL=BFPo} zepgV~rYhxhw9TEl%CfD#azFa@#PS=T+m7z3QJW9yMDL5x;=wN5J&}iy+E+UG8#10( zS#u6|(p;-g8#k94;9XaetS#S8oO?^-F$xFA%gK+zqg({Ik6uni?hZb^D+}sz ztF!vkPRA_HRLFKL6G_S^1qjCGX)7(S#9LR_3rky`B&>vU&NHDKmPsqtS~Mry_oTdx z9jbHGOD)R(YUhUEj`oLWG9Bm>el>iuU$64KW8VH`4{#c5gwPBB`mKV(BK1$u>lHS0 z@mv3B@vGjI_s%BB3|fqhW=;4;0!Kfy60Wt6+LvJ4rBI_G-)+?%Rv9X);@1nP+_mZ9A=AAhr};(rv#;K(m#`MnLtA;Lli(wu0ik>x!AcNn z6&gb;5-bB^J;ybE$&={AaFY!wYf|#jFp1>aNN5#5K9BKCJdBCP4jm4nwW(e|pSY+f z_K~xM#Vp<8PX?lMLz26N(Fw%6!?zbz(YK5tVUD&~CNPJLV{B)NhmbZG014rBjFKv* zpxY96|G}v&i__b3(2kjE)z2DEfL`~^Z7I0tzANooAu*No1%Yx14*`etXq7mGD5joK zT-y;&WdaYOXv)+ODXn9mAcxVuK>>?jcLSbskI`VV>c@!;lRtrdAFudrNg0T<+7a3k z(fl;$5=Yz|Ma7RSEf{}%nMe=uC(Eei81&RTW0QGG9!Ax9KB}Q;3XwB1Jbat&ZjCzF zB%&Ss#8{t#;2yz}Fcf5)OZ6~9uu;+>FpxG<-rRzM3K73RInKR9>uD|1?bvmH5nC4V z;unD%q1vR*F0;Njs|2(b$+u(RK(?I=I>WVk_?QZG72h1o==BZ1GReP$_o!A|BPZ2~ zzNYLv!eXfV9u%3}Crn@x%6z*#!Kux=K95KHoPp^Bx`$jW&l>+Mz)Eq}n$PYz;Gb4j zbQ8WJ74bzUZr6CrO{*go5nUhnDD@3;3A?HCtbpUAJ}v}NrkcY)s4ZBIORMOzCnn|u zFH|ng?WlR2dQ@KyEz}FB7cua4DCo1kUi7~VYR4hg$)KmJtH&e^(S2!>%=3jWPSf{a@S3xG$34Hw3XKj*kwf=P{i~pg(BY&dl*RLts6Ut}5f=%F`d66~ z#b}G}F=pQ_5n7?@1>{@OY34)62(*5-?@4dFG!YF&juTAO8o%P@fCQ*$XGxu*tM>llDu z@a(L<$2Kug303ZiaEYxq(8b?VjzR|pbPQ6O3s=JkMyfE|7d|nTlO;vat4o}~%K`cj zv@wKDec5LG;kZV!UcUfC>6^6EZO)*uTVRuQ(#x9zhk)N)qLd6r_j-D)v5g{U-*sX@ znA_DN1+vZYJV|4kfmzvdwq~HR%oe7m8v2D6#lB6yB+YSSxGlu0eXvq4>%cW!2L^n! zGn&?Z;^OOMok9>DK(C4c`Hk)yX^oyXJ@8j{YkRgsBt|{PW%F)g??S0x1PDn8-(G+x zty~zGB{;>@_D_0Bi*|)CtR649Xo2tSds#4OF^Dm*PH8T2#8}mz&;4PS(N9J()%vXQ zaCOQ*$D7Q%zK`)NClWWaL@3C&eo}~yvXvmG^tS64d2m^Rj#kn}yn3;Yr5H6tBN!MX*=&Whxq`=r&*^Lj#ag>?iq6fcNpYjr#!J6PI{fF z`xY~R1}M~8)O`#&3{c7{*Y9z`JT_(ByZRW2v{F<#XjG}8b7%+J_C+ckp-)nczpzeLU@pj&7 z$7Ey!D_`}x$6j#QHt*7+M_7yZv&Y*qU0D8MrP=mvhEMe-fPy*^Sft{{v9EybbsgUi zoyWn=ax=8t`g*rkoe~l6{#hF_1}VnyA@Zc|2D5d>K@Oj2(6Lx(uyZ}1JKAGZuQ4;c zCfVJD4tD-(oU$gMgXpXq&n$y~GV~lRNG~C&{3)yJ*-lX=rb$#$7|o@`XiJ1)8{h@B z4yyN8f)3ekZYj{(r3Njj(VnET7}~6vSt)c^V{J#)Jxc;g+qGxJprnp4BnjoZk7?N7 z+!x2#*ZcZ{V&JBuDQ;+P)$?`;T77H_X##IXmGfu+hzvmYOjL)uh9P>a7{kv3Z@B~@ zj#MnP)z^iHJ6RYOfY}7hd`X1bZX-$8P{-mBr($jB?b%f~`#s!bOp}@KRsPYNz0yaj z=xJ|OQI^~B&u77pa8y#6=CY$~B>Lyd1JpIM?oC#U*pNhSpk6a~iC+$wMV(pshikgt zACu5XGf*>G&y9tn?oP*JksoUkOhTS+Jy&Mj{m-Te?xhOEVU{&sz%cqB^pdeMx@!CF*rJu7*E6+@m4cm1kMA-60?q6gd~8$E zeQ4UQTAu4UDTk$}2tl*+YyNn|(lV$9vonch(ELJ}h?BqQ5rCSMU)EL`E6OL$v&$w! zee~K=sJ8zJ=6cX+k&2dcSHqDH$;3S^01qodb&g}iJcW>=PkdY6o+xIgt7-6By*I!2 z4=5LjBkWH-Ol^x^(z1#nron#u3q9M*x_B;DSKZkh#%%guzGs~ns7q|HRh0Yd5Xp0Q z@$1M!Y|P*)_a|-S43~@D!pn>vhnS`$5BqTFrW}D`P>mEo*EoAYNTv)J`Pz#b`yo}; zL#0%i&O_8X!=%mYML#w!&Ml+;pnUtCCfc9CDF8#Db<}-G%RfC1eNkZaHMUhFdYj7W*zyP?7zs z#6^?El6l9@>^1gYdS%}+6W~5!BX`>^ZB@0bMc<`&>{j;~tg&Jx=_*6d35b=@>tg8X<~ zC+_0WGK(A=>fFvY$0kqq=6EVGG1XDJWA3iHTEHpfTOsukBsubAMzd=lO!=t!YmKBj<*Ks7$Jtm|Agia%g6ph#t!f6}@{>0ANYc6H?O!Hm z9vDIvW&6yI&4=CFsdXpv$DE$WoM)*%{2@6kGk*1{*SLFuw6An7_X3DZfV5}tJYPaF zf2-lcxWx5;1`d$ZgEq?OcLnahcOE_DAafic3f#&%n#y+puG9olDdZ^UTy$(Lw^ybN zYHNUdDZNT3_A*^qb{YFO6?JR-fH)JWN!hz6u%TC@AmtRUI%D4Xp6=`GJsr7slgN}k zAjzG`6gTLi$z4Wp_y}BZHB4f(X(`i*>~T-|B^&@Jv(^w2R~1eB zoLxuhVIL*hnRR1^O^}OFh(iUy#rhTV*h$H z+)1G8uP73(KS3YCVe~`wgGh&US)+Kgf#(NfEe-@R!ms;*Re_Kd0XCGhvY;35S-J$Y z#WngMJ#1Lc8G9Nv{A!y1&hgK2?4 zm>6&(hVWjOgQxdItf*2>nm<|qEUT90`bzcZm7~&)At2M9tw%Ezg#=g&FM%KoXP9w9 z?Lw-8^o_#jB*hah|JAgbQd{?QKE~z~c^(d2*?O<*VemrEOBweqRMzkcd4VZ1zWPhg zv(?C!5S(b|(>wvV38lr<0|xne=_pDksmGy1Emg-diXN9|tfk;s-7yd2?mZ(Z(Pxmg zEV>>JZ12tqT^BKev>A~06XKu-XhLxyXrdEJ^sSluH()TyhM4>7K_;=^iIqI;-z|mS zHc<7esq9t#L!1^{D1y3AolU)NV5Y=dDCE~YdNM3p5s|CxP^ge>rRqR4P>QnCxkCJ?hJ zzgNRG<_QqPxrY5YzuzKAB;0o8dDXVYHMDtKb|tk7x38}WuA#Tv&~CNiWtc_%s`B+2 z4`wlh_9Yu&-+~Z$^Vta+M|?wC#ZFJ)o~u%RhLdzj&-k=FXt=0M*oU2*;Ifhw$by>BXVdknW6mq8VLaz=STFJjcv%%$BOhmn6D^SColeH zZV62fp-&kYCaaaxxwWI@>S|0+W{WHI5sVPPUq)E@+JS0%^yzaAsHjz&8{pc}R`GgX zfhqS-JaVkcy&)gvqMJh)hjK%F8ZLD}u%%{G*-(}FlUNne9oi4t#&l!GL`mW$DP?Ul zBVd9`>o#w_gOT8=K`PEqG}NyiB&-`%b@%3NVMF)o^G^1ArrH`=%Y*9vRfaPxg?RcJ zSGr1%{?G|K6!~^PwixW7Avu5DKbG^nKtVH#$!sLEI=saLzV+r84WEV7g}cV)uwwfE zK&BHkLmpo}x8A`E38Kb?;ObPV|E22ulz^oCn%gy&a!F0$r#%B5`NVHg5rX{hbn>f9 z(JRj|BOqSEcp=lV*1fb2$#rlv%tw&uHn_^md-o*b?Q?{W!ak{gyqg9s7pj9P;u!lC zk1H&Twb2KD0WohmI-flBG!cfiL{VDC#?HRUrYJFIn2EiCDNNBBNAOpxLN;R z>X}ij-uuLa)G@ez=?|W4#pA-Ac@p{n#9CFCJR5`97kCepC=|RwrfR<^coR*b-4BGG z5-kzK~~T!Sro*p(IDr3`PNmxj>QV_GYl$|VAWMqF|KRj`R03d6%4!L z{-=23Z}HNzlYysBvRu(yR0PxkJ|ui`>mJnvCTgvNyE_UV6K ze*SqW!SCr$A0{h?#K@=oFaF)XzTv-rksbX zymY~R1TOhK?T?6m`#=BV3;hp&NFfeL>)*`?ls^3}!v61X>i_xc|Nkxj?_Kc!kDs!) z+IYtIf~3uY;7fj$7HuQpLgj1UT(f~9xBOe8f7fvR2 z1Y#SuglazU=lHi9G<^jDk+zY_#P~}|42Z`l_XxzH@Uz*FpK8u)FNZ-2Ju6Tq?q^(a zv)Zl^JL?gyTZzVUSue5Yg4y2MIrL7`nx;_0=E>*Z`Q^YSoQXh;Brxl{4AzbRS@e4t zhafd18mBsU7y;MSYr5Gu0;Zu@vJ z?`!W*;;b!@$!hPF>C0=UQM^Y4Vn7FAF5AUx<0{(4Huomp=KTiBS2x=YRYXtRXef0q zqh~Z1bD$`3521~e0u8$Xfc1oA^%$_5lPamB6ySRU%3D$@Uqe!Cv0D7q=~fa6k7FWW zm^(9Cn3X86%MryiyR}oSEm`ZEBl@oim!)^`yB}eCg`z}YMdZBOzk_SD*u#77j)vAM zhhqO!QokD-nV|}A6#vaK=6V#3zY5&v?X=z@4QD} zNAkX))lB{f_88Xp0gr<5#?9uNrqx^BC@f(kSv~PUP%!00=6O@D+T?xz@`E;Ua_yWxJn1Mb2XnrPql1lTX|4CYvVM9e0Kd!^eRGlz)`01qFh;%$0G@Zf` z82XG-MMa&RQP23SFT$UJtxNO&e2^locp z_D={5XGf9&|G zjllA-I+*C4RXP!qj#mP=eSvD-!Lm)Y2-Ew<70j|A4D@pV#eHDA^&>A46_X%-x^f}J zY&oCzXqVC9?w5*I^708L@{$0me$UgrXZSR+f>L{q2oE*>YwK`G&CWIFHErzYKlO1o zNmj*$Th^+*ZWxyEiWd+wAjPj$ZEA&$XrzjocbTu==JcGR?~!+;$Hr~%OQa22)P!xy~y$zd>;%dC}~OLV)bg4yGeoDZddUv%{N~2%`WktH`m$; zd1=mVyaJm!Kb_cDk)W^dW<4*5&g>A}RZ1;Kbt$gu=bohcytsd=@k*=KFFT`#5(u@D z<(G$4ss0@@+w}$%Bg`f=jDOdCpvX$G`(CZ(_ih#BVjL7DBxK{#ATDco~Wxon8{^1xCvk( z$_MWg^Q{KhRn!mhF|UCooehklj103Kb`>N6>9eS@5Yb3=yIhS^u39+~LJz=yu!NX( zRQ^0otY41cf&KY@-MNcf(qGnn0?IB!6j=lwgLB>~i%+H~6S!F^3-_esIZP+$!2i8d zq;K7#W4{fgfwg)ZFox#qJs#`tIS~qBw^0pN+QL_h=|a9uEe&n1cus};K^&s#OX!M| z9*#%9s*6@h{FK056OK~leqDo6olc-?bq}q;H0h~TZ}9h2YBwAn06rn-lsl*X^%M5~ zEuvvB_#FAh+!anw53rvNX3zbWmXKT$l!BTcs#2nXL$FPogneWX=-qS0q|es;++;JaN3g1FF>- zpaB>cYcwDA_Nm5Z)tF728TH5O?lm0`h9$M+hosqjA_4t{cR+P(LAzu#L6fNV91iuTOp?GGp_QB*P1FyEhWFa{D)LFL+_ztj|ZpO zlojYfKHI*sZ>;?Z`uX!J}Mf)^9 z-L~yc@UUP|TCL(QH^-&T7$043EQ_vr!uJK7V{xH(DgLxAo)^1!y8EF)7t6DjXBxN+ z6$FW_4^5{YKo#%{sP2h;Vt-5S)^r+6+_X+;DEx8G_ILKgc>RA3V+2hQbS}18(dxfH zgk%%|>o&N+#H^crjmXUj2>}lu0l|RIy#heC2E19taL8apSfBbx&Hxb)^B;L7Sk_3GRG!N5xGaNIb}-H1}k zWxj#SuIIa34FHXR9>p)b(CTXJd4yRAq1`mP>e}l9A^_eeI1M6C0Mi=l^^!#xDPK1abTC!4lEMgJvz96Q zLd0AFs&2qmd$J7!35cEa!RamupK36!4ju@G;FO`e;Fxqe2#-o0Hw6XI<^mLo|0Bhw z_ibu*k+~5d2^j&A(Kq)|LkQI4{w%ijZqU?Y&ZL7w%!is9BGC|2%m-KDl~)0?gP?#@ zr= zQv5E4xeNr&@SI=t1W2s&M;%z3z^Dun!$KAxK0R^c;^D5~W1`xHZz`jgXrw3DMfX_9 zKzzeb3`jW}dYTNs>UWBx$Ayf}OjP8(Fo0dGHu?wl-Qf}}N&|rGUh!Q{3>J=h-#Uwq z;z{i@d#PS3h!{vFPC~fdPPWF!Smr>PLed9@q_)#5H$YcZh>vNT!Vf0)ZSqq~T@t;T z5+Upin48;ec)@*CQVAwKb~AQuwzdnnF3U&Gz`(WI{SoYRQUIgICy_{O)>zL5Az4jq zbz6t7>)t-9RVMQEa4=L{HHIB374avnX}F$jnAAClpqA7E(j&h8RMOCx_QEEs23}nY zE2GV>{$6+k8e?}dq4O(l#G#H2nNZWogR&G+Krg^=u_yFwjq+;CKaV`I9cuREfL@+1 z%*=u6k)>mh43ZC_9ACviA4DmC9EZcf%l5^&; zaG6wTdxo%1B;K1R8`8IW=(4lHTseJB5^=E=o?z+h>~8Kkgf^7%&2h-14OQQ)zF4j1 zp!-0s(r{au)pgKq|8h)hb??+?^HlTSj=nc2E!y@c`{Vx-L<&|@w6wfyH=0iCDVJxum*7zGwf0e5_ua3gn7>_D?Fk_0YMQ$;ttOt$L_Yg-glJzd9@R{j z#?c#kCNi$=-64DCdYh) zZY{QiXIO0@smssZkW;WA<)nD#6PkM~iN1S9eHd6d#NP)vym<34rzz;Hg>5Q;JDK7* zKf!rAO!WivPcT&~`fAAVKgiYAH+PFs5?)FlHOGz)Cx^^?%%ec4jGC#pP&wKJDXyjn zY9|kb2rQpj;Y-~V+TCGXL%d*)n;L(7?F@ak%wn#M%@bJXHX57YAbEGUGl!P^RH@Ue%rnh5kJL zy0KcKaT$I&V1CD_R}aVDH}#x6iKRt-pzCh|dW#q?``^L1&Gsc$GaFHwP}R!y z?#HcLo=(v0xRhRQF~z-EPK=P}TsD5LdG=vgt@2ib!Bj8y1GY8br=0V_@2Jdl$&9$R z`^V-EfHf@Pn#^g+e*0yix~EI19JxYSmC4wQ(aN6bhZMI=XQLa;GsdGP+IJQObH8RD zk@5fDWo%o0t0U=26D4qYlYS2}a;;EtU4G`B2Hy#!yqn#|Z(#sqXou^TFi-c{{eb{W zGVYU((*mRl%s~OFsXlu&*54+L8|Ug}8yT4bcblFXUzN)|*9S=k%6ndz^QWHtxSUf9kKqMr5f?O`^XuQ_sIBtA3N^UYd_G^9 zkY3Z+*6?rwxQ62n5w?RrbVASbF^d&)qvsy!SrLuQQKhhrqB?zU!H||83uyS|RM#Bj z1ki+u2!A7DK+W+{SG_a4*U1j_F&fk9&9Mz*<#9j)&Vad9zllXEhO)eERNE3O_Aadc z^%PI$tZ`~E7SaJxnZpjc)NH<-K4Dod9qkP1JguJ5SvbgJ*H`6?tkys^u-}7CPn$G= z6rv9@PY$(+Y@WihkdO@@$SDN%`q>}-%7VFU<>u9-Fm6tHe%H*^b~xDvs%m+RAQl!qOxUsmbwKjz0WI^s@@PP`u`NkY@;HKdCv$$eXb%n_D2kP~SNxDL0nD zFZf7dyd(TqCp{j+X2uu{_jXZhG0=Duj)u8((7`kH`jPfFc z&{=Th9zkG{3rmb721n^xLLVtf8cY={=fpUlrYcYVn}ZIIi9eI=?d?{z`|s{R zQ3a&qnc570$xm=z(ku#YUjmcQ<;ZKT>Y?*}fz}5od}}_K)prOr8!FbOtxQ5=W+bD4 zFUaMq-_6jMy>4dl2rp~DHK%5 z8JIQlOu=U)&a_i2pyf_`8r{$x=`nhTV1_AiZJ5&7)0fJW$p@R^8Rm^07P%{K^8kcu zoW1aMS)xA}@_n|7#htqfllA%uc2EuS?247D>glDb((PyGX)k*>a^>fjD|oM6M7p5v zQ9&}?u?aiOyW5Fdb{;OxTaB6t_v7eP^(x<4U}4$JT`?$p;3V5AP6Z$byEc_8-igE2 z(3UJkAJv=DKg;W$)LKz|ExnCHvBoJ+iwc)n8a6Ue3`@dP8se6(HHA8^G;Tl{YAy%| zjIgHk^j5<%MF$o5gC@|}lz=lF*o&{S4$1Vg zL^`J*k(JszVcT~OT@SFFZyIMagDj*3<_9ACTQvN6ejEp#+YGqAlO}c66FG=M)>fVg zv<6UD-RCoAiO$aTYsR&=!#68=&KdqxHq$@XvH=roQ|Jv_kw|N0 z8#C@dYL-CNHlNqqHNT0hDgPubEk(>`SCepl>gGza*lPEOD0}LgbkftpuO}Ww`b%-p z3+oG^VsN&qH~WU6tbmjDiI!N_sgHhgmb_>|RMB*YD9_)wG`ien=0w$KzUeYI=`l?H zLn&zO4gBI@RoDH4b-bZ}_fV$LY^N5!HF0nz`(Y6jBS|i&+X*F_-1VziBsO6Kk}d7L z*Raq(n;ZWX6D*ic2Qt8WM|Ge5Gewz*VL0Qxm?Nu-wKTIF z_Sn5QD4RUPsSP7guR3O0Y!z(q;f?PM`{Z!E67dwtl`=(D71;*rtd}*&MX2-hhv$AP zbG$NCzgqNt7(BR_zRX^6Pg0GHejViCVWHVA06peDi^fLxKIjz?GYg>ZY#61p z4ELwHFJ=}d>bn9J^}u6J*hWpcMfWu&&}(5rG2)M8tQ@u{=J>_=rq~_VSuH--g;<=R zhGMtMw+<+&xKE-6T>ME~Agob+kGgt6eB%*sA^YVydyYdFzDmR9O_ywV){ZQg#Jh;t z270J97rdoMJ=mG49y^`bj((f86TU9BcGSmMD^rcdjA3=O{WO`Qf#(}sY%JR}0fR=C zveHRv%S3g~+=vVxHLE{`r^kL<0QEQM?vBs7jfaOpgluOxzf3KDG}Om1s!iZ`Pc)q{ zKiN>wJWzMat$Cl2=|iOOR5E+Cbm0%f)CNfR2r~U)D4G0j#L)6^0RM{`8B!i`H#nJA zdq+l%&*Gb3FCP6?fLAqVUI*p|-DnG;vdE_cgCR-XL2<)|3}GvNRSs2`zG2(C`C3@eMvY~QlZi2$%sOHOC{S`~S2Y2E6rZ% z2#Q`)J}O&yo3T*x#bUR?6dR@T?pXWI zI7!`gA-r%KtJ++^z#h^Zk?P=u;W*V1S!~}5dz_Z={!qR3#QqLumNy*u7VpwdG z+K%?`-mP}xVUv2XJ0BUJx#=bLy|5b#pR5zz5c2CQZJM{I735MDcbMK(Gu9?VmD;=( zU#}tIsj0okV-GW~3Mf?NCu3UeA+ziB0>$EjTXuj<631Aik*|YR1<{Yjg!WyYer

P*7d4-|dRais1gL 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

R7{xA%nc-vOH=8u@SUVREiQ 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

JD=pUF!i{Wq=bgY+MrC2WeXFs+o-(6 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%0A?|k&X-s8{sf`9>aqyI)F`>%f} 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ቯ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%0A{HP41{=GJ0?QpSfR&%*o=y(|U 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 literal 0 HcmV?d00001 diff --git a/docs/plans/miner-ui-2/09-updates.png b/docs/plans/miner-ui-2/09-updates.png new file mode 100644 index 0000000000000000000000000000000000000000..cd5b522c7048fd06fa3c942628edf8e593cfd66c GIT binary patch literal 251013 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%0D|To}~gX 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%0AoJpD{2=JFfR!wgSH1TCc|HM3^>3 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 GAOAlYl5=zb literal 0 HcmV?d00001 diff --git a/docs/plans/miner-ui-2/10-settings.png b/docs/plans/miner-ui-2/10-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..f79072fce63130d6432c51c60965e0b8805a7912 GIT binary patch literal 319138 zcmeFZWn3i5);@?f(73z1TjTByjTa7$ySuwv*|WRew?L`EEW&T~!>{#9NA{xi;J5D*Y}DM?W!5D*xB5Rgw;(4T-;@SSdGfd^1W zB?)1W%1OK<;4f~*04WnWIS?A)IW!0aC=LkZA63N#!gbh5d;K- z{Es)NloHt`2#6qvl&Fx38|Y~kloy)n;vga$nV>LHu%O>B&x?){A~7MlF9Q*P8rL^3 z`r;}bJv7Bc5*oR$NRXm}f^qAfh#jcX(79d@Ee5@(CMKTGj*s+ho(GLjQ&}UEX$RRE zl)Sv#5x*q;ApU5ZH2$fnfH(80Y>T>5-(Cz-QvwJ~+&^u8@SvZot-6{1t6{Gq{{R`oC$^ykMIrjf4Hpbe zf632)<)4ok1g4o(5Hy)bcvb$tnJ#t+s_t6QKBNC)%qVseeqeO2LCsnJ%_>^~*U1<> zWaJ+R)qhrbGX^Li=g{^%|IKv$Mgp$WC%6Quzb}yg>Di4B6p&TqZI}OMx~hqR>jaNQ z@n1wYa6zX^t-*8C#~6XyHyFCrLtYN8$?ck*UIAL_Uqd;1wBLunHv$xb&7 zlmtfUZ!^YHoGEk?DBpMc)5y4-?*lTWWRq#tD~-gP9Aq=l^!HLlM(~OMiH1npfDBSS(0LjuPb*83_PD3%wA{(ZDCf_tv*AFCgC5M8f-Zb8Mx#aDmrTmap~#JfYzs*krd;_ zEyvmevpmf~`ZSWVD1IudDN5kvP;3?U`A2`?=T*h1Ty&rOi3HMqcD^5vCc(94|*$R{&J87>nIEsTJYivCSvQPNA)kx(?L z8z-QKmzZFR;4tjrQ{o2Xn7;SaUHCUxfheD3> zppq^v?kZ;;9#y_KsWJ<|7+HcT8VD(IR2C<;)ATPlKP1v=`vMqrIx{OCX!*DmK-E0H z8fX2n9&ynyU;UDz5eeG4!}*e!5SnyBW$kg7gOu9HX|`7nCqh(?x{SWGd599{+S2tgmEe}>llSEYzzzm(um-_Zt*4alSz zPd@KBTtcL%jb$17d!E7gPrOVGm-HK%>(w>a7ZVWo2t65%?30$7ovYF9-$4-E{GFNn zKYaw{Nx$+`He=|Klk0uQE{jp}N}5GrdZ5&S7Ay$6fjofkkK~MXXjAk{T9E*G$oU6I z%z@bdnq0&gf>5;c*>QrI%ZGvsQ)!bS!Os@Lu5+`!=P6JUz?tJv2fTGgEdRs5L0&DV zf35MiLY;3WT_G5=5JMwnn?A_2Q0&&&=_*XSLmL<8zed&4z3 z8sQ+5xO6MwH_!*DlVz(GPmU)sj$Qvz=~@Q=>%E=b4q{u$4E72PokV6sGHWTJWb`2}`cy`Yt8^VksGE~kgc=Kbow*XyMWJLq}aQ{%HP z{&!QkiSD1wmQO}T)pZKg46XZ|yHYw zlpZjvQJAHOE>P_Iq{Kp@84(;T_5Ql}?nFUJITnsexwn}sa(&SyWBlIhYc!ol?(lqT z-gL(r$mfr6wjv$;dC!_PkwHhAd(AHE^@~lOHG$3F5O!LyEwO+?535u%-DWzV4p)!% zegWzwA@rtK#An!G8!7CuH(5%a>P~NSnXCPsh_Vbs{HS}zO~jb2u;pLE+Jtk^pHY%O&7j|y_yB?MWgy5TWu|&XDQ3##F)uEMZ;E!ych5=Y&(*J3ymZq43Z>#4 zp@V{hN2?k66YkHJ9~+dPuJ;mjn{MN>`E0(ADElNG=y}OaW^(PQXB}y{UG5-SZl$(F z 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/plans/miner-ui-2/11-logs-drawer.png b/docs/plans/miner-ui-2/11-logs-drawer.png new file mode 100644 index 0000000000000000000000000000000000000000..07d5c61324a86c8339db69da7a2d3c270005221e GIT binary patch literal 465492 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%0A<+7UOHVHr? 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⪚LagH}cx2h08 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%0A;^$LpT=l4HSt9G-Xz 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ð)XW;a0bDr8PaUm{j7%3Fb_b!w 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¬3`rFUPYi%7@b+ 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%0Ap#Gko}U67bOBBIetGn<){l_~4Rmh^2og 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

;6gnvYpnzC>zzxqi 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%0DmwhpH(6|}`7 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�(uAs5bZ`U~WxTw%^|+?Wxp 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:* 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

%Bau-IAqSD;IT^$yQrA8*lpXNm zj+(;t1KKqq&>f>wFn5P-4aLGS7a8mrmN|a{zU_Xl4CX9nj~mJ|f!AUyT!`My;Bnep zqll!A6v>Z(wh&5FpT!I2*Z)Q^sP`kXo_bV68p*85QU*~5Rd48$j9y8)ri=mxBL#PO zecW$JmX*ULf5)ncMj<4;nOce7ap~QLgT;xsByUy(dHZt6AA=-PXd=(hw!UyNyR;jrF2;d1p50Ed>I zzzpSt{w3i)^UcN3u6}<{G6m^1gy!2t&hGY3pby3gMUR!OC($2VQO$><;LFf}0w=NG z=ZR-Bb+_g}57_uD`_+~Zt?A!OSnY+Eb^6l}Tc8nrbRyeJZ!i6pUCd`u`>e=JakRdV zibGZYJEfv^bojTyo`UB|qs`gnWw$VadxlaUmIQ#&_5e~hsl#;7*@K|+fn(3 zODF@m8VlRAbZkC4{fdDxRksx5-5t!P^~9}5t3_L{Njf*DPuNTrg`%}I!IYy@3| z>g;3OoeUL`VcP@RNmC6P`?xt9Ke*{5ZO z`o_U)wV6+m2bLTrcgvOie5VRmAkrlg?&R4o51cH8Cjqqmo4vb2uasrdALaznhq?~LX>YGby-f9XPoM}EcD+yH|@XnxaqAj`hb_H5@}A_P~Mie zel9m`s?(JYMKs{TT#9Dg=I-`Ov};3rooPH3rIW+Wd`u>2#bhUORX%2|r&`hx=& zkIh1#zc6ii>??v2WU42-XNATn)x?s^Ex$k-nl6zpnOcad$0S{<9{j?|SCE0N2LCn9 zAU(+o&j`w?(diCol`V&xo&Tal_>YwMD1G0~cOA^#mU+y$_gttmw!Vq>L&4Q|Zi83~ zm1IFVZ8$TC(!rY&b0Q^od*mFX5jsr<{*%PIIjE~PfKSo;c&I)=>sXH5;1`t}!CX=c#H zZqm1grY6|TX>wVY#hSvvFkom%9K9BPdCNso?8~~(EpJ!U6U`eF8~?Z`2t`D+Qr-C1 z8msy!M2Vv0E?uilqJ4Ku8qz@)gdHu>*KdEaRuO zlllNug-PEcXgA+Ec~yqtR{EgS54TEw`!|;NR!kS8hyW}_yRr6A6}z2Gi$hLnWpe4% zK7e6zMK?Q%Qq~`l-t(IG>I=9l2P682ZS3s@@(YLSF4f^FrmNYUTASB1v{v}^%?hMd z6pZADjA0fV%Bln+rul|xkb@9Z9t_1>X3Fc%q#XE%vaQX<$!a1w9gX2C;*XlhINd1H zbu}+F&0_kp#Yi2o=F7g|r?;qGkO^w4f2b=DWg#f{DJ(M$~1{XOJ;lKA}d z?kKqVkiTt#97IZtq)5HtunXA9eqWWsUqc~=sg%%cNmU>%*Vk+I-%zv#yuz~f0rewo zkjDV^0?dYJ0B17R7i9d%$c?gfK4JnjY!Ewfh$zzMFPyY=Dx!%RQK)Rs#DR2%L`v;B}b`uo+d<`=p^)q8K@F?r@S)!-^CK!>oEr_{jCfiJd zv?{;U1dU)t@Ueh|uQ_kCmaV^HSN$jI&Wx5b^x4(k%A80((V8o#XiWf4?&Tn#s@zMuz%>(XhqM58 zCL2d{N3Y-L2pwSr`%?XGkb&83$>*+QNb#v$_C+BC+^t&g)%UP{Lp6`%tAGoQZ!03; zWRHJr4hRf-qdQib`?yY5n;hV53;0wsnww<{BtU?H#1yVx>?}Nw2#A}zt!84rRP9_u zYV=hn?EZ3Br^`1e$+4yzoCfj}pzR^k_5zZ|k6s(*Z_hO^LSFEkZYNnr6;6q1q6+%~ z@jB@8X%y+4O~5699nO<5k=Fx4tRE?w0Fv%62E<=TZZNzzU~=TGnYRdt0KU^RcSBGt zr#IddG?X>_UZ$53b+xZee7q~{LT-VGF&>yGfNI@%~8w#;VVy}&e4IQ zODC%sDp84K+?4#yB35g_{y~9N6Ya+UJxp@@Q$XN??iL>n$U{$L(STCctg8#u!2U;N zqcx(-;O5Cbf%rELXosDYuN^P-reYO0#O{N;kg#JJ6{lw$Ql|7u&gQY(N5(~xAiml; zAjigv_un&ovUnyfQJkwd0C5BpmNgPO?+`4fmEVo>@tlg5?4c@Qp zmJE2NHJxHSj*jFphML%tD9>o!G2MimokgrUsR!$MoJNRO6VB5L=0k+=&SNK3b zLIsJwm~^Bm(Az&;y~cA{?Mm*Z5Q+5*7}*H2eOULXHPN)#FH*AESt~V`v>Kz*vRG7A zw`}I9vA1(=vgn?Dug?=q$tP)NRxGPgA8CI3UV4O?{WX%(WOOU<>KaOj>|Wv1Gy6%y zW;3{QKY^#4(@!KJG%j4;q;VqObTG`{3&w2Ivn5X6Q zZL{^y)tO&Qi*+xW)aG(WW~XARm6G!L)6j^7&R?qv&!d~yZrM#huTMr)ku2S$! zCtYvKB%wx*N=mpU+$_glJRjh0Yjd>d2_6wBB$<}P8u7tO#2Tl{V%~v)LqLE0PB>(z zq@=W){w|^jV{sf3)>DQ~;lXXs2=fl2{ZPH2)YF+i_WC!k8ZC z_dz$f?3L?szD0H6)M~$Fr^`DlS_PucveDE1VX-^`6+L2Lj!!w~w!B@??qKqJ^XQS? zXoFE}eOTD){Zp@d-OBQE5|o#JbCqH`ipB0vT{+s-TV?(xa#yaS_RG*3Ip%MBBL1s& z)y+$_Ac%vkFG}j-w`U3)glbi=d~2<`CJ%RJpN6PM+#milUn(F`gv=RATsl7b;J_w3 zxob4p+BH4eq?h@1Kau8{bH3K_Plf+_o%6KweCXR=!iUBa!*JAM{f3Z>9zHR-nD&w) z*^O21w@PcCg|_-xpn)MTK5RBt5_DVdS|^3W#FboROGuRMwhQ*M>%?;Vg!g0qI7CJJ zI!Hz9WqTek;IkLn99DnR*T?#t`O{tJ=N|X_Y&yyVw{>(9T+6v$%W^k14x}SB&;k*ixrR zZ&h@_fEdTeIC~rs_XfiKJw~9mEN2?Kr))3+4ws#dWKo3lOX%io>@)4|#nJm8JgRk{ zH5!XvU#)QZ6ABNrNeR8aiH%F~d%bOyzG_|~FEpYO3wYen-9y}wwg7_~T~BlT5s%oZ zM%9p~jaG&@S!R^q6}6bS^s`xuvNiW#*^3)cmkcY0KiG|`RZ;F|;Z@EZUB$H19oig_ z+@9bHI1|FTg$jg-@faC^l_ZrMNJ3mmv03KZe^(qdXi$RyW#QG%rCj+|mq{Rm3o4K%f5b3DOana@m)Pnetp)!}2<9dllg# zDD}{AJ+1}4qb|}0RBT^nrl)hds-l|eSWkHK2tr@!omUPu(}_!FnoHFizdWA>ywcNZ zJn)_>w2?E=L1qyz=ZLED7xn^gg;Q&D14X=3qzPHys-4qBkq)Wj|HMWL$9S@0v~sk- zmH+jq+8YuA8WHmYSo@kk*aQ*qPbF|9`QZN8qtX={JF?u(>i(CnWVx+*io0lu`9_qn zi^vLz{Rez%RXoI2L)>dsdATICgGIt?wUxBMY$jTb1TJjh8)+9khrv;f58&X+dR`~7 zz+WKb)oy{Qsjc1dL1mO7^7_W391Y zg73w(#`v^OJvzBn&J0W!so{R26`gP%2Ks?0X!35GHrgDzC*nLJkf!AOYV43i4mE4Z ziv&^^OLZmXTY`oJa#fA?GAqrl@enK|kgS6kh7P3{x!ezGq4Jf|ZtKggx9@V==giq| z>Xp-P2Emml(j51??5FOfsp~}Ns`&P((2tnvzj~qZ5a`WV9x8&&ay;mowbWXeH?Ik3 z^L(EAC*k)O&5H(k(jK?2uIusBK6%~}Oy>1>n=(3R*6cf^zP9zEI`y~QM%-6*d1hTG z)O6MPZIj&B5*2^Jpv{xP4Oc9{Zp>6#FL|tFF+cg3eO-pGa_3P|o=I67Gr3K`MkF}~ ztopdM{0;kd1D>3Ow@T47WAT8~10+jsMB71!{SMsiAVH-C#H{7;$!_l)o%q^)_vAE# zfBB)TJ3+)S$>f#)3R(WGL0pcZ0a3}z;@WIoUW7qsG0vP7Ji%@KGo4(~WO+C}9;w^; z8gu?5c3rB7pc?xwmduoBkS)v1RM)qQ&HF1Mh;=*LMw88!Js81EOeh3P>W?9AoEEH2?FvV#y3&A2R2&$j=PPsIDLb2*<^ zT->gxxLCTj_PVgK@veq!^Kz)KY)%J@*1W$-!*Nq@39lpAy(S7RuwueQtBvUSR_&AY z>)oHJ>hPGk?x$RE2~t(!2$wg|JVovD z=kkU}L9!1A$<>}DRfGye8LCI~YwZ`ASM5q%DX&ry9S**TB=20OSF5KOA8}JO-JVAG zA=4XFp^GtB9ce-||Z ztC93_^+w_q;KTa|#ji<#+xHY8azMtU2M1ib4}N4G>?B(a6)~bCgs%cql0oxDuFh24 zJ@$(xuSu5e&o}mCTu!@k4kwMfnkH-Rx01~#O=_cvV5KLIJzcA75g7E(q`cZs(09+w z9w%{W$b>-rc}Jlmc3_+6^ppP;j3~Srg@u$OiHy`UbJC(XERIqk%wTk867)p>Iy2!s z>^#}IlGBWLrin=`1=%sYedmR7K;Gi7FUIT}9iV+SVVWU+CA%uH+X8#!xN;B26%vv~ zZ)jnSC`fsss$M3I|Hs~&$3wlh|KlY}sgSi~DJR*NBV^YiWzW8k60%ozgGqaqRQ6q2 zvyGi>Lxr-&V63CaGPc2Bm@(%28qT@zbGq-G``_Q=_vsH0jd{P{uh+F-&+B?!Z>Vr$ zv~iT6XYv=*Z#;u7!h2XmGQ)=ybDx!+$!&9=GEROaHcLu5Zt5A!e%sRPyZf?X3Ta>j z6M{HtW-^Z!-elRE_s~0KfTeyW04<9=uqN39MO>6hCFE7L-zm%P2X~9;@jJwv2@Bb5 z!@6Q*rK~zdpn>?WGaWoPVSCe(p`mTw604Rl-LZbKQau4S%ntQRa00I#y~+~QA^3^d+C zcymW+%iX#FnuR!LItw@lV8(~0l5RuTdcoYPVYp~5e?k=;Q7$Z`=!vcs!JKU^Pw)25 zSz#QjAS7`NA+MK&kgZ^?ToFiKt$B>O22}gNAn{q2ElbtWIMYq`qWOR$ictJ3(rkAE zK6_rSO$m86gZ5VKRC(r{nHkb#H8=OtK7Tz!!z3Vuhzxt>YFzCFg>c&;N5;u%tG(wl zwK;Cbj>p=(Xvf!X5oU>s?Ky@?7y4|%6-DVIhhgM>0#qg@?P%QLw*zY#I>ZpvwDELC8lMb=09&YU}T;b@VqQNk>QZ06T2kB$Qe|N6?n zAiNIDP&KR1MIeedSQm*du@^XBY$tZ1HhcHx-Sx+LB<=h+x*nM2V*UA-nK~)2j_l9& zKX-EB#_@cl*ZCLc*KP7zas-nC8`?MaxIQu>I4vEV=$BsWx?;qW8DF)r8G%$aZA>6_ zRw^haXB{7ic(L5&)}G_1NGg16$`UQBy6?>O`I*!ayUCPL#Q2*dzF28H7F$t-uP;JT z5%Z14;hxc%AA{BMLRS7wc1~V~LqT(3wz$x47PWOz*1SRxYk%Dq!`V3` zA0Uu>lvST$&Gqj5fdx59$Z&xAr`ZM51mia7au80Rx+Iu1Tub<5i6aFrW)L${Q14!7R+7sVX36)4jS&~0D>X8w*ABgKpDB4K zI;)|bKbSZYevziGA77I)8ln9;=X4Xcs}r&G(xXYn6%+9JoFtS}E!9hdwSHz~437yX zFW=#+mpf0i6uRGQetT$C8G1vDu>5A#*EYw+MNyIHjUf^HCc|A^h5|@faMTHp?mK?5 z?psjwO4!GW{oK`VY}oHgUMc~8BL!u?pJxm5y8D7BUoayt2)A9l3AHtZTNUeT$$4p$ zVTWowKlY^%vpQ0Gc>N8}LkxV09~xFM!BIyX3^mkF%m=a%ifSP!I3#7ErliErP9a4i z4d-nNYu@OqsC%Vz%B0_}nbTVn3mr9m?4}VImZN~1>g*I+bsp>?V^f6Sn>KQb^$)w> zs)lrjKsLKKW9R4R9U6lt1U{G-Db=B9L%oA|U!2dGJ!Vv35vSEYKqdwev5?nK3#~R< zjBR@>EB0TgKH5Eh?a>(oz7{+Az0q^FZ;jSkD@93B^^Q|(^wz$dk)u-rAU`YDsK-{! zj}=Q$U=4=YuT&&*ukA%hRS54+oUmW8&_RES=Xty3&1iAu-UGl;(S;_=6W?w7eLd@@3EX`TU!npR7u{&6D5T z2WE3y^^$oEzWeXn@N3;Si>s?6$wH@siY}_=_2YYW&{A|#!V+u zL0(trvE}eIvm%f*$#o&kci>!vGO>Qi_t|u|Bsmr_IcDZ&R;*+R!w&in)_NnX?-D*_ z;MaJ3v{*%h1s%p~113EWWXUMngnRUi4PYRMdp0wQo}&X|BVzEemHOZ({(>JCP()>z zzjzXHW*<^UTlsYDraQ`$G~6WZ=11Vd#61;j%WN5n+`2clsFUK;wI^)`qv}b9ZENDiRM=;LrD+GL5Q{u~$ao zm8t|t>u`F!3BD16PS{$-RA=Gs1A^<6`d$1`wC*HqTyy#*@06$)a=2g*a&Wuin#gK< zLY<#Y6#=R9B}<}~P#}eQ3PTg1zfoBt*1s_=L)QsnR*;q0v)olY${z3DS!*PrmbHUE z+8NUG`?HvL#wU_KMvoN*xOlM+3zkBZ`r@_r_8S!+GIFjspTM^`mve)+^bOw3qEB$h*!RwFC5s|9D+Zn>$dTtQDqJlGeWxs| z+@isos*@g>cK%X zoVu2NmbQ+heMd)TlZpAk7{!|?_9wl)5%-XVA~CEDIS9LlV{X1{$rxWX@XmmRud*&5 zUpvo~wYvHXr+5{f6oe@9=B_ZFie1Q;)0owSgOTPn^UoGSkT8ENw`%UtO8@OWqoIwm zxu~|_%^CQEWB4;v^o+FZ;V!*TqCGn3v7bL`$Rv8- zBRCk%3tgmRy#G8{^y#e&k7z9dz7ZAo9ksceKYOA)9Up)%7cO8u+gJB zD!;uTwF^5R(+^4Z`MS9>rIQ#N==srRWA?{-o}Yezi|4`#ec7Y-L_OBq!3@{ls4|=x z%uTM*kh=%(2v%7l8JhMNKxxv^$`$g8O8FyfXrJ3w@6WD1XKko}Y}VSdG&LmSs*Q!U z#1y=mSBlThzdqV3c!P90VL|f4i|cXI0&xSIdGP~K1Tnl|(a@e=k6^+&OhAy&iw3M1 z9+-r8(H?8`h;6nfHqLHL5>Xl1c~Z$kXGN=oI1$*ymCZD7O>h5=c~+jR?o4#vr~$?SS*K9A z7d0g9XcfA67r)lqy3h(yCMO#n3UMONYqjEFPDe*za}lbKxJ+*qRt_C|3+IXa5GbXlqsg(k{h7Y$bg3Rc4)Ri`YyIwFcyzMnGH zy<=I0Tf*?z_#tCk)@sFu&svh=vNDxN$^}Q3>#LYqmdBCL6)LF|KPP%fhoYd{Q6ES^>~x>*csV9RYk;|e z@X~#%gRTC=nAXvcx1CT&VKgsKd5NT$Ei$o@BpcH#hW1vsU4873SB00zY@ey!{HR=I zV*((byB!=Q7gpS?wp9G)uT^K1CHQG?REp;om%t_&PGzuhR_Oa}HN&|o42$bli3f@W zmZdYv1k@!Yhc>!)Ho7U&^m@_cM>13`Tu{!EBeWp`d+SXSt&#ttEiucU`~9V@XM2(4 z>d99t1$1s&Fw%-XalTi=R5uM8f*EnGakDhintjC_b@z#T{Ff(_-+l%$#%R1?SF>nxz7Zuo933(x&7LU$1aa9cjiX4L!X_U7f?S5HY9`rsud z7nt<{xjDPfR5ca7H?JX4@+fOxmOo*TIVummwAqMh6He8;-HMW$_})$zOg*->9v+2( zDEVqtEr#;q-u<{K_x?#{pTY`m&22cbV7-5_nw21B2I0L8U6zGi!@|DQZFK$sC=&rb^b__Qkkc%eUeOjjF^P)d{rM|#>Pp&|9M^=)%q1F8C z>Hs!>yx7{NbZzdj=cP5t0Duie)pWo~lUvMthqB}GllJp*STH6_Fd~J3fK4I~d5N6- z7LiMS&zF?&cwd?ziXI)HDYrDIoUzWy@VzLUv|g|>HDo*iU!^_7nyCH|J9oYwIp`cp9WCvZu3s>e7pSlkM8$lSFG*UiS=E zt$sW%B9io-Xp%{FS3t3~IYh;vIJjBY?9mBqt+&%NC;sMQF%Zf`DN9D9S8y1q9(z9=NSI7}!q0uDEf8iX^UCqJ(>zPP^7>d0p}0msF*h+0i85eAZ-!ctysNID_& z!?*V&Pw}pwF=fLMjA?Uz*(5p1|3qHm7P)sS2}7B$c&7tblx4XG4D=rb)6~M=y7H( zs93n3fG)S6zn^tsF5o5Y>ZYTw6XC~5J0_?-fZS!NYaSIy`t;5ri%{2FUI0mWTvE(q zf<2L#)&)nSx<>J2(CVe_KG}ASduvhFVZ7;xqI8mkjU8cHO*uM%|E7x^emLAKKV-4z ztnvnClNftKJX2YyG`kL0hfj)vLDvSbom(l{*jnGAA#EMvT#1_;wx)Bc*Y^;Sl_=*j zSh})Sk@8AMrqXY9ru5dA0Edm`35SLUj|U0f*9+l6?(7Pb^CdPkuf- z){O6+Az9M^&n6FdUER=F5Iw%|T)h3U>1uXo-PTL4UbFqsj(h!Xmsl@Wt}RGSe2sEh zyJ0%X9lrE+tn3(lerdh+!qlbQ6Z#5#_d*JL15CyFxgI)Pe!$QUOp*w1>`Gje&WbVUyoA)?&ieb$#R*#{7~=5!OLQdhH;j@$Qv0NCv_DXe;kr zna`3E=9L=PfZe(vfrBNWLzzs6@$G~LZf9*>YTOYyT!^P&+PF4?H1F0u5koFNDXPW2 zL&fbxYugFA&}QE+JHnHDkby_nflAOi3eU;iddTFIzP_aFJc8Wd%aV6SV?jGXdUcc8 z*LOB701HrvxV1s2(HcUprT0j*mPEH~{^x$YCVh+6xAaS_KYH^Qpw9YoV~WPi`P5Rf zxti`>u#+*{xc%4xa-^$Saow=!CD4^MscXbJ(E-3fmHYLTOeXK|sm3cpt>S+N)fn3t#qNjnqQ|{a;;fuU zbE~QdJ^hukkKbHLRSwD&na2RUGzm?}Q$z|h&qtz{+Z2(dgpP^@bF(4jOqd8p%)3Ba zxz^8=umQS{u)2L9hWSI?9;g@;1m_r5j0QrD1bmSEiCqFeOV+((;xsp8M~(ylv|r^o zZR~@>!>$*VE!k%UXz46-^Yb-`bMDsh=N$C>=89|`8V~J#`9P}(PRF(o%PKM`OFrjs zaKy>ML9{&1mYTqlonVnPAwxg)Oa+B5|^1c9VE78k1#v8w~1Ew+X zs|C?AD6V`ZZFt;f6pokoz98#WB1~YGzQL^D{5WpW&~!NZ?!fcOJi+M2-pU$JxgGZW zv>VD}H17ah?qJ|UORnvQvI_)(m#b&kwnk4r9kq=a(Kqnz_B-_GTK{FbTyf-ox&Ghl zjWwZ?Vw$~Yab#!p;_+0V0jx!*0@qPJWtcoOo0a~QJCLWWq9WW*re(pRx3|}H(J}lS zbZxk1+3*qkkdZCQl=Ji-y!iKR|Ktw%#QG|z16~>{k=%I#L&Mv?zP{4g0_bE=nt*<$ zU2ig%W`eX!_SHlT(moT+4H5wl$to`|_aD3v1s7b-`1W>Np#G*U|Jsw_WtBa-7hOwR zf7@OIuK>f|H0Z*6-uXHr8bq+5lImuL2y`*4+k8cF)~~Zf6KvF0;hbLm0`2iqXR-P= z_q0dn?&jgJ@5TxOXN|yoKe3bXs1Q z;Yy{HK(UKvoSd9YEtxqvg6)a2^6g3T$`mvoQNFfTuHaJ+2Jg-hpL^Ca%}l;$|4E1b zCQctItpl1bkM6V%&^}U8Gkbr}*R0x0i}lmssrG~l9(q32^8yd99XXOv{O+L4q+yYn zS^V-lIuQ1^D#fx~$eo#)G0@kKaCDm0H>i$R3b+DJocfjf>jln^6O7bv0r7^)>KXa7nLdNPWYBGGGRdJ^} z^9un+@c+~}|Msl=;)R2>)<=TdwYQ&V?M!`B9dSG2Eb4{UfyAg?NVzBIItzd35!R+dYO6oA zrFnpz*efycZ2OAHrz%cPPUu&IW_$CND|!7c{7u^azA@`tG>2>+J}&z3N--gg{));T zCvnHMonooYZ(j$i&M3QEjzmh#1R=LeSUbA?pJo8qlgC93A56aAP9A!{pAX2h_*wI9 z+Z|NQ@XP=T{z=n(|8dTtJH2>1Li<07@|RDlBxn9!#2#QP%DzsT6GP!DERJT}lYt5lyf;Ywhx#D{Ohc4i87-Z3oZo z%`1K{AO~t9zuKFMH8u6lZ5Yo#zI5~>NEU z=D@zzV8hWnOnXbkSSMK11$1wUfE_m480A&m zS-=^Z6DOo6XF*CA!>3aq@;+&IQbi(ELN^-wA*Fy|8+bE%n!=j>J2G^h^|k{jKA+CZ z$x+8mVPEFtl8{O-+_DOo{^HMmBD_JNO6Y21EF_d9CGDy$!I~XLzc;OPd|U~0W_v&U z0$;xt3Y3+*ei5|R(xzh-ZW(3CQxFH&F22#IwQbml=131 z+n!M68pYjDxvQXhd%ZKpG+dpxQ`o<#%HI{ymRC1+p09~8I5?;mN^%(200QqnB38SD z*Q(MC4h>P-=JfoNK%iz;c}ES&$Qo6=Ej+)lS1KE{pGqG}l(X6Rk2G(#fV$T248YO^ z@ee%NmaNcyLBQ9Qx{eGMsGG>o6}lU}E%iFNV!^hHC#N+?RtcSGjJ0~RY)GMKiu4vW zh$lxCkv7}3($(WZzs!dqq*K`BCZAfB-kv%aKxlhCWWJjjyI(&|_lezV`@T-I(f*-2 z>y9hfk9U%a>?)7`IKLyhD!#Or9lEbJ>m*1kiW!0zk8cV;lH$!bEfFj$Jr|>PcJ;xp z)#bhl%^~37GT0Fz@GbzGgJxVU7Ji8$QPVw6H%D}gcYJph^;paT-uuE&8}|^VNpT2JhQ9c8I#^Sb2 zQ9Z*igRkQgIB*^T25pWmE?P@XVwt~FdW?Vl*l9>=Rh68fpF_-P_VrU`c)<=cob`FopH&%zxRTrmGdP+1OHFWM z0>Xvs^Zvi>>*r^K%PtyU-}C1E`!5C$hk8zoa?o0bVToDz9V+TlCFwJHb)4nAwizG) z#rL#wORABvPRlWZp=J1mr1~9a{F4FrbAy5^Ji$ERn9{tu~+=clwXJFw`%f*hM`4`losxkX-IRXfLy?-Te1@ zXsvyZ{9=_qyBX`3K$9LmD&hF~55FKN^&kEs^)!u~oSfitl_h8|+ik~J^iz*D+0hhU z!LdwOP`;=uO$930zL1=poR&5aF_1%YfNh=EZKLbM3Z`|IIB5(0%vYu zXp=9X$fW^U3eQsKsLjkPegp_^ra`_8^ze>3Tczlr7_>cVfQg)`IXg7C6F$?87sp9+1 z*&{~kYMV#68TE#%a%8lTT{e^bhRLH#EK8Mu1L<75O>B zHlxy2Y-}#gOizE_T7qi;%YCMzR454{GhnJ+@HnM3LB_w^S}JE&|6; zOZ!s`3JRvBS$EAP08Q&@Zq7+HS=$IpH{B9?bI{uGgQZv4wnpitzPX`aS5V-p{>3Hl zuZH7qc5;W4K|u$Zo2=+;iuMgT`MaIDFI9(4L2vVnfkxWR^|Kx--(mU3mw_~|XKE$# zyGyj7F;A0)&Ca6A_2Ty!Wehu@u2C$<8EU!iCr4&+o}lh6QWVy>n}q=D0&_?|#5zr} zJ2#h0r-;we6!Nv@dz_+NByJL9>^*d!qfwM!^;{`Dk)~FxK zv#b`>xv7&RCjxWVy?04y*Uj;$@UT7mW*Ay@r8k$|J^vHmeZ%M8BT^LRsay}5oXqqW z9RpUC5*ac>q;@wW4E&;B1DpXdjtDea2o(Wo8ng6n-;pLz0PQt3ZJVs2>RZ7`s=M0n z9$`}pTJkg*$Y*gmcjQ3w3rvpg%?qww6;0gU;lVt!G6jI3WdY2y#=!2~yzs6_!hMyD zLzLh;^Mp;VM3dPU3hA+RPLyka!sO@YJ501RFUtmxYh4N;MZO&}-GNGeMYbSy=4TQ; z>>BexXPb>|Ld#w!>@hks8i3N?7E+p!#~{E-#TY97+n+Go&MQ8LG1Ej5zwg$$I;Ce=JSmAhO1n4tFd*f=^35LAtT{MYp`^A3)eIWG(36Z@PwtQ3v1v^{N(x z9E7;kHc*+Eizw_Q)2cLMVVBH@(g0egDuCA2j5mmQTqvEcp3+>&AHBOSXz~Yv09Dvu zO0p5PwzgJ!Z^9;%bI$Xr)`fn$PZis0lp3qjX1>{PL!T8LJ-Hdym}lzV4TA6x7sunj z(9=JR6yGD2(C1D4w?Us+-nk}Cn>mA7fmA9Z9s1F2`T=dbzBE(R9GxNSJ~?p^3l1qw zu@r%PH$%kSOnu>>9fNvt)eqMAmhkjlu52jyd!^# z-}Gmrtt(0ee9b_cvgd$}y7YpqEY=7ZxWU33mR1`%76j7{5<|V%QG{|NR0CZ*n@+JV8C8dptZ|*vBwJarDIT;H8$PruKKh z%MI4NKP8{=t#|FUTkkMh(M4w`PF*=R{TWT!s@6tA3;@M{NQsrq&o>< zkrVpNeLb4mQc_X^Eq+FZhRR!ICFxWBcnkVnxyg>Dy55^pt7!Mbp%Vdc{>;8Uk%2tL zrQ6!Tr;L-1wdRniOl&>nJ+`p~z8saBpd~)Lw`fb$4!M7EbCol62FA;c)n5N#_5TbG z4yytfgx_y%2L<^DU#OD$7UUm-gthAJ5b@7@{K66e$$D}cV4cj0#D9or{t$HCE|7w- z1Q0D1b`{EhE%Fit4+>5W*qx_;`rqu^0GkP}BZ(&etXF@2<8S*3dP57cIMvR&e?4y3 zUkoV7x9Vsh+aGfHYnp5`u3i_QI#1r(L;g2537P@?@vR#oWOtwW=fZt&eZ#Jb(Z+2%^^ezxGXp}tnI71VT>kO)LBZfyN6z#RcIB*8eINQ-2W!!|6=z4iTl4u@zXE=C+`2%J_0KJ zf7`nE9JaOp(=qL1%FfT%0?ynv{L8ycJ}#+uJBTds<&M?|idlXViJu4pMnHq864Vd$ z)zqL{AVQJ?g^p|H<)6}I%Sua~$7zk+!hum+yFIW|{ynT~4S*ttmPm(pb%E+@*_{`S zBKB8iweN0A#m@zSoQ~NhTEO##K4>0TJ#P(#nmal=ws3{LD=In#p1W#-O_*n5s2@}k zGvA3h1F3Vm!K+idDJM0)8P>cLC@nAV6TXIyuf6+R&3x-wsqb%{<8OA)QLN9?+^i|a zT~B&ibo!+fC_GC$>1b_9HA#EO#fFmzVyqRfNt@n(ywQLSyUuZZgdKzInEAHm9JViJT?=%l8rt7{uq zJNo6y0ej9}xoN}q6^%}2i+uL>*!4Sa3kpP`nqMxOMj#WxD{LjcQW{LcI8foH+|9`6 zG=)vbk#1wk7Wg@WKwdi+*1V~}$dwc|H`5iLA zZ}#opAR^w*N_(zkH&}9>jl@(I!peikZ22On6*V z|GPeY5D;ZO#cuCig*)h~hni_S$awu-$3Jj{Dsh4n0lf?A;Y5Rgd~+=(#3!o3?XJ+BS!! zn)J%F_wE&jQ*R5o?nNHGA<#?SIjZ73SSkUNyna(ZWcQdz{5#EQQ0&cO{19AZ5LGDW z1}fT*V3=wZ$_?2=L9T6)?a$)I1M1@k+}Vk{$4b^04}R)*IQ59dCP$*&3URt+GP(!Aj~~_8LCRKAv+FlE zJxI37si=hJLIf-6&^Dv1%byon>`Qt6OvBnj~SV?=U9MF{pupZxm zHYE;|-C4Sq>b;7lDmw)&I$@I^H3;&Q)sAXc%0{Mn8Qht4Qj{b;Jv?rG&pon%QsOZE z=eyyHdfA=ABv9(kU|l{a%k9=JW)`rnKuXCXi!(H`eAk`^Kw1*rr+!CGHFlePiAl5P z6x0`6{8nTSKm{?k+xcthwP!|LQ0kOuTb1Uf z=-9OgJ_@L^tkxB_duPKo2KsoZj~yEJGsMdHY{oe0jJS-O^r|%@PsnbY0y#I&ACT{a zLBp~76JJ|DUZJdC{5nmhc2cwB_!fO@_tdvOo7TgqVeoZ$Lz9Vf2IyS* z*J!5TLqMMuA?V>@6OY>6K`!v%J3$}%oo)zweS^Lj6#+ILTZ?qxR=Bq+ZHgA4*bBrc zeR6X06j%GgKT!JDkiUMWCZ%uy8t!mMRQ?B2@1?uEF8E(De*>OlV8*I{@WHo3Cc9&@lmahp1#Y7j|JhBl>&|> zTf~Ts7H5=A`_b)gB~`2!|0q6v3pdz;9PpA_|@&`x>r zD7LG0_BNHtgs1Y#Hu3_7WMpPC)H`xyNXq@u9{MZOA*hy`pYJYQE-o$q3J!vi)1#KC z?dP7M0b$y*=VRE4hU@Krz|d)(D^zY!?omB*fL%=<6z0-RWzhqyt<>K!Uh&ZO;fX9K6L z{~V|gDJ?`;QJ|c9aXMzbKCqpq8;eK(X!!qy^8x8Ue<-ECc=q=vfWLLs1z{Basvj}{ zAhpFm0k^b=!&T0NwjZ)S(&^NAeRn(ZCs(pn06cN11|u3rdC)&z6ZD;eB5|py?X>m% z(*>zi6a;KBv-;NmaBuMZCu#s+^Et`+_A<&hOQ=hQ#`X5C84{(H?3+(m#T@U8} zZ83tnAZns=tJ2u53Fc2a0txV;fK<(0|NkI-_Pcr~uP=Y_Kj;SI`S~!o)&Wi*l@uGw zjHv)Z$rRpHsh=jzGr3{StrDMGI?L&*-2Au0Fn<>~c1ysWu2nDTZSIKw7GzwzoPlAQ zO4eUtr&nCLLx^Fk{T+nBZ>9-)bfb#|RJyy8hf!Cr#sZ8dX?e^zLo4A#Z~=VfaWl8K zhaUl{J@c{f))&&p6ZI$ZaVzg%N|@<7pyaQ#Y43uSiEC12@|@(m`gBLU(@rfSlnD-1 z(^)ahioFHZ3$M3k=~A?TCsIYRnft6p5 zH9WdH9q(HA51?j{D^;VKMYYSf?Vd@{H)2HvAaYaN9PccM#qv1lxt>!>k{_RU)2{Y#;6;52I>ICOZyTLHY6xa6?(JRDJa$TMn7}P8W%9t^p+)umTpH=vN5R>}w z=#2ZbGaAw9LIss?JD1hX#X$vWWjO|+AOfjVAdRshvP)aJdsekqQsP|R%#{zhV&Xde z?W%|i)yNdG1;K4}NKRX}U=$47L4pHBaje4A8+ZaX01I_!jTV{cw+)y%qk9yA^cr-? zT6xP>%TKOYeH$XGo@xqYpS{iEy%uBB!siHe#0V!i5lWbw+R7!K?)=G61^s^P85y4f%Aes}pxvJr ze$O(e+cP|@c`+fTcFxv;u!6zJk8X5iuZ}tBK+Oih5&mTy&;cdYt1jlCZTTJM6O)dn z1V7ilcn)djt3XY3t2QPRN`k*n$nvwocq!9Amk}(Su+1e~w))_$q~#A&SuwRgB1=1! zpi|t!7G;i}RphM$-SmO$5i7Z^fkW8)GeTBtV=Iwai7$`JuY3c2`gwU5HP_dY3pf))*o=m6gunT6qdq8*TsrFON?oTQp%M z^D9*{NNvo<{Z;1zGsnC)W_l;^xVo~KA@W@PK6*`f;A$>z(F3dd#fQ8B&QMx*b6DZy zJ=SkLP2YF4Upe2T=rp-DU;|t588+pHFxptS4!C>ze(m@R`N^w5D%=M47g}w;g?$;N z^FSoP<;o=edjd@HQ_Y-E4?=}Z?Wy?+?AY81Hd}Z`6eihue+-uVO$~#Gt=r(Whyh#O zF*V|~KUBr$1}BGg0;NapHd|Vb(ocNh7`@}@H;L5Y`g$tfJRY1hhMJ?wC2ZP@nomZ- zlmiH*1ry1k%D`qVd3UbFZab_WRIrU!Da4SEc ztpf$?yr_?@(Y7gQBJTd{4f**}8?zd`m1Xn7){nv2t!Ohdby&_+HZQ#nGIKRxUzOMV z;7a(AT$WuZSt4Nhpw3)if5B914E!p&hL~J{9j8BjfU^=7EVJ0m1vQA5@pd&Ux@Sn} zvG35JS6aV=^12joZ!97uG8z2r27|C7Cp!~jjBH{2CgpQo9;;HY)|@Dk!zbk>-ye)m zC-_2G=UjJy)YYXp2fJfy-w(=x3#`4sc&a!AD=d#iAfE{jKJprly9C71^S{<#g;1R5lS? zzEer4aauP&WWsU~hKsIsF@g5n>e28p1DSE`zO2N1>T)yLX?m9~UE0Dw3OYQjMnoYCN#E(**;JQp+`5we^I(>pMwNCLMt*Q`?W1~1S1sbpxh%h$q_ zp;yxAqt+!QPZUh}yttTA0}q&Y+CUgi^fx!G2z)3WB{Ug#*Z8_udxaw!ns}S|kDj+i ztZd-wddfN?-p~zu4Y@c_Mwm69Sg65uyy3n>{bI4rJ~2@V_b!~3#1Z%o8L;xvJ`0sh zXyMAWN#y0yQ3A%h8)6ZSUQNd1O(1g#{acCjwaP?q*V*;%wmH(5J7tPqqat)4xD``B z9=B&_KvoWeO0Wf5I%y|>N}__024#>po^mGAq-oDwI(FfmPQchl%!qwps?$gyaUnv9 z^d;OQV3c(2QL;7}FNvIya;g&3K6}f}dUUG~rUBlU@kx4=ncNH=jp8yPSQD0XM72k^ zI>@(R>CW?a^FGLQ`Vg*RK9Ws0k1R21`}i68`K#VQDnU9){ZE$}Ot37lx*51-3i~si zLjentQwF4ZvGl|mEhQT!1ydg{j36J9i5Othaf3`JXY`5QIhhi!*8@& z-+wU6Bt(3e*K|>2%O3YSg7Nqk@q*s$4KO*1#ph+62^(mU&wL=sX-taIKi3) z%0$&yl2_}zSu9D(_~a3r4W$sgw%b;P%n#Cwck=tYKTO%jY@W7UPr_ya^9SRp?%(O4 z|I=4DUFEqbf8AyF+e7*eA!8B&h0t~j#3dZ)Nsy`ee#CcuW+oS|jo4^+t5P~wre@?j z(W9@^o`pA!Ad!9q9pGaBx^%@o4gDsiOTim972MEwaebz0wm=6~Io9HV4bbRjDxue_ zHlIM8|iK$BdyTXY%H-2Olr?Vgq5SRqOVMtKeW@`vdS=Rqrk0P%(Y= zd}83{M{MR<<<)@zrDy%)Ds`vCUr=whg{CD)eJT(&E4}?pjUO>GzHeB0v*EEoPPA$- zLM&k7!REet#{0e#LRPQGE|q2>R<%rf;j0*Pwm|r%sCD8RzhG#X-Q%u#+hBS(*wSX_ z+@l%U~qt)II>VT18y{WSws2Ur0WChH%WV_;h6d@bK??S>D6ovRD zBsofK@F4Hg2;rJ|OkS>8A)spIAZ#}hvx8aOu7;F_CllXo!X6x&eS55jt&^-tNWogX z%vuXEWD#@*Z)H#4tYQW-OD9t=u$i1@a8~H}8Z|zGCr)omH;du~bkS)*o8#*KA zEm@<=lswjm-Np!it1nzH+Nl@P51XoI8ml-lI}ByOORsO(H$0%hBtsXj#aMco z8m|j_1@h2YSo$wDZjkA@pw6A@+WX{uXxu!|8<36%cO-Z=N|=#{1B8g*hN55u4Z@Es zrIe1XuWHq|wB;~gEIY@qZF0^|h(WK{&B-800hCZ&8&SJSkHMlwW9tGNDv%ianzInL zhjC&)TP3)uEy;61_fncXZX3%4uB}N*QpPH-vG_E$LSB=%UbKY5ediIj+k0d4C>yRudk(b2QWZj^?Rqq&U_~!IBGgG~(I@?oHlwy^uTYmXq&IYF-higA^Puz6h ze4eDvwWr)0-#z9%WjxxTg~`K7BNrIGRU%_5TlpSzDG}pz>@1J=#T+g7=US~%nq*ST zs+3PVd#3n?BCc(YIj)_cchny*kL%g2O;Ss|Ek9QH5ZPL?3j1_jEvqp^NHR{<8j>$; zw<_!%5e7-_ke^zqaSDPB-!)c@=s9Ws@JOG|Xy*eO-s$`r3!kn=%+mo*Bh}}jt`Pa} zA&sq8idV$PBvRI|uKAjs;=!oL!nHZf-BG1a}NflD!o#VRHq%Zqq2sVSL>oM5KJ0owYUCwQTIcoRPUp zuSAW{v5|dBgxjkLZNYbEeEsL0APLgLcRqRmH=Kp*R7Skjs4kdf9AUZg9+R1n^TG>E z0<~qsPG)JzjGRMM4x>bGSrNKpCPz5XAeM1R3zKfC4y1dS4UN>29{8>Cd{N+OUexO| z8gjitRjPSj3ZStt+%PY^`yg)u>eJk0HSM_&QIU$_d#&fd{0d>-k8~SVp`wR*Kl;F; zKL1vW8lu12sVIZE84Y_s`eb++7#*Fl;4XX)fT(v1TO>ydbMC!JcAv${TJUb zPrX6Af7|kHn%9=Rz0B6-awCuYxrcGc?3T<%v+~v}@2~+wq~EeEWiE9|0zUIcNk~9L zE`&6*>FZ42tCZE%Xb6Rp?H5dh%<*uaMxkXb7)9$GTp%`%*YsENGeeo-@}(5~4#I#iU*eSAW%>Q+2oi@Ph5SGtL5$t`x3XS1`^48PVz)W|<3 zGY-ShwL*h87Vxl0JEiL+`L}&5B&p4B578qMxg{M+@Nr*79UxUlx{b_5$y%g-8_Dh? zGDhSiv(B1O$wDfs!u@@y;q9cgK9yEsvGV}ZKAd=y(11sn7S)`;dk+oQ&Skec{LBEJ zLjFKz5d$N4rafjlVTnmK(*cWy5+$)Hoxsgn4={r&X5oDn_CR){|EzCb)Lx;?u=-r@ znt)C}J7n%%tL4{6x(_Jd@quTOrn|U5WA}(Ci|2dW-DPWzLHPLBrg_VvU%*FC&4!a& zF|jt`v**y}=*^~;jdA4>2NO4|P;P{o+<0jOvcL^3RQ)i+9Tl_MA@%?h7`XhL^Q?_V zR`ui{M{)ISgoPY`z^U2sTC?X7?QG<=Vh@xh{+2^gN~?AtJiafv+{ml@?(BQ-g5G$@nJ%`Z0# zUf3ABcq_89o~=Py!X=UxlpD5!zlEMZyY#}lh8G53foSTxtmjvt2gUZ9pe)?A0vU-| z7#XTsukE0iqZnRa%wHzDrnYE8{m!LkscD0zwUT`(rkJ!gG!?X;(%lTsM_xjUbOLZB z!G1LO1|x&ftux`@UbTu==OM?ycoX=Xg|>Otjis?}4YB#<(|4%{F5ejjxkG{%Fdlhy z^-^-}yfb$u^8$&+7R1|2nW|`PJuw{C2#VdVy6`qF|oLZ1HU^D5SqE}yL?nL3Fi_YPU9imv29ZQg0# z@{h~ME<9N>w&L=ee)FQ!X9HRrz)4IGSR2Sf{&4g8$kFWvBM$ke^kBlsb@+<4 z67_iW0@81&1Fda*{f*rIypoPm;aD$6CA&oVe4~QawIS8#qSGft_6XG(gUO26;Ld;^ z+42~D9mTR4tya|pzbc*Cd2BK5By`qXs6_cny|wXPlO;~3p&Qix&2Ayd3$PHcKH~b1 zh>~L0edJf=J`+OSPkyOVog?L#@?>-!pMdP1lUe;pudFALLMt3VRD%BaXhRJDw&d5!3LjQT+6sxl zIkD2);sQG_sP)LdTY93RvZs$B)%>^Z=$4%}NJFezq1G|Pc!hsfZ`hXM-B1Wq+I6`1 z0)8!i@7blmbSQ%0hROgQ*FNwVkG5;$y)=$0$K#b_1y9CH7UuR7)oy_8K?q}ald6(K zN=v-ap5B4YYKiHC)}+<9??`%sLu?ZVXkVQmqlK9UCKFszh+gW zI+L69ZOCxwq<21TeNLv`Uyrec2{Oc4j9oOp#qxf0xfSOuaW_clcahqyRd}c5DaEBv zOcGAc# zMv@*K-x{!|ogAclVYc)K7B5KxEM>S@-BVwFema zOxh^?tjYNCw7jnGp1$MuiCv6)*gT(W8mY3v{bpGA(%lUr8zTsY$=}ZuH(h2>p}ufA zRcB*)CeB``DNeyx58yRA6E3uPCfH(xc7#sc?*cH+;~$-(vg~VP&w!?^FTTc4%bHxG zzZ0cxx3LN%(NYhu#Ms`Zv8=v z3gh@2`eDko#_F<1VI#K71pQmhT*`Gkxg407dOWqx_i?^NWZKvA!# zhqB(!P*Jx9vmDn-?TSF>8mN|$IumDq=iP<+v3+=MAk1$`M^-~@yBAB&xd9XQRzO_%CI>6nTCKf;9@-es{ zz26VAB(n2aac4LwcyUv6bQ@XNIuZs^uPGPZKKvkvk!x_-JS$I_)othTV#N}49ygp; zFa^(@Ug$1ie>vN3vvucN(}eIiVXZXR{P~t3kxo9On?d-X=Cqz1AMvAE;Ibh=y@D zP0>{B*h!-(Xp&SMrNpZ?dUX@<{Kf*Y<&X%%Pi<`8UeC+I zuHEgc6AeG}_<{sl&sp7rTh2h+BjcMy6Zuan?>a3j8+mRws3N1r%kP32**=Ty4Xuuq zmvYRn)p^jDeKMEXt*=irxy-Zpu|W4yr(FHi7GQ>uu*Yn26c^1fv154n5~2h}kp!3U z`S7J)YGoliI$~PEn5EUa3N#<78)5})PfyyCB_shw_A_XguW^=C^Iq0IloZ!Diuawr zoif>M?QZKXN}o?A*_$DGHlY(FCPL}9-p+`i2@r4UGuDb5~C1%m`~^#dA1P?4LK1#@r>t7pzVz^5LF=7+80yHvc* z<_jKO4KGQ?VJl6Hv^*pQl$c@SQJ2XJAMw!DB_z#9rsB~YPY;)4 zm7sTUGyJp?5qgZx?bWMis3I2%uE^;gO>M2JOS5CbO#AWtvZ5=rbv!^M=n8owhNFa| z-jY5W7d`S40-mgtUPi3%m6E~%lOWR6+C8FXm4?4aSiETa;6Ya!tS-K%2CJv_G$p3x>&4vypSLau>O?C28k!x#Hbb8EzQN5LgoUFs!E`ZOxCI4kbrrkOseyOO= zilVue%ctU!%6Z{H%X=4 zt=YtTttEpFG56qQE_r){BM%B<>OKVR-`RyYP%G-tlxFrI+$Y$CQp^u`z3p1#JU|`? zYfBw9NKpn^)LzJX#gZ#4WrXn~zf-6f>$f6t;gsn$cRk0KVnz?$)BJEJ8`qG z$FFTX?08Hu&!ob z<$7>?GWT}oLq*rqesaQ!sY=M3IsKw(1VaxjMkR$NI5?{$)hk$%oLv{+m{&*UaBnkF z3G9*V<$jzadKVge*E~^Xp4~v;6s0TC#xbch^A57;YOCtH*>`tu0zo<@$}8$eG^F&Y zu+4)@=ye3!g+Q25)u@!ca*IY$#mT4XWgaC_!rRrYGkf;dgZ+cE&V!YK2M)Ym<686H zqK*;+I~U*}Y090-EIzLdp=Lz;=3wwACK)?PjckegDn|I(#I8gWTXqRO5u?W4bG;$@ zBH8AkDUr5`D>mC#XP+EwHImqV2;ft07PgWCct)F{oby;Y1?~}M{#?*dN;|Kd5 zb@$-~)oHE!_F~GSD-MH|f{m^lN&5&1yEwKSlr|In3%y)yepF`l zyJdU6eG$uW`(U-~tPrdzpbDIu`e~RwtRilMfuo~g;=j)_h3a%B)igcnUqKR zWbaq3_QqHtY<8!5ky@gq6B}9c*KCUNAARyzm9uP5(aR=e$e++l=cmr(9XZt>r$M4) zcXveB)y7++XyyFR*%snl*b-=R6gV}8p@6(Ga%9Z*z{HXae-1ErcA&DIzEd&mBR1hX zK^y%tA{nDZCU(Hw(H&X1kEtGdTB64BRQIA8I%Z9-fk;?yK?!`~y>$_xx-3lYnlHo&(Fe-Mvj``?SYR+2|XQ1O)3~yuLn%K@tP_oyEt+=zUgRN?;$8{N_61EC{ zk0rM))0=#F#`ZCxIlJbQH7nDHF^sUtO64oG*w+rS4vqao$ktS{orLVqtd z@=PT0nvcI?u+t6BD>cPL@6sfF60kPK?$%&XPmELvr`QC+8H(I)4t3(YW7zX zcGp*wy&*()kJp+@l;cwE^kOfS9YN1@(vu_0?H(PL!<Q3 z**iMAqc3uZ*(>H(txRp#vWw(p6?q^+NfuABoq;-vPQKf#E7my$*?@acvj=PP>SV|? zytmo&)nX%pRLeY@2UbaSxedW=^M1TZH@`z|}r14HUZLKT?n;Wj8ONzB2b zyX1%+)uOk?^3h_UC{5W784qP2T{hl9A92AO5~JK6`H*Qcm}x3n`?E`>>i$^?62oJM zM)p??g z;5qXf9^Np@^Q4ajBCgU#Y*u^lShsuqa*pUJ= ziC$&_9UQ`*h&IGYG%7>G$Binjk_?b$+g+coXH8? zO%$Ff^lqK&IPH!BEmzkk^)ZK{v1E3)m2HazPWqa|H9S=gM@;K^$NwI^+KTcQmXeA=WFm=!P@2AI4VxV^dR><^N;Fu zB!`W4=Bd3)&b?)Vi2gFbRky}sB&lbF>1&Z!L(CJ?IhW8$v!%9eD7 z33c!7X0R9cGgT`%QBI#P?2HL5A%u?H6a?=LC2EJpISgGb%X7=VwAX#dqHT0? zY^$=&Fg&<9H=eLpU-anNk*tsfQrmgZ%DSaYqkM4}*=yhFA+pBTQgu#A=QVlXWYo9f zC~Z|3fWf9x-~b;nf!Wo?L3&-qMqU)=tQZz)Fve}|@8DzxyuusQ^V-N~(KyQ5J*1DZ zP_lOfoslB(Kc(K&L@|=EVd^oC=g~1~P~7j_7`-HYgc4y5GC{#Lp66zlxGTG_mh(sH zm{;ZnnukdPwjq+|Ag``dihVTKppdn|D|%68sM~IQt1>FCoK%5hjt*JDqP;pes(hZE z5+5D3q6*r1?BLy~IkLH#IefFaez5h**0W0ol}p>ZlpUM!VcxPqhYA+v9u08E9ITgX zFVLoV;IZF+wW|lDt}PD@^m8t4TPp@oiP_d2x|a#dkpZ&i5}2kcdv%6Kvj{K4$Kiz% z_wN`{`Rwr7qV*`MpH2Z-1zXG&z^v(@cMxVr*k*RitJJ-Swcu_ zlafa3aoalLstHhmnegRFI&9SCuMtkBl)fb6)0TADG+{T-E>Z!UGU!jz;+@SLY@GDd z=mUU4ERASc2A@?9Vu^=w9PB^nujp`C=|3>aG7FCp_8v5)=aiT1$?vOmAsi#y8^<muehMEQ0D56BRhC*`*FG;se0ClvLEni)|-1C)<@vg4a9mQoM zvHG1UVjvuvmZC9Xm{nZ};`^3}ccjF70p0mA)wF#G*CQeP#-*T9<5LQ7sc2Ko`2+qk z4xyGw=^dwV`2`@DYBeJHu*f;*)xvGx)s-OkbVrSil>=$Ya6GZCeZO51H0I*h#}NeA zL&&fj2vHHJBVq8F-F5b;f8-e72)W6|AL z30Bl_j=AfoAQd**8|?w|rn3M18k|O{3-r$8f9SX;+UVh`N`30IONV*!pnzq0 zKQGw37sCuh-m2|Fgdsnh*m_anl`e+M!A8bafMm1@_O~u zr{2I(KU=blxeJ;ReOC^r3+j@^j#iE)hcASy+Eu?Gh-+aSav43KOwxMv$$FLg1?hdG ziWx^P7sBC-R(sKwt2Y5{NPkvSey<#RxooL&FH>iKX>nf*=5deqBRyx4bfR3g#a#mb ziz&i`3IzNbb{r~|!Ty)IQeE3#mS4$9hTM4y|1H(|M3k(vh!tT6~qWRok^bW#_ZCTVsy;jm}J>f`OyB zy{Qjx(gJ1K_ioqh-4;mfvzAD0HKX%18!r*}2&>#@L%{{PS6zG53}pn5V!2kwVA$dQ z{Aa3U4fVp!zJu)9f`mhfZZHXpTBKE_@y^-BOxi9| z(!qACGQj3mP__(B3Ut(Uu9xjuazv$P$+j>Tgj7s=cpP@7fRS!nEp>jEI=K=YIRR%p zh2z04A5wmFXA!RxdWLeu=6#9b^CLGxhy>$*h<#!x&BKR4zz&wadK`R?M!x`e8Af&F z)m@2k=zG1}EM$Yu5g0^cD`7{g43l^sBE=SrMQ0`9iA;;mWnFQ5%KL<48uG`KCvWDR+KDz!edM zqV0I>1Jz-?&ARErcCn=3uo)m>tEJ%D0>XUqZ4OVb7MEq&LNTMKQ$tx0vu6oSqncB0 z3t%2ZdMpY-@!I`9?MA%?{Opfc0LNi7rFI1|jJgMu)oVt#S-0}G-=>aoO-^lcqI|Om zE`OXh-3jF3s2{4-p^l@{PCeN(&mRGP`PSCyaBDG2TDzD$cPP8HLw za$02Rt~8~+(qr<82jdq$bohb3JbUWX^2WfWbw!x(ATmy0&`l@`fAD&33>=g z{83}zD0bT6cm#N=iJ=pvTjJJ@+_of`Y$;o;x6TP9&w?GeEdhr6eg048qxE$H8-=1Dx4~9Wp#5 zRKqJk!n6+N`6i<=#BSp+E-bQR?-(v;?e+r)P5 z?l7i7nigy4C1%7HOPMWK8k&Wk6M+o0jAo^``E_kgB6+mWGr+&ZBfEgWv z#eb2!_2Z(1&q6_=1fbK}mPC_Wy)sW@t2OH?{PTGQagVbSr(;)f>Vut_8Z<{%!|z=d zdb5}OaaPTNO|5k$kKf=NZ`I0l_vsiSH=MQJbq zh}9dGK)NlsWZ0o`G(ptOIbNHd%@awLWKBxmom z+bels@lrnicr`9?!dE=}W8&lOPmMtr{fv)*W~xgcLoe37n~sC%j2xcA<)?17Bf_1i z3u+xC)i!+AP6-)OkP}S~asgTdr8!6~-wjo*0I-$QPNFu4rmR;_b$ct~h$C*lQ#To} z{3w^(GCO0|Lu@N5Q~8Chw6w>5C()9msxZoA7_SySs(&#@`QIERW6}B9XnRd zb0YXj$oDoDN=8MG0)3Z0bS$+*U)&rISY1dhealtZJ5it-x?NcgSUr9bJI${vz@{GC zc1mwr7}~SPNGcs~?Y=*9=N*LrGGu3`(z%9cP;lxI;5JNIa$l0Bgp*I+wez?u8spIt`}bHzTgJ<1 z^UvTa_tT*+PoNlFGp5E{#kTzKEm`JGZaS5`HxD1;zJjD3hpPyzW!ZL+AlHjDthdaz zPm^bjf)N*bmi7^yF?8$>{Z4iBdtfKt+BNoV`{D!S{Z*^h`1ozFgyddJq_%qioo64s z>~Kl_(XOsD??pqr=qG4dH6A zmnDuqOHxg0cU$*lrFDWl+%;5_>~==d0!2i%A00*khD}(;qlqzQjI>NFPnhnTq6fz$Z%No?>d4$MokFITIIx?|eLx!3kjdX>3So?(`D8%qL$ zM*fZiv&g0;ai2x3Mp?`YQmR47`o&aVuU8+JiGj=~n+_Jwf^qhHL!uj9ir{zepE=#v zHNs|IEM-hs1W-~Ho*x$0T4(+R3g1rLtxHgI-sqa6u?~^vo#lp=&S*Ik2(ZoQJg%pk zmA7_Q9591ruOj&a1N=rv0Dq`E#U*tI8fw_O+KdlF{2hiky9c7$T5TgHF_wZAR3v49 zV+NT#!=3$*vH*vBwR!!ly0!DmKfIGH;-}4zzd{ z#EVQXmX~;+^(b|>W!uu>=S>RROr z_nNp`Ca+LJ&lrrRQr`PTOYhiSU*BPG73Et3k6l!+ zO^mSK?$|H4X`Zd2aJec@)QK=Xvrlb;qx*CXth zm?x!Gv$~7Z%#aspA6i^LmAl($o;$r3fz*m~gYOIBeAZ&#DjR#_hsu0^oX{IYGAe6& zz(=d4eNp8yqdSA^d`NQ?;>Vm!ay_7q8iQMs3=YZTpjULDD2VdHjk}^Rv z6eFaKKnaI!_m%bUhXaXW?DY|A>z$wr4Xe)$t_I_7e5A$AM$A~2jM9mgqr&*$fm!q-uZA;+HI-Qgwp^|)&_lyqtP zS-X^qt-@SF3%lVJz!8*I69`jwu)9q|P+3!754cvl{6&cE8RXKb3r?>}{LEbZevYxd z6~|PT*b*SPfv>JPK7J1*qc!9wcg-f27qU21sQ1n@UT%bYq897up(&=WNQ^Ot>Mk3; zHuk;7e4*^}RT;3e!{SUmgNuVZ8>fr}T4%-9NCM&*kmn$oAfs62L1eDq!v2>lL52qZo^a`xadWCT_}+Lm6!j z1w11)Huf?~7Umc1+;;G*r)?(!c;!ICD@IpByCa+~Y!<5%DI(m;? z8w_i%3VJ{$a;vq{k}N%pWSUnvK|Y$B&e{>C>v4rcSQY-Y;3Yc7@L7rw;|IP-Y@J!K zk2r9sWM6r}4GvMcQ0cov1mzA!&xn6)-m~y&Pi#AH-^kQSD%D^Y$o#lI$*y|9ibK37 z?(kBqB!@{P#y-xmT7fD9yE4;!JU@7`a(ZOFr=)^b`_Nk~tU__L(VNPNdugj&Wm&n2 zuBT2DGRk+b5RF2`fa}$Q^_dVn!q;$goY`b&heB32tli->+f1oPwhjypy70=>xh$2| z-H4;6yv9?D4thk+WALictozs-eEtVgMKIn#V+-o$88^0 z2YJEYNCrdc5mSB^T-nYxLIamcG(Ox}R(`k+^0B2mcIEhz7dVr-67fWe9;~bf&C^#% z;YQ90Zqz+ zMeS+Tj$Zlq&jZP?(QX$q#lEJ(i9=_KSvk+RO=ymSN=(n(A>i&-_DKxIE+4Q>IOvS1 zT-}(zrqByP0s<_H<;CR7^`sE@a1)UOxo&l_xH$2hL%e%3XDGHS1GBV`T$go~>xS(@ z0${jQnb7vQi7V>(1IH$Hsw9PLJme7GsT0+&?2&y%yeno`IQV2Jc9k}sOr9BmNBa?-R%OU9p~@6%Rky?K39fwV%&lptO>i*NK!H+WjSdnzlm(I|AdOtEl6;=S zmV4rb1-A#UfmEu(3xNPDTbZz{e zByQI_b;AHgb&m!1#tvJMObC5;{J?-zT3SMU+<`G9${0cf96>o%QR~xHr)j;BS-3gS z*%4}SXq*4!HE=vpco;}z+zZV}?K3f+^8G?l-x!2RM@ZO@0Q>3rN#V@{{ zuGF`IEF`|x#Q(R0-fgQGA5w5`p{ubX8O^DIr=01j(O{-JwZEw(0G-45K!+wv^0d1b z`TExY{!wq>TTz1<6>&6`5(%jvd+?7(e!eAc55#o3wa@=q1=A)*9=r}R{ZK;3z zSSAjT3%O`;{SO~U5aB=RW(_9z*;oFz5r4jQUmIAfJ<08#^QcaEye%8ho~?ZRPk7|# zTLAPiax=?Fzf|}6J@lbgSeSbDvPNI)RsB5BuNSXGg8xK$xaR)PO6>O&dp|Go+emiI z0Lp06(4O$f&$kj0z*^_np8r|xSn)(n01bu-FP~JU`DeM@4Xjle!V&uOwG%)zd=9&6 zF8m|*36K1IE1eBkE5h&p63XAaxW)6m7QoN50?6in(f@A%`LX~1kE1_SVK`9#8|fyd zV1@(TJHlDiUxl-2M+0fdKv&EnC32qsJXZUkL|S$KTvqP;bKqx0Qpp6g;YR@!0-PQq zq4xRn(2Mv_cpeo`$$jC_WKz@^S;Xu*p;$VsEnYcfsRrLePCt|HH)amVOf0PN-JyKr z;7j^KOb_eLT8Hro;^@XlWFXmN%lTQs^aI?VA7fxg? zr?vE)@OT#bH_A4JvCY?Ku&c>9#u|&BKmoqQvs@egmG7L^PWk{9M~Hq9Wg=rNE>TDrbgbx^`)!K-wBZBUB-uf%XXSMDHj zmpvTM+-OsK`z|Fkl{3#6zC52OfJH+D-p5z`PyG2oNnaNF&>cE?Qw>3Dk;=n}28-$m zM|@cQ+;7FTaQw|ZWhMX}z--?>`T5u7 z-=cQ6VAbf-;TS?YX$6=X($qO;Wg6PHQHp3P6L4CduMLWbF#L#p?BL=YzuL0zl2#n& zTq$qgHZ$O6`SE5P=9>i53@M>t$CJ9fB^qOm59n#pDv_xI%Ed$uInURuIb|$NU1ejA zlZwQNH=Zk%>g$84ps5(bsfxb7M}#38&XPlF^0)o{SqB4dWiyYF=^Mf|0Z>=CcYW>k zYe%S7Yyk!m#=&X&I=^HN-$BqefMFAGBZr77fDsU!9KC)TUrNwHGmpQWAvpA<;^p7I z_ycz}G^>i<*J=ZG&@XGCiwL+@TM*zP#7`pYJUv{eOF}4Q`9&b0S4O zfA+-uSwLAdmLAU{Lg6bD`HIlK`lnBZ&n`v|5$~cP@ssXSl+Q5)h<+e zDt#%bcOtPKgBDtQth@dX8Xl@}K}W3nTgvC@J9-0!_G<#IksNMM!u5(VcUARPKWAiQ zTiQkQpnSSUMn;fae0&(EsBh@MujV#sD&>YO&I2BneRMTXb;224D3HaRvNFMt`V40} zO~BSIP z0WqAVU$M*chfNYHvHj;7t5d((UeT(0wf?isnoc}a;TS-_R*LY2o|&oRim491V$Z}2 zuvye@h}9(AN>s-Ls%3|a7B=S>cNO60HWL$GiID^VT#&`M!GcNQP4v&d0bt%|q&}sR z;&b%};r})7U;Ri2I(({V%Sruw0RB5zzVn^$q;jkorvOWrxp@0mL;ZsD+>ZbrM@0I6 z=?U`t89`+LS`4Fq{$t;NG3JliB(edI+jCt0U6IG{lMMrQnMwZq&snG^Ema$Me9nNA z@bB323$Ppy!*pybMU$Rf1^TbCpa?)>ch?wQ`!kfoRsu|`Om2?f6w3Y!#P(EEfN+Wds(p_0!h%1ptemy3+7^}if3 z)e;v+%n~{Jz_K&4=Mt%qgBF>Xy8+Pd)jb-(Qghx1Fbx@?iYKdle7VVHnV!Pto}P(p zk-VXXDuAA-oIw@KsEJ$^?SkwFZ_RYmsWfa~;i->Mcpqw%rfTyDu zn*Yoch3Yc_6djr4*!&w`$~OQfE*(>})kECBAadf5^NIi_eXy%B=lyGgPW<5pD9_!H z+qNg7=U;fD7kSQ~EOnvB;>%v&n8yP4L3I8C8h1ohmO57q?7f-LJQm=~j2j1CZDYZ} zA#MV#q~Sj#1{&jTe&(|*$YKTP-J;;x(>fCZfa9{5%ZT6reD$AyO9LNJI`{F3GS~q7 z#QFaK#vCEwo*)Sc{!Sx5?)>r!z+Hfn*wy%dh_zjGz~gN6-|_G_g9DAe0A}>Ryz5t6 z_h0j_q&c$2J6{1V-gs6nrr2sE7093~(Hi7V=fR=}#YZ1IW7P}d?*RQpv-|I4n=wMq zUZhXuFsw~^YipW@g)>#6G{~LGgH;rQBR zyz3XJ_h0K>Rq2S@@2X4NV~oz!?3fZTpm(zN@?J2L2P%H2r4|>r1BoyU4_sVazKV3c z_fLJmO_4U>#i4$n_2CX{H*Ww~{_iHiH(dd!W8U$(-rNL=tRWBvcb2%5O<*`{cog11ar z#lW?e*nY;tOBl^Gxqvlx$>SWF&J>YeiZ~r18>0b(A_DM6F#)(}-gje$fO*}x82s*4=u|PgY|w$}T?BZs z!)_f<=654(+&q^@!$g(pB9=546eWM%{TThx>^-J(3H0J6RIc$ zRG!5u{}ID;x6HrU)2=qB{8n`FYI@;tb!edbT}=cB&NBaI&UB|2PX5Or{rjoL3rD-D z%qiORL36gbass{S7Gd&Dp0CH3j18reOF)!iq9rym3}XAY1Xpwyz;~%8a%uYJG-KeQ zIwj742fy~B=q zT`C{0__8wVsEvA7SDH?-fuK2x16(6&bi+lyIG$}(-+v6I(Z(t)oL?Y`-_u&GB@i$l zbRAAy+)+4%5173asJpBuE8%T+7*rl$&3F#*b*3vfZ!sXvCJP#|1uJ90Q@Ag=st0gGq6^C=2jW(sGSMDRqmjfi9UoN8WY z@7+hJZ~9?S4J5K=b5#$n_!X`Ai!Wh1SCY&RKbAZBTj&@c?u@nBOQ*HF&BW&*(>HZ& z?KBK)ddj#fABTTJRj4m_-jBe3E%^8#9&12SZ^+#|fNU;P)7=XjsJ7yrJy>9wels(N z3{wzOV@WeL2^{|$E(IKwo-cJa#O(8L$njmubtca4GB8H1&~`Fi=x`ei6j>j3c>l5R zfyD52%tL1UVRa_VXmx-=alu9GPxgO`pE{Qni`8pUY&M}4IO1H?gR}TBnO71LA~Ji< zCWZw~_Ue76P(z5^DxoJAJY=x2w>9vY{g z`@-r;<)Glso@Nq0!`k(CT3N;fGetnxYsKUGh1}*n7c+n~z}_{EtI&6Ze-nm>daON< zvDY>JE)6T0fR2#d zybG9Z3EOhQm>Aja6vYO~1>nREz4|x1k|rqxjl0I82RtG1S!?nBgSs1B3AH3@cUwaP zkgvLOQvqSg11>))p!Ji{0L=l!nf=0q_HXv+yVBfsY<7jzlK+#V{PM~S&}UbrS9Ii0 zbBW{uW%<9Fyp;VxG2| z)%(i8T9t`Uj6wr|Of11*XVLB%ynDt7F&%;X(Zbsha&* zZiZLemN5+L0%a%aKyo-FEg+y#iq-aRCN=bLI=47B_cMUQq^ij?snd+{XJLNmOa%kU^y`zC?$$_q^EsftSBRr^I@!!vZ69#9n{x+0;^F9n^+p&mGI?C@in$ZP39@ zE5256#YX#Qs{X6+!&7S!$vb}DI9M$=g{m53)Wbh`rhn==b7;wCCG>LXy*p z!_Rc{&Y_)^U>RXg-LfDjOU#U9h3XvBI8WT!lhq;d#BgytS;+Y9+vG|FP|?xtX`G_W zzV#0t`;T0L5QpS1PD|tWiL`E%m(?#F!bF;#^=D44pMGC0>_Y$OK{2IRj~5$@3opd* zA}IxcvR)Gw|8ds!pBqH#fB}@hws7aNeaK1K+{!88Hnw?`T&KyOYv{0-Z&$s5)5+C8 zaX)m1cM)f6ldWpOhedqpqs2j!`Ae%y-tSvE6%q6GHM23S6xYC%cvMg#AS}eq?K1w! zFe?S1$A`z~9eP_(LTRUC%xbzgy2f%6U-&79~BDJeA zW6K)@9Sk}>;q?B2s;6|VVhgTOy7aEizQYn}ZQ@=oLLJe`X*xYy-;h_8DU-VrcKSBI z=ioi}?Pqod#CjydbsH&JC95yivzt|A;F@kmx+aDL^cP>u+le)CevXSZM$S#pcNsSZ z4O%DdA6#TBM>eiR&o%?DZ5~DY&g8p_tGmU!8`BE%MJh_aizr9SPM^)^nvUgBHz)9H1G1=ohTAz*}XW4i5Nxy9HG zCa!!I-7~|h35E4cw4ICQ{xb0R8=RImOeDw>u*$y}LKJ5+lN|LuPw=bS!=|h?v}B4V*vAXR>sQC0!mP=&`!O$B3W2* zkh0%&ZI?xS^{YL!!9kg8Z&Vv_6%a8TJzg5Wq3#qcn!fS!%YLiKaH|=7-o=sW@Z@dt z+Q)U}DYJo26{wcn%Y(2MOzgt&suw+GfjuV=_m^Ixw+uu)FT@woeG5wdMhdkV0Mx8u z@yrbO&!mbM0}vow&SL$uU@(*z$kyvNU{u>(X!breqA=Z2y zefzKFpY)?@?5saoxup7UL(QbZP@;Uv1x_cW0dU91!VNRp&aN<(JFozqtJ%VwBeQN! z`t|wTkd^%PE*!>TLC9UiZWP_DZyIrm#Ut79F=xf8(urgRFs#WYAnbvLW!E`yim0Gy z0<>APkfqsGUmZbbTRt~SH_HxUzb6ah&k-|`7;tG;nC-qOm3pBrpsB}D24-YGGZ<}A z-_3J9z*o6Y@2-sPU_^>EF`GP}F=`0{Vv~W0m2vAHGOLmYJH&7b>guAhg=qq;HLnI- zj@m7(BsMLGk8oQwF>TzhE2o=%E+E@U$2GX-d|oDr^{F@s2D4n`jf{pIm>*;L7b}dF z^@jc2fYV+o$Rj;v@a^&0s=+OQ*YGF{k+8*%Ds3pM{|mdnp@ebF8+PXaEB!(p?!CJE zoks=SZl6X-I;i3`gq7s4yquI8>yF^l=8(j1)(bsD!XlqgHoYU9&Kl0gfiom*eYGjJ6g=N~|c@zspOl-E?6V))XqG!)Cu z!m@dYP74J4&XL z%^x(ogmHlle-JeX1KpF2V7q(p_Q-141g3Kl$DdM&O+G5$gp9|`$oX*Rh}%Y1$*uHD zE)P#{Y`BUBSnhGs`$y`i=FxkAUm1z(t%(B|DkVH4`wR9IJ^QFfrA1XXeN{OtUcHy& z?hP1Bax>zLT+SklD%y+}6qF2)CoiB5Rk=~SWdh|{ zBTliaFE(6bkzqcsLv$LJ!TL59te!Zc!F=Ift`3?MadBLJlSNDpCyn;z=W$=^tX^wD zM#$lyNNb`0)=ayle0Mm&v!^yJH&Cr!CEa}8bE^Ng*~>@$Wf!Z`bN1Sf#4KsAo_+Zg z6C2v6T3~2VWl_B1IWZhaS$8)!Id9^crS7X#wz$?jz{ho59oPZfNl`A+qaiQe+VvmTJMgWlbZt_bW#n-ps9Gw7!SNa!@#C~a?*e6r_e8S?@VXR z5(bSid#j?B4;W`(&3yt*D9Y0w)?kX)aIN3ndvxiYYD?}o$9X6`&}UdFN?*xadpGKd z_tEFAg7`OYfAfjs2}~2I315>;#g!9S63y#VGs5r|~TeO)^&9soBzN2U0 zkbk~IfSVD!TCGat6OzMVO8a~Fy|ljKJ|5=3pI2>sc+k26zP6=Lp`X)#sl%11by@4od1ON$b} z7wzI1e>(M=&kl0hS0(EFt#}kK69x5+K1q)=7@0K&o;dhV!p11&)_w__V-)><1A0jF zGJZb(McOq*qJ%SI!62^ZI_D`+?1r%p4(u;K4fl(cSyQ-_s#JM-ENYwIt@-}mj}iXu zS7O3hK&G||#!k_kh`2wawfH(FaGp4x&h!t!@NE@PcY44c6hl3V_B)9@e*Ov?M*Br- zG-l`U*t7pO`JW*4?U7xeHLlu0)(0@#?BJ%E&j+ zisK`{l`TRiewqbjPZIVAwDEoa`OdyXZTW65E}*Enu6ICS3#Z(9N&!Q;ww^m3d^3NrQi#v7fyRRD3k~G zubl9qnHix@r*le4dKzt2r2RF=bzcDDIyCVZ z55?oV0+w$gxnI_sOTt_;YrKfI$hbEaxS$#&2b41fQjZOK0WLI&V0(S*4}?zz!F6jp zP(#LQ!N>&zLH8b!z>3RfC^|i`)=P5A+slwH2PMR)JWxxDImrtNBlw&?`n(-d^sqfMKna;?P!7GZeY!(b}cF472UmJ5k{ z(ihP-qXI4%jj!%o8(EMgB5jY3>g4m@lB(XM26AWBuHILB_IuEeX+ymOxf>~C$c(Jr z=JP*=Mm7oQjy#rfe@t!TDemr`d$+Q5M^3Kb+ya-IL)vWF8JXxwz1uLYgL@Uika9_l ziKxKFGUPc9_|9JUw2`9xd@6djrB#RrFVs_ahg$Sz1jm^v&m5vV_Lj}*q`w6)AWSPR zsd=o%vUtFB*qHBx8SgN1T0io=WOA*3tnk|9m#?}S3yP_F9zNjij&$0+$L(vu@s1wV zRHP%+P7#r%Nx*Z>-%A(%FjyI-@%h|DDYuyN6j`1AWg0KVkZZKJ>I!NVPCaZO8#F4g z(Nc7}r=oiLjHWVAQGJDG>&M6@qqq8wnN6+o!9M+rsZ^4uqXi{;LiEvcW$&N|c*FQ? zRCrQ7aHGWE2BaS8aH^*}#GBzc#|rSPtKiBP9Pl`c=YQUQb&X4sW{y3sr=VwlWHz!- z#pT{ewpEyEW3+|HEtOYc*wx4SBB_BbvAX( z&VkB(Pwxyxi1*iwPm;n=?rMWfR6WtylIJiEB#znrUFlA_-;e#Bg}$gnyPW&Ry5Tax zSKNCluAq>YZ3+haFwDu)5&VbXx=4*(szc4k2jb)^NJ&kVnM0wpbak0th^wd+fByXW z?(N%gN~)^po=;F%uA&K-E}iFt<3u;3yf}M%BhJwzVhnk6;Fi-@xkdIm`|m!cswG{H zK4z*Yj=4rRQp01yn9%^@6xxHiSl_4jd*Or64+n64dQzYEPmt>y=V>bf-uWN<9q~D* zl6-=1ARtspEG)dHKPdHqP%-h%8x{_7DHw1Kfsk4E_I#p>32cJI|6%Se!=i52^qEal=`&5kXv-`y zmP|IS+3e8yWcH9y=Ldu3q|tGqH+SVB)Lz$xm^AI>k9n1aSH>HWo!M4OV>gvXhVP9* zirdbD*U<)*kjH8$RY0qJINe)h+tD~hR3zfn_!EHZWtEH^MYGI6S4Jp2D|^t{t=eof zup)e?W?oR^!~)ayF!eWIWaDnw^yJ^YdGEWo#( zkcJcvX#4l5eci;DP9d*#O0g~Y3eHVJZo)W+p+&WQMv?BeR9bQbr9Y38!N;S+ZO14J z?B-W5?nRHzPVEY`eVSw6^fRNeN9U=0;DWGI4g8{=ATafLwm+gq1CPBPdSc0aLs!I8 z(|(4iIi(f%fca5tN5_r|@8;IK(Sth&^WE~23+XRqjsM(+|L3x=y0b1C%DqHgi2v>W z-ZQUMV)<|+2Q5=6Rg}t$E8&!Q>6=HdJ0kJ)&78KpWH)W7jam-d3+CKI_dkM*JVkn$ z-&ySaSPvM+)i(=ORuIb{QE=Mq_>OZsTIi<^&!ww{w73S$(AvIx=ZGQH1mjTb_!{m( z>InG4*LSD8uN|WdQ{9%WXd=Qa{hDtgpn0W3K|@X++WLVGNFT`l{tOMyxTcVK7%D*Y?o)tPB15MIX*rEO<^?9Lc) zt@xvD;u{LUe{NKUJ-zYw)a;w~^sffKR$k@B@0A^bGnfG28A&?inR5XYSbQ?GgaU6`X?)G5!zVz|$;m%)^lyg^xWg#YZQqt9>Y)yYacM>HDV9_zZcB^{Hu)Z8CI_HGh;aa zX}gU8)8l2*9(9h`_8a;z)#E9VS9$S~{vLn|GQiswUlCtF)Ib|KT_*3N(Z;vCQQ=n^1=A^u_i zeqjHb4sYvCuauWeGLB!A7AU!c*~|Kzy=wkAh^`Kc;0>9nC2@X;D#4SxVEw-FxgL0q zsgscQMX_7`Oud!`{}p>td4Lad6d6eJP`b_vJHVn8BlD`Cr8qbGg~o$|T+aG^FtNVg z%+J-Fi}Z^ln*Kdl0MSH`qNgW?{Iiu(WbamovRCiqre5+fE6asU&WhI#O`7plA1E~J zR$srVdPtLZwLsg9;_oL$q6$x7DCUz3<Qu}<=e6Zhxc>8<_b zc!M%G-5yIl#;@P=@95C_u7glTH1$z*;syYfA_u-&Z#e6`H1^b17D+&K8}B9c()|Ru zyghnd>{LT63!0U^;>Ij-e@a?AV$&PvFazj<@i)4GGi%MK!ZW^L}rWnX6J}3R30r+32#-uU4{0&qrz^huAd?`c%ESsmGD<{Ggn!Xk1nVz0^*syyRU#Q!c=av#!3yfqk zI%r~U;(=DfsF2&DLad2w7U5~5ztmVQEJHs7y`Y#dvM+rwk*2T+nWIos1&h}S_E!6a z9B4a=p`iJTW8BQBF-^_U;_ZBCY?x^&GH{PBqpNl6&cf14T%q+uB_`nCv5YUORiuH| zyol7}RP?4hs)*i+9qmFap1>AzcQsE~7bt>=LSskk3 zzXV3intBg^xxCyydL?v=wXa``sy<4H0>tqUdxtHj4&^@sULLl;|1GqkY^(1ZDpn2# zlQDHow+3ajrD@uhidXvmzgc`6SNB%4L(wU)^;zkSj7R;s(NR(K7>AuX!Y#lHP%MA& zg5ijy_YSxI0xo|WJS85Tp7ZQ$9Go3!HT`qr|KolB`)K)> zKLz)kF%rk!z6XE5+y43@3vvJq9VkWo|8jDI;XT<&cE1OpC*@4mnNZWx>YEd@l&Dkq z$CJTcc_57l`vx9Z*tdCkwav{ll51)>X2r6efLHzlx}8hSctMT(=+Zx;+Z($8qgazq zi*;CuKJuynKV*sAD5+HNIpiwE5MUM|K8Ja$Z-!R|&Q6HITm2WYaK~?hl#U<<-|4^d z%74FF70=Y}^wq?I^H1ya9WX`X{dE2Rfui(~S>i%Cvf|VKArk!W&FB+-7ORzQo>QNH z^{juD$UvIQ{l)m1%&}8am|uucvpiy=!ltA&&M&(>BmsU4@<55=6u4D4pX=+h96Xk+ zW5ZTZP-w8w%H5{IIO1L_{GE;MU=x)Rr3WK?osgmWCODkc-9Lit?f5&Ps}Ii*3<3|3 zijg<`=N~*D35_!V`u!W^KlvXMFyg||dFNtxzekHe4(!*w$l?DWV@bAfAA}zh^1*+( zoB#Lx{0?X@&oUBJgqQfNSedsSr5)UUqhNf%C*cyw$`1VEyB|vjvl3X~Z3_SlQR(DQDL^bXVW5-J7#H zVjyP;i~#KhMRwejmC3O*ylJTZemnj3L`+J)Z=zqX_sK;()6#-Z%`IiB?L~m1ioe3# z-~W`z8f=TDfpZbs&isj^hs5WrfF)K_pAg6a9#aj_h_+v!PNqDV`-lw479=?bn6=Rd;Z z@|qeIGAznzu>iYiey|Oz1`Yco4!N3qhR$SHt)LhBtBu!iz)q4F!X~8jDR$dZi}J4L zixRbXdzns)2{a=0Cw}jK{Fz^XEAv+4Y%BSke$6RqqGlnsoVIp!)lu8>W@y|Ak8Lq* zKSYkIMey{%;#ZmPiIaZSGDOH}spx3pbnl{&<9pJAW3Kz>-W9=h*dO@uUMhuDoiv%t z@&vg|9W#>$@T-k7(|H!VV}J!L$Qz22Q{tJ|$pGtYV^g>= z^byjm5GST^^1}mW;+N~ZI?i5ObBeX!cq^zqp^=%#v`1ne2|J40$w&ES{ry_{Yu)<3 zA(u(Jtijsay18Uw(`I5_D@7H3UsX%b8$&EB3t8;98yMiHwZ4bWTR$yB%wb{mIvsb#Q7lx)E__ zS?y4`(iisE=l=bwXL^ZumF)r>vt}S8XGHc`Nqfu)Z%S&3+N76DQUIOc9ixjmutNl7 znLpmVQDm*vX(;FG7pYsXOXlb1+P;05L01e|4DS2oI!YNDCV9@}){qx*EEvR|d&Ybu z8VGNzTnI;rq zSh)oKLqi`Tm7`e zfd`A--ptZL;m(?CbL|l=3W9njrKg~SQfTRvw|ZN<;_$rw!O!{GoiXcG$1I4=!q@Tf z>mm~gTQSmq1;W3NcndJ>)(@kzPBvB#Y*5h7Nb6F@2e!{+y$^(7>tQ(^Ei$BTmW%m@ zedatyW|{@AFOgp0LXlhk{*5BpCV1zMg||~B8t@H3GaM7T?or*h#B}$faPaqB7$0zH ztLfB?pFXtx(GgX6wl79iBRKT$B|W9w7o?`7Y!g4tU2Dztc8k+-{c-)&{XaPb?C`GI zUuv+ekV9Ut+IVZh%*H0?Z9i4peo?!$HMr_w6t^M#;d-Tbi@x^^1MA(EhzeVR%YPO56J@w~~u7G1rBZMvt+uJZ`hFj7?_(!yK!K9z&kL6j)7lm1nHbYEJGM ziEHVVm;F^`t}zpt1)EQP$2&Ju`{5m+OCZuDZEcnj;FIoFE zMZD(1ItaXTj#vMF36PqAmCfOzt=swc&Uy|Ya+7Eg^Yh4czX6*a6-X6y(bvtLM{N1? zYyo4RKAA1dFa76k_Lac}q8R!Iz}8=4-WpzcA0;6vnXu<+ru4#22M&k7a@OT|BVqH8 z)F-DD`QriZt2@E};Kt&v3Q_~M9CT()Go*j&^dCHxC_v)nNX|6E^DkUia69k)Ag5~s z|AA}nKgn+WK0&6r0pNc)_)_KH`~LpvOdY=!ycC=9oc#Cdd7lDK3EBIc&BbKzHO`XU zKOJnbabK}M*VSeIlkor)_9qGI>F>W?m5O<0b9OFpFaI`TRrM+Nqkto|UsQp>5iHltLPCr4V~GpCd4M@y`t$+upZrf3 zSPV`Avcvq1??@wLL+{8wd-klMu)781T$pWlWy29rbM)@ zj5y3*6yH-%&(8kb#OmQiLX5k$?^ytf^Yr49a!Ic9+|~#8<*|dnHapw;OvtnBNgzGj z=g~Q^y?{t;+Ot18s+Cevq1D#jo|2!>ZK^#TT2#bGBkEoR67VJ7??@R1Fe0~!iHUtH z@z`0InOi|EvQdB97hrib&QZQ~k(41gBqSvMcw08D!fCWMD=mA#;riv`^yX!JQil5| zq+&!>)s4QszWbDvVVghRF+NjP9wJ#>Tr5$bB?to>`CvYBWvo{k=y>L&veziORVj?_f5sW3{%^*&XJ)c%8IY9-=p29J145 zX|Snf1-EllRw=8MD0(d^Z|=JI6x&4z?rRmb;Om<)p@=xEm{L-t z6;d;>HA^hZK^;do{^7%oZ!`3POQ17|o_nuRf;lPtnc{Bi-Uj&-ze4MpeX8_~j1a)R z4is}n8fs23HpB7!*<(9s_>Bx%P-JJiQ>zL3Ok{2r#vS)Vd+bTtIy%1QdSe*q(u$y+ z=5&!NnwqTSERZ5AxUf@(j>`mQvx8Sr~L(VZKTlTs2kz`ft;n4QeEc;KRfDGsUNM;iJ=;hve&`r2^dxNVcf<4` zaboPC3#`w|RIOqcLd1mEMHhBVyjkAurRNPg7?0LC1llnSsQ6V>+{0iJ3Qe4f#h1?+ zv?-Y`e6k~>#_H6VO-)2?v+GuLKssGdk85`nanh8>L=P4inMFj}@ujN)Pr0)JTBO%i`VvTFE}bH=q|%x@xc02s)X!;U`uf zu8N#&VVqw!2O@Z^$6eiieqcFD#b7W3Ocvk*oI6ZbRq|0hQUaF4)3x9DhdkTlg(C04 z_8J={X=?dQSA8#fv%wBaVJvXb<)PY>u+wWMk~o9d)deZTGO&k%vzjM%adL=Q9oOAoNB?c6Pom*CCTn`%!kfk-E_?$RDXczDM?DOZ(lE{9uRWMcb(N;?hM4Wsh@KL|0 zx0hQ5y}0@Uqn@KSocP6FLneQGz?U|=Fc^H}@#~U~IB|V=bhI&0H6)iYAMh#7xFP~8 z=i^{vrqAkCGbdMyY8?WgU)s95O0X1sQDko))@5B6^DOJ^OPR?|Oiaw2XwcKw)O?AA zTz2w-TuiGCJ@BB`aJ)V+IEas@FEr+K-&9;aT7Z2;K&aUnGdwnxlGD}felBPy^U(JV zLEXK3w5%@U8zb?PLY{khge9Y#fOJ(7wv^T3GViZABT6j0V-vFnue7PU$D`V1I}-?%NMvvl%VnxO4)E|L@tx$gaVhu*fSQ1Y|msP*(G_Sv7~gqiK4e5eq@ z=tX4Q&P4;W3&Tk9V>1wLb0(1a8(GOnlVRiRc^mI7I$ng1ZBO@rdTvquQ)i_3DJWbT z=WK0dweRE~8aBA5>$Sbm8-L+G@xlI2>RKb57{^LTcj^#qC0`n2){A^zoSJ?T@9W73SrFiA;$Ve~k{#k;FS za||^)YJMo^*IT4ZkJA&gwL5FKy}<3f3r=bOhaH41biB|an1}9!S@~vLre$zXH|)eg z$T_ROR-tpPV)>Im@Dg~e4=^tmB3k)0sb+s=;3~0-kAr+z2#_|3-by>P@QK5hU5s4F zF|~~gbRzB`x?JOq8Bmue8;!nEsL!rfH5|KxE*)}lIe1@6O>wz+zDGg?xv-Wus96}| zq{{a|sW578e*O@4S}|H$iK*V#U0RK?{0)@<38Vc3gz)`FF7y6Eme=7<+v&de>8~}! zn&-R?db7Z|{RxiHq`L(oM%bxM9&zW+-K37%DS#y`OA-(wE~93{eCslPx*X-RSIK9$ zS?O9f)*EX}4@GETG4~-AN;tKU*oUmQo(8PPp+;6$a_OizEJ?-s(q{xLUB{pueG)X; zQ{&}Zv!fQd{8-wG``rmdq+)v_bOfpGw6{x)T9s(Hm{8!1^57>Afj;Sz*Yce!8M7{} zR~NP#gY#!vJ=cUl0<$#o8;mUOk&F-2X4UI~O;oBpmL3MZ)|cp11=V7 zuQw2F9xUe`=DO`p><%+!=pX%@KXK}MWDxuX|J`DZmiKPuMS0snY3EB1JKs39vzN9- z)NC8?2c7P{{6-UnPts8`)Wc1dVQLVB?`>>Cx@|KkuNm}k^Wy%&Q0>dttE<7FO>6hg zht)iLV~^k%NUpk`-m3U7Pa@i)6cm$rQ#N``e0wcJQkqt!UvZ;tD#R z+uiPI%FRm8pBt!~+3F{!2-eDd(F>iq^1c`(gAQTT&*a2ge_mS_wiN}oePDMo_4~$l zFYw*bnyO;g_Zot4kAT~$+o-rR91hQb+A$?eNYnigaYvp?#d>&}!W;}cY zvz{=>BK0--L=<_=-p^0Yqh+n3EF#Elabd~l6%B-*S~Z^Kh=T0QMO^711}miOc(Fyq z!^4%hqxp>VbncO=*pS-&=-Q23BAuQ^8Yg%?0Ea?%=|Toy;MDVJhbX_Rn{hA5^5s34 zpedF!)D}PmoI!Ix?X3a@T)-dL;*gxnDCB)&*~r$-TaQhy`?r=kO=-mzI~(+ z9o51|V6(p|XS}A%eRhtZ z->k5rh$#L^*Y=J}I!gAp`2KeUjF9sz!;#=Fi=)67pO$Ix<;yK;xXsS5$N&|(qk47U z`H_6XO5(b{coLI?@{b?MPcE<#|LUoW1ZRAEN`#&{lE{*@uC1`13#k~l83*yJ!1l|H zA*Ky>1nLppY)TZYA|tc#G)Gqi{nf5(D=-MuE*EEt?M?dyMz54REeV*Im)6nqJ&-Td zT~vW3q`G|K+q<`}C8`?>Kxn7!m1~}JF-a8_qxcdEu@6()17x~tCPWWB7bPnJU>d;p z20C~nzJU-O3gx$p!qDa+XMifkA#m*052Zupum6Ey{XP4+XJZr(^?41<^2V-_ZzFwm zX*JSP3@7;7L!tU89(Tul;<^IG4)L!2nE9?G=z2!BsBsfCsTyKum!D>(sj1nrv34C9 z1gC@)<}K#W6Wt|xn99;FjF&jbE>wQ(BEjou_B;_Pf;zwp2iW{Ys(h3sZ$Bj&SU#a6!!m7EvKAcs_v31oT3I3vz`|%nYQ}zY{*8 z6>-iYOg4}6=CIXA*eiU(XBL|K_Tf_WgiM^p4jba0BJTsUp1zhZQUz9HYIH|0Uc6Ym zesw#od10T=Rt`CcN|bz{)j(Kxk6jeHi(X>vJV*ijO?Z|=7tb|YzjhLrOyO(oL1t}u zNXS(Yg`^Tmq{Tzm^?1{g4A-!uBTrN;oP#r0Qe*TTtvs^5nw>U2S?W--9kg||^n>xu zgeU`MsbJ7;HgDE5V;CfQXX;?Ew4>;K>2O{8)X7e51%g%>Uh4jr6m|3i_vsS zldc%!!cwz!y~mGjC-_ViktFhp-ND(~rK2+lcbAxuKB005)*=P*cHfE~hy^Kj?O`l& zo(&@un=g1Fix0dG(WO>oYAuMH8tIa1yHlqYr!+vyy>r%odX8>tPi}uB2xiIKE=1>a z|7ey){0r#O-ijX#me2&gHhSVPzw=n1bLkwD;9PJCpNb`~jjU;a8 zm$`lw^@qGjiz>kWgr7SOG-v_2Al|S9WpM-v$2g$hu~*VFk9m3UhZS`3w49t`@Us?F zKEvl^cu#@m?AgmoTWq?M3EC0Y8;Onf5B%^d(c|Sbo5F=O4{yBc>i6iD(&3DbqNpQW!bIGpdMgd`j)cAhOGQ;x^#-!eVF$hR0_R{7cIw63#KAL@ zJ`5e`yTCpVhdhtp$%Rf_y5A*WGF4|qAHg5L+9@~{48)L;jVzRD>_Zx1)8|10ixe0* zoSlP83MVDjYe#dxO?|awGAHEyq>Ueg1d!g{wx3Ei51hC>seL= zf{}S(kh_Y{Q?CWQf+x3v^MLKr=|^iQT9PhPEjxr%Y`AY?)wvQJJ3CCBCCMXMjupJ@ zv_8cK_1Kj$5rWCe__2zx0|7eE6ciGyDGt+&dXLp|X>j?tLD1*zIAx?cnl)xcOH?Qh zzxrrV5mxZdRDc=Lq~Pb1Q}**&Ic88XS|A(`?>1(3ym~_2FB3Eb%{0SoS9J-n5Fks3 z>{swfySR}L#uxc$7m`vk4f2E>ehp8v(US`csO3~OLDQEU6S>q@CMt(UmsLEqx?+S3 z3(bU_2%Ihf+)-zcc8on_rrR93NFp6@t=IU%;6iT_dU#;*0$XR%$NYO;YvF_0nTho( zklJO}fh;|s4s`bWdsGPDUcq#^E_ALb4O727Ut&duz<2hIsT3!z-OnfGq8Vv-9ev*6 z-`EF1)x?=c+br)LL}&r`gK0@J77qUKA|C%(7{0lXRCki{tXaHF^EBIpG_v@UnjMeC zB87Frv*w*QqtZiWsZ0p14L?$`;Q3gOK`13?>ENkexx46I-0xGGgZ=W-rAtqcr2Usw zzI>$@aUN~tESr8EjI*HqZ9j=7s+i@%hgmm;(btv`*kqoFLlF%o5!g+wqwV<8biUeWml?$#-cz1gr0~2*a-GZT!Nh~l#1-NAKDzd; zufW0X``lLw6!BMWLFkfq7LtNKLeYKF#FtPVCV~3X-1FKD!j5TYM>Mor*najsY-o3C z$~1z%Fx(wC9H?tLJiNSsT<_wjkHsg~9<|pNVEIVWb%gFL%X81{WL+0MXm!HgMLB1_ zgVqS^tEGe9d97#AKk9*9qNwLmfBNPe=czw;+2PqFG>+?o|RiJ@F|~ToOOX z+dPt&jEsxp1U`oymv{JXrqcilvK=iu{y?})=KFGYwE=~o3zyNW6ZiE|an{)>n)nr- zzH4tKkZ=|!k8zMOD1(f)wr5e|uo^x=!NWr0MR($bK1{{+Yd$GM2kVVi1-3tK(%nte zOyLWbL#jn{8>WJ0Yu~N40Tu@Wg9hLF<8~=(UCY;1qr4QK=|#G7qD5SC2v2COA~12l zno;0&`0iw$_BLjZK%gzB&u{-6YzZ{CxZDB`>GSpFAyvrvAZ(D z4t^cd&Z&Q6w7b$7T`@2z*X)$f8z9FJaE?D(1(e*rnck%ZsFIoSW-N%A;yBRE zeM9bpO^pq$mUn^tZ>&+2U)KGk7{9U4q}WrTm>+N!V#noMwqS=$M;PKt7fXTTI5zQ ztt`R%r|%Kdva+%gUdj-1UWo)kWK$e-JIqJZb>llVGe7@;QB=kAJMyO8SCdgRVjJJc z1$<-*mD#l%KXhu%;To`OrBn>G2cXynm=@SFq<*bCznM8Ps!{lC*tY` z+#)C68d%3LaY}_2)!Th?JMJf8SVY47@p{s{MpolxtOj!HiGg20*ci=UGm7gIFl8F3 z@j_emOi>b3q0Dab`@Hy6u@tEyehh>r#irENN_UJoo#z1ebZ5D{hgx>!6C}6i(MGe; z*00X4)HKnZZ5LK^>R(UJg(s(3eUrz>j|3D4%7X4?Vktd-M-nYZ9-c)%FnUvRMApYq!R5V z?*}``ctE16%96z3N%dLSV){$>d?5*KN55{bfuQJ9UNuz}m8aGEPE~mLa!#H(L+FfJ zK8J98c@kQ_7Ticgfu!$`qV!4uX$ud)ViKK>U1N}Ww#n6&va*h;fb`Neltb<8Je=#> z+Q@&btirv3jY~(F`rhlmBc%D1EgcEV!J&L-JMR4)byR!}S;SR8h zR(gEBaNlK@wD@m~(7#RA6O(Zw<-70fZ=Wm7|Cw;_sXfceOdTtpkDUFH`ep(Rt@Ccy z%{)Jsj{8UQd|?VC7BOol$n#N)Kk}YrpsAI{m1(=X|MfUNySSb;d^FsN&o?*Uy5?^7 zm+4>Pf*u-|derpp@9!(A@I3;?I?*XA+8%LWUp-EWBTo;j5|c@uTw%0&*|8Jk4M|qx zfs&^S9x7_PDvZGngkNa2MJjxaO{OPcnkHC>q}{6j?`m%e6+9juo{o2Gw~PU~=^Grp z2NHz#E5k29$xGmple>EXr{|APJhszJ9>X}KM-lz~{oC6VD{=AIAaxdUrY~%8YlMV6 zfgm!%lBDI(h7Y4VB0nQ*L~;?Kp|?P>%!FD_M#eL93&T@E>(Qb``vFz;kpuKdp|+m) zev0*S8_BVv!EoLWP*Y<@=Y6nkmY$XMo(|hkz&y~Q_3P-bO)eo$5h>=CdSACfr4KbWwcn1%-bPqRNiaSHgV*UP@+?j{a%5<@la=>7kUzJCF%IP+Y{VI#AE^@wuXrXOOTkrOR&89UBJZ z3GN&$jR4It$|IB{6Fa+tb=5{HiG^QW&CaMrfy4aHbQrg5)3V<+CYCzprvcY)luqo> zz~lsMCXMt^IT-PvIq-zxqm$)o&z^m}uU(=81*Cnou|v=R`y+m7%|??2NcYtE6z6Lj zbu@L2TBy?k)atpU92fGVkw`?0O02>_j^%%iCX;cUPX}yi`ID{pANy;K-~nUEz@VB3vadRAak?HYNJmVJseT~r z{SF|j?`uDGXneWw_>_HWXy)WaQl?VPEiJ#EF&Z0vMUZPd%lQo=h_?cI$AC4sv+ByA z`Lf+~o%cx1@#9cLCc8WJw0tZ@k03-HaJQPp+?fQB?lR4b$6q#+7<_+4%KpF+(fZT* z<}b8iM^nr=Id#8{b`Y(qWg(FOAK&oG1^5jK*mfb?F_)a2tn7!@hZ?k70s{jBBW8LS zy5eX7tJ^*NzeJ~3iS(RD<^bs0vSL<@xf{U}>-uP$JJz3@PSI;uPg^_p#j=zHrk~I}hn^ ze`L$~O2Z31SZmy__UrGb*wPYE{3~K`;YhasxxT;Jcky&qg{?8YD zh0`cSahf1G2ZabC&=p4x7?!Op+>Z9RK`IQ{(f@7Z^fi{@;2aJMWI#@_dtHCO=Foek z;*d*^aj){7KN&VSwo8@k1^M|8kW82JwdK{hp@wbf51$slT2brR`nq*$adbc2`r7$1 zz25K4!61(i?!T8=*rL{o%F_2dt9I?~Zc{qw& zHg3M0Bk6Bbd+e+;T5mD>$BPfoqUV3=lL~R?U`M z-z0GCV1N@?J^LyE>W}V<%8LAvK|1jFfxa@c_@qd??2RRV>lW4eexLkc=tB>NLtp~SyJk>&c|A1Y~YbL?4H;acC+L4zzl&f!(y03NH-`~Hl&mQtjE(k8z zH#oNJ@-T3Il9Hff(D`R~z4|Dv?x4*}QW~Cbp0)#(HWb{@Y7veq4Tnz86y-8gU|--t zRqw1u4{gnNmrhNBbm|k}f3b>iL60&6#!;lqNJU9GG8F_|Ap?vLd96nI3fG1qmT)sJ z715Y1hr`{qLRhIuC!7@&(15z|TiT`GOck*q8rXgp=a2R~Pj)u;`;`^p8>s7HPkEq8 z4K1?51V%wcN3;Mv>bN1N$U4?8R!cQ8ljyh?q0dOJQ(klL_g z=jms|3i3Tb(|&npHW*#C?1pC_mX?&jIcrvmurv-Hvau;My6apk6umR!!-WeB?@~Cq zd~!RZ#hJ&s#=x97Mr~4G^VeMf_x*siM}B8l9w?xBzM3y%cucAFQ22K1w=ZGSTBgtx z6`LAN15S0kYxLqlH5W7~p+%^HaslnJ12SsVh%dLqYehX?8XNl~5RXloZQDTFuET_= z@p#$6VXI?#afegT8c;{?`WLl2Ig%v+>CH{JOgWK!9!v68ld(er6tTKOZT{ztMG`Vt z#7t&Z%oo^hld$RDm`?sO&=w=+m7$CPjtwiEJV$duh29*LF(!GTvm5w`UX%3puerIE zCo--G8;=yo9fPNTo9*pUV%S^p=29Ik_p3Ljw6w1LracCy34dY`B?j-kgftl0d@TVH zH}!BWtk&(Z`B!3w{*ys7RMA2K`SY~H?NlzPM^Y2_1mSg2tyhx|t$4B0>Xlg);d~?F z1Ht&*m=bV-YirF0#on%(cvbcEYqOA0)X8U?WBvPfV#ebm23gb1cP~$ zT+be$sYpsZq$YOD&?kHB%=+TgKNLgzEf+;BrR3^G_JN_KdE1&nY752rL^A0!+v=hjkfmMQXlDZ=bk zbIiY~N(FbF1%%g?SZN!Nm)ZSlSE-yk-c4fU0zp+x(Js~&*L^Ft+M9K(wE?a3xYjgt zO@DE%IYSk#-!(YRraRQr!$cdIvUV!6W(NTK@P({~hMSeENkKagkRO)~n>nMKi+OY& z$^b0FMDpA6K5dPqr&3-COG8A~Q67^O4|vxJzifEow$&*JZ0$SolATO^&X)5lcdMPW zYrPT2@p?9id}I>hI%(P2Uw||hWzz~BVZyC8GdF!;SQAy&1fx0b(J!?1SKb~g&8^xD z5siR`_157KH^BMKuKHsAxZrLS2l7SHbs>u-HfvQ|4tKPJ@B3D9iD0{@_GSGS!8@EHxQvFrj4=3?Emevq@jjcwmH8Uf> zh_UF!g}(<=5ff-sIF`FTE^HAKcggv_g8}L5TU}hOk;TbJO=z9;*&^V;5bf?LRsAHZoNEIEE@jq+{m_(%g3B z>_QlSiIT8fR8-FFbmodlQ(qIxw3)`aGqZc_Hj(0@?7-NfDS|ip z?L+9%XOz5Ikk~=7-Il zdK>8I(9P(P=Tq{1r#a8Z*~nHS2l5w_n}=Baa6Z)tL<*p>qwSubPCgAG{=`jpx844( zZ)}xV&!S(^8+UY?IpmYZ%o%c~Cj{LN${Ut4|DTVb8k z9X;6b=M#msYHGLe8+xb%Ax&@z2aBQF6g7IeArfuzvTN!EQ_Swle76nEDnXwLw%OWz ztlYSCXt6ta$bZP-ze;}HhrdPdnP1K}|NDLKc-YdT&f%Qz>{uF}YNnNqVW*(Nv&}j| z=}(sOhN_1&XOD}gh}hy#4J?J~y`OkDI1?FTML=5I53=k65 zf8~sfjF|6wqJTXOM>7$}*nNbUq)qYk3sUu=c#jyjg~vR$G_=RWC=m?WTJHTZwcqi- z2r2h%|BAUzPUjmN0P@StpkzmPo|No9CueEcbs!ugXweRFhp!PFnaRnbylOUDb&=@L zTBjn=zT{RO@1r*m;;CAgekqp5nP|gE-MZ~}5*q0bDgqAs_qE41-0Tb7Snf3>Y8qj; zCp>;w#atUN4Bk$6%zat#dkQ$;{ri z1{xO4I5cI|(jA~5De5trUNDuDD|GV;z6lqVD%@NYOf>0;oa-J$j*Oq|HA}mrTbKRx zP?%^O4<2X)hc2|B$QaI<5)j9l|j9(~*v zTtMmN8spR$_n12S+JmPeVkR9sVVsp2red%JU1cT=~7I~gierFRP*3op~&w7i7fHOBA z`M`BuB2Bs69f9w%7+ngX;#5};Z;PtA1nXETMHJ~{#(K|8MbWYfgoTBLb2*hYri@O) zMKaiVKYN)2MqJaRofXi9&r>onT{Uns2^{sJ1q%4Uz!$u7;rY+ahWBd_bV1I{OZ=6z z(ey(WYJ5o3rZ(M5=V2s(i3LGtQmz;Yqu98EvLe`Lv6v>qvsvWIJONb*Ct0tBCw4Yw z=Ado;b^_D-e#O;0cr?eyv;5hDO__r{APk`@Kum2ESOMwHfV*RPV~*Dft(U)S4Y7=K z)47I&JfT8K=KM-Vf4_<*pe3E}Q%2OB+<-dnTUqHH#l3vMv7}NTn5&DRuh7aJ!#q9! z=YkN^o_XecQw9p+HOp@m|MoBfe0AMy7uY9vy~j5PR@mzfdRBK859Z~$yz<|uLJhBk*pOwyvOK>rkF)SgxAbs>pb~J|JunY zu&LX8WB7A3mpxXACHD@_~!`7vN%;Ur5&mHq(yKaX^ zD6s&+?JD5)<+BPehWl$gecF8VrwJ6=gV z&3SVeMBV$L_vImwe3W&Zki#8Q##kmY_Op*Af1f?1TeNWXaHjvpgnUqnF+6HjRory6 z#*1#(kc1@Y1vU5#XQ!9P7cj@WQxV{Bjk+-ORovt$+T3RF78(87-Vs(ukwJnU#R97g&l?4ai*uW97Trz5jI%8~M<+o7Y!R|i3mJGw*0ECt!<3#B(vK7~G z-)Lo+8~Q17v5Kv;Hrv^4wgt2r@aC9*D~}%K_~?NbGNH|H{`R+wnSdM8@eNf~cZs=C zSdd5uka=Jsw8-2$Ib37z7c7`qflO3T_t04&O?_0GuaR zFl$BDa?yjs1$zaBY%x&V8Nd)0#^N_9bT}MlcOWfJ*WXRRx=P7}6Yg>N7XD|IcTb8B zl)P`j^Aff-bx*V}TTWS@_n6jlh0Rp6otxn^aZ6ik`4}4?78aH$dAc}HoTu|J8>_ia z-O`|R)1lnd)KQ3NnzAv_+Uf%a%KT3dq~-YF6|-RprwaKD zAPn|1p9M#n#yz*#?<@~^D5otpXFL7kU!@7<_IUC<36xi)l$0F%9mNxStpnlF%Li_NJgW^+QW7H+>n**^=lpnz zd|AzKVrevSV1&cdWsW&gTHa?3WFdg&>W6Qn5Vo`T$ zxF&XC=57vtptzH!eSP)xBwdpachj3!hVHVD7JM%x_`fM4?IXl`L_l_xfI0a2Vt`M7ZrWU~f;R*#v) zd7XK=a!U2(C}DtW?L~;6>?N`91QDMgczTzxcY@^CY7PUhyNnZZ$aKd(rp!bs`Lb6* zA0x2B`EPSMulQQ4g0j~Rgh*wAd1uY|Yb#I@^TI}4`nuiM;l{QCW4Q_a_mwo>$zeXm z1VD=-HR?-FN&$X%`=xaKT$dw|p6%N=n?`rt`o$9W&*A97g0UV4H&X1TNh zB~h36TJ|$O`o83W6j7dm5q~@Z`N5kTE#pnVNlH`ixN!@<<#C{i=p?T z_md2ES4C&PDlfqVhl?$co(GFacIS>NsCIy`nM;5EjM%{%z6XvbNMv2z)PQy!DV-vI zOaw3gtPWl)+MrUX-hqf8JDxIH;W11AtNM2awTz5=b6p~*v}F||scxMDbnL_z34u*> zp2xiZ?qbhNMjCPN*B-cPtoKF}V0TZ-VJ{4v*$#DWfC6icP#9xe)gsKAp(a(E1T0+O3Z){*YP`JF(+$*S_r zoiUq5&TUn@AA|#tb)Lg!9JU|$ohoW?rv!-{2Cah4q_LiG)PbwnxY=wwN1gr-=HIiKJZOlK*I?P~yqZ&9BL{bbsr zZ*-cUF8lUaR9%BVk#0#zBZ-Jkl|?pt=~S&+mc|e3tkIs_{IAc4%BPJz#otRsTH)YT z4Jl0*$RPdn<4U~Nd2GagcnapB_eDD>`f`Vte*y9tN=SaE<>WAEK%T&vTylocsRqC$O_Kd(WCRYu5U{Z=I96&-$27;L&R`k4+DrTs}|| zHN`WbedVp@d&eXjC;l-~z%hjU2zC>e*ZKy6=qrnTc@{|@t5M6E;doAb zS0m2jaN9p~d~LpOpbJsb&$BCiH0+ivs8k*+Bp40#n`O-ho@1SQ;5i_ItlL)@Wi-kornu9{TYd&2;Cr%3HdHFv zRTMMmlQefD{iBf4BAWJDcf|DDLI*5of6#1jcHuz6IGGEHnx{EL#=BmG%tPgkj7mw5 zS8Y(a-nDw&1c*ScpU$Maz%X`?4plsK1c@aUgSb1O8T-+M^ps(C&=ESft` zO8+$Wqqo)Cza$+)DbsP(NXH9y`HKm8Mtjs&r|3cn@^^5hgM6J}@Md}mDY{=z?u<%XZj&_I^G!zupH!VEXRr`}Q3 ze53m6CuM=Y4Z_Z`09B*`1vfuW&y|Nb*1dly!O|OobgG$3ce1y)cQ%*!+anA_unW!C zYJV~~h5cL{MOGaZFx~X%lT_XpUcFWLr`oB`O=23oI}8jS{)US6Lc>KMV>uj?{ZoTe znB)Y!uzhgwK9x7>QU>sgfE~%_Y9}uLb7KFa6o&X|Vw(C}Fj$zkJfltFiSnlv9~-Xz z3}z^pM^ROoXgB~*P`{I%B*|dDh?&yY({tld`^SC_G3V2F%!8OK+)^D}HS(`5q z_Gi;%yzbZ5>aWR}e`}4Bi|cBz2ZB123Hmp7MgIgS@dn;t@HF~c4bVT$vhWW;?}sfn zpAh)9&yTAX<+tFLB<@PtB{Q~V*`<|)LV1}YwIlD-AG*o;T}ozr+5P9S{(4V<;RfP# ziv?e_fBkLiQ<8O(v|@2m6E+{|H$y)VhTYXIOuNP5- zxd?rfH53ly!BxwnHiM5%$E`HsRFHXehy`l4_M}|3w7rr`84g_fRe3c7&CuSJDQs@@ z<-ald8=1h$-#b{%(ENH)_B$7<+ksatMWg2+$`FVg#G3W`?eqp{-ASXoMTDEu-@34X zC%~dL`IOc!9NNSm|HPbKB03~Z@e)xk8zk3T}jjzf?tD;Ttgi9$7L6aSup4gR8dk}u0d&LlA^Ps77Ko2U*s0E5aisi z6aQ!HxZwi^@x(l7=6{hqU}#mM^mnG}X%t&oI_Ow8pUTJ;Oj0ujCZ3T-kTNHapZ?rV zl)_gXla%e|!>RL;G9A^Inq=vUaf&xoGzmmiXL`ejorYe#EtMRyvh3IVJ59KN2VjEZ z?nzpl{dI!X&n%IAE!Nh@kR&T>!N|fhwV|IH+UbVPlCjY86_&h{!y9ru={BNJMWUcb z2-(?KvD%y2M9fNZ@h|1&zCNW(O*J&`2tA%GMg_Nbl`OO(u`d7Hi9myQaKc`1=1-kU zs$}Tzl-EP#6vhi%GhZsG20}y@$m&)gI=0wdS9s1DxT~Q82erhcCz*hbt9JWl$CTOk4F^}eBb1%fOPhWWTRwAl_^W8m+AX!hCdv;<#GuAz#KU(GRvryA~z zc}2B1%IAQ}ssdggdlOc6_{q7{fdN@0pAWts)koVoRk z2T4;j*}vqZV;Q`~R7u9ABO((z_B0AjOQW8yIFM(XC!`{$g6d#p3&Mshd@bbeWlB#& z?A-s)9??bk2RIc5=1;pv{)~+Nh~PevAV?UIE?vW7%pU_G{97bR%vpgDS%n%HG9kJ6 zazqaiBjycio%vi)3Z7kU7X3)Q0A;U~(&Q3P{3>+j>5(LdkTmAeqz6=RRavbBQniA+ zCmah>(au4{$+sk?RYn6BkyxcEW*DMOU;D`S+Ce?1-_GJ6qagu$gMq>f*TYjSW?Dru zv&@ydX!&y`WCIDa_y6>ssn0LDWuV<4vIU*;+Xk4SN=f|}fzakmRB1Ug27x*~P(zZ# z%pn`Znr?_TQcdg@XT)g7tI2KGbw(%Yk$r1NQP;`JrihZs$?Ee{zKah@<)pw^iuNeM zxJoY#J;ly-%MF_GRR5iju(*ZmC$jQ}rl!E*lAX??yJxW{GH75xM#&%Kpx|MidOPOL zypI9%c_z1HZ=0NQQ z!UU3rQ`8IR@quYQlFF=6A<_{G1d2S z*OLVAS!PkY^Wthwnq_=}pX3?>e2OMh`UWOSY=Pmk1`jcba;WPH%SrmUD=ORx(yGY( z4D*^c`=Z3Bb|Paw(|}m{{NIFF;tsBLi`Q-tmN)GI8aY^epRl*Lx*QG%xOR{g8u`(8 z!|;!YJ69O5?7#Ea%iAE?8XL<@na-74l3zP3pf}N8a4k%cA@^Rxbybdopp%an*Pu%* zMfoOvf9G`u^J*Xq_JtoyPZaM%u?=0Cq30GbXfO3ZS&4nPjj;UI5Ql{3<0tWp6&o+Z zPDmU<8#tGmz>wX^->mE0|Fe^(Z`C7Vq;cuei_y&fO-c|UdaRQ^4GRD z^Kane!r0^RspMmvn|g0Zk%78$HTS*f-+h78P}W)Q&rol5dvBt?7mjTsix4V1MPc6; zht7CNeL5#~Pb{HyJE2%;AvQhFOTId2&^ddja5odWZo)Bv+qb!g@Q7Gjh)bGm`}pD0 ziE}kiigFFPcINB~c8u{1dj*+C>YAH_XpA>~0Q&L<&xqaQ)hi#N2JP0~MbGA5IPw?EP4N#CKWEhbG|8*Np-uiGeO}cwY#&%@#mUBuH?fpk2TxHZIR3^)Tpykh?;}D3 zPn_PAAkG=~im+|YZPEw@N7%=#=DDOKa^yA~gId?=_jsF&z`Hl}Ir8R~+)7>2 zPXX6QEVn(sP_4zmLV+QBpmQ}C;l!LaeXaI+2=lqq0uP@`V2+pfW4^!g6P73&akFY+ zCi!kng*f^g+XU)YyUZXHRnmdOwP831qiKY2HpE;0_#YRjF%CHt_GN$iYgtOk( zxz00U5#hkA5p;j0g<~1{Wj0~V z?`1ch5rO7rmM4cBAhpoS7?;4C?5Jnjc_FA$Pwq`7ld^92ob{zZ4dnq`LW+AWuQ>Io^ zVg3f8tv1qA-+p!weocw|3g?{&DM{rILo_8V)H(FGkcs^9YH-3cc#w~t_y-dCCU;tI z`Q`r1Ttm99oRBomVW!)o4ODtLc^-ubZ8Snvq1Yy|H#X;~xyraA)<#jU42VRSMUnCH z?otm+PtAW_hnyW-4bqpxmXXg8{~8Vd5gw6~Z0B`caflBQZ<_^jq{kL)d02ZV*md#I0=iw)w9@UT!**Cn+nF!N*0)$PY+>4w zwn4)WFTT*`SDJz#4SUl?5+v=Bf;$F<219c+dI>-VfL*sej&I-(i;EQg;uF&KES`73 zD1nv1AgiFD;KTqApSv*MNq7Pbs%a)lHkXA8YiNI*i;!7R_=qfc2Jhp0`56cG&A57W zT(&qDZ8+e88p?De-Kf}(+bp?D|jf=8g;41U)alfswC z*^b6n`fQJGPijfHGhvG_{Am#8p5=%mzF}j*em`{IPn6uWLZQTNJ)lEsKj{9}#IRZb zGk?%t{I@{o{T*9CKzkpC$ky>kBT5<|h6+W;uGmBauK+U~CMiW50A(wn8)e0_u=kl$ zHYsTop3o)Q&)GUa&1*3<=Otc#CxmbB~E+Wbv@)^^h1S~ zfdK(StVbG;(K(?-hqy1_ZYx#c-$VB!Ny+EgK$5n@(bymV)$umA+wi#3a4wAyfj-z> ztN&P3{+NxZ;b6RkHm~aZ#Y;u|PDYj=t_*25e2N`hD=vU)R@O+ab7w_+sfl{%(A5?a z0E|U?*lAjT$HCZS^2tYXYcC+bs<^}yEIZkH;+TXrt(grza;SOR8RtlEzwxO%nMTjG zkNgt5xDYN_cPcf9{Xt95)$!Wdj35lU&m2YAeG4#}3*1~CuYq!KTddg3^q$<-Qmhne zq2E2raeX!`&AZPy`_z^D!et)wt&YsWAA~}#B+(F18+Mnt4c@IS3@BOQhGQ#lu?6db z7L79nz8nxN3hhHzjFWeljp``~Hw_w{akTN(my0rF~j^vm43wnT~Uhs@E&>8hcJFb-y~{PzGv6HAsPeb3-zaPM%q^ zL7Mjje0L-1D@J*d%L>X+vaalrt6Ihs)bv@o#!H4dG-dKXNIMaGLr7?Z%#cuVj1D}f zr3dHf!CUQEo4EDsj<{}H<2TEfv~h;^blyUn!q)JE?IstyG;ifdSgMpFIo%~WFx^!p zTFZ|MgaN8Ic|s*I98UsP}JgA zaRlYBH3-OwxdsDMJEF`*PGnuUQ@N$WX;d3SW6n6q4%~_LG^DGc-A=}I`Q(_>u$Y~X z*IMX>*&O;Mo6o;J{vY_>dA7^;_{DKTp+Mx}uXI4p$YQm+x|%QwOZ)z#+G}0whY|imisSa$ zIwjWO3-o&?1QEo%|1`n|KV(g!9dbAhaZ}==<@I2peG$IJtPlI*Y(;Aiwnp{^{e(uG z@|+d!otus{FAO)|m|ahbjKw$;y!7M0>1dZ&Qqr$wB|G1U;Il+$EzV~>|5!G(6Ad(- z2P}g;LKzRXL}k1_pzONbUt1dYA@w`esdmhHtrD}>bc&V_E`&T*v{g1}Dpf~>mS8J* z@mz5B-aa?CX+wQ2#@HEuj0$}||HqEJ@(-=oN77kKCP;YKKb;#aq^3=7r0TtDMzGZe zI!O*+i*%`bpMRTu%}F#L11)1YEwu6`oRE@+dHL9>3E5OUY_m#RfL(OX4@)WA>XIeU z8zx)S%#Kj-mww=+fTe3{Yh!ZC_6nd+p~^-b5?p?>h1h*Ku2_MhZZcTqM(q_2UYm10 zY01t6B{Icc8RH0+cOLQD#i|wR1>9}q`B5k#0MjD`uO7Q@kT3O zm5ls+X&piSCrQs5k(`t?5m_ccZ(O==6tVq9t66YIi5-sAMieZ$nGeMc^^%_|_gpJ6 ziHnr5_Ziy$TIPPLoDVn1v0kJ~x3!yfX`42$ZlL6$zFJz@2w_Uw%)~JLJ(tao6SWOB zl3|nbWu?O-PUcc@hmR0$g$!+MFiX~tlE()9nW}x2B%91IS#zoZk5Y*tL68^H7*>uN z-02BYyaV@vR-38h(h-}Ix#D`tiO@PNIS|~SMMn-nKgXJucL!};K!g$6pEOGR_RUWx z)pYTqzVl_9a)MctacU}0@s{m~pv;JpR2@tPc^bW#aJ|I$v7%5YJ{3RwnM2zN9%WjA zTfl`o=kjygOC3F?+P6bc`Wi9UJ+OXV-z+E$k)a;AyVBFs1)X{J2_J^NAvAaVpOu}H=^?V5#!eiv>utW9KY|B@M|{h-^sUwpxcYB^x-xh zH^Qvl>%LfoA1>pP7+5yS21P%@Efy2zS3qkf1zN7xk>EoJw4~a9;mXf7{-7mNUQ~?4C2XG4>_&%#8ftzN(swUo|CtM8 zWbq3Oxhaoa7d;RJ^k*vzzj5tg-i>DI^m!fx2AHz50AKluuC@N{NUz}zH&{R^Zg~Mt=-j2i&--+ z89N2J%yrs6x?&DeZdY zCcE`rC|dzvP#;tNW$tB)CiW~@rv#BlVcf=Tqss`h*=nkMfVg-~NV6WMDt=O8i}|Kr zArGqA!8>#|ukgIcLOu8NBV5>C>t>nnOvKbeUso z@MYE+(07@(O3t+r_y&h2olmjExLP{vY=^zhb1D&lx6@UhkE+lKIx}CER(uPm)13@bkiKPHly+OO z5j8*CMt?4TTm`>diB|ynI1CXMXwo;dY(2Z%Oj{4iIt;sU%y_IdYzLtl)h4e@Pzml^ z{Pa=EZ+d@WXSkzRu-LNAp~hywef9Dp7c59$H-`3oEKRbPcm}47R@`lHdhLx*(B1ZxmYuR56S>i|5v#gEF z-<7GCSJnAf4h@zeZ4`_>n=LxHFGmYZHFv0;<}om<9D^9N&rC$NOKVTbW@_Y54Y%1NS(me04t` zM@}2LD9ke-!ZWY3@8oECJiD^WeXwvUuMAID1YvFJ9$PiIUH2p4Y;a(Md-N&h<@B}w zte#XzOz)B_!gf2v*$eXEvh}`LrmA4oPL)9Y9SA7_y(grtZHDXK=w$2hXt3_M=FocI>f7j8Ml z!$t30q-?p)MKC~FeRK*TRabEDY>R#Tup{tWUWN-pFbPJnRTpZQNzahi3lW?pu@CDj zq5Kgwt+*?rKPHKzHQOnJu3X~ zH=WFBC06M&5XslK?t4vHc682W^lWsr6pxY=mK1L6>3t&)HmD?DWMVaS9UtZ^8XnO8 z@xj^R?at!aCq!~_QFn(0D;>%4p%*YY^n;}@{?+L6S7-qJIz*bA2n(WVo$jm0KTgKC zfegvJLTdOv*d_grkl%Y@w?23CBLiIuk37SiWM|G?zY?=jP@pBYTUYKQ8+N{u1nuc% zU4OUsGL~Jh?`hHdp|4VO_|JFL-0tr#iP~kMP!S8C50NY4EI?@H>DK&eVbmF`H&z^t z?pav|S6&|@GhipeWFGIEu9IMsT0e$_JnbWM#H?{@m9-J7H+U_3)y^qvxtD_g*A4Z4 zdlC_&uED;%i%Y>CPi5T5d$eKFJW+rlR*iX}(#4VuMlQPb<@l!0Pxt6uDzkTkP z)%i2Z%+clU^x0z@cDlJufuZjk8z$->=YJtX5^%``dJ1Eufql!Kk@==>dtZAqyW3d#?T&x7y>O zB~SLvO=yiDeJjjm4cM-*q|xgb(7xC5i#^`$)wd_?Le`U-I&sXDE~tu%ifYVKZ|vi| zQCXkn$J>`WbTsawy(bkZDvkQi|5|kW!wBiCj{^HkR`#G*EG;IGm7V8zmb}uuy^f8u zw;pMFfziB>1VZ*LDkb4S`KO$fRVDag7?p~}7jhxaI*1Ax;8mvNmNlpVQ2PDV#t!xF z3KT%c@eQ^;%lL8}i1^HG15hNqNx7|JsF!zdt>Zwr6FTgD*ipds{Mm0wsqOTkC6xw{ zEB&Edr7dBEy`9c_zYFl%uq*5~(P7H5A)g*xC`v3d9lu5UJnw^3%|roB55mP8h4f5t zz{jjA-*G|B7mlal#X26s2YP*KUsPv4ffEIx9Q2rgQ6m((sh~O_DH-ALt*kK&#nIF7v@U;#$VaP%x=)k8U?0_i>*^rC z2pU)FvW4WCslL2KsvbHV^* ze5Prvu<}@e=k6geN8Hx6oqfb-qSSGR(-s|c6Z2Ff>Jm3|1erg-$tY1%fBFkM3p`5o zHf)t_taIWy3DdK-XV1CA!%>`>SEmic?_=D$0^$?is~tPClW8$)w$)*14Wd+=f^dg+ z(?vCrb3j`?03@Ss?AtD1;5lzH%H8%%DOyRi;^G&#%wosO_Ki1qZoHHtOPw1m_=xeH zNP#*nwpCJbA}Fur^XD7oulmEeyR;Y1#_@<;in^MwV9ZcR*2a^*|L^(>2Hd8Q9z z$J44W7IG%!%Qh49Zck*XFtB{?ayOC$j$=vW?MG9la6Z=sF=?TILf0J~gTjK;t}lWu zF}6dT-T0brThVJg7DGuV&qNQw?R`@8XE+iv3}aM!!h76wcB-s?m;JA21kfL|$h>s> z5~~Gj=OIyY-iKLkEcljzH>k{uch%#Ra)C+~UmyC`^YGi}Ze3;qG(-Q!5Ei{g;8wcU+23-$D$AIpbXZ^G*r%i^T-p#Cxe=^l=i1mlW>su(%Eu6Z{Fz(l zvRbt32pVDq!qHzU-Rn#oPhIXsiuqh-qwQs*3dV4G^~85B-jcbOzmavv$)=Gf$PK5;SLF`zif+}W`!=nvJD4ib)Bd70A3h=pb9@T~`qoW%{903YyrL$GYIa0kc7nZ`B{na8@>hY|kt z4wpAtnlSDlfk=+E9f}+0)t&#W^h2?=m!R;wZsC4>2M~nif^hc9i%q0IXi6D2?-<;U z<~7&CB*`Xg0;!4v&D~4<8$_UnV@)gz%8vn#_#ye{_m{zfiAI%$@~sE*HNhzY17DL< zGXSBb#hTUOFL8QR{k^4*9%$u)@6P5rWqUPs1=Eq(Out^Fw)0xs<87){*x&}}=@zV3 z>0ZEXc!?P9>qd7_V^ch7G1uEhiwEBxcAo$!zXfRKh$4$16h_`1ZH(QRXAI=hcNC>D z8ceV6%K9>Pjm5ZZHS#{8io+;tI@0+`vmlmpYwR@ZN3656M4V)f&Ypn%MjbE5oiJl?!53r+yGH~&Osa z!(3_T2sxstZEd8UiP@0aN%=GcHccxeojmLwqQ-|`IANysjd97k(0<{=(Bqcgln?Lj zWwA~4YTUx!siIZ7PoxjxG~bzzAB2*r98j?SIolJgn4mNYXd`v}fcr_Uxl~b!{&YXk z#^dyGcGr|)epL?jrgo+9TfWoXcQLyf6|RUNA02+0Ci{ww92Ay2PXfb!UU|OAjMLS# zV!>v0*uLLg^Awkbb_+tFIH`#+ub#LjPrPF&tP!r4cDi(g%z;>{bhd#>2_}v5e?jc} z(nyVCH{C{HC!%r#yD(ri4pCDmGwknyrQyabZ}gkf&gJNf4wh`TzoK%v@WG|J_1!YdE$Xg_2-p>abtjy1Lyxx>+cdw4opb|M%*zf#RS&(qTIyO@E8XJ_}O zaOcl$^P5hLD8-<_Y3JNR=~=ZRoitOQb%Abu5e>tv-*xAuFFDFCvu*;&d1niDS1<>X zAzm|T1HHM4uMFOK0{Ij;(9`>JiQ3^3Me+DRhK5J+()Y4SppCLlxdlS?U2~wrZoRuo zCTY8 zClqgND-mQjY_UJ4yGO~zp;|S?maz{>MJa4);myZ=(ttG+h5HDwYW-H6j7fs6w_PwsOnU%nL+vQA@pZ3Siy%FEg!!3T9w zay6dZ=Q3L6Ald#c3(cifJbDXiVkV-v*hdn%K`OwdT{h@sh|J-)CYJB^=y$5h#OXAz zi_<2o(cy=#1|zN1N`w%XtXqjw`Jt4ozVS*%N^BQ<*vBE1_A22{+dW693TDOA#{IkV zBC1w~eR{ORtw*^#7l7C?*XY9e3~$wnNd}?P2dQO?@3Rb2{E2 z26>nj^|0?&sJ`+&#}D8*@_eWBcTG{nPrDXcziS`={Bip>4oI4R+cvHw++Xi3t72EN zTtyBnQYAK-Zd9RJt8V@+n&-m*`iMG4>|PXqPCM6%HR=m}ECt zUl@?z@3($m6tV^wL%Ko9d4pu~G;^&aSwLRuvKi8Kf&H)@c5A-yd#z`Tjh-y;V!sH$ z2bP)RLlqhX^$B)6O?;OZl^NSky|a?(b7MCcqBAZ~;dm~$X4S%6oJPMq#u}W{(;ADpnVKRaCGgV8W)ihh zge6=|Qj9=x4@NRhn7sWcQh0%DD_m|KyrrcFl5vQ0!n}@ zBFo*_lW%(LDNhsSdWA>Sn?@qaN~6kti{V`etBKKwqkVk}4-)4t#>~iQx^d*#M({!U zyIBawTrlHi49f0O!SV^Ei|+moOJlVYwL--Y2)kcthIm!-j9zOk&%-qEBU1C>VdLBD zG~ee$C1f*8fyjZxL4m199&=x-FO?bEIkP8Lc$EkT;ueBSn9k0H1wh(qZHXunW?mgs zaW0Wfa9qkD*C=`HPJSs)bWs+Q)TJMX91(1@N^Z8QuD)runqm!PmP}H17yFTQUps`X zaUl6hU9^8OfYw@kBTN{FFBKy2Mv3 zVfjJ!UT!WLJ`fm6f?-E0++rTclN@OE*l_CJAHr5aP<k;#^`N1664^SiRIm|^l)$}DQ=Ni2AZgI_v)fTzb2UeRe4cF9#lJ-b#4b;J+8Fh$tiW3nL-aR$R)W2%_hW4 zh}j<+*BF?Jo^ON&_W#HXB$z`RZZJFa~92JacJ4aCh6k zGdp)&h51f{dxgM8Z$0Z~k6XZ$j(a#2Vl%Cm!qyeljpk)Ph`CL~%A6cIW4i_jT>zQV zPSe4Ixe&V0;FdJ6R&8`&a4SuFGVyB^zGwZGfp|!=la-IM)hvX4_S^MA3e#aesB|GV z()o?Rjlo!IW=_QG67u`1&b);DSnOJpKF3mD<~;#`0jkclqP3f`$(uW|@U@d1LIvmP zo&{Dr=Em@Rzqf=nx2otT^3p;wf;wUfHq4Qo_9+rp~T#EwuiG{HRGd`x9Wa++oQ(^*OWdZ=Gs;i9I{}xzyXQ(W! z!%GF{mEjqJN-x48aQGdy`%gJ>kO3SE_4)N!It1%6ec>jaQm24k>uiwuDm-QEyt!vQ z-p1ujLwSBPcmQV_(pW?1;D@o%#&sp8m0o4TZWWF4m*RX0WTB?ttM7ExpcU_ZG|pW7 zG+D9&(xrUlxZ#-lmgoqTXgxGYh6&m-OYI= zlUcH|vPY%XTh{C5SQETSk0fm>9#p_gDr&l)t3HXtE2);XJM#7GTXGtK`bA8iRE)v7 zS+3@7+UCRhLrib$??9}s-52U+dNU+O%W!!{#=Z96k6$)#yvvSixSJ|uJ>n?;T0}`Z z+%!R}_u{nCJs@FXDV=ah642oM21&q4!0K4K-TKOepx-UKk-RkH+eIl#JUlNsGUS}U z2G`}h=7@V13t6$)EU-DBmO>NMT^EZTZ&Tmn27ORClhvJr3+hPjsxt!Ui)BcJCs$a& zkj1ISn64K^+^?cgrvXqW!k|C9kIcXO#gHb#NYoa%3uGpm`@J+X#wQ{VY}!CdoLcsb zR>m`gBNs=9kz*6O=ZjAzZ;u-HkUm@y^ieD29c&j$%iuvcpZMAd9?_QKqRPdu=LdAu5Gd zRw5>22p*-?y{YxE>1~oWNe!3f4S!{5sGiqvMM?!-Syc)P z-oGl_e?~-^w&_PAV7KNMMd;+wlrGSsI8ewKzQfWkoUCUZo0OjZ!lU;RJxJ1-4YY)C zJig*wY{_o$<~|W6Xhzcs4#AHkd#=$;=`5A69((6)KodpJ%(kiLWa{If?=Jpmwc0cL z-D&=VP~|=`IQhgZ%cjDGpC_Ng;&sG@QhSye+bh_VdLvIg;kBxF^Elf`Q6nNmtr zZ!E{nB_>NC1J|$kwz|`1H_G>f3-&^gO-LE$G8+f~PoHE9qLVT``=_6dn8!oZb)^h% zKQtD9fX!#My4g;|JR-MiK<+w9gr4d>X850&Nr~6|Sc&0`{72Q2iYb(^_qdG)Zv(Yy zzi^B8`p}G+FrdeLOH-ctp8ocl)MS_ZIrDqbei2$%0p`xHiiDEha#op^UDv_~`rUK= z1+!NW1gQ`P(345^8{mR>B&;VwND+PJIa2}c{x9Mcr#TD7iKHh!l81k}DVUZ^1b~H!jy8KqX7Qne)*A8MLmq{G+zxWwc9`E392 z@XI{AQo6c{0Mzyb)FBxzT)6PWO4H&HfNta}Ir$?~Hlia-?0J`W!~ta8QanZc>HFbL zr1Z6w0N?V>G_s?}N9vDN09@UO7}?u3+*ZK=wl_UciA`awxa<)B(hvgV+n*E*-0o!fw$&W$-enx#$_3PbIeE+l7Ex_bcdD zB1W2o7kUmM=@t`)wi?}Xw%Uu_JQiwJatTv$NoPi~Nc9zlTz)`a$!V!bgsHa3fLD3q##O)-){8s70nJ< zM-MD_r-YJt9%?rGnUNrDK2hDCtd>B{J_Jk+l=dvqn?;2q>Dgw!T4%Eh3;HPiWckyu z<_3r!ZLHmnl(wO09y3K#wWLCMAgP=}elQ@dvqR@IN11pgqN(1+V`0n`5ps~p;sqn z^Q(9JhK9Sy_=I(vFwvUbxgM*S#F^gkG$Ex!oFQ2$!Vt@$Jo-wtJV+9|pQDuDGF z$%;`dAsBJwQdq^Neh);n01`R0+8NB?e6Nr2|{-cDeq{^ zWXnk+m^&LhA%Y4nvI(r6ezzlfEcuK>l9^i@+;3(#!ef@Uvkg<)Ym9=N_+#kLXOSnR(R-~3wwQw$W4gJ!hf5&oTlJIHl5lCa)M z%Dvzfkx7(9?eiw{*{jks`xdUG%BX^)aIh4aban$V4zm20zsRkh7SQoI8q{CMNTnK& zVX15A&m&?Eekl=^BFWPk&6+9929vAB&s$BCWDk=3El;WaI2a1&S5Mzx_Y&dzN0}$; zy`ok~Vlp!wB#Im1lny9~Vj?CQyVhLuWoe^F!q*Y6w+%kZDZ2#9ZRBQ*!l(>nm1-Ay z(?oG$a-KGom!zu8y<+Q{uOpb+5A`VQg3-mu|V0B zmvW{1j-qF~wa_a-gyJH){KvvqD<0Mt7?RXMvpqG@vx=s~D#SG1+(kUAvW5a_B`!(N zetn|9I3QuS0}5F*gS*k6W?&WCsq-liNWU2)2}DfL()Qc8p>HzpBsKPzvly39y_;Nb zRa$Ja56LkY&N*`}1^};>u!hwfzdpua-1m(Go_M`Y_spLzdU#{BS_q>3ePxQHIRp|n3QboVZvbgYhrvi`Nl*qkN?X@|2$^yH2H6-2c0$ zNIrN9yQvwTUoU#dJo;A1HYBZhE2+Pn$+&>0Dq3hT$;uTtjWOCDqcMm@m)sgjR`H(g zrLkg`lp+S-*L6b=aB$YRx&el2(S{y9DgM*Uqp)niolKJeb8TD`CqK- zk9|mtWVi&58#Z8~<0FPAI#}^?A6C#vA$odXP!t{){wBQmgRbaJf*=aAHs-FGI%{W6 zl74o3t(EuiCR1mZV$dAyo3{;EHTwUlo4o7Vg{8%6JG_>Rf?HAZuOnfC!G;RaXf!D{ z{XnMWH!uCy-yQJj5NI%*Lb(797)g4-`29AC!{OY6zLJy6rz|lWat=q6$9rKMdtydfz>#hhePQx{G*dRABWP7rnX1=<*>=9!R4AG8JGU z+@!RO2&u1Cd;Q|S7}vG)>GtVXY~73X{Uv>`*al6XuX8jmA1)2f+bi+g`gqNbFThQZYlmnYeo*H{ zbX;6-3I?O!p-Lt&4s|Wwy#%saXMGA& zL0_ZGjuSP#)PGT;_?^+t!c_6f3&Y7nuXF4(W~ITa3+8AtJ>0-J>0eyux27zxgg_u< zooauQ3Hbdug=I*F1>{ErdP5VEzBbyCq)Sh|GNV8I+P0JDsc;q=r*~p9>fJ7+Tp#!w zM|$X2g||=I;FSPwzG|@|LFOu8?lyDRdLI0VLOMF3CR2661bY%X;Et~RFWjZ8JEYh? z#R}K;*L$@_m#3;cl13m{LDz})1TxVx+{dd={C;9MXe2ZWsP${iWtcD~vX8i%LQY30Ey=%AoE|`?g8}Zuo4UFFr!rq7cCTMzu0v1Y8`qpQb?}xNP_NNj4~-2 z+>YzNSq;L4&|U%Ak=EC!&^;G4sA|HTov0b3H%3(t?d>oIHaw^|5-03 zF8NTW>D9t;(vXJa^aCO}z9>gu+5>+@A14ezP@vsco6k!FB=ySbz#DY!KTj|{EoA;m z(vU<-VxlQtZsCeP9~D@#Tl41ghsH;CvbVPi$cI@WFESh65`mLRXZ+({&<>_JqF>+@ zv^3K`$;y~(=-@pPPL6IKIw$J$y(L8tOSkosg^pvc<^P?y!XNuw;mKYB8Me)a#rS>! zSx<593rG^I6Q? zHtawDCaHn_pO?_hqw|s9hG;-)rbu27|7>BHF+P>(Qu>_TouBdJfoj*X-gu2=dxnNK z)tCyrw;NT(#K;%~RJTa;G$8*RojfDqwB_xPhEieTDGT_aQtJylp6Xkj@ zjjRrp&HPT1Y*c@Dzd7HV;RT3idR2HqRw8{+FFL9o|NX?&#eZ0D;xfe0Vy4@-DeQBB z8%cGQSE{$`s=~Swp>Mgsf1colZxb7ff45rT10r)f`E2yGd=q*^a&MisdSgGZQ=Pb? zr}V#oi2pb#@2~m!`+vE=*GH;qOp*Qqzwg z5k}(QHx~Hw*=}sa$EBy&__!LJ8RNdM2DEIy(?MzfY2$KTqX2!VES}slsEPuLVj1*+ zNF*CbZ8KSPOi%Q*^!IZ>nd3f#-cL{78tuX}wG7BFT(sI>XrR18Wjri(lbb&#)^ zn3#sL9(fJK0%cQF{SPmR*V+FVWw_pkFn?>n*L?!9TmR!+QUnUSHtct`2w9-rVgwqz z1p&ghh4E3uqo<|v9PKPJ-<)cFX@}YjMoIiYZ&C=-)f;=Fz0&dWBe&I+upY`Ir zkz1Ir2&aOV z2g}>x7iHLpk^vdZ=w8Z?AL?6k$!Wz&e|MhyvnvBRyAVd}hgZqJfzg|NKf%!8LVQFN zZRp@{s4<{O^IjS_Eqc(*@ASmG>%ONhIiU6~-!t)3Hmi>}oB85VQ!>!;RVo+=YZr`B zYd3CYB6e~|=ype+hH&bdl~^+WW-tHcb^h{K`oN>of=A?>oJye&=lZg~f<(BOQNiTY zyXKJ)?MOyY4hR4Uw0Zz(g8{I~8ACc;1LzpI5`lAgJa2=ZA1+avYL0*b;n2XCm}_Lk z)VG1YbTFVm7cjpX*{tCiGI;|;$9FArLkJ zx_$=P1}NPfgLI>$ zFtm-*Fmy=AkV6fPMGgZ)H%fPdfWWs0J@=e*zoVS%A38F>8Qyold#`xbTF=A9B@d>a zS@4wL+t}#-Vd$qmt86?3C?b_Yt_r6Y6e#5<6uj^ymg#MM2Ab%NfjE>SL7u

jq4c$w(*>5xW=F^8FshTA~Uj?X4RPg}45v86!s3-)OB!jy^%9pHSD24m*`^|C>j zoQtf8$Z!a5J1ZYg5HouDd^2>(Kd)D)-KlkY9)eQVWFYlqR|Zv>+<}(LDA(U zEkF&KHuLlU!Val~Yu*5EonA8f=9V88cwJqa@_G3je_4LDisFi5x~zWsSs+LAzL1)Z<1^>fZWoo@AK_qdGPH({RV*^dB%WBdtE|H<3p`|XzItrK& zHCPadO@n#2k&FL4_xzV%{^zCPc2BBcfs7)X8hbc60ojt zhdc}Awq@seHZ!&4t*0NZK|4|WjZshDLmSfft1ZINE`;VNm;v*3s;}{WY6R3V4Q{Kb z0Tgch6Ezgk9R!FL8q*b%23Bm{M(Ma6XHVo%(9bBcFawe*TgnCs~fXFEk+a zzs$xaE#x>eHCzWYrg1toveqrG|I&)#69r2#WywbS%m!!HopNvjC&8}u_NdNv1)Q;; zXr|#5VDVwT47>wp*;IJ}Ms()^5b(hb4HW?X^9NC{V1qzfL8{tgm$xP(1n?$Qk<;=% zKtA-d!&m7tWdo?$%2ETNIM@2_>03H}xf8;3mIeEJJ52~p zq;7ffK%?+t#U&tqIFyR5_VlD4Gd_mkqT}CwAzrxU>w9#%JuO$GRSOJ_7ggpjwZ-t9}YoEn@L+J1-F%Xrqol&o zk&)MM0)jwm{k=6~UUPo$Qv{YZ^4amK0=b30qIU9KQ+$oEFsg?~puNR6 z5sAiaN5dqWy8ag%@z6UI<3;sk>Ima4J%6ABkoWQeI}mdNQh>66KFC#{hfL6Y0X5kO zNZB248yFiK=LCS*)w#FobioIQqxeLp)x3`Xf1b(zvV=eX3n4fN2O;*yk_!;(M;IIe zVSrLy+UnK&8Uq^SO)Cv99K#kY6b>iJk?S!(R!l485K(B}{#gW(1hk~c`k{x@hW`;d zL0CzXRY6xDB^>C=gNP&C$LCIo1%NVfhw8F)tLoCSk+E?@-op>c_XFZ=Isx;(5oo-q z?To-dP_#gKLoMkR;;>{Ew-pooAzDZDJA?EOz~mZGuK9Ef|DX5h-)y({67h_GHT9lT z$G`b4f7ny39v~Mj^d!UkBbfN(hJWMy+HkT1+4P5^qknz{qz_lp-QdE<``ab|_O6(; zKnj)^W+?n`?8%R>eE`EzRqtKF`|~#+`syBzskzY3>TmVH-!FnW7x+kDH4)x_AH^Sw z>b-BfC|8rjihUkX-Rm6)!kxUQR%AP@OiT%{5Cx@77wcKLlaPXMMw^{uXDfL&MV(D@ zoCm_hiHHD_dPemFr!c44bUd$}-np5;Z7 z6R|qM@ega`jpP4R)e&s@D;Mc+R~pYdkQ$&QW7PzB|9sH?X%QihK}nA_hko)fjQr0J zL+ZgUpjv_cFS{TCNAOIg?!Wp!Y@9K0Wk5EWUHRj}Ss+X@nJTZt zdU`UMm!v#1@|%h*d(|$x6tBN|Q4avFc#ps4(t)+VZL2?^y6IX;e)>1CyOhBI)L_=PzGsduz0vX2{MxXXzZz zI*~$lg;Pg>3#hy?1w9kixKIZ+5y8EmT?WKtA3v!g334-|Up<~rgE5<$m}un6cwD$f z>VHrg{@t%GwE=joAoIIt%wet1C~w@j(Uny|6-sr5jEoP>(|U}$rQy??$=rVWruOG# zHLUW^Z%_wis!$s5hzO}JuJf;%FX~)P(agcNw}VcJ$jg+6yZEE0T4glBBfY_e_Ek-h zq6%S!Eurstumt2W+A0J2@P6A5 z^<&G2n&P`Jxa_&X$!dK4vMo+4UB1$JL;EynH7mQt#gT3g3jvtgw6oQB@Ub=MU zMt*TGp=MGI-xBMkOO+Qnf{G3|9?bZkQ1+(DcH^vS5(tggZ?a6R6*VNWShE&|81?1L7ZY2<9qQw`{wjxGIF(F|oI+5f%comBou+Bq$HP`| zohxEmkA7fRUf~cctC$W%|w!IA$VtpiGElHH&U|>CqLTP6V$H&t`-Glc5_If{oMvV@JZ=Q1hif$7&$&t>G+gj zGPXrHrI+fL>TULuxQ`|X5R}>G$8Je^dY9T=NfP+*=%1)-{`*uCpuca5+FMrdfi|M1}o&+5_s6GBH7f7i})uvy-5CID4q8P)jg8J;c$P9T@M zGH5z~@Rl9{IG9(2gd_@(NZCG_+!O!eS(Bc_AM{ngeVLkj7j5QqJcL5!D&j!wrbZgF z$(#f17BK7CBU#5L2D2#tO{HFTM4Ht z_Wh2Q`Ddu%?L)lQn`v`D9iz1#X~}XHJci{Qje-kwv9uYI8e zis7LPH!TlJaF5QkR=&D)@#5G_C;O`1+`WU|kl*hSFM0eOmn>0Or)l0h6|iI}^bVHuEjgSWOw#Uc}3K0HqK*2w7BAyjfKZ zvgg!;)F!gBjH)x!`6C?<3uYi0q+`alWfpO@Gsqr=Yhb+^V@+ZY*2|wDKVMbW-NfXD zp>Wv2Pk#TGICWlpWgo`ijfctBJYc@rs&sBXPzyBSn^dxc`(oJla9|GC&+8;-Zdyb zSQ-2?=_(n)H1jj61bGDYjS9bCF3?i(m0*KMV<*x|d=hlGCk{XCI`9S~EGHQlIKF&2 zWjC)?JT^8au<`D1LJ9x8J!(Ahf36lnG&*|<+@P<=OHD=`=IWIMQBirDQSHjtpC2oA z*!BV(!C#N{U<~F>k)Ied=HkJV?5)1vd|+sqND06Yn2CyCS?b{~hLlLH#^bD|STyO1EwyStq7l(Db4ywo_ z%1qqc*uviZq($T<^262}m~D@rcjiH%;4cRjoqmu#^+$V)fcMf;#Xeax8cjBk@b+I+ zaP*1hO!GI8y}r%B+KX3}BMw^4YSJJY5e!~ovq*}6b3LCZ>(S_gJL4E)86RZ_i&W=s z0LMT1%GWm-2+=*5?<*p_PJ0)0p1DxxB_Zbb+qf2IAMPD`s}Rl8dGVk6FeXh{F!>Z- z524V|t7{*Lk?^CH{P4o>vXGD#(CyZ*+;~MX;EIQL{jB7@+bYtouCA?cm253Ob3nTY z>L1Dl$lS&ZW%B|6M}dmW@jgC71A}DarZbXqa`5_kQbE_XbX=7T)KJb8TEfG;J9pl_ zWNri!ug0}LTgJv(-PY$HKVHTbnkx%BOeMqsO6xN8hv8%D8A_jyCR>`z2|Lb&0^Q)A z;R{m%UZ+Tp9lLvIiwI~}sV8{1WCh1#pOrqt$jC)`Rdg3t-3bjs$2|dk8=W;&a>;cCLHU@ zH!QH~FPe5Y13~~=?X3^@dd;J7oru>koCKN;t=0o|*OPChum7zT``1T#Bpgx- ztz^@m1pN#T?%eTl`@Vb;l!B@306#l&egr5(0Gdrr6XEyKHf7fm*oG@y-hhUVyQ%W= zoOCx4;Z5OO(fSC$;-TTK2y5n3R#GBxMb|!ksiA#BqLI$FIS6!yGyv#dmV_O>z-Rc` zGB_ycLX`WP(?SGsz1@x5jgD29?)!6#u>oY9Yqs6kg}-^^J3HSWj>hdd{(@eK$FzhSr#A}^-P4r2B70;_@2{NAobK-++(_DZ?ULL1>Q zR{s!XH}2c*qV;&B2EPsOG0mW=d250-0aQ0#cP((GG9W$I}7OSgQ9I03<;(uF!9i-HQ3tJLt_FuR^V@#zex@9 z+V_CnhO;Vq!yy9$I@6t5;Z%4Lmp!+dp!9^PfPjFRb}V^5&UmWzg4&1*)hw-|-D2gY zuYcOGGzZ{e4pWE5niLec#HGc9fhwD-&F$STYo2xC0lik9{F!~AT&Q9)StTDlFrsDk_j_WWW}U_b^z{2BTRy@4MfGx0s-5IO)i;* zMQ=fh8I#KGl)EqexeY!(=q)gQQ?WTzJ^_1Is{m$T&kVj-Tx^RGL{`3NUF=-?a_L@) z-vFpXR5_t*96ylD0vhxp|60vHO~NsN`Lcl$&D*f8nPgVa}-bG57d-Z}g3G^;97wI^wMmc4a+n!T0%-7Tj z1TJK1f`PKRv(%d^&-G6A#K?v+$602-Y%Ars{!=zRd9_4R<~5j~hL#sfMt;@L(Rk7X`ohWwcU*u;8=`}mq z*u0|}+(e}r)q~IJRLlm8NR^HDHMvmGKkw?^?Zvd2sF?tqzHRXr%Z~JSun?-yuxi_? z&jubqC8oF-Rjyyz5@|h=sgiLqDnpXcl0a8tI^&|?U!%CI4I`pZ?E85uQrQvQ9TBROUPV-S=SL%fou84?m zO^G9o%t3S%=(g5&Cj?)bXTvPNpA&k*O&_#i)*1B`W=sfeGlJ$L;O@VFpRCR+PJ(qA z@!0XfmqrFq_FZrXj5fadCE0{Pk|ndeFp?AQOSL;IJ%-i!J~~d}ORQD$pib^>2EWDu zG+0&!^NBdhQj$$VR+=45D5eY769-diath7x)OB=J%lp=I3@zJ^Tgr()eF~x^+0U#y z<;ox&M8+nVw6ae1$&H%F#?DKYu&l8QLCHtxKwxDWvZa$EWlUM2iZqd3wg1wSafS`- zwT$+5gi#o)YC-Wcx7;(PO)3AJwfs@F`Qu&h#A7NEc^! zRJqiEE-~ly^t2x9##t+;M}8y?W_fR6`TcWM3)bGEqL05mqdLCsEfe!CZ*6gS*a~&F z-ZD8QMH3&|E~D>HawJyJ{`2wNZY+ccQ*>mdFAD#fpkVLO)YtjN8qL+j1_?RJ8A`@H zDvy0)=Mvb+HLzZcX$7{!q9N$U4C^L1huVvL88*DUr@c1vJAkNqsI;#)KBk4uCf3BU zz2*+Nl|3t<^giuAz46>Q<=f6`F9;b>%b#slwkD+!`X37m3XV@4iX#2kQ)86r`uG}_ zvUqTlN-Q1%WC5>iW=ov1SG$a(9=?Fdz*zOBd0W)7MtRSo`6o#%pYSX^KQ@VmIXHw4cMG};ah{N-n)RNhCaK!kA(v_oCTKacYb4HC)T*FihMx{DZ}S*l^3 zoKlt=M{aiC#fukl35lS~O0vrq{YT7;XU~X`yYVFRPvE7c0?H}l`T#~&&xwyD+VrDAS$ z<>=zLJ9ck7v{o_DdgcAAG%^ah1Ey#Q}mQ+)h3*Ot-pX?0z5`p^_q z=x0|H$sv~I9kaqag1of}b9n5jOA=D;sj?}+*?{D`6!Kq$$1W1Ja8_ z{Luj*p?I9@bc=I_FIKK=P7?SHJ~>9WrK-99lI{#`R93t70*;axN4bz&y(cvMoo_Wb z*KV%ohP^!SY@EYxtJyK4v7Sxk;36aRE7bK|G+W{^O@-rv@FC^FOr??wZvz7Od!yHO z&b=)#_@v&oS?&iUS2Tt|FQ!{~tN#1@jJqg=&Q=%@l&9Y68xRv9wn3E~8SROW$AGA4 zJLV2p-Ld>S55t9JKrlXnWCZE<$tVKanynRS%!BNYQCoPK>bcDF`=ixHgWG;~-=FA> zSXZ66{Y?Qrar)W#jo(eI9+-9w0xHmv4oC{QLH-9M?PE_eh(t`$m4gT6lbCP+u=Lc^y;D*%t3pj+KXfsbL<$&*51w{;z)U2 z<+k~A8_BfZpuS>jwuXjkzSZ`5K%ZrC`P#LNM4CK*dO>!m2da75tsEO~**Dp<2r>c? z${ONw4xqnfmkYU44s~(c2Wc3#$dZ~y)a_XVqzJTUBafqvoW`Xjyrs2Dm+n&1Z-NVH zJgTR4?Ii_c5G~(nnGrBAvjs#Yl15@iFA?kZc-eRP@#1~Ol)OIV8}%@0b-%GqA|70s3#WJf5@hk|gmkCAX= zVG(0m!`TcWeU<>hM3*%nXq1oYY_%EIr3`1^{m65!v?fFfm2ck841y->oG43TN!#K4 ztX3Lc%dU@X-x@_F_xbbg?i3f4h7#mS359<}4C}Ei9_C~fgm4duO+;kyD1GlkpEO?O zAQE9JwILVNIGxf*Lq`WtiXcT*Dq;30u`aK)a6MPag=)_jhlPRO3T|$0qt47QwY+{I%DgelAX;7^D2Iu%rrB99 z|73+uZ=vNaahO)xnnTLF$IfJSAD4Hv1yR$(g_JDN{<%6?eF<Ga zrJS)3&*RVZZMr&`VUzRSDjro$!BJWu4k8hDoVNBqwheeth>ujq3ECGYv=jEJ1*3S) zl`7VcIN%>Yz}9{l)POghAANS^^5u!MeQeK!%=Cjz`UHUaZ7>_dU~$P6_hs`|IYnQR ziYdV|-B=PE^CbmEOi|tDX4HnuyQTBg)Lg1rpQEgd_R419ijtB=**h+BptPzpfdaIj z%pg-t)V{V|-o;%lz^pMt;5gI4-+k*z-V$DyQoIfxS5Mv-a+AWs!VrA=BCF$J!B`#9 z?UC4vvL>@#^LBOhus0ngg~F9f1N)5$-X$nb?G8M4kuNCIJsaZi-E(7B6a^k1OlWwV ze5iEhVK9So+k~Mv!r{RPF$H^5`a=>rsf&RT5k_*kQ!^o(*`OZAgwpQ$W?E5gG4M&Yb2sP0jE^hPQdjy)lPfRZsr!RVD*w$fPY z`N6SS+oMA^CG~_P>4p{-(lk$C`vw6U-zeo9Kl?=+=TSXVy^1PMPKkw6LESl&p|P5o zwd!SFUz<~sXSr_DUp)+m@*2jK+de`S2vgN zWs1$VRcUIf8*jhuofuz4c`ra?P@xXH-g<fJU}m8V!mC}^9qJ)$ z?VYN<2G;pczAc@y)O(HXYure|x3I0Otf}C)?dlNa8!opIDR&xt;pSK+x>k`9{p{ua zCw!Lhrm(E48|%-}pH|0$5&NxQK44MG_c0fA;~nM1doyqH6+OS)CF2k~>5=Esq{arD z6x?L>|Iu>rXF2B2S1iR45b-91tbF1-aF`7l%4sWp#wCm*t{tu$f$&`P?p=_U42TJx z-=R&i8*jvdtlKg?*(X;qL+OfnTjFzogx-^j7oC6NH#TM@Db?6-ht7Yh>H@^5a7lcw ziMqR+TLnrF0C8I%9M{{z^~w|WtE)i(n234gs|98ZZFV2S)D+8i26I_9C7j)a9*ua7 z_O!LN1qTOz?U~SXSsJ;0&1{kzy2*U$(g(Ma@4c0X!~r^IF@I@h&;iu&UErW7i093H zNukIB@wN&UI_g%XQKqxEyoThqnqqqW`t`;P&+@w?W1F5&uvtUu;(oMbE3;5h1LOWO zUEpsgvZ6CQzb@Aypb-=eV;Swndp;Cydru*riVQd0UfmM-;!(G}#;B27xgBg6t;YFe zlhEa>ynN)wsShv9d`dxWD4VY?V%vR(0we*u*Bw{v{c9~~OFPRKA(Rjkxs~Pk6Z8Fz zz_rk8hjVIwIa&i|u^j>-i2Z>5MX|9A&;2A`n~v7UdX!gp?^NkjVe`>BZ+(43Y1*Or z1bG&HsyUmus0+PIQYLEuYx@R~Bwh;uw39nFJk6lrWNf_~is3ghFi13q0zEwp9%5jFS-esdS-5HiH$YtVl0D6t)8l(!Qovm2Ls zhpzzu!En%mAZ28vr}2_R2k_KJw(%;MFASi9oSMmeCyfqpxDr#!=b%B?5L;vAwMRj`+kg)S zK5P{8;|3wUXG)#dbWc;OcRse`F=+|nF>PtQ|3)-O_`Zv2Ef$iQw!h^=FXr)n*=@7& zi}P{#)%{_{%NO32!5qEofU;`f{?_7t;PU7~$nf5Re1YZWdG&oEHMLA2h(*n9He#LV zDR_Fy;0P2IkTv^#P?J`4dV4u?igc=&cN?;0+yXCm2{$l9p@>8Cf|dYR#bo(I(;q|o500u3r9Lj)<&>6KZfMpfqd^?j7`7T`X!bezeIsbZy|A;n=sc&Uu)!+w4v39n z0q%#(8QgZ}OSzfv5D+mv|v z9Sn0A7WfUiOvZk$U!~u%7q8&o=zGEy33@KBtd3g!@H>sqy!O^VJX_urY%%WNh%InM zZ>CyL%-S;$=J8HF!0h*n#XY9~kV7wK^Y;5rhPUI9a~ zwml5nzw0um9uQ$oEi&nBzZjkDvFmE0ErGeYvs#*OIQPMAaXSFsJy{W%>re6Ppo~Zg zQZm+lTpZxir_lFQV-m}Cz8!UCHM>P_fHfs3qq~1Q#csS}!^h|O+^T$xfFc4lwM9FY zB#zWU**d0sYitX7d@nV3K3at(Nz48|$3fD0zXZZQuQUKlTJ3<1p1*9eGt0@cur96} zGFhn@vRJ*Z=doEnEQw2k0A6k)ES0v*`nB@0)>X&7W6yRxkDC4jX&{=E=Y)(BE^9|7 z$286&C(ANRtotumR%y>}&v{(mjuRFyxv{27GT`(EK%I}FXz`qGj$@`(soDdbEGvSw zQ>D&}9$Vkva522h(Y~s=yQ^x zB)|X`AZ`I3Ew9Z$4Kfhz1)6ki<4bz-7AaP zIqqQAZ9GG4Uy;|VE&~9X@)KxJ`L67K{aw{h7cz8(CAS9#l+QmI5v`k_`#L&09w7f+ z&13)N)$Hm$0hZh{UF4+fpg51Q3%t3RJ#zQ6c3fu;2AD4=2I}<@DmIOo;=Nt^@&Gzv zttyS}6!mB_16unM^-8t59+WiPeJji`I=4|D;kgjKI%X>7uJAQmq)nu0(Ah6=Qtyc- zuR;^@E#qWGcH*9D;~EW8qT- z0%4SprK2~`;A>FLy(l<{7rk34r#g`@4Qqm>D)?-@smwycD2y8>o5t$f43%L?$pTqn7P9C4s)K>b&kfD`iOmON|Tz_S7(`%@Jaf)c}fw&RbHM* z6D51rzRhpTlyb5izYg_IUiSv@VLh1QOa^tvl=EIG-h56k*mM*D9 z!F(yK*fO@tN3+;wkhK!F3HK&Ef}2+0GB-D`t)MsDXG-w))N(UE-+pt!k5wUth_uE| z>D>=Tac{fJOG_Wg{E?kZnJC+;(azKzBaF+awF`(RD*9$Lv=_-~+{yv}zhYJK!@DHo zJ9B#vza~S+j#b&&3P1ju{Jjpkci%+Dv$wL(aR7bPZTr!et2vnNQ3z6BkXRqM+J>{Lhzb>30wJJv9Tk<( zDK&e5jrakPm?opdKBQ>?Su55<{2H*BL3B2ywfVC+%okUIvDU&3{Me(JWuxkIJ$EfB zy&T8tPqnQA5LA(qumCv!H)qE^Z;n%5#UlqPlM+B_yC1j?JC;k57lRs2AU%+o=$vp- zIJM|ENxP+U3&iNYlbT?bQ^Ph+F%aJ}c=x~>0C5u>0TB0*U?Wj-qm+Ei>UjBxnOQJLf1 zo&IG7W2)z1e997A8f0d|o|dT1uUGDb&X_LiNQo7_bZlr>VZpwKGheyRsmoPcV|K`V zBDL1UkYU?A$wa2V+_`$X&hsxg;GePe!7I{YFPppRAgrn>e3I|!`3*snS?27#x4-N- z1m@jhF?j5o9Han@v5d0>rGX)n#wEXQKm=8?_rseRI{r1WI>Cj zQi)B0DgUa=?h9G}^@T8?E6ojS(OytBUU<@2NV760wm-qgH3*ky7)BN=btKlx&?3e( z9d7PV|D*VRyuzW6jp+(@d)J~B9|-nQVk(8BsR!d7Wb3DekMn>gDkxGr{ zopj#!+}MoRmI`BSh)r5}G`G4P!!O=`F}x_zavR{Qb>@RNtO7r(qRTx;>--+it*-O$ z2g^p62o?XP855F6q?W5^?Gvk2Vl&@;g*Q*~DZ;JtC~b3#RmyEO+DDFSQ;KcNyi5o% z5I&*e$*sQ?^|WgiN$vT@q#WzTiGG`cf0y=hSGwXQ&zC5hU1>X80+DIZ@en-QP+yTqOVRQy)?kul`n-APr; zW4F@)`MkDySky)5<8md1x>W(S8vyU%m@KM2Xf9tboNxqvg zcW*BJa{4a^Z%8w}OgymJL!fL&+c8^8VPiX)hFg;qP}}Ck%gS6of(Dg^P0M3u7%VR}#fa8fcBTh04cg3o?J-gJubPxYr3|C7lM{M+xkyt?|Dev|BnJA3 zp`6idN%g}Emunt_vxJVfJZp~xJ8c(^KH3am;Mg1v4<~CvKD7~fvJkT=bZdud-9Dka z+PYtE%VNCSKfHTRZ!@5>JKuUVm16~6lEu*f>ji)?MG(-QM1raHkS~B2<61cn?NZDz)qf-Lh{5aOrrt}L5ay%jT%e`@^ zjfV>^ptNvIbEIsM(KYy2Gm{b1NUW8%)c+>RCZl9x`b=hQbF3y;Dn0Dn(prf{hvLCE(hw}oY zKGkp-ws8JhwpPlMqUo-;?e0b`D>FJDef1H;>daXtXq&+0O<^!LA9rUX>*5f1*)Jmk zqTBj_H{PTJ*ZD)51fz(Ii+jkbU1;`jXdcX874qV`LVDI~Pt;)Nv?*>adC35|Pm3?0Ibh|_pJU6&;HZq^Mb8kb^$+ju$!k_lzpxK7Hh4vsA`$1o)q<}y*-Zz9_Kh2*Kb$FPuSoh2tlXs&#J?( z<#}H3%H6cx-IgUDqS%z2A1s%LyHzz;`u?U%L`oI1ufGLUa7=`267>W`MfF}#FfJf> zKqbK#C>{IHgKB4L6|fY{BQu5lIW$qk(z8W9yUi~N_S&$jCRlyCr&$R%N8Pxh5^5~Ta~bQ-toni)_M4{VMV?q1ee@Vj&@Obif+ zep3H2zvpeo4A3{G4BdXg!3EeNSLiim({DwI7`WR7ws6mha}LS<^i^wE71WdI;O~1B z@{JYzrdmP!ot2*hUcZK)AGGD=%9VI|lzbv;y=;{$x4(Eka9-F+N~)=%`ug&%%7-7e zyVRboi)4*|{UojDxTgj%{o6xPB@kv&fvQ$zN5;jBc9XMxnBfz(TKhP#X{8Bs)?7$A z6@5~d@z;|I-m}*L3%o@&LEbH3AtpV(ek21>jgtu*xeWl!AXUnIbZ{Gyv@4e`aVe`+ zojAc5b#uMa{-sIB3Mfh5&DKFF@!}x>zL$7g0;Cl=Y-SQU7_ohz<zGiwTGlW*Xg z$IW)Y42G$#+u7AVk=0=B`2=!-!2IVjz=u#^+0B`~!8$<4b(+=VB&qa+2V>K;&?5)V zFB}pps-;zVpQNLtn|h*ML*`Vzpb_W4==B-pMxz_kpAs&e6;rgX!><_WLm< z>JE+tCwzIrgL8&=w-@T(P*<{*&FkMD@5vv<=I$%3hVv4yCmr;_{&!LTXM!emKLK=~ zDgmq$G%Q&K5!OhVn&>=-DSvZjx^aUm|DL$#9=)CbvB=N(F9AWiw%{0o839a^IRXIn zF7CA-T8UhKP$K;HZL@R~aH$eBx8`g#Q8U`rlH5E<8K&-MM;NbN|0Z>0Z%C^tes5k( z))!jMrShIqE=J%Qqg!cut0J?!OLnX1r}>kJb+PTH;l{fZ`vVnjXlsCb6G6A$zdQfy zY;uS{9EKpA9bPtChs`xkqcWb8;0I4IFMlfTh0iw+D?0QO5Ts!yu&xdqpEVa_O<(23xYlTaZwnk}iee)zmR zg`E_v14|^H_DOg%*BMM#D8NgKgZm^U0ZgB$l1bLoJpqREtm%QHt=ZD$*~=UuWHP95*5#hY%^ zHEVC&U(7J^?fvsNfIfk4dJtKD>(=sBe{FWrT~AEYSVnEroT{38%_ z_}CqSJfGFwhbrJKB@E!EMr~{vDDT&h_TX#8(Sum~^x3l$n)BOkfawNhJwl2jndyA| zYasZb!3QEqbmt0Q6U;U$IQ=o_f2#8!IAdY?HUm(Xm5=dp7r$l73<)y-Ab|T-Uywo< zZo+id`PJ5cys8JOPMJe*?)*6}s9gB4$e!)We0A5i$5ds97IttTFGsELAJ?ORPo$Q! zEwOM;P)l^*K=WPJ7I00L{k30KmaoQB^@X4KHEQ=`El1c-PV2~W{PBID-<+za*gEa| zT-)#iDJlEufs7LAPby_JwVGquG{{uP;aTZw2SV=)x-8yGw>X$CN{)> zP6!gP<+<;0wo(7uR{uPWK38kbKC49<8~w}>{wpv1r~jspfXbKZ5?%Akuk*?O{v6X2 z@EKwZJm}Xop8x(xf)5x0R;{(Y-t<3ACy#*XRjOOs5{InVU+k#&QO=X_z`zLY+V1)v zH}w5vx^h|-W99V0_`+d(*z8y!K3n0USk{km)xUi6ukGDw((j%@ogdA}d; z^PRKL-+OtgyNaT^yI5<=m}8FB-xTD;QIQFdp`f5pB_%|ZprDX+p`hUL5aA(bh#W5H zAUkM#C2?V>(vi1&kY8MkK$2f%Wua&x$B0m{&;(F$zb}D21fU83bu0$`9_rN}=V73r zf=!`d|GGvFvj6?_1M>L&nm_li(xLyl8gecj=I^VKbkkq`eGHHH`(?>ubr_Hxl8uC# zJrooUmXYOcT@CpMOx%ekK3@ zYroXd@hI(})N)oE8b!98>kUSe&xHF4Lk64#86Zlb^&)vah&S3jPk!J27M0EZxZuTo zWO&Iw?V{C)oWeW}3m%gc`uD~yPs+&n(R-oEnL6L zTuvqg9fyi17X42?@vnRHp@w;*>q_3e`A!JtA8)#!d|>DanM41p+m?rY-+nIgwNKIq zR^p#-?Lkm*AWmI8*?)e|LNKX_3O?K401^MyaG^pYms_+m|NQ6o>;ub(kq$M8MMg~h z-^?;0WS-`I^w|G_QT@yOT*x5?LM_ZE_umW`CuE)~p}S1}GV%Z1EFo8(ia-oRKENgU zzZotI$UGUn3L5{%W(l$6wQmpuNku&4{%?kB4gi@a_}5wgm1SZ<41^D3!}h-!uK(xA z|7foNZ**jZ6=j%z96-&Eoj zNvYUj1KpM7`U{B)?3k}a6iwVJOkTl(dN$ZABL9))!pTB0!XHePl6$I)O4giyZ@gD- zFgChbkFLCth_l;_la2&_OnWo4iwpe~y6Q}6qkTv@v)4*a`vbsOl!i=Yd0|~D0H_x7 znv9(fJXfLR(g_4jlk)qR=V;(n`eNQSiMaB!kdO8b^DQ{RfHfG*+mg(kREc$iy^K zLCcswKS0&oi6p*jZ@v|$=klFGF_MTXdbHD3k>sH(*-UP>!<|8w-Us_@i;82__XP6L zD(~|vW&Zfs-rpbFOC2HmpX6J_uR=9oJ=SV>zt3oNOllvDje5e_=N%JNykY#nLp`ZN z{=aPYrt*UVRO#uctK0dU zAHiz(@L^`*y-DsC4U*ivHH)%W-?CYtVO4?!N5lNHWEvv}W0Y-P^?q-RSxGMK16`XH zHx+{No_dKxINbFDA&kbgv6P!i>7_MLnbAy9qq7S$Jozv9@*f=t zNm7jEddV`hf=Bo%E>uV|ZVR(hbg!x3!ICOzaZ+i>aOTMw`1b$D4U|a(T&}y8g2ats8WdL zokEFSg60gF+(;^a-ZGk!2M`$ny6G)5^bleFQ8!FS9>gV!pU~lPeQFHO0QTse;g}>7CNu+YKU` zu(fXGkgSwTspQwzMAy8Im)@F=e=%NswB8+xJ;OS#<{x?W@8d2c@k9cQ})sThwg&MN~9qYE~ z0yRKbiF!*fO}Ng#jS1hWeOZmb{XXFpjc0UD*^fkbT-DkcB=_Z8kEy_-6F*Dl34vE2Aj%DXjlwwb(8Z$Hb3T%P>( z6y@J|?i(E#M>EyPeIPBkquOHLvED&#m!{P?jDblGmbr|e&&V8NH`bCgi9H2(&^0v{ zLJf(D|C5MEuq5)aPU3o6vop=1=>!DibQ?l_|L4#D`~RN4h52idN6gu3_;{1DF8UB3 z=?yEiffxQo$&Vyu-DzbXpl-ez#$WdxV*%4M54ZkwvrMU{r$_k!TjBg~=3U`CQujnmVV@9f`vln_jyWV%6?gw|7#XNU7Ql@Gyr=$FQ`BFuRAooX;WAU!Nf#GA<5(m`MA>R5QJSm>ECh+s0$)e} z+Si+4ttfm)hcR>Ck{A{Hi7bwd#_K|av_ggQTNysp!S&nt=nuyHluVbK3C0Q53C;=L z9~vmVhj5oBdxZsCEdJ&`-pIl*wVoE^Y=UYSDJVWYyY2G327}`m=;EKYF&j(|@%9$; z;)M)12~>(fl*)PDq*ZN+26PE_!~Q1#2Q7|UsfN8?mWcx8U=K+M+@)?C)p@U+=SeE~ zcw2?}MeEjfcl=2=%J@LlcPip~#d&n~qGtXps7)`6?_mk0ceeSa)>3_NjLiSMwaE+5 zdr;P{XZw|AR!XZk|AWTe)+AH%hGv_?;hc7mHirbfmTN+=Kai=!yk)c?)V z(gOf^oEG)fX4C2_rJ7``o?O&hEm9H^Kg`RBa%Lh&`iBxp`q#UHKWS6bA@cj)UmXml zfAlxn>yv zIk4JQGX)+QBdZJ#*Y_Xi`}1L3(DDwtlytRXqH3)a6#2r;*89dkH)Pk2kV>)@EsRW^ zUxdqe{rbLsBskWdHGS6&aYggUblC4^&qq8z^xo1|oIvvEobq6JCMw7S=k8D zxwFZt9I^KySwp@!R~C1~$u>AvnY?7?-tmBeYoPXs*KlI%#&9M$jr-dHA5J&y{~Atu z{Gd3muMX#>)s&d%THLM}0nj+z+U0BEW6iE8$mQRAB+i!l!LfAGT|r^*D7QNoa&|Rk z6h9+P6-1}g?@{CA$NOZ0Gf5YxYb|+AS6f@Aid1!&^RAB;shgaRJ|{VNMH)3rgM*-QvHE}!$o{kTi&cbST5Ea13I|gw!q3%08Sf2v$L~=^jUse< z8jb~uIex|SDP+>MIfLef=ot>H+=}0l^kbNhZQR0_IB8z&)1FJr!CIs7D2!mz_k(}!TRSi1TA3Z}NMj8l)+=~y zoCVk9u+q^PrS8M5jtVByyttfV++9{NEmH+^ROh9743(fr)WIbS&Sl$DxURR2_Mhr~ z+PdaFRzk_7Suv!GVLv-_jOnRm{@`-zV>lkBpONgJsct&_vu5k|?DfF#Fhij%ebkX3 ziW+@wsz6E+?t|ZSzYQgQSXkKPvHZ=6Jeo>{S3K7DvyOwv)jWUvf5AOSRz~)PDo%X4 z-9%r2nJEOOavj2DeJfHeQxQ%Hf{|W2m@PvWi=_Ae9bdll`-U*JifBZB5)UWhr42?S zin<3^YC_^gj#||YtLwSQ2Uk|^%w*k8+L2j@;>p>NQd;AY)M$#nSGe^br}QaXg+0o4 zA}7czzE2hJyX@G}v#D|(@sgo%arh5$5O!v^&kE3>yg02g860kp4D-{IEUz}XETp!JXG5lJ@|3IGVUmX>zB=+(| zM6_z;{P)f$vpSNaO`g+c?86{8_yg z*DToEH6xaH6btbQE;+IDHjm%kJ3VQNvxz&m}NC_9U%0?*CP zf+vpltOk*0J`QmW@eTPf`!tw?J7_=3XXHAx zDghe5H(jY?UHeXEx7PmAI|Rv8AVIWDqpr01V&vU?wW+eif&O|=uuxw(kw1 z8`takyb$@W5Fu#;ef_Ny?>6ahVt!hA!Kah-8bp-fx)ryiXTnP`9TQxMrZ+P3^zD1( z*(~)Mv-xMDV7krBM)R4pi+;ZUZyQ1E%+Oo)e? zIw`KrlI9E&?u9-AevM`Lu=?vNr|eyI88I&R;Xo@-|O|bYXs+e&4QM{D_zzs!(cyk3UZ#cK<$7L5-4?CHS`A z4wFPp^1iNs<|6iVXHqn{niS6T2PWfdZ*_N>O1qJSs=Ld(Cth{)v#}*0&3WgcR;5>2 zSBlAJ5x7KKRo=ZZQsLCb+HXtzJ#PH`V~+wHGgN0|rQ_-zj_(;XN|l9+!X&93z*5*? zurwEkDtRTy^`^|yup6bwLJCp)64i5`8>xJMy7Zl=22ZzYM^Vp16gZxaVpj7FU6GSf zf36_{Zqbr1;u*Gx_f5Wii>|U+>NIm`AmMZcLJ<3ph!b-reiAME2j!}g??`5EcNW(} zExr{gN;0EIq`lUJ!k>bNZ(v3)_j?`F^Jn7xA;gR%GF{4mwdys@bQU{~_8zR`z74nM zy|*2e>ebO^XK08@dFtA6olz3Gy3E@x7ERU47>q{j6fbj4Nx98SJam77eT=CGYPb7_ zHlPZMY~0%L`bhUsAx<2#>a9bnQEO#S%SKrYy}=fWTMCi4dNr!#b7SgUq+Gi80e4MMK(^bY=2 zT=>bStJfelJYBaHazt`#-G<^%8cx$bi=hda#z{aL0t~*y{BHZ%%DK&H)UCp>^o^rG zuY4=1h$&=$vPb6Uo79YtJvBDfIkKimT+w241Z90J_nL0B0{WtSq!9ABx}n>by5IqZ zy&rT&th%o$rK|u;%^a*A4P)tL(8n|k$%lw6C(ZmQ1*c??HTrJTQDhr#ft$cTCzI-G zqcWB=w^s_1x`Ey}CSF;kLaTk(_8?1wo1I3!Gi)$lth>swhjW&lo~o!@&DW`8Z2#@r z@tWcCscJjpASd73IdP3`m$*-FoIOc`uL^P&C)mwrJu;pJ(mgE;v`RAhUbf}xfLaT= zaRSees=P)kX2x6{cqmL>RT@rthNcH5aAd8l1n%sg_|ueN=!ZTbm%pa<_!lSk{tZk` ztP#rWkK|?@l~N_DB|`QeaSTcBZxnm_`UX~yo3&ECcG_G;m#^yDq-10qR-BvAnGqWG zx~S%B-0K_;$Fv&?>>kJ1C$c^6w<}!-RZPd56CL)JB!8pesp3R$1UOi}=3+91ep_6pnl9mA7T3-NBb&q-OTa-M zYhse4{>m(8S@~$GL3yTBD+|SJxiO4%7!tz`pC4CiiUjRZQ+OO}m*(n>N3D*|!O1g^ z4T@P}N<}J`^Ubc&hDJt`v!z|eAIY@x z)ra!b>1d(op>Y$ua3!XNTipG*L>*{J&s%)oG?N8@Rw?~~VA-DA;rVcF=EN*gX*i&- zg-!p0)3SsyGdApx*Zkd%{)OW}3q{w5e&y>s*RD#-%o-W#Oo9J8B3=cGz;eCTFavB z+=W$MJxzTNMx9oAYfDDFP3$N0x$?-$86Ek|tSpXr294VMGp~MwSx8Qqu1SyV?wcLI z@p*{3@*V=t3N+$ORhzPzrq4v%YTFXcH#&x-+L!KqUvy4(u0QezUTQU+hhUi%$&ToH zuC;qPYT39*G2bInahOdhFe=R!pnl17;Q5-yEN_GAdoZQj2qU7g_649)c6~T+z)o>O zG&@Zx9oK)ljxJ+>u1j7EH_`!B{xaA#N) zcY?}?_{>FjE>i`VR(?`_O4Fgij?H;rbqnslX~Enuq{C3oLEg^(C}U)c%>?&zHJ|#& z&HXsqtVdE5t5G1PPy@V6Nm9+J4*D4|FyZ;Sc$!2MrY?R)oZFgP7i!j4 z2EqwfCjF}TYeMo-z6Ze;ZWWcIe7w?DcVvl|j-iOH1XB!hmW*&re61`vzW04e<<4nU zaK(PbBq$yaqfHZpe1_Qxi+pz7%&vPqyB1BSqroKcY9wCz?r#xBk{fh!@W{EG^``eZ z?2hsYDJeExv@0R?7C0}>ljfwA3n=hln5%B{4b1z+!(_Fz1k(gHW`5PfSfl{1IM+3; zMBq`tJ0s^7+0<4b&E;G$st{-7`L5klW!~|v=3AT~sKMHgd_pi*h`Oa^YE=ue$i%e6 zu#j|ed)lRTKjm~VqgC2+&GWQoxlm2xm14yg<69#Vpw;Ab2ZU$)D~0}1fx^ZMr3rY} zKvB`sN8NR5_up_j5;ns|A^!9EPTaf$r?_9}tKGOL8Xa4`>f`NZL}+%; z`SR+8@5oHqs!$2h%KJ#OPpKbtOV0AryrvjwO1d2&fi2#4(+VS*&>mE8lY#kQ!7jf2 z@Y;85uEHP|Z$!`a_3kSJ>?>WYQRmg+0W7xd!9(M2%6LuMR|wjvTqHImpw{M1miMqK zB%bWjp~JNKFr8wd4T3L^p9CHbl?T;XPI|*zBtzWd7`39}sm9Kbp=bLeXC?Z=Q_jKc z0zyz1GaA-r<`QDbXK3;W-gkpWZgTozSO!F%B7EKA<)?9m>))Ve5j9b4&_c(u`b7O) z@9q!E2o!s+`2_^D(*}(OS!W(Ju~a6>a{Ef+u7`j}iS zJFhCv4x$L-=fMhiebfLroz%ANavt}Q#i6i^h|NrS>a=<^)E2@i()E72Ip(hp>1sM@ zRgEC)c05{;O?5xUXQ*Z0XDBvrRP<(E%_4sbQ+yeH$3_33B=3hQ70Y7uRe7d(&1r1b zhp)|ACic~?R(pm_Ov{x|i~9#>M|vf4`X;C%x!YhUj0hkidmAvZwcFA3vlg{ZEyXe? zVX-`!gXy7!c@lH`E1OM3)mWQt1ZbRMc~RA}_vphiaoWQvFLCgoSk-x!-ut4n^F4CP zY(68;=TJXgIMj8_?PI(3ozE1FW2<4V#zS4g)(VJ(LeMIR>y>|NG7xc~>(L$$Qk(~w zz;;pQ+-`>n&FA|`JPRp^C@SS{+EZFM&lM7GH;w&SS9D*?wB0u?XZK@xI}GnmP?!eK zvFWytxO9NLbL8haEaGo@b3J6z98?x@4W9ry_~bOrglXfwlg3M{7J3=*DEKP@+TTqV zIZG^~trn`)N*j*F7PX8t>MY|kC`zMOz3)~gXs3bxWe%9Su?z8P4CbL~oXz<;P9%G%!BHB-efH8R-1G!V*IIT&Ddg)n$Xl5`TY2gSy`b z`qec5OYem(606rEtYuF((1!z3rREKrK=R(S3b5Iduba5Mmct0_?&gVr;~a!a$OyoA z{gzt+rDMpbgB7Q^H=&Ej`EmGh`pCGL&GC3i^ji~^Sq7|E+Zyizi+=aClu#9LVlstf zL>R>WWhhKNfYovTLF#R_TG=kU$(TYA8c{BTdB=A}$+_yygF7FTD&CXFq}s!a0csLD zx*Ni3lTx`99=q*W|LPwIogp9WTOE0r0Eo+O2bK%VA4c}9+n#6vneBF%0hYKr9^aJR zE^ra~j$0py`Hz{2Rf|ETeR2*Cp_Th=n!AA}h~%BRfjA86zn%>Z4f}V~ymPx5nx+Xf zgKG6*2W{hZyaJb8Hf7HrFGkJigz|BZ7gz*H{eJR4m`fih0%*vDvW>7G@yp)t_7e6< zh2St4EjQLkL2=1zICp*Ug@W(ILj)L)Jd*0favSTmL9oCux=N4kUeTxEa8|>91;^5t zQaJWfd)Q9$uVuQ>NS>lz#qRpyy4vf>vDeHlr$DzQzK26Lsf4%!*ix2#9$q1hnNE(I zMB`_yH{S1!34_ z*$KKRAZ@qmIA$xrf@ZHmmA#b*^Er=h=Bmj{f!m*p$`E~X1^rnD;<*-0|0tjQ!PoCdy|8lM4E`gKGPKj z?u+c)P#N*Dx8P(XQ+V_Sb+2dJ@EqNKiKf|}Vzv!`wU@_LFXqBZ52p7w{(#s9l)h;g z1X!C?n4v0-KnRP;6x-Fcoz40XQEnjaWxufFQLcM_;>~XrK7m44?iTy=XD)sATj0*l zO_7%EfZ0bj$vAqATyXWO>-J{_#8zGCG6*G6Y`w^@R;bE$ z6W&(?zQ||UVY(1Fjgkg>CwYT7PI1&2o z+xVA@$;rtn?xjdofqMNjjGeKHU*SYWpm&;eR$AHwoBrkvV$F9%y9B#LAwO?J&&zLL zM?a`^)W{rm_KpZ9A4F4EU6xAGi`-#(5V3WbNNZ{)yV;BbM`J0(l`dVlPM)u7NlMgf z;_~ws?w3f>T_BiJ*|@L1X2CMV|M~ot^%N4WyA6U5+?P;WRogNvpJDIP$Kt3v9s$k4 zcF?sEBSjmN!BS1DP^*D6b7{)e{;ufq?<$BRNDjo9hiRl&n-PI>D%QG2 zs5cT2E^@{dHMbt!|B4-iRY@qVs%e`eKr)#t>!^=5>vq!2rbg^>O6G_@Fa3@0`4YUG zs#w?b8&&Oh2|nNY<8a`cj%zLVhd6=?{#?p&oT+D+aWl^djed4Uiwx{bPIV)cQ>IlAbkoNt=J@lbo;xJO2nlTyBI@Hd)XmWXZDqM;Nrpz(_0X(|8}dfsKU;{@U)+0!hy)LEc_UA|%ElAV zRzA^8(vD6q6U~g|J^nOwoHNi&zvEX)#}pCeh6U|W+dG~kgO66d#W*782=vbHm-s6^ z#DlVpcuK#Csau1uxQ0VlqqmRg_6H19xuS?=&HF=E4{<=47gV1W%#T!^BPwLf>Jh8? zOt3ykHE0Gb_ERnazuY{^TkXWL>Oz{w+(Kb8~GHzE3M-C z3Ey~#*MK;vvb2*i38uJH;8k(jpM{75QZoPUbDvWHNs|n@=8#>J)0lEf>E3D@>v9&P zf~wM8PqTspRaAtNc-lgqmc#^RZvwqRAzGS~9cEYy&WiyWn&{h}b(lhMRnL;qs}~K= z&Ao7EpIRCnm*Zw?q>VcUR{_M@l5SV65?;6K##-GIMuSF+*F+Q96*nk(p!#oxS{iLX zIekHWAKDw|59jT-!s@8ZBI7Q`$GnQPc%_c&3HLrD;gA5zNxx9HcXlf6j*MuoM?p8i zY6S|5t%H7bj&K|T*b(UU^=>vH(S2x~SkT~x#!0CWzFxe2<3yaqGhg;FDZ;?EtdxKa z;B|H4{q6F2dC>#Jl>c(kq3xk0Q`nXi%?OnNKKkDfwo)-z=^=E3>!gRgc zjz4E`BQza4Dbc7?Y+6*O?NA9Xp+h*l?LIcE?i-pWiQBn8nXl3)ZF`1iQ2&@@lZpzB zgCn>uHw=vVdJ&VH{O+XXrgU`v2K7sC$XRE5pX=QvDZ%{#2h zN@zty%{M$@g!N0mCUQcWU!L9NLNdj?nw-6RWzsD>Hm*hJwkAF@E!3_@FGCmwOAhr_ClT6+H}Lvvn)0} ze~qmR3>)e6)ho)UpKzb}!=ujhv~Pc%=V0pfJ9x$Zj$Nm!v|j&*{H&JuCt-pvUwRvB<5&OHB0-LC zuP_k5Ah0>l?&6QMPwu+sUXn_J1Gy$XKKjTqm#+6mQY`4Bb7FjCU!$$JlRX){d4+(s zg@iS9r+b8=j|XzFL!GPkhx!;|K38c}70#+@*+h)qd-LlTH>E>a2`5L2l3&pB{`1(` zDL0-`Q{4LCozGmGrMRA>w3tY$-2y>*e5s`LYXg--+^87f+6z-Vc{AyQl zO@M$fSM$~JI!44WqKY5a6w(<&F=~zVs&zB`<895Zi5%(ahPN3F z5MLQ;W=AFDoOo&%`&d28p=*hJgOf0tfvBe!JJnm#Xy%!hxlrx`Qd8o6``rwN5sRF^ z2nvH57L*{}0npWxNqoi0tv3u0J$sDL=5#Q6iyrq&Gtk8q!SfLc#Yv2L$8UNSfXL*J z|CmV8QM{_DQorC6^y47sYj5)GV}m}NvAHTo5f4h`kSV{X)9`_MZQkR`+ctY$t%gj4 zm_yzuN-XcWU0IwwcHbRm+M(fi_35{7WAoIBez!5XuH?tkaF3E8?dak#DNqM%8IN4kaB43CHFVXa!Yyv!$jYcbCf;$u zI7J9N_G6uQT;5EVb~01&q~CN0@YwAVVQmGm%nr2N2)?s#eGo`L_ZUcyiX#8bE7W$g zd)!X*&<#x)$_yjdj;Aun{d&F3m#F$<2@)cJU+AR%>JrH}O4N3^)r^Wg)-XN_3BZaAJVkESS2 zy`RPq$%djRrKiGArxz$`0%$>o5Y|UJY{&HqEeJpd0irXrr$4#?9C|~I@ZJldtfYAU z+eOPw&gPML#O&>jii@>JQ?5;)QzkXGV^y_P?B`l~cxSKcz0e&z()o0D?aKS=&C?EB zKk*@)Vec$1HLUKLlXib9FGT3TD&>dtW|e2gi*X$v8;)_NTpfUSCyF=Xc1tM5U%?z!3t55tMz5 zRCUCHy7oU!7mjSeIpDN)0c^a#xog_D6F_&4XZbF3B@D8AGb-m6Zve`?t%d0oTigsK z;HaEDibuphe~z)*U+=m;NCT{p;*oPci9Q*FI((;Ti+&`TTYe;T)Q?6uo(#R$x*)`9 zGSGiZD5@3la6y#RQ{I7md}&h_vAPBD)f?`!(n7itih+qFK0d<2#JuwKQxrUok8~c- z6b#MxIC0wTT6PHw=Nt*)SoDZZj$n2%GES>B3VF_aflzKUnY4yIZul@Wd56Msa=%3U zBO`03c77siaqLJbV(ZQ$siG6v)dz=!I@G>4>YGr0EKr`8lPqt*bQBR0`GxAY;}62^ zp_}X2w?>?46vlu=u?!P&QwIBh%FfIB$~6b_htN=W`;_doOG`co>5F^ZSC!ni7UbCn zr=O8PfW5uzhzlF=o=qI7OqiyXb%7tK_SzrnAeckk!*1C{>CEe}t3-@N?!=!*6h)KA zjZo|+JbH4UYo*Uy(+0Rl!z2QFc`}Rl;`mlXUxHaAbkl|4hdfgS$_TFVYA)xT4koK3mP+*=7aVT|Q}-Nk zWa%WKDL8)R_jAwgt5ti z_#BNXV!3Wc_Xs61m{zqkxz@)ZCIk+J{wM$auT3`u#^S-pyWK!h*6cK9JwMJ2bQHB6 zQMIp-DBp`X#Q9*iL;rB4UXil69Nabrmrx5?LQ@;=%S5ACd0u%VMPBfcNnomtYJ z9R=OCT+J*xtdh>Lp0xMC;0Ap1emY2z`Y|qh1iNfaQx9d1=sC1OUccZEBtCp2=03z5 zsI{p3O#Cb1tcC$l5k|x-m&OL-WJQrW{Q+~KyTt8I7qnALm{wtq15HkjSk8QNSUg3R zQf4VejA%&e(@~$qzIVhI{}VtUoFA+;Y?EUSbQw9h<^Zo9bGT8BUv(@44 ztSD^gD}!A|O~S{sJ`yu=yG*AZgpV01P$>2!aebj{dyN_jyW=JicSM_LrRGsljEBbo z4@C939pN4z%*Qo9?0 za^LzhjwDhF{*NTu2OHWs6Q}6Xx3{6l`eJ9e(FJ6yUasrAV6*0;fFjKZ*d{I3*nNVR zPU#qY8KF722fSH#rs^zEzALG7Q~NA5P<*?lP28cCR9Pvv$37>d3;U?ua}VWr@YERD zd%Rq(5aiwyOakG(6T{Kt(%}O2l;P-q7+k#KJX{O(x!%+!VT83^HJc_`LjUM9oh6Q5 z^I^>P1?r$QIkP$QIGyd;A|u@GT`7YCv1FtiZ@U8suHfi~WvCaDF%(>{S( zp62)+Cd}}Ow3b9t)H>i=rC>`p(QTvp(hEWevh=c0luUL2JnkI3SmvXnD3!fsuL~|U zDHDD+toazF2C>Ap;OGL&G_^ID{3#i|ADWNaB|8p^a-?jY8s6UojI-|5&e%QpG_u;S zLmw@SRc3q)?{5Co&PTu3v3^*ceG~Qcr%Ia?nh-{E;Mkkn@nAtzZO)cSg%x)>&sqLr z)+3D{srB4Q0b^F1t&?_vwbb4?*qmt4pZ0w1E=3LjeWy;!Xk;^_s9A9~yc%04LxxUI zq5OcWZu!dIuX2CO+Q!jyr!;w4DimX1x5nNk!;aTgu}pevyf5|hOo+0pH2pjfKe<)pK-Gj$1A+h{cpqc?sCQ_}G~R{NpB5bb7@bA5~3 zUJh<@x2l->rzNP!?-lv_Z`oGJSgRHWf?6{UAxkJ0GUFpx9??)ZRmfQm=N@*FtR0j< z_GfR(e^l#rxwU-4-g(h_g;S0J(TGMmzsIAmXurJMO;clpB0!l{L6M!yL1268g#l@2 zSqP+;B=D}8LQ0&0inWWQx~eNxkV7T%9ELgMlxgUhl59h2U=q5uaJoV7Y;8L&fu^7C zE>m%J1N^B2btbpp?`k5Lm*LCfFvEjm5v*acO+ z#@k@f{Qgn3$YFSn%FD{N7$4mLM7YOpOsk&3SHEm-}w z-`CS~g?E8j{OXe0Jobc~YHO~T<&E$TNJ+f72N_gtxU5B3&RuB*Kle>egcqPjA56i= zGE|z+)+zw8&h7krSRfMLI&4DSBbb>)K=Dk^;W_gPn{Tm2a85rm*inm=jABrYy~cDE zsV&6*ckR#yx5}H}+RRrP_*OA9Hv>$xTzRx&T`!*EyK|Gb96U+nU6o03nARt%>im{B z`FPcFYSyWPXrnDQHh(n+P{a&F{{vX4Aoqn~j-+~@tb4)sG&@GwFaKD!5TQg<9uEIv zBq2C;0(W;agKJtLd9VEFPZ<3yn+?9Vd-5d@N8sDskw=J7H z_ZPWTUA?x4r-y1+BGksfn10@XLl53fIo512B)rh~geC0vwYAQE+#Rd~ee~1*0v5 zWvIDeypH3RSD_kJ{NPkdF4R0;piFI)V^-pXo=f5RIBG&y>^~o9NZBi@u|Fj_cG2aC zy8^i*6q>p1P=j(S{|`=ULb|*uE%(X2Q)uTHxFUdwFGU21FTkyq zHNn{vmn^t%1UL##OuQ{k@95lb4DGQA<=1Sg_+?pZWdcOssI^|rT_!}sXBBCEzNf1z z<^QpQ=j{8)e{QUuVfFXd1^iwdR{(3XI+w3#U^-tJ`^*p=Mo{h&>QXe!-LVBl2TDxL zXem3-YYDutHrA*9z^8B)*u~i{}s*h-k;d8 z+)ix=CA5LO62+=yg2fMRgcB1=j!MpcB-VrHTtqYSr^rBV_m!WDfymdLsNM&h_4sd} z82b8+>)8W}B${-KckxY+CRkRuVDPE!V|kmTz-~ab0SCO7ixQuU0%cY^%ErbnDf~fsIV;L!URXdQ>UQWeSKxv>pdpJ>;CrwKjhCtEP_zP1vw$TcIK zc^@MCXGWKHONT1TPSd-N_=9C^XYdUpLIt zBS2>r*Wyx2b>E?>>%^gll1XD3tR-%G1!y0-U-QHlT({nb=kXwd*Tan*qj7illWu+^ZW}*J={R^3(K5#ExP? zzNNGFx%Z1#Eu~@RBH6qiD=9zCi8tMgR2ae{h*u#EzHJuN;sW0yLFk#`S}Vsx%&z0^ zHcht*OBR>($76S8hKNrJSVaLw0At648K6~~O$bgnh)N|AV50@%tC9v*Z;-JH+0`#YZ4oMv?{efOh+y>%roy_hD)>2V7 zp`Lu<7(r6DxNGDnnJ9>@q*H7n7R((wIa1gRUs;*zgTCjCi_tUd?b0pO!@OFsB3Z;J=%H?>WM!6|0 zz+9-xUH;us?vf3;#p2GbMpm24U79WN@NBamUxYTNGWFnO4O#7|Ke0sxJhvA*Pv#E8&X{Pyfh5qOXN=-e6|Cmn zf63O{bli5KUo>mG+muW$JC|E6)qm6W|6oJf7DD@V;5K`Q5;C=`QA$f{?Suo9}p*gWU07k2P zo8GE9aT>%Y(fksApd(Hj92?Em=KM^kzcWGKTKU{a*{~L1k>HG&5KPIh-~*f(WquI< zgI8ONZ09|ZwWVtphW5O_jB+fBN7lqC)oc(u@>)o@n%m6we5jqM6uscPm^!9?ILp(S z3cC<+mb8d)R25DnX-i|Mld1nS-NvKSczv`04mCZYbvC-z&&HC{JnKRiJZN4WeNcqd z8}qrrMn#EiE72GfW$`r!=O>qbM%g!1Z8?E_cLbZ5zQGk|wiT7mI!Y^%e#NU`xXke% z{s>DNFin?H&l9RC539RO?3G*Y)-%=LEEGHQT?HSr1IT!%VZLwX^63a;pSA6#&1~)r z*HO7v=LbD{r~Mx{-N-QF+Ay*9NfV9gA65{yHsu9IonL3h zK0ToSn%$rre79}$z4>y({%!&vLru+B?UDsdgM5O4fmT5(621PtM9T+A1dE6e7Ch%< zTA{$|c6sHt{@S!W-}uYUQI~tsIjXrRAoCVNU-KiCmkWJM=>VBnthV|;o`-Czk3*<% zt(x_yK@;)!AOa)86B5tMjFpP-T6v(uPGK7D-zo)ywg;p8OH;EBu-^1&zrmGN_4Jxv zEWB;6ZT5{QgeXeY@_64D3QPeO-E(;rgK6`*Ht+9cmD7L5VuOE@6xZrJ*Fv!xgAY;NC|!G8nz(gSktbRs`e&ORvU|I zsg(1T7}$Q4Kiay=515#`K}y{Cbj^DyRt=Fit6K?oxxx6wt|6V3n>AZ4^VCpHXjgUK ztCb&zDYSlggs7q*!Y_WKb{}Z7uHG-(&!>JySjD1Ygw7$AV_M=DEM>dt{7?5+lLzGe z#vnm1FyxLRUK4U^;xODR3p`z7QK&aj1jJssUF}Q0Jmc<`+~FZLI_wp4W#g}gZ1QT3 zeX>aS@gp79Og5PlN3YrGcxl?8?*96yh-)mN&phUw*5`o_cIcaKAf+_U;97@j0Y?wZ z`HK4qw;Dp%oXL1*U}`+C6E~+aLea{Ewwzh+r&|mRZAQ{J)K#%SC(0P9eAjigp}Y_@ zNejaS_A=$^qDn4mW90LMS74F(=YYak@IYwO0$5b4`LY6&dYQ|zDp_DNxNwbP7(76fRE`7$6vtWreU?PRwzpP5Z)9zF|cv zN3QhxI#K>G0CvM$f+q%AjhWGe2s*82sZG8qA~Cx9Ka{-%RF&)2HVjCIpa@8Z0@Bjm zAl=<4CEeX1AR!HlZbWL)or0&)Mgk_kYi~U-uXe#u~18?wt3$=e(|K zZr3;nHQ4;@P}V#P_*P=OxS*_*Tx&C%ovOd&xmp@}+$-}?Lnn>qK~n!f(y)B3)F_MG z6dM~m{z0WEzD!YH%S1Zf!;R*`|14e{CE8(p{>$-U`X{FIKblz8buBCSe zzDyozsV*4ba|i?4HoBGt(3Y2kH&fk-&5uaM)Ugb|?~%f5c1Lxp>$Z^%u!Ja{A z^jp1>mxAW|%4LJn8vyG*%&QKGz|V!pWFnBVUi?yBD* zzxth4PAQxhAWSWkq6x18%q6Um4V<5scATI<<}Pno`0wLuJ=kXfJ^-+D+-o}(L_Env zgq8@y?6x^PrIIo^MsWp;c@q&rPY`>7EP(N97e?b=S3TMWfD%Lr z`aE$l zm61V{k`HaZ3;-Y%RF6=x32sE*24`q5PvOBhr_~f07XK1S9b3@Vi7yC%IMOW3`e@-t~hXIhHQd$Tl zF_PtDrq}FTp?~2rMeD1`NvVdeBXX@>eNu?s@9MM zb8uKqNk%zarM2_N=v|%+25I{t3}cKmfRCouPuNLtyEgl-)T1VmNZb_5JeL-TaZ8we z?F9W-O^uN@WN}{q#vY;+mkJ^*{MvZU0Q_boVpy&`cM-BlUVa|4}=;6|ak( zUzJ#1aJk$=jgG=jBLToANLzggQ`W!p7WE6tnQWV-d*%64GE@D0A9>jYE3u5Ml|_f0 zr?l&B?zXtI;E*_a}MNxcqQ zO*1T85`$eR3wJb9L%4YchX*#5hDJq1{28y(2{k}Aau(eceYy?al}6T`H3PAyEy_Wa z(#^QVpG*rYXVI2ZhA+}XDvhUnHG2D+W>Mx8_2*xy8){i~4xpoGm=F-fkWjRYVIy3m zb|<-Z@v+V5K3}A1rwuhr@czd-JPd`g;jn$ltKXV=lHkySv!$YMi!FHX`=HM0L)Rj= zAO95Bt?viy=gh;#Bg9Q{x&m27gQc+D{<=l`FmF+<_W@85$7y+#F+9HVgQ@Hmp9T`v z)?DwxLi6pfY`vd*FP_3CS|Xd??O*$O>`+ZqyfA#fyaFw2jXC0sa<Yel9ByA0i6qAJMHHH`6sUL?mHK8u8t&pdO>J$#GD@%XTR=)imh|?|`F& zn-iT0>a`&G6cS>ouL)d(k-LB>y>-{6J?B0&5yNUTv&{N9p2WARToDx(%|*f}GKd!5 z(hHsiG+ZatRnKH~$oWL%-RIcJGJ}G7g(czc4F0B?L)xSXI5_?&ZtkgMZ8bsJx1T<> zp4Pk_U6~2T0|U^bgvov4igFv|a@1=A?Ie0l&Odz>p=E48WW*KfNk31)4oc3*_g`MW zge#ev(xlw&v9(6L_y#`0N_O)c+@A3{alLK52Jlqdt{SfXY8sC1-mmE6MZ{oav;cG| zu;OBaJikopdy1pkmT#fLVZtM;lP&n0EQp4baN5Xa@JZ=Z{&*RnMgp%1y$1I59GH5g z6)u(6&>DpaIX40ZZ_mKzDyWjGy8SciC9${vFXa8P_w0n~)j$q7PJ@x-Lg+Byi{sLJ zJRlST7E_SOQ!kx42C%79lxWg?Gf(zkz0)l%i9Q>k!`@*k&p?BksKX|fl`Cz{SeA{7 zKmc&;ma$=^5!Oeoc~RT>Ml~vVF96r#6Y+eUeS*o(pKE_LN-!1xaZeUCe!xNcT0ekJ z*sI zvtE&8|KQ{GUW=V~`@Pc#UH$K~uD*D@@3CB_>dY3;tNh@seKE_mvCn^Vs@1Ud#rgPP zC#BnPUHYg||LHRgmwhd*Mh3ZkjhyUKq_l^DCc3hE62dVWX^MELfI*1?zPDc0O}*|_ zAF8{R*em>@*4k4IF!K@1kCjsMe{Vzc}0-f@ov>nS+Vki%-b8q>IU4BrPQIfo>=^?F9Y?>@6#unCftx|w?atz$g^X;STzw~pWk3)NUDBE8;hOwuJOE~-59Zc6 zWG3J>*>=L`-zM1GAN+iBLHwchsBVEg(}zVx3$1F=WX1Ms!z-23c6jsP2tvXlv3Y}z zW@v}MW!a7)_$iQ*<|Sq`5RNW(ef}Z4hkZ<=Nx0%oxHvccQUe;+2>BGlg0R(U&$gmy zO>w^)nJpOYM*)C}-v0IksRTiD3SX;GxqkfvJFdjDUD=(TK*opn6*l8V2_70&sT|hv zFi`R|CuhOWR^LsxMqbn)E2}ph1-N}@H$2w^`2ym$Z-sMES3OvDR-rO*66 zhPp^4Gx>v_eJ3Do9auMvv}bB6iu?W?8}}m9`+O^UZ-b+bI{E}T<|R0Hin-0v%*Qgq z)DmNZ2W^#g=JQVM_^q_`OfiDyhD*>jfdx4H-vT~ig8r_H-qZ%v3%(C6UiJW@s|PRS za|r3V?Y!AWAASHxFNtl-8Y&93r}!G{|H7$1 z`XRTzo~tDo&QdFbBYtnHLJvq;NcAJCA64}9Yu=v-h#JU?M^8fy1Fwul9ibni^jm}N>2+m4G7Qko1YX)|E#~LuP)S) z>ywH9{NOY!93!3~6L^VgLZ(2rTu>uHxshma%SbVFtBw=p_CBGu%lR#Xj)Qz|!2$yE z^b!vyljcd0T4_M-)5M)X8Sw*)-pryM(F}!|4%RwUeRWt7UWHBA6>30_Vz_0Ma&^u8 z%H`_4YmZgbo>b3>`ha05Iv6=Oe!T8gZ|syljxP)3jumrOLN709#MCfzwf9CA~~_IMq8ny&w0ODQsCzvs29EDKJT1+obH-5 z+Xh4doDF<$?K28i;CM(Pm{%>I*n!DPIwCKajckU9`b-{0H};HMyNTA%HWe4%8r3nm zfH7a%xo5ltSSvp@H1ycTng$7Zoa&$LO?sAhg_JpRCmGFwB0DYXZ?<>-XsD zU58n)x`D)S+);pb$4LLyWSQ^6zEQ6CFQhITCFD!bql>mhhKo5zgABXzIq)N`xopbb z=98j<=ob&&MbRtHi5fGOb51%b7g$dH@;&5xT&0+nX#kqA!INV$rrtM&?+;>B z1VYPnxyc@i^dR|UKQ+M4coP6&a=AC%7~|?g4>@-}y)VLZ!%!=zPsn;re-AaPxL43Q z;Yojzum5Qw*BXNX5kU_}>_3!&goK}I)_Mi$dTrtKwu}n`tIFo|=Kb3B8R5FE>tdOU zj5Uh%D0!t;lT9Gs&1qbs>1rwM_rRlv*2Bt2a-@2QGto#yrd`3D>%ro>|INWsV_Si| z3G`LeeP-YFFKoR>dh;xg9a%+WM{J~Jj^Q{an(00qyV-%j)burqF)8^F{0Sb^gbv%lyo6iQB8+{!t$iiH;cc)@ZR#%xln z5IXsl8?_^z($}}@0Wk*DYn+G|eQv(!TR9aCb>Pu6zPEqQhh?bOZIjKCM6Z|1Zo9xI zC0kP5b9l#5_tNzY_6d)zEl&8k{A3ZPDa~R0DtBD|BpV`b=&VU8K zE|;7K;(+O-CxF=DyZ~rDW%+QqqnGI6#$ndF&XSpA!@}X%hoRMSBrg;f{k`kPSm8mF z>Dd+VfPd^yq7_o)U9udJcK17oAjw$fvc>o2CSTxmwa`CP-=}@IwYmWqSTc*B&KON& z_SWcZ@D3yu&{~P8@KYQ2QzYJL^4<>3cfR)C+oJ`xS#s!Hf}cK=zd&~71lVkC3l$MZ zVS2ksc;&Tzmf=@?StE-2qI*0z+ievtXaA|~Bq9Z{-{Z)bc72Co#+scGAER+a<=tgu0dww?l5leXt3>4 z+UIu54Bug#4Bu;gy|-7^)!O|hN5|EZ5X^#)P-ATBwt`RW`<*7;l^*@t`0n!<77#O5 zd`_2$)8=m>&$ZbOwG~5<_QP?R#=$JUH{SjQ8$q{R@J|5BELdE&e!9B{r4PQU;k)K% zAQ7KZTLRDNy*F!MH~5*@4@-$&e|LJUTxkPB>PtUWI8|fGY2AYgMmJj^k43woG-r0)m1piz9rl#wD}M9WPe30KQa=Vrt-q=`x{cM z`bt52I7S;W9j~CKU!OjSwG5*W=G(RP3&A1!BawPZ`hH5i1O(BnrRMM~mEUnc<_+JF zz&kh$b_-DckS@^^U!mxz#-e@6Fxboe3R+tL4KA0JQlu#P_8Im0GksxNbwxBr+O@Te zxQG&6sh;kH*OaXRCKQOz%P9__*G849U-H?To-DpJSMw2uyw3n|ChCK{eQhIHy#Rwb zcqsNyNkumQdajWCtQJqE&z7IxRVU@gR5m}y#%8EsX72qUX>Ec}q4Jc3C$GZZEr4>VGy;1$gWhQAu#YGk=Ku2xltKOs+WC^>q;Q5J=rm98@nX;5}qHt_HWf7$e z2>L8xa8a?z#d>R^rb;`lDA&L&L7YOfJ|I;X87k6(tTc5NNQkS~=(>{wFZ3lo{A-GG zlM6$QV}8Vvmk?1xtb^D@>0*{_An7TJBY%^D;xu^szHlC2k&u8dh7MF-`?hp?&|M7}wiATZJUsF5{}1HH973+ZIv zO$_pTj+=NWG?xhjWmVetq7ZZ$^`J!p7i~|J(gBn(DTb%EBWgdy^#K^Ug-lVch{ve;tbrv-X4pr7d5c-N^IXgj0Y9K;q-egL(U7QPTO%ug5P2MM=w$tCc+xI@ zAh$*)8`eDA=K$0X^C2%wN^n4aj=1>vh2r#=gqu=nzh;<<-B(i(7Xz6En$w{h*TBN= zUr!O7)JQ~td}$I#3uwLGMuYCC0z6%Z0#}faBEk`2$$JFWOHUH!hhdJshd?CE7v^>7 zSnbQOq$JnNoH+$;NK1si^Q{~SBZq}1o&3<{- z8-fOp!$|z6Qq+tk>)^TL6Bz_cjvlzBhj;LG#NfoE+D-O=8~m5IR~}NaM0^{IR7*LN zbQcUcFi?I{5=tQ4?XN812L}-5*6)GfRBJ*>47?;+5_}1~=0f5@Y4p$8#%}*omm>~l=c<1buTb#s8EFiAF?dA8UNY_8Lev`1cysw@7 zb%GhGK2H51BTWV?zI1UG!WIHZPRAetB2}M}U!FjdY{`qhcH`)@cGrBy`1e%(>#6=^ zSU^TB-$hh8_Z^1b3;?qUP_#Ir|FAd1-n(x9q+z$xzvxM?v%PYx!`3f54b+Rs&`{@u zo%ycbJqM%pjf1S$)YN!`k(J~BDWmo8*Gs`dfZ%tm4~4{s1j}l~;pNI5yTsHnJ~2S( zhz_=o?h)YMS2ywk5EGR%OZ9%7w*UO+uPjCeXe%^2?|k)aww~pfE`Kjdv)$sT0Wd+C z0PRmDxDr?vuS?|3z|FRFZpU{%cV~FXfPxovHRAPu{?5N%`>)6^%{4e4-}MGMSRXWw z6e?jz@pwo(45rcT&wsd)%96zTrU;J6(D;bHfxn?+j`~% z&z-<6p03w1sam(><$$&7r4aXqme5E zry}v=$B);5IERCS!y4e(39OrZc>cy3(8VsM0>EvQuU_dzk_zh2G}=xtN+T*!#LvNjt&~kby)Q|vS-fgFH=Et8E!vveTG5)G+`K6cq}0pe=Dl2@8kvy z)33Z|;z3jivWRDncxyt{w#7Kctk0h!!L}htzX&T|kl^uaEel}2l#b#Tdk6`DfkP(q z`#=A{hqo72Ra6>w`kr&k2wWfHmWPL<$~qwQe)x4Gxu|g6iaH?zqYh*- zBMJX#)L;McC&QicS<1U@z5o0vD7bsvIUMT~&d*7>ko_hblnf5&`QH2K zdt`2*$NBi{u0Oupo-Y*8v*f5M5BVu|gGiaLl@OquWBBQ|f?0co@ww(o?3duI=g64- z@D~&+OV)i-hOAFcK zk}4SJqT0dUWkPO8J*V?qeSCm2GL~Ng+$Ydq&xtT47Qu50&?GG*yEI;Tgb+|SzQjjj$VmG0aWg~*Qej!nXN*=JXqNn>E}wd-A$9%&xhF&FS_xi$bJp&aZ|!T88E**HsKHjXoA;f+N-~A!Xdq4jv|SOs9|Y%?awzALxTCc}GK!tYvAs(cMU;RuXzz`pOvW9iI3 zzvw2Wx_Z@-_Zg*`HbYQEL@rg&g;?Mfpezja|HbNp6GZ}#+9G}G0gs^e!26~J9&Ck??7td9AptB3wQ)O>@)u$OMKukwHkOmsv zT+#gS_6rCFR+AAXJT+fu5sYn9Z$Jh$_C^I6^I;Z_#g zQr0seq1W)A#^AW+R*GGBQrH<^IcWM`^WC=PGpg7MVf>cq{lhxL=fl8i%FEw4j{nFL z_J83ME=MHKRrMn+Z0RTm>hHIJ%n%Jr$yg={ z(uLsA&8ntn);PkkC;E4qpy~%x;#(HD!Y#f4#%5e52R|vPmb>|A@OK&^1e4f+_(QI5 zyvbS4Q0>#s65WmRu)vH)UG{GrV*mVzkR2M9pUJ?(H;eZpS$-EU{`R-sNS)*5(y!kI zNgjMt%#4z+8@8tTYb*Gl?_xE>2@?yC)eAj!uPu5=E^*Q!4*bni_^|`uX|SqcH9H-{ zn)Rr4e~8{kib00-tJ94O{dfGl%$kK{ww>J~v<>fGKw-gRK*Y!UYYYi)Nxd-uxe zy3cq|!(dz;kn>L{VT@*yPvfGAE0C3AHx5Ew7r&U)(`vqSy|SEkaMjDxon{$n@#L)Y z_ThB#CFlUcY>tMuP1dt6S03ke=Q2@I<3;K*fIz+5E~qkaAlp&tzjpqA4U&JvbL2*t zl2#@15nfuhrB;`e|FgU&FVyuAbsO+DBB83x-Hb|urkTalGj?~`LEqR5-)#Ka65`r}%W z$uMtS*Mn^)rGZd_u8UTKg>w;12~ZuQyBulXzCli#(B)uZl?|r&L(nnFCZJe-_R>$S z)r&i7^`}AeFE$t`&}ZH>K6mMjeuFm^6`Omsby0yx^HeP!=XSFYt<{;D9prWgL{Z}3 zG8TEMLQ5k;`Lm3OHcOADq6%Q|Z^%L>xGNopvyw7>FXi`}E*R^+YwV-5-HarGGC@Hn zW->c;jINXHa@IkpII(7#K74XU^)qgOXb3pvn=3^YRJpcogO-}1u-HLdOcGD9(jcEC zl;F3FvJlxFLI4#^v73BM-OeW&H_2-R$*n1#kb#8xFh)+UqdoiM?Ib+0Kyx^yBK1Og zQg1FPo4rM9tPXBsQP7U%+uHQiJ!6Jd?+8r&3l(2qorTD-dR+bU)dsF*yGG ze>%^9*d1dF|16aDqBudZFbM#$l;EA{{4|}8H(_Wab%WsP~cf;K3b!RNjUf^~oWB=I1^TDvYbM?BnZt{j3f&v%~vS-B*#v*&-7haynFgv)0Q|I${zD2*mZ~z9L2aHDxxC z7Sj6{x*Jjcg z2V*Sfw;lxprzKAIje^*R&p~PCh16maYx*nR1ZomO^5x}a63ne%!$vH&_aa&?rs*YG zRq9WPxL#ArBt(xW<>!f?m^zHgaY`D4b!{M_EZ#^M^=e&hSGBV~_iP*8F``|~r_D~k z=Hv!4qfX2|tIh(jZP65V>$IYUij^%h3EtMz>ni|a+_ImVzym1MssPz$3PMh6m&RHC zMuW}j4C&uovsI|@8ZZ;XZtfEyto9U)%tq?|heZ0eXSvC*n&JZx&^&60=D_owdA4n^ zl}4Lj;}D<2A#$yTW76c4JwE)KYr^9n-wN_%HY0nk^Stcmozq7tVPrN7bsKVkyqeze zw&kjd{LZAM0)9ErN+!TKo>}geG!FwA%6zA-H=vNATnSG6yOrX?nX}umQWajc7QG7R z9gafP!koKUiQnvm3UZz;&o^27&kuV)!HyIH*S?!lNvRjqfGd*2E& z?Ur20YG2qm+q)5e+`w;}Yh+e1QZ4l=D&(XSR3*-;=dSYM!-qT-(0(kdnS9-RO=jCV za6CcR8B&|jiAL977`R^{Y`KUs)d`C%^Z^nxPwVC#*2AK4pg!0N{@OH1)72)$5ihUA zdbjCMJnDMeT=J43VF(@>sz`CH6AoF8ie8*^e0l2kj9i` z;Qii4YtP4hJbFl@enPVl_}0v={> zZ=d74r3OTj$p9g+1kZhv6^F3vv+eG7*t+h#3jw=nNNO-Kud6h0ERR~rpP!3|y}-RY z$^Y~z_+|5cW|&9@hGk4+rCKmm?>FIQGgc9ACAoB!s1lkDhs`z2 zo89r)ALjvQaPRuC+8YCT52u5T%6YXw#Qi*A<37oK$9J69mv$5bJ;A}pr(tTjOgr7; zY#+CAQ!9gz;;gGpW^Lzb4VZc#)yy$px)eZ4ziSY@zPKtWd#xe%2~htYHWw_Nvkq9p zeK-$+no#Q5NdE4@0SiZt|5oB_>Ix(m3_?w-IS9CQWN~hqFi$OBv60o7H9o)Ykce!Y z?+iddl@t?`uk&V9Tg{XK^*~0yHl^Q(sWaqn%FWN8AB}0&TA3^8!OehS12Uu+_}uxN zD=HP-cjLRBW)9e8*7QCdXxd0|iDfd-2UYc7S|vg`2>v~)fAz{=h9SC?)$r`iGYSIf zv(*?k?qw|AvpDxse|m0**9O6dbDi++_A1-E8s}0YwTzTMUbC=Wxd7homL}@UX%}%( zu~&1Z#F7geQ%tOaf(~n+A@jv*#b4oG9oT8U=PN&~)vbc!LUG}s|AfA=+FJo!gj_dQ!LR8MNbN%`Kx~ZdC%*fft%)mv4k|hU**`# zH(+Iw$wX0i!(qQZyIT&g9!fHM#P6q=$(#=PYubkD!K}ncKBATGBCL|)MQRr2sR5)&@Ytq{s z=r!pKQ_B$++fO_kU2Qs8Xvz}_I%e6;mUvBd-bP7ukz^%zZ3gmiwuFu<@F%|iu5sS1 zz<<&V)Uy&n#i9SGfv{E)5Q44=07X%{`Dl=|D%Y6IC&6;P+s0+H`tCt}_s6VP&PTSD z4(k@&c#iVhV?~rHS4Gyd6Ft{Fp4pq;n*(|SDPBe4p`kHlW`i+4-nQrFyn9}2-AY=Q zcM3oqG{oOK8id}V8sET_T!pNfY)-?|fOxMQ0tcc!#e+Q2+eZWzUZksR4s5Rj>RZv& zq@-I%Z`nFjL(VmN5*cQVJG$g5eeT|Id+wJ2IezV4ZC?`rAx)5jU(V9_k!=B;~{lz&(2Dmp}Wh4%h)-eNIDln_niu z{7@+H-3OesJ}raZAhi7_6nESlwZ9n-fuo7uds6NM+b-3_*nKk2k0f8iNI(k`UyBZ` zXjI$Iv7UjX<~Z_@cBYoHDr8&Bcm}&9U0s13 zfE#d*u#!J54!?2xh&$?w?t=>_aKN~a-f5~8%XCdMH}$*c zYYm%iSMcSpl)rqwoHb3ljMVA|35Xxu6juFDH!s&aGr+5Z@q|L3T6 zfAr5nZdV?|lv7l-YTh(Vpmi6`7xo{c*DkQm2m@jYCUupMZs0+pe}Tpkt{midSSycHxfgr7B{Jfx%m%-@Yh$dX@R=0x33|w; zKpZ&Qd*TX=_gzoY+4GBdowuv|u3YqsJZOLCU;zT{eQ%<9M&7XQo4}lJeU@4{4FW;o z&cmcz(6g&}%$o8Yf9XzF&|jS}H8ghA93NG0J;%e_oFvZ(4~?0GC`7{s2KP3e5#AEK zVju*{nm`YqMcuSKoX-ITsp~TJM<5DjUW}7Y_44j=Kca_p7e?L@1G!*qRW(m>)+`U@-FaT0wBEtV+%1k@PHRsXm0qP3LLrVS6W4{2FPl7~{LN4p2%`A` zyhr&+aS14MRo~JvPO(KYDaSqzhkrI#*=D!me z)`b&JhTZuP(0!wC{JkiaQ}uLEWw^pg_HRVUfjSzBh=yA<8LbC7Y0c5+-)M+MA-@|p z0ZwBe?F4TPGl7rI%5JSN^Q6G(Z-Wt>HM9CmkDVel3e>r+;2ppJ%6@B7-aJ|(j)#$xZO(M6qQD}5H8r;haZ=6 z54?0Dy>&|@Zw-?cKhs}ZkAJ>PRwFdTB%GA;={JL;e++v&FDzVnvtth4@5N32v4f<6 zWRB`<)!%g8|8VmbfQ06HI(Ex%_2eH5!e150IObDi!~O0Y0-rmS_k*E7Nc$D^8&SzW zKa%$xutqp2IKStP{_!n8SDFqZr3-yDHr_XD-jV*JNB)Lb-5{nm=cJ=e1%!@e(_hRR&`bjW zqsMS{f)@=)!W#dYYn4^6(RQZM0jH$_=%oQj*LCF<<({R_sAPJhbG~E@g~R0#dD%H- zaT<2n(=PD>L$0p)5RwQ;+DLoq7Kz>4bc*Ak^oUJ`z9EctK}}#OGY}^t!aFC2!WD8qq*Byay+XIV6T{*7=GtcAd;I6AxN>1mS$bUE_>L9+u zalB>})txq7<()HmVYgnSmLH3FZBaE!`ydn2`9U3cbZ@rSogb0-zbEv`EFUo*>;u@Ip^xJi zEe|)jIHVVhLAva4da^kvQx$xsS3CN+Fpbq?>E;4LpDY z;`A(T-RTaq?ZP>O#L)Y@8{<5bZSA{3TFADO??RKUAl>D5)mCO$E0cVtz}{J!_w@w` z_6d?qOin0lb|fLAPNS+m=}l3IUR75`%I1IH)zLu+$nCv9sH61$a5N>G{20~h0M3W% zll6ggouZ^&v=_kECTl`PZ@uG)d;c}|_8K}ky@EBXJFTwIrWH-mzj+Z7w+OO-Ahbk~ zHU1h1jo8?k04LPf*Bi?spy5_0>K1rwb!Tx zN*!AEh39sH))_+P*j4wNZ%8b4^fsJ(NZX)5nlfMOWMjg!2p}Z_5dXOR#eRO1%}8Q= z2R_;VM|yaRP6$T*`kIC3(NqXg;SHs_4cM%0VQfYL(VsQX!Tm?GbB|^<+bDj@lpD^t zmB8ytVo3vjf^?2#d`-Y^*(+1l3IG6~9q+0r^lBLNp?25%ldLHxH}OUy?c>viV3Nv@ zi-UAi5BFbNUR6tw_==E_Zt>_C7ky!^7pYQX)O{-$Xol01%9N=J*6mEus0vcL#rwEe zk&ENyyzC>Ff6;?eIYPg>eDhR@&HPH2ax*Kf=ya7Xw0#bcsB{%w~rue{+|0%xL*0W zK*mLVf#{);UAShOy@V2WltbqlsJ19i+ym^}G}fED{aNhTfU;dJXQ@G&gBG)v5|z3P zz5f56;sB{;>35huC&?Jy@g8bz#8?-EWih-n`_B50Bg*S}H%PEecXH38P`jmZjaBRI z)7?c|hV`1Yi^UcK3D(!u&9@HbWA&jma|1w8?UHubmf>F|r(27Q#uSC*tOW`n_TmTo ziB6Mw9P61R6I*S`LgoCmQy_ZKkh;fmvr{GpGIY`q5f#n%=?HdkFQxkYJe#T0 zm9_`^pwumn8jHz9s{RxiKv1Bh57-A*iMD-S%*@P%wRB23dDX3zU$g|alf2sO{GCUY zK`lkvrH#O#w$lq&(@%3WvKdeAG)iram>5q2<-jLL@+(gV1>??+E`rK7e`XXEa;x|E zDUDoaaC>WW*v`B*@VyF3Q`9qX_#OXc#0S ziDDAy)ITq%x~h4#s5_+#f3l=xUlGhjREIIU7bB1=X}%Bl*(cH(wg3>sm|o8 zx=AtZS~+)J|0z<>2tM~?$+)lmd0-Uf;hmNG21Zcog+n-0rzb)|u%@w8uVrRG<13^f zlbDiHIvhPi+p^^;#Wy5T+?09Jp@!(eA89#R71fbh2C>)@gyatFFucdl?S zI0g6$+KUi^h(uPiPxKF-$+!2jAEukkqT~-8hZj-6VXFs`u^V?lmY@~awyfhrXPDG% zF_;Da%z;wQUZpIx-?dBp#GBB_+x9g(mwPOytw6EM;=MN`NO-==tZtlIA!BahAOiqm zQNL39uP&fa7jkt&Q5>7K`9AMBm^#y!MVwLhvm>v)re^)=FYnZN8Vw~>Tw40J+r#wP z`|l%?0G>6?JQDlS1trUZ^T0melRD6Xe&l{S2OLCK)<+a^-*F0;>v-{6!#GdY%eK;al*^=z zcb|Z^@x{Ox=GQzcX^*?z6DCQJWtogese(;|w;$h8x8&7T0cqU$4qA(p;8jOdpWOL3 zRqMduIhrWG+vt+FuUaV*&1)E)3t=VA!$A!88)d=#_c~p`5|MJCk{=dNfjI1N)JTr7A#iJw-`lD zp9NhJU7P`Rrdiy#Arlzh#Koco1J^*Mz3o_Af~5jzLSd-hX|VGqSg$mKipIC(#qEyusZN6+u~Czx7|cJM1xu*2E0NAUIiQl1DoMdTwUaI-P2H z>*K5Rh0Kb^+Z@L6k~XQ`kqSpMIIci-rtPTO}_Xm(#8pM+@Pf4gVn%m7rDDH3Qk68!^1G-1(<^lvf&_iMr!RyR_Z zVw2x^$39Ipu$E2JYBl)mcsZViDrt*pIMwHioGC#|q1R$yM@b2Ds_eUr_D5}W*h9wyc^9Qi#Y_U3G=j;7FNy0^ z3*X=y>ay)XWH#R2%1ye)#Zjx61&{dayB(Pnsud@e=rm3AR^~^!45JfbMtS{gOfF2$ zXf-HrH#Xw7Lcp1yIx)3;+Ei8+b)SZvE^6CtaTtQg2jCowZto@ysIV1K8hVY*8|UGJ zzxY}PobJraoFB}BEI>{JoBD1_3#pfpDAGDM>9!a}1Z>8itJ&}?t`|L0n+3P#jc0>` zf_h$H!>fV4+@T-Cv}~+z&aIUzlud@w`O};9>B??x(@q<<-t9Cx%uQup?d}0-j0`XB zuC{wYY_96Lv_PzeMsLrp*$3R=tdz5z32lF+xc+-at>ciNT|cEA9qC=?+(BnPzou_Y zuhw#-c9&mcO@^7W47Z2fQFU|=?D6*1xJiFVhcBU&v&axj?$gCy@E$~MC3tA7U~%kn zf|y1P-X)K&W<4by@rd+l$sqdbay^!LH@uXDDUe}`VB~W@jgFml5p+c~MTSAZQ3(c3 zXy}_G49>B;DGCKVMO?5CsX16o>GOg_k#cIeIiy^OQqq;nSG~y3hre#TFg3VkzA)AG zz4c)mcu;(^$e8I+@pwKjb~7vUWWiS0CD*v)z=8=(*ptJ~{(7;&@*if!AYo9{`j^ zSa~^VAoiqiCTZu=|2-zO-}_p7(HSaB^-h73VG+`*kB5JRU%WU;;FUiuxahrUz2gl| z7Itpv1sQnu-$D{uGbEdBChbY@Ig#&HF$JSX3%qVHxm)!^QuI!)%raw;c!DtwVSdvl8I!ZY`NMFB)h^cEK=Ge(g#YkRfxT8A z4@batHeG#@ukrAGgS1&f`YKFq?KJ* z-LU_I_&>rwbg&qH?v5*BbkP3KA;CRAu1;cWXKS6qZx8Q8k}vOH0Dute4?or=~|-hlLuo0{|V7kNsI%oD`m z1?FyTb(zg^Kc3RjeB^_M2Uz!UcrkPJs(A6uU0b*~B2e6zVW7%JSEZ@*_Gp=J5e)tR z$KHE}HPx-%!YYcQA}WFcQY?T{rAr49=^#i;=$%jkQl$n&@sTbaLX%!XCm=N-3etN* zs4Bge5K2PGx7hpa>+JWuzR$PMpYPYl53UOWD|2PeHRpYgdyH{Ydkz2OHklX@P(xZrGWJ zjbeGyc4c+`&{TO1nH}e`KBX9X^=^!Z38%*T?~i<$`PiQxkRqBHm{xwWtXX)uJ3%54 zJn*R;6IVdO*};U_l!UhKN_wfAvPe%D$Gt`iHyGyyMy(Lds`K=O)aq9K#CR2xj+<6j z9TE5Fd*PhztUkKF_|3TZJdFd3l~{JciSwFIdz2R;o4~1-u{h!d>uqMhWXdq^37NmN z44N!X;7-5xYg77j%CWHWD~cfEw7>f>GK&Jj@eM@=I|$}A^SiW2YeT=(Iu0+LNL9Zp zpmgzC9sUkEqko&$zWDnNuA_K+Tec;zV8u0E!W;QwY=ED7cO+Y_C(t(WT$yQ3K62gx zby625cGeJbB(sliP~I9a?AtGDi*MjDFK24xyzc*KBg0ZNTFb;|~q*%4~m*9t1H5i8wB*15CrAwW1Hx zu<|L-fo8!$Yw2~XnTw3BsFCwtE3w}ReoH@tDbOxFE@jN+oOyrFrUEbDm8j6NO=Ux5 zkSIY^kGS;9IHsYi+-3vEPaz9bCd_hcH5xz{r3hpsR88g9rL+G6DVuH&vbzckeVB>x zy4q5uG`R556tj(MvGq2X5B3Dbdq*U-_zEc=y`W4QKr zXf-Q7e?NR6TD5`71+$i8YcZ5no-XP2qkt8*K5i|IskaL_0TuhYa370vI`>gq@VZ37 zq#qx4zn1BHns4fxBLX`iC#KdAmv0(HwHbId+a3(-d{MqJ#GY}tr_}aHiI;ZfP9_gL zbXtkj;PHh+Z#$uuD3mF^nK9iMcfkyc_d4^AG7!>H9seB~9mbV@>sC&{uy5(|9aare zE5Oq4%g7F9Njiv+djmv7PBX^pw>uz2TFE7 zs^L_mLt`p>jj+nWrbnj9q^!8sd|=9b$2Em&HHCyXsPY-%cx+gaMK!v{;{r$6F)IIZ zq}ub`>N`G3_7f^=l+lvGr8QZeGiD}xr1WpKkhO(zS(_tjIho!P?-~!jjd9Rs(UVN8 zDR%B&9Ll?Ro1ZXtd3>Zd%&SsXYyxg`g*k~h;zgb7G|^O_u%S*7;Qx5vJ84dIvt?v2 zb!fnaH`STjrgF^zy}t?8>^Z0`$0+*3I%@5G>c8*gA8GvNVM;h@89c~CEi^DeV=UU0 zeoPh5w0e8Qja}YCg+ybR5^UIHQk1N+y(`;{igbQAzf4_altg)&p>XA|S1p4&BWJ zAZIdY`|4>>cIIsKIiv&U%)=RTLFsy~hN(nK%dsa|0i78sv@?KAEjqjc;Ra;&jTX8; z7D<|T?NwQ<51^tiHQA(`d6J|oD_K$&#eZ5!~o|E)njRdqyqgiK1ua;_xSiFLPXG%N8ZabW=M-j66>FCCVT@gonan8W%(b(K zbN&Q!(-OM3>W2lrKFE0K&C4`({`a`Em)Qo&is%)`?3{kPG)~g;x&WkshKh1sv?Bi{Dfpk-@55Vyr!50aXsY$1iqo84ac4qblOJTwC#Z> zx)UQoJ)SANpukNr|BN=KxMyayDs-z^a_Dj{*>$48?I7U5NDWMo5GDN)6ISLA4)F8p zLTJgxJdOKgs)l6r%M0_!?KL z96O_!MN%ZfRNjZtDJFfu)h<`b1FY}3A5-;t9w~y^42HuxGHFsEz=Vp2*9cj4QtLd; z#iS859)=<(&eHQ&CDQDA`fEHcW`8LBZ6&|q*l@Z5pX*fbh8CDV62qx0TK#e<0*eTw z7nQJ`fpy|-CGD1;M{84r?HNZ4wMmz$t_a$ca!^m<*p1YjB91T@I3S5-dCp0N!#2Gr z!5oX_7rK`+ZVo*xEtQA;cLvjKTfzqPtOH(4=f&a86Pa} zr(;+V3>tXd*n2oN>z>;go9Ju;`oicA;IT$MP^6F=#bUrOAMP{VEIAIyRdkiJugNh| zSm5a>6t`5F24Z}rmPR$@|1;yWN}J%mA>&;^LcN{MUa~=M3TU7EC3`8R8AbfnJvF9mt&%PXe>%Xk=~IIvX*U`1_V^Ia6opa2o_WNn|m9gIpAwj9ad%YWSAP=ec%> z#V$;&A^0X(3V*LkYRES3HCBCJNHXfV>`1A4ie-8OXOiK<_~N>cuRrUW)Mi=u-eJU8 zuQ|I~V%0T)=9V}Z06AE{W9*e#|8-;dAkk=^Ij*k)sF0wM35dA3+mPmK=~q|u^Z?Xq zAtbs0B>%QODklm#kyo&Dpwy?XJUKrx>S&p7{(Xa@7JSJ+{^jw}fu zhI0;_7iHAGj24>kL&zdWLBL{CbpUFUI`Eev|ES*tsRdiwGDz|6@Krs*GIexv+Cqj; zTF#q;CdrasH)wyxq0bZ2N z_C+F$Tc$4iG994Lr-*r%A^r1gbsP2@zWq!oZ%MWZ5Y6@M0LoDy1%xS*3pi7yYnhi5 zfLaI+YXGWs#>=pwSspaXIso1NK9C0V=X*O(B1WBJHp8Z&u)2wWr4)8o);G%*5W7TQ zX8hjBB9W{A@@lDeb^iJ+N=v`Y5bCXgQR-{S#X!qv0uWnB0G$N6!OyISI!}T#Ia9kf zdO(?eNE++{@r$d)RyBj~g*in|N1vt2a#f;c`g0*Qktg!VhIe@dvFNqIk;Afytp*|s z_x532w5%$ff8M11_0@5M4&|gWOc)=^$EMM1cZ8-WX~$DU=2S%ck16{v-*W3(%b1N8 z)>1|gY&@OyHJX!;`hg;h+vYRgf0imWFNi|N*^=rSp51p}aGQV{IGl;4-`H}bx}tUL zJL6ELcOe_Vg9pL;FFJ=2bP2_}iD>7tThu}I?#t6vfz#k2csGO>qZyr17xFvm!{v~4{ztb5;i64Cdy-JMYFlUk?Zqgm^Ob>bEBL0e}x9Cffj zSZ3j8g_q^RZ9ThJ*`>=9;zeCj)E_Vg<*7jeHrZw{OA1ForD0)@!(5%IO z5m)~3tYM%dvGy0eJ5>0Rn3U8MI1F>GMpazZ8cJ*wd|j{KXo9LsBsZ%wia_u8rHa)F zikjL~6+?!yS|YeoKdxj*e)!=y*P%9hQfCO;pK6F!_;7Xq@ec_}e^#JH@#N(cY}gm=Np#~gL&X$tI^_>f>~b}G3g~G z&g;J3lM<4g0=6m2)PzFa`mkl>Z|Fx}zo;6S)kq?a1D>2AW}?3W#QhNF-B{<$3{2RZ z{7RoMb<1#->xtA+k=ktl0_XA})GygAGl$%5q?|vhueQs3`T5ymd4*Hbw=DV2;1KpF zYSp(E!g|@S-0n>hAs;^we{kPAgzEVq`&kig)2wa+(y{cjaCJgSb{iwX2f<*6Mx1o& ziLFxbT(_z^S&6MnEiDAParQVx*_@cK#+!bzkz2D|YeK zd;VgvR`Avawz=nWOqWIC*Abh8JurGbvFuz=6E+h;2tafqQs#vlv-m`sHfIkkx~;BV zp+1H=02D?Gjkurps#BZ!gTGwcsPy-#2^qfMYnR!WJ-Y<%_cQFrOy8;bBb^|Ig5Z5#CYj*=PCkW1pfjHzI?edgl(NE`tcIL!1wRwDmQbPh`$pwR;Xg7Qm>mJ ztsHVI#cSQyg$0Ny9m&lr{?2u4>6)L*Qk{z&kPe!Y0i~m7V)I~pm7;5dm2o3|SKv0> zX1)u01(=!3p)X~MX-WiIjDUl zM#PaLu~rN*IS|jTEx!!1F{>pJkX{Vg@xl7 zAPW$6sXe(%%v-|UQ}_TJUDOE|x0(%R07%KPYvvT1gloKzHVY}Q0x}I>#~1Rv>5ayY z()q`%BpLzaTK);<^<$!V3(FJxisP+Fu~PD1bA~Rm19pkON}+)x_D9p!<9oH^m_!uj za~CvJJ34(Bh-5k<}T z8!Q-+NxZKZ-Yt}4s!aDz4<)Zqe-M0eJ_3{q1-SEZ&W@--c&(NczQ*2`x^tM&&219{ z%GZLm`aiX)vWoLma2t-NS-=2XeE^E%aCFCKYtMBEF62OVhYEz$ufP25_B1NMZ)&v1 z7c%?E!wI)kPx;gyC|YP0_W_Z}J(Kda#NwIBFcNVe)T0Sh`ISuX;=*n{))B_$d#;Ve zG4;iUozTPjyW$Txx>Vx&miimF5&@iv6Alh3XJM2X2A4S;K8R*_RO<;B>x`@xJi5y0 zRv-+W5XQa)Du{`db2Bm5(j|KE(?YhOZ`P7O+r#&o^rtpL0qlQ&%mBgH9!irGo2Od8 zw&_0EO~;LJhBi9~CEz<>2YDHoIbXOej<)Gs;_bUxnu=ExVjU9>Mljwglm`)#c7+CZ z4n-bqm5949OdefPoE@0CMZHT{Tq`fF>_p+cirIKbQh~=s1&GfrB#yZ%4xj3+;PsqY zkXeA^Iz3OkFE!(Nw`Ji-5r8C*SPu%>YElCm2l`Z13Y%}f#!27NYpC(OCIZ*(&OYm| z7D%3M2o+A&lQlpQy)?U_D};t=Jp&`qML0Z5gBo31ZbO|xC4jAH;>E5I4WoS~MvKtH zAmFWE2>f=cisMga?_~2vFG0eB0NV4{cyr-VU*0S!wQsZV z=x$v|@m=B<-wfa!8vC-j3P7LLV+c_K94Ja>cjBr*Hw|Vlo>B-B6oCjd`iF}eITk?4 zho*)9ynkJH4fbL#OCUtvBwM)qA1fZ&qB*Oaag?XaHJyGVC*X0{g>xuAx!oO~f0hR2 z^6Bp2dq;NLI!B&UWG*64v`?d^3w6tVFcV7GA%4`4q-q&0B)Pn4FFXDbNT;k}kr6Uj z@%gxUonPFG2h62c{4i3zmV!E~VQaI|dIZ(8n0Hr>0=1tKlr4_y-8C*TfICNnL6r^| zm0o%Q=(^+Xff-{Sla1)DZ=y#lHwp;6Y0%^=GU7+rEO0${Y77$20=Y!Q;s7I z`j3me{7FEy%1GWGc3eA-U6jEDI6;A;f);XhtW+*&mYz4(-L^<3z@)|h0)wj>%!k}w zgIN+BhJ#>j=O-h6e2Tbm@=ua`X0tAuR zsB}rS>P6mp5bblH&eq9k&x4|k2XPxK9U0Q42=~l@odg~ckcRMI&#Th_JWR=BvxP(( zA{oH=^ol2XU}&U33qH{CLeRORSJ$u1IrY18h&t?K=R@xPKC`51J*;}(b9Gqn1cc-& z>YI*R>Ro!a(-UU2gzHp9b@|58tz}VFlv*SzjqC{9D^iT&+pSk>9VPBGx-FDi zE!)e47?7AQy(ywm75Xaex;2fq8+-RL4l&@=x0%du76=(H{DhYo>Xr4qWV@sb`q_rnR6cye-e<}|DbiG-?%bL;=?1M7j`a2d8t=Kc{7E-pJ#2kHpb0WZ z30Sj>`|Kaxoh%h!;gO z$Uf;5v^>OgcC)f1b}|5!C3IwWRC?n>o1tOzJO+-6)sU{O$#!<>RgE=wy8+eQF1H$6 zysQd-Gh;bA!!FZTN4WYgO5tUlH>saqsHv+*@TAFVW%%q;i92>sKi3|xIGE*rz#1^s zeJeZI5-dD!Ip5xHJ=UGDyV}FAf--VJR?XR0#PG9s99|Xbk&~8oi1#ep%Wt9=fjP~) zax*3H&*iPJt%(n9h`fXzhR}4A+F7dhyIMj=Pdc|yNl7nc`2hlgvbbKVDN20Pll#YW zp*jdXk0h*TkdZ$>CKzJ7aFk~mF*3q z2M>^Z!os=|-i=+yVk_4j;fLY3t%tO(4BMcpCbGmY({ZK7jCrM!{!v0xmJN<&S2GIf zOz$3tr>%HDpNTZiey(y#|M85|a(Uxpw7b~nXagEzsw5t4)x8aFn${<-M8^XNw-MZF zwsx%h?wo_4Um4O7{bluctxQsVut~KouQ(KHx;b7sU2yd#^rGfaq1r|~pGime&03ot zTXC;F`!E&=3XL9}P7oeq6By#-x1jeR`6wQKr5)L(InKpnSyv%V7csRu4v$#v!S z*OIns zd(K3*-fYgO>3=g5V-upX_j$o(!1yLkpuCyGte0SXja7o@ zb{_%gulNu8vTfHG*v*r;kG|KNF4XB8!DRyWn51%v&`M%g6Dqk)OsKyQX!u2LRm`4E zA#}z$Z^+aoF+|dxP=g#{?{lke@OO>N*T~bJ<3PKmGKnDurY?S)`C~E+;26;J{!}eh zA9n1R{3h2Af82bB-9fmipE6|`&l|pdLjA_+boA%z<=Eq>mp^r8#B^{x`L2g;u&It< z5m8MfZv;~N7)-v3mV0sO-TU_{nSrE*sK(GRKKKg4_=~H2et!)me+Y%D- zpevYkN_#Qk;6{JX68aTXoi8`zf0X+cBs$WP;n~#-q(EJP?z~$7yug$0Bjw0)$1n1~ zKz08H-(7zRV6f-9yUb5-9DVAN@FnoUU;Spw_1|yxzud$B{*UWFOizbaj_VrwPj2L&cPK{*m=7idkE{QYzWC=^ z!m0s7TlbU^|GyCa{E!3YRq#5z=-F#pHL&F}~S z2`DXv=>CcE{vWUY@keWZU}#mCqj~%Pi?LCh^7-YQ^8fSY1}MIOq3ug#-u=I@;I3nGhK?y`40u+z+DGly#=gLiprGB|M>#_{~w;9|4$#DH1iLD zpccO>^3Z#8N)uf4pRc^L=Wky48l=p3{pQX4fQf(N?m|ybrB$bOZ*mg*y?gh3I8Fca zz3j}n7eQAIAM=F(&D=n324EAQm_v2t!3*9CH%`q{|HI|;(+qgz?($tQ@!AgK26K+3 z0m_8mzkkP?;?DnvS2%qqS76bunMJ<6a7DG;wD}>xlUI=$8j{29T>h6Az>vy-&;KAs z`QrnXd&@jP`oT@$a{~I3CIIf&oVa^u)N;++&JCkx``oZL-xnt9aSPG1i5x<|{|sRM z>AGz^fR&^;FLs_iEsu16uN)V8PsSt zP+D49vERS{)i*ss&|JjHP^-afCE4SycAjcib&jd3@5XAE^ug4<1a3WbhZz)E4)9va zrOf!pc9RPa;)AKVI!^eweA#(Xq*qX;PTW=iX#0*67n|NaxO&&EAYOQOVXm`<15iJF zdFS_a^wN3LpE~}F3r`qaXn)U9z@9|>t&QEC;H+z{TmaiKMH>9+Fyseblo-d zIZ#)!h6La9o_(MgPN(L1wNjw63PzRzxF=j7q@`l9m%(J-`XP=+_6Lx(YJhk>SJ81% zCXK4PD*!3U)j~Y*_zM-|1c#*h&%xhju!Qyx*VrdHhI!&7oxUl51k6S-roI9oI4OI2 z45J*bU-}K3X3}WFk7pE)aj_dn1k@k3Vy}-3Q5FwY+@?MGCeuakj@N0;z`V1o^RE_h zgSb?SF@=wPb{D&1j)vW1cj>s4--jhUl#-Il8kz4+8A-!Hhbvywa<{5ooJF~mBMyl` zJG~Nqc0NA7ES!Mh{!PF`6Xmf!tj8$kXdB2%FNV4@w=mciQsvQgGv#38uDIVmqK5)K zfZY_s$A=|wt98ZZ&A;)eS)Zy@pzkMZ>X(BT+$3R4yNf-XCS}cHPS_d{Afx~Q(w7k@ z$gNSulY{P_DoA^1?&njH(2G8>+hT7@ZbQ7*sZw#Q6PuQ*p;AY79?Fh~iwBq#y51{6 zjl8&>04OHhhMsxaUVmhMZXh%8guFg|@r~{nx?@LC`4SbC3ftb9;XE~$x3}ag@4w9& z_mkg3-rfr{EN`9~aO~`+%Y8h~t*k8%_^2y9Q&T?M((ENQ*K5u3KIPp*-0T3Y?P7dD z|54w==L0=pE!DB&vGo;T9@_mguBBWzcV^mO_4V~W!YI%vJMd*z{&rPlxMpbxHb zc2Sm8k=f^sQX#GS|P>}dv3e2!+yJ;_NR_?(@6!Z!d#?RtNkCbr+Cu`Bj_xQ0y; zYuZ?u(iX{>`&eqIUfADr`=`7Q5k=-yOXB(D@oG#}>HC+bDZ?f+EHANhd zRR5mX9B9im^<9>{9kQHs@aFgIK=)((Y7a_%&~kbrb{gohHK9segH3rxQ=5^*muONB zv3B`{L0`WvLVY9g`Pw*myeO5&2yP< z{XR=Qg1G7@vSUZal1sbui@&_bx5u`s?Yh2@BX3ZfuMD0s{Gge>5NZVd)TY26_x=j~ z4~|#w!~dxv{vSf#^IX?s&mw^4$*KpN(#Bq=eYu>3Hr?NvsuO^un@xHJi@;X79-jXcU#*0^hdQ53=+0V9=daMs>riw_8>57j(#x3&4^XL07Xp47P-NfA& z6@5X-oHhX0K_Sp<2A?AKu3{~~6Y6l8SGK*$yZ{ReH}f6?9E@_GFWcLEV{IYo*WhQb zzY?fCNU=$18qKx*Qus;SS1rK5$yXX#DVbw{w*S zOm~Nt52OMMM%ZRAZnbc0$q>^3RN?+b%!S+3!fgeQUcFw8yDN(WnjdnOqX<<}_K#pXZnmBQa}?+s`?h_WUT~e9_&LJ>^}!PP5MM760)*;W;28H@ zOldYA87qBd1URhnn@D)aD6s@Q6BMd8UQ6Tjbl~g?=a&LF`58-}ab9Z^SjulK{`R@f zBMa;8r;Oy6y60fWqLY*r4x^Ve$6CQED*L0r&airL)3)!28C(uU8~>ILkel70E)j8< zdVW(q;Vv~5Ab-$C!;6h3YYBDke9gx7ZlXMHsw9GFP$jgFF-6)vRLS)l7VQ&JYN*|i zeAz}sU9J1a4`r;)+t!Kx*Bhy0{TwXO+S=NHzUX$e4Z$++X!_YCVr5W2L9fIh&h5qm z0G88L1x$)IP95dgl%R)yoX^=ipq+N<%2q7ve%3GA_Qts0zg|jU*X-sEmR~no+!iy$ zyrYgJC0>n|Hb7<<12X!(E(DCOmFU)2*^iah)Cdo;8CKKt_RSs%WN&q4E`Hgxc#O ze&#RT6pw>QcINk`rWC&{_#sd0Hdz5J1!@4O!Dl3}d~w`SuYM_5Pxws@pb5d&ybb|) zSKWG`0r5IH)-f%Vef){v$W3l@W>*@&tkmcBZt<#PnlYW-5YkpdaOHo zXPxkG?cJ;2{9vcpD00A;yw22!bu8~EO5og`fo5KbG6BBbK-V1`U?bA$d{+*j+8bpB z>1fq{=_Q_&&SAaKDSD`jd<5vTtTBo=RS8l`4fNE+WBdWhOjB*osQ@fbkU1D#W z<+I0LWC4svvXswPRaJ%L*tGXhF1)!&bGHUpBU2jbTu&FTqK19QIPUZF6HEor^sXnY z_`R%k0vWs^6tK1=X~`V7y0ikGb9a2!i|lf1sb;=Jx7cL@JxlPp&f@DW#+e7}?)k=^ z8=l#E_dV~*22QcK490w}wd{wJ4_F(gNYPJeQyYFI8cpBEE^Q@W)*<+{Nz+K#_up%r zt}J^)POmd*

Sb|+B}S9`N!PMS3wyMZ=q-BvWwuDn+k2^^-GsaiVz^R0Sx5e14_aS+6Dbm3 zx_+PQee)ZRK3a1xYvVWo_~C{~2NB7gU{l|3!|%ka5|l-ts;5EF^1?=)d={ zI3?!uHEK%1zYPM-FTKg$+M;>y zs}Kj!n~g-ke;RW{cf}W(HKPBKJw3V{EV?l<*=}#tY94U`w}h2bJ;;z=@P1{YR&qu_ z>cYwEgJw~Ge9lp$=fr?)R|kG=gOagOV&t8JaG^)5ZWjZ2g)HTLyf*fp;$nB2Y+zDS zCJe1_P;6Cv!{jv{S(}bA@uQDfh>%H>Qm%!o(&c3ZcI0$#B$a_;aG^p_`-(>>g_62b(|SQOcpY@4L!WQ>OtSvN%lmg6 zCVE!tPeUfB))yraCc^d=H4Xu(Q^cPNZkUZakh?ULt*n8J7n`-5?CFC`8gK-PqTe;v z8V+chG$gkUzx3JL;aq^aOOdXMTLD%NwZowH7m@6hs&v?Dzq;yf{CJ*`7|}mJ`m(12 zU$c6@P$nI9{mPPB2~sh2Tg&d1z}jv}S?mX;s90hz@?wwvY`I#mz$=|tjfyR1P_;uI z>B9vuNF8QtB4z#8RTPe|o?Hf-Sbg2Ec&lScsd-PRuiG4`Cs@1`7ye~L|WvKyY&&=QhwC@aW>qL$KB zJvrap@lyCZu)_G2FX7yS5ZnrMl?CZrDWW#<)arS)JGdYFq zK5$J;H6hcAMr@;iHGoCG9?EIWSN0^>_f2Z>QIT9e+NAE_F8=a$H8 z!UgY}RVmT<>;5RkSpkkS(^(b@Oka=e_xxBk?8K7U67`c(1p-p38@{}3&fpPrrr?Ys zHhx{GVpnszvsRyER+&)NL}6H>{$x44E6%!s%CDYC-kqcIvZc23+?)_mI>S9uQ{`UH zHP)>wGBWi2ouq!sWhs6cqKkOoda}vl$%k68bqv{<_NfGyRy3nciGuhV`EWxF>cUV;2T7~P!ISA#af zv3*I042gi9<>%JZj0e0puv_q@jKJ>NHy-`peMUti2-2l=T{U$12lg{nc_kCmW$B#z zjb9GZgkb5=SsWaoYYF+{YqvC1-NKxM8E)L*d&Qw8`m5>nOykbq+KN532#&dA{vA#) zpOhSSyWgw(NFupT?2MUgF1o&$7urt#n7g)^i3<7mk68x_kn} zN-C$pd$F!c{8FZ$PPY1$XUtY7V*Qk9Z+)`4_2&^)l`dvg>p-92-@mK4eRPGy9^;!l zJT~J^^j>808-IDu>J}m5h{3|Qz?E4o^=$>ge_6>uZI{MBT{sQ-&t9l8NgIK_^d8f% z85*Kn=b6Y~C2sj&d=;9EuC0%}aWKuzL_;HPcFyw1&YRk%QO)F@%xF+)V9eIc8Kb?Sr=h3RFv8sUArSARV1M9S+ zuocH+8OMBQI>T!H*zz%%qy!P(WsWZwTARciEV*#@c^cG9&Y}siO`XtJ0?J19EA$cY(8XZeNTbm@0BI~_B8$3*UKLssW?Nxyb-;Yi zDZQTG#ifYnD`tcf)kn6c~5 zkZ$Zdu(82HCqyV6mk$yt@6l?N-}h8kMz2s7cY>i9mwDMObiLHWq`|iuV6-L=0WQx!M)?CSQk@ewShCGJxC@SKqcTqrqN_;Lp03G$9a_uK^n3Snzgl#shdZq) zg`qDs(vdTz+XgO$#+`~BFNL=5SaoV3v*&7y!)v>TE0zXi{rRu-Mfx6G7MkfC&Wc3U zf*UqW-K%H)?0+g9jFhM5)4@Y2R#UsBdDYh@eeq1=4&ovjhl?Z8vb!_LQIw3wc2?=c z=y=U zej=SafD@wU2Pd!DPE=`hiqh8N!~_M01Ii?vPBf_(2if`tEs`c3G$yKsn!!d-qT?)z z-ybnDJ{|pJ|Ms^}WRtNcc~!)50wV0R}~q>z^?DfvLNL~ZN|&i+zA zL%PNzHP}Fkh*fCy54<6FQEfn3<^Yh0d`WDS#a7!zKTO%E8OtCxZBE{rKRT8Lf+v3i zGr#5|b&P1jc;bW)(lZ-1+v#y)?jIN^^!a&d1)Xh+)^CkuZ6lJ&({0NYi;3bE!tX1h zQ}cnm1l-P8lIas!Oz0#!rl3(Tcg&M~u-bF#hw(h*1DM2fM`1G9;i*2X0JdMm>6|BD zs+|9;l119DOCdU{Mi!%lA|)(-h)man@Lam39SfR!WtjLbUe**vhrok_9fs#0yR8h$mpxWOkt>xFZezlp`C4uo5bo~mye?Zvm-Ku>S1Iwu zl<21;Sww%92lkXl=}d6YdDA!h4A`p2l0U_Fy9%7-tcoBeNk)gNG*3Hg$Zq?RpJ>Nx z$Iiq0l0q3fQc*cIs=eE=na4|M-Wm3j3Y>_~be}!!m)m9sf5u#cS=@#Z>FO5vQ`iQ1 z>t^o(vam!T!E4mi;>OtHOIX#Jeu zuLmq;4M*ZwPoFH*6>l82^^N(;QP0d>=V`aM$%~nO6Gv5>7gifRLLaRtEo@zyn2Wf^nOSGqjdOLfX|og}SM!3|4+PqEzYL#A3}n@0B&V3@-Q(y8q(=tK zn9}C%vWz*}ekiL6Gc{Fc_wGONFr8ei4Fs~|-F=u@(!KW3t_u`=Rb~y%ln25Uhs1Rd zAeoU>4lK!9@Z#C>1b(J-C^fb)Dt0LO?zj16nvyVeVB|FMb_TX2o2#(Y zuQM5+JuEJ~UOTK?xaz%+6wWo9%_|dZAp=HFFW?3Ec+xzGlB+qgQFsw_BX0>hEuuJ$Ufo6&eBB z5K5bu#?&n?ZoP01DR~$%AOn90a1IjQxNiU|(byGKKNo$$hG~~|M+CMCN(z-4aKxEy zT)|DnP@fhy&zzSDgw;g&??USL!nhT=`<~wWbSA&|0H6akjMwyyU6z_i?W1zEFI*k3 z%(K9>$1WHk3<>D}Cf$QspT{ecx${Y;ao@UVIb1xe+RxkiZ23AWCjd zf&*_Lc@GcH=zoYxce)@ip{K=#c-6_v|X*&ARck101=%%@+tN8 z)q$&}g$4laor!H@G_M3umOe7L#GRbUin&0-TemlOiXQ5f2L?dQTQU>*OxK$~k}h5! z2_$q``NS8!oFq5lN{__MH1USs+58e7tJWy9rQ?0L(qVT%OG<|Zg(0(qCr8l2we~+# z-Ts*+0-=cxQ!AGp^cf9{|Hn@15g_0V4_pvU949b0Uuy)7@tz@Nji-6J?atCYOXM}XN6V@4Sahl5BM@5HU$OUOFYUZdqevzl*AC-^+j7`FccWDfFz^KFdET^(~N>Egh}#8F?|Z9|AtRLGwzl8#4}_m=ducf9bV3ey>Db zu^{^{c-r%5IB&m490~|{j^{T0oYh7~)J6D9Ll+|CWM%uMT3BfA{jGBwYDs|!8sMWFR$sbRN zkPGj7di(Az<$HxexP`se4edNX`W&y8Y~_N-xT>sIb}ile)e`yQOq;8?<3%7-Kel`n z6%)itv%ZFSS@mxP8=j)qU#R>EU7f=%R!T567}8^6h=S9b z{lDrO__+pguI{{bGdo%<_7knVpeAKgpI<&Il5MrU7bGgMUt+1GAqfw+hQ8^#4FA&g z#hIE7V%9LFNK8TSBQuM_JGABqaA_*_KC_I+kEJ~!jXZ8@b5DUbfCBX@7w|%?jZGeD z81yUfbVQGDAOrHstyTf)UmehLY$=>F|E)lcTfF{v9R3J%IY-D&z3wbxBA>dBw)OHn zE+;eHy&omllDhkTK(&$}u32o-X*$6{3+@?Mw&cR%S0)E^`zP3FWtoyPP1Z$y`6j*& z&JJeANyM&NXGWKAIFON=3cf905iR=`41c%Z#=wKfCp`5Vdgsv0Qg7sBMD%#*`HUZh z)AM%Y9os{x%eaSw5{S#dIXx;OR_?K^Q7vF4Q^~74Rri=`06#VBbI@+-SuA8n;$z6q zeiyYl-KZMAsNtTL8G4I_75|qMaZCHA9rTCL>CT&dbFf(M73%P-{)>j=;*t=zaUg*C z0fa|Ac}ZsX3{1S6<9CKQN&$S{&7s;h`&SGy|7H>~8Q1BuzwrLHiPPI6n`r}VB1FUy zT_sP?8T7MKp33L*y=a5kHzSm@{^?bCp8WjX<~4MXk92?1L1W8O>YtmiR;*jPDv%etTK;7bOq7=X zB}BF$u;&K=UW~-7+I#DxdExKgCnThxPF4kyh~b&A<2LGf z1(2mYpPoc<(eRIvbj>WxIQ4MQ-yg|WgLRC~s=XHW&olFh1pL~&9~Y!qim7R8dI7jJ zjE%U7syCibImDWRk71j z$mCKlg4u56mp!rn$~3Y8Wn}bqo6m6@2%*8!yB5GvRzTsJ-BJCHlF^c=ptBo1EtUC2 zz)~N1vG7>TZ&6*Gn1;(8gA{bd4M@9`Y}^%;FKt-ddQ#ewLkF!%Fo4b()4Hyrn;T~} zYHmi>3QK~ly0YI7S8K5{crEA+x1ax{UuyiA30)qyyO5Lnc?U97l~BqkVv$BZqai;) z>fCBU;;!9!s$4@z*1sLjV`d6wT5A75)5D)Hm`FKy@_L{-dn_tnJ;R#jh8_5lLsmb- zX-b=!VId==(Ytkh)9QF!%Xxv%4r|BpFmQl^rYok0Pi)JM=~UK26>pPTY*;X{YPP0< zEcC@!FS+ki&1(-7K&lr-)lC7Q_7nm?N%qdNRd`UVfyJv>dSWKZB4Qy)7o0jyQF@j+KpL&&Tj#>>W}qw#DwX)IYMR1!Ihfa z{*lx=+^?uV^c|7J>VIYj@5UdAR{-n-BNO$>rw+vLuloE<0DTbcuF(VB5+$d(@Qgo7 zM38QBB4swkERaO~>c%<^(MKPX&+(*qgAsWjQ<7f{Hq~pXxttXW#@k`k24v>y^m4`C z0CiI5A8JXCPj>&2%6)4=4-7x2tzeSc6ZF~sq{QwsD>L*`KkSGZWwVAxzkqE`S#n*7 z5=pqm@F!BvzkVn8amOz>ws-K{e&H+zP(#XeZzCI*uyV<&QML=vmYkA z^JU7dlUejm-mgLcB^wVKmIO+!FJzA5J5SntKPQaTd<8?T%8O3u*DF1`EGPH1N>J?j zMV1AapTn3}1wZbTvuUuEN$emPP!oxtF|+>R_Tx&Eb1Q>vWA9#PcF$AM`qu{3Fp=<` zy(=R?T+(h16!injc(*)~6oJZkJD{lX?FHxpsjeWeAKD6^!s+>9hFnlKCDQe*bI&tb zh0Y34Z}S>8eA9i&WUk=1(K~R{Zh2{WvK%Ei%bAIN8DA|Gqg2Wz>h?{+Z78d4c&%4R z;|Vg_QG(idWgz`Ehk32zk|xG&i}MDDweJSC*u)kCNe10*0ERq4DWL2K8Pl_&dbna^ z{L#0MfCqpt(OsXScykG!>T9@1)NU_kWVHGtDf3@+;=-5nu@Za7D|$vrRa`QU+AJ4p^lhxPP#~w}LUCED+HsZgvS7Lp6#+!@iyrlxCH4W`==FmIYy#-a3=hHVw z%l4{}%H*3J5aJkxvgA0Jt6z#XHu~#4d-g2aU)J8tUIvYl=+S(}SK>3g!A^rSbmZe$ z2o_o;_Go_+AAV}CLQY9~I^|TqqLyOeTYDxoT3K}@9LlPU?YwQMy+RrCiaF=%)`SJG z%4#d`A8z3%&??3x9DGstxo3be$#NN~+mymXBOSH88-(CeZw)PjQ|bs+4ISr-)4$>! z&~Egt_EJ$SyX>>Dy=8r{lJDdBDiBx!F}MWp6DJTL!y=uayh3QRNp`ZqhS3dHICry#E;D`?BAgp|gzNiBn3Qm6RgaZ~?yZH;88P66%HKSCgTzn`~%{j>@dqe)!U3L1<}AK3-aZ{EngKP24#=JtO=RJ&9d z`>=T@Cr(ik;^O`{F#b_2&Cl)SU+??-#0Xlzk&!e_R@-JBb#!)yf~Y=ZczAfaPQ~vh zcK*{V+??p(ziRdg9d?u%H?~JI#w|B_-70Z~?qDc>FZn-Ef1knM7@CpkSOMQXp5y-m zM;!wf=@iGC?CQ$)^uK>{j5jc_c`&=<*nd0Ce~$Cb+7MEI1EXZrc&)+rcSosz9VB>C zK^S@%UWeaQF8}3ne=)sY{Wzp=uqPn`$~R>;f4_eI{1g@mqX`8jNb|oRZqhOYq*p%2 z=Y1o8n+5onFCYj5oD^J>uJYfljDLMbBy(6em6XdX+n;Ok{P_U?e4qa>PYn|KcGv30 zk72$*zQe`WF-Q0JX_P-6tVtVAp=lxQU#;V(kN@Yd#3#dG7fE9T&fJ`u2bp&F!`U+~ zF{}Cio6(2ENrqEu@|OJbVt;?Dn@>~dfT=3XITsN8_e%oq##SGZVg35*e~g1*HVQD+ z5y!;Me<}j}SDX2deaPz#uvG-fcKAOo^6P~G2&M2vLPrU!HUEbt@e0@~EEdZRA@#o; zH}B8Di^#gKR$Kfxlk(e&M^XW9v+?KtMH|zCD~Ws@&Egc zFZm6eKB#RA!nI10D|QBzF6sJRk%JBHt~ttu4L8TVReK@<4~@}6Z5q%#H=-o~SK`GB z-@a{?n=@KCju$?A=vIc=WLZI(2Ikn`7AXAwq=G5P-Wo8u9+zhogNlIq`Ak@4mV%vq zgvmcJ@K{T~Ss4HiB@xA=`4rK?x84J~kSrZM(yIvn%8-En2oC-;Sb0c(!+X-Nd|+ld zb%&er_KbTF5f3%E4Vz-YRXBLGNyVO6cFvLFNwLLNDM|*0Zg<;r-rDE$NgN-a2Ye;u z1yy-onBbpW_(tdWA5E$LKHv}xK@H!4Q3|7$N;m9F^iaREGhIDYffFy_nMla%)KBAK zW|jwzcB2(^d4TvGXdaK_(|EBTkf%}2(&hppC>S^@DG(FP*X!zcdW$`K=6y;>1GsGhGCVq457pt1fcP8T%B?&l5gAmVWt<~PvS7Xt_U(@wU+poq~fe&aDklUpj- zbPB=uD2O9V^t)WXn*@CELB$>aJ`%~O&O5q%L{3ZFal2Tpl3%J${r=4m>vk}W(@_e@6u`WtC3T5#kD=e zs@cEbp>-0`q5frLMCu8rsw%SF9>OLjBcMYp|D2WiV-x(>TT}HXJ49GmizwR8l@E9a z&;xH*9GU{%4eG6>iBwt3lFi35N-(*g(YMWPwc+X}+C;5m67SWw(h{BJ&u->@FMdRE zr?~xe%J^^vvSg$1h2(q=!;L{~uM;C8F0%_0ma5;2b^a}H`G;|V&dK4{_6x|Us94+G zSj0QL28J4JtEm)xd^1a$cPG11M~bEIG!EY`n&xtV%rb(ugbv#G`)$#z7`5u!J!b63 zW)^bUUkf5WTuPuk3V!XuPJ00#KdGkjxSS42jL(Rf zO-0{;JA;>1G|;bA#4Gn_s!0E|7Lrv^jY3l1zqj#p_nG>Nsir|rmJ*ttNgN3%5pk)Q zF)e5K{!<-3&tP(^QgDw?FSvChC8KwJ+Fo}?qJ;qrroEpGR{{`CC_%~23tp!n4bMfZ zpzWJ<6j*$Zytck8zBQLp&y>VdAE2Y4Xb#`tCC9^CM~M5F|4TRHC4|%eRahh_bArR$ zCEI41Jj3mDpE?-(HP7WUk+x`3rO(JZR9N3SS6!dN&?m`K8J8}()ACQDGHF%_;V`K& zKpHMRk8MIjO(snkV>R@CxRCpjO?vs=ef{Fp{b~b|JHtwEjg5_0=ts)%c$JDe#{ZTU z`D4_ch{36;2sM@|zE)5uL(^5vmi>a0^2BNP4YJNQtItE}!H$UFX3Qo!GE0RbY1_@w z?Go2M;+)WEqb%NXF^bd6pz7n(CXg9THI~UGX;F55Rla5}x^X!q@KT4@`o%PcV+kt|a{^Vf{i*BpgTXbTzI^eUu zV)P$6E_W(Em$1dGqr3BZZyD-#Qa~%dMH(E(ch;4O=?e zHi|cjaVEAS&*2H=8@&Aaa4AR@LgFdS4k*mW@U9X1uzv&0cy`TOPE|;zvF^!YES+V z3&_RGEQip#ldnUVSn^*~^+=BDYVEff0-_kg5J~Wj#lP!2=dIpUR%0-DRaL=bJ^56s zSjfW4y3I^_(TyC*bnvT9w5bEf>aEK9x(Fc1t79qU$Z6gs9JidpOx!x44{SrH52R6y z@1-RpdwTnBoeH}KPFJ5`qk$|s$!X_!lqZbZ#>_BCG6(R!A@G@D3`nSOjhJ;u*_lM8!G zmAuT6t28p2zvs{i5XCrcCOIUx+o;jbf00M}b6-qWK{g^osi*l2ePO>=FIR8)87hsJ z2(l6Thr3RS)i$ZqZU<5ogJN0I33|=GMl1BvkJ@|95 zd0k9IW(D?@7speC6BcUsh`H_BE0~6fcpNuKIW;`X6LbrWxlYEj4fRGg*pJJv2C`&i zfcoEjCXG@0M;9~iJ0u=K#!^*d73*jAtqcUygQP0Zq1yIuV)7d2>tUb*Y`woBK{K;W zyFn+yynT%F)V5~CdNOWVl_H>K%Wfh_;EJ3v zmNVkY5E^k(^#tqv2BYp3G^QZbc*vlJ&hTh$mQe&IS3c|bIBvv($31gKZmF3@H`Pe)io>Q;oxyhAoJYX|I8Uw**t5sAqc=> zj#y3f9Dz^+kT;=kR}pkCVY77jY}zPrbux4`ec2JubzlY~5oNzVJ%HV$R%{coHC-+7 zIYwMbDweIp9LW(fyEv&RtlAsRaS1tommu}13l9~S$vV-6*M#SJ{suNNu|=VFu7R<( z+zq3_Diuz@%Qvo5dE^=WD}6n^*iL2qhO=`>6jnmi<*ybGj8`aivJYJu)oU}e_;7mM z*N}{z@sx<767k6l_)9kla{w!pgefua5d)OiX7Ll}WJCBiD6g)Z^N<)E9Q>wyVsf|! z)tlj-%Rl=u63{*WW>dz?3JcKA#q6sxiTgzkGS-O<*^bNvy%W;~B6F=AM8%8EQ6N)#1+G2UdZX8>)9qrx&O&e>9(Kg(W(&t0>+8 z)ffI{ke1AbV)VuT79yKBHgibGz2n4 zmXMr{`Vr0A3LiVp6u?p(G*JK=e`T<==0q70`Nvxm+h_cu($XAXl0L8)^^+3x4q}X& zSE4!Q_tCp$5NyPASKj4Qvd(!E5gJdPt6W&VioGQen0{K|6ftEzJXnvNT!Au-q72we z!SXWZ45065>U?=1(PT~LX^VH+w4V}E)eeYZ6R^IPFn5!jB5SW#aA&a|s-!$R-jQ(7Gyn^=uT!VyDG)}} zoZ4pfdIU+qE?^?!ZofS~S;-^CW;K!`%u_qjDivp|P$2FQr#rEa&0;-xA42PVqw59# zoRqufEgHn+df^u0U2i*ao`=bQ)e`t0PAWr=w3cWdc4|bjHZ3D7OUz_rUd}n+SyMqp ziy(XnG?2@;P4Fl+=+Wq#Nisq*y*Sw`AlN!Z(_Sasv8^HLa=#ipb~`^NDshDHyQ<#w z`_5I8N{Vt4pWy~jHV(5rpJSqA+?AtGF0f~@v=?Zg3*~IVD2~spZpWa0!E-5mP8&X` z2PU@StkUqlRT{T@f&jmasU;#$9+(AWU?bx#8 zvPu8WsB9Y-XaA3>_=|Uf#`pVOb6tWIA>|uDIu6Xx(!P zkwN{G!Mh49dh&GD#+R5P{Dq5)?}h1- z{QiD`jKnAW5p&Q|Qek#+46k%)+mU})Ys)1+|Hm%io=?U=G?aN-8PD&Yslfg^ceAio zyzFwG>K)=Fb8XrbiMIP?!bgv5-AXFn8-Q{jM^v=kf-gEU(m+_n8S(|^FDCq-Jq}&6 z57VKRDUk1!oDX<5QA|-wua>Ii zCeK?=kdwMR=bV%swmUs!X2u{OFqm~SEvq}5B;e}paOX+9IZe;PEq94XRNLI6_HiG+ zkPF_ZkG)Zu_~)OiOBRqOC_1z@GNm?!mP0et%3~|;ucQe3 zEwzV~1y@@z{pe)+Qv>?LypS>lPxoJ9VqxjpP^_2Ou2#H~*34U_J7Ppq)V?Kmu-X^K zVLmy@xwsI3C}v|*lBW$(b*C+K{H#^)l6uZ9yn5$oH$rVoz^ciEi|P4Zwqhll)$_H_ z56if2!)2&9tqbRgfk-9eynbclSK(5!6~bYnDWIk^zDa@V3=IX{-+>M2*ve=?NLl2y zUZ4aiqgr=7Vqzv`E$4`M8*x5E`^2D{{GGJ(js9vo=^q4C!48~kkgd^rLtXT@{Jh_% z(+l|tR4c)?*sDj0<@jz4jM9r2HRiZZKp^J4_Zd)mM&q0&1tqn;2ML4bcIq=Czyk9T z?f#LmELrb!N_0%prNgP5jS_O(iGpt1l;BVr86ExL++rmAr7*YM8tKYq6zDb)gZ|Xp zZhKOSsn#rdIA4A2C=jGD$T1-NM(O)=mST|fuRFthat)F1ID=G?{i{emJ82Om^ zL2{{K6y57wrOv7NBgu74y%?k+eid9(&=~8-IvE7(M0_e^xhgT1-E?Z$9vCzsUu=Xf zAL_4+6RaT=CZG&a1vJv4g&wGum`|;|m682=jKutUm7r{B#Ds>#^CHCjsne|Us+gNw zUBVQPQv@I+$!AI5B)t)~$0faQtj=m5;gI=ach@ScivPvQ#!@3u{4X)%aB|omYvL#| zZ#Ir`8{x4K$D3t019p{iw^Np>x4VZ!wO}_()hsvcvxs@D>)Rc}+LpN&zx|sW`a>eP zkdTmLAls7TrpP_}vf!5-A^)&9vjLwzG8i_HQ?ZxZ%|)S|f>N>FvgwJfiPCLlAUU^X zXg!oE)w0goQT@X=!mE<<@_y}AnG73!YM{&g%M$^qHrxbMrz?g|Wddq&P%e)=^ByL0 zj6h_!6V*HFX2NK(-@mtlQxTC^$F@=ZPpkMhcihy0^~8nkTr1Dk@^~phY@UQGZx{W` zz27GFp$A;A_I|JSSqtP27sW5XTJjRg6R1vtYbg~}e4pPIPQ{s^_=hT=Anko5RC?1w zP}HTHvAa=4`O8av|Kn0LP%{+h8hjDTSk%Oibb^15U<}Iyjl;zg*gTyx9BZ{-J)-Pe zgm~BD-ueklH(KXJZrD8D592@lIyhTBq1^;W>ZsPP#n@k(u~!(~_vrxpou0(uFxsVe zSWR?#uQGpd{2nI4=?jqoorHBR`d>X65}B856{iFYy*Gcq1|6{KU+#~;{R+G?fGVu` zV$lBBeSQ_@0OFJ=_!Ff1iT~Z^|Mfv5<*gceOz_{$rYtW!KvO7y(2!h#1{X3qGY#0e zQo9}?#WQ#TW4w_9rKG19W?^BWWMovd!%NcxTt&JG2#Mi;$4t|L19klXP>`lmcu!`v z41{|YsFLS%LGrTW%e!+b_9Lr3ZmwSHIRI?$OSSm^R~9H6j{xu`;3*YpwfQ>}Gkw*8 zxM!($OiWDXe*eqQXxOWqkKlWy=!<+ntD3JP=kNXN z7fOB)UzREhia^Y3pXNJ7#MiaCZKn+?-aEC8Zh~hcjSdYp6H=xP4VD#KSfrmSeE(i_ zqS}@qX|c&Ez|0a(<@t*quYY(R@l?Uef&@3;!b3F^nvu{pVs}vsA`fM;x-v}9UNnN? zD4SY$?*Qnbx#oBtMJ9EUl%iBh!$ZR zr<&YbYBA@lZrZu~1W*;_pxo3%hkf=ja**@$g?^Fah8aFEw?M$Q@4fdX99&c38nkR#g7MT zmC^AocQ#o(rbPzR#V876WG?iTHrWlA&$djfcyTccIN?eL#!?d4!rXBYEJ>s_f3Ov zH+?nHHu3^`t%c%5Z1|SH-nDbevVj_#?io)2oXf`#Ii!}+2e~inCz1PXLE3OR4Skm5 z`mxr!_o|h{FpB0ibQ0C(IgH|3e6`b0@ki2eIBV-YOn`S!7Hl_pg};*Nb3?k13VUg` zX{aHvc@|@MAE-d9v_R|UyZ_;lt}q{%sAWe0fK8D2787q@_)@-XvipVk1rYNRhp_gA z(HAy|5xbl`uC_nX9L$Ks4S*al9APD!%JPh5IdUBPeZ2~zxh*j3gYw-uRAF{^%Fgs%tFZ*Q~*Zv1xg zFsf0oV5fHnaUqHT&Ei*GU7d6tjpqPo2|}(ou{?K3UCs202fY4?O3S45%*=!}wiXWo zMq%}G6YLHazOT?738vjsU(nBCgziIVNX+Xz>8nP<&lYrWM|`DGZU^R9Q|DXkYH!@MQ6DH4uGLuWVG}^VIyh6mfvsJ)&sb$W zsX2^fwS6_YReE`#W&{4Vv%>0v4Htu~EU&(2WSp&LLhTQJ2InWApoBA%=wE9qWuAJD z=j5E8`}wv&O`fpA+u-*1Re6k3kavFYI4&p0NjpIXVT%U@+3?5V4Tz1RvKh(B+}5y4 zjCVhdkt=bK{~deRSF-9y5i|BuPax0AY!e z0LTN6QrdTF1Fl%-XMQA}dq~AbvTGlSXUtrS?^5&1KIRxL*S4|o?~ZE}Y<1_D@P1&> z{t~O;VS9fydvLOO8@J;Q>4+pLE%zBj>9tG!R3)TnjB90R50G+3d9!51KU0lquy;co z3hR!2Y3<#bATFBLGv&(o&<*~|(`_i%=&Y(LMi+36cvkb(6`IZ-9P*lGY-ww{ACUTU<3fG#P5 zvWweAla;0vCK!2-k3At%eb7ODddYFa2;}=zlgTo+SjRDJ&i-zfJSmrMtSKLV7DLgnF6GGtq+|D=((0EHaA4?mzSy*1T`}eEbM_+;EMp&lixX6t>+T* z#W!%hs;0TT9aCl!q4WiRul*r}_TY$n^di2d-L3G1fL=MDV`R{x`<~V`C{kk3tkAeo z`E$M~_PeZ598QuiHyH^BxtvoTAmaB95pB0x1J{IfD|XW&;(0)%lUbT#5vW=>Yi#|L z#9-OD_qngr`bg5WnZB?}a@OfUGw9O!2cPAic8(ug9&nacSK8O}&4@Cs^IX($&zX{w zjsVtj%#RD3N50xO28;Y^8I(!?wsgT!9ACWOgav4M?NJu%A0RuS`Vg zo$g0uUaCh|pL*mJ&TN%%*{aI(Y+ zAH&zYhyh5$PJL$svD3gQe34sc5yrp9%D=hbY;crF+aenp z9P~fyCq(xOBTr7UANt;MA#l9acO}M!d^a{%@L1q^pFP$eah9Ml#*>!m=%oD?Lyu0_W3#U zZH~@7wZgk3YabgP3e*<5UCdAMH!JsY^rk62bUkP+Y^TcfQG5LnXwPwM)O@NhhS&L&>N; zM=2klf6PR7Z(>z@53j*yZazc#O6KAcJ=!0nwEZ$xlu!;%k4C2;T|naX zAYLLj`;YQ!7i_ym2%Bd+r?CmD*$76|;%0Ed1o#b^B*{@naV^=nr02da+z=Pg&>!n^P}Z?NB5az}l1AIOnXN$KjLv#x6xq=#2C z(1kmtwXAV76O`$7tIiy(wo}vRaV25(kobcML^;Yy{;dQ7attlBhTO)bcD$-oz)(dC zl!|216h~P?A0tl&%r)-F(ylkaojGbnNhi1>eKl}afA$O(FW*~KIu#AF?=The`O`fC zH>z6@d}>r&#$x)=JY(eIj~{5sE$jC}T^8pz1me8x?zY`0_Q%-S?Tnxo&pWQevv?GI zhLOv6aHtzAlT6$JXFT@sp!xm4WLI>8UkX#DT_}*d5e2O^%}0*WJ-ZGdc8Cb~%4ydV$DMdxhjlqB z!h<}#s-V_v*<5Mg-~xJub|HM^V3?o3G-TjnSo8;L8|`SF(4k|cbJLv2RaCFyH(RSA z6-1*F+;E3)BJ9j2SbqQW^apT#0_WZ*s8agdYqRc%W-W~F=Z7UY2emsg*&*5=3LDP$ zvAkY2f5K+OXN1lFpdsZvv z&oDO-zc%rmA7Y93AIApbCdxWPEdXJ_cFI3`CocJ92wAxAlLMheX zMN~I`$ufG~FfSGI0*+Gf=dZE2H+yi*S}d6%f-a;#1~7@ZHGZ;H6kR1}`+afa?|+9u z>Gc+Y;wZhb(FB7Phz_8?-8BaKNi|0ELI{8oWMNZ_skPiguu@nfGkd(vvkb-nE+jBP zF-K#72Cx|4O?KhbX;A5LI+`#SHl8MmoD{o;XQv4H*aRxhBd;1_yI8E3M3k6}(9tSn z7Y%<_)U0vbWB~Q5&>Tc&wIIIzH@w#iru@zwF~JyJAYBqU!lYIA5b-WyXm+-c+DE;f z11-zx>;}hBc=<-LMc+^XPyP=9JS#)e*~oM50^%5I1HIp08!u%SJE(q^X619J?dGz@ zZLcurQD`VFYAeusWQFpi&0;`K@xG zI?aQ__+Xvzc7L(l8@xW=qiBATTMPyD15)vaS-h895@%O=QnBYB(?ae=JX5MQFLhty*Zw5OQQbJ z)IKc+NAFcKUGzGv4*G?r#Ar{BDDfU~LI1S~NrOQ0uJ@*bQ#A;GlL~@%Juv!?cIAnr8f_&(dF>34C?E5K@Jb@$Mk`0+ryI4F^`cfU$ee`0L3zbY{{zG5YXwTQX z^dSfdEib_&27IoIdsj8oK^`XiX1PoTa>(a;+;M>rP&Sq;6<6tGh**BP*#4M%I(3LZ z_5lR+C{LMs_CVR`yeqWj>dax%nv7~ABjhi9)1N-e3G)?_kPPuOIV>hAczLmjBPbmkxrbrCI%Rd5+z?nJKDEDihW@m*;SzBP zbleLq7C6T}3a3|}G`kvA=izZ#ewyOth<1RX9fLiB^5Bl_<$D3^ud#V!WRFbjhuB|! zknz$J7k$6%(PH-fyX*4Ydg-B4e?BO!=zt1*3b6wo3!MbQ^ty| zXYM{}WU%{HOXZ;a3~R&HvBVguo-Mv*@*Bv@@yr z!h;R?Z(f6Fur5vFoPM#lsD2E#Xdyg?LuUsLv+Byd*1nwSd&^HZe4?~~Qu{u0Yw@Un z=T)8kkl|%Z)Lifp-BLmiHrD~vAp?+ zG#@h=hCZKIH89}!VZWjX=Iovb=msS8TPxw?v+uw$Xu)yL+PV!sF~+~;ela8N>wEBD zTt5nI7Vzp-mrs_P)a1vS9bxSBblmuAkhVRbzhPRI%iDvdIjLq-4b3m#C^o$yTpm{m z#@mgBhaKk=Mm!zvj^`?!&ipFvak6K~jZTI|HeOnaMI&t@W9r~MM#0FKbUyd6Q2B$t z-(BJgw5K1A(@=16oLo`9#Xr-?HpSjW7*u+s??5l3trTM1Dv3vBsS@q&=f~rV&=W%8 zS9i668p^EAe|i>2@^&IY6$h@V*7r^nZlbFkw}X*sCSu9{YPj+KVJUOh`cPJeLQZIf zV*Izn3ZEF!UwGzqZx~^W%&=QMuS0*hg>-GWb96#0LbvTnuYdsGIiBHSB2K(>#U~}+ z3>>3e3aYIi$B1=NaOuKj$&y1w-CJ1G(_lRQkX3BRDtiL_k7_XxGsx;)783phOI`qS{NWvQATHdz_Vww{3M z|H1d5f8KPqr;gy0KgvaHkU!m)=FBv|(O$9vHT7U8ePKN#r{lW52lwo>u!YgA(&rJg zhwczUh5%e<`{|)eC>lUF-~vyP^}ok@?9S8Z@Al8Lcz#KR=OeNhFA^hmzre%0YSMjM zLnU$F@etqlQ8C*OKlhur`}w~zATC?(c&GmR_7%K>1Of$_jVDO?bjqK8NzuHg zhBG!sP6a;&YKKH@9a0cK!A(Vo@cwmh`}+p+h6Wa;h}YTbj}`+^+C3!J{ zQq=#U!QBPLn>!*ry1y3$v)ESI+_h&P5^y&5_IG4#63=JPk7uQ~`t4Z5t&GNJ9GxLA zEGsR?)MnkUu|9k{BJq91%bRq#HrrBxMky43KM%A;?_kzV5N14Xa8HKEoIL^-P@>Z! zPSs>$K84_vBF-Qzs*Hqvs~kw}aN%6K@856ZHTZK+{m(b>FJK0UOcV-DaAu3F!{T|@ zJ&2pl9wIBr$!mwbIx!1{hocsRRcgvs3I5%a$s-`OW{ zN$PodS(v9;tA*i@LIxF&VxmIYi;O0RnYv3Uv`_)hTLLm)sh&j9FHHzF0cr~?c$f)jNs>J<@?KgsSE^I4zr+iyKXkpWtUM?##cqYD)uNFCrmA|-a;3OTor=JV95B(C#}e&U=UoLxU*yK+`GQ{L4x zSf`3bt6cGs0R%SG*N+uR_1Nza@7Sz%u8%YAz*^RwCcd18lz3yBajOExwtETYhB}`8 zoW(>*j0_(ogZ%7HB!i|!oT1cB!N)O!p%m*Vz-;D~Uzvn2hW_QYI!NFZ&t)qb(HSue ziFr<$wQm)K>&Iz1v2gO%N=3!u=`G?K!QXh18b%Hpt#9~srW)`L%IOm{Z|={ zy1nccs9zOBFvgM+h*IM zOnc?rCnP7r42h`l*~+8f2S671MeJKD3;(obV*Hw0PV(Ixo4)a;^16+oZ{4gaPi+hVGV-<+WhrJ+Ppx5=>eG3Ko#g07H(jM1VPVw&3`EyJB=e&_sNrJnRs}(ye(jO|U8o8Wy zi{kklmB=Z`)Ac$Q=oripb2M{(Mff0}$~Q;j#JrBtRza`JxD2=I5(7XatzBwmXVAssR64564Pv5ymRpg0nK zANJIHB>Aa3Yu#Fse5NsMSQsutnS6;Jvhxy|tEECa)DRT;8$RP>R%>uE&gQX8c4 z(WaVmi9ru}0(Zp_iK&aw=5D&vwQx`us9wXO>va3e=s^@09^jo|pS?=)J3+C%YdTsW zp9p0ZP4hi!aT2@V1)Xyjo-BiqBMw_8f*8cBOpH`_k`PDa`5!^scPW?!`u9b?vj@p$ zGc_|jQ-%cBq;5w&=z}@7GJrArDg5Z*HX?oUDrG567%?<`#7ow9qkWWso*pA2E zbY(gebU5#5qbp(2T+(9jQAd7RuB?R8m!UB`U3E{LPYMTJC4tSaPa9j=8H0xpdO=SN zegEpL;?=y)>FaL~l+PVLA5LyUyzTmIO`_KS-6Q;&&k0sTKCCSb@}oJkZoH!IvD_lP z1*uW)YaY&-)9C4_)4R*8ToBLVBr9MWM-MJy90bGnDx+Sv^WN_V*f-j6*B3m(yH8c> zILs}=qf?)!Tg?lF^0_*I((Ly)02~PQ$yp7$M`u$$w$dp)w9GqWkV)dt=hY)R5R=*Q zrlQjzojz$eTIurOdyOzz2#y%-E(9sqb&vTj*5m;M|IkY$=GC244krK}%+0`ADm&bU zr~(n^$}aIS<6RSk$I?)Bp7Z78di|V!`}K~{&FMxhNkCLE{tzTUR(Vvzp3WrLegypP zct!C|HX(DEL!OFBj_i4Ug)5YJeWW(&;-ooBF4gI=Y2@Y+(01#Zf$~#!MHU_!QH(d# zAMO*_y!>EhrzjwPz%a46*T0O~&+iNzAuz_Z69+TSP({NK)!d@*VR= zlRY%6%he5O6+e4>`z+skMi11+%P*eTeAKp7cRujVnNzQoXhB&{*O*|j*}32Xl$pwl zi!S&WR>y|Lt1XuA^P5-mj&TQ6R&bAOhE-})4b*BY^3yG5)LdNK0yV+mF%47&e;z!` z#2GyFZN+pv%w~|aRlEsGzjJ!h2ovyGY?Fh{bZ}9i=sKA}t3=_&HCwOYg(Er3e=fT;B{V#C$k&9Ie5m;+nIj<-0_S72<(>f z9tAK~{p4aq_=y;SMJaFs038QA1IZx6Td22Hym>KQx*}hAE zQ8%-=qtiz#e;rvlcPi_!9e8B?RccPlby4%_!)8B{I_0{0$J4DQYFMo+UxAZov$=*L z>L{_lHXjeJ+o28J(f| z%I+ShEdl4z%BL3rmNz}e4vof)DRf&u($y_&4wgKZ*Muj-sr7XBlecEAPsCr{c6Zq@ zcRFf|2bpcp&uQKdd@x#zRfk#!vmm4`yP+XwOb{Xtl^tc~T6MI=Lgm7W>4D11X@_%| ziEIxF_B(fxKjPLg=jy&)V^+F3e%fu@lu84zxeg~pHAp>#e1OI1djj`$d&m>#y$sI0 zk!oqUuNdCE$iF8WNE0xOT!gV=Se)bU)UGmpc;8O4o8A2N>rz6c)=SH~-dv-nYR>lJ zVh$%(@n?QZ*6Ysd0(g)2X~6a^`*x<8;69II%SI(_?A*APbBFWnOE-PC+l9gjwa1Z< zH>6OU{H;rTI&GyJVI8|flH8au)S4>HCN>l_=oD>^IaX9?6ZFic%bwRCm5f7-E9F6?W>#nnOnS)4W=Rk5@CvhITQt1OscO^8x`Q2&!LsK(jSKYQh_(EVcNE^H z5xualk8gYxa_50vkcgwd6vp4@_W=OwShBb zP=200?9Q6)sT1U|rA&^>b^^HQ;I5>&!F4>(+Vd36;@VGb$?!n0VK0snvwQJ3( zVBqECR5u7t^!pOsOXQrlCW>vN;;2>q;DxAz30FPZ6YivVv2^C!73(}7sn0J0RT`o! z2HnZ^N%JnPd}vzn(<&cP(A6p=@s4f}(}8e$10Cl}*{v@o`LQG6Jex?!=;+ExZf8A= z-Vb;|S;bu_N(#=`hTEnohiD7x7fQ=vmCr}%jpZx0?sl%p@2gFUncKJ zGq4_No^1qlgQh5w#|1(sw;&R}ig|Kl!fg8K=jtncNisOSaTYieX+pDnhr3uVv$qc( zjEe;AXm(%loT1&TV?fDG{(4*b0jD?!xY8?=JdX9TXRYG3{A4%%ECpEkjLF`L31Ig$ z7dg&(D1wHiS)7j@2Ry&?Vdk!g+pP9dUG8k@`p1GQ5W31a=O~=#&d{TZDMS=4S&}E; z?>a9OBigHDSUq)%=tmuXA&}_0f;@B!XRzV=b$H$RRhxL8gOVDXCEfdtw{yy`L3}Va zTX*Pt_AdBC<0lex|3UtXHC7^bc;@5<$3B^BC@2VjDTUh>kfUk2oi=vQ!Fu3geZz6- zqYHnwbiIMi!jf2ma*htdOy=wf1YN7abEP_}Cw)xISmdd zP8AAra{D7-Z+?T4{)L;gVUl|@`iOsq_L%PkL1>+B9v64mW`v%~fP`c7S!Kql%Fl{| zta<9rHFWp@8M}7p)M?FE_D)hdi_@1Feq9GS_C5!(CTy>zCodR1uK&;ifNI(^gwUD4 zw8+^LGn=X>Sm@>}i1T@G+}h1muz8;QKz#1I*k&K?&;@%R?_Wm5q+akyOUmRVxny5o zWFLbxw$UE;E-PeBlQ2%|QEUvrr_9sU`uX>>)U;ji) z9Yo=uc{LKoW{W`HGk0juusd42>;z?Vclcc4r|v+f&x1)R2|xX%*Zgy> z4Bmouk`*_bDs#>i2?M#=`%Wz^dBHB$vqhZ|bojX?HvbQ4Zyi?Uw!MK00#ec-h;&Jp zbccd8NK2=5cXyX`D$?E3jWiieRGl_Y(a6M|8(bqqDd%C16a|;59TUANXnop2EsvsEI&FDQzGdcZ zwHX_<{Hv_2zBt^1X(P|tbVb5^Cey1>75pXisPK5*!Bj3oj3}zulA|P^YgO#FIy>V; z{UjE%!igi|I|7f}tci?I@UKB0amxAZ35L=TwiXywJ3`FS*vdz$MHG4A03*Jl_Mf5# zC?bh+K(U(IJ_+tKhjPT#o^(U5bx&?BmdyUgT2v&z7=p;&pL@%%B(x z_0KrUlebJT>D%el>qvpJd)#=DD%Ft!7T(uV*^Dbr+%}s2jP(A7ON1=R9o#JYqNMjE zB1n0sA0F(?)}GuKeKR=dUbpywf;B#S&Ck`{@_8UEKiGJfk59Ag@q`wQ_P+MVNY>w5 zF#n9*`Fp}MURU8mtS-PHSQVWe`VQfrLHhTFgr5)k{#`z(agcO?2K-1s2B;ftZ1?Y8 z<+~kj!#gkrGA@;sW;W5^x8~kxI3C#v%*mLYZdYYLK%k%H3d&ua+xl^NvcBISghXsh z_$gbt$RNzv*!21aKJQi7tqSjt3+AA}=N~)A#jGh&F?a`gu6<{Dc8N@EUY9o+k5wI2 zzq9vgI9hkXzxFaL2!w?0e{op;R1j9s6B-DrWP3g|Ej~7dKEgCzFXT}5gpN`?F_6p@ zye#K*1F8kqXS*tmU-?KEB|V8?8-px=27bdhUZ4O2k6&l_HHn%p9Gir|K5# zvd!nrSxVZ$5tysi(T7&Ds2=95DkQn?&>pbtn2om*-=_A9zfd3mjM4kf5wL`tmHqC;AG}D$>RW*g42TS{*ACB3l9`>%QM>k zxs>$B9e`F!ba=|fCz^v~CV3>?4s)t^7D`#pWnc1)%U(h(<~g~x({TdJIaP$pLDP9z zEnY9Yub@p#VD9T^e^u=7BbyfbyYwsi0QZ!m$s)XcQ#UBQ69OpY?h_PUHT;ARtvdZ*;beCVu(J zw#@K@li)hN^5$l2pXOVy^?@XwiD!JpV>`EqQ46XSDE^AwI9_2VNN3ML7Pe?A?5d`|R` zunKI8#o9WjoiRj0Fl3%o#AEr2XDFI`vzXLwS4c6@6w*#r%jW1&lKzi9l*fx^KH}cX ztpglH95Bg>1#Tpc#+FD#R;kG_DyHw@T#(fClR6f8zsm1ezP#dC;?X65jD|4fN&l{@rq{+eJ2lJZ3yn?(X59ut3>+m%&Wa0&0l<{KPP zy`H%lNZO;!RIPURdB*!}L)QMPe+qv_B)lW>`g`{x4BQWZa?_;2|Ks-m(+5zXARJ%U z6%9UcI@v?ko7m^!WkJ2&9~o?p+vm9!GcB}elJ+A+0~V6x;Unbe1qJpNU^^X0eXEOl zJS1Yjs=M^PC+p)rEYwhn4*5I()uEc85UO>@P*^7h^gO%32KhG>vx^Cg|6a_fU++P%*bC00;qs*MF z(|35kf((aZ@(mDCv+CD}fs6Ef1o7Mhc&W^h&WI1B;_*d?-5W4)nb453vM)~=fH-Fy zmj4R7KPaDVH-M%$jyP@bL;u7}0ZjoUGIh32b9q4fAv@tKCY`(tn4Uojn#ENut3YH= zU?n4G%L+zmD>o?10!_j<+OTXrVA&teOFBC8-WQ@$xE!Q{y6wf)X3IQxvSYjtJrx?1 zL-Fr!Zywn?3RuW0ifx(X-iQWmxDo0G2L_IQ0k?`TdL;jr+$MjIgKJ;lqip+CZ|ve2 zJx)aE9jkn|oLjVJy?tXxenR#O2PiO4m(tF7Vl>upAZe!b~@%GoS<3z_S^J6xs_gd&iY$O6?4xypB; zdOh#SUWYTyUViD^kHcX$==FcRZ*CkkRHj;$FW!Y@_RtufZRm-nv--HjB!-L=*1x~4 zd;ZTa|4Jz$hj=+ob3VTt61+M;peGZL6Jt8Ud@eBQ=cc7OX=uz)gX(&jWteF?HJq1T zzf1Bjmh;c+ov93IH2hF8iuh%wWO7S`0NHv}D=+FByTLS`pefUDW&>%=(w$++o3ivk zmO!q-k#TFRA{m1Qn-Yk!zNaAY5QRZ}x-J`z`=|5s)2IC37roF$qah1HE0UN$BF+oH zy_xEsT-gj#US3{HDuEcF`VtKq5>4opCk;VwcuD<(op-a0Q>8;}4U5xuxO!{(W5=iQ zA2v~6iHN+OZ*W?>I9d@1O?dfR7WP{x|Bo*aA&ulbF;P*CLZuD*6B6~7sdu5;=yo4! z!)ixGO`H{>%T0c`XMCl%2aqul3!WG1_*s_&*!_csVu9kz>B=VAV631xZYS#kP7%Gn zI5VIyL}0zxA`o$m3Jnh+&Q#m!Cbvg=!v`MDtGXF7>E70T1ABH?AZ*c}UKnxf6@G~s zCxutyO!pS->HgH}Z#2;?CW=6<;gh`V2e7$Cg}r{1d)x_3;o(v@->|A6J>%Akt152U zQA!@CwL|OVy^P4CGSxE}lOm&3Dh}K??O*~$-9APIH{q0c?Q$H)IJ%-eO&XE4}SP9pDSxkvU zro`334}2EI6l1}51mkWLW4D54=lxq12IxTBm1!a@b_ozg06Pjd*%{74Wi0l2!3izi z>OI*CG)yF2cCADd9Gd~#VrqT7N_h#&`Cy*~Cq~5LCcTI$RyQ9vva%8}gI0cwJI>r^ zI_E688^h&5=d>5Qkp+$bzFB7nMl7hLQw}L|UET)B9{1HOa37bU;wf~S+I$ICP!&Dh zWsVSsCgb)QNUd%W@*JyE5O>;ab+Jo3%F*#V9nhAjkC3(iOJf*EYmEdq4}MX zMnZit%~J-LrQsx&oz?GK%yLbFDtkTDpfu0)(L-}8a~gDYg$?=cSzjEcXJ7Qz^Y@^A z(FcK7V$bA6I9`2aLZNVw&hhs<9;)lc29HF)v0i``CoLD;(`D8{x5q{S0PjR4hKTFl zp_2*Ee0B>6eR@+-MK$s7PbVblg8;&W>wBUx$>JU&MPnErS;Tpg93|N~Q+LzapogTE zeEz7yl2oK*4W0R1_0FsJr8<3a1Efk{Q=E}OIQc#-nKjwPXCoT|^a0F>D7@o!be*oY zXih{%mO4jnehcC+B6{xgOc0_AfLt`6tq8RLzJtJmS1OX567q~?2Kh;08;ojE8k(L8 zwDN_uurmK7;Ik>bYsqlNBQBksv0F8~1@{e|0yb+n@%S9v0!YHaF^>s<~8i%Ey)9(pnO@;A72^BYYR z$IgGg9svvv=F3DwO)LgR#B`JoZdZ($-j)@imqsa3{bc{z$n!@+EuuX9D#S1tV)qm- z(+-k-LYsTx&3=>=*oGi&&|q7huvzH*2ttPELNMoStfx$GIwgRW50$TSOiW^NN#pm` z!-kUZ7rSUY=Q~2tOz#KPTR3j^v0?=wl)6%H_9{*ux>4(FN)>B`QjS!WB9+rkRm1HD&kJ^d!Lj{nF+(~_qj6g9V+)Jdho}hp z^W5AvXY@~HnAA$RD!`;@pCfq)M~b}6fArM+pF^Af_(FaeD3G=0LMwwpOB4_%f>wz4 zdrioK)>4bqUMnr02sNDffSQg@3N+g01W6~eN+RJmY_C1fLlS89_Jc}C(w+pkrCgR6 zJnt=HS_$vfCUb?TvNC=i;8M-|%NWNVKhQVCWi=g!q*;xnebbA=~c@6mB+5naxDD~ zX$xff3v(3-rI<~}dRYvbLtp6Dy5GE_6=eP#MwUP%uY07!2r9CyThm$55*K?8YehTL z1A~WH2BS;=o51rYdwx&qbc94v*B?lde97J`1)+F&p-h5#|41?x8P@O{UQo7_FVBBA zONM8w9!rYZ+{qG)mHN&ao!<)Bm%R=P5i!lc1^FMhH^F0tzG&1;<#AE0TbQ2ui~v5! zFwz%Gt6QuK-IfPn0fN#kV?~PF&|_4Cmyzw;&Fx z9UA)`dPrf(e)Pnkdn(HYa>` z&unF1C^=||lpThY>&lqdgqEjby}Nua`a+8b%{hQI*qOjFCLh=oKu{-bVfbb~5lBZb z6Ez9G0-URXF8a#Irl^P0lFycg&tx>T2uTDA{7Q zS_vA`NCUXVXI-`KFF8V6e>o=4Tf`E($TBqqHf}VKbGT8Wlcw?s-X;2u@hGi_W(PeE@`hJy%y{o7=g$`Fx~88PrSkHX|X#wCtrS zj&l1S|1kmnKVN%!!m2em3V@%d1@7a01C`)NnXbsg{4PP#$fw}R1bklpzV*i2=0hA4 zoQ!8IQ-t`ZbK}v?4{LdMF2dC_*{5MlXB$WNaiR^Ml}JS54i2+ES`6}dcvn~7xYMGLDNXzAxq6q-JkH2-Z9LlcZ=e2MS4 z30Hv^(+6~@KEamW4@cZ*)tqOrnH%U`ChLPDP{ty2EXeP8r(Qo_$m34*cnOb4D&rmu zZaF241Pl&L`_CV-eDK*V2{-9{12*0iyGrK=)Q1R6JkAu%y+P0;kkU`-R0LXRdtOJ2 ztvdhunPnQQx$#@=?1Hvrvj5!4nzd=dja#)f7y}c2O+nG*;M%eO+f6` zsd4fQUl`uT{TVkP!xBeip8Qy3z1GXlsv_ej_1$ou2#rxHuorYqsB_#cB(%b8%m2da zts(LZ!gqXxc=k}>ISo1aE11VmNmIEN(7x}32@zDtycdy(xU4jrbzX|FSsq^d?Zqk0 zWnP#G=@zZ3U@}ATOom8Xx?9-ik|z&!@6^b7AlNou4xdx$@1+Bg+N)_WEaS4aKd&o& z)Xq41>$Q;Qgy68HegSFZd)Ul17i(h|gB2b&GIg9?gg@H@ejQZLM>LL5y_xHwBqRvr ziWQ~-P;wX$DKqR{(y5$zjZIm(U(1;Bp*A7<9tPaJO5@hH?xRh0V>;5E0RuueqSNHi zUs0DRN^{>v?$3v@vBop%dc-!moNt-=o+PuF6x(jizP3QFEokOGZ!6&S&gl%p?T*e5 zyK}pW{c*IR$t$E@XPO0=zzR9%+f$M*hypVq3X=lui#&R#Np_r2+I!P1WDdn@oFy@AvKQp%*|(Y7#)6t)|L-K`;sMe&B_EaCH#RH;9vn7 zz7u6IS5y1OWox89py7D?ZMpHOs!5!+3ywkX7s4~91}`U!i8&2|&e-)6hoH7U2QW_C$VV<%BB=nv7>vj;wZc1JBSz>!BQJ zGv{>sqW-?<;>qR(0K42Lxgp~Fv)WWwT`K;>-(foT2ti zV{Y58cLL9lCsvyvZ{2y9I>Ns&QzBW>aB~@H|ja#{@e8efpad$>dkib&^wlhw=!Hm zN|Py$io49vM7N-h*9Q#KM%cToibv#F0U;lfofm}JG3N^&(f~7yRo*n-tO1lBF=KSC zR&>k;EuOQ@ht8t|$#aB&h65N3CGb68Q@JPxdGCT7?)G=&YAlIpU7c5#mUw+Z>On5e zcTyg40S`^DR+H-|f?}EIok7;*P2x-XjH#xXvPp1mt=A>4It=F|e9cA>EOr3e8@UTQwa+1fQz=Wsv= zn^CRv1)l8r%?uZp?B^r58vCN0fM#JMcIX%H9;La_{XtA4l^Oqj^3|hJ+?oh$*skLt2^bN`E)dht&Sar+t?q-o`Js4YSiG^)<_W-Q1_FqXZ10{YE#?~}OW(GmJyupnWi zjoj9xQuoc2x3`A=_tDG`Rx9i7C3}W55qvi>AV7bBO3c7}>nm|}BYn?Kir{s$;Qx5K z+-LFpsGW+#?y6GcjByPDk?1jj)9zN}_0|vr+wYP1A|K>bxjqR32t;0bPC!aO{QHPR z)B|dHCBjHr8N>+VZLyzTis$Bv)s7IrhO;Z6w{V2mJXxkr={t1wvq_HQgw1@7tZX=_ zflwh|swF_1AlBRN<%Vq!>PxyTD)IMl^I~-55-}dbyMzTDlvjWcnLJ$~AGP`1&T2!U z%sK)=_zi_Ld12u*(9ntYQu4SSS?`R%QQBKyMr*I`vGsXrK&EB7cQ1lU%rkCVQNM+0 z-Elv|5^26`RgeCTJH_<^h9I*Uhf^v0*;j$B_l>T{0RaW2Aum^dfI^_vh`}4Yn{5+| zzJd}C#dy&Bw}mX92YZT*gG0n+zb^;B!@eQbPXxeGuN_fB8XAdvy*Q9j$Pwuc=6}oM zilt~D`wYZr1VDUki!I1SuLgqUI1mrzSBn!@ZU{O`_&PeZ`#=7?2mLUx%x;$;-I@`m{$Q2(W$?R*xa;!+r3JG30amo9$tvHpp$8!++1fA( zcwLnvaCM~-5!SJ_Jy4X}O(?=CJ3~ri0o4C(euz%Z(q1}QxvK=Eg`Dz-*^Vy^6|&f|$Zefa~&j4LxvC7P;%DGQNd_sL6crQKbo<2&jR@JeL(@ z{!-Ri+X$MSF|#kHPXg67XVadvK5GZ5vM4~KN0~)3*Ztf_^WKQm5;x_RBm#{1!uIi! zSS!pDJV;uKyDHpooZaTHFpvl#Hjs*CQ*2bq^@h^xt>#voPYr7nflMqNyGUu1ZWj{R zYc}2+kEU(kOk8tzojl!Fl9iR^RAb%uP_$|98L;!crrNjZybO2g2C4ArsqS7x+5AU= zhjSY*LqKGS^wiFgRxPTd4r}(A?h5-PZF~Dk-iqccf;W|6-}XTB!@W|7US;H|M%}!t%d^_uwb>@~To|)i>m>u*jaie9 z$6>z^OLNIJI}86FTdo2QlEp|-g}@J(5^4z91UcwViXPe4hzX#6%Q(~7G!oo;g3e3X zN?7jAM&tUveK{Pz>TwzNX481w%b$*ZuZ_Y)Di4L`UUP)mU^9#!t5b4_l^MGp`JV}VXXPlMA5Hir@`)w5u5=Y zr|ltZK?tu-`O|B-FWx+_5?1jTv^=r6oDQC>;B(m*?oL{H+R3tP?Hi*D912c^uBQ z&u}s%HCJbM+W2f?&0gcb_ltq=t&+6^>s0_cRL;=mW)I8}mA#uVc9MK2&-_w=$aYv{3^`LT-df68>Hg>3PEQ z_P47SOJ(}v3$N2N*+mciJ>5orb$fu7*qy|ZoTin`VUvx8rzMD~5uC(omd~TZVmvAe zwk75_2Ev%_+1iRTQ6J93Oq;9klxsX6YtBA;1+@e9mtJYUOXM}-k65o;2+?~&MDHmK%!bG~2O>S0IBZ7iZ1Jw^dA|gwIA>U(d431d&N+rcuF135& zrCqQZG7LsY(r-|r7uj1*Fw+8B9%~2?nqRF(OMmakA1cXYcYlYAe&&dT%i~;m7U=8# z!eU+`q{6bP;qLbOYJenzKzW(l>jECX#L`?x2YaQh7H&zgM5D2i{n2HQ{vifrHVQev z1S~!2;WHmNDT!~okht?rh3jVxmNtkHK+hTgX`?zpAj;S_mq_EDI(q~KYj0k^$Et1* zLVaiJ`NMs`GJur4bo#(a1$2`ukh-k!5DA;SKRy_2QQ|pMlbtUWGlF=){r#C-n)pV4 z;>yUYYV$e3+Z!BYAJ+;wT56(9Z@(!#Z zRUnA5VT9=lv*F=T9KNnvT*toVqC33x zx4~IxuEDpuX|ciMCIRB2;x0?=c0oJs4Byj1BZw+LoEJU!0I2DSdGHBdgt(A(u+(Ty z;WQ}`PIj;$G14{FTMqht#JKiV;!AM>9&G%k6*qko8OrM&?)fP+0+!>8J>U-BwV1T; zxqTTpU9B>)88IX1hk2&xeWM}5+4xvM4~Bo(qQvEHzK#uv9th@WgNAh4R64}CNl zG`NLoDIQ%nei&V`IoZ`Ytb7Z@#ap?wuF^7DJ+pzF!5jZyA?Y|Qe~Y&?K)uE&BCbAA z=cILY$54u>;!<@*_rfhYv@#n?6)apysc=;4HK!q~!r>a(g~GpAp*z!Lc|IGJNZvGU#hsNg=E)TL^$Oh^;1_J1!H zyb|E%(^tLVZelAGB1*{MxajrBmT&R$%eMN%`2oZTBTd&MKP)orSR#xTJMqB#k7I!mb5M*y`g1qsu6|4YV{>}ZM=|FQ~F@SJ6U(vR>m?7Cv9a>0Y0q& zv7H6_KnY~y3T;#6)6vAmhQ1^_Gv`QqYpC7^t^`kE(n+?zA1r7h0s2s|?y9sZpfDqO>`~;)@zDm7%BBacsK5OPWIC-bX{7%$n=26aB z=y$E=iMeAxOLRyYSk*>oW%eADuTRp70sX0jhvVr@JKB<-cFmJVI_a~~bM6NeYZoot z35HKRLTAGV2Pz(1q4u5xG+Qx5{Ix_o3<&|NiYSY4dk*L)1-C`e^f^PKi_WMmAqIzf z7LI!L8aIpX=<)nX7+Mgh+$0Ux`&?I&?!K*M{z_sJz6q5A&&eg!ru=k8``yZDf8Cy< zG(Z~D3N%k<%mGz=69`36tvvNDbl7~Fkzxl&FTwyT(C`>JVxFk7p>NVmC_$Uzffww0 zN%QUSQalLF-PEC#6GZ(!_8i`>@F0caL6Q_y!yPc)e)i#cHAz7Jbq_JxDZ$q-fW&d5 zTvhHFU!_HA3)@-1$|LQk4Js-^gG39kT!AYl=MsM3d{8J`5X1pbL83Ky$X<>XRQR@u znwZXf44eHQg;Pi;McS!fV#?xwF>XgG$6RmpR%6vzg-H9FVgt4PiPewpox|;QN0p zx7`}{-r(6eNhWH6e=%$E{9LaZICqXFtqJ6_r^ z!-`uq8cPhf&OkOi)jl4l`g}-7(9J4{&50w})C!OF9RrOcRyzMX!0UIi8OuU~EVwG9 zjHlPG#l`z|eQ#Pw;K^^6wmOVoTmAUP`;~-?84B9zWc{?;KNsGB0*L`$FeZ8ylE$OU z@_b*Jwxz9B3%2zAaf2nNKMa9~xX;1Y)cR*3I1_-s($q%LC5D8961k}I%pyhrTf32h zFy%Q7t9`YQnTr4ge1P1<M3}}9_60HTeIPN?&V~o;r2b);b)p94Nd>EA#Do5y-Yk^w`4E*@KP4t)!!iU_x>+sY;{ZRsEr5 zfV~xuP>rr*1M^i3+CFuU9nWQbHBLiHoYarhw8q(EzZ~uqK0gGJt!sMWNd{LBLH6ll*^?njKL^M<8iR%B-V*8VJG%kcV|dEG{tE7!$N3?A&Q6IhwATk zXBLJNV2c6uB~7cEI+^yZVruAX7p^0Jb-WmAjfR@DcQ6OsoAp))rTDrTbYDdrZ@s>B zX(GIRw|f27Uv5IPew_Q469|h5Z;v8?NWiP+a`>f#vw;|3?<#)p8rE;Rr@LZm((sF+ z*}5jTl$YiaPbiE}icMzEL$5zN@O~!x$Z5A7bKeuM8NF#kXn(X!*8GKVRdA*zyJ(_n zX6MVnS9ycdbIi*4$%*J>UeXtI9+&nDJL!inVFi3WX%!0-w)BhCYOPgc0vdLwmKrM? z10GNVtUhEHu`Ip$mY6=q#X5K4x`&Z>=VC8Sz3_5=Cp{c_5Bi!W5_^zfer1vM&*=JZ zBrJYQsOK&4;ZMYWTh2)oY(Dn}Bse87`io~Opp^CEh0(CEi^fwfd(-!h4xk2iZU@jq z1W~aH@SQ|wKATmsKmNq#tIAAn_n0V7{X@yb5%U$~5oZ6)+mI*=ny??-PQ`I-!zJg# zn|xQO+fGYo*yJ4#yb-avQyg6cD#ls3qx8O}OpT+5SJ99M+@}h!x~X7EmRJVarTkI) zRDsCB`Gn+iyCS#4YJB({tr8MPTOru4^?~G_YclSh#fQ?~DE0kGEaG4!(tb%jB^`Z{ zCPFK|Zq7qr8OzorEAyul@o(Hn2sCIOVrp?z#mdwTFowJAhp_G^>jWfrgsa^6HSQy{Y|)^eu4UNO@ZdGEx^qveMKS8xPy8TEBVR`zJvj z|5pUNafgMzhi^caLlhuJO=H4w7FjGbsWSK;DuCW;2A&w4$2{k=HqJT=lbvYzJB08Z zb`RbC`M;@l8u7%`Ntrq41oTo)?!e-ICzndet%n%Y0^?X9>GcoQMn1hn5C|Lr#UCdJ zW?wT6gdVrhJ)}6)`GJ~%$=D+)A4`~tQY(V*U)eq{;i36kkyrWk4i|fv?^D0x3bNzz z8-A2QA5@0uN8g~lR9^{Xq3Wm|v3&RqkCwkU<9eV3qYN4yzV|~3N6}iU)dqEU0~`-( z5UB{=%u9zKUE;b3I;DpW?p5$VA0&Va3%LAiF#VGo=!IH?bxl@!O!HgYJwK#79H9gP zc`#$oIq;*~a4nmg@9=cKTD%lD@JIX_@f?MczrhR6)N!WlDfeMZO2dOmr`57?WP`bc z*;kV+6wJOlQkOxA8lb*DHw!@j)69GYSImBQw3Q3!3ci>xyo@SvjDn6AV~4i(X4r*9 zVsIU3E3StQU$Ji(ze0cSQD!^dz1p(XART9{oXKhJr^Luo$MY~^mpUBxjf`r(IZKYX z=*^>pg7DWGfNeKW8V{F!Znt-)9UrHBA05=>2%s<;4U^Iuyu2gq6hmC8vJ6{3nEQ2obxQ>n_w16IcR)raYlig1b(0R8RP{vkj7zdQfuJOUAN~Ho z1#fV0@c1VlUjkpV*_xrQuLldw(k_E95H3~V*3zDB|8fEbzoGZT>${qN zfEIMR6}9B2`T30h`s`6qAU383Jo771ep(}bX9~~PToUtBQ%5UZRU+i8m+0k=6SVTV zk-7<>Tc==q**~ydbs}LjJUFKgMXlvF%#LltZ$@{M z$GqOm#{xjOwt-^xOknsVm{cXsgK80PAsG@%lgq}~V`_-P$nJE`h&?7lB)->=0y(+n zCpS^dbMWF9pHL%BB$1Me(}qyb&1TeqRV*w1RoB(5B+cYiAX}ggVuV5(nebBg-aG%_^boIK!{a$4w>(tszJ!)F7|SYk{cpsPi;>4D`K? z5Lu&$4a@*ubXIiI3!~+g*LgZ$f}PQZ>%(Wv94C|;ovYGdfqrOxL;$_x``kA<;WO(# z#SVvyMCv(Us2Z=GI2d~&r4tV@3tl`}LT!NdPbZ^=(?h{13U$+_ARl7+pjf<0z%#+A zKkZ;A04u*fLg1LNHC~j=yPojCzl-hH$a)tyP4oxO6?BPh4gQdW1%wEYn%$bKuO^fy zBey^=7YZo$SMN_|D~X6FmskdHLuXkKi06z-4B3?c(7Z2;q~itp0ojAA1@m^wNCF;L zr+7Dpw>WQS$<3DAZU=*WY-XpYn85s6YeF&CDE*japTH5Tof*#8WBdRf_A{z8$~mFyL~AIg z*BQ@$Jo_unXlqwEtJ34|bUXIs#I=CTg8GmKh@w37a-edQXLr3Q#j?_BL)M{^_y(;D2E2YFiothWgz%!k_64mm zxgRko<>c8;CNw>I04C&`1W5J8SUjx-{XFN0(O~x9mJDYusH?N8d^SydVLJIOC#~6c zZ^mKs223H5l{O`rOR$qk@2K?;KOB)y2CPxun>B+rIG_84qxcAt0=`ciZRvTIrurExpS5ze+aLDJjZ zqhjdCukdo#04Smd26$e%ZVzPyn45qpo?;i+bT#;(_m((eHlPK*%N7iHJ_eBO=DMZY z(33z>LKvSMg727Jb)r$koQMR%5U;vvOb7(?Ow76#>HyxWA)@6MbgWus+%xB}P}J_e zH(B-uMGy&6IORY~$5D*w3PG9`lvhTRQM71PE4#ao>sPB#uI_Q)GZ%jObx?zmSVAZk zNI|bLAWZNpMco0={4oms-Q~uItEc{5l%uUIp$A=Hthn3U(zSg@AZj*6lkZg|_N968 zlboBn#*&)GwB4(v&&c*>WUo}8yIQ#~a_56KW2`E3Ksd+|hZPEpbg22zl0l>lqx+lB zCF_73^wCl))gn5bb&9~!iy+|BP_p4_l1YP}<@MnI5o+^59QN$!M=(RgZ@JuG;v=s` zSQ$^uXY_I8Ki@bc_Uy?4V7;JAL#r?py1<|qFEW+@4RP*J_kS%f$IDUAwv>qNmvDl8Cy#&;t29?<32^nBV!I5<4ZOMSD0i|D+5dBcMcAv-O zoO|TY=hB{-N2CwsP`=6T4P%?n^}|Maqh4b<-#siISnhtcMd22{SXZurj7`yD0IKt+ zxhobAbvgj_CRff6s}zKLO*M1q%{}5+RlxLM`jwJWuQ|Fx2zEFNzlD-lJvRhAMmLW#oGLR-NgqW8m=U<9`MNB}ldnsA#QD|!6; zVL*VT93)**-Gv?$o#-QMXk~;&=qreTXvl!l4RZV8Jyl(03YdSf?6MQ; z&%}%InjjCnCB)!oe?{|<#aE-Z`TOL>T2zq9cPjlGD_E*`Wp5v1NP9mb+2 zAnp(|5s6s=nUSSXi@IMzLqw$L^==1ej~AXUX{) zpoT*pQ0p?tD98Oo7=9BqTf9PfH(OJNL=;^52`DEu@TM+m{I#a~+lPO?a0SL^&Bqt+ zuU{;(9F|amq>U>ahxyfK3+Wm2q&HYjXa6Mbi$)&Fu|!PsvxoTiUuww%2g>cTAa8Zx zsc&)nE#dxOG;wy6j}Q7F5TpNi|HJfNRx6#afr}xdqocbKT0kE|{9L2^4aL8BkL#ph zivLI8_kZoNzrPOpIDwX{rKQgwHp~C9G=F{zk^=0s#X{wO&-DMp&vV}&MK7QKFYSD! zDD3xyH^VRQ|8YhC<^ziY0GvU7Hv50C$lt%~)dC3CnC#SX|DyZFoXY_5YtoncpCS_V;a_X#|-# zx=FQ$N|`K_sV5a>c}f~7?pAIfs?yHu@p5(aWmLv?#Z6`RIy3Q(RG^xe%AB6X@AtQO zH^qf~NR|IR%p<)T1v34dPMtr=7DYHI(3?;awe`%wZlb)n?o44~zE2fJb{BFOs%$7~ zo(r4P*OwTt-(w#AD}w`*jg8B`-fDL3@7DX9-S@ZV2;~*n z8GfjAA-@L2Uc01`TC-~bN({W1FN3>sH+y@L+sf#&gx$hZB;%#e0z;X3MG&b~5dZBT zh*XC{Ove8G;~YT`cHhp)NUe5Z5j!Y`^`S0PUthlNCdj$+=lxVtW;D__Prd(-ZtFjf zq=Oh7MxCR=vVmXkW>N5?DMXYsZV0z3mmz$~?wr3q{dY{aR|`15FXeylvp-%T90n#W z_=VN@&vnrz{#IDiooPTkfNuQ(Occj)d-M?IzYo~oUkN1ja*Zkswfl`0?Hjl{~GAt_hzAeo>>ge7xr1 zo3p03wedB$FqM9**`1`^xmZ_K7|gFwZM4OyQq&vLp3vj|+YR)07l}xKb?%laC;auP z1afz*!xlT*T3m2R>mvcLj?N2wqp1mQs zB5NuKipFtN?a#$SnNlGWJRf5-{#)C~&kkp)!jxjG@)HX2jcQR>5Z=ZJLp@UPYD06F zC_tOg_3iiqyrKL>LOCbwlNm zwn+QhuPn%5$V%vr%qqU}#ko+USGjD!n+GJNI$#;2*pOn87d+k^njvS2k>~5Ohwr6e zO@B5kx;pP4iCC*FzMT8;j(ft4-hyn9?>w{8WTx}rR?iT|Vj{*|smb+nm3$@1<#cVM z--HH3@sZu-kKc0f|Mg@n$s-3jlFEKt)*-QvNlR8GE^?Khzm6BL&yg1IjeYL^cJL|u z``F3LxUaqP?8;tK;n9gmeq^731kdnY5^u5opk}G^dOyo_Qo9g9Q!?G^`}FT_eG;8j zKk`G^AwDWi9TBW8*O$M$6`hYAsatpO_;jRpcXHd@M;1TI$ZMy+KZfvQ|Bb!=p}pi5 zE6?RcyY*aEnhhP4^48WCCtUMC#+q&Xz=tyM>qq_iJy4_|ANB^XGGdKp@M6%=So!RS zDKpC|T_x2L=kUZTYO#DYt>bPcVUh&H!Gc5UoP1664Zo&^!9ysvdPD1PK7P0c+T56K zE9li``W_Cw{3y8-2RcyI6Pqo|3N@P<>z+O#DN?C*4nxx_(r6qn`%>1+?s4mCt$$t& zx}8yL_rhKJn{G}gG}l@G*)z8ZfqOr+)X)2Q?^uwL>Zi2mai4@V-{p9rDSfit?6adp zwaapNN_gB}BSG1+>xV}iRSHqz-cIhc8)PVt3$z#MXy3BNr`Fr|Jt8ouGy&}`iPQ0T zsDKk0=fQjE0BEw$p_QksAR!AYT&n4qY`ag7S4Zx$9#Hf9Bl8_+He3wvypOY=Eb~>? zpIt6rMgP!7UHIKsKWeO)D$& z8Pkg38w7EXZNmC{CSOn`Fja6_2nrtUnnUjVg9MG2nmD0!G0^<@IopU)^rNI;=!-fj z<{)aL3$aj38M|@wZDo`GQX5mZO%il89eoDBrh_sy9UYw@Wpj6l%G6NZhX{t+Hwcl6#hD$6gD0RQrzv(*5=P7zy6$wNS^7kHha-=nD$!6PwiB}@U zyYmXu)%8FwHJ=a^EMfrC+&!@%^&U{X4aG`Ge&9%RMwBc7V*5$r8kSk%R&yw$s>!PPgCNNLlWt9#Gyt=zL zbPLjnbO0e7lRVwRov^*vV$f);qjFnn^W!PrLhcGb&wEDxkLoJFrh>XsKRqwI*Hb?d z4Sg+4MGFk&CYe?1JTBF#(`~0LHHST*Ey5PPnoCrE^IJKa zLiOtT&{=ii7X#6$X$dP3-% zm#A9m+_gNmzBLs!-0VLB;!ev)M+TZXg9&5~e6ICGYCAha>jl?mF{teU-7Nwn;Ml{3 zrmC`-bjN@2pgB5vf*FO!Rf&FC-woRLO{MqC2w+EmQfI>6xSt&}l*k1kzpdUAtuMH4 zsfSyS}j(iEmJjJHh?Q$dcxH(mdTVsIuCkOp&PK{^tYfjDI0T)AyeMI%J zpwx7%<=BrnUVLuoYcZI^b3BYN*bQtFW#41%ksx%6n3n~zY!Q+0Ykj>ogS>QZ*N=}; zck zE*{B|c7e6hB7-9Wf&dy92XDk&PXn|iO^#QMm^_wrUbFc=8we}!nzhbQb2id-x}C~d z&6ce$;4b@am`OBgG?t&$a6J9Cw->t*hQ-7_ZCscH2ExzD zKmeSQ|813Xs)!qkQ{!o8h?uM2=ho#h!IfDuoJ*~nt!z}G)mxXl>_Rbtz-%GHmDd}m zYcrbb(TE7yib3nA>HmkZw~VWDUE94$0jWWEi%JVfcL{Mofa`QAT%+)DJ$Ng_{}4R>DS@GJ_%pQ;m&PFM-RCDB6hzL+dwt+r67=BH-HXMs zMk}oGhBM_Lh8uV%vjF`uyNQ^q&bv3&&Kv6sr?`@7VMN_5Dr0lI^#&hZc2&{os{M-0 zOBVXx?w^czP3?IarU!*{qrlk$5Vl6Lhl9~Ktd8KbzE0HeA|Rp(!J>Bm^gV-mEF?yq z3EFUR{ORn6UJdWiS8oy7EnV~A$J4KsZ}B%APQ-D?qh4iR(?YI2Pk*8fWuB>b<$s+? zAM#0YSHqZ|@7fDC;`4ysCU7|gd;UIgIoyDsQF%~4eg!2RUdwXG#F6(6D(2k%gb{Tj zd@vyutkXtZVm)(@Oz?_W;B+mOd8S=a2dk-H63pV{3SXvnto&X}Q7b^_vzkqhLgPxI zq?pIw;5Y&gzyv-l%xs1|9?quolmuQ^h6aDn`YNN0@`bR$Idz+(Y4@|w(JHM5 zMpj2%hoi{2qB$s^s{L#+0A>vLFy08U5V#)(Bka!w^OT1i8x_u7B-H1~@-a60yS|Dh zDI|0I%LM1YriiqrD2IW04~V7zO4T9GNT|jueaQ{t6IyVMc~zIPQXSyp6>=_rhU*`H zvc;D(14er`U!Ukz*)yKhS@es(@-$N{8hU(`dh6a}p&&R`d)Kb5xxINw_O$ht=a1pW zp|RZ=kI`MmxQUtR4Yk3`Yq6H=SJ%r?!jQ_rIvQ$}-UXwg?kPH8W}KnLM89H0190S65X zj1R!sJmtKL@Zz*ezDWEVhSyoPv*lcRyJ)@7o716cBY%dh;m2M9!64p zdEP9Nf@D-MbN>`xl1upb{oGp?UM>_nJ@q7|wGXr}g#7HAt0bUhuGz5eJ9A;A5Ijg5d|Duh@~+jIJ=@!p?b>br*$l}zw1I;iGsA%I?H(Q6O-_*J@f2!eAl z`4A3`iXxb7Es`ZU-JN*^tj|9^!`|u!z9RGh2J(rF+oJmdn*tlA4MQ&RTv72eL)%4X zQG3h*gvqhBZihJx2W??ms>?~e%gd&t@r!NY3IZ+ZBlXv4BlX0lcgU5Y;o*Q?TmCN^ z`3ZDG3W|!K^hF9Ctr%E2>7H7;s`wK9JTSHSLUF_xjHv|G4#@3|qMCGr&j%{>ou3;A zE3{daSYO}s_+T`J8~EyOG>-g?P{qwWEl z_ESxoV}jkO&+Izv{SEs$_-{~$&fsu>d#ByfzlfOmAUNBFHQIqG%_r6wGj*a7*{^bT^(IqBkQia|jYd;1#2)n^T zC>)mKkFV9LuE$jF&N*r%)*Wx##bu`1Fr02q9{~yFA1z|LRj+K^uM70+a>kd0|H=36 z0Yv1khLqya^zVJ}8Kr_Ok{r_-1?hC0y3V9ThqSj-`-9QuLtQ5XQv9E0}j;6da=pj zb<9S|QgrfRW^sO`*o^BEq_{@rIQJu|oagzz3`vA+6g75E+XqAUL(||PTM&mGE=(MV zmY;zH(!AO>@=+-g4R2o02hX8pEJO_uv7vmf8E6j-Tx@r>$z=TiJUvE3RHYJI4U{sK z%+aR@!Yop}R8Gm+=}YfVRqS;#V?_UBu}&JpPnV;S*&N=NtyTJYK{eC=0VCt{l`b-C zoxAyE@TnsQ|ipCA|M!+rs^zxmlGI4rD zbuti0Ef-l$;WSxncckW6gHE;5tm_VHOQaTZ)op#vHL+pS3`MIZ8g7PM1I*?X@l+ELSn_SxebEEwvh zbl%@qUTcbxI{yfu-`O^qVxY_H7=06T}c zSXZf5pB|a7G&D4YL%(jN{WnU?Sk%hPVWVpOa3yP&540~+gpvH>3+W~f0^PxBJjM6s z@F`>CCNN>gH`JS1V|GFxD^6#E+X;G{CfTFjmoTkU*mNf{{Ow_#o_+iFKiD1rPoH90 z1If)^r-Ke&y_V8&H~)#r1-VC`{5=?Qt9=7ovb(zbCN5OLk{Usi$`*)n~r6Q5dt z$B`Kt_!TAgQ9T)OY1GoWJf;Q;6cV3S$XZP9O;?#@@KbTFeoh=&1#i$X=3=(;*7tix zM)nTpH(7Ve(p|^hk=bgPwXFXJxWv>b6h6O*p$kAuaGK(y_Bl2P=HF9i(mG6kIcyvC zXoLpcVPe)`$;{ zvqeBn)b*YvE76&M5#X6sm=5n3W8;e=r8n^5?1NC5g{C>$qYGQt(c&lYi5h|X{L1?~o`ZoR)4F~@0p6+-*1GPpz$YO@=H-1EpmM$D|)UVo63ISgL z8X}jYP9v^Kh^5xdTmKFQTrpZOYTFfqi`T>@L#CC?2i3lq62Z-KJe%>*%n&7J(NMeD zi{j*3zkvPF8h-fkwOuA@kE`TV$$dXA*(xTc>^r=OpHnG7hDwWs^xte~wkW!etqJBj z|BAL~<5yN%5{;*ISYv-5M%2aB?dZH<3dWIFuFqz{C%P57iF0IROu=qOvy*Rb{a8G3asCdBw%W$(GsG`O9=;&gDbi{O{h^)j7RTlu-(Pk`JVS3F2~{3pX(jE-1qpF+7t6Od=q2o(~fLeHc43 zxV&touiAE#25Kj{O5eXzMpX3?@-7dHRT4h$p>aF2@or((Q(2^rr?m?T|D8OVina0R zHs<&d8pX|qvqjfuXI?!)4q}0gn#%GQX6FQhZAE#9*(bD~NRyKfBUHcSRn)G>W(z=uys zO_Z1==INGe+u{~#?&14wRnEn+a$j8J;a`f|j+XW#{3>Tzq6AfuPqo*p-CdS=DTVJ( zS9NyP%oh632pZliuxEdR`}TLLxMWo%V${B^J#O~-{&Fu@Ei6VV#Pri3-*J^jFFDu3 zQ6V<$#~^ZK1%{~LJn0ZE+aYZs4#Y9-?Z}6jG_BHy?Tc1oCw@F33oQ{cuQ}2{3~lbdZ1%6{ zHIxbof*;wN#~s#(L+YP(TpZ0Jp#UdG|A4ecnB!h0$ zBedt8)k@26&PL%Re6w77IF;lU_Mt2Bk&h`kcwS?G^wiH-9V%=uQzm9L}dAyL$O zq9sHACS#JoP>7ZhyDx14=nBYtI*q_rZAGq^>U{+I;|NE z=UegywYzvQonMtoMd32bsqKEYX{6zGbLD1&xKC4JB3nXcF|+RCWK-qTBS*wzwuyzS zKsn~PFqwgxXZyjd>H$O;QM>VeiqQl4$EL4+GRZC$!h$?W@}<-D3Y;9tAKUQ>&o318 z^5oN?#hh=Ju3;{976;1DAwBXI@mxQC zXpb_>L(70&vDqlI|BQWpj@*Z4kUFqQG`L(HgwW+27Q86aHrX7=4%y~m#ifg3j(?0z zJ`xwe#B{IH>X}%7_1GH`Uw7DOUO}rF3l<%Pv{iku9B0sZA+XQ8Bk{Hj{bFJi9=`6& z$zLFUnZ>8Py4P?9Eb#`EM5)i>g-=q~N;f5?qtLoAK9}Wa=H}#8?o9bK+IrQul*N#I z2rk9{vkv{=x_hAre$&Q?EJs_1c)k?b%$6frbfC!oBsbuE^K!3&@2nVcA(q&n>AH`m zeUS>O!bn}5CIo_sh*?*S#n!wzkQM++hqKV9zcPlTTWH|JRo4|d%QtA$?}As0u>aNg zp?ioRG5ASGX7&wa(Z5AKiSE6;m>ImC>ZYi5rQi05;;s6IMZeqyJX4xj$&T%Qgnjt= zeTbnE6Qy+!E+v1KE?KpIUj{O${u837`eTQv4#)7Y`*b7jt*~hPPE`C6P526m5kQJd z<0A!cCz!-TKLYL| zXn?&?ps=Mv@76q3uCh1d2Kdh%c|+6>L={d&8H09@6>1pD#(xZ(p7H4>G)f};E4CHy zd$3=chE3fdB;qV+f&ODCm5Oeo23uY2J)+rHR+~kZB18tDUhqqj`077k;=g^+iHBmJhnfd~D$haPZuGgJ*F_BAvKzUzKs0_yfr3eI)=UjYb$*j` zo%_is=XWhIdmN8L@fg!=)@80Pcq(2phkc~VQr%DkB77g$-U9fB4}lm@y$hTA5)I{P5%}dMm0I*Yb`fc~5!77m(|KH!Gfx~wO5EyBW{t-I^UjW8RaXVv zXL^OW0_ES+hZ8{V8vavVr26@%hmmU67D}lfye`kW3mbYrRXLEj=FjO9pWPi5w2$yz z`vx$_Sw6G(5*W7<867@*?qgr{9W*cPG^5_bTe-ZGKOdJNf4phw2<~nwlG20`7XdRk0MB(xtPCO&MssGTfO+g?f=lENQ zG%x#y<2j3Wc^AF8`}a}b3Z@hQUB$m1n(Hp0rh*nz4g;9I`~c8zf~P`l)c+XfE1A5El5kz5o0$k3mz$lxR}m=OO3pMZ(F9hbE3NMc;t!z=@-cGn zAEmegGO1CqRa@66a7@5^Gg($_NwV3n)yH`_#A2geYX1j8&YsH8WKAZR{2u8X>^-|; zSVt&2rUiy%-ip}s>H`V6mrqcj&x6gnB-=*nlel&>nS!0V7=jVHJBkipS4Z%oyfJJR zM{-8Gb2aX7=qySJvl7%N1(1#2|1H~&WWl@88|<8J(?K&Mv`wzC17timC*je(+Jt@8xDHC@}o`tD*K?j zXUg|>`aw7`+Xsz&_E*Hn)>Pc8H`nXKqUFfOC6B0mI}x-{26e26%s#N zpSJ0_q!1}6um&4<}XnnU7H8u9s9H~3F z90xY@`N6M}--8`XD_6g<2xvj`B2I&i<0zXdl7ih;Z_>YV<{~w-vY}y}mvF^h_0aof6dn zjvV+qdN2w@$I);Y9G~xydHScI1C-8u_t*C*WNzx1^rDixMQ+XpG=X+4^PHK_0C8LB z9|G!s?MqW!Kgu_VEKE#4zk_)zt-Cq7Dw`SKwj6-W%E>xD21JG*;U`73N|_sAC~xvi z&ejOcY27eir^9UgEQ;zU7l?$as%P5r+GqU~ zXB&ICp~4L<`)aNaGf$$f|Jw}pe_Vi}M1Pz?RfjT&$}Pc>D5-f!ff|~+3#=oOvjuvE zojK@E)7q6U$(oa1Pd_5~ZqnhbN>3+IMYs(j!rz-)@AP482J9^-wD>z>wgWtDph{s# z7gIwk8F1MMd@pkNuYT3Iq*htY$hknAHkPVcw>1G7IvAVkJFc5&s;HMXM$w&q0gRe{ z6uXf-yf~^R<$BUY{bBi2MZomG zee(X}r~1!7`k@naen84Y=Z@IioG;k>7Tw-0;xd;uDKK9RGu<4asjaAzeur{&{@a0! z9jh0;yB%N)*-SQs&AMVvZDn68n*7{!dKDKW4U`nhG=*dl0kPPFB&bj=l||sJQGECUYb$JKMr8oH*^5%_a4p9_UH?+vHyk)&(v4#F9qQ5Yk-oidQq-~o2?JtXe zh#mj+6NEC|y`6H0HKpfISVif8?Y;KtjzdK=O0MQ)3DGomU&{5jj-M@@-z6G3zuVnk z!3tNI>>}Qbs@QMo`88B(;gYSBSwYA_%`vV!EKA#CkNN23g^kkk^3PW}@Nb|jK(ysh zTYdu~h|Y=~p^}~&hqVIM;eWO9m(7tNti6IU2Z$k8?K}CZX^zi(I8>h&$~6fO%B+>8 z9f(-J_8smE+vc+7v`DsT8XSFH@lS&te})mXn)r8bBPNFM$aHtE!2l4!%yy^v01JT? zOq!}o=B9dIJnu{GFFFt8-}wl!>o%)LVJ;=+2jH)8h8V z%WScgsUTloQGq*JqG*K}pS5F%eObULwDL|wHT2_?S_LTiTh8TUWuBKkZK?ekR0R4! zzfWuSPT2ocw@FGP_-E_{&l1!8ujQ|_K(sC$Ul-meWZYW?O8gF$?kD7{6~pcC2uRo@ zh?rDIUKr2Z-Q7^$JSXT@#e<)xy^4tyojqD&+0KM)s?rz7^Z9eC(_Ub&g=pz?RjTk7qawr@V?S^fj&B@`06dw1;7TVetV|Kq*e0WzRG0`LI8f& z(PB0*iv0lyXR=IyYX>o$RoXWz2aEw(UPg=Eya!G$IaZf6@F!q@WrW>XBCtY>8dDz7 zU*6RO1f49itMA~(pcK`mwg<+L@8pR?i=cSh!D}^3AMRsd*-keDI^3W{Vw}ycCx~8u zw9@@(G*|t@R>97raop{~32IS$Y3ko+uMXEz$(q>-@5&JyGr1vM$Z48uN;~S4t|pfy ztG%YjY{M4CjN`M?nJt-vbnt#Z9GF|ncEM~xI;Uv zVC39>#fTpyYn-imACHjjEL<`TujEi`k|J?ijQdw#H}uAHJP)3DbDTx-1E{LFJSPmj zI!@wdVm-?+SPPp5>L zOh&{uSMh-VoHrT&*M^G;0;NRJ;Vf9KNW$emhB^QF1rR(Wfgx17P9P?oEHhUJQ@CXO zwqHw{O*5-4`mMQhz*f79egq|Sb(5G0%ospn@8xKAwCvUypu;iR)LqxV?7cjl`pLZ0 z6hID!-m~=$E(5$u!IY z<>%m&+|y0|K$*R|vw}ucBPA?bF(@dRFukfMOA>GxgCy>_5XuOgh}g{5s+L=t1vsR? zoh?(3qU?Pe)Wej@gL9$@kl2Dy%Wc-5x4PQ&_wac}yE z=)~EhBr%^^!%``)tDhE%;TLr9^z|w0-t4e;3qp?BhSxc zzuXIiz0(m>MMeQtGi7hMn?PF3xh$d2wk$~=6Ddggk6HHr`vvwiezo8_aASSsLh-o( zs5=;lnAP47{ow9N0ICytFbK~CdJ25H8&vPh%AUUYRTA5*47eCf2KBX#PSnkS&Kg^u zr<5%C6PQ5&3%U|!Y9HEqaC}rX&ogd1Qh8R1%A3S)Fa}MVB`TrM9 zw|}=VfjSK6-*WEr+b`SNqRP1K-Yroq15cIN0mH<&M=z;22A7yOK)K7FA*(yUqXxV{ zIaSqt6 zN#{DEX`(NpNFFPTGi&5!rAPp}1aZabYIodAeEhJxyt!E;L=07S*exuh!qtS7W#@P- z+fCDM+pu`z~#%dT!z`8wwKfvt$o#qpcbms@W8&OHfHQ@>iR2Gh}8IDfR(*)mA|UO z`A6pRSE1<7ET)r<9o-=rK_S`zy$CS8b6TMwy88^sjXwchg6VGT`}X@>iCK7_PNfoX zv9}2=c-RG?ATh5M*4F+wCYIH}{7+kZe4E(&aFXaadYStko_C4sH>cyu%~)RiMPwNWBDpErr@wHTd6PKOMx#d8RJ*e+ zW)8r05sM`)q(zUY8_4?65fMfB8i%x<(fVlO`^9greq(P@$Y8SsXRd(AG@fADpH$@8 ziOb{a7X@iw0dkI?CNR$Cfx`H>_0wq!`d>C z`1m7xtH7V88{q!b@W5ra|M(90(%!Aurmh8XY;u42cb6TCB<(n65t3iK@SmF*!iGZo zKuMwI^)CjSSxX8o3$VR< zFzh8>@b8)|lV2l;97!CfG8pKPDxQ%UO!YVp$|4Zwx}WV@)vkZg)T+Mo&Ux_Yk%F;t zx*E7_7MGS{M-4(tMQ^VhWEa#x*;uXPay5V9FBZUEbr%3C`bK^N;aAo2M+LR3vNyxJ zLAvo=T>o>%{I8uuc{kXL5Tx?gqhJBf@DsG)8b5u_&MMp}f}Cf%Kr;;OabHVzS(hIV zQ0&^BH|UnD)`{fMHAu`jw5 z)}D#%ns$%<)bJ;Nr8al_9`Gry*FI?G#`HeY&X>HK8=x(y-mTt>(Pl zM-r~TOO7XK5imntrtL!9H9%6O_4cV?Ykna7zCSa{RY6cu7jwPj6$sdVv@XwiU*?xY zjl%6rfCxJR&~0Y_Vl0^EYxQaumfO}or_sTLbEF1=E5-kVL% zR%MmrwVG4|-p!A|Rs0#)^0JIOom*6j%%@I1fBwA0>(UwgR!s95RCE-;QD8b=poc_) zcdA@sntl*$&t{hV20++0pG-53-B|#%01-kmA&0*1{KKp}zWe3~({B){nF96tY5LV< z2gL~BV8sE_-s5pX+ZxpI$Dn;<%Z7G)UI^ffmS_{;`h1~T1%;ptwsZW!Tc zVE(HF43msMM6-`NFcfiOf9 zLgF>mSFc6|B=v0&*_ABE3-SwOj**?txrmdyq`jflQi+@on`X#uDVM0zb3K0_X2WoZ z5x_Ri6dfx&wexc}##(d_9!1p+7@O};^7ABR7{wZYGQa$^@T+J96-tp9@OYSPr}3CV zA`|k{k2YtZ!bN{_N4ELHK+FzeQ+!q!Rtld_AVAj{-zv{NwKjj zgw}gjB}h3*`m9A8uA1m&ip`}ZgsWtVSrzJ37Q}#Sbc2m<^b5+ z3&Y*%_|k5pi!yCv;UB$(|4)!h z>vG4Pa7pz!P5FbQKpR594}6TY-?vI)-88+u4dHB6Wq1~$j2Wmwc>d@JkU>_4Uy7;U z299+oD@9X_n1UHCF0TtmDl2b5fzui-wOe`?J1%T%^zF^C`4E7~#skQ-wxRgbo2n?j zB$qv4Hzf}q2r=A$0P$9?Lvj^3D|~)#nm!EoGHEiMZ}c1bDiQGF`GCeTZg|G2Z%`b# zUYVQxu1P0vSK|>0m1iH){8k;7=<-&UO0l>NQNQ>qq)@W z3~|~{P`S;V(z{*S#?n;`Mi^`T2H&vLwWgj^>|~&CyP|Ip@3}`*F+Zwif-^5^COFYl z)}$(%Qf<(YIwGpn*)%piVaDz!xp{Cb2mwViLBw%9n)9y@7ZXQ(HpHw^qoM2tx z8aghxsEEQ0rM-tmN^2P9d-NIz6@@ImVzE91kS?8fPo`0z?yN=@nK9=!*&(WCg)=@^H^;2nhZ2q7n zg^B0s_Af4*S>f`%{wus5&!lWRQRzT?_S4FpI0NshkP{sAU;7g00ESK#)O*VQTPIVb zVc1(^9IwCtopnbNt4Hd~WrfY$>f^>{2aJM8(-b>MtXuqLvmFDX(*`#1g?o&I#g!yy zr?_HQ15!`%?-z`ner)7{q{$x&yCUflP*2DTfW04ueO7fl75suLkW zb>B(H=^35JSRnHE)(>c7DY^oTveDXf9Q|{XollhxRND^nSsni6xb@acH@?0k?zMG< zY25+ycu1_aO-|^+r&Tn@_MVXWK1hM}$x$XdTEhZlpF-Rql@u3V00G(c&)b```pH$A zzbO~l(gUmt0a5#(lmFs4^az7Qfv<`JO|T)Uz_U-9ZOHI|Ap9v*yj3ioPP|Dh3*ZrR zQ5Xg1KC|hJJe-xUK)WiX2dK;(cc_LkJFyfRaNFB!xUCl~z-+c-4Q-sn zY<*Nzf|5&KRODA-I7pOUz&GbPT;`m17U6m6SpK`64E)!nabY=>mK&mIp|fPLOfh7s-O^Y_5&W zpI)JV*JpKXB*NTmGA5L!nf!=I{DqwN?Q7`L^H0FUL_mFmJfs4P{3-NZ2JL-+Mzjd8 zBoo$PJrMX?;?ZCl3mO(f7Fp2!h}pCqn-X4CbI!uhi4*7uTaEjGNr@BjNn{0ZyXfu8 z8A%bTiL?`z{UPNAHEdpFY^RqtcpXyt{tHy{-9t{L!L>P`+ZhG{={FD&UkqX*BUJ40 zI?_r_eYYRl`>m3XXRloDLkWX}5Xz4B7Z|A1d&O6=G6j*|VVZ6Oo|QYy;H!yv92wj7 zz|O&GWRMed&36SO6yph+^X7=kJ0VU330;I-SwTZP{6!Yl&yuf$ID_2?&rn&tP3UOO zAU&C2gEaMYRO{V%hS;;?eBYVBr$7zaHpC-yAlPL<*I;>U?REdVQe}ywCw~Ehp7Yl^ zC~YAO%KNJvmPt3O{k5GuFKOO8dMyqZ^mTdc)n(>Qe~mJ%(w=%JKz!zkuJok8nI5w0 z17&aF#*%J=ThorTUn9_m1E00*t$Xw^T|Q@t7qtD%3)ZiS&h&j&aShgxVX4T!PBRs& zJ$(ckwFxf*4(83>h(8b88Uz~-3Ig)`cy;6jMSs{An9&S|RpZrLADrWITk1(S_Kdjs6hK2CeeO z`y9OUE)ge_!0IYJ_7@P`YQIrOJ;42&pnC#j3}(p9dVlWV&NnFgxUW%I`PUI7Izc`Z zYj!`>{TPWO%A^T`6q#sU{hY0uwn1Ob?>z<*9^Bq{iY`}yj`g0Q1mYqoYoO%YARosq zgssQWh~{Qx`7Szer7oqe?A2cugv=vjirO*;$}q9IUobTKGcs6-+{Y{)<2x#b2lD-} zsXcgRHnRW*^7-Zp+Wm=$U+<4`6DK%|HyG9}f=fvAy2by~=p@>W8}1-Z;On9%-{a}T zNR3sfBj-=Xogv<@H6L6+pUkBhM%p3xkx%BMLLtdoB&^$3$Cdg; zl!A_5$YFQxFlaWc_9h3*pLMKNFn0~ttPp#%*_Jw@&pHE+qF{(-#&}qm;TqlO{84S{ zP$eOAJF>uo2+rsr;aV>n5<{Z{{P1H^m!h6@89n%zGR^x$@SbD*;1KQ%wapy-1{h5<0A%Sa2+EAt*^Fgvi)xHILKZ6gjvqa9YdAYH z{*aX}4GYC!En0ldq;$~R8eENFd%5ETKoJ)2Mey;#{FU@Dzbj?hJL2Pafd0`|=8cBc4?`swWwPpBt`stXJqs7W2T_ zI_0_AXeVrQwM28P?7$~o2`}7&te1xMMeiwBG7g@}kw00w2vCQ?q zppJx#xAPooA+u^UC1mi8hm0MMSxqN`Kzwc8*MfM*p42}H9P5)yWT;*(r99Ge@Q3;R zK)RgVBT#fuOU=7PFYZN`YQ8g`jKZaRmZMH%AI!u>@|Z|?QE$NVl3 z;U&I%yVF3^2lZP#VN#KLuskkfHj>#oYWZG_7@uGJrw3K-^y~lGDi9xLCMUf zsa zR!p>M%2D(rEvUsc*n#-Zut3(vDu6Lqyxo_bTl*qyYH4Tc4c5ZNz#?Ag{rsjW;oz#7 z4foPjmq{E)QD^4vIDQ+ieHZp9$Em>v<#DottzKCtI|D&iK*xlkc-)(shC)|AL#l%k|ppa2ca+;{clzy-Uis@#py0%k{F*uicG_1L%VJQD>+5bFCHO zO@>5Rh?2-GFqoDS5({MrDHfvj8$oR*` zM9DHTN^TG|l4xs*IIWC`%+5YGkwv=N7JBjmBDO_wVO9}CBYcTyzH*b_0c~y~?QDaq z54(I7*jVqG6ywk{W%{B%t9!3mWa#(exns@rrl2Q(uL^Cj|8W(%X_3;Wh^CrICpfPjdxpsa$H5E69PNE?yQB$9UOz`jM2CJd9b_1NEuy>br#5!6vsNFIUT#2i1ZS?%qVRn%g`{Dsp zbsOu05q6PVT^lk8P-Gdgeg7ch9@Eia!&Ray0D>v*pcI?*|4`>$X<_qys>&fwzF^9< zf2-R;g?5}>e_ec_-|W=gs12jZN)+Q*7wsqsKdRVpuhMDG-3z+p^$X`=A;$_g_TSv% zt_n3J=(CiYpF?Bw@&E%<+6n#DcQDN0>J>K3x#P`>&KmJR#_zSy z>AB1>+G>UWrZKSxGo?r@5`Oo5)|SM#FgkQ8vnzBiDRTr{+^DpswzhaLf;@y)6X>LZ z;pPf2ShJ%R>eEuSbO}P%ee1nv!iGCeu8}qzef-@t>~e7rs+B6AUck#0<#P8b7vN{r zy)@QV5z@6fiT(=+3VslE2{jw4fXVBx6_i0Y8+MT7rHk6}SH5d_WjZ?F4-?Ltn_eeq zmI=KdVvIrlR15|!3@;@rslevRjtfwG@ztuM%%)zu_w?yKuB zR1Ugx`~eGeM7+EPH?N=FPyeRR1(8Rpzy64A7Reg^*Lg#mj#7q-zC|$)QKy4r2sk32 z*g;chSo8+n54?i+0Z2u~m9$%9*;I<|^w%bZt_{^Jh(4-%2ZG~xy1&>`(@PuD1D9qes z__dM1Ew)FpKPA-M8PT54>-(Z!fC90i_swq$pYT}mRg(smVpH@LUCND$Zxm^H+>`C8 z^_AaJXLm7Nm6~MdDnE#l2kb_t@XbtSMNg%wPYGU0=?x?^da6(l+TBL#E93&@52D4jvsKKJaR`2rO}JJN-SlV|~=`69MpIMoKK zFLCWkK%P!ohhWShb>_1->$RG851U+WuHGwFc{`k=UirFIJN0h5LD#O0$lSdy4b`%~ zK0E(Ns{u#`=+3|DLFIf$(?DwgGY_?q>+@9lr}BuTvPioS&ZnSB=~wjlqlC`c;2Mz= zE(Pxk40+H!WNx@R4Bt?duiP4xrQw;YAGez{j$wbCZpS5#J__s9=nW5{)soolHxUlYE&wV1MKv{FR2&@iTFr7R(?FW+j8Lu3&er${});L ze~%9UvwYmx#eSF<_vebB7e}C{_JkN>$fDJnMf&oE&mgg$%`1A=X4|-|_y#?IPBek3 z9lZ-39hzpHb&u0+joYUoZI9tUB*p9HP|A?Q86fP84?jO)BmK2Cb`7eowV?LJOvZ|1 zHCy{DrOk;3(+R!$MP^`qnC*Z!y8gZd#ctIaHKTD0#o31<+SQTM?a<*hndk-O3dDlz zX{0~~$S#OVn~9Lx@$!BfzI~bS^d}oE!__JXb)|{f@6&7MR}U3X+7a66n?ZYP)Co&M z0oWYIeChhAW+nObU0cq`h*~Uh`lB*LcDCa6jfc<5;!Nho*P5r!ofEl>vLfcqPv|L| z^OW_Y6J$U+WDR#&i1Hf!JC)yZ(@6mC>JwH~KG@icC(hS+hNyxxXhZs$!c9GGzXnf1 z__}@GQXaiUO_Pag=TqpFgxh0cJE#t{(>ydn$(7J|n5$ZhNnRsIUi`at3vXSrx^X@u z=aDb`CT;EnTmq5NR-TR~Z{ADDJH$kXl|Gxmcq*B?NSWmzAht}fi|j#&f=_qB)+Pj#w` z?Kk{>Jk+!cPHp?Hpj{Mmr&uFP#(Q=S#_bF|oKPEF2IaF=H>+QhTG?Q3JYhyJ& z60wluW5BeTbac>LuAs#@3qxPeb=LVt&+1bIStF$F8L}#-2{DunFQ{ds|8=wy=;eiD zL?y0cQhJ7|F;VTfL0=y><><{iUsHAQW+dx_ym@&jn9C+33X$Q<^p)YTdfwzt87@A_ zrP~tRXM(pFh=_bo4ul(Hd6t)j4SgQ1P1O+MRok8B(Jvy6*A$|lL!qouJ%5NzTr&`7 zNc{be_>r2 zB^>19HfHWZ#9dYVLsqg4l6n?JOSqECD3vhEG0tmjclPQL+wmtI;SIVLvS?O|KgNa;BjQPSo$m2w1raD52ccIozQR$dhYR%1>pF?RWvE5}sZwTo&Q zmHZ% z6Tsokw^(I&zQ3Ta=Ps3nsyTITb$`p~Z#R7M-G*1X$os4B(kzak;cl3034ARqIVp)f zfrM&Ng+N*aSbY;h)v8w$U|jEtXmk7%>i|Rx6NU2B_Z(bpOt~+hU!^8$-9E{3S*@U8 zzJ&Y~U;fUv;Q9c|xFhi^a3-6Jal*QKMV*cZ^9^qRZDUgZxQ|^csp?72^NkX*AU6Fx z#%f7n%vIUO33d0=A!A(^e}P;vhL9_* zNy9@VCm^j%?jlrA9!-*6g*k^pPG~G){+QV7f7DK=FT&>bo>fcGuwC$G* zAv7atEQPhIY@6D{Mu_(pB_rI>tBX2AaPwZFGR$L|_cczm2?xTE7N5YxYZ@6x=)P{&dM577xV=YE3Mb2=nwYu@VR-=}EaPDp>6i}n<8MDhv@?B6K-vD^*q+o{KI<)==%q{?;HXgP+;F~f(;91?u2Ap znvgKbulMQdz4c4Q;x2{@wq2DDet41I!mTgQ_VyI)xv}*H8!?56B>YA;7rR*=Tbds7m9}t7XF=!x zu=kd6QLbzIFrA_pfTS3Jk}4o2A&5w$bhmWFAl)G<2uL>w2*be8T_S>X#|$BzLwCN{ zxYpkPXYXgZT%X_H^M#*sGjrefRc9T?ah6+io?X@vkLR(;*CNi$%5qWiX}&-Z2;abb z66`TGe%sDl0G=DM8FpA?e~c=J;(i2qOt~n?qC&3w(Ngv@i9>L2#QOlf9o)C%P&4(_;;L*-~RqUEiC^odrFCV-jrY)GI$WPH!@$xEl~<0IaNH z!+L)8+Dj+jCjew};epJ(xCe%TGt*6=iu38KC7`*gIDsx&+E)>TQ{-K^2(tH>L$qM1 z%u0%juITDvlD)kF+)%6M@x+_-tP*;KbTW;2x9+@CPrS-%YT)xsGj6N24;z0Ex-z|1 zz5eK{fA!!St>lO6*9D2nzm_&|og0S^`Vr5^m?GkB{11OF9r@>xe$?QGsRR56^z)ip_z<2F z^-gq20a?DTFzf)@VnxMX&lf9YU@tdJOML5RaSY+sOP4vUcZmj9F8zA`n97??notV5FPHD@Y#=XU`fdTxLXOqh zUon4}09uakg+9%n`Gv0pm;?zMy_i_UHR>0q$jCj=$Wd$demJSWIDSpkh2CHYUvLrq zS)*`a?VBw4htTrS7s6VvcyYfPhF@sZRF`w{c+(`h1`5RkSk{&Ha}U)=z6dWC5)e<} z;|Ltk>xJ6@ZODgC%SsH^MMD7rW8Ff>GJ1m82-AU=`wE#?nzhx0x zpbxf+eG9E~u00%H3P5yGWV<3A{sHVOrD5B?Lh0J1k40*B!4}Lt@_=#1@?nc!m<=)( zBv{yJbjKYc)!#uOhlgJsB(nFc^lIXQ9bhvDJcm%v*HDfRhP*Lq3N5~Xhw%P_k1XGu zHL-Epsckc^U0x*FQ=4sx``Zul39Sk*ib_IxM^S2b7pC5B87 z5N62*O)DwgpSiSf3@~pB(XZ(H0tj$1yYE);Xf8N7HM9tmwoKRc)Veu46$AuhV$P6G zs|LSHTr2mn-z`+fWFnqbXZX?P-f_Llt)QIob;6~M)xd>&1mkv|NnM5IMc91gtoXqu zwzLhom%BKVHOE9#XArO&)6h$=bT-R%^+Fe13HoV;16q}GL&eh`S=iv3rB^8cVB^d?t>rDbHc zJTjR~iuKn2xNrdrO`-FWl$(goZ~?ino1V`lzuWS}WC*N?Ex_T#D{=?fd%YPF?{M?Q zgl0ovcK|qxi+o1|-Gm@(Sr#YWUkscR(z|%EOQw@Tx|CU0$Si zbF*2wHe5|_C);W?j(q0o%(a=_#oi3x?cnBKJ#4MFBBcJ_&Dv^~TjYBBxYLgZNThAf z$}p{8!M)$P(m!oAXLYrByL;VV*AM@a%3B3eM*dRbA*>(F7p=U#F~7u$h^9K=k(28i zUs|}5hawL1DQS**3{s?WbNB*yiETR!>;tD8nO!1%4V@N%pZ?LfQb8-bv8%;r2h&CL z0fmVKvPn6+|DegGZg)s?P;0M^rJy1`ke6)x68WV5AkN#ZG=d>F0i5W00i zqH{>oax(9L6l`ht^FkNE*1-RT_%4u7}5 z@Lqi`KbK;LJ!E9Exs>O9{ku~yD9v%jcK*A82!S9b!4vx<;g5wq)7=KdUs=xP3zI2p z#zW=lG1q8N?6lbTn6l@56z|vix!v zEv3vQU3I5175LX=n1D^1uI?@hJ|Q1e#g=xLgX;KdzMBnwjh@ey{Z)Xx!HW?5{5X7; z^!*I|-Pbw~@i_}#-y7N_)#5l_@fqoq%Lr!Qz~+le|0_C_ z!_gzdNPG4^UM{GUSItX=AV^c8_{zV!jffBsHZV|+i8eWD@;>djY03Qd7N#l|{M%cF zPFp02w5l~GxvoB6h~fBq(wVoTOr|Ic`B!f8C&KmQ7TkKAzY)s`h=<~` zx9SaEv{}6^lBf)Di~yxsrLp5DOe-k0QKp`+%_E zu4>Na<|ex3p?Z-KIJqydys$0A=Sg_21J$jsYpVwsxfc8Df7!-}6dezRbZvi8`FBeT zG~LEq7w1O=;TI^NNk|sx)Y5E!fpr3KMilPl7Myo~t~Z~%cSLSVT}!_9=(`OTsfNc! zBYq!XnB=GyBp>mT6#*b%+E`g8{%{1a&&x0>WG3MyJs63((?NlIqk@jAnbh>thdz<&P-D^_4L zF;KqHpcySOwych74R3l@-H&6y_E(fhzzab$aPsdk*>vC2wqM0()8jMH|4${huZ%uu z8B=h^4>CP+Yfu4M$ge~`z+ksc)VdjFR$4Lv$qDwGA0q7UF1y7?MBF;K|CjWpSsI{y zC#O1c^{)o*e}oNxMhyS?n_yPZM&(v}Lg|0Z>A%S-fBTPFdf+zArAB_w|I6q4`(Hkd z22Ci!=Q>72nRB86g#EAW#|%R(f6g@+Dh@uCY**&ZzQK zlmX|OIKe-9Gy_q3W^6TDnbutSTxq#QmyQxoJgjHDDaxqEba;Y%C zH1k!5N|_sZ6COPck*so=eua?oWTzYg<-c3V z4lygcG*u$#=Qm^lF(1T4|G{wTrC`0d+#to(=pRn2)h7&lq@=9xtAb>H3W?S8RSC(N zB+wn?(I>(pc$(miC*Cf;g3cyiTq6-KgeRHLsg zafZ>WlxRzY;{StD_W6c`c^){(Usvxl` z+|eOUwJbV;K6=Td)FfmC!~D|r>8N%1iIk`s>hQ>Z^&gD)CPPmaX5K^&JKz0&tLANH zb0Zj`uJUMzs!~Nr0rEq!IK2=qmuW5_LB(BBKYdx#$M>C0!V&Vl#+PbTL5hGyXgwq= zrL<)x{N1~YMQ+ZX{i;Nb5!zLb;UNF}y}dmWH1j64d{FhM7pBQ0DJK_=LY7Ob#~n7^ zY14G8phIHc2U553EM$i@sXGMZTqn$LGD2^9 zi&T^Mn501DKSvdoFza<+i}^TQVdPA${H3jR(NvI_NQN)}x^-OK=-#T7loVo4T?Z6U z>}SIz5cEpp6l+tD(-h?el|zM&OphakBaTTH_zrE3YqqgKiomm+o>YLkKQ|+=j(IVE5?t_VlCk{+~ZTCn6%c z<;v>h{H?e$pJ3+K+w5zo$Wv^!H`XyU@u(tK1d2YYz+mCov<#^Ty(89<<$#INc?rT~ zzFMHvP4qpik9343HA-toObo;4;7V}xq$glzE=_Z=EpMQ!bw|CBCK8?zj3Gd(^5{=r z^X8-fo3RTf#B{#XZD52Sb0>jRMMwO-sj4Oy`tN;yU>&_1Fv%3++#Qt^OXb4?s>z+| ziN=?~#<91zhYp#{^$>0#FC`iCmVMnN3}Qw;{pFEK&yRCU1rwUlVhpV`C5tIF?zP*Z z!9`C}C)QEk>S7MezclwNLx3`lYQ~|f_|XWukv5v>;(lnaTdo0!cToUW+757uHkX)q zcvLSw-VhO}0ki>-#gmS_*gvz`iYDj*igbDt=ouabDmSfbEf8%iQ$_wEy%`#x}&zZYa4>*19y8Bn!W@O zdyG#WBEd+uBnLsnqmz=X!i;;<17w9_ypvAvryncM08dKV+&p)7Vc{(Vw-P8i>AgM9 za$4+padtU~*C}#?YICE6w`4Y zfwF4M*|FL8Vo|StSKay9*@HFdTBcoR)MUJyhsIIf#-xcwpxExQ*|O861)DtXi-2A6 zD`PI=fOCtyfoia@%(tEK>fO(|W?eUnSkX%&1uTgHlUKt1O_4%k!boupU_AVPn0Son zM}7iIz*|5~>nV5&!fWmot8fR^GY5U`BTF0Izk9fB`IJ-xBVmSpzoGaE8 zNh-A*d&M7JS97j+tzUG3f!J$}b+f)c2}4EE>o~1~!&m$GsvK`4U3OsX3AS3N2O}@j zidQIAB4DG%uOC@d+*872JwN5zS5KSjy>YrIQ8!~B>H$*ld+N}A6^DW_>x|59B9rs9 z18dZJcIB|3R(+v@8l2?U$4@*nX*h;T5Kg$YBzw;N!2*cVvb1DXzxgt*0Ur)&unnfi ztG$gX6sJ;3rug6{s&SNuJ=KQ_ww$4?%~g_076*U^=Bj-dii(JLl$@3aQY;rblLe7@ zge6Ds0`<7GlwZ7<`0*-KV2zi$t}jHjDjzS)Q5zTh(HzifDD`;@faxR5JR16vO+-PokXS)^l6PjYZS`g&5>2X9IPn6` zYnP&mWSpwcuq`oUSr!2UNKEK-NAY}c)fTJ^JQXhYOy%yKIt3FLMiyBrVCvkFxZzQf zV_x>M1o2W@Lq{jM=4csO=G*^G=4P=2nZl%Z?8>nl7OP zU#h8@u1k>QzN6vmQ{qIj0otYxTsyu;xyban{JI3pHM1pqe+KY~Q8rKgq(QdTuE#0q z%~XZ_uCv|Q;R4bkeeg?3e2flpt;Fs~iD=Z>1W2k1QQvnA%}iU*I@=So)8!r3VJTkP zE2-T>SnO2|lRV?zk5kw`*{RIfhYE!`)@!_whvo%TaqmwInD_{Z4T!AYsP$xEXwItA z)aCo(jlFuBvv39mZdA-_dDpD7_|^bpRc$no3w9fC+N359!v7nylKQ|W7zcjzX$UXB zOgVVqHK8syW&i$*?)R z+|mFQW8!CkCQ;k4xYCv+Jb{JIP3}&1CAsYJay|c)_Pz=;Z%FGBiH4aQ@KZc6xk=c~ z2qf39qUwq=r&z|VU%UvfE$8x{#(|e4e6PZ!(H=7Etfi|uaargr(XOuD>66|9>0jQ2 zqNoBcrM<|Mldpl3iQ8Lj8%^Gj@PlrzGetn6olVGW(%_bbJ~AVNdEmkwn-wOrsqXY0 zi$cXc0C&62u-{_m3o%D)x91+Gn~kYj$9ath@erlyvW@Vy{Qw6P_t+v19Own;7Hnxs zrx2cHXASPtfu%MnxGK!?v8l6`hubuBziz@=L%YWeAqx%E+f_dR#65nnD&-{hb}g-! ziocX+kX|a8-S6*Pp$a248O6F6wnGL+)ANMRq#1bI#s^tPAN%@UrBv)`PLx9}qwOoC4U_lWBrNS5tI?N5 zLr?2b}3cObRLrUrC*m?iKTx@FEfF? z_)4daRBo+-+YQUL%SDgTW_$tbXm!Bkvyk*|oRk>778W8Z`4LGC*MKjstD`!TQYM)e zmATyOc6F7Q_I9LkHO0z=;4p+@F%LR*8^)grb`^1@7Q1Frdt8A&!+0n}I%_-<*Mfn8 zp^@=8dicJYVuC|Q0uPV7r^XAZCW^|+%DvYKMOLV2h3e-1Z3j`>E>~D3ot)|1!AzKq zN>9hAGjCmjn;^}$)5;LfYhJnGcrc>?=wxQL;g*`7{zXN#KdOWZXwMCc{Ywj=PJ?&7 zU+;KmO`w#Ufri}jG%!ni3ph%;4qy?t?_a|z3a zB6nAaO%Iq@SlYYpvW_Ww`8>-q{UFIgXx1DP=zF2c^~n>TaVd}j#VX4h$1x{f|{*(HHm{SDa7FFxgB%AX#+H;lke36jV~FTzKkp{NXm~Y8IZ{~BZkm`;jEH{<$XHtZ&N@MVl5}@#v$)N z4}Mhvgzl=i3v?R&PPfOYs_5iC7=kKpowx3|p|qS_Q6yIb@S(c7O3k>rth$^=hF#J2 zMX1M3dYurqH|M_*ub5<|+kz4nez`Ahfgaki=q8f!gd^k-vpJAtu1|y@%*v#dVLF-m zoF0rz5>2seW}O}?B}O;W)R;!rvrC5sX@H-R$b$TJ!YuV3dZ8LK4Ap6|GI|ewTxooW z4r>)Kk1udjUh~MxXYwW+2z~>*y4Z8GwwcULkPZoTIt?@Ua8 zA8dTYeLuYTos1}3G%{LDmCCWtHoOc39AyU;3B~Dwxg~rHJv~)j?y%fh95~lU+vS|> zkbdKI72Q5|x+G&iTyG5r!JUy3L@dOO7ZhVHat2FwG6r1-)`6ZtG}>cL>kkfD3awP3 zFweX4QBnW<%x1}FWO-Jy<>&Q&5Tmr)wa_vF?Se;niW(Zsa-W+C4Y+ql;;Xbit-T!* zx*G2)WK}r^n5mZ45;OvkJd&_wPHEvtM%g0$mW32Aof30p2My_Dvd&S@iWn=D)1_S- z*0F{5z01EGybTHhFN~ES!HEp)mojl#k->u$6LrB-n5g|oIMXSuLlM{E%Qt6l%mtc=G7Y-)R&&qWUm`5Ov8W#By zaI579X2Gb^VVz>1%i7PH)OyEgwr!nShQ}N(&3W)UE-F+Xj~1ICSEg^&nXlQEzB;rW zr+|4dmdqz2473GxNzBYmcfK>Fl+;hs3A&Ett0%ZVX7f>|jrBxp-bXC*? zC0Z%yLC)V@T!m!2dlw$n>F5ey6JKFpe^)-QK4p2Es!r2wTx^&6Ego?w=C76weI3r& zq?}}g>NlpgfZDMLwS*|59PfpoVAc0L7pN~Sg%>>lkrurx?#~tUe&Y6AYxk zyX)Zi8gRi)nfT_rP7K2YRXh*db=!c@R+8lLiS<%#SvN@nZV7*+%C)xE)(@1NuLd}~ zzb~;aBszqeI(xWwg`0+|Tden^kQQFpN19M1S|ouafPRHt)48T_rMK@meN8Bye*$45 zs<_`z-k#*PR#|4z3$W&c#Oh16Y5RF9xMRg8@(okm5D$&*JX6bRkU{3HAbnr5R~59( z0|8ict;XZK8Rf`)W**G^yztbvY?f!S(R1x zTak08?JdZ6Af>@Zq4PRT%eE1>21_5 zC#KUL!?EA#fLB5-imH+oV?hiKf(Rv1{pPVw@hceLu#Wxo8>W!JoD!r+w!wg=O)Z1E zx_UZU9G7ioixx;Mf!y97P?R6thFmn=UvpQla?HuO@JO_acjiKMD4jC| zQl`BCj@_aKm=8}Wkn@bIIX^pw>_+Qc8b1R)1JT%i`%&YV(R?b9@Mc1Yrw7jLXqd)? zQQ14hfwxx6Frw#BH(c95k}zDv>JAS~>kZ z4`Q7ZEvG@{p@Pq!TaPTbnPB%n?Uzl&gqx;S$#ZM*faG02>Q%m$8>)0q!!&@0cG#Nz zhUbBPC0M2?j|}Cy63yx={baobP<*?1wj(UN!4uc00e^ci00I@S3CU69)&>v_c*T~BS(mMhicgBy>`mbW0oDt%O#3RwI0TJ@2>>B2sa_O1@;q18w9c@i*fYD}eUzP%ZJ91{C)7y*##?N3DRVo(#Vo_P7tP zjf+%Sz0$e*Si@O&d+?-kA-Q;Pm}~K&{pOW{>=myvm7v{^_6}~Nr~=)3{=8$emG}Eg z{gZTGx{`LQn=Dog-v*vql>6}U@i87ug?A?KAW4-SLZM*AGRk~3hCW{+wk4rye%Y@V zt-{<#q#F=`%UJWVyt}`jNQtVfSD_aOf?!ptjSaVVc)>Bj4xg2SQ{gnzbAVN;^D~p$k!m+on8c8b9y%9$zi&&QN!-JDw z8))4rU>38N!GVi@X?=z+li&0vLnL@(sF(_lY8+FJE>orlAp(dzbd^c z21`x#y9wC(a3jNhSRO;AyJQqA%ht2?Mth@weOe?K2V@VfrLz@M{yN+G^C70?y)mLE zxl=x%B4+&&%ewqB810vsF8I~g%v@aRgC&e_?N72t;h(uWU*G$}`0KFXzs8gx1p|{+ z7>P5A+)Y1+Pu_YV@VYCHIMYFn!p&W?+%lRX*|n~A(P`wXjt^&kc%vEW2342M zS-rXKSFiK!Prr4SIGg6|E|J7&cgE1C;ysU${-l~R0a;nE-OTDZKH0vHdeix*Y5wiq zzCtlD^Iq3(+1XA+hVg1W3#GN_b{yOy!+}r#h<6D2XHPBi33n`f;^wg!y#W?`75BSX zZh?P-H2@2BS_!;tqRxSuuvHA9?k;Llct}2S`=8=IObtQPV9Oa|()ZQ%Q#|$2pz!Wy z`j&$IqiV97&No{34>X=Tz4omi?@ilR8t;b8ZJk8Qe~)pP=Xye?H;WDH%!rg0SIBoXp9#Ay55`y$KlS6t?M zTRI*&A97enBjvlQez+rd^`5na*15DKJEo5Z%Dp}w9A0*ki2bqIdbx$>c8mOZINwlR z-vdrXi1j2pw8O4$XNR+BaizcmnJe(2zw~XQ^JbXw!RHr!#qu^e`T5Z(&GNPow`Y|JPt8=9f1|@l(1X<&I=x}teZi$2>(d#>d{h`w^ zUPo6)TLfLL>l?h{SBV~FCu^$p(dV_h(Kd0`mE*>UouxWE6OpDDN4;g1-}}Ip#A`l# zeePl3I{UqAdQWqo+qRXIZ{Gjv>=TF3uy_X;a_7YQxc>%7fHCN)3|o&I&Bi{kZ&Nox zq$0-a&ua$;25?xL2>yL={ry@NIi?7PvJ>nYne0N^2T!lwxt(@MAwS&faqW={bGzi1#|r_Lf*H}cxgbV2sg3zNk8bWh$Z0+dnDzhgEDS8j#4PC>V} zw!Dq|`ugYQ1*~I|Yu)f=Y}Q)4tt%Pm-IY}|fVKo%WNt3=WY*5>lO`j6vHjx&f%OXn zx=~S4lH)Uv4d-1RPQjbpkp{m^HvZQIdI-)mgM?AR$Uv9%q2!IQ#L?SN)+m?4-@O_* z?=yCy)mAM}Lxu!Fyq8>UQEkfhn{g)JFXPkDo6rf%PPm|Uy%o?ghpN8B^~ z)|S^V7gMg|F<4%)wxXm+*ltE#A8!-3J8zM1$2hv~CA`TYpxr&==RC35DX@JK_wclX z(|yJ@QG(VHp6Jzxbl|Lm~Az$(1m^N!%6Mu<( z4GjjjJ#u~`*N8{%#?gaLB}<0QQ1~qpEe-W1@=NVm6xE<{Io><`k=yUl2+0 zU>+!#uO2kq^kZP)9=y0BeC~Nd=(0FbfBa4OeD-Q(o8|W0{^Y(aQ~(Zk>|BIHSCro+ z!KcZ`!;y8P?a{2^^=E7Q_B$nJ)0+bmb$%UZiDlVvg=lt-JGj-(0+Flx(Xt7vJKk?z z&30^aY{>S_YzcQoOkX+NbsBCLNa?RIY)x7aK&`rUk+Jgm(g)Hd7k*9 zx&X>?R`_lLXsSD;-TW<{e)B!f;h#hyTObEVFksc6=s6Ig1G z_eyUT4D4`KYh}wNxwlu#VB|5dmCI2C2gxL}p5kB3Ezjx5W1TEXYud*HWu2szgnaDb zi4)?=Efb!2+fhdK!_)nu4QPRka^Al2%6ung`vc#l zJ=C<78KmnEpPfM#v>!QUDmRqm)Us!!G==d!W3NARby-Yy^B5;zbHHc)hpURdCm(0} zp?9WgE8)w-Ab8-XbrQYK#kDfBB1d%?J6Y!xi{Lk3U-#1@n92G?mtN00Xs3lE!@+u& z;JVHC*0mb#xww~JdgfmJrzm2LYrg$ zjb^5lcg zZhgnUf!AS(^DD>uzs}m!Q{t~y7}6ZtF`|rt7qqmi#WLJoK$|EQj2V86@y!kykbCv5xEmUE9Br772g|3KlLy3lx zZ_i%7R5bp^w{SpyxSA0*I}a_md@iN!<#uqB?{(ju$%kx|tw_q(GB;}XlWVq4wmT;V zRKbor>K)2ZTezi4Gs#AwI@X%PMu6x(&vz;H# zT;y-{QX0a;f$s$tI;K^%FLRNoJGFNXHoBr3-dtq>U95OcbuS5BBdzPBO#WKC?Lqv= z;m2|2WQZ48Ug1m&Pep(oK7MjRvEK07xYc4CdY4C_J+k~{iknx&g*{Z6MZA7|cSWNa(8sj>F6>gw19ea6!Z z=8?Upovu`_4hW~1GvPQ+w=&K)9xS#iyu8pyj}KE|gKQf0IV^MYdqr>ab#II$nJNz3 zaJ26(DQZsHm?W(|N49S|B>vX-5Gf&^opme>s_;CGHx(XGp-stFwrCk?qpFD4RiM6x zyb^z+{xo2%a+{q}&-bZV&^pRM;gSf8+@Mte<2%Rp_rg$$y4>oq^h4eDA-04$UupJm zPfxO>#JOk=O-6aatH0tj^f+$7suqZCG~8 z6wO<&IhbHTfcoc)~^I79#0DMa7niMzKaKJ3|j^OHn=BXsY2-Af$UmgD!) zh1Qw#gKU@<*@di%&(9}b9~xKMelsiTQ3^Jfa@(-kA0bjY8^2&qzmznr6T`xu$dJjv zu=}XEQOU1rwI&XeEXVOC?DOuv@R_8L>w2y0%7J{vDFshEl%wu0g6va&n@nbYC4jt^ zpHEXn)Vr0B*^bT?8TH=kH6$FOt3Ai;r5{-)V|mJA%RNwLGtR84;}}lp;h|uoS(VZ) zeE2l~!A+P6od>Jl)k$_8R*FP^F`e`7_9Ff9DsRI6D6R32gu|Ol24})wx6Q&f- z)#DG%SJmv|)}5fL$OctIq}}}7-l~4p#Z{BNdPNm>-8C8OKIbgHy}GQbD5TP+hWQs2 z4HxD_+5KUtjc)HcH$aO5zcZmm0F|o%4Kt&+isI|u+JdM(gMBw?_qDnSw7$}mvx&d- zxE68$Cc6R<_xYd|$Pk%X&`ho3(~GV4(x(>>MBN^0c$s;6zPG$fjDl!Zm%eAT%uG?> zhE10MH#wn}kK_S2Q|oL!Hmqv5(--$F@(al7Z5QkeCMJ;qTe`i_W!1&Z`fD)de=fD2 zF5JhclLP9d@ zxheeaX=Jt3n;)Y~b&e@NVZ#5+g9yIi6ASZJR$#E7DPra;iPZd()Rg;RzulzT{OP{r zKTTl(Q1ZcL0;&e)N8?M)q6rD>QI&@~gd<}Xhu_*cq@|>CwXK$TCb^uQqS25QM1c43 z3R%qCz5vv;f!&AS9a=rsxI5#PdF82?d2`K$Uekr1`kD@&&gW-cLwQ|Q;L{@2q_$XU zR9`}Z&Nq6s6tj9L4cw62k*NiEsTmO&zRnJ4rg7Up{{srOF zXsdwIe=xbeguLqpNzTQM(Ql&6FahL7w^KvuUeu1rb(Z0n?VkxgQ<2o%+%iN4GO4HG zip;bBZF>LvHl{h=+w4OwJjgy&Nzm@&W@R09omDavM1e*3a-I35nD>T1VdADR5oP7f z(WfB*Vfj4sV&8v%&3|PV(f<`?OkFo99jr?r8GV&uFP1!2%<{!mPdO`yDJ-XW4&sve zZG~v1ZiE+|g@s}rT^P*jR?;xV$;~xCKX*9mztpKR3aB{$!(;lZarX8m`eRHA^R9qn zh*z%UizYRdZ8d>zKvRs>EpHKb0^V+gg*d7kCbSd9d3kvxdM=NXH`an-f7E3D@;m>W zUc0+LYTi0cbbfpIWCUA{VX)L)a<;(WP-uD#Y_JZjF3tg$tUwoMyTZewBKytw5Ad_> z^;>snD25Xwe+{kwV@7;h2^Iy^JSd>+M!V=m49ACWLCpyj$L_!lPG3!*fE&2~c>^2y z%@X4RB_*Yiji{E$>+(!pmA}t_>XY?2!>PXXITzh3tz8Ik3bLx%+`xx3TU_p6n#y_4 z!CftZ;j_p3h3bK&oX9Rmxc(cpr=QWI%4%+&Ai zcgYRILRH)fgC!C}+qPLdVh#KK{!!5VQu*qGmj(~x!TFeP=(Y|l9Gg-c>iFLlD1%VRjV#&TRv9s7xc~yUXixAjXfSAKE^(c zy=iM`7YcR&6@X4J7b_-gv%+X+CKU+5JiHy&ysd%NkCG!8d^LLUw_AbK8SAJ^8T~pV z5fSMXeCGgb8zK{r*+aSWd!fVNdkNdjBWOD!!{ z_L;=^c=97!xcF~vCcPZaP7<`vZO|D$IJmhTA#e+mwKj;HVo0TK+06Cz)>F#@JNte6 zc<^44KiL6Uj}|C31isa$k>9=B%0p;b0CJHD%xWi0WaheMXaMlvuD3#t)OC&BKX14> zaBDBR65hsQAypHx2$8J6frYz?)A@5#bdvS_L4e8WR?s-WDItoEt{=Dqadi#aKn1h? ze#bvx{?8kcH@qBR5ZAd^uqm~0r`%E2zB!U}Ap|@wm$XPqIGXm5B_v=rMKMzARt_?{ z&wnsMH^EXFs(KWoBCCSuLS6d`2m%M&KGF#xw>zYko6CBdd z9I4|MmpNagyhjt47}&P31e~qQ-&mqT?t^UQ7JdWW?<_Wk59C(ry3+1=u1am6Gi(!q zxZ|BU0+lv*kAfd1kKTPMeJ|qV6g4|H zH=2~4oef$}y+qOwxQYMPW~Qr&OlB?%C|ezcDo>Vvv#{uIjZ|?yq%U~ZX7Q6qnITZ| zDIo`A2J&K;LC5BK7q!o8pf;K$w7oN$-7VrlzRVfkrfTIy@iCfQ`pJ_zQ{84tO3LZY z*NC%JUaa3vC-CidXmjhN2c2_|td%DVzd2ZJ^RyjC>9{y&x3*p^9n?LFW4=LjJKHJYW!wxp>MuWaKgRmpYu`&u1WE7= z5d>3>zXZrh0GTJHtbD>)mN8dlOY^%0-BX4|RoCbI8PuLaLPBIPES!;a3fER^JF(Tw zQ+@n!19a4trz2j_T0r%7r-ZC zoG|@EfP9N&Rzrjpf;yt`UG(ex{h=%Jn836#1YL8?1#p9ilzJ9Ax$5r= z*P}357Z$lwQ}y$1DKb1`Y3}*sKrm&&JjE8Rz5zsFi%*+Gr7hL~0}rQIrYiLJ^zg5# zBBH)6O0{htzCH~@JpjV%j}`L_0U((Bv>ZJghd4BgVC}OyskpT;g`uNevS1t3Uh6a^3etYllMi2w5^IA_np5n`w z3^#8!lBa)0+1r+n64efKKb6FQ-lWX>6BLx{J-lZIkXv^~NBQQ%!i?;W9<#HtMQ&|9 z%*n|yv$cBl`y2J%`2neB0E_5a>hsIx0*!PU>gtuN6Sa}Ks%*t)?1YzzW@R|pc!i);w3vMVI^Dm(55GJJQ)O{WES{iH5`L<0 z-n=P;J`tq%=vG&YmA@p?S-}tP=hK}2@B8KB?`C+#8W|ZO>=TcILOg493Y3(UM@6DN zRaIBA`A1&G{(g!0%ZgNAlX<=o+%f~O@~1(0Y#sbi+v1q;0NLUZ3AFTJWL;-}o)?6#U+ zW@%DXz#QEYX?k@oo}D5GCE$`mZS*^*6yzkxumCossHkseLh&9M8QDt}74y1wtHNLv z%V=dUQhM)Z*dO~>bj>BCg7p zqIBR@Rk>tHgFz2M(gs8f>GmI%m%z{a%tuihMxB45X=wD$Z`?HS_9CBf?v`J^XSVkb zbNcAD{eVdXgJqum9+UsyA4&i7YWNj;pu7BkxB0(8`@fa;fBWYDFNe*>Q*#NFi!dO^ z6-|0JF|e`80`KJBqSKyFg_K#5sP`*yc52WWCi7)wWT8yL2S*5?l%>WK(b^1|zP zR!7CkEGL**RPrO{SXA1Ij3=e->~?}$LWS2%`ScNm(T*SZ!Z_+;nRE(xsDkC?595Aq7eAD5e#CW(MVpzAPrQq7L8}sg(T#wj>O5SLR)R+diZ8XbX&tL&wb>WjWPX>6 z`?oo!55Y0u_l}B;H2m`1?<)$zo$&aZKHU~d`C6-{qOz1 zKim#~4fm?^@#+NxJCW0;k`g7) z{pv|c;7un{EO~jl9?H8<4&rCehWHzQIWaqRFlcCZ*&TV%Z=34<7`H}M4&QDLb8oH!8|Be+l?PSb*B^d zE9*v3LLEM<{w%ii6u=VG^7QO4Ei!D8Q^Z^e|DYPRJOumy$b0j6sQb2kTp~r(m89$~ zQiPHS*;2{Aj(y9TWk&YwQIUiqOU9Oc-;LdbB>OrBW8b&2k8POwz14j^_jNzd)!p^| z`}a?;UX@Ru&wDwS<2=se%qnVmgoIbC&lD`Bv zn0%tBjX)02p9!h=!&+3ZIf~3;rfjjkt1phyoQ`Z^2fBG5EqfH-gSm)+6d2}-xpw2m z#}v89XrANNQuNYxFu zbn(IkX4A&@d)nppB~^QapidUF^}@;Cp|VAWgN5T@4;Sv0YW1;)C(fxx9ilnV1^P7= z`HJ}9#d-l5S5g(Yl$qUI|8Ul-H?ghS4Q7Ttjnmi_Hl3)o@ARQ~tZE+$lr%I58|ztx zPwKCZy44raq_q7J1tXiZ4sg|gGdecCz zbIKg+mrN1cwTTq_6;bbxfMjD4$>;dpNXTQsw0!ZD_lFK-)GT~i{8{@zTFdV3j;(l8 ze~OcHw#YjVaGmubXt=m}>$TmsJloLC;^G&sKt4J$fR^UW@N%rn!NVEv>YGMlmw#T! z-(TDUj?VpVmoPuJy87H z`vw<&wr82PQ>STWHocD0G{kVKj(V03Y3u^|!uRpSC1Ksz<^gjjr;@YqX2ZQT+jjng z+_k>PYLOeGkJS7?JK^+5U;NJhTFvUugYfgZy*c3%mHPrU$fd5La`BYdP6AK|c?9}B%9205 zl?DPF{IyrG(Yb56Se?q1nOx$Q$O=5aQ@(O=O!DaTXYUcNMYQe5hod(l=(seR^wdf+ zzr1?QU`5Npa)rK4xTC#K0_`L?bOUrb;kdo_?W9{!n)>D^Ri_1LS1Xp%(1Wn8h#R{a z<}uE3*vz#YL4$Q}@Ah)~Pdul%JSKN{$rW_kOYhTC_|Y)mu&u_$={dD2B%kCF+=~y>tR#X+hWK2Cq76k;(|uqFtO{y+b!5Kp6zCD!$^PDDs-^5;7g_b#S0mk znOH@;rKqk%$hTt4F}c{7RDFG`{Kc=d!C{66Gc?zEXilaGwX6mGHQV_w#|cpI0H4W@ zyJQdr#E-BRtbvI6C3*u7?K5YF?8nJx_kxp@;tdj6p*lWRU4n8`Ng-k%h}FaoIZvl` zJ5R=lnN(1#%oig^JONW!Hmw@B<<4i>{XocQNNawi8V;1R)Xzm_DnW~3hjI=0poaei6%UE1j z)VlM_ks7mou0T&ATU*U{6)**bAZqLC)d{XthI8XwjbXO`EM#DdAfNNS>6g2VjCYrT z5vHL6MvziD39Q(&A%e--05jsBS^!fO)_Lz4^N_N|I$=Ek>9|Q(<2R^6ZRJ_?JmWUk zm~Q1I$HT)DQ!8*_P;R9FSkgjKDC<7qmjX9u7;hbIYHF(K0{xJq`NS7QM{0x+v@P1H zJ%Pz-YCIH<83wlHbdCL$o!_U}KaRJt;R{NwTh2j90Z7JXSk&AHAeS6)ECpz0UB!nm zLsQuYi$wQVn?RD!<6XIZL}aAIn%)qXRanR;bWl7m2|0FhZWb{COqb@l0xH7@ zs-*J4*4|z<)K-h4?^CAcV4JbCbHqtzKCQj2dG5tN+g0wcu$}=&5uxVbtVWmA#0Wr_ zmc|BmPOd%9GZ39BkY0|YRzpybHQN%I|4%!RKGCdgnhN8TDVaFel zEoP(zC$=tR*Z(7+WN;Gh1*#L+QgdcbF0R(J?spU&IQ?-VAt?_}=ZfWT>}`wBh{D*$ScJ!$Xb;&^7`9QJo8XP4KxoC!h?1&Br3S`l1AE9EIt|40pSg$K@9iF_d#fpdizxJ|cH( zG~=ox1aA|dMx)61y8t2au;4y;cKLcZK2a7E{ z(>=0SN7bI=A{_6v^?HAoce@W>^Sr6leqb8ilq^GQw!d>=_A29MYMdhDZMWp1HH=}C znRs>Iy==~yrXQbl{AoBBbx~@X1(~C3W}2Z%=9Xn$Rc7XvZe#3J`QypjuDj!t{J*QQ zuw5bLF;uz-E5&TJPOjY@JuC%L;Jft0BO~SP)>t;KrwVIOVNV#9oX!=^O`V=Xz=nB% zrbVIuXtk~&gjSNW z`SnkDRW7-^E@HQBZeBWH*IsqcbsCw6329&HW|yWB3@>WGd!WAYLN<~`)PDMX0PR$L zh!_n*7(^YQPYd*dG_|&VEG~A$pc3K|k7XBMdIG)PK^Z!E>-n${`)>>kXh++c3X6)` ztZy`-lDopeA?NJO0TlEie0@pP)DsJRF`wt5txm%qrY?o~i29Bz#-(<`@seW-{q z^Z){NQ_(5hu)~LZ*k~73^KzMuH=Av*N`QC1SJ?&#vunFO@IT>I`jyEJ;Izj@T&)JJ zDnJ@$H)4ZzuCFA}22)e-ty;+>w)SdrDpbzADMrZfJ1fyqsp$S63tFXxs90NpQ z#PFFouR%xa?jj3VW$}0dDBHP`megk1&{oM7ov_b&3N*By$T~%QT3MfgzDxPt?vnW2 zq12h{hwH zMlmiUpZX@igku3N4^-2utz|-szI{&+I_k>PoD`(wZ2Lfs3U=fJ0Fev0#_R)jiHTt+ z*0zfEX%;?3L@yh-tck{@NMWA#yu#ar9gHGl;92dmByyr)9vP3DU^+b08bIeydM11YtyYn@Nv)&)m+?wCEj9`XA?Fw`dUryp>M2lWgDr$%G&r>pN!lI!aT zjKPbESBV2@+TuRFO9~HGD5d;Pet1&9m8IF1;Y}petR5cvsaThh0!ZGWD4a+&*iC!F zE`5}v;gc{$McXecHM&<@BGJd3qKj3J)9+372o-jbrN9o_I#MvFE@s8VwDS#+rEX4+ za&mG_D|y#&Bus?2^Mb5vbZE~zwW24syV_mw!o8;nL(dK%gsj1FAhfZI%3C>`mjZ$JGYXk+#oQRPij)H=pLEnfKVi z^b#&j1J9%wm*r1D!}fD%_@@i`Vay^f&CPUK(<#J#&4?TN=WcdfXzJj7Z@9lMuv?Jv z?9TYu(W6Vn#124MXCppeRRK6jR*~$N?aLve*YAulbmdZ3PEC=uZm?P|*b^3Cx$P_? zhj|YXom)xL%MLvhtoIDsMQ2aCP8fFdCty(Mp)2%;{A!QjPVTs+CsOAt{X<#$S-GLLTB1L*-r~*a ziQj@c?)sTqZL1;i!z&N)4K|CZ7kz*)QrOmt{c2Lts8engv@3a*fAO%m_CC&>@zd4s zDYZ9p@*(Q#z1h$Qi*=W!mn^c}E#FMc3XWP;unw_2OJ9hLjpeq85zzk;m)L?p(|$lH zk6KGny1S>xrX>Fo?bps#h?yCr+AQjYggY&eFEcykda#GWVms@ds|Ha<=-hIIRh;u~ zg@w}R3^&LH#6iK6-?%&AF{8;2JtpY%e!E)+Rgwfn!GhOTiSy?%>srP@J1Jc#m9poH z&r5mwSGgpU&gmlE>(ClcRU;7-*MRrO51H zZESAMJPi24j64W`$11Kk;W@v|6JYjYSs4A?U3kA^TMUU65V#8m5F1KX2(bi zu7DEIs@2u2R|o8)S(JRSR?0_n4Aih8zb_4b4SUftjd<;Gk0r#_MTc9&+2+oZ?6=P^ zJzdo^K~UWnHKBe$PW6ymyYK-RB;}^dq9B*d?77)>L_#i-=gxRcDQW7hGa{sy<;ibA z0pM6Le8URuQ>!x@qbT5!vH{ZCYyFqx!Kph>|M>B0RI}#rQc+@JVz+ut_bNpGT=2U! zuBQ3l^{z=+lNT$$c9x0y@Zq|QOu&{tz@&6eEE0=cSf8n$wPbB%od_LZw|Jl~@qn!1 zjnFKU@-2&d9gYi&t_!g)ZU(FOaIJ5JC|b2uZYKC9o_WZAU8w#{#Of9@|6ANZ+<*)f zqZe-XMo)QGjsEa$DN(hCr%y_J?HKYp=*kMQNGlX4eyx!;Teoys1E`LbEh$Catc~+n zKT#nQsFtB~8*bZ&0aawvs=0JxbHTJrj`?M^%VG5ed7}xpv z<8NFpo3cJRP$c`q(O3oDpv(UhC|YpqYG6eQ)4QckM?kt3doxTxs8k39gvxok0CIPh z%=|#B50;%bMXFz6V;pI?S84RTna3>RLA8nK_P(6J&gaJFp$O{J>71k|C7)^}Jqlve zsnqYJGSC-X8r!>QIlr5k*=yJA^ScbYoU%8+uCC5dq*%+W$bzVv^D;lvi9{n85^%tk z_rjN}w*`Xq!sw(0Gwa!Ek&}~4@(OZ6we1SpqHgs7jS15g@6``&(RV7>&wY%!{Qfn` z5iRqF54n9|w;3x(PSf+~Dd&h^GgW!rPyV|UBh%-FF)t-mpzlb&75QaK?gDbE8I6~! z()1rlvY*mN9I`=C8VMB?Mj3&vKvaNSQqvkbUia{wrXF;KjPkdA4Q406ID3)fbyf`T z_|$uV$QHDrp{02+MK(82n_ceV=bw(}`+bQI18vbI&)ltIChk~kSlFF){yn^t>HlI- z9finDMrf3jYcxI}(Qv9-_;}&)i`i5Y^7G44+1fqS(iO%HZ@)ve{y`CA75-vSLo-)z zeozNBqr*&lL<_Z+sb+p??tD~9$;olqPwM>;K-G!uzB`axzwtXq3f|<+Ic3JI!^dSJ z&d}pVP)fMQo-oE$Qo^~5K&vuB#>vTy zAmf2@T|XAFn>JlD&j*#G+X$R3t;e-%inbhxe;_9C=0ti91e$B*4q>!$k_Asp%Tmz( z5!qiNj~3u|Z1H^H{LkL*@%1r`o+qPCb5K^NENOmbXG8L}9~bl!)zkfPkn7L~R2w(! z@!YyCPU-%8{PK8u8>HGOgIWVxfMWmS1w=oWfBMH7y*c}|Kfge?21rnS$NiIKdu;fn zw0aNGzqy@VrF-TD#5Ipu=!cHwca;P%^06aalKSq) zs(m2EE#!C?8J1WMC8ZQ9&{(Z-v51EL2+dC+#<%@;ja4Y(QuM%O7DR0 zY=^`V3D@Jjx%!RF5hHocQjsRVTPz7D`c-H_4HUt7xK81DJZ3+-P9AeArv;z-q7&xG zsPPZZrzuCidD|XBYg7O}tisu70Park;sc%^cW3>^*yz*&+$-)>$M}B)*MD~;e5|;~ zFvgx38^|}X-kgT|%|Aku_v#7eZ91@FFc~5YR!{lc)gkd&BJ`axv3+w*+Gh{|86u&(~|Na`E8|25oG@l6`{(tzHBc%E1|5!x=tg%D{ zDVdSkiALDJ*Za4>lYk!oXK&<_c}+R#5jB_wcIeK~R_I*oJJx;t+?Oxr7m$Im^A=#L zLkTKNb08CvWwX0(+{33jeGXh4dqZ*if6)B_PV5O%Y6kw|Z$WUKdcUCKWwQBC5XL92 z?N|*IrXWn4E^+Y~XcX{C!@9v!xuBc^|G+bY2;uDwWo6|KFpbU(w}5gPP=D6)mab*k z5bzy3H#!;I7&E`q=QB(xh~@(y(X!B+F}aYjU;_fk;rNAxq4FZ=bTb`FBGHNt1YVg= zqSvYaykW}o)Am-=BKId1O+#MN2`pXmeFX;e2gD{+n<*)<(by6tbw}25!|)j z3MUc4p#--ooqQwMs$hIvZl-H>`i%z77h|BjOm7UI zx(^(-4A0qt|Jj7hiZ47OE2l4bq}aPGb&e1`hEdM7uOngVew8a#D3Jcpe)6ZF5I3(E zC-B=dQ~eU8Y*tE8fN%9BNY*E$WuXKdW_V#`wd*2`(vK26$j;uo>rJ@F#lwRTfc8Q+ zS5B3PEj43rpqUY*i9xrjY+b*_>i`iHe$mPp&(|l74O=lg za2aT#q+EE!vK1sa8lczyORwm&PWyDAu!O2cr`oOLV7*113qZ=qFgsH+ffrxE(ZkNh zr|DEW$LbA(mh0OVeaG(JPW%4w1xdEvWfDxt^&+giE!pq;v7fDw>Ve~m{`e1kO0UAH#p!M~-(_-&{m}EQjM@HA#QVKMSgOli&QVW$VU8-fDAV#WZ5o zIQ5rjF0(+QC-t>Pr89HSGiV?^Z}zt{XRA4>(qUP_S-Dy5VVt=nQl~YBtfkt2n#%sC zq}V|O0!hTWi5z583FxrgwHqL15Vl`55N#RBKLcIs!>kPWvwGTiA_fM99iLi&N$`bh zv{wg$7qRUsBu2`pBjHp9#n99$zj`QH+2l(N zq*NnQL2hYzzu>~p^ByiU=2IJTwGD>VDFO4E`w+tjPv2!(IQwv}H!(KbeVf~6ewcHazKhr5T4xdoA`qTC|-Dxk`RH#L#-)Ti;>te6LD*1GRZqnJK3D89!j464(JEa z{#B)=fV8-x`5v`xW5=%7v*g2R0s&aoxaZr|pY0@tnvv^=WP*WnaNC)hr-mqZS&|?6Xa8@ih9@d}l!|e3+v-41av^J1Zy^qf;X7!F)+VZsfo+Vk@5O~0I zp?V<-kF6}lmNe$})kwKpYp6dh@f{P42Q~MXpSNZ(b6unTn3XD9R%-l}q=f8x8qrRH z(d;a7y&>ya#n>e!@2X2M?TJ>dyqYJ5Ap`+CI~%c4w1D#%-_!g4M9i3;94xGFDRIz3UrIEBTFfAgu#q8f-LZ6Mdz~tCbRfpJ z&D)Dzd>08{)jG^km6{5DKO(PDgcZ!)a+8mCnO&t!G~ z4{7CuX3BKDY)XcjcXpwfdMyB}CYlAoAWm8Bc1ijeD81oX(R&G``G}5_L~|@Nrqux<6kX zMry0i|?2MUcV_uq;~`H?GQwEO&Ms zi+T1ATeKag0S3RD!o$n=7pw_39b$r4>?_yH5Fa?+YTaF~a z7|&AuB~U;vfcjo!xUEn@A%1@E!>|jnUckN={b{*<>lc<-;`JZO?iw$TQNLvMQA(r8 zrgVUOf9y(`UXwMFC3pjcZ~h1ZsQE$>rv;_(>zE6(cbo#1zxkW|*@u`ePnxBXQ@DbR z|H$70EF*VGE)*lcqr9*pND#t~aWOyex#oVXT2hoaG>K4HF-Qr{A~$JNc%zs58g7fo z&@J6IaPOxL5!EtZ4HwYh)pQxL3#58wCqhAFI=}g7v;XpTIEX8-aDJH0_7KicWI5{c z!Lp1YT#&0!*LJs(+H0)+-OD`3PB5xzpoxRo*0!49%j`JjmC~LRS25zc$nL&5?moPy z=bIy#au~Ts9%Qye^pMn zkCmtL`*P*%R<>`RqaL%=c1Ku<_)|^7_(ZY~tZDs={Vz;;LgMkK*96#09NwQ!ry;G$ z#a9?IH|q^FKPJ&It2Cbam#e==RcsYoMV~=VMc)vc^DuB`%k=dA$Lz?=vY@cm;*-H^ z;M*}Y7+X!NK%kc%sWU_Gh^@$QTIwGmxNcC(xU;Qe{0!;F>~WCDfmArm7>sK~AV;5j z+LYC1>fGi{T<6PeEdc9JY{^)pCM0~xt)Ah;cOY%;O-#~?l%6w^ z_gy>A#aW&JcZU_Vd0{_uS;1{04t6WDph(Z{W^?qr?Sa#1fl2w4a;b;w$gOCu9QV?; zo>tfSHvZz7m1D7LR(5)1Lm;Z{PFm&eTL6QwEL>@=L&Ye29Q&o z8!YOmetKCt2w_{Zd-c3xP7n@@!32x$9bfE9(w&&$dB6C!g%`0%v{$nkRuWez08F~F z@f~9-DcV?Hmi#(PkS_^X_r)aK%Nbs^8g~llsa&d(nz+AWiQ`wkuXAP+g?*rR`@B--p32Jv5szU6U%3miKG|Cz}t50Lp) ztv+bij=N`;S-r7Lv3xg(@eZZmSCOmpw`MyGjnZPAgB7CgUPrVIxl-A*_SM6}blY{r{ zev!I*R18PruN9wzM00}rpm8Ff2e#LSTI&Mq;wCPNkuko=s z58wD856{iDv?X4_A*NE;ce%WZ-8R8Rt5c_HF#JnK>Z-Pe9C6RHhaFaoM7%pw?BGK? z=*6Xeli)YHGZr;_dyI_Ft>)uC?(eNG`c)vt9cHZEr1mUAEed7qA zKP9w7f=7!JtePs0YmlV4rK<62h&h3|3Q7@f`arWD)ZU7F?YTP-HutG`F(4Nh>Wo!k zJcJGQplGxh!Sq3|Rm6V>Taqt+IsM9#t>DDZ2p6T0kevKAm$(sBkn0|Dg49!$`oLvo z;N^sGUY!Bud*R7!Dx0-X_VEUoj^s7`a*@l>dcbG4+%h_3dwOI1?T+%!5#rcodXJQb zg|+$d1aty&c(Ws2eI}Sz>P_T42;?mT!s(pM$;KE33@JE=LjFD)EuI@&~EeH8vdlOd8q2r z9@qJWDk`26q*NNRHgnt!s!0biktTm;8aE*(*HG z8z3tA>LoZglq@Q1nPi=Z&uWVm;k75G`FfnJ7-O;?snVX>i{PGc&s;!Rw|vJIE7vm; zR&w#7x!YW+if|Z!Kib-<{CoptxpnzV1ti(pc#*AdbYeFOsa>HC?KX8sI1`C?&?BeG zD7>QH_v{P+-s-?%Jj1>?pR<^{CTid*Xl}EAOel7uUp6l9B09OcEl4xw0KeWTREPNa? zTPiz&@Yr21!-loJW5PiSAuv}^4{YWp$-1PE`FQ*fF0N?g!px#=74wIl?!ZUE`<&p% z0YU5LS@U77Q$RlOR&&h(EE_k->H5WR{`Nj1SzFl_R|@H1aUMH@ub?wLHcM<2x8-)q zBO>V4{(iUr1a#&pZ<0N!P__9fry)Ui?)gTJ3wAqF&(fb&h>edeNnx>XT4T@Gx1z7u zGORcRX;fSH5z~I$ZBHir9Iru)mDB0Dj~+yw66*#m1`I_g+g)u%pOCaUwDuXkb;VGB znx>Ah=h^bJT-_yY{3~>A#L?2BIOjirk=JjA>XM1ZzwSvG)i>`+Bc3t%*t6-3*(ykj z_xK)sZ=y3MxW4_rzrH!ZP^o_Uw{&09w0KvaU8Ec@vZD12kpAq#4JEN8CwAAp&UhJ@ zxYcB;ZJaMt=qa5xSKYh=p}SGgOlNhBr$WZNe0UE!>Z9{des7IN5pM_f|jmM1qc1nWSqqL_CNKwF$r`5Y-x$ zSW6>uhK}Q|sVwkA+c)IpW!~It*_R|OD6YHUv``$M^L(ICwWUX28EH^mETCWH4AsG~ zW1eE8?=)$cm3S=l&i6qB+b(ZoF&6Oe(<)SWC|c0U&Ss$Zi+EUXvCN+6TgXRixXkW8 z0_@Pc9z_I4yjy$o!fS=yQGq!hPHmlX`xJmsruQ!FZqN^o!uBGEUAGVVydz_6&;$|f z4kNAFElDLCYDR%;C7!}mU_!1L2ld@PX2DqOz@g~Q-{SRbE=NkxHa(S^OA>pZ_q$UD zN{hbIsMFAfU(cIX_Mi!n7$oh=$+ui-_xsl=P`*G`L+P7E*ccVTo{H2z`w7T3S`Q+L zu1UxF^v)&iXY9-&(A1<4L3*A(S1q{d`p=F zX*t(duP-d&AQCrK5+fMdpk9P^6!RjP=-_#e57&*ZDX2Z(_)EMu1574hV+e0vwD zgWbaGp3X0_CdOVyTaWA7A}tykQeL_yl1}eSMhifVDOmFJ4nYJjm2ULUwdLz1Nam?` zh#lm|iqD09=k22mcANU_E|0#Z%^>WkWdi{z>e5RWL%BisVG1en!mQky5nL1<$~r>4 zv&6fi`&gVK2wB{rz3ESe?)`dX&-@8~*ib+4OH3j&bCr)UMJyYtoP(05QHT(YM zsW-YTapilNE%J+oUeiwSyLO?6v$+IZMo3EoNIwpeRc|;h_16jvBo{F|&-qB+?01F@ z)6;FzGmjUt!wFv9r?w z{8^pzQx*`!q{c@t0N}!l;xXed_1Ph+Lm#4#2N42BQ3PuwO@`J(vqKoxyR!;VSsLMG zi)njj!VlTx@Ox&_R9?NEubV=H!)_exOir0*Y5kHM^8SGYag;FTNi1p;!0mg(3XJMr;Cylt?E2q|pMbfMlf|j-#7BOO- zkAn9GN4#ba$|echTb0n#VdsLYwRu9JDLL*q{)1h9ZvrWR0W$zj{^~HT;muXeD;;r8 z!&dcd9deew+h@336yKZ#1_B6jnDr>dE4$QMlqt1fi|{b0bA!gsIztXJdV<5<}bF z*B;WBA=7d3-eP=+GCo60&@^kk9N%Z_Y?ic8oN&0QprEOlafoGysQ6d%C3ehgp&UKQ+)Xw>P0IQXC1Cs>cP~yvAN1`{JpxuAw2pMOXL|gMm?e(8| zE(vKlRl0N>X1Ppc+K1pZ8K0xpsSCNQ5O=SkU6g1nhiXxL?yewctzjA~;*?!jtI0W8 zW=Fep=-w%FIl1Pkv@2?X)bg$F+C2Doo>dXgkQZ8#B`( zmfUJ9Pa_TO6f&UkZhNkbV*5R+E%j86dDr_Fo5l5PO7|BZRSKRbtcw$Cns|_#i63h< z)lwCy7P>aZrEdMQH%Zy=i)C$y@f`+Vr4Hn3D7Ij`))}z=2VLp)j4{}J|ARFp!5viX zDRahtA&b~^a0^m!QR6_Y3aPf14fyF;uBnaXR;@mTuD>qoLHzDTb1p)@=;don0 zKiZfD`iAO-8hm4$cY){D$ZeP_&+WZCeP-S}CdwV5r<9^v)&_n8H~-fsre~6lcbz(n z?DU;*IJ<`^;)?jj5*0w=F|rh7dzipBZp@ZJD4hq0UoIa)-rY39&yZzK^X=EhCZ{zGO7W~7{hN|ioXL>}UZOk7 z#PA<5PRKCc0+ReN@Ek^*fHT}qxeTp6s2kdcuv}0*6NRB=H)W-oNV1 z$)k7cVFw+fIS)7H+_~(;^|u3gt2w75b*QIZ0gjCARZ}c>=nxa#3GKhMcJo4;H}gr6 z9sF2KGuJPr2H8A)V7fNuSWIaU_8EG3yD^oPgRqW9}lbSmpR(;mg)6Wo#br)inmM6=uma$ zKA3=(6U(>yfbrS-1%W4Z+XXmgk>XO&vs~AfN1ZEUOwza3$ZRIf>>8z;*Tr-8!MPe3 z`0Ap=@FFK((6;7a+7E8hXij?M#7(k`xPK*RR-89o3_9^`)0%L%5t)ch$<4W>KJWgM zwzplDJp`9V){ZI`HL`dQns>y}8ZZmx7+#bABs_ee^CYDDR3Q;RiGy6-X{Ohzyw!?QelK3J~}P)K`_>fAES>xzqT9c3=)7(HOZNg8K#zN6AiU_ku0TBA$P^hRw2)c z{;aNb&ZCPG`1){f%B|g2FxCE?L9D$SAYE>KgRhkF4mZX<2cd0x?rq@(V_9Ir+cBw|G=&!=dG$Ue&$Ph@#0g$6Gd4&LjD%tk*h+=dXPurt z{{Yv(L+C0zM>JMpk z^YkaG5rf^EwbhWUh+NGa&^`a7Fkfuh1GQQeH2K#0vDUz~S@yN?JD0A!OQ7_yOjw{W zbUXX;w#&#pwbkCz(V!DE^fTO}dwib_^X91Jip(EsbS%V(2r zEdt6avYsC{U3VwrVc)Mww9R@-?cuBTG(Cm-B4-pOUAnM?xx|OP?>^mz?rj<_ZLqB# zj@0w&$u@v|;}%%48m@FHs;f&)Uq&61fX1JaK;G7BH)ctk!yh8x>jgr_K=(@wrso|7JeY2z* z2JE1=WAJ3KC?CQ=e4nsc)G=K3=;np>eOiqy4Q@g2wi0h@KS7TX(C@$aq1L6*t$S}L z><{O`3$qQu4lPJnvD7J{&NihuK9r*PhVK|L%Nqfhl!KOOac8Nl_VzbEIIm||R3@Z< zt)jWRE014qQE{HRkk${;)SC!S`Ji;xBV{DJTZX}>Vf#f27B@Mm3#-0-TtoMX`*H#N z0LP2|`p9?3eoAxMzkyK3z_*q{AOEg|eaIO<>@0orYZ7T$GzIpO%dl$}Zou5SwA|1C z-a=H^P_du>{(jFX`D*9ut2UKb7r`L0&&6gT%ga%)8MODNl>5QXa2lN8?0Iu&avZkR zOss4-c)n(aw0c<$Yjum4yQm9lPtNhexHLT)wejtsQ(@Vw1MDyVwd~4&kZ*q8g0p>* z@=hKl6YvZ4(q3=QvfYmeU`i8sbiuyW-e&vldl%37{kwA+3zJ&8>mr}|6r=7o9ZYY3 zXZmFq37f2hxEBpa5J{+Y#dZya{%Uw|l8Q78Ik{X$k`4T}hbKv&q6mzYszx>8TEX7yIXQ&DPYW@06pLD0%n zN}P#BB(m=zJGH~}hH0cr)+Vu6lQ3FhNnyy&q9m??zIIVKxEr{+^HWbk`k(nQ2;YnW zRb|5)JF>eq9Qhmgb}BCtaX^00?#LlNt4!Z`akVxi0wopUhE|o(Sjj21m(;?*DucMP z1cp4f2{1e$Z1wjhNayqD3!Ly1*F(1E94B;zM-_~Ncn4@USFt#E|fb5+$4tzkre ztAcHJN}#1WdHjewQA+%RJ7hKD)aF}m`6y*xwFQ|Lks%kN)YgKv?Nys_!qO>m!da?> zb@w19TzI^rsBeS0?+J3q?q+Rg$b_F#uRZzu9KG^xTVq|^ew%fF{;1!@CHi+)Cwi7E zu@hHOU0(nOhfT#GpwQt8=yK|vJ-J!MMd?4H`RAqSugq>w(;eY`Pvm$h&X@O#8~nfQ zxCGsqC_!&8Jr!JA{HZL&Eo)sy(nPFZuEhd7mN_liCPmJ1An zEgg`|(-Ffh;B>$A<&S?R$u@K^R?RcSY%Wg=u!`-gF`nDsopP@ogki+IRPqU>CST9- z`AD#>V)Q8~{u@aACu|75S3=dtbz|aaK47s%I)eV6u4K!}cQlLs@*op^@(9n4(W;ib zzWaoT7%Hrcb!!f266nCMUsZscI=0rODwtqG2s0wOpb!q%R5E6Wv-4+01X9oMtZ~E- zpp%d#z(`g=_f`s%+=me>0tF@~i91F8{Ithkkz4s&ME#|Q-pGVaK|n{vG>~4Ni+F32K2QN!+TeY(SHJdFA_gQs4R$yGEnt;@iySd( zsf0m{!ajKO(zu$Lm)Kd^5|h2M6vfq4F^mlqs#7Yf$0SJn z8rCrgbs2&Jrj)ZTBD+7w`aIcveHSdm4ik=U6&&5yAc(q*mh@Uq4S~8_|9XDpIY0l? zL$FG*8D=pWH-zeHc(*?*wM!ml_n^r(p|2V<1M>oNfG}_AxQ6(NZ11muus`wm#;0fdKy}f-zabJzl?qQ?PK~i*%rcQY7%fUY9_F&>ehstdLbse}EmM{|C^oMH3sSRDR z3W7jsU4^r<$1fFAtUkI<3?^v@xLdXR7kH@d8ny00^`C ztFHP{6Zr+a=Ppu2=f;Pgs_5SAJcXt(vUCHiLtT(h-myF>K6ieHBjC%PPN*q+;4fG) z|HsUF(LNumm0s!uxD+{Ysd#crz-A7#UhVNBo{itfn1(6#CEHE*(;&%2&4<_HMT|Gf z<*z9}eSZ2uFOsOpb8~Kk*R3$XOYJjh#ve)lUlna^CqW8EtJG?F>P_T8_&+ACUC~X_VLIA(9DUoSB`Ee4-biTdrY|iY>4cz3~j>*rC%16+qgnjN4oifuFr_!|E$U1 zdZNb{6Fx7r-*tcg+TA1*ScTIhs1ZQNz0I>@)!aMp-2LS?biUi{dT_ zL01eQRrsC~!LJCJI|ZP#t`dw93V?R!Lai(3TxJ_*T1@UwBUiU0D@m?1BQ1NbI**iF z^p~LZF3@}6Mlmaevxd)w_^1wfT(I4KyGiKs(5_vJ*v>Ppq5Ue?4U^e-y0s@s|DRa# ze5aLpWsWKe*Av$Ah=aYB;*o=D^`pa19Y>v)){cTWwtG}u`JSG3UODss#C)b#XDM8C&v-Ew=g(+>pNW;zU`wrc3iq;M}#I4jsJiCH$Kf*^A9ByBh1zVB@D>Y@vcv9M7{(R zQl4z4v;WBIG)ox)+rFHC$NJ2=GD{6p(Zq?JisePGhL(#$6P6E{|92heZ#Tn7_A=;C zvWi3xmMp4A-wRVA1GK0^&W~PjL(TMMusyqF_UivA{=P=x1If~;NCM-wx%`tH9+Pp# zy~tavS=Ov#3cdV~TSt^F$uzRHme+NVUN2?CEco6^@cf{w#kUmnmwJF!8ctTt;6DND z|L(u1+45}$_znSDfTVx7>SbH{%=AXcl>Ce`v!=dgtmZHWME*$J1GAN0l4UgoaCNVh06$|KS&|c5>9$f`c4K;@#?ogS50OV`nBHwut`$LfH|-Lm_s3G zN{Q6b?DH+$;^yQmK8$poqWI8}PH}vvluro0hwo>)9rl)Fd4syAd}aD>rY7MPU5OV8 z)FY0aJa2TAf~I&6w}@Yw!IdONfJ%2ZjhXyrX0Vwe|97-UO}yZ4vu3Yd*|G{}xWy+i z|M`umT$8(yh((~9VIcxk2caFJ+b1pJ=Vh$y{P z(a_Kko*QE|WI0$IaObHqveyulI&-uN61&3#PNs2mrpVEY9BlUFc;cM}?5DnOI`27U zspGd6P+U*nh_oOEt+*I(89#W`s4#RF<$9(xhjWA8;Q424_4bH>?Z`bi==p_Q*T}GE za_UtTE zAB~*BGPm44eeI+94E$Qe`egtEYeF^a&^<9J$uamYV6CE zBn2DLizN9%7PV}Yz-b`W{80~dQ-4Qcfd}jdol0T0-=irn0{ij0?4;?>4Blsxytk#j zzrbXe25wv9x|$U%ocbv2P@mGPOYuIN+F?FLjy$PjUSzQ`E~+L1C62v3r30wEHg&H~ z3@jO#ZD-KnWYPN733C^+Np74WJ#NF~v+gnS^r;HA-`>?;k~@OCmxiW-WM9fqsNgYwbe7@WesI6?Y_wzNGA_j zo3{q-xJC)palLLPS(s`3V}Y(>EptzgD~Y2>CF>-p@7eD|K|Wu=U66MU-nWJq>r`(?h(kAI_K8`zlNI((bD?-~M$Jkf3<)*74^vksDDZ zCuPl3Nw;NuDE+z$wTpFQ-OPFx##5bro_BpMrS8GIuj9UAKmL=)ToV5N=jUVE^1F4e zBcqTCJZh?E7Kc#&uHn{?_fkoHJ4mdEqYxE}m3mfDD@4rIz z)_!tTf1?-S!Nv7hpsaBlGS}7De*?x@{X5r1-5O_-2Z6~fn78KwuHYv?@V8}K(xW-P zMfc{l)&cS=_tH73DR6wTS{Rs`Ittt?s{~_>)QBTyErI^yzClP`1XS2#Du`A-HYq|u zf$pRp7yvYG>!eF|iJWM;E8){RI)vJk5VuL-)&@ox?sZ?e(;M7^H-8)k17GV$WA%=` zAL}N3$G#tM6_awEP6R3r5F#(jbses(WTY*R)|M83V{1_%7~VGlnh4TkA@e$K|8#}m z7Cw)c>aDJN%5URi-+`+6llG&v)C{RQ!%ue}`Ja(;!WX?EQorah=>x@24I+s8wLR_< zqQR-y-|{5WR231Kj-KcDu$-%N()Gu9Jxa{oMr+=Itk#4sIP|?J;Hop- zbt%+>!kF`b~H{XXIfetY(3b|V*Le7Pz2prqU^ zCm6j)%z~_sQ^CZ|Z6O0L8)NgA)Mg|Q%MgGudM_)XWBH>d*~u-)Nt<{k&6G`7Jr0kt zy7B<}1$mfv-_A#F(f(@XWg(dq7z`^h)1+g++Pv;B!q0y^Ybag%*# z3ZaJjvBN52wUE_;jwS6kT6Ulw{|&6GBmLgB$FQ4ImL?{|%$NBzU>?b>6`xbAgD%v= z{k!I>(^r?LhWapmqC4Y-+XSjBerK@R-;1I(v|=Iv-&j?|e5Z4aHPHpE(B2PEy{Hu|r^giw+FeJ0N@of_z&%u?pTmAr`8sG_ zp0k@7l2eWRL0wiNH}opw@AD!_k-obFw1694G=4KHy83bg!(pr79UleufC-aLUADf-dyk<9*3&dMD-Ad$L!&(ZvtRW!2rUlHQr1to{@T5xbX_ zBO3O-Fw~L0US~zno6cs?$$I+pW#R{&AKTAx(q6O2Whvup7QbIism9!CT0)oZy>UTU zF!T1~FP9@5xblm*&TbXUX*e_MoC^VxTzypn**^-#l%fV$iTRvCClt0Hv)(#m)zYsc z+t_Z@r~=_|GUuRVcy+%dS0U}dyb|M*F%YC5dT<1P2`()Ss-H6D$XFQ_i&2|6`2|qf zR0DVHTkifI=aY#db8u=G{e=ioxI_ZoUktt`)d{bfk<53W2?0tt8Ch4v{?6Jr7ti22P6jN zStO!8Hb=4*@!=GLhSFLCL=cd1%6Z6{74-cP4U~8yfic&E87j*SRm?N3IIFh52sdIOd!N() z*P9D)lTN(#Si(P=-y&BAM$=R%T;unF-jCnaw7V=6!TPQF?~%&0ySjDxwn+h9v-BDM zbjVhD0|S-*22^nny0z&LhMG}fWqD@X@o`YkQat+X!B%+`izZO%zVea(^0x-upGD6G z$&h|d(DXaoxq-Z-h>J!ZEVSj;)?*R+$_22-mm1DByA4U<0KiFv`OA+z->!LscoEc1&4(6+sosA*p7 zmaVvKammU@N1}{>SOsHVq;J5a$rxy06U+9ARyoy$g!{LjM zL*zlg6uc^w;{+xD1}LA>U>Jk&+6=p^eiE+4w=en9sd?FlAlAixOb|2e6QpXGi{VKg z^ep2?!{rmCa%5Q|&W31ZsF)ol)w22X7`yb7{LXL~pWapzg!1)W2dB$P5WlvkbR;84 znA^(i{5=LAEJ^z9l+E1tQRQ``SoxFQh-*k-F*XXZCQjmg5(*g~HISqz3X`SzPnOs)>S!KEuZ{5!>wPd`v|zjW z=o1P9D?i4-Ea?+kPAazP&XAT2+x*|TA>3Z+qe%q z&~Hg5L#F#{83W~iJUeTVU6PggBe831dw0|%W@!s1(j%YErD5p-nV6FvM?NxowkkvWVRhBSA-z(Ma3w*6+{aq@$L`69xrEDzF7f%)%?$x+?KR%z?f>gq#8 zP`~)%3?SM97*G|mFDN*WCrfZQNGlrWNbih%lyIt$5A&1y56T834fkS^?24zMU=4T) zJQ-ihA!Z-lY1NRTCiJB)@JCfdVYUC{FDjH78jS8AX;E`{GG%M%M}`*U@5|OQl$>Iz z@_afSBW2;yLSizZxI7)UhcwR9VxeOKT=#H^t`|SrO`Ru8r^|Wp(kq$z|Iwovaqovz z&Y>Q*seZ7Y{@&zsfD$|b91T!fpSe&2ii%TqKwo}hH!e{9H!D53U?6+bxa0+>{Xf+Kl$L6S7O5DnVZ;ZI#It6+9 zJYe99$$hC${y+)2(DIr;L<3{8ng4@l;7iGONrh)w)&y%ikU%M)nC7o}3@AXtir(D1 zxldq>nx`mGAT<>zCiy8VEH=?Y0$FMj?F|Hl(Se+r$^v|w8lfsACmkPc^)@Nz^D+|o z^MP#q8Zq-P`3#jOP&^2^o`Ed@r9&$!N(llJgpw4-$E%XN86@f3p+^`8@o?s)=Q@;T zhWu`j&|Cd=P|m!UZvAHVvt({2u+RDEL9X%fuCMp&`h7V6mv821u#PcJuu z@fF%V$Xx!|`naL41Y6cu9;W5|Y$FM-`3o69wjgPT+n8MS@2(McR>(BO6ceS(vQEOe zN-Ew}G|sX-$PM7ywis*~$;reVT3d z+_PUV$s$lBm41q7@ivumB2(GlSYBqarr$z05*Xk&9Xm)S;AXw7Te4!HPqmfY8Q&Yg zqey&$DXUvgxLfS0&0jlcdXG411vRC?XVvci#o-zXLLb%ag^a3d?|@1m&le75$8k`pR05R%eV3 zAYsDlS}oDe-hC+*TJd4Vfd{moDkQA{e+J(u$uZZR-?Bf*jXEAhYt)<{c&@~^>z@^e z#O~YFR75gVoJojyu5S<$8aDlrZ;YD2kH9M9B^VJ>a+x@rt?vgR>Cf1Ee4ymC{0Q&s zObKU`L?w*4QeddJP`H^b*NcigipliTTYOLNsJBT(grl}4pBRPQfyS?n$jvPWjMfwb z^6c&~^=I(|nd^|bI|ZN}F&e5X1GQ2lAl@t~I0okV{DgJ66ak9(-P&%26mJaPF+&}T zTo{=&hgzo&sw@_AA`*=Xf2F^*f|HG#W|GaX%sWdG);fk-wgN@;CRv)I_7bJ@o z=EE`ti&N^o!%^!NMmUoacE-oCq;laAf2>*7vP>!|>lJLH+c5xKAD@$V#D{Zb)- z0@^_NvipUStaWkcW*dt1TyGioQhk33jI%hh*cLba36AxeRG_R&Us=H@69#(1R}Div zZndI0M*^2LeK^G6lE~?Jw=Dw>*?xe9O-35b@Hku6E%!D~O6mKnTOe_VVOhl8Z{l0H z-XA*#aFZ{CN*yLQFhQrX+s{C>_!SK>8O~2{!`tH+H;)lY<&d3uP=|ON6A_`_GJNhI zV9_hV?B!8`dxCyf)%qExBYiv7~KG^3xzD{i6KI zd$|V4h?dJi*hF%urGCdcxe89(e6PuLtT6w3xh)_6DUp%7;$&Lj?y4p+M7}Lx~Y<`40>F)SvX%-O#VB;zgD^({>@ zX)VKPE1p9voe+al7G75Vx|-nHsNjJ7^GtxhG*}?+uQx*bt%G?2Vrc==D^0iSPC#23 zKN;bCp7iRqlZ12#Fp&0kxY^eSQh2P%(*55&9O+tX9seWnHP!BzHedqX4k{Sr&1 zDM!iP{e`;LL&ww`ezQ?EzDi|B3?)e{9!UKtuUZ_cf?*>zJD*Zde)TG?(b&0C?|_DX z;XvuoNm}xP9LY`1|K9zNGwOA4bmze9g73#6BjPUin2$@>b4^%!jS3?O12=?8H;Vy6 zTTM#EG;A zrkLkW4mS}UKqC{sSUY4T9HOlx3FXpE)em_d_Q;4{)p$7XIQ;4Z(DzTnRF1NO(S4m$ zHMCNlU@{qqVvS;@OBz`{Z4K1%o9p?&p@Rvzu8HQ6@Xm|aGSm$Sx|?G=7Ma4+ba?eX zBB|9op431>VzaGUKgNu01x{`@-${~B3Fy2eHQ-`oY4ke@TjB7aVX{z02R)zLozmUX z*uwfn-^XM~A7i-nzVTBQSN~T{yrhIP-n-2a_?3JuzxTd$2^UxxIQUfyk@Cb)gXKul z@+vYw4vZ_ay4c0GXcERDxvK;OV%NXK4km23)+mC&sm@$A#ufyAjZN=bYj5)JD6%1} zLYaMRuZa z<4a>AUiOMIX%9E~d88l!8hw3nzd-r%NJ)dr_;>~>gNg8#ltW~;+(T&97}5DHA#ZFM zHyqmO{EIw4U}(m@-}Hyp^~nQUB{;T2dc^<<`_cbdyY%5kQC{p5+=Ftx8}V!};Ef;m zzwnfub!txmw^m@R%#pXgB(ZMHT~_vc$boTMAp`UJByp-Okn3K>jIkne7rOmh*)U}N zoVn|dFzcPloJLM-8UX-Q)aJ6|=GlmQT(=PlhZvn zObsixd1b)A`VEyjkJ1iy7B}(`AxS^#;P4Y?8pzLj$Gn~Bg00;xg0^n2b_%nTx{B>O zlaX23%wH-ASpn$xg)h(YAd|WRIW(Dm0YK=r*yxi^Axqu6KaK}diG>^O>>kfpXT-}x zZb9yyxC9-S_*fWlgWQcgqy@2c;?=oi8o#ug>Rza7IAciWGhS5|r(T{ka7%9x9*}<> zM=i55WFAi`q2z@gIE+?&|K_UNXitu^jr`zdvlaMVy2o&Yd5L!mjg#ghzd9-W(`)fv zH~Fzp`GAc*{NfvVbg$lR%3^Pmrvt>>y$%PZ{kq-+FodjQ?+r2)b~mTB+e%6bm6lWI z)v>vAA#e(gZFueI~cz9?5L27jI#D?Ua%9dc^46_ zk<5HBKjfT*{qsl_ed#2l!J1t^Ye50r((xpsQb7xQX=KJ+}N&}}k{(xeIvc`;A z@JG8hjk}3z5|4oJbO>Toz#fu>vjOU~K?&#B3u5g6gkK^(gT?gLu$@>-#Wy7zlw@ZY ztr4ZORqv2n@^WIm;dut!F=P5PnjI9uh84PR5MBaX>GQ$sJ$uP_X|H3yZ;wrH{&nWW z7kw+BwFE5NtZ)&`y1}CjuQXiE!d7#AUsuDVL!cf4W{EY~r;jG!2)yqbkxXM5MVuv$Vo=IZ9XwVyvedAhGZ-s$1E?{Lc0I{E7KcJkj?0K~rdVi~J z%+_o2_byRKpz@A9i^_F~(Pnn3QOu}5joUogZRbsDL~)cxOAep2CB6zYp`kAdGkw$8 z`_$wKfdI&>@YGYfxOJk%;C?d#HnxgUyPgdAQ?YeJRsoIKIls|h7}GdnOQ|4*g-Nfy z34}@{+Ex7+!_U8tpORc@2DR5#1rBCU>Bu|z?raz|Dan7Isz(4#?SRpx_pz{V*<}d22s!E^_5El3fi|7=8d*yy3@Xa_| zuOUw*O6Sn-D09uG?FBBiIRjJ*0ud1=S{u=$0Ck+G{Mwi-9J%KFF5tu#MTRaI zJ^uKB&^pBhm(@#&4Qql2v3z6Kp?)iD)q+xHXd~ylb@AT9Gf^^Dk~nrmq{U=p?rOYq z>YtK~{b!55UV;JsB$q)FU%JnT%$x;jOB-{5L})Zn z9c?*Tu3}5^n}Yid3B*Q(ALD`87OM%8*$UcoANTq62H6$3cBgBtOj(VrnFK zT6nWb0e zW9qL2U9Atd_aJ)m(u8a?7Q#r(>XLlGyY%2gbQooC$$;O;wr z^7g)e<{%W32ieN$~n`QB6iDq`T!SJ$i*=S>rw6^!0r85?w79^nA7DzSih;AxsI z2}{OsPDUPx{=9#&91(uy-QoA%ld@u%b27mt)1)BdWq-8anKc11_>$iD!Yrki$`>6# z%{PT>H?lJv@!~^l#A&2LJyuts%t>)Z0Bw<%8KZF-u$t=!wgb7k@_l>7Vi^L-C!F zFj4gPFr^JJ{Xjpc06ZMXejNz#?%`!^PHLEH&p^lrB(p~>{EQNjB-iBU^d(MhEDlka z*TMe3F|bE-x~eGq4AYz3L*^0{{HB^QF6d-N(EyOVzn%1Fskd?DPG92>cD8UCDDn9o z5b9zZ1bQK5H6i)pLqcTkN-&r}D;~AfmGCb9VTrk1e1h*WkM$mK{X=}xcT#b{VWo2e_V*RIlMY)NSjD3cBx1oKds1_1~ z1pY{HuL^5wMJj_dGd`c|2IO{$1ILu?8-+p`ytjq%g#mzBa^9qeogJP{#)n;KWfk_JI(Z2uLO_w~<{eNVYIOoQbYrC04zBdv2x(6q8g z!d4*rR|D`?ngI)8`T3vB0I811#0I0Up@}G*&cwy;glkq>6P3e32W!m)1t&hSPoR*QU2;|)s`{=<~qJ$n3Yyh zizUzH#i1#KfxlFi17J9l?3AUh58;QK+ORq}I}B;0kdjsv0W`Cj-!}ZWz~ln`;C_-O zr6o3x!kwhQKPqjI@HP;#5rGPy42Y8<6@4F$3MY&SvYsk+K~k(CzvRF~xqu;W&;I`S z1zDk|xcNB-ILrAQ^v=g)BDQd$8k-WzF94N~frrJt5JKtNV?7v#@AHvAMwT0-y__6c zzu@%c_2nNw8;P!ePU|8H;{*ll8}khPf_*#b7wBQxn20h84YqvUwZ@%7$pJ!&1?c=1 zZi>zM-jMi!kMX@~cwF~v3t#CHOmlz_KFAJc(7#@{1~mn?*iTnNAJn7fjg82B8S5Om z4mhC+iC%&W$5f#=$M(ic6nh?&kVg0{kiE>L|O&r_!fY{ypeEn)br<^R$dWQ>foiu;?riKTIL=T0*=ETXT`78`T6T- zn!Ju?=<4J10L|NR-tQ0RdUEP0lL0A=+&!lZoYzxIVFj?=ErR8=L&b{{6Eq#K`HW<{ z*`gnUWH_aIsO`0C0jhZYLPF;5 z13<6_uoIA!d)!;)-czbdTOnV*#^T!)MlwKyH&8+iUIgLyb7gloc(~;4>J}IIYV56#kcurY-XmtqSHFf0PX?|D zQ7)Q1E`ll~C2nu*rY_d<&dc9FL|w>xa0a70w@e>tI?Kg}6{k;&M2@RTi90AC2)n{n z^z?fuO~bX-5Rt1f^)+IMZP_}W})KOc9Rh2yhV$JrIt4+r+M zteiWYHtRkYu6dAb-I@X)D_i(RDk&=?m5`R%d+!6ey&*RB1*F%(Z6Jj5iE*jR{$ zxt-Xsy46%xBIA9wp>CrGfBSiV)uvf)IktrEGoL-o1W>G{NHo*`)@rF$^u$X}e6dV3C6L6#=J3VWQmq84gC%B|5J$4jomS5#7b4wO0ItR#o|CfIi$(li|W6Ib$z9TLAP zjU<9JdHU8;f3f+zZIg&ye4qF;;M7vxF=UNmxQYWCe%Kpw_Emx6XQEZ(9!jDjF-J`* zf}Nece?za8yw$&H^x!rgXWc`%R;28V7%kqAl$5+y=6kUG99od%z!kev#>H>gI)+z~YrsuNbHr6BGHR7rxu5D|UObOKQqi zY?@a#^%zz!6We}H4Ks2v%3Ee;re|kK!vQqQ;qB(uVC_ZZCV$KC84+H02C%+7q|GNLH z!g~6>xWW6DJKB@;-44%f-(w3Uef}DSyjI(I> z`6czj3mZ8Xn3|nw6RD(h!3c%@_Ctj6Ui6cT+q9!Fjz9N6iR@6NwZC?^SVU~3rFO{p zyM;@`bFz8rfcJKuZTZ*FZ=;{I7|~b)OIib2;l5;6wXd_L`G&#hY2k|bmRsF1#P!bd z1b6y4l_+NqYx%knz?&(#HT06o!7)To4Sh?^i6~2aQ&p>B6glYZp-G;7Bv)_bAi129 z7A$$r^QMYF;@X#V;05NZey;xMt&7<}9fsVq?x`j!{?&-?@ zUMK!LF--4f8w?h5EUvJbF62Ct06*c?6tY2cT z-*=(%6>YN@_7@-gHUzq}ZWxf@3!L~>4SD$5qSMdmN)f?o*{&C-ueyf3sL};~FxS1U zo57o<9tF(4Zu_J;K+A0eOqVVoHd|m$z*^)K?|@$g8V)+vT3IWwsl}d!`S)lV>@lFo zZM0XU74!9JV4A`7+?T4P9Z*nqW$SHjHC~%&-Jt02-{}A2IqNrI6V+z&Gqd>!bKkq~ zXc3Xf=4<@DMqTJ7lS7z>s)*!+$20%FrvC(O{@PWcDESwcHCqqjZKx6oY*e&v0$%mQ z2VxSC(Y^vrw{tP?S@t#`y*bG2C;^DpVsNUrnEki&G}f9tppi-g{KrH*QxAZ?(g4Ob zy2LMw+vBz2$?@hJv*{km7Kk~$hm45^@9iHl7EYDimN{oZDF_}PrKU+pNT>!g|6jQE zp-&;BNhv1V=a+>?*!NIi#hb^Dvx$F_Yz61)O~1U)btc9!mGk&PcMs2rN7gxDRhnvl z2H(6fU=q6N-Gjq1z1EhNnO%K`3-E7#G$5OErLGfgPF&pV#t$xi@gw6+Y#CTsST?E) zan^C1^53%L0~QFVoSaEV7!=}?WT1bETpvJjr@S$KqXFq_- zdW3zz{HejlEExoy4 z1pbXX0h$6U&_Cflb{ZUuDT}Vk%9iSWXustrW389~O0u`Y+C(Ezx}|w_OLw?Vpa*tm znu16!!Z(7ppRPq&k2~(M^sB)cYcIfi>IXP%Bpot5BT@lR8xVJE{Ar8z>E$NT$EfRk zQUHh5-1gC?p}=Bbdq6*}YXU`ad8(-0tNTC!Gy+sGst1GVlwgl;D9yFnrsD+kn9vs$ zpS|B&PspQQDHOCVZ`r|i52Jf0BmoE7HJrh_(ez+L7XSEFV?n~3L3f9#7P#yCd9ZL; z8+@fr=+5j~nrP|*1kSe!;pwQ|*`K3Vt=({tK=a1QnT(96-ZtnzqdL3%sQ0`tucxxI zGF;tHUXJCqx?g(tzo-@Oj$HH|euz|yTbdp7prnIBkwvrm7%~%VDy?1 zY`$v`Cxymb1Q z%1W9`+^@D@a^p6uP0jy(nq@D7eFp;@vC{H?>HV^c4gzlvJHSD=17;qk-LspIML0d<*d3kti_3g7k;Os!Nu1pN3K zwia`VHTOGWBU8Z~ZNyGOf<4Xd(Qpe!(rKqiWC=sD>TeAWUIUvB?o2?&YPnuJyVh6o zdHj*7C%)b{^y<0i4RBwcf>gGC=LY<60KC?J&E62CjJNTPG$s+!e;@M^5{Yia;%>j zUSP%v8YFwm(xN#RcMMs|cdOZ-lD6DRZyAyC6m;9+TKSh3z;Fw^srS-#KZfFV-z8@| z+}|JcomDSn;ChP1Bi4EJTB<{qI@V&W1s<@MhP}3CQrNXMpYDM@z%BV>YkC-PUQt`U zjGcv<^k=&`V9Lx`;)NPCj#z#S|3& z{jyV$;DUMbs!m0Ql``uUtb*Qr>^*;QNE7m+(Szhc<5Pprj-LI7O%ecwc;H#gqdGye z{dQ9K6tl4Ns8)CSt2#09KlWMw6d1+<^Fz+#S%0B^dV&mKjF&+iewTM2tfAQg*h0EQ zQ&m;paZb5m5Zs@E%R>%vXA%>TzIQit)G5S51RG`$)uR@D{$U|wZ=ze8*)#wyTt?gJ zcgUPx-)x5g7(#9X+Qp`nd<`ODU#OH{;`ot@pF-TYvAodRuRaSfHWN(L$=ZE%Qo zfkMY=FE9$VdxHl2CQQMu9JhWH)#s|ZTViC;XQ8z?s9DmSxuqjV>Mci}-RZ$n6OlZk z@2{e1pvQs9o(tn&_bBxdheRVe@i!l<)S;!3#q_ zBVP!Lh-hA<>2G+It_UB{?kK|Pmc+mG>KXQ>ZH(DISr~`r#s<`50@mvd4TJ9)8JV&- z&aIW68SmEnosFoVT%G%M)AFKjX*O(SkgCRAF+|ow~tV?I;=15bkD-D z93qrC58Yw^_HqP~fNu@PbgxtOeH_*<^llm07z~AjpjHn&jH;T_O|=kc_pk6u))y4z z@TjOLLa@|hDEb6VxD?4ub7~&F^vcZJLSnJ1f(h`VUyC*s?JnldGisYW?NjHGXiAQr zZk}6=tl*nTCXK2LwRLa?lwnTX(=#pkNf$g=o=)_~x5n|>@OQt<{MypoAutCsp-Zx5 zy?tU}qdi2WS`D^xkeA;A+zIDvNw4twyv#l$d%b@xy;Qc_t7?oV?(g{emk`@c7eI4$ ze&2{U|D?pDM=}8DCaE|F7T_K_^nkBn7H}5CJz95s4fZ9w$Ih{m{vZL*CDYSAc9Srv zGk3k$etbESW7Xxqlw(?a^I6MCm9 z3TTCXed^l2JPX7ZHCE`;KVG^2S{SMBaD>T1%z+8FRBv7s!e=B|A?6HdVN_rJ-LxbW)k3aCH_EFwvd}$x4qz? zP`%6-a69MV151!-B!BmimE&Z()1Yb}j_&^CP@ZHrNFCm!5Q7)Xl{>_`JxkahDA2cX zZk4I6RxuAbC?t|I-F&0A%FH5g<+Hktk%!|q$Ky~yTL>L>>uVNzt*H z8>j8Yo6tLPlfL|18?OW^DrrM7wkhF#0sO?ZNG@0ww z1(Kdevy?ApdA`B+i7?rHKDc5zTL9JE7tJo!O;7Ii>jASd+XH?(9`<57)4P*~V-d$M zJZkOtZW}jWAK4+5qFfc2i{^zUS2Bs4{Xw}G+Zt$`cH4Gi%dQoL&AUsD_438tVrB|h z0=@|t2gZ2@sxnu4et@uwTgLH*C``C~Zov6;V{{R#(WrvFXsH3#nd+aT#9;gi-mjTq zbdN!7KNP!5Hiwl5`xVV~oGuRf^kXBh%!KiM-_1opZ7_I$Pm-WgAH1B`-ASc{2Sw-1 z4`%0X%*$vDc?m%t_$wS+LXS4#cnSK;8>z9yw>o?i;r?+c6qpITul84lw3qAmB&&Iv za_9yy|ETG(NJXuC@O!LKRkw6rJ_$11@kAtG;6J7RA^`m>P87$^%=Of|)CYT*TO#ZK ze7E=@5Ovs1xjP=gO(rl?}6VFt=`&YdAYtDaoN9b0#a3PX>%lMyziL z9O#P@X>qI{@V?UIw_GQcD(mft_)>xITrx?0Vxb?>1NhD!VqC=FIuWy)FM{S9?pHLw z`Zg&H6k2x$9FQ3o$@j5QRg%GGRC^N*0=(`>xf>M<08U%|PD$~|>tRaNEO>+Z3pw7^ zD(BGea(xfk9rL37hGsqfxT4zHFep;wlc%@{U+a=uM6DO zUYmQM;5-unchwsNNWfy(q55Us6QhG5*ldwISpB$|n3#>He0j0QEpJ4Y_PaH?nAr4d zUx!F84!>(l-yC0c6MWq^dca` z*<$K(8n=E0O*O9K{VFT?)-gq1 z)%CVEY%}LemTu8FeYWlTJ;;|yt~*-DxJ#n%AsOXTMBgVqCuUX>LlK2|6A8AAp)(Doysj_l#r z?lc%U+u<+XAB^}cU6;4e$ss}~R|N3_l2rZh^2wb56?%6MC2yChaQ@y-y5Ud))|MdD zErq3cA&KIh{X4qL zt-EBD`h_%aF-;|n*6kUQMJ5}&lfBxuCSZ-at%vE*z(6WsG+*U=t1e~kZMcUf`Ihp? zJ5QzA>)luMRvt|n9p%`FsHoaMm+~7uGXE;f!-)A5^1;c)tGw*-?y&iYWcT+t1U-aF zxZf9@nkPe6_?@($0FF-$+YPj)M}Z|XZ?0rh?)z&4Yw1(t_JwEz)x-B2zO!Rc?8oTq zx^%pF7lLB1%Y4|Rp3`T12|X{&zHcUZUNxVgEAfNIeGO*bL4!9jf-JvtUC;1SKetL% zzjU_;j4xMO)NdX{VBeve4LkhdA?{C|%Xn?l!kore`eYct8R)-{moxHCz?T=M2MK@a ztW}X}_cpXtF^sGg|D0)gQ9N5#XbXzM&pt8bLaW1#}w#m3S&V{-_O;DWmqK3+SwRprwLF6N!+Cj_OWuSN7#YC zf4iql6HIKPa4=40$ujd{99W@MXJbxaWe)6gJ052 zPiCw4n#GhIteky>l;?gtK$BTud1I1*z32}*&SBiQ3BRd{b$GT9UNZ9lsui3Z>c4e^ zsfOR*;k+~2cQA$DW5KSD;gD#c;KEZIp;$wNCB&}8$}T97GT}EdrX8oRmG>uo`D{1F zsuBGphYTyg!DB>7b%hpIK*>`5^BuRKSFv>^(Y{M*A8R`-18Tg>&kr?r50rNBp40Cz zPsF{dFJEd5D`GK45fS8_rJY#6`8)m#lbFZ}EkdOCD=E{#YK=zhmRXb~4G2&&9|Zv(Mv3>F}Y zmGRB_Fcnk9S?qfnzOIj_b!`cy5NzqUen|XRo+2Kr5!n&$Q~nzn{C$k~1$LrlwWZKi z67a?1EybPjImF#@ny_Jf5C>?an<@*LVswWzBjDM~9UXrN+swG$-{ zvG%9r6ffaXAHoF^kml=OXAUJDw409*?ZQVw*k&ubVn+{XkEbF>#uG9LnOxsMEUk%cE!t7C!L?dl#%vt+C~{*n z6mHN_;vyzn41bc>VQI)MW(jxy#(%4GA{&2xC3%k%Xx|8gq*9yM^1NbOcTkJ2rL>Np zJ^z5Uu)0*NA8s%9=ea@?Y2#iv$f57g+!DU7@)9B&+HEA=^>Iko|jN9PMTp`BP*28(Jye+w1V!hsr*{wauke z6vCfLdUO}BR(v1b4ZM1(?E`P%W1w6p;g2-VA`^(rvF^VC8vUJq45Q%JW8EOrF@>m4` zQ;Sa^zn&#!hoo2fOgw?u%>{oGNKd3)qAy{*IVIrF-G9y@@9Fin)Vq$jPW7Qv6D#@n zeWn#Z(*?l+>;UKc5Ks!J#3@A;#yR4g32hSB!t`j>5hzjF#em7~0Ah!6dt@U-mIJ za=uA)>>10DYJEj8rqVYeW(V9>izZ1o#^rEPIF{^n%U0RH zbJ9yn=q>^}^U~=GX@M>$A|OybCMmfq{`^6d>{?kidtF-W?fuf&nw$rWerHYkeedv% zMg~cvxJq467)fu|nHl&(02Q&8CL=u7*C+C8f>secNqzDod#A9Z-#i$Nn}~@9_!z&q zizcBv(mm_=#iw%-%~knDyk7CKwW2l?u=#R(0HsMfs-?aeEZf5JMw#+4OFg&Zo|ZyL zx@$eV-$V&-FRlZe5kOMeSSyLXu*7X{b!$yYoh@bPLQu>+Y&hxVy7ueb`a~ zZIfJzd(q1D5H&3`^n7uuRqo5j@&w}3sMY(PKE!vHO&ha&?Nxu&d~H=1dR_t(n>RO} zo$U&N6&TuPml^VpN+z7~wsLg}u)BhA#j!D!0O7<6b)dJ^FA=9os`hpGW5@u=ud#~VuS&TC4deTdHkDms9&DsLx-=0myswN?9_>(TG7*w4?S;H>^U<@3IQKLcHf6s9ccmWXGB4L$ z-*uIn+>wx!WZP+^KU{_bF@@L6>nP|Pchf6(d_k4Z1tIVLt_fztI#z9IX*(>l*ppv( z(-)Ong%N-@&y*Yh8h|cPU1mkuvu;D9KF>Zot8)VgEnD;>80-r5A{Z!Jo}s2e3jkWD zva(FfySBr7eI$Fo|8@FyM?DuE9QS)o6F$a%U}+~yKD=<0aZK2;vD9$6Kt{@86+?(c zxH#xx%w{J|ke{^}D#JEdXU@;&F6*~j>jn2&CNGkW(By;VguM_XXYYW82L0seoSVz- zI>VAnJ0M9b`G80~HtMI@neluuN^~+e@Lsclut|T=vFIM$4dXTb9D0yt-e0(TbM#c zv&>~n**3NrWb~8%*Ae9@-K)GEH(23uqPu-W>tPuXrq36pGG|5tb^1qI2R#w$emirq z_n`_`AG*a%Td>wGXU_y3D?e+u*OF|Gt%1^dbe~{MXlVr+>-fsi?)eki1`0uRvAb!I z9n;A$(6?b`q7rIe`?C1Un!fmFuoZXRCIrDy$vs?RQZ6H|@=$W|3rMbxTE$(Sv5O5G z1s~DnW`c2*xY5pDNr+*KZmo;U!mXTo`Idc9#gUP+GvL8Tl zyC{a06FCq6O2Rcv`zM|n6DQ?wi5Y8NC=lmRH`T97G)DnxiBzkyV!#Cz^)jfXBeQi3NQQz~M*_#GdFWH<$9Lg;3;DPBhMy1r=1Bk({vL z_Hh=3lLt)pWW*Ba|HIx_$3?krZ38-}gNOlwv`vaMf)X1M1x6aAQ5>2_8iaq{`r01@BF8T@XY<(_qx}*)^%Nr6d!BI^(;Fo z_qSZ64|Pd^7#u@z^3GIY$_1WsPl0OWNfj%Dy>lNf%<|7D62_JkrYN)}CQSr?6r{L= zCDDf1K(qANIaW7ej>5}s`#6dF<37KB+aYh_bgmq3PF*qYo|ri;Y+noACp%(R^rp-o znGnUKVsULw(Hpq}zpSM`n}$)BOOQWzeD`^$U{QDV2 znzq;~FX4Lfcn|F+oNVnE)ERUu&$-}lEQ${oJdw@nmp!u_F;h-XNq>8ni{Pxj>%2^y zWaSGxY0b`oegxk{AixVz;Z z8jRqplyUNFU2QVq;nE*ax<#8vQ&9ZUa+gtmY`iSW0JZt8%%Eb+;Zpwt3XbCAL6~aB zekJ9779n2Ei}LPEMS}7fOrK{XY}+$C^g52XGf6)_AZQY4uc2B^u~J@*?V=QM^nRb- z+rj+23J)Ora@GTEF-1vHzHvQ$NhB`qk#88lrR@sg0?CF{E^tBiUdOuV`>VL?{sAW7 zLOF(?5rG@a)MbxiGqCwU^ECSRZ$em;yhK;LHazaqHXmX&7^km@)yd3#{;h0w((mr(_+ zQFYf44l6}8u~DU*lqH!R6k~C{Gya&goAAze!LlJP%#S_)!jDM0_%0N#nt`_V_G4I8 zH$YY`S)<9;1-Cw*YmgwWrsp?&vSG-Q_&fK(^X)-k5uEqimA=ypV?F5!P-@8*kfbB9 z{Y@B3RCPQD(bN`NRe}_+l!hMQe8$~`FFo#U1g4o4|8>MMV>Hyr&*u3|99a?o!EY-i zQ)YUpunj7~huu3)qRapice=Lzt)j^{Q4Ra)mGI2=$Id*5*)s(?234BKRd`_}P}Oq1 z?_0;cx{l+6RvnoGAv^{+013^&LMX>rc!ZNTz#K;RTJbs`0%UfWTAiDO!u2keybhKNoWl5ed>NA zycj9y`u%JC>{Q7}xP@^y!}O7}%u$Q_eLRay@%#PFALB}0o{R5V%TU(Z*SIXYV z*{;N-%51)NcBC6w<7J6@sn&VW=FL=2_D5qRZr|&jbSnq8ON8&$+QrDUx`KhjOWN|D zdgwllj^6Iv%-rpx^TyTf6}uKU-N8$|caqwbrp6ARF_T>Fp)(feuhE@ITuioj1^vtc zM2<5%lg2zO;%~gRx>F)a`Oym!jVn}?!pKUfsnesmHXouUOl(PVE>z@qT(}UimBam0 z1o$0o$JK{#=KEuN?LE?z`D7{89(!zUDRa9aO|PVb{5U0Dlf=A4zE}hzFCb@nXjNg= zt05-y0j75uCt5by{nkUsx%;I53oH^J^zP()bSn+kg|D0@WsJ*>SPCp6iWp zkZNsKX_*EkGBH|FozOr7q8gc&m8XNOex0B)6F}sa$_%3okrpXONL$v*+Z^L?rc76GJvDvofqJC%QmmNOfPrR3+qD-}ZF z&hysp>pChlqU55oQ;dnR><5QKRoc~G%>YWuOEcExXKU~c8q+<@?rv0*gS)n3&1RS8 z_&0}StJZA-lg0`A8E1?qqUb*F^MXHRhZi*r_Z;K#D((1~o zf{c}UXp0lJkLXLOq2D$~ZPk?Ax8DL?YgZaW@wN1bQFP1Kqy2>%hF)ZOb~u9Ny+Lljd4N zkzYUb`U55dipVJKRIzU#%s(}J?P>gVNUp>WAF~0nscA}HUj}!rym6U`V^ZCg)P43t zm5Nhrk`@c2m}UF`ctt)t_g&7dzPj2vsJ64XIhB>2$q_qiqg89?Jqfl;xl#_rpORP= z+z^doQyXKyX$7cc>Ja)v;LW#~`8XOo+hyQ(kf!Cqc@(X#;VvAFU21H^*R^tl@!v|!2zjd0R9tM6KBtcjX!dn;v^5yx#+Ivy zePJ?ry6&P_GdEwuQV>SIi)zz7buj%Nxn5Lwc3bR(DX$s>Ff-KOdP$J`x ztZy`94q~776lR~X@&1A<3CHhIW(|6V%Q1fz6Z{OXt`fP%G9K>0r5-up24l9{$?eA7 zUrdEeIQx_=!T{q#@70ahl{GarUwDeH-g(liF5Dcrd9}(Kx>Q%BlqMJ@e1kyH)4PAy z1nx+OgRlk-Yv61sMX%;@F8|guw42HYbrF1Jzj&FIq#j=Dgk7oKo2I}A2qT;~m!G!2 zZbCfHZ{3n1=#$f>-r~Mx8^0PhT=yGV!St7S(H6&0ua`7mdg(u`GsWBLi}(DV&hIyO zDRRudywk?g(($D?^HsD!U3DR-Hv(@8ka9l`gt!$HiWh~L&;;gl3@8M{L!6(QYXTuc~k*g zZx1LMjl5Tv;(QT0{<=d)Bmad8Cr*oc+nfTXi+`_U<&>jiW20s??3aWSy2|k4hdkOZ zR~`9rivk@>y=R!UKY~0VU2^Up4A2&up*3DT_6|-v&!b4=<&Zzfv!^9gcAJq-5G z^5Sw|i0GXT{6b8l)1Z!D=Gp$n^VFwcPo4YLnERvj>VNiOg`eqp_>d_l>5UorFZY@$ z|DGHERi3dbNvfu$+!os$YKXM=TsMiy4tp{Z&Hf>1f+LNsHI~oKZ2@Dp{p~{9_P-SA z7*D^1Zf@s?f3mLrc?t&!nU$8L>TyM|R5djpfz&Px;UnO?CSTp>zWU7Zsr(%O|*ex%J@@G1A zn7N+88jyNrv3$pE9=IgGou_ zCSuxpdOfPm0i>x`J|@mPqvbqkx+>f&x=Wq->Hi51k3U_xWTdT-rWQ@Mhl*K1hhX_B z#eTQG%y4RYI%Z=$QK+Zd^5^#~c?g}y^H!+!_AhqUKNHOU{FZ1|#1sC>JIRy(^m_hc z>izq#MbjZKw>s`h#7_;)fB)?}8{)seNAmxrb^EAb2pngGCTIsN=Uhsy5;j!dJZ@0j zF$9-!pju7&z#;lyj*Zgz2LLOg-j$>e1JM zmqE5oBoejL^%8DmiwzX*_q%JKHyXJt0&h`oAB{;^N{S z=$Q6G2>6nHTJ7hf_pcE*?+s8m(@hlXcMdgmekIt&V;2*@l?-k+kQHa&R$6xVi=MddM4r}m98L^#%$ENHVBmFoWe7VD~M~ZecZ}nDe>O5Tz z#Qhu^|D@3U8DJ#Y(Qw^8lMNF8@tt|>o(QoRAv1$1K%eo;l*~+F7PrNh-Gw$ruiouD z``R#X7F-xj4={?nSbXCWW47BL)_MWHIW&k*TX%c0L_PHyT?69CkC=HD&Sw2x#z}rp1k679(>$+flmr7 z?g~M0`WE~_0xqyNXmOtXU{k|n(o1k&GA}9DnPuh#84>V^tz3jHyM~TN{7HXBXm;1@ zyMRc_@4RU0V%b&!ctwvTc+;6}f|~FL%r5S>)@8=fnQ36{uhWV?c2T+Pq^w__TOS1C zW+tg;$f55DsHgxm#!%3;pjyu)yd~aQVuT_n)ixS$1F?# zXCVn22EcQ z8sjZy*RN7Qz7qhNZ%tvr^srfCq%pWQ@9Q0i7e35G{lvCju(OMmZQBKy{(`{1v{sP} z)RDDPYw8ppOrzXi`KlQR)Mf+s*R6)ZJCpiZPH$m$wSbUNID>U*cC!C)jlVF$y`kK2 zo=i@(%<2OKEqnl`fdDOdW)QZ7W%>4&OXurAe{^WcpB%(=VQAMN=y!O*fOBfMOW3)> z@1LJrWrcbTfYV7xo)aEf-OLid8X8UAo8AL$A89x>=x9A?V2LHVeWa}v|A!^ELY8>_ zdyCkQi~lgKlF)Iiw~A6D{B%qK0Ed;$h((6?19Nk8%VjoRwuPXaQ|wRJxkzeJB4+&} zwcyoa>76+_IccdB%BO8cl)~af=SmlRde)&zLDD3YM(R*}L!?*)$hkrhlv+Vi7601I zI~RP%Z*G0Bk)X%k{}xQaQ`)56)@FHiw|tq=N_v_annV=v@p}7CJ(Jn;bzGd!O`Bji ztG$wG2*zdnOvU5Xq?%IS`=>{&jQLP;h<(2C2Nj_laL+o1QeZC$SxfN{unrTR(`Qf` zZPdjNiGh`F7iW{X-dn|CsqQAV$6n;&O|F<&?iLi zeKk~V6l{;gKPf>(de{&>WAA{#rODhzaegq)iKJYffH4udlJqERc1iixMfP19uSY_S z&UE#&XPLIAs4o}wo&gH@2;KZ8EhEIvpcI&quNiD(;W^iOpt7DdQfC zGDBvW-8#fBADrW07JgMzKrcQ=pIttnXNIcs8?!-yFEfp4LXK&3?B(hV>uFo;pi0MC zRFRqMM-Fr4gCSn+0-M7dt1H40E$5%vW>o(kE#+!zZ_FG7DyPmIyA3on+evj(! zk@=^UjJDg|6LMX1eBVwMXr*X4LJYcArFmWQScZ^gk`Ou%gxq=0X-_Dg1&XSyhyKC$gm5|)TE@Z8Mtac zCJO`b^Wwx*X%5f?a?ABnG3%Q7md;_AaV8{pi1R|R(HB9C;DNHK z`F~L<{Fy}VTuydKR@iSO-}qxE^>|1s4f#$jKcW;|o~ZQP)qg^aGDg2JnLJ|PE7x)z{t%YwhMpd!fs*8TWmaTO+-Z$QmGM%twAv7q< zGGP;y)w0@lQC&17SPwE6nyZ-zSAS-FytHKB!IKrk%Ux&3Sgpog@;}6vJEVA4uCKc; zFYFwXCmhip%t55P$OUF@!4xSW9Tm9Jl$%tf-^D~nLrXmlYj1DwuvUzxymc?u<0R&( zuz6e3durDs^}a;LokNA_ldmfQUDSxYx=gPhw>j|o|Z<|E^J>cJ!PuqRe0Z6#dL0A-vm zmoHJ4xEXnu$WEYS5_8yZB`?&F&U-*-(otw`kMBFn1DdBDG>ElA1u;j|=q+GkPy zyd6JMPahPu&zEO>TxQO1ku+g`xTfTT@!JPR(+kn;9pM_p&!2LzwU&39%Doyk$bODyplmh^a^O+0j^Z=a~f*fgyT& zq}SNm%3PF4@Da=96dmhL-!M^o^J+aY=;!)a4!Eu8F}MoCT%tHhF85E>Mfue+6;!4F z!>BaY1D>Oft)II%{}N1V&^D<#a1ft;;SS%Lfx|}d6a{H)zBRk4EEe*R8Kb`KRj{#{ zR&zj12)cs;Z+@adt=C0KNZ5so73dfHPiB(23b+j*z3W+H(y4rk8yfeXe^~+LLD!?8 zprEk(n`_4~$CK)Ap(}d|ZQ_uGrxjO7Rs>dY_r*?s{`&hnUwqbUA**n$JX7Ts@f>@c zug$%rT_0?81G1ccLPCE^+LW3QV07{K7@M897k^agOlnpOq($T=IT%SJ*xufbxkT)^FgTnQ>ThvGRPpDF-Ehs)rEi-DuXk1Gd)PHI}dQy|C3@>% zv*ga_c#9IS9|EmofW|lsNiLessZ~IP)SU+IR@9e=d05=?i07aymUm1T_j(nAFQVR< zHsB_7A_*`Y+dQk8*2F$zyoy5#WXwI5H?q|i-Nm$XzF=~#2gSuugNI}pU*@@>3?kjP zRvkFrL*{XQ;!@citv3j*pIBS_YCuy0>a6g*w0wOeGw%cM_fdQV4!!@B7#KrYwF;2u zu%m-j&z#4q!4wbM0EZyoT!+9$n=bkq^{^3;;b|=N4sH<)t1&I4Ur>L?Gkyvjl3Zj8 zQgjq2#de}-wEb>`(E=vJ3b_`VaosuCR7y(~j~DnR3sX}>SXj7^cwSvo(|VULsBJCZ z3Ci>Xe!@5Tz^j5fK0bC$4ZH|(%X5R)iB&G3fSKXjG=VX_#x3#JjDe4YsqH(0)kOxt zHB&o6dxaMy2M*RU_uJH-5eb4Y{)->@C%JGEA2c3q%{5Wvzx94E!*?(`6e{tnIE-}_ zg&6>r#>ocTYjbt*Q>@N+dXR2nV18FUP${gv3pRR3*7Nb!x*)Y{7Vs|UyDJ~-9#Iby zz^w+U0~70ROy*AKvLm33qQqw2>T07q?z-s2sRhU&Pfco>IEKM09;^|`qr5gOk#3_4 zjobdWPSzoeJ|;xkL2g?vEbo&NYF|J7w8%&$Tt@ILSm9LeeA2h+$7MWW!Ld zfc6DBsE^EDzyGeNdcZmAF>c63`dAv$wAdE48#f%}=HV&G4C4NzgV^#g1SUvwVo+mS zNOD`L@xuY2wXew53ePo1P>$lfIu7l4!QKLEVdBvQk}Oa=(_N z7?ol>SGJ&lsV6VVfMn`pvPCCu6gm<7&J_ork#)Xto@CE8Z9V}Z+OSg67RrKfWS$85 zO?IWpH!y9!yqlm&YWZUnHc^1)CNe6`;aGe3Or_XzX=DGo%_Ty$je2Deg-2R)^3zn# z={>(%pq)gca`z56Z)g>J;YTb?XFG4&bWq2|eyK;pQf4+a18_c0fb`z16Thi>;~A$1 zE8I^ksF!tT8<{-MH+L=@+*)R-2lIG~t*?(I=&+(h8RaV(;DXp)cMT>zS)#AbcO-C0 zg%I*2E^q{T(0vIVlJvkf@!ai&vSaB z5r{X3qk!J;P{=T3X_P+yk#pP=gu2xhEJVm>Hy}C)L$)9Ym(Bs#sD}xsk>8fLd*Y7) zGCwchAB!t_d|w_XPv-aR7xb5Pems}9reRa`aRO<3yM<1{@e3JcDdrE-V+al|eij^Q z{f^UJ&1!0Dr}&iIEa>U!>j83qQ9$l6(eeR$L!HKDlYlo<5)puAEj2Zj9|+R*Iyy;7 z_9$&zMgp!e`9DsaHPr#1j!nzQsi_t06Mg_g$5Zh{?v#P7T8Jn+UFgFgaxP$^v+4FL zoe!ymGfm;cS?OA>2LEZ}l6dw}Rkj@0`}#qd{V^pL$N29n)v z{ZLGt`cATWyeZd&N5=}Rcr4VS3PfF2&K1}L5n9;VzFO<0mp*vRm_f17Go2|P(ttSW z*l2W4uES6U*U!eYzN6sIDg+#Ni>2lF?H)gQ=J;3?ICVGHh4I%ToCX4}-+Ws;B`6^l?z% z@&SZ)@AE|^vjfZ*4WVDq+142RhRsx=>RcR5U;uwFJn(ysJ2UjStvVL(3)u_LoSKif z9c#d+r4?+7uL05L$fn4+o7mKt+gjk+<^KBF3RucfK=aJz_;up!IG?HblkKOuO1G#t zt9LXpne2>U$73oM9LB!vg2SgiwGO34WZ4iMT+0$>24!H6E)g4qv!XszIcPoA@wE0(B#FCaJjQD+j{Zc zgXw0+QiX77ybMBZUFV=OJ@s;*#S<9HSR8p*kZXJtCWz*U_atRxG$0MsrwVc-qfbuy zo%h~DB?LQ55P$~7p$q)I#bq_Oz*Xjo7IE=XTxAl{3}MqzWt2G8! zuC6vFl}w)eRL$0Ye{&A2>Q8Y>EjUT9gnqLG=Nx%WBNy*`QPFv2)+(c7YwhxAoxYIL zB#?XwVHDt9%*JpkfrJlB=K}TUMI9z*XYelUVdd4+aRuKikV=Ne*6-F>Vq1uRbeVxy9Hk|DHik^^La40vEo@yL05 z5oX4FMnnM$J8B}a9#$_N!+_7v&-a|7dG{N}WC_rMFJ;NZ!UV~KG&zo6BBRi~S(Qi= zirOPC`b~6;_Sl1H!tG)O&vS6Dvnk6}I%2>*h~JEsxRFo+^Ku7Wm%9_6lB9kKtUD)y zVPH4BQe~Q9Dkp}GYYBYBZ>)O@x=lQ@-0PuM{R5HgoGq}u%yxa)nE9GPJ5of}icHjw zAjNoU!Vrpr?(&Uep>LNXeU-b!y@O43{9&j@bU*k7F{sIK)t)OHB4cn(U^g18k1*Ml zoM(ipV}D@|J4L8z29AJ`MMs)himH?*Q4%kUz3l=d!T_%%frh*p@E(~7RV5d&RvubJ zPXG0b^Oxf!&uy;{*gNfr-2axR{|LF%C6ep6=16A#plx{?dV*U^jd5X93DkjAoY?|u zhTv5^>R7QJSp*&Q8H4d>h<9W*n(eC%IAlBYC>yz_0var^eqDq zT}wzYzmdD52!W?W!83ngh?Bd`?r!)RrnQQl?aCOh^(Q#snQ%fP00uaQa!sb%awuGi zy;#i3hmlB5o*Jz$sj(a^zZ9p3H43~Y)7>$QZ&!bmsT(BjM)c(iN;b7*dmdEa3KAEL zDox2&;Y!elNw9+vqz~8{;!Mwl$`&8Zfo(-|6dzUA5J0J`{GvqlR5{AJOM{(7Z|xDX zI!&V}nA&{{84_;&*2Vh83S4?f**o1=)N0Cc!d-bZ42B_Ao$CPk{u~ipmwa*M?%)UE zKz{<|Sd{!kKxQaKokgg&>(vY)T>M%$e;`I$)yhd;XvSv!WiwoYX{rEN%+%k_*@xWTf}V2T>c8HitTe}PTYMvQxpUEsON_qFE24FuHclG7d*j_V;#W z{mdP3Y3(Sn#x{Z6<7{}&t$Tydi_`H=DlQQB&5nA|*hgjP1)NnB0>7jTw`5BebhwQS zDJvjnc04++YL)L0CbG4mi)M%2iwI$BA%HNlD0Sz-UMbZdO!fjOFWS^1kST*??2N!j zc*ScN)jA5As%0J_#9S3&UqrsC^Cc~K)hRD^AUvGtONGJ}G0dU$m0hUs3=_Z44_kS( zU0UfIJ}Tp^b|o0`ccOe|Bc{~!0h;x=+0upU3|(WvE-W-Y@e>)kF8waVwAkd_AJ#}o z6V1JY3N@(?u|_EqpQ#Ui;Z9Ml^6f-ESvK z@}f-4Cv)wHkeyANCwm<&vs&1LH6MnN+M&SM=}}M}5Vh|MoF9vM%*?k|`Zy@bNWjv4 zYlO%cm{Qm{1A3JJ4b}6}UI*1xFq~5Z0p1mt(|c!RNv+TqRSX*-@-ow(8tZ6Wv|0T& zvb;dJJ3=(>4sYq!WW2JPB0HuPq)TpC#5+`dS!vK=HZ6CI60?~^Qfo(cy8nJ(-iu_f zAevLDBx#U1_V}3LbeMm^NniWNZm3dZDn4AWlV1_H1)3iH0 zh(M+hfA@{J`8`yHLbr3%dmnG}62)nJVum=eUzHwFVV6MRm}3Pkd#5X;O6Xao(HFtb z$b{OgvCo`Ax9f`&0VZY5_2YOFYE;H+=xK}sENw+dg#;x694_ghjr-^@s%hMK>6ppu zoBUO;fqmi-H9*-e1cV6u8gwAHcosZvCQT`g1LuMAv z!^CO*qVor1A#<_32T2YrnjJLtov$4j2hm#vb4Ad`2=4=Ag54Jl%Ms89l*q-vz?Q_A>M zhQf0M*h_*ZgZ(QmCo8VM@b{u@r$R}w(W$6rvp-R^M{-tXVaz&yna%L}jB-IAwR?oI7=&WPsqb>#ZH%&pWS%xpI~Pk7PgF1^ z=FlXJ=iY<)dOeb5fZp{QKTyqjQ61*B@g=_UJSrE3Y%Nm*<=}}>&b?PYL@X&}g34>6 zig`sXEJj!3gf&S*f7Zk8_>^9&6yD6(I$bnl%LN!vl>3t7V~tf*!!S&032HF+ochx9 zQJr4)(z#t71%u3MmTV>QTr1PvOgi|atxYQ5W5Q>2*US7x2PYTs3Qtlb`boG@NgM$& zd_s6pW1Oz(_OA$T(P{{Dp#7wL#ZIPL>OrOw81|d6hQu116FODoP9Xa*j5x693BuE= zN`6ELA_8vVV7w7Qr}Ty>H-9(0{VL%sbx z!;@%|6nN1I$zk^POG2%oWTtRAm*GbS&Y~|CPi2(6^3tX@jU(vZ`~Be+&s*rpZ%0v= z*+rzoTm|yqaE1?D+d{umT7O5D6{tP|y*jGb#n#Y_5_;#0>ly@7+BGdxJD;Giam0T| z-FPvn9Bg~s^Q&+;=_MIe{N}fiiagb2_Rx=FBSC&M_MP=j^G0IcJJaIHE>3Cp9bNc26xBKb0~6$e8q}xohQFb7R(ECPFmWQbHQfaQ9ope1TJp83+(en_BNoO6qXe$> zi%R*UM}gLDQVkyEZtRkf@n3KVS3`-J>lVd&TxKdj99+qB*^T@M<)*G2i_7p6VWzp0 z=%L}uzFky=N?O^vp$jsVaL!Hi&`NN;h}mxrMZ-f20^oN?d}+ z6*o{bjI50e7Vu@VtH$&|5XE-TzuD{2e&IXl)Z{Lx$3t#<=k%}IXl&}9%LzTaYdiEz zNu7gDYPW%QY2Ol+vwQQM#D;g|IR)VlLF}?p7i?&)NuKl;@I{Z~OXX7PIJ+&UCcjA- z7dHaO>ugTlenQE}v75=eu?fltU*~v#_qOZ1A9VbxlmN(?cB>=l(ylwD za~5)iSrcozz4rY55_(a+kG&q1uZ}XT@J%Qm^HPI3w(XUy==}Dmu(M`+*!9_xIN2oS zjAVu9)xq*LEblSUNV+OL!ow-AdCKBWhlz1wz0n*}H6rcz)JfvJFw{*Wl;wBVH6*KG zz%Op=TZjvpzXg7&Xw#hQo_A*7T}K7r_)ME`eV$bH@TPcVGX)WZdT$cz3mA+`{XNm} zJXTiaMU>aUR&Fe|%*x|2Mb}kFv4S_%azMVVe?-|1yrfw-%uE4Pr|T+JSENo8>Dupq zVO|^~GxwVO#xvDhpxC;Y30)7|(ZbONe%!O)wwGt~YgrS0?r+Txb<9f;)CxK@l`@wT z8!PaOYLb3l?-y59B_q5Xsr#HZm+BMU+m@xowS%9&TnM|1 zt6dSow&~;=`%*fnH6=8xqdyjd{D-V@_@>pOPBCk#U5(FFNI|A^M&(tkjhe$%)D4dB za$$R6nuk-wwdRWQmHdWgVos7R$FKJUDW74`lIeW&g>wbX7^-`q{ zS{m$QbhZSc~O8V%Fm){UTv8kM%IUNiunq2hW9!)8HpMtl$STm zikZ4>=O8Y@iaMIj;ePYzwLVmq%ZNf6(dVh+1ca<9T zcw%KKBGw3qxz*9-?s?Xe|sTNuF;gwj<-gIAL;dvUhaDr%B9%|JAmal}c#{XJ+bUT>nmY?$mDT zFL7M#8EoHgHN3tcr1J`&*4ieydhVRaWlv04uqA;qmD4h+cs_ml3Nh5K&*q2~;q07v z;0M{$T=IdAdY3vEjfrG&KBob$D5^giUnfm8jvwOM#5$kUU006|eI}9mR0kiiNEE`S zNz9HC2A+S;oT1Jd$q5tyi<|tIfm-`!N@vsnn*~%yBLL!w>LgI4DBtmSnYDiIHLgZ} zVE=)X`OG=}$G%jwU37v?bu)w8bQ)3am$s+8_f)L#$T_D+@AEYap*cHI|DM7(st`(> z12YDyRwa_5P3YAiYI;YTs2+wCk;q)B;*<8h6C;w2NoS-j!1%QvUk<(sFwJG&dR zbL205t@QRBqo94z{VKhjLT%9rO_5|{b*%uG4o&V!T|7H`|LUh1`R;jYHakEV>9D4j zzON|aqVK;~_Qx}{n|zo{T3g(VPre~oZzL3Ko>=W%Y;?uwViH@DnpFRQigI%&h-ig- zgeKiWo;H^FtrS&bp4xdMs@ij0vP%TisY_oQcc1VMZ^Gy@y!(ItBzbZZ8*9bbD}PYLV9Q6+cF^ zZ2`RizE_9|!qW*>3)R_^{lYYs2$`(+zEzwgMZc+|aDg`&p{3i=pSizPi+gO!Zf+os zokYHB7f1N@W=tYY8_9~6lB73<9a@_w5=;E5!f{&D=);_C^)VAVtVU~~FPPcoFhNXO zh}z{XztLOV8NQ_`qcs>yEv(Tu#%jqj>xK=kJw;MmA~8qTKBleeMIMx8X^PXs2qso) zVdfHtQW3I+t-s6QdXnv{XA}gx&9518W9Oc()B^D<<4kS_Jwg2F_f<8ltz@^M49E3%{rBB+j{sbB^gpej$;@-e|BHRw$kWj z;WN_*c5R*Hq3O)@P!H#BS*%8+{0Me;VIn@@w_RC_Zt?R&;)C@L@5z3JD(*&5MNjXm zv^#I^E1Hc&UdvSA(hD-rz##Ut%akqqdJVF0S z^fXoUUZsNpIpgu@1EyvM$^(vC+NHB-eO1Xm7{cM}HTq4tZ_(>qfRgxRojOwyJ?GXk zN8x8k)(To#kb$*+tAJ1lVsu6gB*ycrv-#AD6y&9*;5kVa5XKVRCKJuhnMuxhNX_b~;F^ zi|{b-Vu;a)!A77A_GBlSRR>aD_eYDX2^HEsua5Go=!^5=71jvkWlLi_;_v0H=1*Zd zpTU6jaV}6?S@j^@1)`y?mSy{;$@b-oFX8@QSBES^T0PJ0#xc+$y49j;zIQ%XDOmp9 ztB!i(m`313g>up0*?SMtBc6}g9LsMbxVFWyiqW9n;?a2db`4>2>fMYQb{Ch*SQ40N zr|E)yG4{(dIyX}t#@e$SvVO&LW;6EuDrWehkMmEDP6k?~qiNfX1A3L8)FZc9B;H@@ zzCBE^QM^iaT=KUj$-T3)3Dle_G29j3c+}V){)S3#`8<1WpUQ7f4pQyQ_ANQ6=&`+U zsl+iXvW#D>c=>eBF3yhQ}KTN%O~*CK^Rr^z)Q7Y=WIS22A01*mZ|GrwJxFpiO=9mF^aV0mPVj{?JN0H8 z$vOi0E|EI#NYR_2`{x%!ayEB&zw#RM>h(S#`jN`u*u{(|1>N^EFVE(ZWq8F7y#r!j zR|PYK!n?`GlD$UkQdud>+*SA8rx@U*-^vAk>C05^;z96pa+e4#7=QX!EV)!Ru2Z?k z2$g`?P>Zvw4t65jvT;Mb=dSbp(YmnvLReY~3YFI4rSf*i&_^-8=O!#&Mu=Wq25Y4# zPvPdN&a4cMHO902uk{w4Tby2JD983*HxYB5x#G_}197U(&@bz*wH^X&5i>-mccBr@t&( zQxFN2x`Ed64;E3hHU-=BXY@9E4IbBM<>Z-1P|YvIFSz`(Y^znyz{MdZR^B z)yG%`8RDrk@4%5K|FR(lhof+W>92*-2QIv~pY6MFw>fZ*bLb{yZCm_z-|jmbxK~J- z^&NmNGZGRhnljw*X5aT(V*^zyALb~dNSWtHsou06e8T~Ksr_e8yQUh}>^pMidnd-z zpjqk$T3lu5RryKvimX>e7Xiyl*?2_Qt_S6GInKI#74NdS)M->tFRHhmXvl~aP-(jP zMSL>%PM=N9>IgFGc=M#@t673DuJ6m^8|{uG;1Hwv+4i`BTe&&_$;?F)d}UF7%6v2! z6EU=1M^*u}EtdvQHy>kPT92O-2!hCdS~dOF0!OV*)&`)>!-K+A``0#AgGD#VIShH$ zN>+xvWeb;Q#J1M9+D_81DBcnS?CiwqNjgJ zjSEtbzGq>H?WdUhQ~h{)H?>DXwI1p*AWj9NrcS!_es!ro}4Yme}2=35UA)JI7Sa)S+bh4o=QlkPu180I z9Pw7LTWj!ZnEnp$WZil9LZT6M+R2x_c7(X!k1ee#k*9f6iFZ}_t<8L@oH>( zaoLN$2^F!qWlFQekTk7>CZk~Zmg~N~C9|F~r|s7G%N`Mt4SZ6uuI)+uJIuL#d)Yq9 zkxIxi+B~ox(%h9nkMWKrzl$wZ5~34)J*7xW3gZZpBVd$|YDZLxe%nymBP`bb8DoPY z>2CTnic&)8TdL7j@Tus84_@}2a9{707GDqa5~tocM2(Uz1*O&XV`hBnZFe%cjG>Ra z?1?5C#@5gumiPS~Tu)f{q1wzOUU7q(aEa6j{dMmEi}N;H?I(aRrx}I=V2f>omUG69 zCh6#Y*(Cd^GTKPb(>JcnwIc@5CW9?^&GV?9ksZxzq!;H4z;3Ql&O*|*LVDOaYPa+$ za|X1&(sC^EAlHkT6}gMHD2a&@+3q&#CP%OfIl9h_*3lT0MxVGio%QbZqpG&e+t1*x zX|aahi@3MWmn;kPv$l0bQu%o~j=AQ0Qxq=OrJL9T)XYwcPQ4h_JpWvLj>v_t%waXu zSQiI#x5PPuVOML`Qev)EnK(T`J&bUh;dW%J9i_5}QFYxd(u$kCJXYc5w70t1?r}SR zf0q?Q*DA=7?pL)iVJX`^K)sQcc5^L5gCuf`dOR>HYA*N52ec)c5cM&=+<+z?KTL|s zlp()2v98)Zv>3^vw-_$IHXbL=5Sf$CJ|rjQ!-1;0AwT2da!dIU_x9yB2~|Jpv3KJ-9g|4U7Amsfon?Oia`z+_!P1(c}a)+B!V-t@^lCDVK? zd!vVSzk#*-d3ggD+gTu(gnT^Bp-$Xppbf!*tb*wJl`_4mSz5ro`Bw96mWmG4$>Z z>+Et{{!KlG)$sBB77IK{0caz828~%ggH0a0P6%1v=O9=wG4K!^Ta0ygNacoB{3jdH0F5#ET*I%Pyw_*74IAptY2W0o`EpU`*r11qHcOO{FL=EfwzM za#_`W{hV6%uPVoU7B!h=qCVKU*2Xqqv4OJ=)O{&2`nh`4r$Qzj7M;(AFDa5nq3;fW zgsAZU__MaF5>Fyj>P^NK3=g9q0ohE%J1Ado7q^SMf$4m~Ptxd|r&xiMWCJv=R zgQNtsW`(7t7o(Qg+I^1G7Xk@fuM~n#WVP}@yE9~iGzH(< z_IsLhG-c?CMPV&~Pk;jkLA#n~|GyWSc}|{zIBf6oq5DsA^j9)-I*p-r=;%#CTE>tP zdprh?_{pe;XYI((z*2OeU?h=utQ8oPR#P71doQ7%XZjLar25UaeNrTz&yHtVcJn1l z{A3>T*E>*(gyK8pdZy6&vp>EjLpJET;Jf~P*uNi41ofUD<$j%`FKE@%wg(I2S3XTx zgRu(^cvv#EcR*A@)HH-KHMp&fRUA0JZDqPzyzBTy<8OxzMfE)`2|`4`i7JkNni8A#%aF)J;|H(#Uc2;d~ZL>{o~F5 zPk)LQMbdF51N-G;f9}a&ZK8ktD83W${;tqz{UOu+MTz~_@*_t;O%@(4WjM7#e5c(0 z`?2_ArT+FmU^wR$cx}YJCprJK#`K?`*K-oN;_Y4AyG?$*wEy@ZJAeB41PJxVZr(oq zzxg0LkE+B3t+y1vOwo0ppC0-@{Ph^j&*VJmllBj{oS$?o|N7UIIC!-7d3BSh|Fqx! z{^nRu0(gsk@G|J%uj;>hKuLRQKoLdDC#V0&jQ%$(ar-i83VhDz9RA<0bdn!DntF|9 zq}#ub41fP1JImqev-hA!(4i|DUjOdN|NYn6mEqAQzo+E>Ip^+wvEBZAc>a5M4od#_ z@R0rAeRz~OPg>mkGg2#!l5MW9I0~9Ss8N{%YF~YXFb^(Ln_xKsdd{JqJhNdT@VJ4& zppzOIx)bF9+lJNxas87ZWP@D`ND2UXbgoW`Lwc(N`=2pSQvHx=Thhy^p8V87CMF#j zmZ`Q(gTbZo=64dvaK!@G)o-^-1`_0HI}2@wZ?(p-LrE| zWiF2HGqC{A;DGYJ)$<`mJ+%*#YU$JgKmj$OInXrr_6#)KJ0~3Z(((YC$Tw|%D-rZD zKUma$vJlX)kQ+f6k>fqJK&ZckHTe)sO(*Wv~ICJkK6t^l9CE)S674Jm!Kq!YrQ1RI^z`){tK%c9 zF3eiivnM?yM~=Xi^R3Ld?GJ}_o;%+@ouw5d$^@{{acGbP(@%qDdH4|(3p247RnoNaSnO zOx`%*shU*0r=e6=xYDNJ_ z*jJzsmz5YnsCt^

w#^2=l!)YSw0|(|WJl+WRAlTs`Gpcx2*YfkkIVlapXJpe?-a z-bZ$mOUwpnfKu80N_%_|wsW$yw_jDah{%w~u0D_^7~!mcfBSIuA+7$M2%(_;@8!l8QYi7cR@_nMI#->x-lPry zq?=TVT;Kzpe&IjTI5k^Rlzj3$cTS#pd&&*#4MB^v+s$|`@w1Zk?K3^9Bgx}43f2% zA20qt#Jy)+liRjEtZWdZsx+mlY>{dK0i`2?N|#;?0qMOHIsys;QWR;@k=_XoW?% zy!9*cRADC|eSiOOr3}9o?Q;UU_{|S~&bPYI9@qyw6JVo;K@W)JPu+jMEteZK`M+Qu zc3yp?!>OTJpgnym@wYxk)rOkACr?%vC&O%GSJ^hqow4x?0{6yOo=3B z=xGmy5OOM@@q!F>T--KgQ+DbD467p>pKH}4G^fPTrl$i#pM)4 ziu!;xwPDpQUdiW0b{*lKdrK++J9-dP43 zUcw&DZBOmDO(^)Zu#I$h6Sm74Gzacr0RqfDo;XhUVP_uzsR+T>uE#>EXTwY7xKc(8 zAeIqtCMzrRS6jzjv&J8`Z{b8oeQhsFrf!l9=7?%!X8m=DMHao}gXT^v(qA|g%)Q80 zyIqhezlARcRpPXsMdifpM;Gpq2-;27m5;fz{EoVF%1=QUBxv|OgDIZPE=k^qM^>j! z3+hLDr%xOLk)Bt~1%V4oFZTV2Sw1YLcm4u7B;!d5*cR-IX2_v6v z*X4>+2F|LT+f0?xkzhOeo#qLcsn}0O3jQ zNL+~Od9~l!pkE$`3h1)TI5}mF*3+*?1MGJxJ+y0V91R@j_S1+3%Kbgov-AcdneSb< zx_sj=tbz8~)hg38!xP?tZTu6e8RGI*%SU`FnQMrRWG z8F`2~D?F4+g_ChGCWkK!J#ztwYZ0*F60Ebhsnu&Z9o^pGImiljWtBUs>z)OjT_!r$ zgT_I?pdYMAY{N8WPL^BHxNsvf4kR>JfLNV-HlyIe&*$eC!ktR?n(N)oo9`+pl$e|^ z-iaskvm{r){UrJ-wdR~hb;R#fNu9_Km{rsOVq;&@mEsy4^n5Q^i@9aaZ6FsWy_}X3 zoZiAt%cpJpFv{g;25Bs9urc#ZCVyzXnv_BFs3wO0MD*riMh z)R)r@d(>T5MyEm1Mszz@yU7*EW7P`?IZ$rao=Is<M>ZqKcC8*D$~ z()r!mLtWi3^_>my!cEh7n>bnJz=rVg{!UHLn=_dV&YWOqkdiSt`jQxkqlsRCa3T$3 zdi^(^q7qL8oIe)*4bRZJRe&M$O(X%Ez}g1y!x~@{O;2(5LwAb+Q$+{^d`Mm=?DJoI zCfEDVKsJE_Eg`T05fc?w3#Vm1DTYR%MnkEGlPxOnT&L!F|ANh}tt0{g^>Q&h9d3DO z0BkLXnYO|=Z)TgDmjTgM4G7U{LHu_3<4d3hkIU<)o%W!1ss@xbzPhymTHIA@)Q?g|Yid8aYbyDgtLbO~ zichB$Gy?qMVGXx|J197Ez_BIu*9I{uy7O>9R~i&Uv{~2J)eeo2mPbo4y$2!+H6e(!xt= ze`5?9Pp2ZKDLKcP1w@xOKvjy~J>CS^;8cDABM8cItp>owq-S(~c_?50jAqL{or|Zx zAjwWd!;ztIkMyPOKvr1Kxm<-02dBY9f(Wc=jf9TyiHYL`@b_BJPmu8S>9cf!p4A5D zy3_JoI__GxlFvXvOpoMst+gKOe??Fg!uw$^0JLTQ_WXLhl{D}%(G%L;NiW%t50n%n z>h~_@whAXGQ6H?nx0n;zC0u4p$dxY%&^(le7DOZcpgnRAFmtpfAv|}br(ZdX7Xy8X zGl-RJ7V}!e7#9SsIg-7(qkhG&HO&f-&iIk?f}8c)$32JX*UgHjRjUBG}o?x`nUUKi;zw*HG6(cym8!x+`;BnNiXwP-t`zzwf86-I6n%0;l z%zLlGfQqCxS1tQPRc}HV^Poc_{N-smq|n1`#+S72od_39rL}sz%dgVg$cuh}Pri@m z41LW26#-cfsCk;=x99kzE=Z6bZ740(!_{6jROcSv$VzUPfqq70uGr;wkonsSAW3*g zIRcdCU?=N4g!2WqMqb>Tn*x#JS`Zy&;8tjbbQzQUvU%lkJR-}g*CP=qB_-rng*>?xv$EY|q zi$^@z{{6aT82@EbZH#(J23(a}Z~Mz@GB%;vH8c3n@I!v(GBs$l^o0xnlhe@aTyK7z z?(>As@6*o|fZji^itfb~QGalwA3RkHr+Dad_XUptH%+g3LJIjDvUHFV;=*2~F&h43 zt)C~Nd3g*d>O-Sk;9w*S#Z}J^SEehV~VHw zjy%7>J;^sOi_8|4!wOEVU28$(^5+0A_F8|G#jgrhS6R+EP|suAhi?;MNG~Zr!^14Z zVHM+F(JgV}mEHNQCh-006!(x?*f(L<@9NJML`Jjg@pbto>=F$HDg*9dv|-55uXD5G*QZr92@b@7FI6Qvk2W)wq13OB_0y;XVnMCpHPdrzb#>i7WC?;cn`db(Y>Uhth@j2@jF8Zj zYyrl2&sNqz?zP^-$*8{2P2|F%d%rvBv?xU)-|FT&>Ev%Z84ty1nLIfzhz<7!(&l|p zKj(L!;NXbGW$>8~B{NmAZbdt>E>~@GNFXCs1Mv~z@wJzd=g*pCJO@-sN>Ptm?=6)V zEslwiVU>~X$DaD(w}}&EQ^x_es$rvV=}L1NM5N}Ca6sxk%sHL>`d?sXQkCp?(Z0Uf z$thd_h9E;hh%{}`4JcyUBv3=-qtlf$nGQUQc}d$p-rpGL{b8io+12H8N~ZhjinY?| z3G)2S$EaukE5sepGh`2?Ci@1Y;Z+K*E~h%B#dKqM*z$el))31!KmY-W@(L>ea>@qN zKqKhvQIcO<>obCb)f!Osjr2KOT{tbl&F(gpYFPt~s1BEyMd%2h=AOMDl?hyrWy0Fp zoi9RYyfQE>T<@f5WqauH+RbM{m$z6Scu~qAAgDZI-ii+&&>CObrPpp5PZeHV8kO=r z-RyyXoWrR;rtCCz;5c+I_z@p|iid3KOMra>UoyiGE<%!}k~H-Fp;QuAjZ$6@0pOKi zkktVz$>XO7=?Ad4JVd zxX{G`L}(3?P8NAe!7&02_XKDiFd*}U7Q#W|VJ#zK(Qs-_kOaceV}f}_A(XLEIq;<# zcM7?6To20b1z8Rc7G*-kKt=nf!@dZgx$LDcNetWm={8oPuz%e7Xd=)c2g5I!leP=a zXNQ3-MJGU<-Ky*$bsKSr3Sqb#;0V-2)j%bF7(`ms2>4_F?3qbReaIsvcBepsqV!OL z%@nWAr>vc{c&5KV4-UJtMEC1~z5xrW4_z$>hwS40Q=+|9{d$@r18R;pK&oI`UemN5 zz+ifNWjv4|E*2ngWl+vD-0`d2z0$1|%c9v+t3ZWkQjUGced?;0bo`|qC|cIG>GS7) z{HWHmts0Y$W4L(JoqDOtyb>4U_)_(h#Kf<@tuD-_;6MYx`DaoBp)Zxs zo@H^iDM~Gz_Le?v^X5hTIYd=x*-w&-;AWzI-3d!yNkID|9ps*Ie33&QhaRMNS|Cn& z8fYzmwB!Pvem6t$6L$c|KWvmLLpD?>d`TO9?$VKnGw;-fH{A@&cp)L8d z_=5-4Xj8kLf}bR0D*Sx8%|w&d0||*TP9*n7=~EHLsi;7&FE2*jo92FIlxOBw%`?%r z9`*$MuJDmlMEJD~*)3!h0Ey0;}gHK7byhfGvcfjRFQ2tuIZsZk% z#%(_&GR7xpYl~iEiP?B|H7VrpRRvp>Hp%-*8pSN(ENXC&3|0d|NKf7 z(0AN2_v!w5_5a}qFTDKb`Ol>0?~MDOujR=p zZ$~q&P5#gS`mclkf4<`CnNZyC)b{Tu@IPL`um47-14@*U|O^B)%}d4_K7 z|1c!Ut1^J?R()l3_jl;V^OYh02cV47r-w7&Sie1@0guR8|-qq(_z z#iG%FLq`9&+W+<{@|j;4qmZ$)cL5JS_(_wNcNM?xBtTL@n8XvcT5|D!zfQlefXtWc z46gn)fSd~iz&bPk!cOX?|6u$?rz=9~*Az&-bRHN=)L$Bk$@@E6{g+>%0Zo-1Ld)#_ z!btwJN7xn)CdinBN#Vcz)_?kk3dO0w5M*PUQe1MFsz@1<=2nDtUgV za)2orQ~H12hW^#XT@^WfXsyu||NGtgeO;0Qr^YM{b!{tuXHfrbPW%sl8b1bWccluh z`M=MF-$Ttg2Zp8*KpzkJ-NENp5KH1H}`x<5)939{d!sGfAHqN z-V(_SmN0y_UH3n~)2R%n(KLm=u`AGM{T}cC_6p7hwpG{XI^6&HcBKBtsg|ZGwDZ>g z;gPzYYH7l`t$vk>{kuu_d*ptWSHaK(+}@4-8=2DYH~+rO|9=*bpCk4C)W`g=nPXl^ z)IkMYHHeaTx!MqwP!nFg1Bd%){ykaLP5rVcEcZddCo_Xa4Qg?$GOgItc6OX|1aeqt zB=bOhNHrchs}7A?Ua!frY?-(MG^{MtSUEFp}2wg*E8jC*6fNa&+HWn!&)9@c_1-q83iW?nl2P*5& zudRt6-p{5k{B*9u-o|OUs;hD(998+AP!6Rl@cbk^4EaX zqt>q3;~_P-n#`%R>QAkls1w^bpohvkM$m|k)EU~0bRJs{AzN*f?HeniossYJO}(P&U0xwI%3ZU76nBKZ=C&gOlOCH!5?53d$X~J-}Hyg};Ez1Y3r~R^` z2x5(n9<9#k7Nc4m8w^{XK)Tqep^Ll+?ehg!DJRGWfC@yKVAhs--pA{O7Q(H|RmjZU zvUT=K9%;T*svOx@5!??a?M$eLYY`I>C`egEG`MeNITpz53PD_I*-X*lynmgK(}WB| zhuG};caY}=Au@hijiU7lB8H8_H(c$$&@-ZxS>qSxi$hqoVbf`=$Q~uEsgyF>-NK|S z*48B!DKzk-I25JrHZNs^RJ;juDNGpRt;E~7Ufe2*rK@hNuSNPd$x(fUTYbaM57bn1 z%fjFWAH}fCjXmMDs27a#vrS<)=c*Ya)>O*_Pu@bYwtQue&1PIUmUx%NqOVG+RMD;iHz6?znaBE=ILP_= ze$#CE(}dOkFS8^pIwNFUBu`92r8!l?v^QLj?$W``Tq6>bl-|Zjo$9O=65jhj8(2yX^sv0 zD*`=-ia3E6Wk+jL;dK=a!VrY8W_0}viC;TbP8LD2GGxJLcadKF{^FRjrq+YkNOl(_ znZ%Wu4+54bty)xm)JWA2M%QZB2QH|%CSKIK{sU|Ul-wl<-`InM?}$j_PJ)a?YEzO2 zhgM5hq*%Krr`T_gzi8sEKuL)?J%@H!?({1LpvGXzWUD5wJ8?>2);n)um?f+N65!@# zYB@sj6804 zxU8nOBYbhOVZ3yNxAZMd&6y(qwHeWy9PO>4s*-6I3Vb?VeDHWXF9BHAf39VKk zP|i@p9X5=NB;6~x+G~H@ZWH5R8(_kdKP^kuUQuA+v$_pkFtfh6OFf_<+v8^&` zK4|JoLJcQD^;%>N6RNB6L`O2q$i1_gCJ;*|6dw7u^1G_r<~%FS>@SOP^VH&?An>#pQ`Ir zx%1{v;6bg0IjV;=)%?EYp+%$(A8WnxAu9>XmD*RIsrcZI9^wipaGz4I?`(>$^$Y}L zhu8*7MamXp%u#(IUMh`lPK%d z;*#)i3-?_4q;c%CzE(b6XtUpdO_!diH9JO(e_N)=OIyTN>~QbNi9J@XwaO!UiQJ%hIrKS@gxtS4`Pev9tu@+??Y+i=&O)x4 zuV|HhPBAlfKlppn?Q;tssT!i!j&0*hqIj2XvD$eel7^azp-bc6zrz%Eq(`le(b_E~ z&4)s;h$H(*DsxZweg>giGtbqwILvuT8bTU&uMPN4s5G-3+=IdNM?08twVb-I0N8tl zCeExW{eZh8eOlJDg8MSvGC!Gm=2h@O_n0(surr4dk{$#C{d$R+zhg8%_(gN*=mcn0 zxxcah&A<4bqLf0TZ>hoJ%Xi+6iMIx2W1VCxccQS~PESjxxE0lx@m9y;tz=J5#p%R$f+?IXGkaz3C&!nJE0$U7eq1Oy+r>uD>w1!X)5YPj&bdzyV)mB{ zY9)F+)-{rJ533p>eQ086t{L__U6tTKpsqqz#%xTd>z8>&@X#1(GOV?fm*BUS2E8}G z@M1e9e6U3)BZ)7Xw3@_4H#WG0dOYsruZ+|`y%Yai8}V1NBS$ibFi1JkE{X6jrYj!9 zD%BausY;z+9!yU|YUxgCI9DOJLz_WA|#x*?pQoG!`S2#uk&MH-0XuJ#opFveAr>N$&sdJ z;WbD?P1^2bpssJ{P3_tEAi*@JUuW^=Bp>N1y?QYx5$vqo3mDnLi*HW5-e~hF{IVWQ zB_JcmOb+fWsr2%5Y6?J;;N682aFO8wpPImJkviXSMuR1so=`p}z(0%kxSc>;kOp&w z0XWb)VXB|etvkQWT+@=x%cZ-&nU|T3Hl&~D(}|gg^gl#z2a9~(#rs07Ng+E0CPZgB zE3Ag>A$mggP?~c&=JV%VtZJxvKJ_Aof9!CQ>7VDO3;22#tovmEsbCcmZn1Qa5FJmqo;gkTo^z?@Mp@yaJAdh_DFqoNOvtvY!NnNQ{|u+Swq+o4 zps-!e64FH)(CNPf*f6l48tY58DzuA_)+_!oQl-y}==A@1Q(I*hlM*X_?+GL0@xr># z1FV6F{0CBqk~9(B)~B65$Tyqeboz*x+Zn z<58{F&$)4!xZOLRNe8ahE4sP?_{}g8RPFo&pIw*^htbf3_*Iz(O)J4Q*JjUqE|2x3 z-I+ak#o;#I_hFwV)ZN}{cL>uYT2LNNY}iSQ#3UKDe(dprx9I1OO5ePBI2l~2R;gL9 zi5C_u$MxF^8T#xwz%pLPJQLgT`B?9qz~=NYgA#X9@~Va(xVTx_9lL4M!*2Njx%!5S z^+|@VVGm6vlqhH5HV5efn*Jh#69$Zr8bwpo*p+}N; zzmB**D*STftmPROqxb^H#Bl!W5!B&K+863}TD9mNV*#~ydv*K^ zx7zUD%JgFjORRTj@*E|B3C9I@1zOCWmQGS2&f0Qh({)rO@kYebc6H-t;pcfAwR_3M zjq*|S-furFZzKm^8*)K5^Bg|Uj@d!dcOdqa5)W+8bN3z6c19_A`@3vgpxc;xKLsbRWVgDTo}@Ju43Cf$hd)?fL=TZ z@N6XNkkdpGYt@nbvu}#Jp&xZng(E%d@HH$;H@qdn?X31E;tG4%#0Ap_!=Gtsc6c=Q z?RU??h9u^{LiD$m2)7d<%L28@s57d`)MNLH$XAZs&Q}h(YdDUaEE3 z)$F$PsD!{_gBp*4qAH5FQKUhRpre)SBRV3pdd9oPhI39$c!RCJ3CiTg=_*lQTv06j zerQ$Gw)LdARWmksWX&n1sB)S(s)r!DNKg1MCfG)(;@&bBBjQHm+7AfMZ2}keN$}pQ zcAi=l*`Ku{08-{8E{7YsdeDnZ_6;|{j2{bepK!yPpk{k342A-WHeuq$M_xHpgjC6m zZQ;{2&z(2w`I7=8!f%a)t*-dIkY;}zi;2L@Bp;yVVED#RsUZJNG$EWXuDlFJgyq<<8r zVe4C-=C|hM;GpvQ1%g;NV4qEbNn8D7>icO#r~k2h9I`RBVxZbrnCJ_$y^X6Ta^=;o zV6)g#TNP-)A5Dt4<(g-@KA!Nh*<^r?{ZV%PCVg$niWSqoE`BnwOFX?KO^;jbBy!iM zYS9UJJBJwzzcG}4#A$zUsLAvVU@RXe(Sb7NU> zG@Mwy;q55B#I+gpCH_8pT!rPZJ-&;RByrWasg2ZMGZ;}P?3PR3&Bj<3GzG0DdBmdj zP54->*T2}us`brnpURrAWd6- zY0k<5k}4c)tg~lUD^wUD;~e~4kZ+wW>A;*(i)Ek>Dw8r#2jo9zX=-oY2k2-e$afa24wtkaQd2vRm z(aF->>{DBhG(08Kx!ao!(XnvnfOhsS0m%n;0H0Vq6#XT-YFT==ZR1=PbFIy213lfrBp$pBj25NcZ-@NI~g(*63JalT&*Q!XT1#kD@%(K0;rljoBSz_Aa680Dl~_BqeiJ zrM={Wj;;W2)tjneK8mm0R)X||%jM-fm%hgw3p%IC8P69QFw5O2SfOH=pGrf-?tfpl z7)MAOb)=WFZO`4)7DP=1O>&Ovb745i5ZXKKKtLXJb?|&+VBFEThrsfThw)mA_RbR% z5B6mit6I{AewCQjLd2*wqta&dcg%~fcIVSn(eED7y|+CO`rVCqplTg|ftw%PvBeLg z#M$~Du#F!`MnjXvP!N_&sRL~cVJFT{Sz$f-q29y!^}7@N?H=b(?5&d}hE+Ag8tY=9 z1%Y6L>L!NrmZNig8xWmBG@K;Q#a{cm%4v}yEis;XXk1JGiw+_x%5rNpt?^PMU&s5Z zUWUgAZK^rc#a2h(>hcJnpmpdMiy3=idaOAz2sfcXq-GO0qI+yAWET~pop`R^EY|Rb z%{DIFHv!7uA~s(6p&N8BUF)xS**1x;JGCl@)c?4cKGFe*H%UnXs=6!3p0RJbk2EHb=CM{+@T3H#Ut*r^N)thIx6Sn$ zf%d$p9!>OOWqb=_R%{^FwIZTxB5hdt1Ib>o`cY0AG0gYw@i|ALt;guBGOxOt9dotb zXE%LqHmt6{n>ZP#ycILCX0;HrgZCjmt1UJzxL&r3+^PtLt^TPEgLPbzl}fE6k`p;r zMEEQ*ttI(B5gBO{4$yZ!A@4A+9dI#!HWI$D>D7APWqmJ&xyQR=7If`>wER)dy8-sd zlhvzY5S}x+@uKOjs~Dyrt-D$%Wf;Yp(r7qlD&O%n`3XKQ+mdV%kBea4hAHc|UQH4z zM8R8kEJr@edM&POC#(yJWQ+dj?%{1i?5)k)buUc^6tmwRT{+(ER>d{CCqO(q(glWP zZL2&K%y8=hf1pOuO1lfChW@!;F>rS*Sm-MnFBk|&2nmSH&Ns0E8SYP5HC2^_qp%nd z+7zuYhr;G$j>N1q1zYio1YhT-lx%v&c9|<#;)%82QNHd_fLIoyOdh+g=k7LZjx;(M zu^LpcU+;)s-VWTEWH`w;6!em3gU!91zris4WjH$5LW^nWd+~B`geDE6uq1WBOD>|1 zL^5Gs+2nRVG$(u{_8T=$;l9FW6;tjz2kOfAA#n;BOue7tS$$L}TzhmYa~B<73&8V4 zPnNiZ;L7~4A&{&e3(hGpGoE{50+0i2r5W8G3(V&s(SEEA!mTzw*e3s(j+iIylf6?M zAZpj{2Y;GH?Z@MzuS3`qv3-*kMlulgewM8diN4+t=;F@-?xSL_D2@?$MmVK$ENTE z%O)PFzFto~p3qhsGu-xAbow79rgL^q{Ht!uM~zCi=@JWH@=$E0Ud`CzDd7YKBXFH0 zhmnPE;cNajP!Bgx*kI~&2`|*5-p&2APXP-bhGv;X0@W&u6ry9otPb%Jotv4fi|L2> zSRBl^R6(8?@Aa_H&YqcUUTZ!-YdF=rOTtudw_89)!`-4^7Z*%{+G|&o_Uf^$XX$Iw z`}jb|$%em8(Dl1Fe~5`lQr4_4WB2+_0

@t)BK7yedM>^3AF-LaSWb^>J(5g3fMv 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

w`{kSB7&7?P7!pb*sK2tFgh!l0#>G!LF>lL&* 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

Npx!7Z$L|bSV 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ℑ(t2<;4U)o1y;ZclQ2o!xDR4rQ9G0 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%0A6b0dkrS8N|TI7V7VnLppw0XXG=WG@i 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 z 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 H68QfB8R00> literal 0 HcmV?d00001 From dc7c67e9493fd487977e158afadd040b4e9ace13 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 19:06:36 +0000 Subject: [PATCH 014/158] docs/plans/miner-ui-2.md: the commit hash Co-Authored-By: Claude Fable 5.1 --- docs/plans/miner-ui-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/miner-ui-2.md b/docs/plans/miner-ui-2.md index e64cba3d..22c31cab 100644 --- a/docs/plans/miner-ui-2.md +++ b/docs/plans/miner-ui-2.md @@ -114,4 +114,4 @@ Follow-ups (engine work, not built here; the budget was three small additions): | Commit | What | |---|---| -| 4d35201, amended | the UI pass, the three engine additions with tests, the hosts' minimum size, this plan and the 19 screenshots (the blank marker in a GPU row is `n/a`, not a dash: copy law) | +| 83a293f | the UI pass, the three engine additions with tests, the hosts' minimum size, this plan and the 19 screenshots (the blank marker in a GPU row is `n/a`, not a dash: copy law) | From 41631431c0ab4bbc20469e22a12436dc9ca96963 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 19:10:07 +0000 Subject: [PATCH 015/158] Workers: a pack's seed words are its program attempt's words, not the bare seed's (epoch 34 incident, 5 October 2026) From 18:23Z both Windows workers (CUDA on PC 1 and PC 2, OpenCL on PC 1 after the 18:34Z node restart) refused every pack for epoch 34 with "the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT", and the miner and the app restarted them every 5 to 60 s until 18:44Z and beyond. The packs were correct. The generator retries a rejected candidate with seed || k_le32 (attempt_words); epoch 34's attempt 0 was rejected (246 of 16384 final register values saturated, limit 163) and attempt 1 accepted, so the pack carried attempt 1's words while pf_load (proto-cuda/nvrtc/packfile.h) derived the expected words from the bare seed. Both workers also matched jobs to pairs by those bare-seed words, so even a loaded pack of a retried program would have answered "epoch seed mismatch" on every job. The rule, in one place per language: - packfile.h: pf_program_words(bytes, attempt); pf_load reads IGNEUM_PROGRAM_ATTEMPT and checks the attempt's words; the refusal says "program pack and its seeds disagree: IGNEUM_SEEDW_INIT is not attempt N of the epoch seed ..." in plain words. - worker.cpp and proto-opencl/host.c: a job belongs to a pair when the seed hex the node sent is the pair's (pairIs); the compiled-in placeholder pack keeps the word comparison. - igneum-pow/src/packcheck.rs: verify_pack_texts / verify_pack_dir, the same rule in Rust; the miner checks every pack it writes with it before a worker sees it (vendor/igneum-node pack-loop branch). Tests pin the attempt vectors of epoch 34 on both sides (one vector, two implementations), that epoch 34 is attempt 1 and epoch 33 attempt 0, a known-good pack of a later attempt, a known-mismatched (out of date) pack, and self-contradicting packs. - proto-cuda/nvrtc/emu/packfile-test.c (+ .sh, in CI): pf_load on a known-good attempt-1 pack, the checked-in attempt-0 pack, and the known-mismatched bare-words pack. The app (app/igneum-app): - watchdog.rs: PACK_OUT_OF_DATE_CODE 44, PackRebuilds (at most 3 pack exports per epoch, then the card shows the reason), pack_refusal (the worker's "error 0 pack" line and the miner's "PACK OUT OF DATE" line), pack_epoch_of; tests on the incident lines, known-good and known-mismatched. - engine.rs: exit 44 exports the pack again before the restart instead of a blind restart, the strip says "program pack out of date, rebuilding", the card and the log name the condition; at the cap the card is marked failed with the reason and tries again in 10 minutes. The relay (relay/lib/parse.mjs): PACK_MISMATCH; the card reads "pack mismatch, rebuilding (N refusals in the tail, M restarts)" in `node tools/console.mjs machines` instead of a bare restart count; tests on the PC 2 tail of 18:27Z and a healthy tail. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 2 + app/igneum-app/src/engine.rs | 64 ++++++ app/igneum-app/src/watchdog.rs | 110 ++++++++++ igneum-pow/src/lib.rs | 1 + igneum-pow/src/packcheck.rs | 304 ++++++++++++++++++++++++++ proto-cuda/nvrtc/emu/packfile-test.c | 115 ++++++++++ proto-cuda/nvrtc/emu/packfile-test.sh | 11 + proto-cuda/nvrtc/packfile.h | 28 ++- proto-cuda/nvrtc/worker.cpp | 27 ++- proto-opencl/host.c | 25 ++- relay/lib/parse.mjs | 6 + relay/test/parse.test.mjs | 31 ++- 12 files changed, 710 insertions(+), 14 deletions(-) create mode 100644 igneum-pow/src/packcheck.rs create mode 100644 proto-cuda/nvrtc/emu/packfile-test.c create mode 100755 proto-cuda/nvrtc/emu/packfile-test.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea5ce045..31d05cdc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,8 @@ jobs: - name: igneum-pow tests (release) working-directory: igneum-pow run: cargo test --release + - name: pack loader seed rule (packfile.h on a known-good and a known-mismatched pack) + run: bash proto-cuda/nvrtc/emu/packfile-test.sh - name: igneum-census build (release) working-directory: igneum-census run: cargo build --release diff --git a/app/igneum-app/src/engine.rs b/app/igneum-app/src/engine.rs index 85075432..c447c89a 100644 --- a/app/igneum-app/src/engine.rs +++ b/app/igneum-app/src/engine.rs @@ -395,6 +395,8 @@ struct MinerSlot { error_at: Option, /// the per-card watchdog (src/watchdog.rs): one restart, then faulted watch: crate::watchdog::CardWatch, + /// pack refusals (src/watchdog.rs): the pack is exported again before the restart, capped per epoch + pack_rebuilds: crate::watchdog::PackRebuilds, } pub struct Engine { @@ -1172,6 +1174,7 @@ impl Engine { last_status: None, error_at: None, watch: crate::watchdog::CardWatch::new(), + pack_rebuilds: crate::watchdog::PackRebuilds::new(), }); } if self.miners.is_empty() { @@ -2282,6 +2285,7 @@ impl Engine { if !p.alive() { let code = p.exit_code.unwrap_or(-1); let tail = tail_of(&p.log_path, 3); + let long_tail = if code == crate::watchdog::PACK_OUT_OF_DATE_CODE { tail_of(&p.log_path, 40) } else { Vec::new() }; self.miners[i].proc = None; let t = self.secs(now); let verdict = self.miners[i].watch.event(t, crate::watchdog::Event::Exited(code)); @@ -2291,6 +2295,37 @@ impl Engine { self.miners[i].needs_rebuild = true; } self.miners[i].restart_at = Some(now); + } else if code == crate::watchdog::PACK_OUT_OF_DATE_CODE { + // The worker refused its program pack and the miner could not rebuild it (or hit its own + // cap): export the pack from the node again before the next start, not a blind restart; + // at most PACK_REBUILD_CAP times per epoch, then the card shows the reason + let why = long_tail.iter().rev().find_map(|l| crate::watchdog::pack_refusal(l)).unwrap_or_else(|| "the worker refused its program pack".into()); + let epoch = std::fs::read_to_string(self.shared.runtime.app_dir.join("packs").join("devnet").join("seeds.txt")).ok().and_then(|s| crate::watchdog::pack_epoch_of(&s)).unwrap_or_default(); + let name = self.st().mining.cards.get(card_idx).map(|c| c.name.clone()).unwrap_or_else(|| self.miners[i].label.clone()); + match self.miners[i].pack_rebuilds.decide(&epoch, &why) { + crate::watchdog::PackAction::Rebuild { n, cap } => { + self.shared.event("build", "program pack out of date, rebuilding"); + self.shared.log(&format!("{name}: program pack out of date, rebuilding (export {n} of {cap} for epoch {epoch}): {why}")); + self.miners[i].prepared = false; // prepare_worker exports the pack again before the start + self.miners[i].restart_at = Some(now); + if let Some(c) = self.st().mining.cards.get_mut(card_idx) { + c.state = "restarting".into(); + c.hash_now = 0.0; + c.message = "program pack out of date, rebuilding".into(); + } + } + crate::watchdog::PackAction::GiveUp { n: _, reason } => { + self.shared.event("error", &format!("{name}: {reason}")); + self.shared.log(&format!("{name}: {reason}; next try in 10 minutes or at the next hour")); + self.miners[i].prepared = false; + self.miners[i].restart_at = Some(now + Duration::from_secs(600)); + if let Some(c) = self.st().mining.cards.get_mut(card_idx) { + c.state = "failed".into(); + c.hash_now = 0.0; + c.message = reason; + } + } + } } else if verdict != crate::watchdog::Action::None { // exit 43: the miner gave up on its worker; once more, then the card is faulted self.watchdog_verdict(i, verdict, &tail); @@ -2368,6 +2403,25 @@ impl Engine { } } + /// The strip, the log and the card for a program pack the worker refused or the miner found stale: the miner + /// rebuilds the pack and restarts the worker itself (or exits 44 for the app to export it). One strip line per + /// 30 s: the miner prints the refusal on stderr and its own line on stdout. + fn pack_notice(&mut self, i: usize, card: usize, card_name: &str, why: &str) { + let now = Instant::now(); + self.shared.log(&format!("{card_name}: program pack out of date, rebuilding: {why}")); + if now.duration_since(self.last_error_event) >= Duration::from_secs(30) { + self.last_error_event = now; + self.shared.event("build", "program pack out of date, rebuilding"); + } + self.miners[i].error_at = Some(now); + let t = self.secs(now); + self.miners[i].watch.event(t, crate::watchdog::Event::WorkerRestart("program pack out of date, rebuilding")); + if let Some(c) = self.st().mining.cards.get_mut(card) { + c.hash_now = 0.0; + c.message = "program pack out of date, rebuilding".into(); + } + } + /// Applies a watchdog verdict to slot `i` (its process already stopped or gone): a restart now with the reason on /// the card, or the card marked faulted with the reason in the UI and the log while the other cards keep mining. fn watchdog_verdict(&mut self, i: usize, verdict: crate::watchdog::Action, tail: &[String]) { @@ -2681,6 +2735,10 @@ impl Engine { if let Some(c) = self.st().mining.cards.get_mut(card) { c.message = "the node is not answering; the miner retries".into(); } + } else if let Some(why) = crate::watchdog::pack_refusal(text) { + // the worker refused its program pack; the miner rebuilds the pack and restarts the worker itself + // (or exits 44 for us to export it): the strip and the card name the condition in plain words + self.pack_notice(i, card, &card_name, &why); } else if text.contains("WORKER MISMATCH") || text.contains("worker error") || text.contains("worker exited") || text.contains("worker killed by a guard") || text.contains("panicked") || text.contains("CUDA error") || text.contains("submit error") { let now = Instant::now(); if now.duration_since(self.last_error_event) >= Duration::from_secs(30) { @@ -2703,6 +2761,12 @@ impl Engine { } return; } + if text.contains("PACK OUT OF DATE") { + // the miner found its pack stale or refused (stdout): it rebuilds the pack before the restart + let why = crate::watchdog::pack_refusal(text).unwrap_or_else(|| short(text, 160)); + self.pack_notice(i, card, &card_name, &why); + return; + } if text.contains(" WORKER FAULT ") { // the miner's guards (interval, job time, cpu re-check, stall) killed the worker; it restarts it itself let reason = crate::watchdog::fault_reason(text).unwrap_or_else(|| short(text, 160)); diff --git a/app/igneum-app/src/watchdog.rs b/app/igneum-app/src/watchdog.rs index 27f30c83..369ad822 100644 --- a/app/igneum-app/src/watchdog.rs +++ b/app/igneum-app/src/watchdog.rs @@ -27,6 +27,12 @@ pub const HEALTHY_RESET_S: f64 = 300.0; pub const MINER_GAVE_UP_CODE: i32 = 43; /// No `watch` reading from our node for this long: restart it. pub const NODE_SILENT_S: f64 = 120.0; +/// `igneum-miner` exit code when its worker refused the program pack it was started with and the miner could not +/// rebuild it (or rebuilt it `PACK_REBUILD_CAP` times this epoch): the app exports the pack from the node again +/// before the next start, at most `PACK_REBUILD_CAP` times per epoch, then shows the card. +pub const PACK_OUT_OF_DATE_CODE: i32 = 44; +/// Pack rebuilds per epoch seed before the app stops restarting the miner on it. +pub const PACK_REBUILD_CAP: u32 = 3; /// Node restarts inside this window grow the delay before the next one. pub const NODE_WINDOW_S: f64 = 600.0; @@ -280,10 +286,114 @@ fn kv_f64(line: &str, key: &str) -> Option { kv(line, key)?.trim_end_matches('s').parse().ok() } +/// The decision after a pack refusal (exit `PACK_OUT_OF_DATE_CODE`, or a `PACK OUT OF DATE` / `error 0 pack` line +/// from the miner): rebuild the pack before the restart, or stop for this epoch. 5 October 2026: the app restarted +/// two workers every 20 to 40 s for an hour on a pack neither it nor the miner had re-exported in between. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PackAction { + /// Export the pack from the node again (the n-th time this epoch) and start the miner on it now. + Rebuild { n: u32, cap: u32 }, + /// The cap for this epoch is reached: show the card with this reason and wait for the next epoch (or a reset). + GiveUp { n: u32, reason: String }, +} + +/// Pack rebuilds per epoch seed (the epoch the exported pack names in its seeds.txt). +#[derive(Debug, Default)] +pub struct PackRebuilds { + epoch: Option, + n: u32, +} + +impl PackRebuilds { + pub fn new() -> Self { + Self::default() + } + + /// The miner exited over its pack while the exported pack is for `epoch`; `why` is the miner's reason. + pub fn decide(&mut self, epoch: &str, why: &str) -> PackAction { + if self.epoch.as_deref() != Some(epoch) { + self.epoch = Some(epoch.to_string()); + self.n = 0; + } + if self.n >= PACK_REBUILD_CAP { + return PackAction::GiveUp { n: self.n, reason: format!("the program pack still fails after {} rebuilds this epoch ({why}); the worker and the pack disagree", self.n) }; + } + self.n += 1; + PackAction::Rebuild { n: self.n, cap: PACK_REBUILD_CAP } + } + + pub fn count(&self) -> u32 { + self.n + } +} + +/// A miner line that names a pack refusal: the worker's own `error 0 pack : ` (relayed as +/// `worker error: ...`) or the miner's `PACK OUT OF DATE : `. Returns the reason, in plain words. +pub fn pack_refusal(text: &str) -> Option { + if let Some(i) = text.find("PACK OUT OF DATE") { + let rest = text[i + "PACK OUT OF DATE".len()..].trim_start_matches(':').trim(); + return Some(rest.split_once(": ").map(|(_, why)| why).unwrap_or(rest).to_string()); + } + if let Some(i) = text.find("error 0 pack ") { + let rest = &text[i + "error 0 pack ".len()..]; + let (_, why) = rest.split_once(": ")?; + return Some(why.trim().to_string()); + } + None +} + +/// The epoch seed hex an exported pack names (`epoch_seed_hex ` in its seeds.txt), 16 chars. +pub fn pack_epoch_of(seeds_txt: &str) -> Option { + seeds_txt.lines().find_map(|l| l.strip_prefix("epoch_seed_hex ")).map(|h| h.trim().chars().take(16).collect()) +} + #[cfg(test)] mod tests { use super::*; + // The refusal as PC 1 and PC 2 logged it on 5 October 2026 (epoch 34), the miner's own line, and non-refusals. + const REFUSAL: &str = "! 1791224840.037 worker error: error 0 pack packs\\devnet: the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT (wrong seeds.txt for this pack?)"; + const MINER_LINE: &str = "1791224840.100 PACK OUT OF DATE packs\\devnet: the worker refused its program pack (the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT); rebuilding the program pack before the restart"; + + #[test] + fn pack_refusal_reads_both_lines_and_nothing_else() { + assert_eq!(pack_refusal(REFUSAL).unwrap(), "the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT (wrong seeds.txt for this pack?)"); + assert!(pack_refusal(MINER_LINE).unwrap().starts_with("the worker refused its program pack")); + assert_eq!(pack_refusal("! 1791224651.247 worker error: error 5838 epoch seed mismatch: this worker holds epoch bed7ab62cbece66c"), None); + assert_eq!(pack_refusal(STATUS_OK), None); + assert_eq!(pack_epoch_of("epoch_seed_hex 009858237e118f69abc8d096e9b1af21c24539eaecdfd1b896588825660a69ec\nday_seed_hex 69676e65\n").as_deref(), Some("009858237e118f69")); + assert_eq!(pack_epoch_of("day_seed_hex 69676e65\n"), None); + } + + /// Known-good: a refusal rebuilds the pack, once per refusal, and a new epoch starts the count again. + #[test] + fn pack_rebuilds_known_good() { + let mut p = PackRebuilds::new(); + assert_eq!(p.decide("009858237e118f69", "x"), PackAction::Rebuild { n: 1, cap: PACK_REBUILD_CAP }); + assert_eq!(p.decide("009858237e118f69", "x"), PackAction::Rebuild { n: 2, cap: PACK_REBUILD_CAP }); + assert_eq!(p.decide("5e5d0c3b2a19f8e7", "x"), PackAction::Rebuild { n: 1, cap: PACK_REBUILD_CAP }); + assert_eq!(p.count(), 1); + } + + /// Known-mismatched: a pack the worker refuses after every rebuild stops at the cap with the reason in plain + /// words, and stays stopped for that epoch. + #[test] + fn pack_rebuilds_known_mismatched_gives_up_at_the_cap() { + let mut p = PackRebuilds::new(); + for n in 1..=PACK_REBUILD_CAP { + assert_eq!(p.decide("009858237e118f69", "the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT"), PackAction::Rebuild { n, cap: PACK_REBUILD_CAP }); + } + match p.decide("009858237e118f69", "the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT") { + PackAction::GiveUp { n, reason } => { + assert_eq!(n, PACK_REBUILD_CAP); + assert_eq!(reason, "the program pack still fails after 3 rebuilds this epoch (the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT); the worker and the pack disagree"); + } + a => panic!("{a:?}"), + } + assert!(matches!(p.decide("009858237e118f69", "x"), PackAction::GiveUp { .. })); + assert_eq!(p.decide("5e5d0c3b2a19f8e7", "x"), PackAction::Rebuild { n: 1, cap: PACK_REBUILD_CAP }); + } + // Lines as igneum-miner 0.3.x prints them (devnet v4, 4 October 2026; identities and labels shortened). const STATUS_OK: &str = "1791138616.597 STATUS 'win-1' [worker]: 70s jobs=486 accepted=3 rejected=0 mismatched=0 extra=0 rate=0.04 blocks/s hash=123.90 MH/s wall (124.20 MH/s inside jobs) now=124.10 MH/s wall (124.30 MH/s inside jobs, 70 jobs, seed walk 0 calls) template_age=0.31s synced=true idle=0.2% (last 10s: 0.1%) queued=2 restarts=0 faults=0 identities=8 accepted_by_identity=1/0/1/0/0/1/0/0"; const STATUS_ZERO: &str = "1791138626.597 STATUS 'win-1' [worker]: 80s jobs=486 accepted=3 rejected=0 mismatched=0 extra=0 rate=0.04 blocks/s hash=108.41 MH/s wall (124.20 MH/s inside jobs) now=0.00 MH/s wall (0.00 MH/s inside jobs, 0 jobs, seed walk 0 calls) template_age=0.31s synced=true idle=12.5% (last 10s: 100.0%) queued=2 restarts=0 faults=0 identities=8 accepted_by_identity=1/0/1/0/0/1/0/0"; diff --git a/igneum-pow/src/lib.rs b/igneum-pow/src/lib.rs index a35f37a4..2c51a620 100644 --- a/igneum-pow/src/lib.rs +++ b/igneum-pow/src/lib.rs @@ -26,6 +26,7 @@ pub mod bind; pub mod emit; pub mod generator; pub mod memhard; +pub mod packcheck; pub mod seed; pub mod verify; diff --git a/igneum-pow/src/packcheck.rs b/igneum-pow/src/packcheck.rs new file mode 100644 index 00000000..fdcd185a --- /dev/null +++ b/igneum-pow/src/packcheck.rs @@ -0,0 +1,304 @@ +//! A program pack on disk, read the way the one-click workers read it (`proto-cuda/nvrtc/packfile.h`, `pf_load`), +//! and checked against the seeds the node is on. +//! +//! The rule (5 October 2026, the epoch 34 incident on both PCs): a pack's `IGNEUM_SEEDW_INIT` is the seed words of +//! the program's ATTEMPT, `attempt_words(epoch_seed, IGNEUM_PROGRAM_ATTEMPT)`, not the words of the bare seed. The +//! generator retries a rejected candidate with `seed || k_le32` (spec 01 section 1.4.6), so from attempt 1 on the +//! bare-seed words and the pack's words differ. The workers derived the expected words from the bare seed, refused +//! every pack of a retried program ("the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT") and the miner +//! and the app restarted them forever. Epoch 34 (seed `009858237e11...`) was the first live epoch whose program is +//! a later attempt. This module is the one place that rule is written in Rust; the miner checks every pack it +//! writes with it before a worker sees the pack, and the tests pin the attempt vectors the C side also pins. + +use crate::generator::attempt_words; +use crate::seed::seed_words_from_bytes; +use std::fmt; +use std::path::Path; + +/// What a pack says about itself. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PackIdentity { + pub epoch_hex: String, + pub day_hex: String, + pub attempt: u32, + pub seedw: [u32; 8], + pub keyw: [u32; 8], +} + +/// Why a pack is not the one a worker should mine with. `Display` is the plain-words line the logs carry. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PackFault { + /// program.h or seeds.txt is missing or does not parse. + Unreadable(String), + /// A well-formed pack for other seeds than the node's: the pack is stale (or the node moved on). + OutOfDate { pack_epoch: String, pack_day: String, want_epoch: String, want_day: String }, + /// The files of one pack contradict each other (seeds.txt against program.h, or the init words against the + /// seeds and the attempt): a half-written or hand-edited pack, or a worker and an exporter on different rules. + Disagree(String), +} + +impl fmt::Display for PackFault { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PackFault::Unreadable(w) => write!(f, "program pack unreadable: {w}"), + PackFault::OutOfDate { pack_epoch, pack_day, want_epoch, want_day } => write!( + f, + "program pack out of date: the pack is for epoch {} day {}, the node is on epoch {} day {}", + short(pack_epoch), + day_label(pack_day), + short(want_epoch), + day_label(want_day) + ), + PackFault::Disagree(w) => write!(f, "program pack and its seeds disagree: {w}"), + } + } +} + +impl std::error::Error for PackFault {} + +fn short(hex: &str) -> &str { + if hex.len() >= 16 { + &hex[..16] + } else { + hex + } +} + +/// The day bytes are `igneum-day/` followed by the little-endian day index (`bind::day_bytes`); print the index +/// when the hex has that shape, else the hex. +fn day_label(hex: &str) -> String { + const PREFIX: &str = "69676e65756d2d6461792f"; // "igneum-day/" + if let Some(rest) = hex.strip_prefix(PREFIX) { + if let Some(bytes) = unhex(rest) { + let mut v = 0u64; + for (i, b) in bytes.iter().enumerate().take(8) { + v |= (*b as u64) << (8 * i); + } + return v.to_string(); + } + } + hex.to_string() +} + +pub fn hex(bytes: &[u8]) -> String { + bytes.iter().map(|b| format!("{b:02x}")).collect() +} + +fn unhex(s: &str) -> Option> { + if s.len() % 2 != 0 { + return None; + } + (0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16).ok()).collect() +} + +/// `#define NAME ` in a header; the value as text, trimmed, with a trailing `//` comment removed. +fn define(text: &str, name: &str) -> Option { + for line in text.lines() { + let t = line.trim_start(); + let Some(rest) = t.strip_prefix("#define ") else { continue }; + let rest = rest.trim_start(); + let Some(after) = rest.strip_prefix(name) else { continue }; + if !after.starts_with(|c: char| c.is_whitespace()) { + continue; + } + let v = after.trim(); + let v = v.split("//").next().unwrap_or("").trim(); + return Some(v.to_string()); + } + None +} + +fn define_str(text: &str, name: &str) -> Option { + let v = define(text, name)?; + let v = v.strip_prefix('"')?.strip_suffix('"')?; + Some(v.to_string()) +} + +fn define_u32(text: &str, name: &str) -> Option { + let v = define(text, name)?; + let v = v.trim_end_matches('u'); + if let Some(h) = v.strip_prefix("0x") { + u32::from_str_radix(h, 16).ok() + } else { + v.parse().ok() + } +} + +fn define_words(text: &str, name: &str) -> Option<[u32; 8]> { + let v = define(text, name)?; + let inner = v.trim().strip_prefix('{')?.strip_suffix('}')?; + let mut out = [0u32; 8]; + let mut n = 0; + for part in inner.split(',') { + let p = part.trim().trim_end_matches('u'); + if p.is_empty() { + continue; + } + if n >= 8 { + return None; + } + out[n] = if let Some(h) = p.strip_prefix("0x") { u32::from_str_radix(h, 16).ok()? } else { p.parse().ok()? }; + n += 1; + } + (n == 8).then_some(out) +} + +/// One `key value` line of seeds.txt. +fn seeds_line(text: &str, key: &str) -> Option { + text.lines().find_map(|l| l.strip_prefix(key).and_then(|r| r.strip_prefix(' ')).map(|v| v.trim().to_string())) +} + +/// Checks the texts of a pack (program.h, and seeds.txt when it exists) against the seeds a worker will be asked +/// to mine with. Pure: the miner and the tests call it with file contents. +pub fn verify_pack_texts(program_h: &str, seeds_txt: Option<&str>, want_epoch: &[u8], want_day: &[u8]) -> Result { + let seedw = define_words(program_h, "IGNEUM_SEEDW_INIT").ok_or_else(|| PackFault::Unreadable("program.h has no IGNEUM_SEEDW_INIT with 8 words".into()))?; + let keyw = define_words(program_h, "IGNEUM_KEY_INIT").ok_or_else(|| PackFault::Unreadable("program.h has no IGNEUM_KEY_INIT with 8 words".into()))?; + let attempt = define_u32(program_h, "IGNEUM_PROGRAM_ATTEMPT").unwrap_or(0); + let mut epoch_hex = define_str(program_h, "IGNEUM_SEED_BYTES_HEX").unwrap_or_default(); + let mut day_hex = define_str(program_h, "IGNEUM_DAY_BYTES_HEX").unwrap_or_default(); + if let Some(s) = seeds_txt { + let e = seeds_line(s, "epoch_seed_hex").ok_or_else(|| PackFault::Unreadable("seeds.txt has no epoch_seed_hex line".into()))?; + let d = seeds_line(s, "day_seed_hex").ok_or_else(|| PackFault::Unreadable("seeds.txt has no day_seed_hex line".into()))?; + if !epoch_hex.is_empty() && !epoch_hex.eq_ignore_ascii_case(&e) { + return Err(PackFault::Disagree(format!("seeds.txt names epoch {} but program.h was generated for epoch {} (a pack half rewritten?)", short(&e), short(&epoch_hex)))); + } + if !day_hex.is_empty() && !day_hex.eq_ignore_ascii_case(&d) { + return Err(PackFault::Disagree(format!("seeds.txt names day {} but program.h was generated for day {}", day_label(&d), day_label(&day_hex)))); + } + epoch_hex = e.to_ascii_lowercase(); + day_hex = d.to_ascii_lowercase(); + } + if epoch_hex.is_empty() || day_hex.is_empty() { + return Err(PackFault::Unreadable("no seeds: neither seeds.txt nor IGNEUM_SEED_BYTES_HEX / IGNEUM_DAY_BYTES_HEX in program.h".into())); + } + let epoch_bytes = unhex(&epoch_hex).filter(|b| b.len() == 32).ok_or_else(|| PackFault::Unreadable("the epoch seed is not 32 bytes of hex".into()))?; + let day_bytes = unhex(&day_hex).ok_or_else(|| PackFault::Unreadable("the day seed hex is malformed".into()))?; + // The pack's own consistency first: a pack that contradicts itself is never "out of date", it is broken + let want_w = attempt_words(&epoch_bytes, attempt); + if want_w != seedw { + return Err(PackFault::Disagree(format!( + "IGNEUM_SEEDW_INIT is not attempt {attempt} of the epoch seed {} (the words of attempt {attempt} are {:08x} {:08x} ..., the pack has {:08x} {:08x} ...)", + short(&epoch_hex), + want_w[0], + want_w[1], + seedw[0], + seedw[1] + ))); + } + let want_k = seed_words_from_bytes(&day_bytes); + if want_k != keyw { + return Err(PackFault::Disagree(format!("IGNEUM_KEY_INIT is not the key of the day seed {} ", day_label(&day_hex)))); + } + let want_epoch_hex = hex(want_epoch); + let want_day_hex = hex(want_day); + if epoch_hex != want_epoch_hex || day_hex != want_day_hex { + return Err(PackFault::OutOfDate { pack_epoch: epoch_hex, pack_day: day_hex, want_epoch: want_epoch_hex, want_day: want_day_hex }); + } + Ok(PackIdentity { epoch_hex, day_hex, attempt, seedw, keyw }) +} + +/// [`verify_pack_texts`] over a pack directory. +pub fn verify_pack_dir(dir: &Path, want_epoch: &[u8], want_day: &[u8]) -> Result { + let program_h = std::fs::read_to_string(dir.join("program.h")).map_err(|e| PackFault::Unreadable(format!("cannot read {}/program.h: {e}", dir.display())))?; + let seeds = std::fs::read_to_string(dir.join("seeds.txt")).ok(); + verify_pack_texts(&program_h, seeds.as_deref(), want_epoch, want_day) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::emit::program_header; + use crate::generator::generate_from_seed_bytes; + use crate::verify::Epoch; + + // The two live devnet epochs of 5 October 2026 (epoch 33 mined, epoch 34 refused by the one-click workers) + const EPOCH_33: &str = "bed7ab62cbece66cf791485336d81d90fa1452ffed28ecd8a7416960ef64164c"; + const EPOCH_34: &str = "009858237e118f69abc8d096e9b1af21c24539eaecdfd1b896588825660a69ec"; + // "igneum-day/" || le64(20731) + const DAY_20731: &str = "69676e65756d2d6461792ffb50000000000000"; + + fn bytes(h: &str) -> Vec { + unhex(h).unwrap() + } + + /// The attempt vectors the C side pins too (proto-cuda/nvrtc/emu/packfile-test.c): a change to either + /// derivation fails on one side first. + #[test] + fn attempt_words_vectors_shared_with_the_workers() { + let e = bytes(EPOCH_34); + assert_eq!(attempt_words(&e, 0), [0x06af2a61, 0x4d67274e, 0x4ebda738, 0xad1dea73, 0x6233cd8c, 0x50371601, 0x39d0b873, 0x6af024a2]); + assert_eq!(attempt_words(&e, 1), [0x0dcff56b, 0x6b1beb0d, 0x234dc70c, 0xe4016fa9, 0x72397152, 0xb558aa79, 0x3ffb3299, 0x72b9962e]); + // epoch 33's bare words, as the CUDA worker printed them on 5 October ("seed words be5983a6 f750dab7 ...") + assert_eq!(attempt_words(&bytes(EPOCH_33), 0)[..2], [0xbe5983a6, 0xf750dab7]); + } + + /// The incident: epoch 34's program is a later attempt, epoch 33's is the bare seed. A worker that derives the + /// words from the bare seed accepts 33 and refuses 34. + #[test] + fn epoch_34_program_is_a_later_attempt() { + let p34 = generate_from_seed_bytes("epoch 34", &bytes(EPOCH_34)); + assert!(p34.attempt >= 1, "epoch 34 must be a retried program for the incident to reproduce; attempt {}", p34.attempt); + assert_eq!(p34.seed, attempt_words(&bytes(EPOCH_34), p34.attempt)); + assert_ne!(p34.seed, attempt_words(&bytes(EPOCH_34), 0)); + let p33 = generate_from_seed_bytes("epoch 33", &bytes(EPOCH_33)); + assert_eq!(p33.attempt, 0); + } + + fn pack_texts(epoch_hex: &str, day_hex: &str) -> (String, String, u32) { + let (e, d) = (bytes(epoch_hex), bytes(day_hex)); + let epoch = Epoch::from_seed_bytes(&e, &d, "test"); + let h = program_header(&epoch.program, "test day", &epoch.dataset); + let s = format!("epoch_seed_hex {epoch_hex}\nday_seed_hex {day_hex}\nday_index 20731\n"); + (h, s, epoch.program.attempt) + } + + /// Known-good: the pack of a retried program verifies against its own seeds, with its attempt. + #[test] + fn known_good_pack_of_a_later_attempt_verifies() { + let (h, s, attempt) = pack_texts(EPOCH_34, DAY_20731); + assert!(attempt >= 1); + let id = verify_pack_texts(&h, Some(&s), &bytes(EPOCH_34), &bytes(DAY_20731)).expect("the pack verifies"); + assert_eq!(id.attempt, attempt); + assert_eq!(id.epoch_hex, EPOCH_34); + assert_eq!(id.seedw, attempt_words(&bytes(EPOCH_34), attempt)); + // without seeds.txt program.h's own bytes carry the pack + assert!(verify_pack_texts(&h, None, &bytes(EPOCH_34), &bytes(DAY_20731)).is_ok()); + } + + /// Known-mismatched: a well-formed pack for the previous epoch is "out of date" against the new one, in plain + /// words with both epochs named. + #[test] + fn known_mismatched_pack_is_out_of_date() { + let (h, s, _) = pack_texts(EPOCH_33, DAY_20731); + let err = verify_pack_texts(&h, Some(&s), &bytes(EPOCH_34), &bytes(DAY_20731)).unwrap_err(); + assert!(matches!(err, PackFault::OutOfDate { .. }), "{err}"); + assert_eq!(err.to_string(), "program pack out of date: the pack is for epoch bed7ab62cbece66c day 20731, the node is on epoch 009858237e118f69 day 20731"); + } + + /// A pack that contradicts itself is "disagree", never "out of date": seeds.txt of one epoch with program.h of + /// another (a half rewritten directory), or init words that are not the attempt's words (a worker on the old + /// rule would have produced this verdict for every retried program). + #[test] + fn inconsistent_pack_disagrees() { + let (h33, _, _) = pack_texts(EPOCH_33, DAY_20731); + let s34 = format!("epoch_seed_hex {EPOCH_34}\nday_seed_hex {DAY_20731}\n"); + let err = verify_pack_texts(&h33, Some(&s34), &bytes(EPOCH_34), &bytes(DAY_20731)).unwrap_err(); + assert!(matches!(err, PackFault::Disagree(_)), "{err}"); + assert!(err.to_string().starts_with("program pack and its seeds disagree: seeds.txt names epoch 009858237e118f69"), "{err}"); + + let (h34, s, _) = pack_texts(EPOCH_34, DAY_20731); + let bare = attempt_words(&bytes(EPOCH_34), 0); + let edited = h34.lines().map(|l| if l.starts_with("#define IGNEUM_SEEDW_INIT") { format!("#define IGNEUM_SEEDW_INIT {{ {} }}", bare.iter().map(|w| format!("0x{w:08x}")).collect::>().join(", ")) } else { l.to_string() }).collect::>().join("\n"); + let err = verify_pack_texts(&edited, Some(&s), &bytes(EPOCH_34), &bytes(DAY_20731)).unwrap_err(); + assert!(err.to_string().contains("IGNEUM_SEEDW_INIT is not attempt"), "{err}"); + // and a pack with no attempt line at all is read as attempt 0 (the packs before generator version 2) + let no_attempt = h34.lines().filter(|l| !l.starts_with("#define IGNEUM_PROGRAM_ATTEMPT")).collect::>().join("\n"); + assert!(verify_pack_texts(&no_attempt, Some(&s), &bytes(EPOCH_34), &bytes(DAY_20731)).is_err()); + } + + #[test] + fn day_label_reads_the_index() { + assert_eq!(day_label(DAY_20731), "20731"); + assert_eq!(day_label("abcd"), "abcd"); + } +} diff --git a/proto-cuda/nvrtc/emu/packfile-test.c b/proto-cuda/nvrtc/emu/packfile-test.c new file mode 100644 index 00000000..c2a4ffb1 --- /dev/null +++ b/proto-cuda/nvrtc/emu/packfile-test.c @@ -0,0 +1,115 @@ +// packfile-test.c: the pack loader's seed rule (packfile.h pf_load) on a known-good and a known-mismatched pack. +// C99, no GPU, no NVRTC: `emu/packfile-test.sh` compiles and runs it on the Mac in a second, and CI runs it too. +// +// 5 October 2026, epoch 34 on both PCs: pf_load derived the expected IGNEUM_SEEDW_INIT from the bare epoch seed, but +// a pack carries the words of its program's ATTEMPT (igneum-pow attempt_words: seed || k_le32 for k > 0), so every +// pack of a retried program was refused and the workers restarted for an hour. These cases pin the rule: +// 1. the attempt vectors of epoch 34's seed, the same constants igneum-pow/src/packcheck.rs pins (one vector, two +// implementations: a change on either side fails here or there first); +// 2. known-good: a pack of attempt 1 with seeds.txt loads, and so does the checked-in attempt-0 pack; +// 3. known-mismatched: the same pack with the bare seed's words under IGNEUM_PROGRAM_ATTEMPT 1 (what the old +// rule expected) is refused in plain words; a seeds.txt of another epoch is refused as a disagreement. +#include "../packfile.h" +#include +#include + +#define EPOCH_34 "009858237e118f69abc8d096e9b1af21c24539eaecdfd1b896588825660a69ec" +#define EPOCH_33 "bed7ab62cbece66cf791485336d81d90fa1452ffed28ecd8a7416960ef64164c" +#define DAY_20731 "69676e65756d2d6461792ffb50000000000000" + +static int failures = 0; +#define CHECK(cond, what) do { if (cond) printf("ok %s\n", what); else { printf("FAIL %s (%s:%d)\n", what, __FILE__, __LINE__); failures++; } } while (0) + +static void write_file(const char* dir, const char* name, const char* text) { + char path[1024]; + FILE* f; + snprintf(path, sizeof(path), "%s/%s", dir, name); + f = fopen(path, "wb"); + if (!f) { perror(path); exit(2); } + fputs(text, f); + fclose(f); +} + +static void words_hex(const uint32_t w[8], char* out) { + int i; + out[0] = 0; + for (i = 0; i < 8; ++i) sprintf(out + strlen(out), "%s0x%08x", i ? ", " : "", w[i]); +} + +// A program.h with only what pf_load reads, for the given seeds, attempt and init words. +static void write_program_h(const char* dir, const char* epochHex, const char* dayHex, uint32_t attempt, const uint32_t seedw[8], const uint32_t keyw[8]) { + char sw[200], kw[200], text[2000]; + words_hex(seedw, sw); words_hex(keyw, kw); + snprintf(text, sizeof(text), + "// test pack\n#pragma once\n#define IGNEUM_SEED_STRING \"test\"\n#define IGNEUM_SEED_BYTES_HEX \"%s\"\n#define IGNEUM_GENERATOR 2\n" + "#define IGNEUM_PROGRAM_ATTEMPT %u\n#define IGNEUM_DAY_BYTES_HEX \"%s\"\n#define IGNEUM_DATASET_LOG2 28\n#define IGNEUM_DATASET_MODE 1\n" + "#define IGNEUM_SEEDW_INIT { %s }\n#define IGNEUM_KEY_INIT { %s }\n#define IGNEUM_CACHE_LOG2_WORDS 26\n#define IGNEUM_CACHE_SEGMENTS 4096u\n", + epochHex, (unsigned)attempt, dayHex, sw, kw); + write_file(dir, "program.h", text); +} + +int main(int argc, char** argv) { + const char* checked_in = argc > 1 ? argv[1] : NULL; // proto-cuda/packs/igneum-devnet-v4-epoch0 (attempt 0, no seeds.txt) + char dir[512]; + uint8_t e34[32], day[64]; + size_t n = 0, dn = 0; + uint32_t bare[8], att1[8], keyw[8]; + static const uint32_t want_bare[8] = { 0x06af2a61, 0x4d67274e, 0x4ebda738, 0xad1dea73, 0x6233cd8c, 0x50371601, 0x39d0b873, 0x6af024a2 }; + static const uint32_t want_att1[8] = { 0x0dcff56b, 0x6b1beb0d, 0x234dc70c, 0xe4016fa9, 0x72397152, 0xb558aa79, 0x3ffb3299, 0x72b9962e }; + PfPack pk; + char err[512]; + + pf_unhex(EPOCH_34, e34, 32, &n); + pf_unhex(DAY_20731, day, sizeof(day), &dn); + CHECK(n == 32 && dn == 19, "the fixture seeds unhex (32 epoch bytes, 19 day bytes)"); + + // 1. the attempt vectors shared with igneum-pow + pf_program_words(e34, 32, 0, bare); + pf_program_words(e34, 32, 1, att1); + pf_seed_words_from_bytes(day, dn, keyw); + CHECK(memcmp(bare, want_bare, 32) == 0, "attempt 0 of epoch 34 = the bare seed words (06af2a61 4d67274e ...)"); + CHECK(memcmp(att1, want_att1, 32) == 0, "attempt 1 of epoch 34 = words of seed || 01000000 (0dcff56b 6b1beb0d ...)"); + CHECK(memcmp(bare, att1, 32) != 0, "the two attempts differ"); + + // 2. known-good: a pack of attempt 1 with seeds.txt, as igneum-miner writes it + snprintf(dir, sizeof(dir), "%s/igneum-packfile-test-%d", getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp", (int)getpid()); + mkdir(dir, 0755); + write_program_h(dir, EPOCH_34, DAY_20731, 1, att1, keyw); + write_file(dir, "seeds.txt", "epoch_seed_hex " EPOCH_34 "\nday_seed_hex " DAY_20731 "\nday_index 20731\n"); + err[0] = 0; + CHECK(pf_load(dir, &pk, err, sizeof(err)) == 1, "known-good: the attempt-1 pack loads"); + if (err[0]) printf(" (%s)\n", err); + CHECK(pk.attempt == 1 && memcmp(pk.seedw, att1, 32) == 0 && strcmp(pk.epochHex, EPOCH_34) == 0, "known-good: attempt, words and epoch hex read back"); + if (checked_in) { + err[0] = 0; + CHECK(pf_load(checked_in, &pk, err, sizeof(err)) == 1, "known-good: the checked-in attempt-0 pack loads from program.h's own bytes"); + if (err[0]) printf(" (%s)\n", err); + CHECK(pk.attempt == 0, "the checked-in pack is attempt 0"); + } + + // 3. known-mismatched: the bare words under attempt 1 (the old rule's expectation) are refused, in plain words + write_program_h(dir, EPOCH_34, DAY_20731, 1, bare, keyw); + err[0] = 0; + CHECK(pf_load(dir, &pk, err, sizeof(err)) == 0, "known-mismatched: bare words under attempt 1 are refused"); + CHECK(strstr(err, "program pack and its seeds disagree: IGNEUM_SEEDW_INIT is not attempt 1 of the epoch seed 009858237e118f69") == err, "the refusal names the attempt and the epoch in plain words"); + printf(" (%s)\n", err); + // and a seeds.txt of another epoch against this program.h is a disagreement, not a load + write_program_h(dir, EPOCH_34, DAY_20731, 1, att1, keyw); + write_file(dir, "seeds.txt", "epoch_seed_hex " EPOCH_33 "\nday_seed_hex " DAY_20731 "\n"); + err[0] = 0; + CHECK(pf_load(dir, &pk, err, sizeof(err)) == 0, "known-mismatched: seeds.txt of epoch 33 with program.h of epoch 34 is refused"); + CHECK(strstr(err, "seeds.txt epoch_seed_hex differs from program.h") != NULL, "the refusal says the files disagree"); + // a pack with no IGNEUM_PROGRAM_ATTEMPT line is attempt 0 (packs before generator version 2) + { + char text[2000], sw[200], kw[200]; + words_hex(bare, sw); words_hex(keyw, kw); + snprintf(text, sizeof(text), "#define IGNEUM_SEED_BYTES_HEX \"%s\"\n#define IGNEUM_DAY_BYTES_HEX \"%s\"\n#define IGNEUM_DATASET_LOG2 28\n#define IGNEUM_DATASET_MODE 1\n#define IGNEUM_SEEDW_INIT { %s }\n#define IGNEUM_KEY_INIT { %s }\n#define IGNEUM_CACHE_LOG2_WORDS 26\n#define IGNEUM_CACHE_SEGMENTS 4096u\n", EPOCH_34, DAY_20731, sw, kw); + write_file(dir, "program.h", text); + write_file(dir, "seeds.txt", "epoch_seed_hex " EPOCH_34 "\nday_seed_hex " DAY_20731 "\n"); + err[0] = 0; + CHECK(pf_load(dir, &pk, err, sizeof(err)) == 1 && pk.attempt == 0, "no attempt line reads as attempt 0 and the bare words load"); + } + + printf("%s: %d failure(s)\n", argv[0], failures); + return failures ? 1 : 0; +} diff --git a/proto-cuda/nvrtc/emu/packfile-test.sh b/proto-cuda/nvrtc/emu/packfile-test.sh new file mode 100755 index 00000000..5ccd2b51 --- /dev/null +++ b/proto-cuda/nvrtc/emu/packfile-test.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# The pack loader's seed rule (packfile.h) on a known-good and a known-mismatched pack: emu/packfile-test.c, C99, +# no GPU. Runs on the Mac in a second and in CI. Usage: emu/packfile-test.sh +set -euo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$HERE/../../.." && pwd)" +OUT="${TMPDIR:-/tmp}/igneum-packfile-test" +mkdir -p "$OUT" +CC="${CC:-cc}" +"$CC" -std=c99 -Wall -Wextra -Wno-unused-function -O1 -o "$OUT/packfile-test" "$HERE/packfile-test.c" +"$OUT/packfile-test" "$ROOT/proto-cuda/packs/igneum-devnet-v4-epoch0" diff --git a/proto-cuda/nvrtc/packfile.h b/proto-cuda/nvrtc/packfile.h index fa407f93..c711f6d7 100644 --- a/proto-cuda/nvrtc/packfile.h +++ b/proto-cuda/nvrtc/packfile.h @@ -23,6 +23,7 @@ typedef struct { // program.h uint32_t datasetLog2, cacheLog2Words, cacheSegments, datasetMode, generator; + uint32_t attempt; // IGNEUM_PROGRAM_ATTEMPT: seedw are the words of this attempt of the epoch seed (0 = bare seed) uint32_t seedw[8], keyw[8]; char seedString[600]; // seeds.txt (or program.h): the seeds as the worker protocol carries them @@ -208,6 +209,20 @@ static void pf_seed_words_from_bytes(const uint8_t* b, size_t n, uint32_t out[8] } } +// attempt_words of igneum-pow/src/generator.rs: the words of attempt k of a program seed are +// seed_words_from_bytes(seed || k_le32) for k > 0 and the bare seed's words for k = 0. The generator retries a +// rejected candidate with the next attempt, so a pack's IGNEUM_SEEDW_INIT is the words of IGNEUM_PROGRAM_ATTEMPT, +// not of the bare seed. 5 October 2026: pf_load derived the expected words from the bare seed and refused every +// pack of a retried program (epoch 34, both PCs, the miner restarting the worker for an hour); this is the rule. +static void pf_program_words(const uint8_t* b, size_t n, uint32_t attempt, uint32_t out[8]) { + uint8_t buf[256 + 4]; + if (attempt == 0) { pf_seed_words_from_bytes(b, n, out); return; } + if (n > 256) n = 256; + memcpy(buf, b, n); + buf[n] = (uint8_t)attempt; buf[n + 1] = (uint8_t)(attempt >> 8); buf[n + 2] = (uint8_t)(attempt >> 16); buf[n + 3] = (uint8_t)(attempt >> 24); + pf_seed_words_from_bytes(buf, n + 4, out); +} + static uint64_t pf_fnv1a64(const void* p, size_t n) { const uint8_t* b = (const uint8_t*)p; uint64_t h = 0xcbf29ce484222325ull; @@ -253,6 +268,7 @@ static int pf_load(const char* dir, PfPack* pk, char* err, size_t cap) { if (!pf_define_u32(prog, "IGNEUM_CACHE_LOG2_WORDS", &pk->cacheLog2Words)) { free(prog); return pf_fail(err, cap, "program.h has no IGNEUM_CACHE_LOG2_WORDS"); } if (!pf_define_u32(prog, "IGNEUM_CACHE_SEGMENTS", &pk->cacheSegments)) { free(prog); return pf_fail(err, cap, "program.h has no IGNEUM_CACHE_SEGMENTS"); } if (!pf_define_u32(prog, "IGNEUM_GENERATOR", &pk->generator)) pk->generator = 1; + if (!pf_define_u32(prog, "IGNEUM_PROGRAM_ATTEMPT", &pk->attempt)) pk->attempt = 0; if (pf_define_words(prog, "IGNEUM_SEEDW_INIT", pk->seedw, 8) != 8) { free(prog); return pf_fail(err, cap, "program.h has no IGNEUM_SEEDW_INIT with 8 words"); } if (pf_define_words(prog, "IGNEUM_KEY_INIT", pk->keyw, 8) != 8) { free(prog); return pf_fail(err, cap, "program.h has no IGNEUM_KEY_INIT with 8 words"); } if (!pf_define_str(prog, "IGNEUM_SEED_STRING", pk->seedString, sizeof(pk->seedString))) strncpy(pk->seedString, "(no IGNEUM_SEED_STRING)", sizeof(pk->seedString) - 1); @@ -276,12 +292,18 @@ static int pf_load(const char* dir, PfPack* pk, char* err, size_t cap) { if (!ehex[0] || !dhex[0]) return pf_fail(err, cap, "no seeds: neither seeds.txt nor IGNEUM_SEED_BYTES_HEX / IGNEUM_DAY_BYTES_HEX in program.h (a pack from igneum-pow export --seed has no byte seeds)"); if (strlen(ehex) != 64) return pf_fail(err, cap, "epoch seed is not 64 hex characters"); strcpy(pk->epochHex, ehex); strcpy(pk->dayHex, dhex); - // The seed words derived from the bytes must be the pack's own words: otherwise the pack and the seeds disagree + // The seed words derived from the bytes AND the attempt must be the pack's own words: otherwise the pack and + // its seeds disagree (a half rewritten directory, or an exporter on another rule) { uint32_t w[8]; + char m[400]; if (!pf_unhex(ehex, bytes, 32, &blen) || blen != 32) return pf_fail(err, cap, "epoch seed hex is malformed"); - pf_seed_words_from_bytes(bytes, 32, w); - if (memcmp(w, pk->seedw, 32) != 0) return pf_fail(err, cap, "the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT (wrong seeds.txt for this pack?)"); + pf_program_words(bytes, 32, pk->attempt, w); + if (memcmp(w, pk->seedw, 32) != 0) { + snprintf(m, sizeof(m), "program pack and its seeds disagree: IGNEUM_SEEDW_INIT is not attempt %u of the epoch seed %.16s (attempt %u gives %08x %08x ..., the pack has %08x %08x ...); run igneum-miner export-pack again", + (unsigned)pk->attempt, ehex, (unsigned)pk->attempt, w[0], w[1], pk->seedw[0], pk->seedw[1]); + return pf_fail(err, cap, m); + } if (!pf_unhex(dhex, bytes, sizeof(bytes), &blen)) return pf_fail(err, cap, "day seed hex is malformed"); pf_seed_words_from_bytes(bytes, blen, w); if (memcmp(w, pk->keyw, 32) != 0) return pf_fail(err, cap, "the day seed bytes do not give the pack's IGNEUM_KEY_INIT (wrong seeds.txt for this pack?)"); diff --git a/proto-cuda/nvrtc/worker.cpp b/proto-cuda/nvrtc/worker.cpp index 11597fd2..6143e1e7 100644 --- a/proto-cuda/nvrtc/worker.cpp +++ b/proto-cuda/nvrtc/worker.cpp @@ -394,6 +394,18 @@ static bool rtcCompile(Ctx& c, const std::string& src, const char* name, const s // --------------------------------------------------------------------------------------------- // A resident pair: one pack compiled, its cache and dataset on the device, self-tested +static bool hexEq(const std::string& a, const std::string& b) { + if (a.size() != b.size()) return false; + for (size_t i = 0; i < a.size(); ++i) if (std::tolower((unsigned char)a[i]) != std::tolower((unsigned char)b[i])) return false; + return true; +} + +struct Pair; +// A pair is the pair of a job when the job's seeds (the hex the node sent) are the pair's seeds. The derived seed +// words are no identity: a retried program's words are its attempt's words, not the bare seed's (packfile.h, +// 5 October 2026), so comparing words refused every job of a retried program. +static bool pairIs(const Pair* p, const std::string& epochHex, const std::string& dayHex); + struct Pair { std::string dir, epochHex, dayHex, seedString; uint32_t sw[8] = {0}, kw[8] = {0}; @@ -411,6 +423,10 @@ struct Pair { double raceMs = 0; }; +static bool pairIs(const Pair* p, const std::string& epochHex, const std::string& dayHex) { + return p && hexEq(p->epochHex, epochHex) && hexEq(p->dayHex, dayHex); +} + // The job loop and a race take turns on the card: a variant is timed with no job running (exclusive numbers), and // mining resumes between variants. Held per chunk by the job loop, per variant by the race. static std::mutex gpuMutex; @@ -1039,8 +1055,7 @@ static int runServe(Ctx& c, const Options& o, Pair* cur) { pf_seed_words_from_bytes(daySeed, dl, kw); double t0 = wallMs(); bool switched = false; - if ((std::memcmp(sw, cur->sw, 32) != 0 || std::memcmp(kw, cur->kw, 32) != 0) && !task && - !(prepared && std::memcmp(sw, prepared->sw, 32) == 0 && std::memcmp(kw, prepared->kw, 32) == 0)) { + if (!pairIs(cur, f[6], f[7]) && !task && !pairIs(prepared, f[6], f[7])) { // Self-heal: a job on seeds this worker has no pair for and no prepare in flight (a prepare failed, or // the miner never sent one). The miner writes a pack per pair under its --prepare-packs root; find it by // seeds.txt and build it now, in the foreground. The miner only re-sends prepare for the pair after this one. @@ -1056,14 +1071,14 @@ static int runServe(Ctx& c, const Options& o, Pair* cur) { else emit("error " + jobId + " could not build " + dir + ": " + berr); } } - if (std::memcmp(sw, cur->sw, 32) != 0 || std::memcmp(kw, cur->kw, 32) != 0) { - if (prepared && std::memcmp(sw, prepared->sw, 32) == 0 && std::memcmp(kw, prepared->kw, 32) == 0) { + if (!pairIs(cur, f[6], f[7])) { + if (pairIs(prepared, f[6], f[7])) { if (old) releasePair(c, old); old = cur; cur = prepared; prepared = nullptr; switched = true; info(fmt("switched to the prepared pair epoch %.16s day %s in %.2f ms", cur->epochHex.c_str(), cur->dayHex.c_str(), wallMs() - t0)); - } else if (std::memcmp(sw, cur->sw, 32) != 0) { + } else if (!hexEq(cur->epochHex, f[6])) { emit(fmt("need %s %s", f[6].c_str(), f[7].c_str())); // the miner prepares this pair (4 October 2026) - emit(fmt("error %s epoch seed mismatch: this worker holds epoch %.16s (seed words %08x %08x ...)%s, the job's epoch seed %.16s gives %08x %08x ...; send prepare with a pack directory", + emit(fmt("error %s epoch seed mismatch: this worker holds epoch %.16s (program words %08x %08x ...)%s, the job is for epoch %.16s (bare seed words %08x %08x ...); send prepare with a pack directory", jobId.c_str(), cur->epochHex.c_str(), cur->sw[0], cur->sw[1], prepared ? " plus one prepared pair" : "", f[6].c_str(), sw[0], sw[1])); continue; } else { diff --git a/proto-opencl/host.c b/proto-opencl/host.c index 457ec209..6304a1f9 100644 --- a/proto-opencl/host.c +++ b/proto-opencl/host.c @@ -939,6 +939,23 @@ typedef struct { int checked; } ServePair; +static int hexEq(const char* a, const char* b) { + size_t i; + if (strlen(a) != strlen(b)) return 0; + for (i = 0; a[i]; ++i) if (tolower((unsigned char)a[i]) != tolower((unsigned char)b[i])) return 0; + return 1; +} + +/* A pair is the pair of a job when the job's seeds (the hex the node sent) are the pair's seeds. The derived seed + * words are no identity: a retried program's words are its attempt's words, not the bare seed's (packfile.h, + * 5 October 2026), so comparing words refused every job of a retried program. The compiled-in placeholder pack + * (no --pack, no prepared pair) has no seed hex; it keeps the word comparison. */ +static int pairIs(const ServePair* p, const char* epochHex, const char* dayHex, const uint32_t sw[8], const uint32_t kw[8]) { + if (!p) return 0; + if (p->epochHex[0]) return hexEq(p->epochHex, epochHex) && hexEq(p->dayHex, dayHex); + return memcmp(sw, p->sw, 32) == 0 && memcmp(kw, p->kw, 32) == 0; +} + static void releasePair(ServePair* p) { if (!p) return; if (p->ds) { clReleaseMemObject(p->ds); ++gMemReleased; } @@ -1243,15 +1260,15 @@ static int runServe(Device* dv, const DeviceInfo* di, const Options* o) { seedWordsFromBytes(epochSeed, 32, sw); seedWordsFromBytes(daySeed, dayLen, kw); t0 = wallMs(); - if (memcmp(sw, cur->sw, 32) != 0 || memcmp(kw, cur->kw, 32) != 0) { - if (prepared && memcmp(sw, prepared->sw, 32) == 0 && memcmp(kw, prepared->kw, 32) == 0) { + if (!pairIs(cur, f[6], f[7], sw, kw)) { + if (pairIs(prepared, f[6], f[7], sw, kw)) { /* The prepared pair: switch now, release the old one after this job */ if (old) releasePair(old); old = cur; cur = prepared; prepared = NULL; switched = 1; printf("info switched to the prepared pair epoch %.16s day %s in %.2f ms\n", cur->epochHex, cur->dayHex, wallMs() - t0); fflush(stdout); - } else if (memcmp(sw, cur->sw, 32) != 0) { + } else if (cur->epochHex[0] ? !hexEq(cur->epochHex, f[6]) : memcmp(sw, cur->sw, 32) != 0) { printf("need %s %s\n", f[6], f[7]); /* the miner prepares this pair (4 October 2026) */ - printf("error %s epoch seed mismatch: this worker holds %s%s (seed words %08x %08x ...)%s, the job's epoch seed %.16s gives %08x %08x ...; send prepare with a pack directory, or run igneum-miner export-pack and rebuild\n", + printf("error %s epoch seed mismatch: this worker holds %s%s (program words %08x %08x ...)%s, the job is for epoch %.16s (bare seed words %08x %08x ...); send prepare with a pack directory, or run igneum-miner export-pack and rebuild\n", jobId, cur->epochHex[0] ? "prepared epoch " : "pack \"" IGNEUM_SEED_STRING "\"", cur->epochHex[0] ? cur->epochHex : "", cur->sw[0], cur->sw[1], prepared ? " plus one prepared pair" : "", f[6], sw[0], sw[1]); fflush(stdout); continue; } else { diff --git a/relay/lib/parse.mjs b/relay/lib/parse.mjs index 5cc2602d..689d5f19 100644 --- a/relay/lib/parse.mjs +++ b/relay/lib/parse.mjs @@ -4,6 +4,9 @@ export const kv = (text, k) => { const m = new RegExp(`(?:^|[\\s(])${k}=([^\\s,) export const kvNum = (text, k) => { const v = kv(text, k); return v === null ? null : Number(v); }; export const lastMatch = (lines, re) => { for (let i = lines.length - 1; i >= 0; i--) { const m = re.exec(lines[i]); if (m) return m; } return null; }; export const FAULT = /WORKER MISMATCH|worker error|worker exited|panicked|CUDA error|submit error/; +// A program pack the worker refused at start or the miner found stale (5 October 2026, epoch 34 on both PCs: the +// bare restart count hid an hour-long loop). The card says "pack mismatch, rebuilding" instead of a bare count. +export const PACK_MISMATCH = /error 0 pack |PACK OUT OF DATE|program pack out of date|program pack and its seeds disagree/; // A card's hash is "now" only while its worker's STATUS line (every 30 s, uploaded every 60 s) is this fresh; older // than this the card is marked stale and its hash leaves the machine total (4 October 2026: PC 2 showed 117 MH/s "now" @@ -59,6 +62,9 @@ export function parseMinerTail(tail) { const faults = lines.filter(l => FAULT.test(l)); c.faults = faults.length; if (faults.length) c.fault = faults[faults.length - 1].replace(/^\d+(\.\d+)? /, '').slice(0, 200); + const packs = lines.filter(l => PACK_MISMATCH.test(l)); + c.pack_mismatch = packs.length > 0; + if (packs.length) c.fault = `pack mismatch, rebuilding (${packs.length} refusal${packs.length === 1 ? '' : 's'} in the tail${c.restarts ? ', ' + c.restarts + ' restarts' : ''})`; const acc = lastMatch(lines, /^(\d+(?:\.\d+)?) ACCEPTED block/); if (acc) c.last_accepted_at = new Date(Number(acc[1]) * 1000).toISOString(); return c; diff --git a/relay/test/parse.test.mjs b/relay/test/parse.test.mjs index 8c852f5d..959aecb5 100644 --- a/relay/test/parse.test.mjs +++ b/relay/test/parse.test.mjs @@ -1,7 +1,7 @@ // node --test relay/test/parse.test.mjs relay/test/auth.test.mjs (no dependencies; CI runs both in the site job) import { test } from 'node:test'; import assert from 'node:assert/strict'; -import { parseLabel, parseMinerTail, markStale, STALE_S } from '../lib/parse.mjs'; +import { parseLabel, parseMinerTail, markStale, STALE_S, PACK_MISMATCH } from '../lib/parse.mjs'; test('labels: every vendor the app names, the app log, the node log, the legacy launchers', () => { assert.deepEqual(parseLabel('miner-nvidia-ae432dc7-1'), { id: 'ae432dc7', platform: 'win', stream: 'miner', vendor: 'nvidia', card: 1 }); @@ -84,3 +84,32 @@ test('app tail: the newest update line decides the OTA state: staged beats an ol const inst = parseAppTail(mac + '\n1791146700 [info] installing Igneum Miner 0.3.4: the miners stop, then the node, then the app opens again'); assert.deepEqual([inst.ota.state, inst.ota.version], ['installing', '0.3.4']); }); + +test('pack mismatch: the refused-pack tail of 5 October 2026 reads "pack mismatch, rebuilding", a healthy tail does not', () => { + // PC 2, nvidia-1ccfe586-1, 18:27 to 18:28 UTC: the worker refused packs\devnet at every start and the miner restarted it + const refused = [ + "1791224818.418 epoch seed 009858237e118f69abc8d096e9b1af21c24539eaecdfd1b896588825660a69ec day 20731 (daa 122513): CPU program and cache ready in 179 ms", + "! 1791224818.419 worker error: error 0 pack packs\\devnet: the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT (wrong seeds.txt for this pack?)", + '! 1791224818.933 worker exited (code Some(1)); restarting it in 21 s (restart 1)', + "1791224840.037 STATUS 'nvidia-1ccfe586-1' [worker]: 22s jobs=0 accepted=0 rejected=0 fee=0 mismatched=0 extra=0 rate=0.00 blocks/s hash=0.00 MH/s wall (0.00 MH/s inside jobs) now=0.00 MH/s wall (0.00 MH/s inside jobs, 0 jobs, seed walk 0 calls) template_age=0.45s synced=true idle=100.0% (last 22s: 100.0%) queued=2 restarts=1 faults=0 identities=8 accepted_by_identity=0/0/0/0/0/0/0/0", + "! 1791224840.037 worker error: error 0 pack packs\\devnet: the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT (wrong seeds.txt for this pack?)", + '! 1791224840.548 worker exited (code Some(1)); restarting it in 41 s (restart 2)', + ].join('\n'); + const c = parseMinerTail(refused); + assert.equal(c.pack_mismatch, true); + assert.equal(c.fault, 'pack mismatch, rebuilding (2 refusals in the tail, 1 restarts)'); + assert.equal(c.restarts, 1); + // the miner's own line (0.3.11 miners) counts too + const own = parseMinerTail('1791224840.100 PACK OUT OF DATE packs\\devnet: the worker refused its program pack; rebuilding the program pack before the restart'); + assert.equal(own.pack_mismatch, true); + assert.ok(own.fault.startsWith('pack mismatch, rebuilding')); + // known-good: a mining tail with a job error that is not a refusal keeps the plain fault text + const healthy = [ + "1791140579.479 STATUS 'other-37ba0461-1' [worker]: 61s jobs=37 accepted=2 rejected=0 mismatched=0 extra=0 rate=0.02 blocks/s hash=1.28 MH/s wall (1.55 MH/s inside jobs) now=1.53 MH/s wall", + '! 1791140580.000 worker error: error 17 epoch seed mismatch: this worker holds epoch bed7ab62cbece66c', + ].join('\n'); + const h = parseMinerTail(healthy); + assert.equal(h.pack_mismatch, false); + assert.ok(h.fault.includes('worker error: error 17')); + assert.equal(PACK_MISMATCH.test('1 ACCEPTED block x'), false); +}); From 6acfaede2d69f8b7037399f8efeee1e05aa6eb84 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 19:22:52 +0000 Subject: [PATCH 016/158] docs/plans/miner-ui-2.md: the gpu-hotplug merge, the PC 1 Windows compile, the 0.3.10 changelog paragraph Co-Authored-By: Claude Fable 5.1 --- docs/plans/miner-ui-2.md | 58 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/docs/plans/miner-ui-2.md b/docs/plans/miner-ui-2.md index 22c31cab..e1768229 100644 --- a/docs/plans/miner-ui-2.md +++ b/docs/plans/miner-ui-2.md @@ -3,7 +3,8 @@ the project lead, 18:40 UTC: "can any updates be made to the UI of the miner? different sections for different features. Made super simple for users, all settings super easy and simple and everything looking gorgeous, as close to shippable as we can get right now." Worktree `/Users/joshm/Projects/igneum-wt-miner-ui`, branch `miner-ui-2` from master -a93199a. Ships as 0.3.11 after the project lead has seen the screenshots. Times are UTC. +a93199a. the project lead approved the redesign from the screenshots (20:1x UTC) and moved it into 0.3.10: the 0.3.10 shipper merges +`miner-ui-2` last and rebuilds the app. Times are UTC. ## 1. What changed @@ -110,8 +111,61 @@ Follow-ups (engine work, not built here; the budget was three small additions): | Temperature and power on Apple silicon | the engine reads no telemetry for Metal; `powermetrics` needs root, so this stays blank unless a non-root source is found | | A per-card "pause this card" without restarting the others | exists already through the row switch (only that card's worker restarts); nothing to do unless the project lead wants a timer | -## 5. The branch +## 5. The 0.3.10 merge: gpu-hotplug (4d122e1) resolved on the new UI + +The branch was merged with `gpu-hotplug` 4d122e1 (the 0.3.10 tree carries it). The engine, relay and tooling files +merged by themselves; `app.js` and `app.css` were taken from this branch and the hot-plug UI ported by hand: + +| Hot-plug state | On the six-section UI | +|---|---| +| The strip notices (card added, mining; new card not usable (Code 43); card removed) | the Notices block is gpu-hotplug's, unchanged; `notices.test.mjs` (12) passes | +| A card the OS reports a problem on (`problem`, "Code 43") | Mine row: name in ember, the word "not usable (Code 43)", the reboot hint under it, the switch disabled; the Mine note repeats the hint; first-run row and Settings card say the same, with no controls | +| A removed card (`removed_at`) | Mine row dimmed, the word "removed", "unplugged; its worker stopped. The row goes in five minutes."; no switch; Settings card says the same; after five minutes (`gone`) the row is not shown | +| The counts and the big button | only present cards count (a removed or faulty card never makes the button say "Stop mining") | +| The name tooltip | the tool's code (gfx1201), the device index, the OpenCL platform, the PCI address (`View.cardTitle`) | +| The card list sent on a switch | removed and faulty cards are left out, as gpu-hotplug's `readCardRows` does | + +`view.test.mjs` has an eleventh test for the two states. The whole app crate's unit tests pass on the merged tree +(99, on the Mac under the build lock, `cargo test --release --bin igneum-app`). `relay/test/parse.test.mjs` +passes. The Mac engine builds and runs (the same scratch instance as section 3). + +Build tooling for the Windows compile: `packaging/windows/push-build-inputs.sh --no-node` and +`node tools/build-job.mjs run --no-node` pack and build the app engine only (no node source, no node build, no node +tests), so an app-only change compiles on PC 1 in a fraction of the full job. + +PC 1 job (20:21Z, from this tree at d62b445, `node tools/build-job.mjs run --target ae432dc7 --no-node --targets +windows --no-tests --no-place`): extract 167 files, `windows app/igneum-app build exit 0 6 s` (the PC's target dir +was warm), `igneum-app.exe` 2,970,112 bytes sha256 62ce96a9..., PE check ok, coin icon and version block ok, done +after 13 s. The exe carries the new UI and engine strings ("Prove shards on this machine" x2, `consensus_switches` +x6, "Consensus params digest:" x1, `card:removed:` x1), so the 6 s was a real compile of the merged sources, with +gpu-hotplug's Windows-only `detect::adapters` path and this branch's prover change in it. Downloads under the session +scratchpad (`pc1-app/`), not placed. + +The ship tool runs no node tests of its own (`tools/ship-app.mjs --self-test` is the version bump), so the only +list to carry `view.test.mjs` is `.github/workflows/ci.yml` (done in 83a293f). + +What the PC job does not compile: `app/windows/host.cpp` (the window host with the `WM_GETMINMAXINFO` and +`WM_DEVICECHANGE` cases) is built by the GitHub runner (`windows.yml`) at the shipper's push, never on a PC; the +`WM_GETMINMAXINFO` case is three lines of plain Win32 (`MINMAXINFO`, `ptMinTrackSize`) and reads at the runner's log. + +## 6. The changelog paragraph for the 0.3.10 manifest notes (what the user sees) + +The miner has six sections behind a rail: Mine, Prove, Rewards, Node, Updates, Settings. Mine has one big Start +mining / Stop mining button, the hash rate, the blocks found, and one row per graphics card with its name, an on/off +switch, its hash rate, temperature and power. Prove says in plain words what proving is and shows the shards +assigned, proven and paid, the verifier and the program ids. Rewards shows the address with one Copy and keeps the +key backup in its own card. Node shows the sync, height, peers and version with a plain line under each, the +consensus digest, and the list of planned rule switches with the next one and its height. Updates holds the +version, Check now and the remote jobs. Settings has one switch or slider per setting with one line of help under +each: the power cap per card with the watt number, identities, start at login, remote jobs, voting, the dev fee +stated plainly, the log. The window lays out from 900 x 600. Not in this release: the balance on Rewards (shown in +the wallet), a "pause while I use the machine" switch, log export as a file, temperature and power on Apple +silicon. + +## 7. The branch | Commit | What | |---|---| | 83a293f | the UI pass, the three engine additions with tests, the hosts' minimum size, this plan and the 19 screenshots (the blank marker in a GPU row is `n/a`, not a dash: copy law) | +| 364feef | this plan's hash line | +| d62b445 | Merge gpu-hotplug 4d122e1: the hot-plug states on the new UI, `--no-node` for the build tools | From a0864f084232b0a44aec5f86fe55e190394a19e6 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 19:23:59 +0000 Subject: [PATCH 017/158] engine: one pack export at a time (EXPORT_LOCK around both export-pack call sites; the hunk of opencl-rdna4 23810df, whose other changes conflict with gpu-hotplug and wait for the next cut) Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/src/engine.rs | 73 ++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/app/igneum-app/src/engine.rs b/app/igneum-app/src/engine.rs index 63318b4b..3ad329d0 100644 --- a/app/igneum-app/src/engine.rs +++ b/app/igneum-app/src/engine.rs @@ -430,6 +430,8 @@ struct MinerSlot { error_at: Option, /// the per-card watchdog (src/watchdog.rs): one restart, then faulted watch: crate::watchdog::CardWatch, + /// pack refusals (src/watchdog.rs): the pack is exported again before the restart, capped per epoch + pack_rebuilds: crate::watchdog::PackRebuilds, } pub struct Engine { @@ -1337,6 +1339,7 @@ impl Engine { last_status: None, error_at: None, watch: crate::watchdog::CardWatch::new(), + pack_rebuilds: crate::watchdog::PackRebuilds::new(), }); } if self.miners.is_empty() { @@ -2460,6 +2463,7 @@ impl Engine { if !p.alive() { let code = p.exit_code.unwrap_or(-1); let tail = tail_of(&p.log_path, 3); + let long_tail = if code == crate::watchdog::PACK_OUT_OF_DATE_CODE { tail_of(&p.log_path, 40) } else { Vec::new() }; self.miners[i].proc = None; let t = self.secs(now); let verdict = self.miners[i].watch.event(t, crate::watchdog::Event::Exited(code)); @@ -2469,6 +2473,37 @@ impl Engine { self.miners[i].needs_rebuild = true; } self.miners[i].restart_at = Some(now); + } else if code == crate::watchdog::PACK_OUT_OF_DATE_CODE { + // The worker refused its program pack and the miner could not rebuild it (or hit its own + // cap): export the pack from the node again before the next start, not a blind restart; + // at most PACK_REBUILD_CAP times per epoch, then the card shows the reason + let why = long_tail.iter().rev().find_map(|l| crate::watchdog::pack_refusal(l)).unwrap_or_else(|| "the worker refused its program pack".into()); + let epoch = std::fs::read_to_string(self.shared.runtime.app_dir.join("packs").join("devnet").join("seeds.txt")).ok().and_then(|s| crate::watchdog::pack_epoch_of(&s)).unwrap_or_default(); + let name = self.st().mining.cards.get(card_idx).map(|c| c.name.clone()).unwrap_or_else(|| self.miners[i].label.clone()); + match self.miners[i].pack_rebuilds.decide(&epoch, &why) { + crate::watchdog::PackAction::Rebuild { n, cap } => { + self.shared.event("build", "program pack out of date, rebuilding"); + self.shared.log(&format!("{name}: program pack out of date, rebuilding (export {n} of {cap} for epoch {epoch}): {why}")); + self.miners[i].prepared = false; // prepare_worker exports the pack again before the start + self.miners[i].restart_at = Some(now); + if let Some(c) = self.st().mining.cards.get_mut(card_idx) { + c.state = "restarting".into(); + c.hash_now = 0.0; + c.message = "program pack out of date, rebuilding".into(); + } + } + crate::watchdog::PackAction::GiveUp { n: _, reason } => { + self.shared.event("error", &format!("{name}: {reason}")); + self.shared.log(&format!("{name}: {reason}; next try in 10 minutes or at the next hour")); + self.miners[i].prepared = false; + self.miners[i].restart_at = Some(now + Duration::from_secs(600)); + if let Some(c) = self.st().mining.cards.get_mut(card_idx) { + c.state = "failed".into(); + c.hash_now = 0.0; + c.message = reason; + } + } + } } else if verdict != crate::watchdog::Action::None { // exit 43: the miner gave up on its worker; once more, then the card is faulted self.watchdog_verdict(i, verdict, &tail); @@ -2546,6 +2581,25 @@ impl Engine { } } + /// The strip, the log and the card for a program pack the worker refused or the miner found stale: the miner + /// rebuilds the pack and restarts the worker itself (or exits 44 for the app to export it). One strip line per + /// 30 s: the miner prints the refusal on stderr and its own line on stdout. + fn pack_notice(&mut self, i: usize, card: usize, card_name: &str, why: &str) { + let now = Instant::now(); + self.shared.log(&format!("{card_name}: program pack out of date, rebuilding: {why}")); + if now.duration_since(self.last_error_event) >= Duration::from_secs(30) { + self.last_error_event = now; + self.shared.event("build", "program pack out of date, rebuilding"); + } + self.miners[i].error_at = Some(now); + let t = self.secs(now); + self.miners[i].watch.event(t, crate::watchdog::Event::WorkerRestart("program pack out of date, rebuilding")); + if let Some(c) = self.st().mining.cards.get_mut(card) { + c.hash_now = 0.0; + c.message = "program pack out of date, rebuilding".into(); + } + } + /// Applies a watchdog verdict to slot `i` (its process already stopped or gone): a restart now with the reason on /// the card, or the card marked faulted with the reason in the UI and the log while the other cards keep mining. fn watchdog_verdict(&mut self, i: usize, verdict: crate::watchdog::Action, tail: &[String]) { @@ -2867,6 +2921,10 @@ impl Engine { if let Some(c) = self.st().mining.cards.get_mut(card) { c.message = "the node is not answering; the miner retries".into(); } + } else if let Some(why) = crate::watchdog::pack_refusal(text) { + // the worker refused its program pack; the miner rebuilds the pack and restarts the worker itself + // (or exits 44 for us to export it): the strip and the card name the condition in plain words + self.pack_notice(i, card, &card_name, &why); } else if text.contains("WORKER MISMATCH") || text.contains("worker error") || text.contains("worker exited") || text.contains("worker killed by a guard") || text.contains("panicked") || text.contains("CUDA error") || text.contains("submit error") { let now = Instant::now(); if now.duration_since(self.last_error_event) >= Duration::from_secs(30) { @@ -2889,6 +2947,12 @@ impl Engine { } return; } + if text.contains("PACK OUT OF DATE") { + // the miner found its pack stale or refused (stdout): it rebuilds the pack before the restart + let why = crate::watchdog::pack_refusal(text).unwrap_or_else(|| short(text, 160)); + self.pack_notice(i, card, &card_name, &why); + return; + } if text.contains(" WORKER FAULT ") { // the miner's guards (interval, job time, cpu re-check, stall) killed the worker; it restarts it itself let reason = crate::watchdog::fault_reason(text).unwrap_or_else(|| short(text, 160)); @@ -3398,9 +3462,17 @@ fn civil_from_days(z: i64) -> (i64, u32, u32) { (if m <= 2 { y + 1 } else { y }, m, d) } +/// One pack export at a time (5 October 2026). prepare_worker runs on a thread per card, so two cards starting +/// together ran two `igneum-miner export-pack` processes into the same folder; across an epoch change they +/// interleaved and PC 1's packs\devnet was left with one epoch's program.h and the other's seeds.txt, which every +/// OpenCL worker start then refused ("the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT") until the next +/// export. The second export of a pair rewrites the same pack, which is harmless. +static EXPORT_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(()); + /// Exports this hour's program pack from the node to \packs\devnet (the prebuilt workers read it with --pack). #[allow(unused_variables)] fn export_pack(shared: &Arc, bins: &Bins) -> Result<(), String> { + let _one_at_a_time = EXPORT_LOCK.lock().unwrap_or_else(|e| e.into_inner()); let pack = shared.runtime.app_dir.join("packs").join("devnet"); let _ = std::fs::create_dir_all(&pack); let out = crate::detect::run_timeout(std::process::Command::new(&bins.miner).args(["export-pack", &shared.runtime.rpc_url(), &pack.display().to_string()]), None, Duration::from_secs(120)).unwrap_or_default(); @@ -3419,6 +3491,7 @@ fn build_worker_from_source(shared: &Arc, bins: &Bins, vendor: &str) -> #[cfg(windows)] { use std::process::Command; + let _one_at_a_time = EXPORT_LOCK.lock().unwrap_or_else(|e| e.into_inner()); let pack = shared.runtime.app_dir.join("packs").join("devnet"); let _ = std::fs::create_dir_all(&pack); let out = crate::detect::run_timeout(Command::new(&bins.miner).args(["export-pack", &shared.runtime.rpc_url(), &pack.display().to_string()]), None, Duration::from_secs(120)).unwrap_or_default(); From 2fc34ea37093667200aae06d48ab7090645bb03f Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 19:26:13 +0000 Subject: [PATCH 018/158] release-0.3.10 plan: the second final tree (miner-ui-2, pack-loop, the export lock), the rebuilt workers in the inputs, the app tests and the DMG Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.10.md | 97 +++++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 8 deletions(-) diff --git a/docs/plans/release-0.3.10.md b/docs/plans/release-0.3.10.md index 2dae0a21..3caa8d85 100644 --- a/docs/plans/release-0.3.10.md +++ b/docs/plans/release-0.3.10.md @@ -22,8 +22,14 @@ Changelog line for C (the coordinator's words): "A node that comes back from a p Changelog line for D (the coordinator's words): "Transactions now travel between nodes; a miner on an older node still carries only what was sent to it directly." | E. GPU hot-plug (coordinator, 17:5xZ): cards re-enumerated every 60 s and on `WM_DEVICECHANGE`, new cards mine by default, Code 43 cards marked unusable, vanished cards dropped without shifting slots, the Ryzen gfx1036 iGPU classified integrated and off by default, a `cards:` line the relay parses (machines API `gpus`/`hotplug`). 14 files: `app/igneum-app/src/{detect,engine,hotplug,main,state}.rs`, `ui/{app.css,app.js,notices.test.mjs}`, `app/windows/host.cpp`, `relay/{api/console.mjs,lib/parse.mjs,test/parse.test.mjs,ui.html}`, `tools/console.mjs`; no proving, packaging or workflow file. Its Windows-only paths (`detect::adapters`, `host.cpp` WM_DEVICECHANGE) were never compiled on the Mac: PC 1's Windows app build and the GitHub runner's host build are their first compile and are read for errors | main `gpu-hotplug` 4517004 (`igneum-wt-hotplug`, on master 22ffc02); its tests on the branch: igneum-app 91 pass, notices, update-card and parse node tests pass | merged after C and D | | F. Elevated jobs that never started (coordinator, 18:0xZ): an elevated job whose UAC prompt is refused, cancelled or times out fails with exit 251 and the summary "did not start: the administrator prompt was refused, cancelled or timed out"; before, `Start-Process` threw, `$p` stayed null and `exit $p.ExitCode` exited 0, so the PC 1 driver job at 17:58Z read as done. One file, `app/igneum-app/src/jobrun.rs`, plus its unit test | main `elevated-exit` 9816d58 (`igneum-wt-elevated`) | merged after E | +| G. The miner UI redesign (the project lead, through the coordinator at 19:0xZ: into 0.3.10, not 0.3.11): six sections (Mine, Prove, Rewards, Node, Updates, Settings), one switch per card, one line of help per setting, the node's next switch shown with its height; three engine additions with tests (`state.rs` `node.consensus_digest` and `node.consensus_switches`, `engine.rs`, `prover.rs` program ids via `--mode id`), a Mac window minimum of 900 x 600, one `WM_GETMINMAXINFO` case in `app/windows/host.cpp`, `view.test.mjs` in CI | main `miner-ui-2` (`igneum-wt-miner-ui`, 83a293f and 364feef, then the commit its agent reports after resolving it against gpu-hotplug's card-row states) | merged LAST, after F, at the agent's final commit; the node stays 21d4c73c, so the app, the DMG, CI and the ship files are rebuilt (section 7a) | +| H. The pack loader fix (the outage of 18:31Z, root cause found by the coordinator's agent): the generator retries a rejected candidate program with `seed || k` (epoch 34's attempt 0 was rejected on the saturation rule, attempt 1 accepted) and the workers' pack loader (`proto-cuda/nvrtc/packfile.h`, shared by the OpenCL worker) derived the expected seed words from attempt 0, so every worker started after the boundary refused a correct pack. Changes `packfile.h`, `worker.cpp`, `proto-opencl/host.c`, the relay's parse and tests, an emu test. It changes the Windows WORKER binaries: since 0.3.6 the payload has carried only the worker sources (`host.cu`, `host.c`, `build.bat`) and the PCs built the workers themselves, so the fix reaches the PCs only as exes: both workers cross-built on this Mac (`proto-cuda/nvrtc/build-windows.sh`, mingw, the staged redist) and carried by `push-inputs.sh` into the installer; a hot-fix copy is already on both PCs under `packs\\workers-fix`. The CI builds no worker (`windows.yml` copies the two worker exes and the `nvrtc*.dll` files from the signed inputs only), so the rebuilt pair reaches the installer through `push-inputs.sh` and nowhere else; the engine takes a bundled worker before its own PC-built one (`detect.rs` `Bins`, `engine.rs` `build_worker_from_source` only "when no prebuilt worker ships"), so the fix applies at the first start after the update | main `pack-loop` af983a7 (`igneum-wt-pack-loop`; the tip at merge time) | merged after G | +| I. The export lock: `engine.rs` serialises `igneum-miner export-pack` behind `EXPORT_LOCK` around both call sites (two per-card export threads interleaved into one folder across an epoch change on PC 1, a second way to a wrong pack) | main `opencl-rdna4` a08c371: the lock hunk is the part that must ship; the rest (host.c's duplicate-platform fold, `--readback select`, `--memprobe`, `detect.rs parse_opencl_list`) only if it merges cleanly with hotplug and the OpenCL worker cross-builds without error, else the hunk alone (coordinator, 19:2xZ) | (decided at the merge, section 2) | +| NOT in 0.3.10 unless the coordinator says so: `job-console` d33a266 (one hidden-console builder for every elevated launch, the spawn check in CI, PC 1 console watchers; its agent's message at 18:5xZ) and `opencl-rdna4` a08c371 (its agent's message at 19:1xZ: the OpenCL worker folds the duplicate AMD platform out of `--list`, a GPU select pass, `--memprobe`, `detect.rs` parsing with tests, and `engine.rs` serialises `export-pack` behind `EXPORT_LOCK` because PC 1's `packs\devnet` has held a mismatched `program.h` and `seeds.txt` since 19:07Z: the pack-export class the rollout is held for; passed to the coordinator as a candidate) | the coordinator's rule: a late branch goes in only if it lands before the final tree; the final tree b958493 was pushed at 18:36Z with CI green at 18:42Z. job-console also moves elevated-exit's exit-251 launcher text to `platform.rs`, so its `include_str!` test is repointed at its own merge to master after 0.3.10 | next cut | | The app engine otherwise | unchanged except the version (0.3.10 in the six files); the signed jobs envelope client is master's (housekeeping C, 318a2de) | | +Changelog line for G (the coordinator's words): "The miner is now six sections: Mine, Prove, Rewards, Node, Updates, Settings. One switch per card, every setting with one line of help, the node's next switch shown with its height." + Changelog line for F (the coordinator's words): "A remote job that needs administrator rights now reports when the prompt was not accepted instead of claiming success." Changelog line for E (the coordinator's words): "New cards are picked up while the miner runs; a card that goes away is released; integrated GPUs stay off unless you switch them on. @@ -40,7 +46,15 @@ Changelog line for E (the coordinator's words): "New cards are picked up while t | 0cbaf3a | Merge `c4-fix` (3072919, 18:20Z). One conflict, `docs/bench-log.md`: both entries kept (the fee-table entry, then the C4 entry). Brings `tools/ci/signer-pipe-check.sh` (`igneum-ota-sign ... \| head -1` under pipefail panicked the signer with SIGPIPE on a loaded Mac and killed a publish; now `sed -n 1p` in `publish-jobs.sh`, `publish-manifest.sh`, `push-inputs.sh`) and one `build-inputs-

3@lQagtBueer$!Yta~?T z!NDTW#y-Ipj&!Uf+dk*D#Vla85&K>;%7h!0p0?v40*>5*=8KBSSkhbRNb zQdX@*Us0`AnD}zA=&JRMWJ^v}{C%yx8J?~zt9UZ;xP`}M*XJ|LcIO1s8a1C|MxNX* zVZV;RM00BtfU7g@(t;5!^kHa@L^9c$Abxe@14Ld#Q-;PbCyGQ&Hc*?i(Dn{YXTu{^i!zZTA z#CJzOI%Ax?$%1hGjHMRI6ylw!sBzcn(R!FE7i3dt<=I^?>_jvk`9VlGmzPsoc5MKk zL{>J4U&I04AZ1wdTxsf5qLgJq3A=4B`U@ZR5@XIzA>$afxojaH z?Il}EMX=U;haJU{6}yx=(U*MXBTTNGvzGRY`=OEC6f z{bDz1vN}3Isyq4xGLSgup3rG%-t2<@!VrSy6%0f#23_W!7Y&i<;?`y?SYcDdhB_*X zF>r&|z;mz;qglblk=bSIlr@9K=y$=dd_NwNJy8q_18PXFwAlA2Oo-7t!x9uWVIqSQ z94mcFQ`fo6i&})I6b;dfUEYJH203Yw^(8?aUml7669M zBzwwmUu$RK5$+2tO>d?$e_*0gZ^pw{ESaa`o2Oy#!qf5kUUe1O%FS0rVOMz~T3#b< z-E5n1@sRM%$17`3OnzCwd>1o;1=5aux479H#)?#}fKw z?T4v^y&MW((4Sb`el*+#Pa+2Stjf>q=T+c&)X;(fTB2X!URdVp=(9)m5OR*RgJ!zj zk?RzFb-O1-LybvBGF&c#rPT8D+z!(2(2)}>l5Af=o+h^JXgQOvu?2{dd7ZhCH4Jj; zgC%59()wG7psE~0x=M@LPPVpsQaRYx=#f;oOf+=Ep?hx=U#XFlVb)@>@NP=YP>CJ& zxGb6AZV~QC7a+|VBu^?%D3cJV=Seft1pR&okk{%w*R$J+ZshiJEW#lELi5Nv`PhqD zdy_GCw*#PWL0@*nh4mB$w{K?ziDfwiciN)83=9mkgr^gq>;S?cOmSmbzgn~hXaPQ7 zOU46AGPswfWa>s{9tXthfdZyNkQL0lrFzFV9kz)vKgd0PD7NUhymagu&rA&p_Du7I=YCEaM>?_a0FSy03 zgr4>3`(MQ;U)y`#WOzcPoqU^NAwC~ENP@Fn;K2fBBV3txFylx!XpI^+IA@)k)0k2n z6Iul=OTe)^(uv*0s}U%*h?BE!O~#x`#>tl_kFr+b2A5NrhV<+zAeQ2%y1C};S#nbH ziW)qLXWvOwxR-8xxz5ug%jiufx&z_1em~NTDIOeo!bykXK5ljjdS$e^5?8c|K6RSM zU>5GJ&wvkZMOsscuRP63e18=U8p;~G47eFO+CVhgrUr(0z49yQuYc%h$|<)E8eDoJ z{;1{*dDv^X#o+MHEU&lZ^2cL2*byzbY2Dho?X*5gh4X2O@uDsg^0gZfb(-obeee5@ zMgCJbaoRgC!`M{^J)0O7q;T2QcN}Y83*)it%VF_elWs0|*T}rRbIB^F&H5Y#=STBs zzSW<*SfH~d1%3-`Jq*E%NP9NkK|li zJ7hd%UYzTrg~%P@Mb8nUlGXX29Id|M3rTGyYb8+@oUBbKi?2-0Y zY#n%))+y-6i5-?g-TN!a~8!~N9F9)|DebVw;an*9VV-rY^ zy<1b>$_wLqx_-S>sauLUrb7i6irXz|=HB~A)3$gWbw|9OB$v-~$%hbjeMbJ%+_bR# z``}moPs?)5BcznQ^1L!;OjF%HQs4{Nu@jp~z-P2s=iyM`&s{q;Bl})_HZ4ZCdC4So z(546JOBUk1>>C(x-is59>X_5dsTj%*91ebCi2Is`J5SrsN}eGwnxcgPZJeKrbR zrRSX<(iqWvL3_vnw{mcE*)cljvz1_6OUY87rjsN;iQ=h%8qD;N^qNj;qck{FTZ(M- zF2$xW(?MW&7d_y|UFS=Z_0!jBs^Vt1+*cV8<5lcfgSh4?9hR1uJms+X{xH$qU<$_o z({(&Nnx0#y?u2b$`Nt#mbs`nRbJv&C#7jH9UF2AOA{WdQd(7PI{xXjneiOekkCs&j z=FxWWQ>c)o^+2!Mdi)0qxMlL_tdt(eGv}zjmzPYIWgL~hgsM(r?%xk1Y1y-_Q1lzS z4YKQ}w0^GjBOB-TzcTPDI(q7m3j8Hazk%nnOb4ZibifG*!t^_DyvWVA0H}J4ARMmF z;MiKjeXfIvuA1)VqxWPR^YmqBX{+hzHeF{sGwcaDziK?YB3@PPhdi+?Jv-w{eZpg@ z=E(eX@_2w?`wHOTDF&&huOzXdYs_ybQ`t&vwCbt8Fm@BzN<;R@t$AKl&eSuw)l4ZPTxI4A_4~J?!jF!-XI5W!-Px?nXW{mw>AD-4zPL9sHEP`6pHNEn96ok^`K!zPhVX(MN>PSY8GBFb z1)H^kFFoYM?Az{-6rzY~CksrBUD0-L*1*HC=Uh7ItPiGCj4JUW{Pr&?jHfH~B*QbV zCA~)`O3}%N+#Dcz-tRBBt0_G-!Vf56}ftyugbdw2$@+ce(A^{n!gQFLWbZn-YL68G!*Y9lAWi_^v~<-SH)9r|NSOIy{2U*$%I2ZuSkDqfTaGzM$FRkD zm=PBPUj?<1jl4E*%1`qSYH9O8gABOOWlue?b)jn%%B|R|soxl9gucAt7AEFA2x)H! zNN_BM>n$%_zzr}6bB_D04WAI!KT`k-^mcKj{%6UQhS#OG*cF@BG>z3rp1&+~8G;QT zL9Wpq<2q3HZZGzgzO2lal85Dou$%S?PQA7z?Sjdph^s2abd^eknUydP*+bg=%j>5- z6j8&;vX^e1OKpi4y*>~!#KL{*!6iM-)pi{*`(BOy<2cS18<}B5$F@k*H#haJYtyCp ziRk4$48p1M;jj*Mwc3pvxqhT!fKpX17BV~TD1KjlP@Cj|=C_JOqB2v+uQEfrEUFwt z+VB@^bB25D$?D0L8h}?Jjp7an$89pJ! zEi-DB%}L)~Dgp_S)DLnK?wLUmq>5}JT}3v+v`F2lCp@~_;(Oa+dvXIWxHHYqh~uwZ zNnKxAbwOS2b*GJezBA!8CPV`REv6sPp7t2pdsWEA5J9Af1jx#23|hVq?6lDDOUYi_tR+>qhsp_e+k?xncgUP9{D^zJ$;xh`*n6N+=p(5YRkcV+ zNy+5PH=cZaC!?@4SYEDdPm@=>(*pvt7b2NxOXcMo-g3|K7*^K$i3^u*mL1-$bvpVv z9jnHI*;#E~F(#-m8*=Tid1L)fe{Q<=eqO326{!)wOI=YU2+N~6z$uWZL_<96fUcZu zj#(7jbMd?K5_1t}!MgeiM9``;4|f{#Jb1WpTqoF8;XaRt-@|U3$hlrGOKdx^E?V1ccRWO<|w>#xZTlWmRR(cwfc5O)3bh zhB^TYpMjm^JNH4&H9+FglCKyqXljw-J`Aq>PYn4t!cuAhSyXjdd>LJ%hWD4`y?9O) zg~A+@gyM-_1C}O@wF;&P*e~&05*X^9b)JF3)vZRD23Gb%io!+OCruSlDz=k$D=&vf#Q$4gD~zV%%JuX=ENk@s43^ z^9Z=zbr=`Ksj5eeh#zVcgFvQrl{%#HQpiB^IyK~YARS0Y(l^d?U1*5SYYR-@ZT73_ zC?k|jFyh<$(Qg`Uccj}a?}$KZ`r@)a`!zQ1SA$x4seKpDW1|w|V z0tEgxG#d@1773?3?aC=;XGD{d5CpOC6r5Q6!U|;j;C^_{dvk2_CAjwGU1gie8e*k+ zfNsqKRxZj_bt2;iB;NF1aZK~d3@3eASo?w&+QTXDVDnSfD+=7O=M$r5VF)HUiLwGr zqyP`vOwnS_L@0-Sn2Z;5YVK#O*k5(Nor(Zuz`t7FX7%05S^8B2mRf1ZD1l%prH`(~ zMJH3-wGUqe>;gII+5Mi{ymvq>q3?%@V*OV(`8%ns%>=3j_M1Cfrn2{8MuGds{hQB zjdcQG;w^T=Tn>$Iq@Hdg@MMVQLQ=9y}ser}@krji1 z)+#kMnM)_e7G#N3b;NE_lGFGLzP?kLc%h8L0m^95nllE2Q@E!aV28Fz1{9T6H=71b z{AgQ&WQ!Y29TFuqUVj*QB;Z7E%L$=}Peaa#eeqiDM_5m4T@LuHY*vpSXm#^08Plp1 zYL4OV#5Qu+-QpO{a7UjVR|)9{X@es(+*QH`WfR-MWJegps%$GKP6+bZ&3Id%WT%AG zckA7VM|qYB478r$4y&d5L5HoL#voh3HO+ zA*La1*9wGQFX5Icc#05vYtfE^g?iCiDPJkXv7LM7_3+?F7=EoSGU(BVlWiAG~llEBWIy9$TXUuZ|kcl(XGPWe&EKkC}^)AR!4i4(C5-0x1I9FsqV; z2!^MQ1|d*@jJ!3#neV;2lQrp0zto57#&_rMn-$U%sd!ag!>itX(~}6XW|TH;^3ZE@ zs4%Lzun8Dv7L_4U%|fe=;^i}P!#8WPajW8u{UPt07#zNw5e&9CRkr85*At;4N?RNZ zx4pa{*k)95*@k;!QdiH?GClYje;*JxI6{J>`wM2S*M3v8L*VAJI-9%4-n^;QGLw4Q zu*wpF*NApv$DWU#zX4yVVCvm{{VPbwKD^dyG0EQKN3lDSm4vPY8Q>oy-*x!-^c-u8 zgK~68#*xs=}LZ!_DeJF0p}kj^55azLD%|~ z`Hw*^X98;Sexxxj#-AlKo2&O4`>o|=)`V^D#phSQ`0T=VD#dq3BB(|=zM8-NR{rIU za7#`{l`9k!lWFM*7JMTR>@;+;q~o^FCEK+)lWsC434=@|0NQ5Fl78TT6?8xl-CHn3 zqw2k;=H@)b$AQoLkA>A)=}PmmSYd<<6a-4;Q`rqM^~v zo4p$=9D6~{>LJ#+?}hy!53Fex6|(VFWkRM7AKCe}lKIknPzeSN;ft!!NKjFi>ZQj*u_+#VSF zi_U%sO5QrB9E$?i+%}MJdQDZ|k$(4LSv$F*^L`QO35Gt_Y?Lfx)!NElrv3P*-R<`; zZdt2eots5?n|15sJvyx3UEqr7e!L-QH$+s`pDW97_rg)}_b&VON1`1<|H?l&%QS0z!NPK~oXMKf;pjcmh#ao-lx z4vwNeIT?ep$n~?dC-kKUxj`xya!$BL<0NM;em*U_53PmoqBwST68*GWZ`uk9kOvh) zr;>pK@@75_l^N0Cq3?z@*bgOIC;Yci;ttI_l>L`;m%k$t`8AD#5S{IpSa*rsOc{cU zpl@vYH!$3a`8Bqv(H4P^Q0<{SCqz|xrmFuWzN%`#2#%!cE8h-(@%dyj#cExv=qy~t zLi-GPmJ6ESJGTnD$BLU<@9ryGgpC}!?14cKfV$QP41LA<0gP_7L+-c=B%3!51nTrND13_D{=_jqwtE&_JO0)Px=M<45>T49@_6d$r8=4-y>Ja|(CAXD{yVcw3p_+|KB zv~_NY1>J&*v5yzty?@bb)j@nQyKFH`^Fp9r2@!>cj9Dt#?JJ$mc*E>mxro#rM?uF( z|7|(}>t}@)gN<%5bG;(#jA{d3Jtu7Rbngw&dvc$EO3${+9JmNTe?3sANhdsn+h!)PtDFcmA7SaRHNon(l3>BFN@k5=n8}#z zVwWL&b&)Opll%Jog8u+V0SOtcPIPk|FGh2~Jx0(jSKQD$8PG?RwePx4@ zS%14sb8p~5+XxRwg7O&-NNszilc6-@?0RPQEi7HBl1tT_SgQ)!>_?D8Vd4vC==mP{ zz?MH!>&+XuxbM+7l5tm;C}priP>BR3=?w~DMBl!dz$eI<`Uv_z*R zxD6#ta_JS_7N1*--v;I5xE2(ksMt1kT{pmfqPN3!Yq%O{bOWI`gjO5~ea=rpAB#2U z^3aqSB7+B!=(6*#kQp=uxhof-~s=@-oJylL`w^MiV}-4wts)$LH)8^=q@3l6)dQ8YZYNEs9x zsgqZ&&?Dz>4DhTV=hA2>NTRLd6w@khj6xXWZ%ll% z>qRC#!AQ_PP*tzKT^BGoPNd}Vj!oYMWQ8`9_#x$LNU0hsC~?IuHpcp6BgE^iqLX;R z^HamQkqgTzUXIU0Zmuj{?ImX`{dnGW=`L<@l}9EGk|yjj2uq+CWWdBT3ej55=1bX= zYF|;*K6_vmakR97*oH8k+(Y__ll{X&&CUx07NAsSm1bynQN=qwI9sMTNp(crwgj!k zOYc>!YPhVNgFePnVcAl)>e9UVNj5rbR+z52#T4P$?&F$E^_JKmXW+~p(Z zHqQ%w4p1sQQei;Lr90Ltx*gNtF#xjj)6%cf5;hprTQ$YqL(<(x_jWf^ZFYF4xpa%_P!e!VXPRZ{tcI~%@lUw)=~$PD`fx*r#WHE4(lJD zGjcW!aQl7q+jA{g3XVZrC0=ng^ zbCZcqGB&(F`ABh|VQde2vIN=rqli7snyGI1EB!Ke5%;&XLTD)^;Ro0i58J+bm7P2YVXxSfV!0w`JoI&BUp9M4>h#|e`vtmR z;#MLAA+W1#Q?;u|5M(B@{}G8;Km~Cl)N(U1o{U1Iuu7*YHS$y~=_rCnkhZrW;)}B; zRP%kGPRk^p!Czbe?aaiK>4QY1Mj54^k#?ltOv)_QA6+EO)MqSzB$+vIj4r4s*yR_H&Oy93}L0_`TO`6>!uANMtEOk z{+59FqnI3~sJRS(spHaA@NwHbyP3nt3s%cz?8H=uv>~|^MbH1bt^SkK;j}M`s`?&a zK3fT%xN#uwvrm19)M%9)q*=(XtT1N=o-cr#vw5c2P*uXyS>`VKNH|e1$$Zvdk}vkl zj(SCZ3{5&XZXF^#ZD}$=iyBeC8#Ph#K0-=w!KQ6@Ym2iCTM#ey)M+v7Fk}l!UD+;B zT*|H`tPk)6@VM;u$j6$M;tQIR97+_@)TcANqCMul)z*xLc1YMJub%zIHSe$LQs*?V z2ZI@3E5EB~I5V&SF=HDrwW@AsgYi4r|mqYKnEZ%g~Fy(%I z-ccsie3A>f2xu>hX{_f*HkX(6%_;hR+}!&)lVT>(n0c`Pcex`hTWK^Bv2zq*}%q5QExne`^(n` zXa0;p&PQ{#y1D|XeiD#My}M|HBjO>XvOl1xa1c_Z=7+%0-D~yB(O8x*-mSNhZDRLL zsjrkd|7pI)=0483AL;evYrJTEyoc>$fORIbC^Ix#7}zfJPMbgZ-!>;vm&^pP@WQtY zw-8R^Gw1d-2E45KaAopvN3EZOOUL=mFSY>0Jn2&IUdTcC9n+fbM8tfa=CK+xPvrQ5 zNg1W4Z797@>oAHSO0OgyaVgz&Zi@W{WT)qAPFlUKS9VO!UTJ`Cn@W((YAG8@wmwq6 zFpIjpoeuy#vR9-}_%lVtRH;jH3JxDBDv0;L?O06mkuY;C#+)f=&pZe{`72F7P!I{F zlji6~@|khneT65 zoNF+)hhh9GO{EQOsa{yNq`J1713whxR}$wt%MNb5sQ9zkzv1p9*%K2Z-f3Erim@3* zVpvdjUU|$d;Prl|1jzjAt%91?km$U{NOw|STWy5O4V;{-_oR**jZV5 zFKc{K+mct8@{yOOuoO**Ub%ixfJ$K6)=BFtczCTbgI|tk58U-^)$r$cyDr*152##z zN5|uT6vzZKYoP7V3nYQvmkEfz1^g;qMW8i@MnT_GI zODosU0g-ff-wV-sft^Kx^Bi=EYFmvdiPTTkg4xS%l+a8DFlkO3zdnCHpi$R-zBv5S z-4vMYq<>+_&#U$iVe@@^1;`moJU=%0uLtiNa*Jb6chv7)&Rn`7{Hm5+S#5&$R@Y~C zWnCF*-H$0bYLMy`3y$u5q`3IJk)n#UDu>!OyhJK$lD6gO+{et?eV)0$4zz!Ml|BjX zI`Hjd?>v4sKS+9FDFSxphcrYQ%x6;EbsBVEGe)WCv#4R@Yci20V^kuy|J#5P1io+g zbtN_aCN|`6??Co+OFi9_<~%L+5k&3U)elB|wh$3!4WxX<_!z%bBEq$X@(U@Bj3%en z`CUgHvF-2V;nTi6TuTcK{aGHW7pR^c&I(-BV15l5!Usq|9EB&K;lHc`_{-LO+K=Yi zrv;z*nQlx%hD1t&9ToR95K@*ZyJ&ypADi^IZ||_bh;w8=^_$85=SnqF1N~t+Np^5X zci#5B@aZW$BrWp(_mJ{`e*Ujb^R;AArOp0IRSkcjB7}Ofzq){LkCcoIADWk!=iG9l zm+UtZ{M{o^4Ir*9?0INYB)}*{nAzv)d{bawJbYgf$;c2R)yA$K`rGUMN^sH>C5{^T zS>@9V=ocEq5oi{Qi8`Lx|Eokeu~efFM^IQTocBT5&jR%;v!?1o@?$0n57DIp3DD4k zfICx)gqK%kLUcq##G|thez-9|3(`NwHDRI5{^)O&|HdZeL8c4=;z2B{`+*;-+Ca3u zEsQ^17vOc-F8=FpEZUFZophP>gUGudSY1E-_oWeb@h8xRzdS`PDxC{b%jet^C^1AT+-YG$soZ!qHraKkndpFfBHcBAfonDR7Sz zpWyu$Ls8yQVbXs>-fhyMS2oF|M1Yu#yY7BquvF!U02$JFu||~`VW#{A)9e4-tpC_g zA3rPP=v!+WR%Rbaf6LYSfc+JIp*8{%C)#)KhU1p(jC6E#*rM+r{-bF7NNf{3Vj$f{ zPF$MZCUa63eIT^Wy!8kC6E8Fp6BDanm2}bi944o`1oeOZ$C(ijP0+R$YC)f%3$>vU zS!5vun$R0f=PvHgLqgt{OHTmj2C$j(rN^)TrJ%pw!-L;8vQlPO_*>;p=pi2P>`Q5a zxO>?CyQ)h10zQS3PNzz+{w?^XFt3DY2EhKdcEuMW1#E$vD_DSa~a z>AgRT$P1=Kt4vfE+$tNjq1#PIn2B=!bkpF60R+{&8*=lXLSnXfj$5bP z+S=wogx6Ad)w${&|F|8A*`($@l2CPYAq09qt7%^$#Bk??4-`VpcW1{FfS*elleG=$ zKoI?v<4u;IPv>8Yp3_~4ZdE6)!u0gdhI(lJF4O>!n2G$B+ykk7Pu<${0g$<@8J^J) zh@jMzXaD1YAi+S@TBr_{hHS}qYdF~yW(yK&JK@h(FaH>@2jvt5sDOp&YSkXc{CQ70 zA%xKDZ!TMp-$V%-E&?B^GM0 z#`(uJmB^y{{PnBTC}J|Tf1d!j(DG*836@x6eLA;={x43@&P%?4ODrZkfZY|Mp}ek;L1qq+nK zX}(mwLO`ZaGJ>{*8$>R90wp<2ng89LyTd~1f62Pbn#XF$({7K%B&TAwjG#+-b7Gu+ zGk=h^CL*he@6SquI%;BSS{m+6SCVjh0c9pSoCS~|M&C#Z&K%rVtx@9 zhY3=hVD3X9~dEa^m> zov8ntv;UuBB!ND5MT2I$p(r*sb}T`kw!G14(?!PLv0??B^nFoWF*b8uT-nmZIqjkx zWqcjb{I@(gPYpn2J;*4(Y(-=)oIZWJckE-(&Y9@Q!mo}+*S&UJLDo0MaKfK{*BCI< zshBw(kfc$jtNC8o^t)v!FM#9W38CY^Mv@E6(R5Yy2e0QD?Y2n!KW`beFL15^tS<() z{mCzQqZH0C8&d&E+02WlId6RQ2N^S4wB( zCwVf5wi*ezqH(!yU_hUis@eJZ>ik%LBl+=e28&SYKOOH>BpPTqQuX2rHMSp^)uxu= zncJAQmJLx&*DJXRg%kDw=#Gv$r4bjF462R=JhzSlBl#vvzqEG0Tm0+*^gx*A@A?AC zUb_4sES}K$!0P`~3I1l2HA43B^LX5_i`ER~Q;36*WtV2ljq&5a3WY4L?QAPqX->^> zeOL1T+~2h;%mL43jy4*Ue_M5?hl3=1%|7o}0&GJk z1EQl_Y->~DzMi*_y8yHlxq!*9KhLxYwl!NvA^vb;HqaXMgtAwq)3lmyP3o!u%>AIj zz5dxUPe8U=@%8n!IsmGk?S8K3`y*%fAAaV4%16Va1y6JNWf!~*H0DKI}Qp+lN4p8Z~KpPj46KpwESm&L z=B%3mdzKcdS}fp{1eCPn(rTazH3oRR92(c|eK;yS^FAUA$gp!G8*Bf?FcGT3V)%Ww zf|QN54wc?ao%|hqP9?VSG`GGHK+X6LFT7xu22>JXJH4p4)bvUpr3`}l{9Ugte)qML zAb4K1G2qbRC-=KB+TZ0s^*)KE4FSRnkL}96&q~YG2=9zg*VZ-xtbr?$@7bN|AO*SE z&@xn`Iyt_}5Wq7_mLgfa*4kukwX2OQ??C|T?tgJ^LCnt-7&6N1nz0>md(J)q5T77Vx1g*29CEKnO^=Oe$I&P;+9 zu-0Z|=}%Rnhb>tGy~_nW2>pOLF~1?A3JF>tXd4?FJKkLQ7pn99BdYYo`->+K8{2rp zzlR`-UFWh@ZbzZ#Q6mt{Zwg2UT+WzQX$pQwtJ*X9I;wpbXq&tipmq-J-{&nf12npi zO-)UEr`wY8DnOJ0im?+kthf)Vn?uPgZ36WI{x6jI*8XB)H>(!2+nJAAH8$Ax6Ykc1VN|{62!r+o#>M+&$%|V?FW{7fp zUI_ZG*(pS3zAPLVh@CbeflC1!7esHlEC9F;9e_?Oh&iJ3%~Zrarkdk`?kRfgEY~Y` zJ;wlQ;;VS`E48+RsBN+1=S6zn3Jy0>Sh#{_)jh+z8^s^cu78Uc{!YduLa6SU$G zWZCE(;JkAHY#sEDBlOI*S_nxDJuFk0?h(>%*vqRA4nd5;bpV`3QsB`y-;J6hi~WEQ zcD#!NQ{zK;DAQx}{Kn4e-g;6tGLuLTT;x^>IsHo4r2iaX`QbJtvg~IaYRv2aIkP@V z2A5ozQCDU!Hy{7U)12By>Zyvkl1~ozj?~~ly_2wKq9x#WC<1cY2Y5uM0*E!%5yws$ z1ouZtX;=bdmLD5?POHHZs+b7>To3}B*M6Y7@6gD@C(fCG1}y%jW@f-L+hFjU!^I#C zzP;|Txp2S2V{@awlaEL{{_*elmH&2x^J^z|9YzA)(tSR#N0Nz|11GoRaVXbmwi^XYUs}?u?BOAmPo_chv$g4KET^I#ok#FBH%18=uqwabA<7SVydPPOO1_DWou-djee9DCWbvjsC_~cu#&t0WMeTl zM>^WuZ{=&vY&?-sxOMBP;v3$NCP>i9DXPJCV(9jhl38?6wD3ZSG#$UCiSpOjn{|yo zh4mG0rf;CoNrxZAiMX{d>-q^T5fTz-qa@VSPF)X>4p7U+>c9T`;rBOD`d^a~31u?B zZd8Gt{qo30QoGkKHfOa%;>uSkh}uj~ZZd!cX@Zl@w20c#GU2WJEnpAW0J}L~TJpPh zB5xC*Ks$p=Z|*tWVnhbiUKhs;XA zk-yBxckAB0dzoD4^>q6wyRO?5*$s1pfMX3s9qGT0=|4Pm^m&j%$=JplX<~h%hK2@S z;F0I%8<#4VFqo(2@>^9#fJ0ir);7EU>Vdb zMvya3=yFwa^K?Fo_FHrUim#@p-%_1C$=us}^W~d2Y^*BrFFHFl!1FWS2Ho#E$}|F! zNl_QcBL8=9|NB=rNRcE-;Il>h(f4ygj-Sitg@*q13P>bMFi{`<C_w{G8-zjf=?df|2 z)KpU>$M&SCpr96T&o8mc8tS|Rfuf4*W{sQ@1e$LeroW#G5)DM)HlUXOAQ&*ebhx9D zk27oK7jBXLN#i7{DTtVvncuZ=%8FE;ZR;}ZEY6RqxNf=Sbt}CB*!ufC5I^frHRRVJ@t`oQ=P>_pm`!q zvgr36lp$UVhyrZs3Yb2pFBI(Vx=-MFdAv%q&9Sl85ncIr1jYUw%O*UvrN#TqGvZ1u zUMeXmNs~$Q{jJ-x9F2X0tbOHtNgw>kEFR6YlTgW(Jc)d$NT7L4BKCV4J@JNbPmhr1 z2n2fSS~kY9d#N3CVt`cfPW^Au%?eC$l{6$I{NFBE3V$H3mbMN_@Ah&P(yXEjj zm|XayL6$g1^p=g|`t^=-J`PPs$AX5?6kqLY*K!|$a^F*#klL$gEFYmZ8oQD2dLS-_ z#A`>$2Op_ktZ{H~klB>#BH$$bqu!`SlB|`Q>FO38s}6jgVM?XOXa2&Z=#q%bf+EB~ zS7J(;KvTl)?797oXL?OsTUU3PIVq|4kWO+n|I+NrM6i+W1*<;`j3QAWh~mkAecPsa zJM+x*kgWSQceSOJ6U2;PYDj2kGZie!iv95Fd?I{yuZF+w`9N%F(S;}|a3D6kDS7uG zUHM-ese^@-yqI{5ZLK$5>|#i8FmH?EdM{fFl7LX#Pju_)!M6GY_}q51(*4@ns+wP% zi$CNqE6x75SnjVpDPN{j$Wt01uKd<46+5qykfgSIS%RReiu1u2F&3Mth4wo4Frll#-+A)+gU z<`_KQCLC)|37QNz#-o(+n_`e3lYeX=aR|BAn{n89CSQZsm}cM+5%TdaqJ`){|2-X!LinbC|V*U94F|re&9;E9(@`Rpn%6d z&$c;ota63v&(0`3*GEjcu=rx983G$zD6~J?>Yy3sU;{&mjvXThZlVUT2ffa%cb~~0 ztix~qOJ5+O^GRvZSnq|4-Op=V327h%N=3ekq#x(?#)Bg}~&r@G+~y=?u`dnAU?tBdDD(*OFi-;8{f zXrV=OT?ZlNkQKT=Ki6)Z>-%f&lMLqf7@RAwZI1BE$2|U>;rT8>s^vandT7kiUkw%@ zVQtO8!S&$1T$tPTBma80KMOV?aV*K&S*se&r{V8f3=n$zFFR=86|_GQ@Ur1O04LAC zJ~I<7(yab3iZQ_K@R^W=irMQuvl^hCxUx%3;ejE)K{{>l0i19zBO+`JLPOI3lu!Y3 zyu@xKZihC@!5%LXq5%@pG58?sKtfIsy~V?sNFLlitVbb4aQAN&f(IY?=YOx{tGy<5 zmyQ=O>AT$gq=njkwF+#%8F*p*>KG5hHDZPO(s%Xe0`<43t{4@4jiA-dgp>Br} zE%y)#`|CAe(REP&GIE9TlzLOAnJLs+MCAv9>z`Bk*$ha`kj$IqY~=T}Z)T#nj#a}^ zo1t$GH0qVQ#OC?zyUD5grH^DlsZrxiy1{>OH(2XMh&@4@nq#Y~XSU7bQ{syk);O?}_t7*yEZBA7`)Yzy#>$fm#|>)n!ub|6%oArVAGuJEy*V z6N*p!7eiIM5aM()@z!0N;q2ZjL`e2M(R=;}D^E!I?Af!c$(Eh`|00B6T}8B!uZ`=? z^p?%hPd@1bDP1^{e{iU#Gx;t(6ei%vd7SP_;Y|%poKQ0?Ay&tfkbyYCpzj*?EVK z&s-JtsbF6u*pnpgJY(>2N}*~FW&3l`2ckF982Q*NcO$PzI zL(m58LD%U(r_1c@^34e<5dbdr8T3FS5||@AbojGPG5NLemIQGXKn_Wdw7PZ`NEhW5 zLgB`~T?KK~Ze#u5nE3CfK%x+7Ps=@wS$>T*aPMg<%EH6LdCgmyX4=NxpNEESXv@eq zifx0&8m55I@%&VyUJ{|9vjs@n%MxgUDxF(mpriNo|BC*I6OQ*9F$S@Gryam}Hn!aHATaK)M(vx>2$=*FAh~OMa>s zG&yvePRe)$dbnX>eITV74)QRKAcSsG=f8e_e9j3lS=K*l;yNVKeD_}%{~tG*tH+38 zhciK}?6t)E_wO&w0JmjlIc!fW73b0ob&&&E$M+yt!VAOlHGKKPsqXD9*p*)05WyOh zEvo>WV(UQ^WMg5F!@|M>c<3B*%F0n7_ZP#dasN{=fB(sap^APx_1@h3Wjif<^OVAn z*x|zunEtpge?=JJuO(W|s_DeN$~m+x>O&b!D@C0d$z9uXvz=!K%ZjIqymj;P^G!io zB+sbEv$wz^`P>bpA;>VYbfn^?1?-0A-_G{rj$aZPsLFL7dYt2EbqjX)Z_4+(ZAw2u zv>VcxVR8?Oa*PBa<2O2h-(Fg5cPp8Om{`C=ma43vSCd z<7vw}QOK#cQTlCSqO`BTyiKjI(8^5At~XByJemd6!n{}*EYs^3DSCxOUUkR@B!j%J zOE#0P-Pv)EmPRDI&z$@DwEC@ULV27>5-2E`=Ub0g@$uY?6`S!YdXpAcBQ^j7RoW#M zmfMbB5HvoKFJ2fC#W~*9(RuSu(sU^Bu;NVDQ{cR%0A!6H(SE?*eAdZIDvcl$G>NQ+ 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 O+tC7Kq86r6q>$7xHHtNc+|@ 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⪙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�?~HP)A5R5Tu3RR#aefp!|ENasifC=E`{Eg+y}HV(s)3zx zKUBv?ggK{wG#VziCCojMtb}LZaT}jg%#0>aMaIl2YPaX$eps<7;L_LI+yj%6l2Da> zO=vDOo-d|l)#qx=_`|mhDzz*CC@Il??E8TsD9su^+4KZ$G+4{y=V3=3SBTRUlJe9VE!Ap$^eFyCBo%7J&Ualp~wHdzw|?4#c<5I1Kj9 z4rsAvtR{}+nnMtd-7c@-NyFTmXSunhpl^HL_~5j&(Q@s!Z%1lv)7rnA{m$&sKx=Gu z-m6C!VG-u*td$>NY-e<*bf-dnkWe3%jkrQPcvQ zo^P&x$~wF!h|;oIbqG~d3;gOrnUG)g7sGi-H%{)wenu6aQNsjCC;&yIN|GFA^r2GCe=#PlziJ5>e5`dCf0#v zJIJGD(C>1D{iC;{SbntWEYHP3o)%$4k@|z}xI#>O*kD#Q!?=f5zI9ik!ozWO ztlZ#o#?34J%6HbD86#WgRBzk}A~%i@3<)!* zg$)~tuf|uwX+I_*2Eg}rh+%rCv{w_7%-wRs$|ggPPdZ|Kb9oARf)}n|otJ)+f)M4N zbp`0AE&oz)%DdRY5tHKX(92i{)?^Jv` z|-ySH*ozF(yFWjg#DH&@MgLdx0pyMp%{aMRZSPWH`rf znTZG+`B?B+dW)#7V3t5l2@fPwI8%|F3aL-|5v5Od&zaU}=8bsnx_%c@*^g7|S_lQ#}V zvwNl%3|Xj<9XZCUa0(nhlmQAqzt7yc`2OK1=%7K1IYll-fQY?=IyMTwr0xx;nuv;u z*75YymRg8>rsL#728XrKEayVxr8z66_jvBP2EN>splg8v80@P?pY`z&<686sRsk`9 zj~+TRUTamO38LDLy6pV?u}Le0bB+BodA)w;5m01Xio_Y&#hk{riHM0+&hCNk52ZT< zE&)JaH1?h8wsW^cB(O5r*Sn^K=LgC0E-nA;(4QKs2pT2_sEoJ2#6`W0mPYLFvb{I5 z&UA3mhg|4MS9EYzus~f?I!&A-ldD_5$-B>dox!G(*KhM4VPV0t@f2fY1h~oaxu^i( zr!Ivj*i-fM@Wt(I{kiH|^_e`2Y$Cu6$g0bRu~+f&i=a*;QrsEmkzD+?bN1+hW@>pVXC0S#5S zj-8R;wx&I%s65a_>X0`4BEP)bAna&;{99?3!+Hc9MAgsFuYF>PPJsNT1_<{El;;*LHRCRLqRuyTl<}Y>wEwI8iBtnQChc_BAq47uQfKC%nov zGyTmay$=`E+hIAy$vIZajy*Xbq3Z|#wP|tHe{!gsbN3Qjd9s-R4;Jw`BtQIxT;5)| z&3JYEbScbtL41zrw<@Zb8ofRpc>^-GLh&JxqT2U{c>+?5rLJ8&BWp_CXj|~kiRKB! z=gKOsbJDoK;V9Du4{CPD2`0|Y(GB0d@GvWFYk5NErArw{LjlG1iZxuz5T4im6;zcS zJv-nnn0A;7tz0)agnQJi#x^c}Z``3`tLODV#d;XsxO5dmPK|Gims!iHZg~!=%M}Od zFgMcP{=SXgtjd{;eAkGf-sTd*`4rOR`8%7Ls1wPKyxWK9$#={G4f0!?9)IzJN@U8a z$*k(4d58FTZeH2;FU-pu-RV0Qc#p)APx0c@r1+ocq{{H(j8#0>X${+7rc8Kx)(KnY z#X%pvY9!w5cK^F0=I5KLOF|BI@XYON_bP4Mtq`wo$f1toP9)FJ`B>Uir7F&AN3W8%KECL1s}bI)c)I3p=5~7^msOgD z?@dXjmlvD8OEN^#M z)qCe3`~ymy``^Etj6cxFX||x9w`b&EO(oi))#SW??{QyjZ)Ydh%)l%L7xo_YCs8bn zkNvqn6=Wi4ZJDw=Lo*Wo?KqAATCpA;l(PLJfzm~%1Oiz@tsL4*>de~HlS3u$0j(qV zsS^hCb~uMCuHs@yaT=(Ulw zu&~%kgE=@lICNbj5yNFrZLg^wH;k-;J8SNByA$Wu#2w04eVYHKmDviS%*@E>4!#l< zqpU;W=1wIWmY45JsZ`d~gz(nq($y`+imOLW;$yIcM@2D9NJwx93Vvum*uWI#RLS)h z{jZub;|ZIu_7C_2Z`qF|hChCWa>mESS#x4nPK^RtP@p{PaRZ5*7^TkL;+BQ(HY>jV zi}4AC>O{WSoGAd~_S^dZqLU8Pe5ei03RfOXP=gGexx>Ytq>{Td=zm}daQdlG#g4J_`5o{_wl1n zxspBu@NdOxzo&!$)rd_Q4^#~5^UMAI;eY+-Z{=WpK>Q+S0@v8?Z~h<#El@ESk30M~ zZu=sRBnQ|};*Jsz`X3(te}2YoSsE&a?*`j{ z)>{9s&wF#-Y9yKnMECtQYf1F~fB4FVE!|$vCIch}pb3>RA%WyME19MD;2=9tcNGit zwCk6%|J_5?V96owEZ7Zr$Zu|K8T!nQkG}-Kiy`qN@<2}izv@Z<(?5RHqQsdCd8PlB zT>8UD=o#2{I59x(2LkLnAGjpKuoFZ6vZ50x5gry6%`-&RtNAD_@5Gw)&kD8w@I0TY z0r%e3Z6E7zW~l;zILE`GaUp+WmOr&W-T79HUh;R=2KF`=K{Eitqa&kzF}=OPdAsZX z7RhTBU*;>`>>aZ05p9-VXpCER9!yd-~5h`sOulNo^r?jofS#k&+$N8 z2x9f5%h3(aS+@}@|81xE!>Sm1uRn>7;Sl^2o47xBW7oL+Zw6qWC_U(%HJ;vY?(3gS zB^-WRj_i6?X7eBB@1MrtjEzrud^-R5_q_Ta%*mXYZKs2>x33Qzetnu@|*$o~&2h9Del4;7{{EQb; z`je*kjpvI_Q~)4Q3=uN=H}%^1vA55(`NW5qB^eJp%pV`%b$x#t0kG;+YXN2+m5WJy z-}bN1lff6OIALEO#KRgNMv$4j8YsB8YV&fn!1(@uejqu(Mky$h;cjUmii~$dkjxB^ zG}S*m%Vj@}|0fFoTOv?iiS4m$7$#Bb76IQhj=Ec2+{}`)kUFfCdmp zO}3noslSvtY4RZH_y}e0wF6Gjb}daAOvUJnlmQhkEBqe^@IUAIw>b^}i~!2|6EGv&RB3CvnW)E`p0BmtneB z>yGeGc~(%;|Df*hUxxst;9U-k7CS?F)JIf6vu$Ui26bdy;5Q`G@H&Z|ID?6_yM)Cb$z0~)y09+^-LTbq$6rja*tw#<+l(j;!B_YZt=e`!F_5#f(Z}H`gwRT zirtY!qYh(aR77wQ_E1j3(F?;3vd1eB`11ZS4Q|TKz-;oSvRa&e+~Y1bLn8ciE%dmBu9O}LyqWz1__l_{##*OYyzyCsAPOEA;^ z<*A`UrY`6C^2pK!v54N6I(l;CT5)jt94jvg$jFz!+L2WVzBH3xTN<17}W>D7R)l9JKbt5>g{tZ^$s9MY2Mm6NC@d`YyQ5osE=;|8nJ7eoe6yGQh_ z(H4reeDuFw_n?%w~t)% z`oolYj}UkEolK9wJfN)kytD%O+HaTeS=iL{V_+iRj97~QZ_B0%clJ=Rg*QNuEn;XZNbaw>RSw#!rtcQ zX-X>?wfT|r@)BOxcH)FGCpb1)2vPK7bHDnRQAZifc=15b*x3JS)a#79%icl7y*8Ws zS%E^JLvJFV3Xc`W)lx|L8f*O0yt{HmjO3r}x>XTy->wQZ2~eJ6S~3tsEk&Zx2xxOu z2|cR*yjj=o3Gwl3Y0=wy&&cg|@p){@#_M36nj)&qb{JnJmPJ?4{1e+Qj*-9g^B*4# zd`J3IY1UYIWB>$RO;!E`Y6q97GoXTu5o!KQ&)3)Obemj+LV%xt$s)Bg1}I1F?suz3 zw`|^D2P8B<@&8F#Y3R0K>~dGTimC0sGOgRk1IdWR!*lbkI{TE|Q+bWtL>dlJ4UotH z8am?g@`VU1=)0+!YzSs+MP(cB9j?2dc_S6{R|1WzpgnPVmv5%;`N1wvtXEdl^S3_3an$r zn#NpNA##`lF!OVL-Zbp<-66#vnV@qesIROYtFD1S!6FgHjP;%Q-N-kvOh z3C=5a>s9>3_RcYgh$TEDgR0i0d?t7Y4m2BA^Z=NQ=p;a|U~M3#U6#N%VX(D_&6llM zqfst4v9K+$+m__%)5xTqox>(XzzRU#B2ddF5v{B&U-_Qkc$!QyJg9gJLLf|nT<*7U z+O}B6S$DB9&Xjr-cpw@)(0hM`Q#Y17H}v=P#PlWcjGy5?y%I%3L(Bg0Lv(cn9&ee& zaIt@^IcQ1e^bW?H#5=yLg^i7sm07OBk_>zKHuM?i7Ma5U|Ft{N6~F;rm8*5_vz(g1 z=9oLTo~VD!{^APT5$n%8TgRCl6kR)CGNbIPov1cSp~B?wGIVu617=^OjZ?u@}QlMg(nUR zDf-(P64)SRMbs3J4>+BrZXG`DKpF1ES?B#U~oGU|1-dJmS}BVnF@BZ#Mr1hbte-(2%Wn$0r17fI7Ri#u~SD zU)5as=r(jO@y<^87?;O^X_yFucC}UN{PkX>$c8>X)dN}m)8-- z{fyGyO$X-rCiQZgKp7-5*Y9*;TTpY)FJioAEHkEKv!((kY(K4*_T~8~tCtv-+?_dH zwPFn`(5~kZgq7Iz*%{C28D6~MnWmkm5nq&XCFUYzrcBxs5LTU{P!3l7D1>DsLV+eNzE)YLc!{$GFEj~Q@;E%}MWDIHOa^KDIH zUE0$FQ^dm)?-dF>bR?AgrCNU*4^tfVbq=rDO&HwOEK z+Qs!M&o}`y^#U8;f+&N?OVir%b+{MT)In65yEq_BzY^O;y1la%bv@-&9>|rnHt-DL zSF|wWbkoLdu& zE&pl%8G%&1IY;X=ukCk9(ywxh;iyWW7)FSC_D>`FV)i9T7VWC{KvmF!NtAibUjI2{ z_mZ1&w~Ow&7Y&_Td%t(XZfJCdSyh$O62YZ`0Qey6V$Hl_keMOX5l(L=qmlGqoHMAi_XS z+p;@;5Y_+Tq0nbH!HeSe_l9v+pSYfk&hUVlI-VLH^&R9jeVgDeU-_yiLMw9926JJq za_EsHd-{|+!2}`q9w$fYU0KjC;5u(D*HP)+X`@}Vj# zQw|6`r+2A-J|F;nQH+E0V^@5fS-Y+kb@@>4(?+0rT-YS{T6)(DCVAQqkIG)MGlDvyhhQ2g&}26y$)Dsy=hHnf&PJRUcK{B^_-zD72qK< zWuNhQbN5XrLF&+umjLfYsKJTzAzl*ui!^p`zP{KxfOJQI;x{_V*?XJ|F&Vk#+6~Vv z(woKr4R|!S{Ha?S)h*bL_U9SjMSqaUk0ygYNB0TT-U>6KC^QUiv&u14QRL&k)Dadb zZTYy$Id<<~p5?5T?05BNK$Ak-rou<`7u!*3Rfr*wdfD|R&Z%BaN0fC+B`wDJ$<3*J zN%0E@7`d?R`&6&pf-=V)izrOzE>x5`3XuIp3MC@Kuw$i~6=`$jv4d;B2SqFs&}yHI z1mx1;;BNX@Z9UG>md3$P=MUJ3ST3^~b6dXzR(5iK0@16P~ zTvImC)O~&VV>em7PNohlLepLub9gCsf!vFvHpw<6I5oU(5OE#3(6sfFH=OP>3GS2r z`v8j1(j?WSwU`a4Hw)X1{9-uU0Mx>5BRZ-Z{-g}w{8%c6O}u8R!xkeeOSFpQ9wUuR zRW7*GuY&t$9f3h^6D~99_(x)L)*U}g8MDFS$C!=}-E1FeC5S^&6n@i6y>Z40)kl79&|U zb~>q73pW7osGn3_bz6hCMyF=Z-~$Fu#@f`fWj-q>Qq-E9)FGx^oXG~EEf|kpG(8nV zl?-p)@&cW%)Do}hO$TGopQ+wG^AjfboXH*FL!O*fGKu?A5og=_)+k~{eX{)(5TQB} z=6RJ^Yfv=ZY6dT)^B!T)pz^IdsLb-R>fuaSCF^jFjM+UWa z9=T0UM(2-e$Qze65AN0uJK>c27;}hC`kT;64C;WJOt}@{i?nKDFNvT|5~0jxEZj?v z)cSdx6x~?!+2_Yoct4|tCI2+SG>HsP{^nrY)X=p{2Rzbm= z-5=GvhXy%v36_#!B#@%<{($0p>n${wLKe)GkAV8LhfYOB9RvY<4x5D=mEe08$YyCc z7MmQ4ULB$1_;j7a6i1N81HC$TJ}$Zan#=X{JQf;TyhN$2rvS35LSSbMB1_1#ecpV* zgQH!YLZ0Hm#Ou3Y%*T)rE3QfKAk9($n89EK;1D&<#P7Tbz=a~aqUhS5S_`-g?T8r7 zgyXhy?rW6sB&;+LF_|?^8ncC1RvAO+H)_deVRO5+OZ{W<|w zXU12Yj;slvxo}R@)nLg01vwL?hESJ{?6^x^MmgU#(0McQ&V$Q#eL^dt6rhYRk>ch& zOo@tFt2oLRJ=1O^SVaKxKn~c>*#j=P4$9|UuEPcCPNJc*-sv>Od79T;!}>zY+unSY+(OxoEi2?J z`Zo=#@j4)xP@{Q0uE^@yPQ_`j?1jgKF^-P{;DWL*%FZJZYAZK|4tt5A(={$F@6n^P z55{I|4)jBy=^vbe9cT);Xbh}g`X1;sy0ASb<2$2H9ImowM43IDew{;Y`wM6?M{Cno`jc^*6qkXqR4RZ z;=%tcy#HB)QvN^FmGN_w)32L5V`i~g-Mcyu2YQ6S{#3*c)I~Jibq;t5lau^MA=d6K zbz9IC-_|Rc(z62CB^drFm)3Q<<9R^QArP|=7%&M#>{cgUG!J)@T&$aX6k!k5SUeeCR;*lnVGN|G^y~^w|H$H z@MZk5ax6+K8$2B^-`M&U7V3O|$>O+k$n7UrzJw^mT_{mK9= z?bM@Ho5DVv%R31(e>WBiG@?2h7(J&;k?vW@G)ZuX;5R+(HM>qSk#jPrPZ~1I(zJR% zquxlLXqtMVFHd8INuS|vR^-jBtI=*A)c1WZvu1XG1Jm|WY^^|y; zUSPD}$6;nq!sc!nMA2rD2GAovvpSweMKB#>HT$A#_wvF{bgOedWJ7ZCgeu@j`i}vs zjeFy@Q}qX{+8*%2V9D5ykk_SM6}qcs>venClK3NAPPd4d7wjUAx`wse#iF`rB_T)XjGj8k_Hr#&WYNB=sDOHbs7SJw5xE7s{(j*ra{fjI2&}0UX+ePQ=*QrEd`)l%I#9=2 zrE+cVHP`5|N^x^Qfuy&)EwHUDK^6tQ!D%TSZoW6cfk(&zlx_}kSAvGATw1WOrq*Wu!<#Ve6@4Be@E|G*nJzp#XZTuUXKI7fw71l7A#naD&Hb4Nt0f{mKN%2A5%v!4 z9Evn8x^U*A<^CCgciENB%du8~MR%vX*sja!cZmb#^{^v8ESGH~2E#zy#@WUuJ9VQuCYB+ZW;a$d^~Q7$ zD<-eOaTAqb>%h|NXG~i>UUM%>E9{k+OH%kLU%&p-k@=M1i5k{x%|(}qdY7oV;A@M% z{28cI5&%OH=xG4zd--}A&F(-S1~1Jx@uDf#LW%}XR+FqXf|5y022y9Nc1q7hqv&|W z@ls0f&5Nz*AN$ROtA4uWERvG`9z^qB?4|59-F8X(mYGFxi&ll0uki)e{rC143 zbczX6@QioxVPj+J?L?Py9eN+Q*jVulTdqPX`*0!S(C5$6gD1PTJ9_Ui0lYm`x0W<` zlY89ZLliwmRLsO5(bVr8Z)iF(UZLB-+8vbD}ul=^hbR+(my?pTJ5(->>n+ z)^hN2Bf0ng9CyEYpI`8w@C+Bq%E}fEv6PvpRae&WS)XtmLkZeH~FBBkzY{YAVZa1=`?^q($<>p2@{*Z3r&{BCO6MAJW(pDo^g&@da0S9 zH&B#r`O`$=kUg`>ot=Zu(a-xVWbN}iZu^y6SymLsuJtDDF!?KJ6l|tH6|^7U>91T% zN~H40FZ_D=9J+U;1G|0*<*_^$jQvW`k>qt200T2u60cBWOD|1V3VaZ%wA!&)i{xhd zSYFv_natl-XV75kJ`>4k@Ti>uRi?}Q!u8bI_ne5NoIx;GP4sQa>d-al5SjNX2BSRSB3s9JHf z6qH=|KCL1k<1f0uo!xVxipvl-QRPCSp5D#ix@kC@C9=y}$s;ix zOF=9Wd$t9WYm*cB7Cr%7;+=THEVFAO2og>f7B8*1!(rGt zW?T_P{goj!))3?$tL{kC4L$c-6PR<_5)?*EqxX?+ec*hVxuX@Us5$lnT*UDfEV=qx zEWY40%e`2$p-eHg@_IGbYa^=v(Me9_xYtSLSf)gx1@k@F{soAK0@)d`J!j`ZSaP)1 z_myE;J9POiJ9(d6rzFdSpE&4!K#pRNhF?iF0d_1a=mP?%t5^O+izb%@U*GLvyRTE_ z-&xhSl^Q)V*+mB1|7L0!?N=vauQ*#LBxN#dgdMf@{7<(&EYxOsPUV`9Kki5P{OUQ` zil|Jv^4veWJVC$4^c{OC_Yo9Jp3u!K@Pp+nD@^3tFKF=|#d*5Ra>U!B$BG4f5Zx^x zqSOx1$e;MX`ONA~t|oR~aia9f6@NX{Z4MDZf6-Y&2L2p7hXoxrj8==EE_3B#XN+dj zCVGdz+h9#H4yv=#O9Af5>WOTs%fdQCdbxMGuMVeirCp93t>)tiZ@?YMjAPhwqG%Ha z4JDJTpu6eC5kZ4&2Uu}2XODveb{X&a8Yrgmtt}=P#Gum9P;c|+2LvwR3Hb7p&ata%i6vlXx!<(SeGK%r*} z*DR2(oo_T_9!rEIMR919nGeS*`4@!IrNL@HL=6ajZfnrL7CZtZW~H6J+lwz(T2EHc zuil(Ti8yx5x`H!Zkf7N>y*j%wD{B8jxZ4OQzLUf8Y*%PCw*+ReMeRgx%>%g>pQ2C7 zZ2%m6B$Mn2poUZD`DWJ&xUHK3vP`RR^%Mao1&xSTXn*v$$h{4+C=0);S#k8hW@pjw z%^nc>H6hPmne@gR7!5vTC7qA1G^nq?+$?ZRX^{as`&u2Qp9p-a1Q<+bxj$^>UDtm- zKk}uhH$rPp(Q@~kU=_SeUq4&^`K{szk?>B1O1*^L43A*iY4(OrI#;kfqg<;@ zFV^!YI(hqh{W9P%Rpo33TWUgw!<@pR8X)~mg##>@|TrW5Ui z``-j;^$rwen717QSt!d8huR5sOvbom8fuwBi3x(7eyuy9m9b8GMqkecL|mBjl~i3b)2};a9l5PpT{6}S-GlI0*Izg`*8|Comf)gdcG;o~ zO(e|7Pqn^12wo8Fe-Wi9yc%s(_WgLsWURq`r814Ah&n;Gt4fHTb!TU1ReSET_+tF6 zR};gz$&dw;gy`G?zk1 zxhD%XYNu!7ysqOwA+8c%Fq0P)cp!RFH2gc_J||T;s%okUnx~3D%1P{~yFi3H?Pi+Q zG@g;g_fI~4sCk z)#?Q<3!}g09UL51=WY-pwrgCS#}qe9HzYV?M!FM7xHYnBDsFKPhOEBfb=^Jl+&k-A z?uyDpcrGQ)UFA$boxXxRT=ct^%9HA3gC2W|g z-m_P8`^vWZT}V|^eNrWdD}EO|(?1INZb_>Z)KaQu?r}QpA~3kBm0^k7d7x8D65dNE z{holh@wjb(-+ykb@tPTmYf|X*3&h`{)$h6CKfn)O-19u62m$eqqz$wIdw}OiUWCb^ z2e!qDuNZh#XF256zXFWEO`<3VimK|vuvqnC!fHL!A9>`Fr3xE5>SOnaHM-ykGWHBE zQqVprc{HOat5z7>OItn$|BS5O{`Qi*Jd9L@8s_SZOScPS!?7NE2bp#ty_Ep4&T39@ z3%0k<0I-kYs`bsO^`rKtuis=viEM5xh0r~7N_J~sVM87}HwabBcTJiWwJCwXdBGfz zB3HNSdW&A~*4Q^6-oM;x3hq`)4>FQ>T4**tQ%`?^9obt6<}J)vG!h%?e(|t2C)TaS zJ+KhIBFUv*EUwZ8E}m4W_7Hw%#TO|P$!=gY@g4RdTBnr0s;sE2IG9eOZCjSLAz6tp zPq12}XZapF?v%9lQPE)|3u{|vPG3*9opvZQ_()lb%Kx|F`Pa*PQu^aBF?`jyYRfkDC6N>J4b`jUP%aC0G&0lh`tfvy&@&(2hMf7QF473=!$S0KRVe3rN29S57v zI;CA%%_z1&XAQ`9DVn|=q+I9Vr>vDt+#JK9Ye{lY4Ovn5kcfp7bi)HdW7uC!!&D9@ zC!|J2!IugXE!AAfj&pmx=KFJ`{R`P#zHI{3;qB0i_VE*W8ir;EP-2@4A{|*a;d|WNcyOmfB5&bfLVGPI87gcsO{~w-^$rFa@zl`L9S$f?P6Ajt>q) zxr9P$Q%+3`L}7cpl3s9CbB zbnwg1lHEr8sRI8kZY(SXHM?#`ux4pju7WnBNK_ zB=XsrB(C(tCUyZ1;$fgI=a8#hINfJJ`60336e$Ips+#iD#liXyMiGsQR^;<>qRbeM z^-=QmI-SDPy-a6;kMp0*t*v>1oW04$P_`QJfo8b}g2%LNY<%1-)$@{s)1>za@fvV) zyf$rv0iAY^%s-PH<$tmRcbEzH4OtM+UL$lcS-`2k6K9=sKoDFaTU;5A@@`{&kJsx6 zH03=>4o^Jri_m`8RtmU00&ZU44vJ;wT(^q+R+d}$seYM<99{>E(s{LShE}pHq!WNc zJ{0SPUKz(}ncX<>birvOQ+;{rQV}za`F#;}}cg>*4G4?CeojU9&#fX>qJS zIcAhfPf=woz+32j$9u$j&P{+iVYc}0s5*%I=8(1I3{$|{v0^)dI+hN4%y#ATngXUp~sl$Yl&l+JirMNw#U&H2h3 zXk&~g2D!5v=qC~9_C_BZ%XzJ zu#g;8adV0P>4f>$7TPA2{{8!;@m6B*P2KIV9V`Ci zI<X60}J~(Enk2!FnB*f`1r4E>>m+@ml)>v{(3}jf5H<;lcm#@+TYBC!dc`RCy%+=so@W*I>P#{O1V##V`8&@GhZWOI{iM=025@ z7JoCaz8`G7j*E#&JXq>TvRP`+R#mWhn5NU!8DY#q0jR?)Gjta&kpNLiAhu|CF~oy% z7vq5y$|h)JE*~bX{<-CdQkV8?vuO+8*g&kf%TtREPLEhTZe0JDitLRgelc!8mN}XB zE?%_@MFMmQh0Q_^Ow2jCJR6*UMdDssp=1MOKriKK%@STOZ7v7W#&uI?qKc%{I(>cM_Su zrd&gP=4zG#$Q?HVIOVdPv6Ae^Hy@p!W!--vJuk<%wlop>BI(WMd)k4+=l9b;`O{aO z#fVF=8VMs&1A#kbLTYNNxTbAKb{Gr}Q(ce-af6Zdu&u{Nn zzN`!|?*Fw;M6WS}NguP1*Vrt%UK8|C`5)(gGmUr>2P}*Yg1qnFxeC-vFiRu1xB;hQ zwKIZx_57ewhMr#L1?j%%GyErfwpmjLFueW;Aw_|JV_>(+RDf^Et6r>Qy`+wW0vLx=@H_)oVc;MaqNTcTCxAIVa$HGyM2Q#&HytMaJauv7%1|b)aMCK$X zCx>fo${o9#n#MUWCnl`K!}S>$$;^#VJdl^al*`QKg*xf6EWcR3e=k10iW{+btPLEz zjx}tYCUU%tY9sswlg!#x$8NeBcWMt}$XLHlzoNo_!hl-l^6bH5vD! z@r#Gf`}1O%BB&^DQbcuiQ*L9X$=n&;mqW6wI44vpja1a>+(r1eW}A zDzF)bzZT?wXBQfjP1}k7&sl#HaVPWLn^HCU;c!YetLazTp^xm2Rzs9l(pj^da@Wt0 zdjJ=_aPea_pVzf}-sAMbY@pRNvslJ@t`Sm{!;%1)35h0?%;eORhdu3+Ie?ARcYfd? zxvR&(zz{s`kP0{gbCu=M6q>H)4a6SnU9aDr zB{-U$@X?TJuS&11){R?~!hPa9G7d%D zr>(LOK|KpFcqZ#hXlVcXWdarf1#D84)&V~)b*`j@VvknhCHaahGGSN!iPzsUV(6$@K((vI8Utbs8a;$->n|Ief=IbO67bfR7bw>f8?jggcvy5Y54nDDiFRQQ$M_rk)FQW3XP9XSf}dRk)R z&Xsb(c#2B%29-RI&cN&Cu0{w|im=8CZhOgEW~$+0wwvlkJ_U9o^?l8Y9m}+D7EqZ1 zkxVGG#CQaBe&vV!K6B|9zKM{jgLLF3^);+7?ZYD0oKv>wS~jNw7?-VCCgpO@r1+7B zv;!$}h%_BR?e_r|cCM^r$aI9v*?hJv@eZ%}P0vfC%l`09sQ2OU`1tzl!3i@Vt^C>C z#3P|$hla(kUa7_^Rx59ida9~1Zm=<^e0?w(FZ>kXOtKEUxz?{E-4|OicN<<_lQjkO z`C0ROe3^}i4%-+ut$7i%wz^7NT_pU9X>Rw$&2Mg&GVLcHHsklGexe4L&JzB@fyd8j z?#u9VxSd)e2GvagU6%%7tk*agLLZ8JaoCf;wv{FUd_VN9vuS?A1jgL zxA$4ntfJR==iJM~noBC|{WM{o4Fv^1?_1ejU%9OQ^uFiV5JG*Bd%4Z%MY?>Han}YZ z4cu&2ru)!(icgpTwEqs8&|~22Gi1kVmV1S|H1=upGY**!RQXCJmuItsUe9rNdIRn0 zGtcvdb(a*dg@vOpQxdQB*LSWPI>kl+?8{^@LxP2WZr5YFsOW73|nRt*~OHe#sY z-!6(+vvB@A$_J~}N)GsPoSM)_PMQL;O>@J;JBJikxnk)+pll72klFQvezRNwKn(DLRjuIQ4Hj2X1hg4jW;tni<#qHI1(Z^%nubo1sl$ zoxy*8kJC9331p4`JXx~mWv_5nRX8)}ai*L5MV*3dy9B5uTr39K1Kif@YF$n8(;nD; z6SSTcJiEfM!!9&k+Z-*h)FOxpc{1p5dugw#L?VT>CXC09q--|)NyEDAm*%4>h@`814yodeK(iDZZ=u2Z& zsik5G+~8O~`}K4W$pAOa&QlUnc-(i`nFnk@mI3LHM|P96iwGxIBErXzwi^TezlTBX8B%#nQFIU9dJ!1fn8G0ePzs9+cWJU4l z#v^mB)+=Xk4n}eRTgd~@f8BdTi2z5&>(6VAhvZ!cE{fqIV}{vd>5AYFCXtvY*$O!9|~ z-ULoH8<$~v$n7a4IyxP{Ke21}WBXgcGh5$aW##oZAT{yCQl|T9X!R!%toPO9JADj* zdRU$kNWWJ9n(9R~ha;f7pzxf~p17b%h~AdF(`H#JXvL|k51U!m0wW)rV zx+x9W1TgpkZ-rPL3kY)5az zKLlN+@s&@^97TvtZS}i~AvKJRW1s;eiuo+m0~IW$ss(N!WcuY85ICxgzw6~c%Bj>X zCR9FBl3!dQ;UHUz*mqbFEyb z8(T>67yum7@ob3R11FWJNw39i~e@eoz zYv5r{i1A| z66E$G*9_f&#m!*@;+N6j5ewBQfDvf1YV}TXYHEW8Z>TnI2*eQ z^=^zgw#r>0iKe*O-8mviX38{GtZ$og-eP~f0=3^LgtdC08)@yDy>M(%ks{!kpFa3{ z7sZA8hj9-PFMb9|x}wlYbh&F(fov^G%n+TN_ZZ0JxRma`zwnQ_$A};GNjR{3aM6v?MMf z{1`TECu4KGp1C#i$qy~Jg-}Bo#R9FE)wftx575roF=5-fC-mX*`OihsVz(DS27k=+ zSaLc?hL4FRX1c`!?%~xfrn@&hGGD!^crT%YpoT6Qp;fCia>uiNYFNj{`KXWg!(`ZG z0U80CV)LNuOy{=?z*&P8>?V1698AhqWIg&3-fiJ_~2nih|m`xpLD1o9(6H zcQY~(#P;;LJ5$we!D#8(?!oH#7ORy^54b-)_jglNY%R(u^v(_+ZA4qiVjEQ(Wi`jwQX43NU6%`=eO;8VW_{2T zH0iUea^j>X=DgT?Ana@31h$JP4r5hEhJ`7@Tv@toXM!$SE1iT|mKRk#dI2enhsMQ6 zr2B5C6Q(IiMmS~Pz0Rn%9_W^QB^Jw$x$J^W@%I9(noc3NtO`tF5SMcyPv&KGc5zz# zw`(0D&+c4WmM>!a{ApG!e5RzPIRlDmdgCRjIE(qFs=xdzqrtAc;gw$qpNheooQGB- z_e^VPpKPSGiir5S^hr<@I{8%3Ub^%OvG5* z5kwg_U$qynw0{SNJ3{O;_P^9!t{H7gU(lwp>0Fk_2 z^r76G5aNvDiyBbrKQ?3c0T5Gh%y@I|S0*=0!r{TIl$LH9H+zI&z@V$ME*=`<;N_Fh z)AFHONfx(p?#h`K#zk(rMVp7T}mu(kKx4kx7eTPE=F*t?)z4twsbl1lkoO!NpEiQP_VP6hS}=P zrC)9nO-fzF+Qgo@o4buEvBFD`&~YyjN@NB!Or?M!o71ry!p zfb=D#!i~$<;sIkSoOEM6uFHN0jQ*%B*Pu*Vnm|*TK7Yu<6FZmUGCf7QLL!u>0h0Bm zJ+O+ajbVz6zE`XKI9_EJu2n;>ioB4^_eG!myhOlaJl~a8jg^`IZ)4t9n!2d;)LnYX zH4O8SGDD5jS2UX5LEjPev>Z;k!Z(n{cDzDMNxiW<1G4*XXF;=)dur*wd z@k%YF?#lNGUy_xQYciG^CW>aEZ?ftm3e}$JONx!y-#j}?RG~P4Mb0)NiuMf#ni`)a z?j`JhuS7*&3$gxp^4WJ56Y2J1$cv`~={}N)gpit7IptNaytk3=@Q!0J|91o;HnY$Q zXZNKbn(d;Y<`QEJs=TQ#jN1cZl0gz!#p3c%|EgL1*@+-~Ix*IFff(0LB#ep_BkvCu8JU{0II>x51tiJsCNcr$w!Jd_Nm`C;1 zff_0hxOA2p{#oZ~T_|f|KqYh7+gcOh0s6}zI;x~C72XdzyB}TFnCl?(Ktf7=X~uQm z9c4)ZqiRpYiT<&S=Cah%O!IU_U)HM?mIx!MihQcp`)g%oo5a%53!W2qeuvFXdm9a% zaybuDhkvEX{bGcst2@dD z@9>&oZ@2kbTIYs0)5TAgPJj7tEe&Tcwrcm7F{*e!Z0_ljFL>o2hJO(puM^p$=ZOPj zNzs`N30?-yqI1-}?D#orh%mjGWX<=p)2J}mT-gn)s`J#bV4Zul4#=Na)50RPqL_y} zK9s$ndb`{^kLQ^XHP>Rehp-i3Vv$!L{bD90`km5XFAs}Rin&72+JX9j%(A z?9I~tJ2Q0)jfqn_dnmel5>rCmv&pZvEAO`ylTyBq8YOe~v2!_$YD@Ua?cbjEJ@`ZM zpREK|HzTBGNJ6BD@FuhwCvYb;tI-pmek>hc{2cuj!~nNKUL-gyzIAo}IGH{eHjotKn`Q_ddJ*Z%A(~@j`VPaz7dzLtZ9;E=9H}7hZnP-0+o2ufqBscwPbP*f-z2^}GT(qvMoa=rh$tx4yTN|}+ zu?ynn{&>$|`-t{@1r5(tP6vOyA=))32Oah)NI2I=%qeebW{IK3;gz~-u=T|;9+Q&b z^C-DT^Kgulr}T7v#gPu71XlVryBYq&`HT{dw(xV(RE?zlm=81&HO=@pR}G!QH{kTS zT=t~u2m}^iLEJ^dTWBykaeVj`^>ImsSoC25}^>P9Mh^GRO6JvMO2{ovJ6R*upp8E)8$#u&UHwOdfRn9wZmsC9BUVWc@w zbJq)6e>&|`$57)_Ggk8v=j75omY8>9YnSE&$p4!yh>|ccFETz|RV33zUWP79)SwefXcmZnJ}xC4dt$1KqsSnzqT^Rx zqwn;n+hR1gjt|<-5fLL^{{f$kdPz#$(l!*IgKlxvTdV|uhC_kR-EpZ5nWA^ee<)92 zB}#gwu}Eh`5e;EE+9?@0O|j+C4M%I=9d^YzCW{5VzE~O@?JquqlJ6PpA2!judMzA<9mNvl9Fusyy4K>RsZoa+x}RYIIte#qw1`cVmN0yg34NbnUJ{&^ zDa?JR(cARQ7KrhjuW6d!Qc%X^MVg2S`vx7oib-}q54JyugDi)%61bPo@%?dpey zOiUr9XCTI$W7A(`w_6=dN&RDlD)YI_GjX2vN7oE6wCqfwf0iGPzWE2&xP6PY zBd)XNNXfhRJ+^{(O(fviQ+%J_V#I{2SKKmkP0B=(diXMrq26-=<5^3M$^?JZC-@ne zYM4a*wQ<4usWo@h4NcroEQrDQ(wUHZoybI!vHYp3>Br0Al+603<$TH~N8#nGIB18rw#cP;OFJH~F{-al@S3@}#5IIv z8>o9gOt^c`kI+N;mPWS4%;$dm1pw$u7rtMplf>cw7`9rP zm}s^{rj|-Cq|e-rZs>g-h!x#uo<9JI3`CHk)XPYqe3Pb3Atr*L*HF)6Jh-{+1t|&a zXYk_dN6BYkG6#^*V*Yhr9Jh0g5Eg1on9HM9GKRU=guEW7$!?&b+5l*6I{@Ot*))0e zT@;YmnG20+79hTznVMR}!b=Rg=_0T_YroQh3u5Sa^spQ9X;&QOCsjH8f!8Z)n0rQF zWQb#M{w*^75Xh+y9Ip27kgm#BI6>9SEeHs|rSQ6x0OeuBfgCOy5r$H)Gy(^Ev>lU+ zD;c$p021#G+1JA+NVK|*IYljN#w3?t9FDeO71xZ%6|?J*h4u4oKhrsCWVvVfbVCY0 zd+;DqwBW6)#X%)}X#hmcSLPc)ImnXcF@s&K@qsl2;#J>*O5G9j61rmG)zwYaPcQib zdUR&_{?gEzI?01F>V`Ew&17ckR1jo&Dyvc|WBqAT;MAiwO!5n1FTHMyw5s}KE2Y(9 zRR+%mC`MiU0Y;4ro>n`&i*u$ID zKtVw)7-Ib`vT;EWK_npgF6g(@GN^2S1x*@jTjfvcwmckiBm!lrZatcJaxSlAqAU9P z9_4{7LtBY%1IEMt&4bs1{a$Mon|@w|7qSxzYO1Pr>CK_j2Tp63s5qp%`aXLYcCAO{ zAh#!#YfUrbq4-Fh7Lf{cSox|ZXTAepfMEHsq0MUc%tmfiP5$!)^HjT7yK+Yq z4`%u3w6f)?W`YfVc}szTN_-^M;~+SO-4Zz}dOv&u%L zzTBqKQ8KvK9aiWHcn@3J!RmFdiwj4ymE8)F^?iP%$Q96>)2yt5?+iK6Jh%3UD83{KRhx!R)90z~#6m7@= zqc*2@O>sanRIP4w+`xb-#!o;n74&~ahcw;P-i}{4bTCO|RSEiy%n@m(Decf&@v~GV zv_79NEM(>qG7t}?A731dy zgSpjvy<>UW2NQ=7zCA4KPXEK)eU0uA@+qZ|46*o=x zRENzRz!b*23>$Q~s}<@|DeY25I?l*&!X&7#jWa#FNrrN7d&et8`zCsLybtQwCipK= zczdC-yb&_AGqAo6@vG~k%?4krw_C#F2-I@PKe2{dUEGR46!$98Ma_R$A?KArj)U4u z(>{Ov%8RSlU2ziuJB~clYgLy?AxC1YDer1c>cx(*eT=}tFWDr@yE+Ri!Jv?V5c0;d z64a1EqwE`lcJ-L%D|j|a%jdK>D_SZ{l^At-$a-1UC~sn7V|n_iy}nt7t4Il7(+=u{ zn4ypedJh065#hO>Z*J>c0I`<&3bh!= zb~?eQ#mlb8evjAd;`prH<`z_YPHP}@%&q1jas^>}p!%G^Q#D*{zQ**t&1Wl&&CQ#d zE;ub+Nl$NgRs*p;2xuaNybrK1FMfe_s7XBFm)?aJX)=AIuoFDDsv>$8VE z5*Z{pe9H<8`{^FpQWt!#{A_+{^K$`mGS6(uR~1ESE;|@rE;mnwPe6f*c+Jrox>i4X zs6+n2)_Uiqxo=_R(#$@<sO z@4zI@UWzrt2{bLSVAZN%#3=`4b4#kc`@OVlek4T>Y)TwGSye3L6ADy=bDE8j4xkOt zDTRBH_Rw;EUTS#>Df0|oJ6FmJ*+;}E7#877>gAA!uW0k0GFuG*yT8NLtw@bLF*4Y+31 zi7`x_F$m4wp|mdDZDu$DIcbH)Q}#Wo0!l&Vcm2-FS_W8^U8~|~(n2dO51y8f8U*Sv zNZu!8PSh+;*Z^(f?4YGzcFSXScWo*_du2eN4y(P-^t~fC=Z`Jb9g7u-y6J6u!}k^z zq~ex-j9QhGv|Q{OS-$wRN8?PeJwdc8o5y+*Te2YnIr4zIo_u(HUy)ZU*oNLkgJ^HE26rF_-cQuJRa{AZ&LjXYYeET$& zc#-<^=KMs*{U(Y&CnLH{mk$8jGVPjI2lEGwyX$+&w1gEWb`r}d6c;GyyG;6mW&W&~ z=U!*^MN0AZW#9I$R;>PP6_y*$b>SMX+v`3_j)jxkvvIHKi7~mfNr&-i&VP>|UP)!u znqk&Eo{fOBTN^OrYE|}n*SL~3>%KnX5;xy|%y6tn>x9t8q|#KEx$`-Y(kEH1nhv|Mw$35D2!{k`LND< zM$J4S|Ncn2B~MPB&+QHF2W1Vu%hMefDTElsK7=5IXn-TB+SM^eWM%(G`OqH=6aVZ- zs8MkQO@!)~i9i6-7nd{NukX1$k9+*|nlhqd)M$;HUP-gt9VfPRnTJO7+e$Br4I1mb zbwZm|5MzjdChpDEx-GS1dtR*)Hp-hXJIq>@J@d-;J1TojzhyYCZR1h@pR`-hq z^HXOlsc(VEl{7~yYckxF;~xDQ5SgcT^&%dA!J!>peIch-UtfAcbKulyK4hcaE#PQ( zL+6vgCx{ti1}RA?83G8tdgU^S-prs^?gQA9i#D)HeJPhs##hDQ>rWioCzr~Y*UJ)>L{p(qEtOi+SMNod7v=4!#ly6 zw?ESHh+p4i6-SK=`CWax`+5w9#(|_o3GDH;k z=NFqk_UBf^D__!XuZl1C6r60x1IXV0YvMK1Yd|b7A39==BJj5kE2^2 z1!t_Gp`KtWa5G)W0tlCV}V@Z`~ z_lp3@hs{}{=Ic}5L3dh#r?kj9(eXg`f%oZ)UC!ASglS~g3NA&)n&KJZr&XtNI-jqUt|weY2~eva5L4U z`ID7~R~h{&wk1=iWaopw!18=`(0`oOtn|gq$~X6^5@Vx2^RLU6)IsZ^xQ~*Zm#H8T z`_OBn?}c0}oV_(JHNHZ`hs7dIwmETEjd-iipotip@hG;Yz*eb3rf1(FrSfpPW&P^d zfUzboR+LG9hDIN`x|}`w#I!rZqEPKFavjqszMGQRx)ytS@>Y7eytZ|wpyfjnXO`=? z_CmQ)golcQGF#Ln=eRvD?0@o<`G+w6~2x<3=jL6e0?{_ z5uvUzC!J=wW=G3G3d~segh##fd#iU=j`U<|7+OykYsMiGzC|roRA7Vj@<&?V#!GRt z?c8mTUe%^-CA|;5)k6{-DK~A{^|RNjG&4tFex`DVPJRJAe}yMLGYsY*BnE(Ucx2a{ zfLX#mC6l zGE%|<4(U2lwAVrtm^HQw_BK%-t+;7uS7y+5L4K&{bI9E}kz^kB^T# ze`>X_8BCuri!xmVZEusIvc*nhrb`Nuclgorw{vX8$bL`c;V8so@fa}}q?M|CG%@?; zil&>Vy{e&}Pvr}u@j)d0?FYGns;X&UpVXYgE6Y+cw2iQP*13YOlLLOivwzzsPd^av zjiK*s3PpeB-(IMzq=h7c-18~(=3|HiZJ^P++A-%P`?u*eO-*yxR8Kqc{wyfr4#L+r zGz2&t(0U-izhT1@TE_Typ$WLm2H%iFarX(s|Lr~hg@(X?KSsI%K=V9%!h!r(a%KPX z1_nO?v4QZ6hZONYCij0{2ynR1aRDO#p|I@hf4&s=4x@|M{Iuu(-@d#5d;3RX0NT*1 z5QX;7mm;KsRHtw01N*-p`@itQzV87MLxW|E@-JnDe!(090XJV7KwkI>{e$rT*=2u0 zcKH7~wK7>x0WkH`&7a`n-_P3qN%g#%uJLtFC z*YaJ_}cvsENpCWg*v=EoU>D6E=za@aWuSN$zA*D4f zz`>Y1y)&7>M;QQd+-36Pt|o(4I5z9)<9HUH7QjbwINh#`U*7)xa$u=N=3{w8bL!# zEZdAot6GZWkG}!0&_p*yyV@#9iEly5y{X{Rh?VsL;N2$>p!+FZX^81ruPc5ni$3F z6LWJZPO@}QJE;!r%!z2xpLoE{&7ni8Hs6PHq*Eq@3*!jhQ z-Fa=)hgCxOa;hI-n}U272$!3hU@|~UGa^JGNFB>82?6pKM3G-!4~rwx6OV@((X^3= zhpdv8Rp98oTED&WX<(U_qZVKyaValL_7ma+>m|hqNcz|S;Hw4X$Lw3suMx)6vP;n39hD--;Lq&Q~W)&p71*c&Rk5u zQzA~_{e&q#HN&O)2h+;ZfeaqrP+L2xaOvU#+3f1sLktgK5XI!Y+J&)K}QN)ah6?~$`ZTtnNKRsh6r26~TseO_Ps7D**NATDrdSYf}~rz0Xx_`8X?kAxNetxO#)mEU86 z5Y)88OSNI)2`pqr*Kv|W8(N)Y7S`4r!34fS0?0slg`=F9kk`4$4J4Fy-2M9pfP~dR zR+xa1T9Jmu9cC-ZvoryS^tm1O(tM9+?S)LsP?b`Mg|dLWa&D1TwzAM?P{NMsM|$ud z`{LKp3q_!TVZR;+8eGj=6E*~ZVs^f5E@&M{*LeW_;!$YFFsEH#!w3KbX*7pWdrY0L50TAlzy|6 zR46#4*jHCq#cagyC~^x_nkmd*k^dwYKgwYd`MphH$pwUM!&>`QBgN(CjLfBHQJI5= zzAsz=ocfNRxCNzo+1U;1??vy+`tQUd44-CGD%LW4E{2$mWJ@y5-W_y;SM7o?>D&Bq zp88>qk8_kn6A|r*>P{aXO2xHg7(8^jbPlVcY_tK#nl{=Rm+d^^=}seQx+D-&`1Q^G zkJXHT;;WcNu19A(TdOzb*7cr6fgcbO*W$c4N1cgkR)@2Ipw5KoGBHJA+u)#_OJi5FQ2dB{1{SUp(R|oP4;W62Uh+>zDV`$*Sad2|{mv)&#zM`af#q-7Ui%s2 znQpLvCQ1pY4PSGx%dmna9f(}&1OW%VG3eR7d$VGL{=e~?Foj;lr+>`epQ*LWx+DOg zGi~FtO!9KQIRLPqo%VnJ5cAtI%kNIsKi#ik;C{uw9+yD>!hwYTyAk}?+j!y(_tl#X z(Zixg@SkoCejz*>xbE4mFOh${L+|eFn*msei$4nFrGCGg|GYLEL^{-+?UMhC$7KZW zb%2_e68=A5OGyRlW3O`Ohon<1;W@!i55~|0ZDntwo5uTe(=Me)pF6`iL*of#DDl{GXNmT3n7lRD{madF;$1C^Tbx7j^TBo0Bqp{q0ovpZ-n~s%U zi!SB~oX`RVayXZ{wCkxf&CaF#_?MMOvkpTEpmtNY%-npI$^Fj2Mh=MWKsmQlL*ATIR2l2QqcXi|_c>b-94lSY1Vb zEQqb7=684IQ;2_NhfeUD04bCKAohXcaCus+f`|6>pKsd!@msngT5L>A!rd9AxcU=X zdOHC6#C>;kbc_dZg~TwQD>p!Pe6u!~Vwvi;W^t57a{Zh+rWOz`Dgf*G&Fsm^$r`=> zxo(Zm)tTL$p0M?CGUw7DRS1kJ6XmAZiigX@ARct9rZH&(CX`!GuJD-U-6%R z^G?E_`o7A#Q##Jqy(}Gxu?94awJL~SNNh!M@psS*#Ap1fmEt8Y+kk`&;v^JL*%{m*ZgW^dOR6~X`Nej^2RX@<)2O8 zO7OQp9nt7LS7P^##WxrrN(;8R1SDmfcp%W*Fc%#BsKRRMg-@{lKi^8|r-1LP3{nP0 zMvgmGY|Y)>-8-KbwfGz0tciqiaf#15iJ_L=|Hl*IK2kimPfzP3U?VP(ck$P|N7-XDi#SWf7GJ; z9v&XX(rA;=ohF>Q_V+Bo@BbHbDT4gu(L=)*IR=%Kb_5AJm^cp`{@L*2+QauD2nUtu zLO+{1bOd(rd`G8{%36F6MZfUdq(d1+*&AKNf$J1X_s(XG5+hI+?2CWoVHsN8?;0x_ zd&ux{yN+-6D?)3{_QFX+mrlL)z`6EmP2QVwaj}1m=`UwYIPzP%0-CdnpaPnjiVAg6 zU|(PGM%_=2o1dUD`E@QWBZ@jZLQH7u{_x|eKZGJM!gp<}oA_gAR=Lb)v@^DRn;Rt@ z2vdt~3K{t9=i+sBPa#a)iIe&7&Q1t_F9|}*eYZ%WIevomRjgDxMyah`2DBV2E7eP@ z#cQsC<~P?>E-n3IX7~NGPXubxzC!6e$f1>qtyR^VrKKFX)Qy@rZpcOzG@ZH8$rsQ@ z;{o1NwfPZ48Pvo!4TbIBX02XOqS=VW8Je8e7v<%3S5^`_4}%UH@c_UQK+WNEbG32c zb$KdUb1kOuN<*D%p5*g60JDVb8o$C7Z54~a&@hX6<1=YBz$}A{-`DCopyXO$MVHkV z{Cig*od0gXDy%L$)7XqVA5p@ZD!hmiM7c-YX9RA!ndtBFE-t@AqlhV!cZRXLq))V(5fNK~vM>tz|KD_ob5d z<+r7G#~Z_9ZpeMW!oF{?5+=2TQx+#UI;RyL1;O!4hn?x*+b(=GpQBLLJlhkT#G~Ku z5r1@Cb^y1-yPj#eCOG14IkJ@j40KJ9lDZ6Gj%Gm7_qjGd2$=J>wJmi!8COp>de;$% zZZ=o%w97284@Nowl)six8@y;~(tK_(GuBPK&SFZhYzFECZ@fO%7 zX|^n8$LU|MQLu`}%-kC}?iZz)NEH-EdPR>rO4%&DXwr4gXgxjZ(h;y8m}~C4miX4= zQ%7{oQ5bN$rfICDNzESk@fW(tf7&@ADR_iW?-g<)W`|l2e!#*1 z9?Pg5T7($Y`(Kf!dQs$l6o~x~#DZTlgL)4qNFlsfxV;g?ogg_@Ib(bVH(KPojZ?h( zvnSH1S26fJ{^eZsfVRbeV}sa64o7h2`F%5GecJ z?gMJJMR)7NMNpI>FHxJB)@px%)eBNc$V@bIbGo9DS^HLFpV`-HiY>nfBVKIp|Iz7K zIjHrh#BDJCWA1>x(`oJI+(mE-o&n=y%pNC8tFt(@u*vx-(t82F%~d_51QnVcB|x+_ zy00uNnBSUh6G%H}4UH|UsAwl@aH6m1>;bFP7$kJeD>=#fT5%h7<|ZKVVb2@>cLI~VAl=@N35h>%koQHcG*D!Yq3{@<6GkN#f~^4R-l`d{kn5B_xR z2I7Zz?_dRDJ|Og68>5AKpy9E=6+Dm>575;v3CwA199fmyH4RY}V406MjX*~1OzcEF zr(@)xlE2EN)hru~k0gAvV?{`X!X_)ZBLbB^4rg^H8g?B{^yhKk7Q4bb{q1CYpdv+$ zk0jtK!QotFSFR9-`l$n9K$|<6n>l~EcRtOO6k^edvPttt4Og!Xeqem5C=n$oB%qvP z#xsoZYcUz9bgLWM5Bv&_HcOn*CIQ;U(_HP5E3k=+`M{U5P zOuW6&zYPH?^JY9Qn`91+U=4;8;3e5gav$E-z=(g?5t*m%iNA+OCTF=fKpHfV?4b0t z`aNTRwN?LwMzy*e?OfdKQ z6Y6mI`v`v|ba>ysedc<(br%iY#%UQRDE(*+IV`m)sZ8 z4<8Qq+hNj{P;9sLIa9oLr1Oij?lji(^Y6BLaj_EC>I4%bE<`b*){#cOYR@H{TFL zGD#+dQxiDB)~7r373y*+eNRm5)c5%Ei;G#$+dOK#_8)}xu@rl>2Rv2v-S^m?(>&o; z)MWe0G8J#$|FybW>ltIRtpG8v7ITC1URA@H6da|{UwwYaBAYY`P0H>Ogv_BJ4Q!S6 zpP34Cdz?fP9|=raaCcTs>#0ZVPZWQ#e2*R9G>2Qo%}4^YKTQ@h-L|S`l@pVgd#0TT zF!b{33#33=_|0bzpX)CF?n~}_dasVw29&UByjEM~6YJhR>6nznr0E%_S6|H&yfyWR znBFZhbklm+I@9;WGK`Z$_sv4urtMuzH+ZmKo0?V%3+YWe$};RXdWJ*M`Pot|TzVI# zDn4l~lqM~u^1Cn{8&xnrBBI5lypKWppC8{!%ygl6=Z?mE$nX^59P@rvY@)#k@bo6( z74H~z9ER$jn)_**Bl*WM*Yp6UOirdzs7xYXYurmN%`m?dCA+-*{5s)zKdYXhp*U2~ zj!;LLY#>YmZv}tKRnAoO!|~>fxE7OsY}OXzYIQqZ`gOa{g)LpteDq8WZ;8dGfPk_< z%jpHl$_~}un>S;HshWu|4U>@zFS-S965ujuGIkrjem!WsTK2ivL-_VRX*i0$ExoPz z`IBH7zrR|2f57v^2@MT45tfRq5$lgd{Ep7cqs zFkz?@zYWcQ&f=f{(_;mPg~WDw!{Ra*E#&kn7R$5edBl=$N}8q)Cgiki(5S`|LaaMj zBJ2L;jLW-CBP@A#MnzYb?BO*zmlJ3~19Q$kD_NLhSC;vhw~pl7=6!9SP01iujc%a- z<$>DqzqA0NtzAOTss8#IA{Y)7mx{@KcSQ|Ej1a$s-WxGCk&Rn@0I}iqXly^>4VlZ4 z#&0gyYY#0zTr|<$vuXeMo#@brOD6Hr|4)wT#v zHkd*Co$yv+1nHLdaCPqvG6@A7MXIeAG8Q@g0;UOnv}l-pPbLGThzRcw*E03uVPpNV zpfq?9czQ7inVzP@<1qFMY8UV0IaPyfHs{>+2N0qgEtvQvB z=}$;l2M7bo0_2$VXiBxNy~oxBybeuIi}1{5K7LB#xUI=9D1ZGH`qm_V4W?t9wLMp} zu)@710tr}JA_uZ5^(DwJWgf*y9(78OxRxk#+DwxpDH3$d4um6&qzYIEM;|j=l%fK^ z4X#vGL%3a&+jXOCAdOf1(0#SHWX^*hn$E3pi-OOt0A12h_E89n{EFV!RA2u3JS;pi zlDzJVsero-v^93}`Cn<=IKNl^O$8uwOwc#w@l!+c&gko56IiMzFBmh3dC>XYtSMdi9Q-n_OnziB=|A1*WkkFZYTB?U=V;4HT+2`TKV zbv7@b$)EoN4T-Gh7zzQ2na$^BCkiLFZMvxKM8xxcxXHI|I0E#mbQrbP!)@o~p67IK z&>fd7pkl%;+w_crO!{k<`riDZe~IWDYiKnQ5q#Se-4lVM44O~fS^Q5+NAqN30*D*+ zn~$1Eq@<+_9pXx1K>sKD$3aUYi#jLwoF4ll$-mXyRbd{^ zOS=-nf+kMC1t)q5sFJ_(hFuCeNnj9=IyRk6$wR&BV1dQoo>pD%=|8t^I=4yZphy`Y zEohUHlA1oUgA=Ho6}P44L?i0oD?4j z&~G`^YIqrlGJNv3hVNqE?}b-aWlxaH?ef(_t(v=8a(dHp`YnCuyO&p<5N;E59nYSP z!@)(SA&>9RqqNIVENrOJS0?SsH_hmgK7~*#v(ftW5~FnZG8XU+b2zx z+sv%uy^x(|B0XyT_g$rU@G#|R(Jq08}V4Eb3_A$ zf8ZRHXC*%`7`u=se_FTEvDfv>D=hB3c|$#7mhthJ7wSzl2Bn)nxjI-Z_Yd6{+0w%M zf?4ek+C<%8O{?W^SG7(KrzzsbF5h+pF4768NmyDICckv+OEqh3A57t=iPP2!h0HVR zYFl*%tJ)uLupYT=>_>cgr^7Hz5Q=Z@4pWpQ{Xy>jq(cGf9HC}BQ*<~x3PzZ5nTj!179Mxg0N?JM-$Zd;c@oyGxZ|598+3j=pmI3Up*6< zb;1|hDfB)X>{d2=UG}`j?lIVR1{eADNADzZ?DdnKRCl;XU60S8pYs+%+R{eFEW8Et zmJDpMt!u0oXn`VO3@nx@?)nlHzf{)Mc4Az$ux3)RE*&;&F=$yj0p*51`dl&aQ@751 zsJS7py4U)|USz7T4*^+Z7^uMaJ#FHw1`T9o@Ck;XUQLW>xQGRii7gJxlsIdju#1DQ z=7J1f?4N+XSGVie>b=k-gtj9u7~>elbm+TBYe{35RiaRHJH?16gfA7u^1CP1PCi>I zD?VFeutXb_pono#d7C;1GoP*1I$v=hVS~FOxj8eRs7=8J-9}y*l?Ss`lJpo3*&MUa`C~c-n0o7N z^k(V2`z8N!YpDAR&q)QmeCvX|a_($vKX6e0mBTe9Ln_!3)_Vh(IWOvQVY37;c#;MP zksX=gyvl_L*PXSH6a0A%;!m*wonB{;Kv(Or@Zve^dmja z$P@fRF8+zIRFF*YDlRSlM<%E5TmvlOxcOq7xo1?~OCd3lsmB}J+W_s*0BH|VzCjyb z?s&%~Hqm<52TAm;2%oB)zT1gQxn?`R7OzLOB1k<@>3eR9(vqk$k+`i1A3_(dCU!qx z^0>as_q-TQV_Evid~*URcgQR0b%lKX^8BIiO4g77BaoAf8}ht}I`kvC=}`6*(NJ_{ zCKmJ_Ni8AcL+yz$?p|?VZYCAx5TWsB_3x4~iG3U|KPHHDtEWS!^5lcLHo0HVdCJ~3 zsg%`RxoJb=v8@0-Hr-Ymn+@=16nj_laVXi#1RD=>i*y^By%zG7QOdPN`IEIZHOXL% zdg*X_UY9rNwm(lS`jLi_=QS^*7x25QB2EQRLfm(@R4Ig1)l|N`bFFNr0__+FypcGO zq_?^iP=9s?yuLkK)&KI+?aMX$7$k=7z1KB!3l?%`p_3DILSoKZP+|doF zRh9t%tt8SaziDqIOoejc=j`0#H4=8-?YZ{Cr$=ca&b6xTR?WvBa7?$xdc|g)+~0j1 zpXV{@sv?{6Fr3lJY(|13eH~m~H5UO|aA0GnQB=Gnb}A#U5p+HxTdiv8u}vjy^?;jP)irih7^~FnTcC;n}wdkwN%7m3P`pV1= zV1~_)>_$55b}S)N*I)G(O(8(Me|d8S3vuUQAM0|Nz_uSa@1Nq;baf3~M4KiGSvJ6r zC!}vO15#sne z(4Fvv^35E68gs%Cylgw7PTnpUxAa2Rp;s#o!a%gvdV^Rv=77cNB(19~Om84e$ zA%&5vX$|f?aI%)hZ8R%>xiRabL!;1mxV6Voxy86{*8F!auLK2fCM^8>vn5HN`x4w_ z@gX#cyj-8#dj9r9R9TpckxoO!6yC6?Xciwz2A7SFu1Xg@otR|CM-6|M@xhda%+pr- zr>uh7AV2^*`dO#kf`ojo9iAc^Qh~v}iMqG@`@>nR^1v^xth9KeSVY39(*Ehi9fe|%T5tb3h(so_oiH%{3GA>0$zY3^hrcxylg6F zN(m0cv+kNELY0tY2N$h{yV#|>R&9^S|44WMA0f!ck!JByI){G!`_;9r*LHK3|!YM z@>`@0GB(tk7aBYoOtT$fDIhn^=?(Gh!7Os|#&Iv;dF%kYj-iJc1+3Hb5bVCP*!f+PjTib&}5~yAWZ+9JP zs-K2$4+8)q9-@_3b8W7ZM@GQrz~YqE8%#>B4Ld@C^Gp9Wt&1Du19?XD7PvPOSJF z#qFB4C;VDZY&$56HcA2ml!-mI4fV~RGKC<;9Xw^CYmN&`LO%3?+Vlvmb03VHPP^db zK}w!XKC6QahDOu2qi)tmv~yf#W4}qcY&hhH-$vZkFM`g>24f&uPbxx9H)z7HRS890 z6(>_>Hr3Yl?BsIv0%F!dQL+|Yi@DH1zi2*vNZ_dQYNK25$d7TC735KE76e^;&?wM& zX5`E(YfhhZcslIqb?|iJrDt^xU6HJ4B_+RzI<%UtTI z58AKXdV0DqjEZabZgT{?d?h7^C|F06J6HN)8<3Fer$sq=`C6DCl*#1)94xmKa{xaP ztzkCf9WU0muNhS*!Ltg(H2NR9yg#IHWwmPFMizrTd0t$z5HW5!frn48Ruqd!+$f)V z_S3lh_8S?>GQOtyh;JWE+aK;@6*!sM@OQD-7;Kp;xyD{y73*C$(=PmR|9bUY-b<&Q zAS@bH;0Ed8YvB*Ke~}yn=|a+Lc=E5UBR)iWzTh9oaN^{jDI}AJMFaGdY%40dd2g3$ zslmMIcG?8EqqlW^QkG}npht6eLp8cZ*F}B$u?h)2mOU0lZ4ZAEl=QO~Wkgs4or*jz ztx+xmomvdq15h0HK3@r7`3k)KsZNL*^D=xR@)2*2G zI{uz66{r)g+q`oe+2QT($g0_n7*Y<&uc&p0Xrq|@r|Nh*e@hR&JNHH4dx$I6_ z``5)Ic1?b@zLcvlAI^NZHMO?G{y7|wAQ!xJ(TGP&4H_I)T856Au8gW-4p&za*v$|I*O}#T|iPr2>}KFxiOuXC2oIG zx$$~xc4>vCaBLe$LNhkGyZPkAb2ubkHMHE+e~c-T;jjqUU-q4Z}Dy#x#Q_Ph8&lbn3%PN5{J2t z^MkBJj`&cjy-X84v;Jp)3@39Ozn_A)kcIfFP0+;PRRXv1+c7d+1#zC380;?IO?g|U zpy@DFXN9Krr`;dH>g#`n;(^q<#jJOx5^(=wu|1&AT>q?cmxNRm8~Zcca*OWe4Z>87 zOUp6DQSj)B#^evnpDpzTukRv%8S<9Pb|6rz8=4k1Fg4@aJd9-BGYk?4TR)LG+(zIN z+##0o(>!aus)j3rDKkWvCzB(Y8Rn=&Jef<5%x)k7o{wlL55V_nnqfbd_iK|t;mvlJ zack%(&0P+{)`;^K>pfXtsAY6CR*16Dr=%}oDIn|QxN?4WfLU(#-973E)$=DhMPnCT z*S02`$!XE1V#8$tY|(+lM@?)KKNnJ>7gICu&GptNx28tK3%&1S6<5n)j?Z0nK|BTp z6Nh;06pq(*zUsRR<5$32cV>!6L z**A{99eai7n4?G^uSr>0!aR*9W@l{hLd#3);LWGm=~5RgifY%#j=X%;1sXR^2j5jF z)DkMMUGYDez}jY9J)l8HeGWM8%q#4=nd+@0gXy!L2PLArYf-G)GmZy}>;09{Wy@7a znBNHvXUiwaF95HRAUuuvd~47D^4y=ln(wa3TZfDO$}x)CtW+}yTm4xpO?Fk)gA25S z&kqz=t;fonSA9OC!q_udM6Bq-^4!XKKEIQ2pPlS~93B@KdXx9~#zJJaWNh75`uZGY z%OwkW7R~ruj!Axn?|9qfroFv8ymwouqg8>G##!6tn!|kWOx7x;t7`Ddc?-sA>f-Py z@}c3!SB~fkhq2p*b%z9x6+=77R>gtYFaOC}Jtpy4sQbK$-)P%xx@IUKv%18QL62O8VI}vy< zRMrdGZu)NTC@<_M2>q0VnFN#3G9a>Vp55~l5mu6~9E}yKt-Y}t;deT^HP1(!CW^fb zT)FW23mJbEHRIvo30mzxW!K5t(ktaGxs$;w`Zt{+utb$vNIrz@UrW6mE6Yh8FkOCV z#<|q{x{_o``|BRx9dSU2{PHar-qqDW++Fq4ZGucFT#BXUlkt0gcq)WzXQ;4-Q++Av zl_(P`o0v?K*a?#A55v4qYu&GFrn4*r!~8(s6plqFS)H)cFy2`k0S^Xrx+G(z8R=td^5U4S=zalCw9y*v+h&*!RNDS zWxlS}U$zUc!ZVeWls+1p{=^=HNoTq)k4>G|CF?jP{7of>qgNc)i9`3Wr^L+On-&t_MJPgALnS2Gt``e>QY^sDAC7eX|GjORKC_9Bpju1u{_QC7p+dgCMPB(he7I#Q8?dXFB>vy`)C^ zshIp1I-iK^__NJ8O)OjqE!Yn3)Q-LtYL?Z-PAy*X^lM)cDMCjxKsf;E$6-JL6+3kHaaJ)kx6XbngSd1UlfXEmq1T&_@vuY>325j=E|10 zc2lsHBe%b5)AZWzXd>N*wpnY%aQu&P(Q#Hbh9_0+37Gg5(+N5L%-3M2zxhztkXx^s zis{2m&D2AGw&=g^JpSX1XeR4PqzbB)IHGCz`kxzY^;+IkfZVdRLZ{(Tn&X7|%Qpbz9mUtb7cPENZtdABU zVoxD|n{0#gY8f9y?{3JXZZ<1eU=p3*?m{e^3EhoHqmrtfI0%O|Vw~fo`Txt1){;!D|{g z-m#-veuNF__WR7_%6=Nk^V9O2^J>`qgRJ1T>F3vEbys##H(e9t|9%+%XLx1Ba9=2? z5{BSEW)0-IaRrNPZ5QF&6vjn0cr{%6@Y?O3lIwo@Pa4^W6v->@)v(h(xU0&|QL6se z<%|ERhsX5cmZ-_JKaKlc_7=w?e3o=a=^`c}Na}UTB-=|&V&NgF1NYNaiDVrmHTu4&Zej6h{IB;Ti=A*!);Ik3l>cvj zCSHOO-h7dxuknApihqWW55Yd5Pxg1g{lEP_ogS>8;mGGA5q}5P`1d;mUrg|CU+}LP z`X3kn|BL@MAb%c;iFdLH1gxWLt!~2DozK5&)pu*M_Cr~jHs*-${?HBi`=W-sHY5=h z;S#F9XI`KkdyM_LJ?BCt_ioUVXzJWoHxUC}{;m(!E)`StMQcW!6G=r~-}nFgNuxxR%E>s_SZ)g z(iB8L0C>B5)DCI$i9#@G9Dom~XXX4>YB@Sd@iavsGWJ(u9Xj1Xgo1isfnI{wdadl8 z=`_uG%DPdvS6qn1?v7pa1g>@kz!^oOCk?HAF5}|+`(W@a#wzT8jwkC=M_P5q zKS%Wf*jC5khGb^v#sLBlLI+#QCMUFtCnRDZ-G0VZHGIDa$i#L*ju3&d*Kt7p+=6^3K;7BLcyQ3>(y(4-ROchEtO~8yo2Wh8S#fmW zq|z&Y_Rj0|HB|)#g-;_%F#o`}Z{M~vUu=7S^OY=K()|Qj?k&K^h!e0TUNcF1(P-{3ux}F8cWJL!pwI-w=Q{S5-XKk*I)stye=Aj2y~|5(PQD(EqJK; zvwNjpGgS(}vMAZ_aa<)7V3BlL=662QOctYjz-uZ^j%VI;BS-`=!i~g%@9+9(Irma0 zO_{76pIk?)3?CSfVcmJAdfeFw`a?>;+q+_a?3>Gezr&S!?;a{^qCg-e+lizgzmB44 zlFc77^`wa05J5EIKF$PS8dX)*!I(CwWLuI2Ela#?ueGPk;biCDt_Wer zx1coG#~j#lq7b@EDMP>eM}gWz+q5ZMjr%ER?^u}%wS+%{=obmHXrns}UH7m@)ULpG zqZq`^2E~I2L$UNLpFDX&=MVu@0Xr|wPsgiI5^%App?9CgNF5H(l|1cZ+oKW7V1QAs z<=-eBG|-)CkMsE$s|kn0b;k2t1PQtA2XcV@K}Vghw`3^t>-Uk;rB}B?=cb9aEPtb1 z{ea1?2x!9<#}5Z=r+@X`r-}gAT*zb`>Cxw9OPSPCU+~*cL zQ5@999t?|qz9U)0jWI-NwYgFz%IL3!pMCE%ANIUI0B@>;=fB8y_P+C$^;X)Z(V;~` z_D^LFZ(gQ9_bXzIabNsILTpLN7{q$>4P&2fBxygVv3hAfy^3)~@zuP2@Q4WAiji^O zEXzKZAQEcAk$BfIS$q1S#^)_jr6@3lF8vt$<4R3+(Q|4$kSWm3Zp2Oex(GK13}O44 zhV5oZL}seEvkrj+x`KJm2PZn}=g&u__udZ)^xop;;_AM=edt)hhmvR=r6G*=0$D|A z>HyG$d==n%=Hc57aRxZ7y+U{`C!lkEcc$9-R$SaiDmLco$`ThvD+KiC(it<)8DW0% zdlP-g60i-E?SSswtI6f*`^$A!HY+UBZlM54_t>HPy850+(?RD$tNH9tpN8MkzM9$a z+%(GKH5ka-OOz91Etc`c#Ge{yYkwG8&ptZ%jF?}@rY<}aF+V?jzkJ0fcb6gEwrs&B z!SJnIAZR9NNq@^6s&~l=Iq;ok1x7hmO{7gpoLaY}JzB__jI0U*y&_I^OiUb_s7Iwv zJe8znQ@c9xZB53flA|A%sBr73G%Kz=d&9}W(nQbJy#Qnn)QrX4NHd-}v`9-6Mp&#x z|B&T6DYF?KYCmVTz$LXdbRB#$ATVz3aW`vnW)CfK*)Wj}X9AW+q(4Txs8CPaP*Z##F*!8U12aeJ%@NnUiAYi)lHrB#{s+8`m&N32{z zYzHaInBEF`A1JD*xL>(UvNOK3YHMdKH@0UO9s5P0-9!+5y>M5o5^p7K!k&4`aMm|^ zdyFaN5E5P=brQk=K4L!a!V&z$eSOkuCDJ+YJ-+u#M`3rS4gxt z>oxZE(}>+rQy?W8m20 zHy~yY*S;;uuwIim4q?WDcDF{qGP%U)rozsUnXRtCblrxmP*|T|=vpu{9&ptYvR%Mc zAM$1T>9?E_VhQZIt+tmZUE@|=b*d&Q}n7KCZ_YQ!hG9>ckIlE94h< z>E&9CeNZysys0CLeal74F4rsqJKIr$VY{l~dJq?oBahr*_`fM7QXbepxsuIG-{GEger>T$yRoTSkC*_pKd;$Y@E47;T0`IhL(~r7|U~dnj)qu=;3G&4F#bA(ok2-Gl zxYO;DMb&yQ<+Tk~eDYq*&+~}(+PLd;@A9fD2k}n=R^KJp6l_FH6`(*3Pg;-EWc_F? zdd^+Peq7AN^%ddE$9*h@&dsB@9Sn0NpPUohM?eTzsk~_G@<8Y{=rIGvndP9VWAW^S z^GlmhWO~FJIQU!C(q*T}+9wVE|ny8|GWp>~Nw*`bP(yNp`m!w;J9rCjX?4m8@N%p)^` zpQxAHW?tIxj(9PAAir^HSM0gxMK%hP9P_JLGB4Ta6y1@^aVj5re886kyezw;fSdvhu~BmAI~57y8z?*=CT|v!wu;+a?&xifyY| z{U|+zwk>h}mB6`r6~m}D|AH=+5%NRSZg_f};mEhf`rT=Z#aM*2KW5)1Wmmcvj$RXf zx8z-N!28NAaaCwkPol=jFW+v`&ZLJlKuB%0tB>gu6|E{@uKb7RZHieCQm6|K+@ znKE-*#DS%Vv^DdyEpr0K>;DZBWlfqgvj2Y|@L-c0rQNm_Jyaa3O#vMU@y z#319N$mPQFt*EFyR2=%MEgAz9pB_IW6x-~zj8_Mzs}VaJql8Ou89``dE7$X(fq}$8 zTT`Oot7%Sr>!#Pfa(I&82T-kM|e}S zZ4)XiSpD?nY)zw`J*u!Z9lz#@0Act@vz@z^+>l= z9TimMP!ugPIf!inHCBsEc$3go%OC<%b@j+;nPz9<*Gp6dWUryKEG^N&!9w5rDbs9i zZPm_Gg>66998LZtPc`m26Pq%>ex9jB5-En=cRt;Sv@#VS7{a@W7ZjOaou!loQNqq5D zb5gWN!p6Q21#4JkfJxJj{oC;#SH=L@V4{QULSaWeRca&@8UHlk0Iuaeb0GX;-tkMd ztAv(mLB3vbrg5^e%6&o<$oI^NQBbK6ap~vCL6X?s34bB0?uXH%SXR4)_=HaAY2Oex za?K^Zpz9UpF#MfI*RDMj3B9^qBIP(8Bjmj(U4Zl36Fub6!fpI~Xw<8H68+Ln|}_LeNNe&Y`Fsy8l!cI-MeHTLD}X9#tyWjK0Zj zlUs$1+UlX>_%Tb0=r)Tn^Ze7Wr5`O}W~mvQ=8)P3Vh4{3c^^0LkIj2bz%o@@VCZA4 z8q5+cmj;>|@-i6n4-t^??K@3-lxR<%LJ5#9NGrAeXkPTDtU+zBx4NeT(WRtwkGSk- z9fME2YXP4=mqoDI=d#(I<1O9>V-sKMZNNVNDk;b2$t5;jSYXwH+JM zPTlGltmGff{ZV-;JW@05vxHJ`mvqkAI&zMwgV@|0BNq@Sd@Z^y`NNB$dXLb-`K(9G zY1Ts7tgq>B=VZ%}gYJyz(zq2=Y{iMapgc zskXk_$GNj+QxU6a^CCugcl`bph0#wTKp02FR}go8p-VU37c!`&$;uxf9OX)9HLKvF zl*r5b`eAbJ07;SvN)Gj@1((GA>Ei)I4<*^MeX274bc#~nKy{%$ZZ?ruOwyg>?lsEG zu7q6MnZeuZcO-vyp`$%2%+JR?td5$g(a|q|wH<#Ov{8=NI#Ps;cOjbdWucU|c8R7h zt;JD>`x%G&!RO2VEHVmTL)gZYF!W(o-7@zBs$NE3XhU^Le8(5>26{bO3mQo2UEW?*He5H72t zoSI5NC0pkf$o7i~boBI0apgY*%J7krJkoVC9#B}r`{s8ZeCzowW=z}ZW;I$$6Bs{3 zDq$Ln`j%bmm9@ij?4FxvTY)`mbG}^YBa1ad=*B>b|EV}wl~Pg!J4tx(S`7-xB&(7_ zxl?(gn<|nm?w3_n?R&pYYKIr;(Z*2uQbUqbzET^n+K;2ksGXEMDkp^KkmNNz`ldR_mS*DUvHFe7BBI}6Kry!{2d!>t7CFUZZ#X$Hc@UckEaPu=}Tm}NTDi9ULDFPtnm7GDoq zhTiRDKWn%-|3L7DGr8Vy&1lJ(`MTRFu>(h;Tnq+dz8C{iN;(HcZz;I_)bb4|3$JA8 zj8xm2bZ59@NsrtkZw7o1EoDYQc-fr+q`>WI2vU~V&K7}enADbg3D2WkSnhm( z9KjLOEXyjt6#v9*uyjwI#L$hhvF&gyfqkKEpQe0I+uzG#!G7=B{;$K>?MXLcLJ?_? zq$X9mCMYLa14|F$IwysgJA`oYmi1)r>8l(icys*S~x zt#sw%Xd6cr`Jr;^6Z=Ka2#Kw`9``fu$QsGhJJCtqH-%`X%%)4@XDDg8vk;1py_;a( z5`M}^yf;F1?~-Bb1o}bxyLs@I&eNRi>^}4}59g+J3x;Z|kLo0AIA%@cxW;@WKQ(70 zTY;BRR*PKH;ZF6G@N@@`Q;87IQ{&coc15!W3(lNR&-O^sU?(v8_TtI-7Egyvo*hOG zUOnrbE}2%IaxBg9%5Al${+!{%=A6+uPMN5r&G(Nq^TJO}LT&MELMnVSH>;VSK)wm+ z6H>zXzjYc6`zEN1t`G6o_2Ye^M!bKy^5?niLA{)85%zpLrdwor+wd{Er zeE1c9!p9v3`L^(HRQIVTaWs%aSGqT9SERlZCl=#Yup|Ss2}Jk3SI~lUbE*lOz!AlS2L6wJ-0Tdw3mC4}sbP>~Kt722;q*f_rsAgFYiQ}km+Wc^;zvhzlR-6^ zWLfntB+??hp;GZ2xX#29(F9}Z`&1{y86hA;LxJ10kqvF_YY z`PIgfbxU;68#NLL-;=v!ZILGB8MiDy64x<7?%ApMjKvlGh#o16^%VhI?ftjFyvG^j zIK78ZZWEnpBu#cd#DETgLz#<(zU!^IA+XJl(su*9D_M0~(5LG}Lz4V0SzD~dt1f5HJ6)(gS7xfYz`<`cSLm|m2(i?fP%(8> zvB^0`!}$QFBWY)2BQV9hL*G(>^{l_i{fe4=gFE)pp7?rQH=J)>_mttm07c8GaRQMm zo829?=szNv7h$ri1)Xc#%0_>WfU2pBfq~_NBU~o>q zE|*g`#dY@_BE+>|EQQm_6i%zU=6u36CJr~|ttFvNYI!~cVE{hEbGVI{eX~I_#9~^2BoIxU;y1)yYBTGJ)yEhsStHf5?j#4V)f<+GwL8Br zoCZw~67{usgC!-gClN~15!XkKxk@v&SF+J5(;neoV)=q8S|S(=M%vBZ?aUJ~PA>re zHmEQSa$|$E=kW(Wih|?5OhK3+71uIe^TIyRm%&>)V~5-b-{=usoea`ZYj)b^Lg}Ls zdyPvl(tKtqXl;+_YcFGf2T>{FWH~h4B2M~{vi$m1334L3VkL)ytU9{*B=X={^Q$Iv z$E``SFax)j-i!f zM&FFLr|5w5weQ5a##9+R46x>E;`i1(e(*NUMJkWVSZMH~2g4MJN-;&nPi=YFA3miGM0gVL8iC*qsuW@(I!0Z-#-%I<(d4aUew2 zig@$q{wou%pau)-le?^Sx&o=s8gHen*pKznrZnDYM$!r#4ZSRf)Pf1Kp{>%f6rA|O z@$|y&duKq7r24R5p!pccVq8YAX$|-lMT=J7#hpJtf|(9x$Po(W4`DU)9Dvyre*`u9 z;L3+RsETLbKy3CL8>q~DtG@lZQpo~VbH7B6#pQh|H_Y(6r)RB8It*r|vwq%`4$nWm zwl@!z1XAi|C~4HK78W)uuDQZ{3a2w(dKAAj!b4vxo-SrpX;9X%0IAAfr95cVF{)hz z^h?g^&05Ivt*^%+4h5QWfE;?Ridc&WW#;AgX(Igl5H?rPd#t;o3tDpsvYT{TkkYt5 zwBdK1Tl?=~m{5>}6dSoU&Ce$-EEE?&45IzjDXG)(a53g>1jUw+ZG}&mbe&*5#S1Tv zHrusr&SvJ-VqsD*ge=7MT2L}snB)r!j+PGx{AF#1&sWxUw|&iku%$$RxV%EZCO=Nw zwo#z2ASys*SS=}Qx^VkrfT@UO=hZLf*97q72a=dR`?gSB=F{ww8IKJ_zZ?OfDN|h% zV3%~LX*Zw*2j!255|{UjnT-2{CYo9*6df#<2)m2JZu-w z^haPhPaOD)nh?uvZ;^NRY!AJb?&Nujk&1VsaELgX^yXvrCUt3EU#q2VIz^BLF8=|wr%smkZ8<#$+`mIz@xDxh6wtU*os9x-|z&nV9qUaZn@WZVEHL zMp~uRNaL5*3v5`VdWh+I^YiFCy!aUiotE&)F3lZ6qdEozslz)^fp3$%%X>O~KO9E- zNZwST9%!p%GpU^az{L)A^dq%31l}m8t60))`?r^M^BYxNffh_gL>u(gLB2!zrYal)Y$8Q5UN(41_}ZClH{p5gXXv4b+w0${WyzPvz>D(s$5nw$$O>*W=Bda zyVUe!PY9x=NSnx+Wx5Gb@jL++wDOsTuSg8|-gHpZ3_8|ta9r8P0uD@~tR%08_@$rP zAL|p|Ue3!8h(3GHp_Xyw3Uu(kqMY_9BvG%KjLkG3V=XPTvE$cCcaM2)NkR<&B$$xU zK~U|pZ2c>5GUvjuNc#$zjKYI>7|N#v%jB+UE;d+JJj4Kj>j(`^T6 zPA9BMpzjZPcS)!im!DLiLs&dXPxY^HQ0(;FF9*|(eC}jZLuhizrJ~UE6sqzIhqpZwENH4Ne} z20}gKCn6F$w=H(ex8oXQZ*oelkI_&@0ZSq-)%SHb9qP~BmS@u?HKB?viJu{)0{mpn zEZ%ITlZ?QFo_`4-p%j#xXqYvZpI^T-gvL+R`q|!^zj?!jGt2bl7kH6 z!O*uU;@S$6lENOx7l$4LH$B#sF^MYBd)4TZXK7O%#3;nC4DD3jiZgJEYTU`KP_{Pc zIdVI}LNx@3niv>Z#c?zon1WIOL0^=BKyQJ~b7|5P8$93WEouls@#KuiflLXTVekGU zX})(=;ahS1pe^-7wb%Md$pfMLoaDANKX3Hj!w;Zg z?bd)g!A$ypz0XGG`d)muMHl<&eq@KK#r`pGR9JklX>UX1&wy5Z4w1TL1gJBO6`}2+ zHOQHea$!m5iEk53*3m?J*O0~lx*&iga4X_{wJ#8H45&mgCTFutCC`3w#d65MDln`p z@QrNcFKE8|A|&9PH3Smn#wg{!QY3@l&18F2S*AF+!E^hz2+{(Cv#l7(yM3;giDdXF zvNGiK^E*&k1^^(?=NqWyTecuqQ`lrIHaD8elW=~Aimfikl%#@ zHiSQINr|?54={&vzya~)oNo1IH*HU!XTh~E$z!v%%K9@;@Jj8QeWetKfX;tn7z>bw z@FV}7G2|rC;Aa%yW0D_e>f()i7e~H3{_>)gxJr-xSK@|l(7v2Vq#D3P(@IN}M8!xt zY~vT+9}2_rl=$a3k4jk|-clD6Kx zr?Jl@Tb#)bXqs3!lM#=wq_%6~UKDL`ch~VS`q;}jFGw0oKn2wQXl%5*{hn6XtDvP8 zc~ebEDOccrF)`KQ^S_U>qOd0Y{5+e<_-{?x{FjmcIDi>K$|NOU4I zQ`dVwk{1%_6?|O&wX^LR1uL=-!1LN2LL{(EJVT7ra(bj(6UK8LH6KVAT`mARA-1AP zy4~bozI=%o9W;go>;RAs?wYk+kvmB-fOHGGZ!%DOL79jUv_F$Y9a1UTSssXymA-q; zjPhzh;lYBP`Js_mnxW4iB-}4{V znl41>^REujSNH0}T(uCuBzIrxIz6bznbx$d8|3%kG&*&@%^kf?@++^Zt~vch=KoX* z?<-%fTfBYd?efubioNX4CTr7-fz#l`J#?}Se@OElF?E2W)F3x$*Y-I{a7^{!TKmUH z>t4XJSyx66DY`cAaZEr9CkVdeL9VhS&sB49dUzdBJmofT8%)^ZE#9@NJoVKXncJ{^ z$a#3?Q7AWNP;?D<{4l#Zg|n*88|G)or&lDK>M@Iaq(8D#opO4tn1-B8(;j0@ z-HLY%u8Q8mk7q!Y6V>uC`X&syu2-aQVDTU?3d)AafrU}2c)PGC`5!@>i!OGpVEHs* zS{N4VSy4*^!(0zEI%Hh1eW5Nu5Z)=*ldGo<1|TY?DWTlz~v zh^XB*jXap}K|N2j^fl`}*|J7odwY8U*G+&&`?%UX2PfH@T`&!q(a{>f;6!Fj6JewdpRo_ zioS_zQ~D%7;N)Qlto?ci{6nF7)&j4ehC6CQ^8;l=<+zHJ^uXL?zI&@=^6KEd@ltEe zn*5VVnz9R23fjDFHFAifhER(F$lLX2kb2yamy%Oa*{Ai0%7C?_x7fFhCcZkw4%ROB z2OY}w<)V&=eBsW{u{L=}6Yll2SCygFyoMFv3pk-)OzPo_=rjjhuQJ!<2niAji2;O< za-#q|*gLoi)j`Wo2rI5Z1r3rEu6-ZFr?^MBqN5glRNKm(vN7f!zpJ1IytN7_F~EMa z+^ACRE&-ttzmz7xU(LQl5oLUJT@w-Nz}_IwWS19_X9;{tSp6dVNtTe^fz(v3fC@9x z@eL4${w_qq!xGP)Y*B(wp}a3Fl0P0sonyv{rRgi>--k}u$Y5?szl(csfO3F4bQ8XC z_&6Fl@H^*TSx!sge{3J!Q$Gy#lvPo>IhfgiUd_MGvhVD0elzByn8{?5|pHs$HT?)vscxvguz&BY$UR^OHbI|zjWmyn$UvAqkIv+N?2*Sv=>^we&QTX+;Xjy}l^OQUJ)}=HAU>W>z zj4Z3LnB&QJKUHei@VGTz<=RyfE!yP@NI{|cLnfG??_JOHv$?25;`x>+WRBA*`-@aq-{%$qj0BAZ#@=Z|tsLD1n#10RfB z>`h$uUa@9B1dA^Kky_P0svl~xb45ujG9w!$au7vyNDl}^KrF2Mp?Mfx&z@Or^!|$B zj|T2l_QC7Tn2;jy_L2g%_D}qz1o=ZN{Lb-(Sa4aJyr|e-QE%RV{5Y1jTXs^*e`1oP zcsI0B$3v!pniS`WTM@Ax%PNp4uSG(xbAM4B>$`^>Oe@+0{+}X@P`}={L$;>mrERQ! zh%fSlHG>V$#wxy~20Z}rvpQ^j$hg`qiAVu*z@Nu#7H87jK)0#;VdI_6X#{Feaf*IbEq1OH70V159_^|gaDCuuQH_PPDxs*P=rd7Jlqld za^8W^fO0KrqUd1?2RFx;Uma>Ir+<(Xl1Swxug!n&GLMnL$Hs1flk%IFC_$VcocbkzbE<1}a!u3nKFYZsAuJn3t^M#v}hefGBb_yDO z|6JlaBHV|H6{*$lT7E=8PS#8=VQPyPCux@*q%3V+D*y&PADedeHbFY+{d;Tt6anCQ zZE|7T)R_1lj>+8VLB5|os=utCHU93QF9jUqH;@Q1D301z&0ds#7k6q*Sd3xX$ZmTr}EbWg`gkll@sqf3Y$ZeTiyVEOhU4H$}g$mc?#sUZ_kd4 zjr8$h}Q1bJ1~bld3eck6>+XnUs`kiA;q>WKmfmT$LnBKSZF$mxqn3jh1Z(s zx4jyEQ^R8chxI2!ggOIHKxmk|{_Ev-3o>UItf~FsVYmQ%D72F7fCL^rR5(T0ou^EZ z--U~h$7vL2!paJ-TbJ?ZXgtM&mqW<66Yr>IUwjZs(=+e!GBrT^kUm6`C_0iZpA@J2 z#-x>1&qDL-0TZ&m+BHkK=+oO3b?M9@bvZ}}eR{96OL|^U7%+ijU*H&$Iopf92*RS0Ye}q{v zvZ5?8yx23fVhrLgk6DOQWqc)8sBW;6vpIE(w3(E|bv2om@OVf8LevnXf$kl@+Q(4Q zMl3jrU+rr;=y=WJ2ZE~n9Nx&MwSra>|2X<=%iI4XOb8~(+S^BlX%x9_7=$Aee6r$2 z|2Rrm0+TsnVzCh#lee#j{~j-Sj^19!(R^=VfKQZzGUq3}EN?K?(NcnHx(BAKww3YP_A9PsVXNINJ&;`^bNExfz83HL9VD-a>tKv8tI=tsdyc9QV$@3 zNx*`aQ?=3wG0EwDI6T#;)=PcMrp5fL;nn1qe@#>j)Zl0kCX}QU;R9m?ZV2+~3M%cl z-2)8|Uh2C%+}SVc33--12QSJi;2j5SB|d-NyZ(GFL$tr|$w2Yvv;Xh7^WgXGSih-} z)0`!QfBufYy`Fx51y8>Ig9pQ(CgGn@0RMsm{PSY|3V2`Qj@0_Nf0{ghib?+CzP42Z zuro{^Nk#p!2LDXo`H$-t0f73&@HLDF`@g!E;~TPpy?nQsl>dE7{rju`*K>=@0&ySm zamDLEKjdF@rN6&8qe_=uet$LkzZ&#F1{T>seuC$05r6Z?|8*DtyznD>$g-~*-uCAf z^?wZXA0K7OA%OOYBl=qDKL+=2KGp-y2j)iW+<)_wDl7zq4_UecliJz-_=A7{_GA8F z*!(vS8iNCFWwAlsg7Uw*mxkZ$LcjYve@4s9w(_%I27 z9=F?jFqHsTXr}$mmJ8WCJ>q-)=FJ=Ou@risD$fqM*)N{YdjDU`KkzQ~vVfzhzF}fv z<9s#iZ|eW-BT*iS2(|xvwbK6P>k45G`JMmr5&!d-kUN2wWR8V4$Dt$?BUiw``$`&$ J1@fjr{}08_g&qI^ literal 0 HcmV?d00001 diff --git a/docs/plans/miner-ui-2/18-narrow-900-node.png b/docs/plans/miner-ui-2/18-narrow-900-node.png new file mode 100644 index 0000000000000000000000000000000000000000..083a29401d3db28bd0c8e28a8ecc2ca4b682c02c GIT binary patch literal 169914 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

The honest bear-market case rests on cost. A miner's card is already running and the power is often domestic, so Igneum miners' marginal cost in the proving market is close to power, which is an edge over data-centre provers and nothing more.

Hardware

-

The dataset starts at 2 GB and grows by half a gigabyte a year, so a 4 GB card mines for about four years and an 8 GB card for more than a decade, approximate. 12 GB or more proves full shards. NVIDIA and AMD both work, because the mining program is generated for the architecture both share and the proof system is hash-based. Apple's chips are GPUs with unified memory, so Macs mine too, at about a fifth of a flagship card: Measured, 26.7 against 123 million hashes a second, an Apple M5 Max beside an RTX 5090 on the live devnet, 4 October 2026. A Mac is a poor miner per dollar. There is no CPU mining lane, on purpose, because CPU mining is what botnets farm. Nodes, wallets and exchanges need no GPU at all.

+

The dataset starts at 2 GB and grows by half a gigabyte a year, so a 4 GB card mines for about four years and an 8 GB card for more than a decade, approximate. 24 GB proves full shards and 32 GB mines and proves on one card, measured 5 October 2026 on this prover build (a 12 GB card does not prove on it: the GPU prover's floor is 13.9 GB). NVIDIA and AMD both work, because the mining program is generated for the architecture both share and the proof system is hash-based. Apple's chips are GPUs with unified memory, so Macs mine too, at about a fifth of a flagship card: Measured, 26.7 against 123 million hashes a second, an Apple M5 Max beside an RTX 5090 on the live devnet, 4 October 2026. A Mac is a poor miner per dollar. There is no CPU mining lane, on purpose, because CPU mining is what botnets farm. Nodes, wallets and exchanges need no GPU at all.

What a miner's hour looks like

The card hashes the lottery continuously. When the client sees a shard or an external job it can win, it switches the card to proving for a few seconds, posts the proof, and goes back to hashing. The client does the switching and the miner sees one balance.

The protocol carries no fee: no dev fund, no cut to any team. Ember, the miner software, takes an optional 1% dev fee, the way other GPU miners do. One block template in 100 is requested with the dev address instead of yours, by a counter, not a random draw, so it is exactly 1 in 100 and anyone can check it from the source or from the chain. One flag turns it off (--dev-fee 0, a switch in the app, a line in the HiveOS config). The miner prints the fee and the address when it starts. Any other client is welcome.

From b1266b7fb7bb348c0b148efe0b2be0f9519ed759 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:45:14 +0000 Subject: [PATCH 045/158] Consequences ledger: C20 closed with the epoch agent's correction (prepare-ahead, exit 42 on a miss only) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index f8c32dfb..0f9e65d2 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -32,5 +32,5 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- |---|---|---|---|---|---|---|---| | C18 | 5090 0.072 against 9070 XT 0.032 MH/s per pound at list prices (2.2x), 4.9x per watt | read-width.md 4.1 | AMD home miners; the public level 3 copy | The coordinator's 22:30 status entry says "near parity per pound"; the table it cites says 2.2x. The level 3 page is written from the status | The status and level 3 carry the table's figure (2.2x per pound, 4.9x per watt, 7.5x in rate); "near parity" is struck | coordinator | closed (coordinator 6b07776: the status, the rollout plan and the public copy carry 2.2x per pound, 4.9x per watt, 7.5x in rate) | | C19 | Mixer x4 into class v3: CPU verify 1.6 to 4.8 ms per warp against 0.604 today; pruning depth 108,000 DAA s (spec 02) | status 22:00; M16 table; spec 02 line 17 | every node (the three testnet seeds on small Hetzner VMs, the observer, a laptop node); IBD; pools | A new node verifies every header in the pruning window on one core: 108,000 x 4.8 ms = 8.6 min at x4 against 1.1 min today (a 30-s block-time budget at 1 block/s is unaffected: 4.8 ms per block is 0.5% of a core). Every miner's own `cpu re-check` of a found hash and every pool share verification cost the same 8x; the 10 ms gate (ledger M9) keeps 2x of margin at the slow end, which is what Counter ASIC 3.0 has left to spend. On the 2019-class laptop core the evidence table names (rule 3) the figure is unmeasured and may pass 10 ms | The ca2-mixer document carries: ms per warp on the M5 Max core AND a scaled 2019-class figure (marked approximate), the pruning-window IBD minutes per tier, pool shares per core per second, and the gate margin left for 3.0; the seeds' header-verify load is checked in the testnet go checklist | ca2-mixer af345b1e2c541ffbb; coordinator | in work (coordinator: with ca2-mixer; the seeds' header-verify load into testnet-go.md) | -| C20 | Layer 9: the epoch length as an era parameter, 600 to 7,200 DAA s (10 min to 2 h), base 3,600 | status 22:30 and 23:00 | rigs (HiveOS and the rig installer), Macs, pools, the seed path | Both rig miners run `--exit-on-seed-change` and re-export the pack on exit 42 (h-run.sh 56 to 61, igneum-miner.sh 67 to 78): at a 10-minute epoch every card's miner restarts six times an hour with a pack export each time, and the restart gap is lost hashing; the app's prepare-ahead path does not restart. The Mac fleet's prepare pause (35 s an hour at one epoch an hour, bench-log M11) becomes 3.5 min an hour, 6%. The 10-minute seed VDF (spec 04) equals the shortest epoch, so the seed for epoch n+1 is known only as epoch n starts, which is the compile-ahead window the agent must measure per card (the 5090 compiled in 1,285 ms, the 9070 XT unmeasured). A pool's `job` cadence and the dev-fee counter are unaffected | The epoch-length document carries a per-tier row: rig restart cost per epoch length (and the fix: prepare-ahead in the rig scripts, no exit 42 path), the Mac pause share, the compile-ahead margin per card at 600 DAA s against the VDF; the rig installer removes `--exit-on-seed-change` in favour of the prepare path before layer 9 can draw a short epoch | ca2-epoch a32a3ece66c02417a; rig installer | in work (coordinator: with ca2-epoch; the rig installer's exit-42 path on the next-cut list as a precondition for any short epoch) | +| C20 | Layer 9: the epoch length as an era parameter, 600 to 7,200 DAA s (10 min to 2 h), base 3,600 | status 22:30 and 23:00 | rigs (HiveOS and the rig installer), Macs, pools, the seed path | Both rig miners run `--exit-on-seed-change` and re-export the pack on exit 42 (h-run.sh 56 to 61, igneum-miner.sh 67 to 78): at a 10-minute epoch every card's miner restarts six times an hour with a pack export each time, and the restart gap is lost hashing; the app's prepare-ahead path does not restart. The Mac fleet's prepare pause (35 s an hour at one epoch an hour, bench-log M11) becomes 3.5 min an hour, 6%. The 10-minute seed VDF (spec 04) equals the shortest epoch, so the seed for epoch n+1 is known only as epoch n starts, which is the compile-ahead window the agent must measure per card (the 5090 compiled in 1,285 ms, the 9070 XT unmeasured). A pool's `job` cadence and the dev-fee counter are unaffected | The epoch-length document carries a per-tier row: rig restart cost per epoch length (and the fix: prepare-ahead in the rig scripts, no exit 42 path), the Mac pause share, the compile-ahead margin per card at 600 DAA s against the VDF; the rig installer removes `--exit-on-seed-change` in favour of the prepare path before layer 9 can draw a short epoch | ca2-epoch a32a3ece66c02417a; rig installer | closed with a correction (ca2-epoch 4300608, epoch-length.md sections 6.3, 7, 9): the rig scripts pass --prepare-packs as well, so a worker with prepare support swaps in place and exit 42 is the fallback on a prepare MISS (the loaded iGPU missed 2 of 10, M11), not a restart every epoch; the risk at 600 s is one restart plus export plus inline compile per missed boundary; the Mac race pause is 6.3% of a 600-s epoch (race default off); the program is known a full epoch ahead at every length (lead and T_epoch fixed, option A); the 9070 XT compile is OWED (no prepared line from gfx1201 in any upload); the rig installer owes prepare-ahead as the only routine boundary path, the miss causes fixed and a miss counter in the status line | | C21 | OTA K2: apps embed `OTA_PUBLIC_KEYS = [K1, K2]` and honour a signed `revoked_keys` list; the rig installer verifies the manifest with ONE key (`OTA_PUBLIC_KEY_HEX`, install-rig.sh 168, igneum-update.sh 2) and knows no revocation; the HiveOS package verifies nothing (no manifest, the override reaches it only by republish) | ota-k2 c722579; packaging/linux; packaging/hive | rigs (both packages), the seeds (if they take the manifest) | The day K1 is lost or revoked and the manifest is signed with K2, every rig on the installer refuses the manifest, stops taking overrides, and is isolated at the next height switch; a leaked K1 keeps signing for rigs, because they carry no revocation list. HiveOS rigs get neither keys nor revocation: a republished package is their only path, and nothing checks who published it | The rig installer carries both public keys and the `revoked_keys` rule in the same form as the app (keys.md section 4, step 3), installed and read from the manifest; the HiveOS README states that the package is unsigned and names the sha256 the Flight Sheet URL should be checked against; keys.md lists the rig and HiveOS paths in its table of what trusts K1 | OTA key af2bb75a5436324d0 (keys.md, the shared verifier form); rig installer a3e7b2b03222f5cff | in work (OTA agent 00fcbb5 on ota-k2: keys.md section 4 lists every path that trusts K1 with its state and owner and the exact two-key and revocation form for the rig; the HiveOS README says the archive is unsigned and names the per-build sha256 check; the rig verifier itself is with the rig installer; the WALLET (branch wallet-v1, its own copy of manifest.rs) also trusts K1 alone until its owner ports the form, listed in the keys.md table. Merge note for the next cut: packaging/hive/README.md is edited on BOTH ota-k2 00fcbb5 and hive-words 226d3b8, so the integrator takes both paragraphs) | From 4c9820c2025fbe4f48958f47215603e132238a11 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:45:46 +0000 Subject: [PATCH 046/158] Consequences ledger: C15 closed with the S_p curve tiers; D2 recommendation re-cut to the measured tiers Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- docs/plans/consequences-decisions.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 0f9e65d2..93f8fbba 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -22,7 +22,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C12 | The v3 working set on Apple: 1,568 to 1,760 MiB today, 2,592 to 2,784 MiB at the 2 GiB genesis dataset, in unified memory shared with macOS; 2,048 launched warps x 128 KiB scratch | `docs/plans/hot-table.md` section 3 M5 Max row; era-layout section 3 | Apple silicon with 8 GB and 16 GB (the base Mac mini, MacBook Air) | An 8 GB Mac holds the miner's 2.6 GB beside macOS's 3 to 4 GB: it mines today and swaps at the first dataset growth step. The app has no gate on Metal's `recommendedMaxWorkingSetSize`; the site's "Any 4 GB card" has no Apple line. The CPU prover's RAM on a 16 GB Mac is unmeasured (the Settings switch lets it on) | The app reads `recommendedMaxWorkingSetSize` and refuses to mine (with the reason on the Mine tile) when the working set exceeds it; the site says "Mac: 8 GB or more"; the Apple scratch row at 128 KiB is measured in the readwidth table, not launched at 2,048 by assumption | miner UI adbf58b058186a18b; read-width (the Apple row) | in work (read-width: the scratch row's working set per card measured, Mac 1.5 to 1.9 GiB; the Metal footprint runs held the measure lock at 22:3x; miner UI: mining.gate_reason next cut) | | C13 | `/api/supply` `max_supply_ign` 4,000,000,000 against `minted_at_end_ign` about 3,963,000,000 (the ramp withholds about 37 M, the floors the rest); the live tables lack `number`, `tx_count`, `detail` until the observer restarts | `site/api/supply.mjs` 31 to 48; `docs/plans/explorer.md` "Open" | everyone who reads the explorer beside the homepage tile "4B IGN hard cap, ever" (`site/index.html` 386) | The tile says 4 billion and the explorer page says "of 4,000,000,000 by the rule" while the API's own end figure is 3.963 billion: a reader who adds the two columns finds 37 million missing. The explorer page shows "Circulating 0 IGN" on the devnet deployment until the observer restarts on the new code | The tile, the litepaper's supply line and the explorer carry "cap 4,000,000,000; about 3.96 billion ever minted" (the litepaper already says "approached and never reached"); the explorer does not go live before the observer restart lands the columns | explorer a76f60b415859b7b5; wording: the project lead (D7, with D3) | closed (explorer 3e01212: the tile reads "cap 4,000,000,000, 3.96 billion ever minted, x% so far" with the withheld figure on hover; the homepage tile and litepaper wording stay with the project lead, D3 and D7; the zero-circulating premise was sharper than stated, a 42703 failure not a null, now a 503 with the reason, and moot: the live observer restarted on the explorer code at 19:42:50Z, so the live tables carry the columns) | | C14 | The M16 mixer x4 decided into class v3 at 22:00 (coordinator): verifier 1.6 to 4.8 ms per warp against 0.441 ms today (0.87 cold), measurement running on branch ca2-mixer | coordinator's reply 21:5x; `docs/analysis/m16-recompute-attacker-2026-10-05.md` table; spec 09 section 9.8 item 5 | pool operators; every node (the Hetzner seeds, the observer, a 2019-class laptop); the 10 ms verify gate | At x4 one pool core verifies 200 to 600 shares a second instead of 2,270, so one core covers 2,000 to 6,000 members at one share per 10 s instead of 22,000; a block's CPU re-check and the miner's own `cpu re-check` of every found hash cost 4 to 11x; the seeds' small VMs verify every header at that cost; the gate's margin falls from 23x to 2 to 6x, which bounds Counter ASIC 3.0's room | The coordinator is adding the numbers to the ca2-mixer document and the status (said in reply); the reviewer checks at the next sweep that the pool-members-per-core and the seed-VM header-verify rows are there, and that the spec 09 figure 2,270 shares a second per core is re-cut with v3 | coordinator ada8afb62d752b1e2 | in work | -| C15 | A full prototype shard peaks at 28.3 GB on sp1-gpu-server 6.8.1 (the sweep, proving agent 22:0x); an empty one 13.9 GB; no knob moves either floor | proving agent's reply; sweep job `memsweep-pc2-pv1` | 24 GB cards (4090, 7900-class if it had a path); the 5090 that mines and proves; the fleet table | A 24 GB card cannot prove a prototype full shard at all, mining or not; a 5090 mining (3.4 GB resident) plus a full shard is 31.7 GB against 31.8 GB, the edge. The 20 GB / 16 GB default rests on the empty-shard number. From H tomorrow the fleet proves v1 shards of 30,000 pgas (about 7 M cycles, a ninth of the prototype shard) whose peak is unmeasured; `proving/fixtures/fees-v1-shards2` and `-shards3` are that shape. The fleet table's "proving-only" rows are 32 GB-card rows until then; the litepaper's "12 GB" (D2) and the evidence row 16 fall with it | The sweep's last row is a v1-budget shard with and without the miner, and the provedefault gates are set from it before 0.3.11 ships default-on; the fleet table labels its rows by the card that fits | proving v1 | sent | +| C15 | A full prototype shard peaks at 28.3 GB on sp1-gpu-server 6.8.1 (the sweep, proving agent 22:0x); an empty one 13.9 GB; no knob moves either floor | proving agent's reply; sweep job `memsweep-pc2-pv1` | 24 GB cards (4090, 7900-class if it had a path); the 5090 that mines and proves; the fleet table | A 24 GB card cannot prove a prototype full shard at all, mining or not; a 5090 mining (3.4 GB resident) plus a full shard is 31.7 GB against 31.8 GB, the edge. The 20 GB / 16 GB default rests on the empty-shard number. From H tomorrow the fleet proves v1 shards of 30,000 pgas (about 7 M cycles, a ninth of the prototype shard) whose peak is unmeasured; `proving/fixtures/fees-v1-shards2` and `-shards3` are that shape. The fleet table's "proving-only" rows are 32 GB-card rows until then; the litepaper's "12 GB" (D2) and the evidence row 16 fall with it | The sweep's last row is a v1-budget shard with and without the miner, and the provedefault gates are set from it before 0.3.11 ships default-on; the fleet table labels its rows by the card that fits | proving v1 | closed as measured (proving agent, the S_p curve, app default 440fd59): 32 GB mines and proves today (28.3 GB alone, 30.0 beside the miner); 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner) from DAA 210,000 and nothing before it; 16 GB proves only empty shards alone (13.9 GB), nothing beside the miner (15.7 GB); 12 GB proves nothing on SP1 6.8.1; the default is on at 24 GB or more. Relayed to the rig installer and the HiveOS words sub-agent for their tables; until H tomorrow every prover on the devnet is a 32 GB card | | C16 | PC 2's app quit at 20:01:09Z ("aborted (the app is quitting)"), logged by the proving agent as "the app quit for the 0.3.10 update"; the shipper says nothing of 0.3.10 was published and no update-now of its exists (the live manifest is 0.3.9 from 17:59:14Z) | bench-log "proving v1" chain run 1; the shipper's reply 22:0x | every measurement on PC 2 that straddles 20:01Z (the prover-cost phase B ended 19:51Z, the chain re-run began 20:05Z, the readwidth 5090 job queued) | An app that quits for an unknown reason voids any number taken across it (CLAUDE.md: a number taken while another build or simulation ran is not a number; the same for a restart). The bench-log line names a cause that did not happen | The proving agent reads PC 2's app log for the quit reason at 20:01Z and corrects the bench-log line; if the cause is another agent's job or the auto-update, that agent's measurements across it are marked | proving v1 (the log read); the agent the cause names | closed in part (proving agent: PC 2 logged "quit: stopping the miners, then the node" at 20:01:09Z, a plain quit command 20 s after the efficiency sweep's administrator prompt was cancelled at the keyboard and 13 s after the live prover failed on a root-owned /tmp/sp1-cuda-0.sock left by the chain job; the bench-log line corrected; the quit's origin is not in the log, see C17) | | C17 | The chain job ran igneum-prove-host as root inside WSL2 and left a root-owned `/tmp/sp1-cuda-0.sock`; the live prover (the app's user) then failed with `CudaClientError: Connect(PermissionDenied)` at 20:00:56Z; the app quit at 20:01:09Z on a command whose source the log does not name | proving agent's reply 22:1x; PC 2's app log | every PC 2 measurement that shares the card with the live prover; every operator whose machine takes remote jobs | Two classes, not one bug. (1) Any job script that runs the SP1 server or the host as root in WSL2 breaks the live prover for every later shard until a reboot or a manual unlink; the proving agent fixed its own scripts (kill the server, remove the socket at the end), the class check (CLAUDE.md, 5 October: grep every script with the same shape, add a check that fails when the shape comes back) is not yet written. (2) A quit the log cannot attribute (job, UI, signal, update) voids the measurements around it and nobody can say who stopped a miner; the app logs "quit:" without a source | (1) `tools/ci/` gets a check that fails on any `.ps1` or `.sh` playbook that invokes `igneum-prove-host`, `sp1-gpu-server` or `wsl -u root` without the socket cleanup line, and the proving agent greps tonight's four PC 2 scripts; (2) the engine's quit log line carries its source (job id and playbook name, the UI, a signal, the updater) in the next app cut | proving v1 (1); coordinator for the next-cut list (2) | sent | diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index 92218930..150ada2a 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -5,7 +5,7 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | # | Row | What needs deciding | Recommendation | Why | |---|---|---|---|---| | D1 | C1 | The fee switch lands at H = 210,000 about 19:50Z on 6 October, and the 0.3.10 node's export does not carry the switch, so every app prover's shards are refused or vetoed from H. Move H, or race 0.3.11 onto every prover first | If 0.3.11 (with the proving-v1 fork, whose export carries `daaScore` and `feesV1ActivationDaa`) is not on every prover by 16:00Z on 6 October, republish the override with H = tip + 86,400 rounded to the next thousand, re-read the digest on a scratch node, every node in one sweep (the fee-switch plan's own rule for a later H). The coordinator holds the devnet delegation; this note is so the morning does not find proving dark | A dark proving pool on the devnet costs nothing on chain (the escrow keeps it) but every coverage, latency and fleet number measured after H is void | -| D2 | C3 | The litepaper says "12 GB or more proves full shards"; the only measurement puts the prover alone at 13.8 GB on a 32 GB card | Keep the sentence only if the sweep lands a full v1 shard under 11.5 GB with the SP1 knobs; otherwise change to "24 GB proves and mines on one card; 12 and 16 GB cards prove with the miner paused" until the RTX 3060 on order measures it | A public number that the first 3060 owner disproves is the FUD the ledger exists to prevent | +| D2 | C3 | The litepaper says "12 GB or more proves full shards"; the only measurement puts the prover alone at 13.8 GB on a 32 GB card | The sweep and the S_p curve are in (proving agent, 5 October late): no knob moves the floor; 12 GB proves nothing on SP1 6.8.1, 16 GB proves only empty shards alone, 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner), 32 GB proves everything. Change the sentence to "24 GB or more proves; 32 GB proves and mines on one card" and evidence row 16 to tested-by-the-team on those rows; the 12 GB figure returns only if a smaller GPU server or a smaller shard measures under 12 GB | A public number that the first 3060 owner disproves is the FUD the ledger exists to prevent | | D3 | C8, C13 | The homepage says "Any 4 GB card" and the litepaper says a 4 GB card mines for about four years and an 8 GB card for more than a decade; the tile says "4B hard cap" while the rule mints about 3.96 billion | Replace with the lifetime table's numbers once the sub-agent lands it: "4 GB cards mine at launch; 8 GB for about six years; 12 GB for about fourteen; the dataset grows half a gigabyte a year" and "cap 4 billion, about 3.96 billion ever minted" | The schedule is public and the arithmetic is one line; a reader will do it | | D4 | C9, C8 | Index mapping at gate 1, now with the lifetime table (`docs/analysis/card-lifetime-2026-10-05.md`): (a) multiply-shift, continuous growth: 4 GB cards out at 1.0 to 1.5 years, 8 GB at 6.3 to 7.5, 12 GB at 12 to 13.5, Apple 8 GB at 2.6 to 3.4; (b) power-of-two steps at years 4, 12, 28, 60: 4 GB to year 4, 8 GB to year 12, 12 GB to year 28 (the cache freed after the build, decided by the coordinator at 22:50), each tier ending on a step day | (b), with the step calendar published on the miners page from day one (the years 4, 12, 28 and 60 named beside the tiers), and the 1 GiB vectors kept. Revised from (a) at 23:0x: the table shows (b) is the only mapping under which the litepaper's "4 GB about four years, 8 GB more than a decade" holds, and a step day known twelve years ahead is a schedule, not an event; the coordinator recommends the same | Under (a) both public sentences are wrong today by 2.5 to 5 years; under (b) they hold and the cliffs are dated | | D5 | C10 | The site's "under 2x" chip claim: the scratch layer leaves the on-die-cache recompute chip at 2.4x at every share; the mixer multiplier x2 brings it to 1.2x at twice the CPU verify cost (about 0.9 ms per warp, half the pool members per core) | Carry the mixer x2 into class v3 on the devnet tonight (the coordinator's gates apply, the verify time per warp measured) and keep the claim; if the coordinator judges it outside the delegation, qualify the claim in level 3 until the project lead says | The claim is the project's first public sentence on chips; it is either true by a measured lever or it is marked | From 1887438b3648549de14978f14d1bd318a34cbc0e Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:45:56 +0000 Subject: [PATCH 047/158] Consequences decisions: D2 notes the proving agent's drafted sentences on its branch Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-decisions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index 150ada2a..7d35a023 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -5,7 +5,7 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | # | Row | What needs deciding | Recommendation | Why | |---|---|---|---|---| | D1 | C1 | The fee switch lands at H = 210,000 about 19:50Z on 6 October, and the 0.3.10 node's export does not carry the switch, so every app prover's shards are refused or vetoed from H. Move H, or race 0.3.11 onto every prover first | If 0.3.11 (with the proving-v1 fork, whose export carries `daaScore` and `feesV1ActivationDaa`) is not on every prover by 16:00Z on 6 October, republish the override with H = tip + 86,400 rounded to the next thousand, re-read the digest on a scratch node, every node in one sweep (the fee-switch plan's own rule for a later H). The coordinator holds the devnet delegation; this note is so the morning does not find proving dark | A dark proving pool on the devnet costs nothing on chain (the escrow keeps it) but every coverage, latency and fleet number measured after H is void | -| D2 | C3 | The litepaper says "12 GB or more proves full shards"; the only measurement puts the prover alone at 13.8 GB on a 32 GB card | The sweep and the S_p curve are in (proving agent, 5 October late): no knob moves the floor; 12 GB proves nothing on SP1 6.8.1, 16 GB proves only empty shards alone, 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner), 32 GB proves everything. Change the sentence to "24 GB or more proves; 32 GB proves and mines on one card" and evidence row 16 to tested-by-the-team on those rows; the 12 GB figure returns only if a smaller GPU server or a smaller shard measures under 12 GB | A public number that the first 3060 owner disproves is the FUD the ledger exists to prevent | +| D2 | C3 | The litepaper says "12 GB or more proves full shards"; the only measurement puts the prover alone at 13.8 GB on a 32 GB card | The sweep and the S_p curve are in (proving agent, 5 October late): no knob moves the floor; 12 GB proves nothing on SP1 6.8.1, 16 GB proves only empty shards alone, 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner), 32 GB proves everything. Change the sentence to "24 GB or more proves; 32 GB proves and mines on one card" and evidence row 16 to tested-by-the-team on those rows; the 12 GB figure returns only if a smaller GPU server or a smaller shard measures under 12 GB. The proving agent has drafted the two replacement sentences on its branch (app 440fd59, litepaper and evidence rows 15 and 16); nothing is pushed, so the project lead approves or rewrites the draft rather than starting from the measurement | A public number that the first 3060 owner disproves is the FUD the ledger exists to prevent | | D3 | C8, C13 | The homepage says "Any 4 GB card" and the litepaper says a 4 GB card mines for about four years and an 8 GB card for more than a decade; the tile says "4B hard cap" while the rule mints about 3.96 billion | Replace with the lifetime table's numbers once the sub-agent lands it: "4 GB cards mine at launch; 8 GB for about six years; 12 GB for about fourteen; the dataset grows half a gigabyte a year" and "cap 4 billion, about 3.96 billion ever minted" | The schedule is public and the arithmetic is one line; a reader will do it | | D4 | C9, C8 | Index mapping at gate 1, now with the lifetime table (`docs/analysis/card-lifetime-2026-10-05.md`): (a) multiply-shift, continuous growth: 4 GB cards out at 1.0 to 1.5 years, 8 GB at 6.3 to 7.5, 12 GB at 12 to 13.5, Apple 8 GB at 2.6 to 3.4; (b) power-of-two steps at years 4, 12, 28, 60: 4 GB to year 4, 8 GB to year 12, 12 GB to year 28 (the cache freed after the build, decided by the coordinator at 22:50), each tier ending on a step day | (b), with the step calendar published on the miners page from day one (the years 4, 12, 28 and 60 named beside the tiers), and the 1 GiB vectors kept. Revised from (a) at 23:0x: the table shows (b) is the only mapping under which the litepaper's "4 GB about four years, 8 GB more than a decade" holds, and a step day known twelve years ahead is a schedule, not an event; the coordinator recommends the same | Under (a) both public sentences are wrong today by 2.5 to 5 years; under (b) they hold and the cliffs are dated | | D5 | C10 | The site's "under 2x" chip claim: the scratch layer leaves the on-die-cache recompute chip at 2.4x at every share; the mixer multiplier x2 brings it to 1.2x at twice the CPU verify cost (about 0.9 ms per warp, half the pool members per core) | Carry the mixer x2 into class v3 on the devnet tonight (the coordinator's gates apply, the verify time per warp measured) and keep the claim; if the coordinator judges it outside the delegation, qualify the claim in level 3 until the project lead says | The claim is the project's first public sentence on chips; it is either true by a measured lever or it is marked | From ce7474a74e9aefbb0cd1d6a1308aee4d36b56f6d Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:46:53 +0000 Subject: [PATCH 048/158] Consequences ledger: sweep 3 notes (the 9070 XT bus drop stated by the coordinator, the epoch plan's tier table, the drafted litepaper sentences); C7 HiveOS table re-cut (2d056e8) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 93f8fbba..4e3bd304 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -14,7 +14,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C4 | Host RAM 25,550 MB used of 63,132 on PC 2 with the prover on; the WSL2 VM working set 7,915 MB | bench-log "proving v1", step 1 host RAM row | Windows home miners with 16 GB RAM (the common gaming PC); Macs with 16 GB switching the CPU prover on | The prover default has no RAM floor. On Windows the WSL2 VM alone holds 7.9 GB beside the app, the node and the game-class desktop; a 16 GB machine with proving on by default swaps or kills the node. The app's own RSS (engine, node, verifier) is not separated in the measurement, so no requirement can be stated yet | The sweep job records the app's and the node's working sets beside the VM's; `provedefault` reads total RAM and stays off under 32 GB on Windows until measured; the Proving tile and the miner page state the RAM requirement | proving v1; miner UI adbf58b058186a18b (the tile line) | closed in code (proving v1 c2544be: MIN_RAM_MB_WINDOWS 31,000 in provedefault.rs, the tile line names the 7.9 GB VM on a 63 GB PC; unknown RAM is not a gate; the miner UI help line next cut) | | C5 | The app quit for the 0.3.10 update at 20:01:09Z and aborted the chain job at block 3 ("aborted (the app is quitting)"); `prover.rs` kills the child on quit | bench-log "proving v1" chain run 1; `app/igneum-app/src/prover.rs` 266 to 272 | every prover on every update; with proving v1 the aggregator | Tonight's `update-now` to every app aborts whichever shard each prover has in flight (up to 37 s of work each, no payout, re-assigned to nobody until the window passes). Under proving v1 a restart mid-segment loses the aggregator's chain state: the segment goes unproven after T (600 DAA), the aggregator share of 8 blocks is forfeited to the escrow, and the next record must be fresh-chain. With one aggregator on the devnet every app update costs 10 minutes of unproven segments | The update's safe moment waits for the prover's current proof (as it does for the node); the aggregator persists the last segment proof and resumes the chain after a restart; the Updates section says "waits for the proof in flight". For 0.3.10 tonight the aborted shards are an accepted cost, recorded | proving v1; shipper (tonight's rollout note); miner UI (Updates wording) | refused for tonight by the proving agent (persisting the segment proof and holding the update for a proof in flight are 0.3.12; the cost is in the plan as the rule working as written); the shipper carries the aborted-shard count in release-0.3.10.md section 8; the 20:01:09Z abort was NOT 0.3.10 (shipper: nothing published), see C16 | | C6 | The prover costs a mining 5090 4.0% (124.7 to 119.7 MH/s); the software dev fee is 1 template in 100 | bench-log "proving v1" step 1; `packaging/hive/README.md` "The dev fee" | pool users; the pool's ledger | A member who proves sends 4% fewer shares, so the pool's vardiff and `stats.hashrate` read a 4% loss while the proving income (90% of the shard credit plus a tenth to an aggregator) is paid to the member's own key and never appears in the pool's ledger: the pool dashboard understates a proving member's earnings. The software dev fee has no mechanism in pool mode (the pool issues the templates), so a pooled miner pays no dev fee today and the pool design must say whether it takes one (1 share in 100 to the dev address) or none | The pool-v0 design states both: the member `stats` carry a `proving` flag and the pool page shows proving income beside shares; the dev fee rule in pool mode is written down before the first pool ships | pool a4781ba117326091f | sent | -| C7 | The HiveOS package holds `igneumd`, `igneum-miner` and the two workers; no `igneum-prove-host`, no SP1 GPU server, no per-card rule | `packaging/hive/make-hive-package.sh` 26 to 30; README "What the hooks do" | rigs (HiveOS, Linux), the largest hashrate tier | A rig cannot prove at all, so the 20% proving share is reachable only from the app. The miner page says "the card mines and proves" (`site/miner.html` 7, `site/index.html` 484) and the HiveOS README does not say rigs mine only. A rig that could prove needs the 251 MB SP1 GPU server, CUDA 12.8 and the per-card profile of C2 and C3, and a rig's RAM (4 to 8 GB on most Hive images, approximate) is below C4's floor | The rig installer either carries the prover with the per-card rule and a RAM check, or its README and the miners page say rigs mine only and provers are app machines; `IDENTITIES=8 for a big card, 2 for a small one` gets a threshold in GB | rig installer a3e7b2b03222f5cff | closed in part (rig installer 189ab32 with the sweep's gates; HiveOS sub-agent 226d3b8 on branch hive-words: the README says a rig mines only with the per-card table and its sources, IDENTITIES=auto resolved per card by VRAM, selftest green; open: the miners page sentence D8, and provedefault.rs's own doc table still says "24 GB mines and proves" without the shard-size bound, with the proving agent) | +| C7 | The HiveOS package holds `igneumd`, `igneum-miner` and the two workers; no `igneum-prove-host`, no SP1 GPU server, no per-card rule | `packaging/hive/make-hive-package.sh` 26 to 30; README "What the hooks do" | rigs (HiveOS, Linux), the largest hashrate tier | A rig cannot prove at all, so the 20% proving share is reachable only from the app. The miner page says "the card mines and proves" (`site/miner.html` 7, `site/index.html` 484) and the HiveOS README does not say rigs mine only. A rig that could prove needs the 251 MB SP1 GPU server, CUDA 12.8 and the per-card profile of C2 and C3, and a rig's RAM (4 to 8 GB on most Hive images, approximate) is below C4's floor | The rig installer either carries the prover with the per-card rule and a RAM check, or its README and the miners page say rigs mine only and provers are app machines; `IDENTITIES=8 for a big card, 2 for a small one` gets a threshold in GB | rig installer a3e7b2b03222f5cff | closed in part (rig installer 189ab32 with the sweep's gates; HiveOS sub-agent 226d3b8 then 2d056e8 on branch hive-words: the README says a rig mines only, with the per-card table re-cut from the S_p curve (8 and 12 GB mine only, 16 GB in practice mines only, 24 GB proves the adopted shard from DAA 210,000, 32 GB mines and proves) and its sources, IDENTITIES=auto resolved per card by VRAM, selftest green; open: the miners page sentence D8, and provedefault.rs's own doc table still says "24 GB mines and proves" without the shard-size bound, with the proving agent) | | C8 | Dataset 2 GiB at genesis plus 0.5 GiB a year; the working-set rule "under 6 GB on an 8 GB card"; the cache 256 MiB doubling at years 4 and 12; scratch up to 128 KiB per resident warp | spec 01 section 1.13.3; coordinator's budget rule; layer 6 option C; `docs/plans/hot-table.md` section 3 | 4 GB and 8 GB cards; the litepaper's claim | `site/index.html` 461 says "Any 4 GB card" and the litepaper (560) says a 4 GB card mines for about four years and an 8 GB card for more than a decade. At 75% of the card the 4 GB card's dataset room is about 2.4 GiB: under one year. The 8 GB card's room is about 5 GiB after cache, hot table, scratch and buffers: about six years, five and a half with the year-4 cache step. Neither public sentence holds under the schedule | A card-lifetime table per tier (sub-agent, `docs/analysis/card-lifetime-2026-10-05.md`); the public wording is a claim for the project lead (D3) | sub-agent (table); decision (wording) | table landed (sub-agent, docs/analysis/card-lifetime-2026-10-05.md, 1fecfe2, merged into ca2-coord at 22:50): 4 GB 1.0 to 1.5 years under (a) and year 4 under (b); 8 GB 6.3 to 7.5 or 12; 12 GB 12 to 13.5 or 12 to 28 depending on whether the cache stays resident; Apple 8 GB 2.6 to 3.4 or 4. The coordinator decided the cache is freed after the daily build and recommends (b) to the project lead; the public sentences hold only under (b): D3 and D4 revised | | C9 | Index mapping option (a) multiply-shift (fades cards) against (b) power-of-two steps 2, 4, 8 GiB | spec 01 section 1.13.3, gate 1 | 8 GB and 12 GB cards | Option (b)'s 8 GiB step (about year 12) ends 8 GB and 12 GB cards on the same day; option (a) fades them one year at a time. The choice is a genesis parameter and a tier consequence nobody has put beside the options | Decision request D4 with the lifetime table of C8 | decision | open | | C10 | The on-die-cache recompute chip's gain is 2.4x at every scratch share under the 6 GB cap; the mixer multiplier x2 brings it to 1.2x, x4 to 0.6x, inside the 10 ms verify gate | `docs/analysis/scratch-soundness.md` finding 2 and section 10; M16 analysis table | chip builders; the site's "under 2x" claim; pool share verification | Layer 3 does not deliver the headline and the rollout plan's own rule (6a) says the public claim is qualified when no share gets the chip under 2x. The lever that does is M16's mixer multiplier, which doubles the CPU verify per warp (0.441 ms to about 0.9 ms at x2): a pool core verifies about 11,000 members' shares instead of 22,000, and node block verification doubles. The corrected SRAM cost ($19 to $37 of silicon per mirror die) means the mirror never stops a funded chip; the cache schedule keeps it above GPU L2 only | The coordinator either carries the mixer x2 into class v3 for the devnet (it is a lottery-hash change like the others; the verify cost per warp is measured with it) or marks the site's "under 2x" as qualified in the public copy level 3 and D5 asks the project lead which | coordinator ada8afb62d752b1e2 | closed as a consequence (coordinator: the public claim was qualified at 21:50; at 22:00 the M16 mixer x4 was decided into class v3 behind the same activation; the pool-core and node verification consequences are C14) | @@ -34,3 +34,5 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C19 | Mixer x4 into class v3: CPU verify 1.6 to 4.8 ms per warp against 0.604 today; pruning depth 108,000 DAA s (spec 02) | status 22:00; M16 table; spec 02 line 17 | every node (the three testnet seeds on small Hetzner VMs, the observer, a laptop node); IBD; pools | A new node verifies every header in the pruning window on one core: 108,000 x 4.8 ms = 8.6 min at x4 against 1.1 min today (a 30-s block-time budget at 1 block/s is unaffected: 4.8 ms per block is 0.5% of a core). Every miner's own `cpu re-check` of a found hash and every pool share verification cost the same 8x; the 10 ms gate (ledger M9) keeps 2x of margin at the slow end, which is what Counter ASIC 3.0 has left to spend. On the 2019-class laptop core the evidence table names (rule 3) the figure is unmeasured and may pass 10 ms | The ca2-mixer document carries: ms per warp on the M5 Max core AND a scaled 2019-class figure (marked approximate), the pruning-window IBD minutes per tier, pool shares per core per second, and the gate margin left for 3.0; the seeds' header-verify load is checked in the testnet go checklist | ca2-mixer af345b1e2c541ffbb; coordinator | in work (coordinator: with ca2-mixer; the seeds' header-verify load into testnet-go.md) | | C20 | Layer 9: the epoch length as an era parameter, 600 to 7,200 DAA s (10 min to 2 h), base 3,600 | status 22:30 and 23:00 | rigs (HiveOS and the rig installer), Macs, pools, the seed path | Both rig miners run `--exit-on-seed-change` and re-export the pack on exit 42 (h-run.sh 56 to 61, igneum-miner.sh 67 to 78): at a 10-minute epoch every card's miner restarts six times an hour with a pack export each time, and the restart gap is lost hashing; the app's prepare-ahead path does not restart. The Mac fleet's prepare pause (35 s an hour at one epoch an hour, bench-log M11) becomes 3.5 min an hour, 6%. The 10-minute seed VDF (spec 04) equals the shortest epoch, so the seed for epoch n+1 is known only as epoch n starts, which is the compile-ahead window the agent must measure per card (the 5090 compiled in 1,285 ms, the 9070 XT unmeasured). A pool's `job` cadence and the dev-fee counter are unaffected | The epoch-length document carries a per-tier row: rig restart cost per epoch length (and the fix: prepare-ahead in the rig scripts, no exit 42 path), the Mac pause share, the compile-ahead margin per card at 600 DAA s against the VDF; the rig installer removes `--exit-on-seed-change` in favour of the prepare path before layer 9 can draw a short epoch | ca2-epoch a32a3ece66c02417a; rig installer | closed with a correction (ca2-epoch 4300608, epoch-length.md sections 6.3, 7, 9): the rig scripts pass --prepare-packs as well, so a worker with prepare support swaps in place and exit 42 is the fallback on a prepare MISS (the loaded iGPU missed 2 of 10, M11), not a restart every epoch; the risk at 600 s is one restart plus export plus inline compile per missed boundary; the Mac race pause is 6.3% of a 600-s epoch (race default off); the program is known a full epoch ahead at every length (lead and T_epoch fixed, option A); the 9070 XT compile is OWED (no prepared line from gfx1201 in any upload); the rig installer owes prepare-ahead as the only routine boundary path, the miss causes fixed and a miss counter in the status line | | C21 | OTA K2: apps embed `OTA_PUBLIC_KEYS = [K1, K2]` and honour a signed `revoked_keys` list; the rig installer verifies the manifest with ONE key (`OTA_PUBLIC_KEY_HEX`, install-rig.sh 168, igneum-update.sh 2) and knows no revocation; the HiveOS package verifies nothing (no manifest, the override reaches it only by republish) | ota-k2 c722579; packaging/linux; packaging/hive | rigs (both packages), the seeds (if they take the manifest) | The day K1 is lost or revoked and the manifest is signed with K2, every rig on the installer refuses the manifest, stops taking overrides, and is isolated at the next height switch; a leaked K1 keeps signing for rigs, because they carry no revocation list. HiveOS rigs get neither keys nor revocation: a republished package is their only path, and nothing checks who published it | The rig installer carries both public keys and the `revoked_keys` rule in the same form as the app (keys.md section 4, step 3), installed and read from the manifest; the HiveOS README states that the package is unsigned and names the sha256 the Flight Sheet URL should be checked against; keys.md lists the rig and HiveOS paths in its table of what trusts K1 | OTA key af2bb75a5436324d0 (keys.md, the shared verifier form); rig installer a3e7b2b03222f5cff | in work (OTA agent 00fcbb5 on ota-k2: keys.md section 4 lists every path that trusts K1 with its state and owner and the exact two-key and revocation form for the rig; the HiveOS README says the archive is unsigned and names the per-build sha256 check; the rig verifier itself is with the rig installer; the WALLET (branch wallet-v1, its own copy of manifest.rs) also trusts K1 alone until its owner ports the form, listed in the keys.md table. Merge note for the next cut: packaging/hive/README.md is edited on BOTH ota-k2 00fcbb5 and hive-words 226d3b8, so the integrator takes both paragraphs) | + +Sweep 3 (20:46 Mac clock) notes, no new row: the 9070 XT dropped off PC 1's bus at about 20:40 UTC (the second eGPU fault of the day); the coordinator stated the consequences (G1 on the gfx1036 stand-in, the 9070 XT v3 hash-rate and power rows owed, every earlier 9070 XT row stands, the AMD sweep queue item blocked, nobody woken) in its 21:05 and 21:10 entries and the rollout plan 7b. The epoch-length plan (ca2-epoch 4300608) carries its own per-tier table (section 7), including the node tier (one core 100% busy on the VDF at the 600-s floor) and the chain (24% of blocks in difficulty settle at the floor). The proving agent's 440fd59 rewrote the litepaper's two proving-gate sentences and evidence rows 15 and 16 on its branch (D2: the project lead approves the draft); the litepaper's card-lifetime sentence is untouched (D3, D4). From ad6c8b4c5249b18a21c330ce4ea384174621357a Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:47:02 +0000 Subject: [PATCH 049/158] Consequences ledger: C7 and C21 closed on the rig side (217d873), C20 confirmed no rig change Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 4e3bd304..55d6df13 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -14,7 +14,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C4 | Host RAM 25,550 MB used of 63,132 on PC 2 with the prover on; the WSL2 VM working set 7,915 MB | bench-log "proving v1", step 1 host RAM row | Windows home miners with 16 GB RAM (the common gaming PC); Macs with 16 GB switching the CPU prover on | The prover default has no RAM floor. On Windows the WSL2 VM alone holds 7.9 GB beside the app, the node and the game-class desktop; a 16 GB machine with proving on by default swaps or kills the node. The app's own RSS (engine, node, verifier) is not separated in the measurement, so no requirement can be stated yet | The sweep job records the app's and the node's working sets beside the VM's; `provedefault` reads total RAM and stays off under 32 GB on Windows until measured; the Proving tile and the miner page state the RAM requirement | proving v1; miner UI adbf58b058186a18b (the tile line) | closed in code (proving v1 c2544be: MIN_RAM_MB_WINDOWS 31,000 in provedefault.rs, the tile line names the 7.9 GB VM on a 63 GB PC; unknown RAM is not a gate; the miner UI help line next cut) | | C5 | The app quit for the 0.3.10 update at 20:01:09Z and aborted the chain job at block 3 ("aborted (the app is quitting)"); `prover.rs` kills the child on quit | bench-log "proving v1" chain run 1; `app/igneum-app/src/prover.rs` 266 to 272 | every prover on every update; with proving v1 the aggregator | Tonight's `update-now` to every app aborts whichever shard each prover has in flight (up to 37 s of work each, no payout, re-assigned to nobody until the window passes). Under proving v1 a restart mid-segment loses the aggregator's chain state: the segment goes unproven after T (600 DAA), the aggregator share of 8 blocks is forfeited to the escrow, and the next record must be fresh-chain. With one aggregator on the devnet every app update costs 10 minutes of unproven segments | The update's safe moment waits for the prover's current proof (as it does for the node); the aggregator persists the last segment proof and resumes the chain after a restart; the Updates section says "waits for the proof in flight". For 0.3.10 tonight the aborted shards are an accepted cost, recorded | proving v1; shipper (tonight's rollout note); miner UI (Updates wording) | refused for tonight by the proving agent (persisting the segment proof and holding the update for a proof in flight are 0.3.12; the cost is in the plan as the rule working as written); the shipper carries the aborted-shard count in release-0.3.10.md section 8; the 20:01:09Z abort was NOT 0.3.10 (shipper: nothing published), see C16 | | C6 | The prover costs a mining 5090 4.0% (124.7 to 119.7 MH/s); the software dev fee is 1 template in 100 | bench-log "proving v1" step 1; `packaging/hive/README.md` "The dev fee" | pool users; the pool's ledger | A member who proves sends 4% fewer shares, so the pool's vardiff and `stats.hashrate` read a 4% loss while the proving income (90% of the shard credit plus a tenth to an aggregator) is paid to the member's own key and never appears in the pool's ledger: the pool dashboard understates a proving member's earnings. The software dev fee has no mechanism in pool mode (the pool issues the templates), so a pooled miner pays no dev fee today and the pool design must say whether it takes one (1 share in 100 to the dev address) or none | The pool-v0 design states both: the member `stats` carry a `proving` flag and the pool page shows proving income beside shares; the dev fee rule in pool mode is written down before the first pool ships | pool a4781ba117326091f | sent | -| C7 | The HiveOS package holds `igneumd`, `igneum-miner` and the two workers; no `igneum-prove-host`, no SP1 GPU server, no per-card rule | `packaging/hive/make-hive-package.sh` 26 to 30; README "What the hooks do" | rigs (HiveOS, Linux), the largest hashrate tier | A rig cannot prove at all, so the 20% proving share is reachable only from the app. The miner page says "the card mines and proves" (`site/miner.html` 7, `site/index.html` 484) and the HiveOS README does not say rigs mine only. A rig that could prove needs the 251 MB SP1 GPU server, CUDA 12.8 and the per-card profile of C2 and C3, and a rig's RAM (4 to 8 GB on most Hive images, approximate) is below C4's floor | The rig installer either carries the prover with the per-card rule and a RAM check, or its README and the miners page say rigs mine only and provers are app machines; `IDENTITIES=8 for a big card, 2 for a small one` gets a threshold in GB | rig installer a3e7b2b03222f5cff | closed in part (rig installer 189ab32 with the sweep's gates; HiveOS sub-agent 226d3b8 then 2d056e8 on branch hive-words: the README says a rig mines only, with the per-card table re-cut from the S_p curve (8 and 12 GB mine only, 16 GB in practice mines only, 24 GB proves the adopted shard from DAA 210,000, 32 GB mines and proves) and its sources, IDENTITIES=auto resolved per card by VRAM, selftest green; open: the miners page sentence D8, and provedefault.rs's own doc table still says "24 GB mines and proves" without the shard-size bound, with the proving agent) | +| C7 | The HiveOS package holds `igneumd`, `igneum-miner` and the two workers; no `igneum-prove-host`, no SP1 GPU server, no per-card rule | `packaging/hive/make-hive-package.sh` 26 to 30; README "What the hooks do" | rigs (HiveOS, Linux), the largest hashrate tier | A rig cannot prove at all, so the 20% proving share is reachable only from the app. The miner page says "the card mines and proves" (`site/miner.html` 7, `site/index.html` 484) and the HiveOS README does not say rigs mine only. A rig that could prove needs the 251 MB SP1 GPU server, CUDA 12.8 and the per-card profile of C2 and C3, and a rig's RAM (4 to 8 GB on most Hive images, approximate) is below C4's floor | The rig installer either carries the prover with the per-card rule and a RAM check, or its README and the miners page say rigs mine only and provers are app machines; `IDENTITIES=8 for a big card, 2 for a small one` gets a threshold in GB | rig installer a3e7b2b03222f5cff | closed (rig installer 88f31d9: gates 23,552 MB for both roles from provedefault.rs 440fd59, the README table per tier; HiveOS hive-words 2d056e8: the same table; open only the miners page sentence, D8) | | C8 | Dataset 2 GiB at genesis plus 0.5 GiB a year; the working-set rule "under 6 GB on an 8 GB card"; the cache 256 MiB doubling at years 4 and 12; scratch up to 128 KiB per resident warp | spec 01 section 1.13.3; coordinator's budget rule; layer 6 option C; `docs/plans/hot-table.md` section 3 | 4 GB and 8 GB cards; the litepaper's claim | `site/index.html` 461 says "Any 4 GB card" and the litepaper (560) says a 4 GB card mines for about four years and an 8 GB card for more than a decade. At 75% of the card the 4 GB card's dataset room is about 2.4 GiB: under one year. The 8 GB card's room is about 5 GiB after cache, hot table, scratch and buffers: about six years, five and a half with the year-4 cache step. Neither public sentence holds under the schedule | A card-lifetime table per tier (sub-agent, `docs/analysis/card-lifetime-2026-10-05.md`); the public wording is a claim for the project lead (D3) | sub-agent (table); decision (wording) | table landed (sub-agent, docs/analysis/card-lifetime-2026-10-05.md, 1fecfe2, merged into ca2-coord at 22:50): 4 GB 1.0 to 1.5 years under (a) and year 4 under (b); 8 GB 6.3 to 7.5 or 12; 12 GB 12 to 13.5 or 12 to 28 depending on whether the cache stays resident; Apple 8 GB 2.6 to 3.4 or 4. The coordinator decided the cache is freed after the daily build and recommends (b) to the project lead; the public sentences hold only under (b): D3 and D4 revised | | C9 | Index mapping option (a) multiply-shift (fades cards) against (b) power-of-two steps 2, 4, 8 GiB | spec 01 section 1.13.3, gate 1 | 8 GB and 12 GB cards | Option (b)'s 8 GiB step (about year 12) ends 8 GB and 12 GB cards on the same day; option (a) fades them one year at a time. The choice is a genesis parameter and a tier consequence nobody has put beside the options | Decision request D4 with the lifetime table of C8 | decision | open | | C10 | The on-die-cache recompute chip's gain is 2.4x at every scratch share under the 6 GB cap; the mixer multiplier x2 brings it to 1.2x, x4 to 0.6x, inside the 10 ms verify gate | `docs/analysis/scratch-soundness.md` finding 2 and section 10; M16 analysis table | chip builders; the site's "under 2x" claim; pool share verification | Layer 3 does not deliver the headline and the rollout plan's own rule (6a) says the public claim is qualified when no share gets the chip under 2x. The lever that does is M16's mixer multiplier, which doubles the CPU verify per warp (0.441 ms to about 0.9 ms at x2): a pool core verifies about 11,000 members' shares instead of 22,000, and node block verification doubles. The corrected SRAM cost ($19 to $37 of silicon per mirror die) means the mirror never stops a funded chip; the cache schedule keeps it above GPU L2 only | The coordinator either carries the mixer x2 into class v3 for the devnet (it is a lottery-hash change like the others; the verify cost per warp is measured with it) or marks the site's "under 2x" as qualified in the public copy level 3 and D5 asks the project lead which | coordinator ada8afb62d752b1e2 | closed as a consequence (coordinator: the public claim was qualified at 21:50; at 22:00 the M16 mixer x4 was decided into class v3 behind the same activation; the pool-core and node verification consequences are C14) | @@ -32,7 +32,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- |---|---|---|---|---|---|---|---| | C18 | 5090 0.072 against 9070 XT 0.032 MH/s per pound at list prices (2.2x), 4.9x per watt | read-width.md 4.1 | AMD home miners; the public level 3 copy | The coordinator's 22:30 status entry says "near parity per pound"; the table it cites says 2.2x. The level 3 page is written from the status | The status and level 3 carry the table's figure (2.2x per pound, 4.9x per watt, 7.5x in rate); "near parity" is struck | coordinator | closed (coordinator 6b07776: the status, the rollout plan and the public copy carry 2.2x per pound, 4.9x per watt, 7.5x in rate) | | C19 | Mixer x4 into class v3: CPU verify 1.6 to 4.8 ms per warp against 0.604 today; pruning depth 108,000 DAA s (spec 02) | status 22:00; M16 table; spec 02 line 17 | every node (the three testnet seeds on small Hetzner VMs, the observer, a laptop node); IBD; pools | A new node verifies every header in the pruning window on one core: 108,000 x 4.8 ms = 8.6 min at x4 against 1.1 min today (a 30-s block-time budget at 1 block/s is unaffected: 4.8 ms per block is 0.5% of a core). Every miner's own `cpu re-check` of a found hash and every pool share verification cost the same 8x; the 10 ms gate (ledger M9) keeps 2x of margin at the slow end, which is what Counter ASIC 3.0 has left to spend. On the 2019-class laptop core the evidence table names (rule 3) the figure is unmeasured and may pass 10 ms | The ca2-mixer document carries: ms per warp on the M5 Max core AND a scaled 2019-class figure (marked approximate), the pruning-window IBD minutes per tier, pool shares per core per second, and the gate margin left for 3.0; the seeds' header-verify load is checked in the testnet go checklist | ca2-mixer af345b1e2c541ffbb; coordinator | in work (coordinator: with ca2-mixer; the seeds' header-verify load into testnet-go.md) | -| C20 | Layer 9: the epoch length as an era parameter, 600 to 7,200 DAA s (10 min to 2 h), base 3,600 | status 22:30 and 23:00 | rigs (HiveOS and the rig installer), Macs, pools, the seed path | Both rig miners run `--exit-on-seed-change` and re-export the pack on exit 42 (h-run.sh 56 to 61, igneum-miner.sh 67 to 78): at a 10-minute epoch every card's miner restarts six times an hour with a pack export each time, and the restart gap is lost hashing; the app's prepare-ahead path does not restart. The Mac fleet's prepare pause (35 s an hour at one epoch an hour, bench-log M11) becomes 3.5 min an hour, 6%. The 10-minute seed VDF (spec 04) equals the shortest epoch, so the seed for epoch n+1 is known only as epoch n starts, which is the compile-ahead window the agent must measure per card (the 5090 compiled in 1,285 ms, the 9070 XT unmeasured). A pool's `job` cadence and the dev-fee counter are unaffected | The epoch-length document carries a per-tier row: rig restart cost per epoch length (and the fix: prepare-ahead in the rig scripts, no exit 42 path), the Mac pause share, the compile-ahead margin per card at 600 DAA s against the VDF; the rig installer removes `--exit-on-seed-change` in favour of the prepare path before layer 9 can draw a short epoch | ca2-epoch a32a3ece66c02417a; rig installer | closed with a correction (ca2-epoch 4300608, epoch-length.md sections 6.3, 7, 9): the rig scripts pass --prepare-packs as well, so a worker with prepare support swaps in place and exit 42 is the fallback on a prepare MISS (the loaded iGPU missed 2 of 10, M11), not a restart every epoch; the risk at 600 s is one restart plus export plus inline compile per missed boundary; the Mac race pause is 6.3% of a 600-s epoch (race default off); the program is known a full epoch ahead at every length (lead and T_epoch fixed, option A); the 9070 XT compile is OWED (no prepared line from gfx1201 in any upload); the rig installer owes prepare-ahead as the only routine boundary path, the miss causes fixed and a miss counter in the status line | -| C21 | OTA K2: apps embed `OTA_PUBLIC_KEYS = [K1, K2]` and honour a signed `revoked_keys` list; the rig installer verifies the manifest with ONE key (`OTA_PUBLIC_KEY_HEX`, install-rig.sh 168, igneum-update.sh 2) and knows no revocation; the HiveOS package verifies nothing (no manifest, the override reaches it only by republish) | ota-k2 c722579; packaging/linux; packaging/hive | rigs (both packages), the seeds (if they take the manifest) | The day K1 is lost or revoked and the manifest is signed with K2, every rig on the installer refuses the manifest, stops taking overrides, and is isolated at the next height switch; a leaked K1 keeps signing for rigs, because they carry no revocation list. HiveOS rigs get neither keys nor revocation: a republished package is their only path, and nothing checks who published it | The rig installer carries both public keys and the `revoked_keys` rule in the same form as the app (keys.md section 4, step 3), installed and read from the manifest; the HiveOS README states that the package is unsigned and names the sha256 the Flight Sheet URL should be checked against; keys.md lists the rig and HiveOS paths in its table of what trusts K1 | OTA key af2bb75a5436324d0 (keys.md, the shared verifier form); rig installer a3e7b2b03222f5cff | in work (OTA agent 00fcbb5 on ota-k2: keys.md section 4 lists every path that trusts K1 with its state and owner and the exact two-key and revocation form for the rig; the HiveOS README says the archive is unsigned and names the per-build sha256 check; the rig verifier itself is with the rig installer; the WALLET (branch wallet-v1, its own copy of manifest.rs) also trusts K1 alone until its owner ports the form, listed in the keys.md table. Merge note for the next cut: packaging/hive/README.md is edited on BOTH ota-k2 00fcbb5 and hive-words 226d3b8, so the integrator takes both paragraphs) | +| C20 | Layer 9: the epoch length as an era parameter, 600 to 7,200 DAA s (10 min to 2 h), base 3,600 | status 22:30 and 23:00 | rigs (HiveOS and the rig installer), Macs, pools, the seed path | Both rig miners run `--exit-on-seed-change` and re-export the pack on exit 42 (h-run.sh 56 to 61, igneum-miner.sh 67 to 78): at a 10-minute epoch every card's miner restarts six times an hour with a pack export each time, and the restart gap is lost hashing; the app's prepare-ahead path does not restart. The Mac fleet's prepare pause (35 s an hour at one epoch an hour, bench-log M11) becomes 3.5 min an hour, 6%. The 10-minute seed VDF (spec 04) equals the shortest epoch, so the seed for epoch n+1 is known only as epoch n starts, which is the compile-ahead window the agent must measure per card (the 5090 compiled in 1,285 ms, the 9070 XT unmeasured). A pool's `job` cadence and the dev-fee counter are unaffected | The epoch-length document carries a per-tier row: rig restart cost per epoch length (and the fix: prepare-ahead in the rig scripts, no exit 42 path), the Mac pause share, the compile-ahead margin per card at 600 DAA s against the VDF; the rig installer removes `--exit-on-seed-change` in favour of the prepare path before layer 9 can draw a short epoch | ca2-epoch a32a3ece66c02417a; rig installer | closed with a correction (ca2-epoch 4300608, epoch-length.md sections 6.3, 7, 9): the rig scripts pass --prepare-packs as well, so a worker with prepare support swaps in place and exit 42 is the fallback on a prepare MISS (the loaded iGPU missed 2 of 10, M11), not a restart every epoch; the risk at 600 s is one restart plus export plus inline compile per missed boundary; the Mac race pause is 6.3% of a 600-s epoch (race default off); the program is known a full epoch ahead at every length (lead and T_epoch fixed, option A); the 9070 XT compile is OWED (no prepared line from gfx1201 in any upload); the rig installer (88f31d9) confirms the rig already takes the prepare path: exit 42 fires only when a worker's ready line lacks "prepare 1", and both shipped workers answer it; documented in its README, no code change | +| C21 | OTA K2: apps embed `OTA_PUBLIC_KEYS = [K1, K2]` and honour a signed `revoked_keys` list; the rig installer verifies the manifest with ONE key (`OTA_PUBLIC_KEY_HEX`, install-rig.sh 168, igneum-update.sh 2) and knows no revocation; the HiveOS package verifies nothing (no manifest, the override reaches it only by republish) | ota-k2 c722579; packaging/linux; packaging/hive | rigs (both packages), the seeds (if they take the manifest) | The day K1 is lost or revoked and the manifest is signed with K2, every rig on the installer refuses the manifest, stops taking overrides, and is isolated at the next height switch; a leaked K1 keeps signing for rigs, because they carry no revocation list. HiveOS rigs get neither keys nor revocation: a republished package is their only path, and nothing checks who published it | The rig installer carries both public keys and the `revoked_keys` rule in the same form as the app (keys.md section 4, step 3), installed and read from the manifest; the HiveOS README states that the package is unsigned and names the sha256 the Flight Sheet URL should be checked against; keys.md lists the rig and HiveOS paths in its table of what trusts K1 | OTA key af2bb75a5436324d0 (keys.md, the shared verifier form); rig installer a3e7b2b03222f5cff | closed for the rig and the docs (rig installer 88f31d9: OTA_PUBLIC_KEYS [K1, K2 slot] embedded, manifest_check mirrors the app's manifest::check with the revoked_keys record at /var/lib/igneum/updates/revoked.json, tested on three throwaway keys; OTA agent 00fcbb5: keys.md table of every path that trusts K1, the HiveOS README unsigned-archive note); open: the wallet (wallet-v1) still trusts K1 alone, listed in keys.md for its owner; merge note: packaging/hive/README.md is edited on both ota-k2 and hive-words | Sweep 3 (20:46 Mac clock) notes, no new row: the 9070 XT dropped off PC 1's bus at about 20:40 UTC (the second eGPU fault of the day); the coordinator stated the consequences (G1 on the gfx1036 stand-in, the 9070 XT v3 hash-rate and power rows owed, every earlier 9070 XT row stands, the AMD sweep queue item blocked, nobody woken) in its 21:05 and 21:10 entries and the rollout plan 7b. The epoch-length plan (ca2-epoch 4300608) carries its own per-tier table (section 7), including the node tier (one core 100% busy on the VDF at the 600-s floor) and the chain (24% of blocks in difficulty settle at the floor). The proving agent's 440fd59 rewrote the litepaper's two proving-gate sentences and evidence rows 15 and 16 on its branch (D2: the project lead approves the draft); the litepaper's card-lifetime sentence is untouched (D3, D4). From 1eaeefd770a60ab6c45a4c40c0f588225b303286 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:49:26 +0000 Subject: [PATCH 050/158] Proving v1: the S_p curve's re-plan points (28.4 GB at 20 M cycles: the peak is flat from 20 M to 60 M) Co-Authored-By: Claude Fable 5.1 --- docs/bench-log.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/bench-log.md b/docs/bench-log.md index df0f3635..6eec6de5 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -1682,6 +1682,8 @@ Job `spcurve-stopped-pc2-pv1` (`tools/proving-v1/pc2-sp-curve.ps1`, the miners s | the same | 6.75 M | 21,611 | 60.4 M | 22,963 | 11.5 | ELEMENT_THRESHOLD 2^25, HEIGHT 2^20 | | block 344 shard 0 (the fixture's own cut, 6.75 M pgas, modexp) | 6,748,392 | 18,535 | 59,678,420 | 28,275 and 28,307 | 11.5 and 11.0 | none | +The second job (`spcurve-stopped-pc2-pv1b`, the pv1b host whose `--budget` re-plans a fixture, 20:43 to 20:47Z, the same conditions) repeats the points (empty 13,875 MiB 2.1 s; one transfer 13,907 MiB 3.3 s; the v1 shard 20,435 MiB 4.2 s; the prototype shard 28,275 MiB 11.2 s) and adds the re-plans of block 344 (27 M pgas of modexp): at 2.25 M pgas (one transaction, 16 shards a block, 19,987,938 cycles) **28,371 MiB** and 6.6 s; at 4.5 M pgas (7 shards a block, 40,011,108 cycles) 28,307 MiB and 8.5 s; with the 2^25 trace threshold the 2.25 M shard 22,835 MiB and 6.2 s. So the peak is flat at 28.3 GB from 20 M cycles to 60 M (the server's buffers step up between 4.7 M and 20 M cycles and not after), and cutting the prototype shard smaller buys nothing until the v1 size. + Reading, with the miner-on pairs above (empty shard 15,670 MiB, full prototype shard 30,039 MiB). The witness is never the binding term (4.9 to 21.6 KB a shard); the GPU server's working set is: a floor of 13.9 GB for any shard, 20.4 GB at 4.7 M cycles, 28.3 GB at 60 M cycles (23.0 GB with the smallest trace threshold, at the same time). By card: a **12 GB card proves nothing** on this build (the floor is 13.9 GB alone); a **16 GB card proves only empty and near-empty shards, alone** (13.9 GB; 15.7 GB beside the miner leaves nothing for the display); a **24 GB card proves the adopted v1 shard alone** (20.4 GB) and, at the miner's measured 1.7 GB extra, about 22.1 GB beside it (approximate: not measured on a 24 GB card), and never the prototype shard (28.3 GB); a **32 GB card proves the prototype shard beside the miner with 2.5 GB spare** (30.0 of 32.6 GB). The devnet is on the prototype table until H = 210,000 (6 October, about 19:50Z) and on the adopted v1 table (`S_p` 30,000 pgas) after it, so from H the 24 GB tier joins the provers and the shard that binds the memory is the 4.7 M-cycle one. Shards per block at each size: 1 at the prototype `S_p`, 4 at `B_p`; at the v1 budget 1 to 4 (one a block on tonight's chain, 2 to 3 on the txgen blocks). ### Step 4, the rule From 06dcb310fb5b190e726dbf1e584c94a60379cc4e Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:49:45 +0000 Subject: [PATCH 051/158] read-width: per-watt and per-pound rows (telemetry watts, list prices approximate) and the AMD consequence (C11); packbench prints Metal's currentAllocatedSize and recommendedMaxWorkingSetSize (C12) Co-Authored-By: Claude Fable 5.1 --- docs/plans/read-width.md | 17 +++++++++++++++++ proto-metal/packbench.swift | 5 ++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/plans/read-width.md b/docs/plans/read-width.md index 60a4d3da..b9c04b2e 100644 --- a/docs/plans/read-width.md +++ b/docs/plans/read-width.md @@ -51,6 +51,23 @@ Probe ceilings at 1024 MiB (G dependent reads/s): RTX 5090 4 B 17.5, 16 B 18.0, | mix50-35-15 (6 programs, min / median / max) | 1,664 to 3,680 | 99.5 / 114.2 / 121.0, spread 18.8% | 17.45 / 18.76 / 18.83, 7.4% | 25.36 / 27.26 / 28.43, 11.3% | 6.1x | 5,939 / 8,192 expected | 0.620 | | mix25-50-25 (6 programs) | 2,240 to 5,024 | 95.9 / 107.3 / 119.8, 22.3% | 17.84 / 18.45 / 18.85, 5.5% | 23.21 / 24.68 / 25.21, 8.1% | 5.8x | 7,168 / 8,192 expected | 0.614 | +### 4.1 Per watt and per pound (consequences review C11) + +The runs carried no power sampling; the watts are the telemetry entry's (`docs/bench-log.md`, opencl-rdna4-telemetry, 5 October 2026: the RTX 5090 at 307.6 W under its 450 W cap for 122.3 MH/s, the RX 9070 XT at 199 W of its 304 W rating for about 17.8 MH/s, both on v2 with the shader clock at its top and the die waiting on memory), held constant across classes because every class is memory-bound on both cards (approximate: a class that moves more bytes per hash draws somewhat more at the memory controller, unmeasured). The Mac's GPU power is not measurable without root (`powermetrics`) and is taken as about 50 W (approximate, from memory). Prices are UK list, approximate, from memory. + +| Class | RTX 5090 MH/W (at 307.6 W) | RX 9070 XT MH/W (at 199 W) | 5090 / 9070 per watt | M5 Max MH/W (at about 50 W GPU, approximate) | 5090 MH per pound (at about 1,900, approximate) | 9070 XT MH per pound (at about 570, approximate) | +|---|---|---|---|---|---|---| +| v2 (w4) | 0.442 | 0.091 | 4.9x | 0.55 | 0.072 | 0.032 | +| w16 | 0.454 | 0.090 | 5.1x | 0.57 | 0.074 | 0.031 | +| w64 | 0.234 | 0.088 | 2.6x | 0.57 | 0.038 | 0.031 | +| w64x4 | 0.895 | 0.378 | 2.4x | 2.19 | 0.145 | 0.132 | +| mix50-35-15 (median) | 0.371 | 0.094 | 3.9x | 0.55 | 0.060 | 0.033 | +| mix25-50-25 (median) | 0.349 | 0.093 | 3.8x | 0.49 | 0.056 | 0.032 | +| scr8k32 | 0.397 | 0.071 | 5.6x | 0.98 | 0.064 | 0.025 | +| scr2k32 | 0.372 | 0.074 | 5.1x | 0.52 | 0.060 | 0.026 | + +Reading: whatever width is chosen, an AMD home miner keeps about a seventh of a 5090's rate and pays about 4.5x the electricity per hash, because every width costs the 9070 XT the same 2.4 G line fetches a second; per pound of card the 5090 is 2.2x the 9070 XT at v2 and w16 (0.072 against 0.032 MH/s per pound) and 4.9x per watt; only w64x4 narrows the per-pound gap (0.145 against 0.132), and that class fails the width rule. The consequence for the decision (D6, the project lead's): AMD's line width is not a read-width question at all; it is the card's random-access rate, and the levers that act on it (the 64 MB Infinity Cache against the dataset size, the memory path) are v3-or-3.0 questions outside this experiment. + Scratch, variant 5 (N persistent warps; GPU cost against the persistent control scr0k32; working set = 1 GiB + 256 MiB + 128 MiB output + N x size): | Class | RMW share | dataset B/hash | scratch B/hash read + written | RTX 5090 MH/s, 2,048 warps of 4,080 resident (vs control, share) | M5 Max Metal (vs control) | RX 9070 XT, 4,096 warps | working set 5090 / 9070 / Mac | diff --git a/proto-metal/packbench.swift b/proto-metal/packbench.swift index 3110bbe1..c708f5c2 100644 --- a/proto-metal/packbench.swift +++ b/proto-metal/packbench.swift @@ -204,6 +204,9 @@ print("pack \(packName) seed \"\(seedString)\" id \(programId) class \(className print("device \(device.name); compile \(String(format: "%.0f", compileMs)) ms; cache fill \(String(format: "%.1f", cacheGpu)) ms GPU (\(String(format: "%.1f", cacheWall)) wall); dataset build \(String(format: "%.1f", buildGpu)) ms GPU (\(String(format: "%.1f", buildWall)) wall)") print("cache FNV-1a 64 \(String(format: "%016llx", cacheFnv)) \(cacheOk ? "PASS" : "FAIL"); dataset head and last \(dsOk ? "PASS" : "FAIL"); vectors standalone \(vecPass)/\(vecBases.count), in batch \(batchVecPass)/\(batchVecN)") print("warm-up batch \(nonces) hashes: \(String(format: "%.1f", warmGpu)) ms GPU, \(String(format: "%.1f", warmWall)) ms wall") +let footprintMiB = device.currentAllocatedSize / 1048576 +let recommendedMiB = device.recommendedMaxWorkingSetSize / 1048576 +print("resident footprint after the timed batches: currentAllocatedSize \(footprintMiB) MiB (recommendedMaxWorkingSetSize \(recommendedMiB) MiB)") let overall = cacheOk && dsOk && vecPass == vecBases.count && batchVecPass == batchVecN -print("RESULT pack=\(packName) class=\(className) device=\(device.name.replacingOccurrences(of: " ", with: "_")) group=\(opts.group) warps=\(warpsN) arena_mib=\(persistent ? warpsN * 32 * scratchWordsPerLane * 4 / 1048576 : 0) nonces=\(nonces) batches=\(opts.batches) vectors=\(vecPass)/\(vecBases.count) batch_vectors=\(batchVecPass)/\(batchVecN) cache=\(cacheOk ? "PASS" : "FAIL") dataset=\(dsOk ? "PASS" : "FAIL") fingerprint=\(String(format: "%016llx", fingerprint)) mhs_gpu=\(String(format: "%.3f", mhsGpu)) mhs_wall=\(String(format: "%.3f", mhsWall)) loads=\(loadsPerHash) bytes=\(bytesPerHash) scratch_ops=\(scratchOps * 8) overall=\(overall ? "PASS" : "FAIL")") +print("RESULT pack=\(packName) class=\(className) device=\(device.name.replacingOccurrences(of: " ", with: "_")) group=\(opts.group) warps=\(warpsN) arena_mib=\(persistent ? warpsN * 32 * scratchWordsPerLane * 4 / 1048576 : 0) nonces=\(nonces) batches=\(opts.batches) vectors=\(vecPass)/\(vecBases.count) batch_vectors=\(batchVecPass)/\(batchVecN) cache=\(cacheOk ? "PASS" : "FAIL") dataset=\(dsOk ? "PASS" : "FAIL") fingerprint=\(String(format: "%016llx", fingerprint)) mhs_gpu=\(String(format: "%.3f", mhsGpu)) mhs_wall=\(String(format: "%.3f", mhsWall)) loads=\(loadsPerHash) bytes=\(bytesPerHash) footprint_mib=\(footprintMiB) recommended_mib=\(recommendedMiB) scratch_ops=\(scratchOps * 8) overall=\(overall ? "PASS" : "FAIL")") exit(overall ? 0 : 1) From ad2c0b566bf2df5494bb956a54448bc429794b9d Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:50:05 +0000 Subject: [PATCH 052/158] Consequences ledger: C12 the Apple footprint arithmetic and the harness fields (read-width 46dcc3e) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 55d6df13..9c9d4190 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -19,7 +19,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C9 | Index mapping option (a) multiply-shift (fades cards) against (b) power-of-two steps 2, 4, 8 GiB | spec 01 section 1.13.3, gate 1 | 8 GB and 12 GB cards | Option (b)'s 8 GiB step (about year 12) ends 8 GB and 12 GB cards on the same day; option (a) fades them one year at a time. The choice is a genesis parameter and a tier consequence nobody has put beside the options | Decision request D4 with the lifetime table of C8 | decision | open | | C10 | The on-die-cache recompute chip's gain is 2.4x at every scratch share under the 6 GB cap; the mixer multiplier x2 brings it to 1.2x, x4 to 0.6x, inside the 10 ms verify gate | `docs/analysis/scratch-soundness.md` finding 2 and section 10; M16 analysis table | chip builders; the site's "under 2x" claim; pool share verification | Layer 3 does not deliver the headline and the rollout plan's own rule (6a) says the public claim is qualified when no share gets the chip under 2x. The lever that does is M16's mixer multiplier, which doubles the CPU verify per warp (0.441 ms to about 0.9 ms at x2): a pool core verifies about 11,000 members' shares instead of 22,000, and node block verification doubles. The corrected SRAM cost ($19 to $37 of silicon per mirror die) means the mirror never stops a funded chip; the cache schedule keeps it above GPU L2 only | The coordinator either carries the mixer x2 into class v3 for the devnet (it is a lottery-hash change like the others; the verify cost per warp is measured with it) or marks the site's "under 2x" as qualified in the public copy level 3 and D5 asks the project lead which | coordinator ada8afb62d752b1e2 | closed as a consequence (coordinator: the public claim was qualified at 21:50; at 22:00 the M16 mixer x4 was decided into class v3 behind the same activation; the pool-core and node verification consequences are C14) | | C11 | RX 9070 XT 17.73 MH/s at 198.9 W (0.089 MH/W) against the RTX 5090 122.30 MH/s at 307.6 W (0.398 MH/W); every read width costs the 9070 XT the same 2.4 G line fetches a second | bench-log AMD telemetry entry; readwidth probe ceilings (status 20:35) | AMD home miners; the "three vendors" copy | Under the "widest latency-bound read" rule w16 moves bytes per hash, not loads per hash, so the AMD card keeps about a seventh of the 5090's hash rate and pays 4.5x the electricity per hash; at a UK tariff of 25 p/kWh (approximate) the 9070 XT spends 4.5x the 5090's pence per IGN. The readwidth decision table carries MH/s only; no MH/W or MH per pound per card per class, and the public copy implies vendor parity | The readwidth table adds MH/W (and MH per pound at list prices, approximate) per card per class; the v3 decision names the AMD consequence; whether the class should favour fewer loads per hash for AMD's 64-byte lines is a consensus choice for the project lead (D6) | read-width a451c9935bfb1bc19; coordinator; decision | closed (read-width e752fc7 section 4.1: MH/W and MH per pound per class per card; the AMD gap is the card's random-access rate, no width closes it; the coordinator's 22:30 entry says "near parity per pound" where the table says the 5090 is 2.2x per pound: C18) | -| C12 | The v3 working set on Apple: 1,568 to 1,760 MiB today, 2,592 to 2,784 MiB at the 2 GiB genesis dataset, in unified memory shared with macOS; 2,048 launched warps x 128 KiB scratch | `docs/plans/hot-table.md` section 3 M5 Max row; era-layout section 3 | Apple silicon with 8 GB and 16 GB (the base Mac mini, MacBook Air) | An 8 GB Mac holds the miner's 2.6 GB beside macOS's 3 to 4 GB: it mines today and swaps at the first dataset growth step. The app has no gate on Metal's `recommendedMaxWorkingSetSize`; the site's "Any 4 GB card" has no Apple line. The CPU prover's RAM on a 16 GB Mac is unmeasured (the Settings switch lets it on) | The app reads `recommendedMaxWorkingSetSize` and refuses to mine (with the reason on the Mine tile) when the working set exceeds it; the site says "Mac: 8 GB or more"; the Apple scratch row at 128 KiB is measured in the readwidth table, not launched at 2,048 by assumption | miner UI adbf58b058186a18b; read-width (the Apple row) | in work (read-width: the scratch row's working set per card measured, Mac 1.5 to 1.9 GiB; the Metal footprint runs held the measure lock at 22:3x; miner UI: mining.gate_reason next cut) | +| C12 | The v3 working set on Apple: 1,568 to 1,760 MiB today, 2,592 to 2,784 MiB at the 2 GiB genesis dataset, in unified memory shared with macOS; 2,048 launched warps x 128 KiB scratch | `docs/plans/hot-table.md` section 3 M5 Max row; era-layout section 3 | Apple silicon with 8 GB and 16 GB (the base Mac mini, MacBook Air) | An 8 GB Mac holds the miner's 2.6 GB beside macOS's 3 to 4 GB: it mines today and swaps at the first dataset growth step. The app has no gate on Metal's `recommendedMaxWorkingSetSize`; the site's "Any 4 GB card" has no Apple line. The CPU prover's RAM on a 16 GB Mac is unmeasured (the Settings switch lets it on) | The app reads `recommendedMaxWorkingSetSize` and refuses to mine (with the reason on the Mine tile) when the working set exceeds it; the site says "Mac: 8 GB or more"; the Apple scratch row at 128 KiB is measured in the readwidth table, not launched at 2,048 by assumption | miner UI adbf58b058186a18b; read-width (the Apple row) | in work (read-width 46dcc3e: the Apple scratch footprint by arithmetic is 1.4 GiB at 2,048 x 32 KiB and 1.6 GiB at 2,048 x 128 KiB, 2.4 GiB at 4,096 x 128 KiB; the Metal harness now prints currentAllocatedSize and recommendedMaxWorkingSetSize in its RESULT line, the measured row waits for the Mac measure lock, held by a prover measurement since 20:31Z; the miner UI gates on the arithmetic plus its output buffer until then, mining.gate_reason next cut) | | C13 | `/api/supply` `max_supply_ign` 4,000,000,000 against `minted_at_end_ign` about 3,963,000,000 (the ramp withholds about 37 M, the floors the rest); the live tables lack `number`, `tx_count`, `detail` until the observer restarts | `site/api/supply.mjs` 31 to 48; `docs/plans/explorer.md` "Open" | everyone who reads the explorer beside the homepage tile "4B IGN hard cap, ever" (`site/index.html` 386) | The tile says 4 billion and the explorer page says "of 4,000,000,000 by the rule" while the API's own end figure is 3.963 billion: a reader who adds the two columns finds 37 million missing. The explorer page shows "Circulating 0 IGN" on the devnet deployment until the observer restarts on the new code | The tile, the litepaper's supply line and the explorer carry "cap 4,000,000,000; about 3.96 billion ever minted" (the litepaper already says "approached and never reached"); the explorer does not go live before the observer restart lands the columns | explorer a76f60b415859b7b5; wording: the project lead (D7, with D3) | closed (explorer 3e01212: the tile reads "cap 4,000,000,000, 3.96 billion ever minted, x% so far" with the withheld figure on hover; the homepage tile and litepaper wording stay with the project lead, D3 and D7; the zero-circulating premise was sharper than stated, a 42703 failure not a null, now a 503 with the reason, and moot: the live observer restarted on the explorer code at 19:42:50Z, so the live tables carry the columns) | | C14 | The M16 mixer x4 decided into class v3 at 22:00 (coordinator): verifier 1.6 to 4.8 ms per warp against 0.441 ms today (0.87 cold), measurement running on branch ca2-mixer | coordinator's reply 21:5x; `docs/analysis/m16-recompute-attacker-2026-10-05.md` table; spec 09 section 9.8 item 5 | pool operators; every node (the Hetzner seeds, the observer, a 2019-class laptop); the 10 ms verify gate | At x4 one pool core verifies 200 to 600 shares a second instead of 2,270, so one core covers 2,000 to 6,000 members at one share per 10 s instead of 22,000; a block's CPU re-check and the miner's own `cpu re-check` of every found hash cost 4 to 11x; the seeds' small VMs verify every header at that cost; the gate's margin falls from 23x to 2 to 6x, which bounds Counter ASIC 3.0's room | The coordinator is adding the numbers to the ca2-mixer document and the status (said in reply); the reviewer checks at the next sweep that the pool-members-per-core and the seed-VM header-verify rows are there, and that the spec 09 figure 2,270 shares a second per core is re-cut with v3 | coordinator ada8afb62d752b1e2 | in work | | C15 | A full prototype shard peaks at 28.3 GB on sp1-gpu-server 6.8.1 (the sweep, proving agent 22:0x); an empty one 13.9 GB; no knob moves either floor | proving agent's reply; sweep job `memsweep-pc2-pv1` | 24 GB cards (4090, 7900-class if it had a path); the 5090 that mines and proves; the fleet table | A 24 GB card cannot prove a prototype full shard at all, mining or not; a 5090 mining (3.4 GB resident) plus a full shard is 31.7 GB against 31.8 GB, the edge. The 20 GB / 16 GB default rests on the empty-shard number. From H tomorrow the fleet proves v1 shards of 30,000 pgas (about 7 M cycles, a ninth of the prototype shard) whose peak is unmeasured; `proving/fixtures/fees-v1-shards2` and `-shards3` are that shape. The fleet table's "proving-only" rows are 32 GB-card rows until then; the litepaper's "12 GB" (D2) and the evidence row 16 fall with it | The sweep's last row is a v1-budget shard with and without the miner, and the provedefault gates are set from it before 0.3.11 ships default-on; the fleet table labels its rows by the card that fits | proving v1 | closed as measured (proving agent, the S_p curve, app default 440fd59): 32 GB mines and proves today (28.3 GB alone, 30.0 beside the miner); 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner) from DAA 210,000 and nothing before it; 16 GB proves only empty shards alone (13.9 GB), nothing beside the miner (15.7 GB); 12 GB proves nothing on SP1 6.8.1; the default is on at 24 GB or more. Relayed to the rig installer and the HiveOS words sub-agent for their tables; until H tomorrow every prover on the devnet is a 32 GB card | From fadc28441409f1181f2b627e696325b8e3f26033 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:50:18 +0000 Subject: [PATCH 053/158] Consequences ledger: C6 closed on pool-v0 (proving flag, no software dev fee in pool mode); D8 carries the fee sentence; the HiveOS README three-branch merge note Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- docs/plans/consequences-decisions.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 9c9d4190..09942a9f 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -13,7 +13,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C3 | 13,816 MiB GPU peak, prover alone, empty shards (`prover-cost-pc2-pv1`) | bench-log "proving v1", step 1 | 12 GB cards (RTX 3060 12 GB, 4070, 5070), the default-on rule (`provedefault.rs` `MIN_VRAM_MB` 11,776) | On the only measurement the prover by itself exceeds a 12 GB card by 1.5 GB, so the 12 GB default gate switches proving on for cards that cannot run it on this build unless the SP1 knobs bring the peak down. The litepaper's "12 GB or more proves full shards" (`site/litepaper.html` 560) and evidence row 16 rest on the sweep | 0.3.11 does not ship the default-on until the sweep has a row under 11.5 GB for a full v1 shard; if none, the gate moves to 24 GB and the public claim is qualified (D2) | proving v1 (sweep in work); public claim: decision | closed as measured (proving agent: the sweep moves no floor, 13.9 GB for an empty shard, 28.3 GB for a full prototype shard; the default is 20 GB mining / 16 GB prove-only; the 12 GB sentence is false on this build and goes to the project lead as D2 with the curve); the v1-shard row is C15 | | C4 | Host RAM 25,550 MB used of 63,132 on PC 2 with the prover on; the WSL2 VM working set 7,915 MB | bench-log "proving v1", step 1 host RAM row | Windows home miners with 16 GB RAM (the common gaming PC); Macs with 16 GB switching the CPU prover on | The prover default has no RAM floor. On Windows the WSL2 VM alone holds 7.9 GB beside the app, the node and the game-class desktop; a 16 GB machine with proving on by default swaps or kills the node. The app's own RSS (engine, node, verifier) is not separated in the measurement, so no requirement can be stated yet | The sweep job records the app's and the node's working sets beside the VM's; `provedefault` reads total RAM and stays off under 32 GB on Windows until measured; the Proving tile and the miner page state the RAM requirement | proving v1; miner UI adbf58b058186a18b (the tile line) | closed in code (proving v1 c2544be: MIN_RAM_MB_WINDOWS 31,000 in provedefault.rs, the tile line names the 7.9 GB VM on a 63 GB PC; unknown RAM is not a gate; the miner UI help line next cut) | | C5 | The app quit for the 0.3.10 update at 20:01:09Z and aborted the chain job at block 3 ("aborted (the app is quitting)"); `prover.rs` kills the child on quit | bench-log "proving v1" chain run 1; `app/igneum-app/src/prover.rs` 266 to 272 | every prover on every update; with proving v1 the aggregator | Tonight's `update-now` to every app aborts whichever shard each prover has in flight (up to 37 s of work each, no payout, re-assigned to nobody until the window passes). Under proving v1 a restart mid-segment loses the aggregator's chain state: the segment goes unproven after T (600 DAA), the aggregator share of 8 blocks is forfeited to the escrow, and the next record must be fresh-chain. With one aggregator on the devnet every app update costs 10 minutes of unproven segments | The update's safe moment waits for the prover's current proof (as it does for the node); the aggregator persists the last segment proof and resumes the chain after a restart; the Updates section says "waits for the proof in flight". For 0.3.10 tonight the aborted shards are an accepted cost, recorded | proving v1; shipper (tonight's rollout note); miner UI (Updates wording) | refused for tonight by the proving agent (persisting the segment proof and holding the update for a proof in flight are 0.3.12; the cost is in the plan as the rule working as written); the shipper carries the aborted-shard count in release-0.3.10.md section 8; the 20:01:09Z abort was NOT 0.3.10 (shipper: nothing published), see C16 | -| C6 | The prover costs a mining 5090 4.0% (124.7 to 119.7 MH/s); the software dev fee is 1 template in 100 | bench-log "proving v1" step 1; `packaging/hive/README.md` "The dev fee" | pool users; the pool's ledger | A member who proves sends 4% fewer shares, so the pool's vardiff and `stats.hashrate` read a 4% loss while the proving income (90% of the shard credit plus a tenth to an aggregator) is paid to the member's own key and never appears in the pool's ledger: the pool dashboard understates a proving member's earnings. The software dev fee has no mechanism in pool mode (the pool issues the templates), so a pooled miner pays no dev fee today and the pool design must say whether it takes one (1 share in 100 to the dev address) or none | The pool-v0 design states both: the member `stats` carry a `proving` flag and the pool page shows proving income beside shares; the dev fee rule in pool mode is written down before the first pool ships | pool a4781ba117326091f | sent | +| C6 | The prover costs a mining 5090 4.0% (124.7 to 119.7 MH/s); the software dev fee is 1 template in 100 | bench-log "proving v1" step 1; `packaging/hive/README.md` "The dev fee" | pool users; the pool's ledger | A member who proves sends 4% fewer shares, so the pool's vardiff and `stats.hashrate` read a 4% loss while the proving income (90% of the shard credit plus a tenth to an aggregator) is paid to the member's own key and never appears in the pool's ledger: the pool dashboard understates a proving member's earnings. The software dev fee has no mechanism in pool mode (the pool issues the templates), so a pooled miner pays no dev fee today and the pool design must say whether it takes one (1 share in 100 to the dev address) or none | The pool-v0 design states both: the member `stats` carry a `proving` flag and the pool page shows proving income beside shares; the dev fee rule in pool mode is written down before the first pool ships | pool a4781ba117326091f | closed on pool-v0 (pool agent: the member stats line carries a proving flag, /api/miners/
and the pool page show it with the 4% note and that proving income never passes through the pool; the software dev fee is NONE in pool mode, the pool's own fee (default 1%) is the only fee, carried in the welcome message's share_scheme, shown on the Connect card, written in docs/plans/pool.md and packaging/hive/README.md). Merge note: packaging/hive/README.md is now edited on three branches (hive-words, ota-k2, pool-v0). Public wording: the miner page's "a visible 1% software fee you can switch off" is a solo-mining sentence once a pool exists, added to D8 | | C7 | The HiveOS package holds `igneumd`, `igneum-miner` and the two workers; no `igneum-prove-host`, no SP1 GPU server, no per-card rule | `packaging/hive/make-hive-package.sh` 26 to 30; README "What the hooks do" | rigs (HiveOS, Linux), the largest hashrate tier | A rig cannot prove at all, so the 20% proving share is reachable only from the app. The miner page says "the card mines and proves" (`site/miner.html` 7, `site/index.html` 484) and the HiveOS README does not say rigs mine only. A rig that could prove needs the 251 MB SP1 GPU server, CUDA 12.8 and the per-card profile of C2 and C3, and a rig's RAM (4 to 8 GB on most Hive images, approximate) is below C4's floor | The rig installer either carries the prover with the per-card rule and a RAM check, or its README and the miners page say rigs mine only and provers are app machines; `IDENTITIES=8 for a big card, 2 for a small one` gets a threshold in GB | rig installer a3e7b2b03222f5cff | closed (rig installer 88f31d9: gates 23,552 MB for both roles from provedefault.rs 440fd59, the README table per tier; HiveOS hive-words 2d056e8: the same table; open only the miners page sentence, D8) | | C8 | Dataset 2 GiB at genesis plus 0.5 GiB a year; the working-set rule "under 6 GB on an 8 GB card"; the cache 256 MiB doubling at years 4 and 12; scratch up to 128 KiB per resident warp | spec 01 section 1.13.3; coordinator's budget rule; layer 6 option C; `docs/plans/hot-table.md` section 3 | 4 GB and 8 GB cards; the litepaper's claim | `site/index.html` 461 says "Any 4 GB card" and the litepaper (560) says a 4 GB card mines for about four years and an 8 GB card for more than a decade. At 75% of the card the 4 GB card's dataset room is about 2.4 GiB: under one year. The 8 GB card's room is about 5 GiB after cache, hot table, scratch and buffers: about six years, five and a half with the year-4 cache step. Neither public sentence holds under the schedule | A card-lifetime table per tier (sub-agent, `docs/analysis/card-lifetime-2026-10-05.md`); the public wording is a claim for the project lead (D3) | sub-agent (table); decision (wording) | table landed (sub-agent, docs/analysis/card-lifetime-2026-10-05.md, 1fecfe2, merged into ca2-coord at 22:50): 4 GB 1.0 to 1.5 years under (a) and year 4 under (b); 8 GB 6.3 to 7.5 or 12; 12 GB 12 to 13.5 or 12 to 28 depending on whether the cache stays resident; Apple 8 GB 2.6 to 3.4 or 4. The coordinator decided the cache is freed after the daily build and recommends (b) to the project lead; the public sentences hold only under (b): D3 and D4 revised | | C9 | Index mapping option (a) multiply-shift (fades cards) against (b) power-of-two steps 2, 4, 8 GiB | spec 01 section 1.13.3, gate 1 | 8 GB and 12 GB cards | Option (b)'s 8 GiB step (about year 12) ends 8 GB and 12 GB cards on the same day; option (a) fades them one year at a time. The choice is a genesis parameter and a tier consequence nobody has put beside the options | Decision request D4 with the lifetime table of C8 | decision | open | diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index 7d35a023..7bc99a4a 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -11,4 +11,4 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | D5 | C10 | The site's "under 2x" chip claim: the scratch layer leaves the on-die-cache recompute chip at 2.4x at every share; the mixer multiplier x2 brings it to 1.2x at twice the CPU verify cost (about 0.9 ms per warp, half the pool members per core) | Carry the mixer x2 into class v3 on the devnet tonight (the coordinator's gates apply, the verify time per warp measured) and keep the claim; if the coordinator judges it outside the delegation, qualify the claim in level 3 until the project lead says | The claim is the project's first public sentence on chips; it is either true by a measured lever or it is marked | | D6 | C11 | Whether class v3 should favour AMD (fewer, wider loads per hash) at a cost to the 5090's latency-bound share, or accept that AMD cards mine at about a seventh of a 5090 and 4.5x the electricity per hash | Accept it for v3 and say so on the miners page ("NVIDIA first; AMD mines at a lower rate per watt on this class"); open the AMD question as a Counter ASIC 3.0 item with its own measurement | Tonight's rule was the project lead's and the 5090 margin is the anti-chip argument; AMD's position is a public-copy question, not a gate | | D7 | C13 | Same as D3's supply wording | with D3 | | -| D8 | C7 | The miner page says "One click: install, start, the card mines and proves" (site/miner.html 7, 13, 21; site/index.html 484). On HiveOS and the rig installer a rig mines only until a Linux prover build is published, and on the app a 12 GB card mines only, a 16 GB card proves with the miner paused, 20 GB and up does both (the sweep of 5 October, before the v1-shard row) | Qualify the sentence on the miner page and the homepage card: "the card mines; 20 GB cards prove too; rigs mine until the Linux prover ships" | The sentence is the product's first promise and tonight's measurement bounds it by card | +| D8 | C7 | The miner page says "One click: install, start, the card mines and proves" (site/miner.html 7, 13, 21; site/index.html 484). On HiveOS and the rig installer a rig mines only until a Linux prover build is published, and on the app a 12 GB card mines only, a 16 GB card proves with the miner paused, 20 GB and up does both (the sweep of 5 October, before the v1-shard row) | Qualify the sentence on the miner page and the homepage card: "the card mines; 24 GB cards prove too, 32 GB does both at once; rigs mine until the Linux prover ships". The same page's "a visible 1% software fee you can switch off" becomes "solo mining carries a 1% software fee you can switch off; in a pool the pool's own fee is the only one" once pool-v0 ships (the pool agent fixed the rule: no software dev fee in pool mode) | The sentence is the product's first promise and tonight's measurement bounds it by card | From 9fd666f3ff3c81d5dcdf54c583aec8fd87dbea86 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:50:21 +0000 Subject: [PATCH 054/158] Consequences ledger: the read-width hash is 30ff674 Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 09942a9f..07bd1c90 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -19,7 +19,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C9 | Index mapping option (a) multiply-shift (fades cards) against (b) power-of-two steps 2, 4, 8 GiB | spec 01 section 1.13.3, gate 1 | 8 GB and 12 GB cards | Option (b)'s 8 GiB step (about year 12) ends 8 GB and 12 GB cards on the same day; option (a) fades them one year at a time. The choice is a genesis parameter and a tier consequence nobody has put beside the options | Decision request D4 with the lifetime table of C8 | decision | open | | C10 | The on-die-cache recompute chip's gain is 2.4x at every scratch share under the 6 GB cap; the mixer multiplier x2 brings it to 1.2x, x4 to 0.6x, inside the 10 ms verify gate | `docs/analysis/scratch-soundness.md` finding 2 and section 10; M16 analysis table | chip builders; the site's "under 2x" claim; pool share verification | Layer 3 does not deliver the headline and the rollout plan's own rule (6a) says the public claim is qualified when no share gets the chip under 2x. The lever that does is M16's mixer multiplier, which doubles the CPU verify per warp (0.441 ms to about 0.9 ms at x2): a pool core verifies about 11,000 members' shares instead of 22,000, and node block verification doubles. The corrected SRAM cost ($19 to $37 of silicon per mirror die) means the mirror never stops a funded chip; the cache schedule keeps it above GPU L2 only | The coordinator either carries the mixer x2 into class v3 for the devnet (it is a lottery-hash change like the others; the verify cost per warp is measured with it) or marks the site's "under 2x" as qualified in the public copy level 3 and D5 asks the project lead which | coordinator ada8afb62d752b1e2 | closed as a consequence (coordinator: the public claim was qualified at 21:50; at 22:00 the M16 mixer x4 was decided into class v3 behind the same activation; the pool-core and node verification consequences are C14) | | C11 | RX 9070 XT 17.73 MH/s at 198.9 W (0.089 MH/W) against the RTX 5090 122.30 MH/s at 307.6 W (0.398 MH/W); every read width costs the 9070 XT the same 2.4 G line fetches a second | bench-log AMD telemetry entry; readwidth probe ceilings (status 20:35) | AMD home miners; the "three vendors" copy | Under the "widest latency-bound read" rule w16 moves bytes per hash, not loads per hash, so the AMD card keeps about a seventh of the 5090's hash rate and pays 4.5x the electricity per hash; at a UK tariff of 25 p/kWh (approximate) the 9070 XT spends 4.5x the 5090's pence per IGN. The readwidth decision table carries MH/s only; no MH/W or MH per pound per card per class, and the public copy implies vendor parity | The readwidth table adds MH/W (and MH per pound at list prices, approximate) per card per class; the v3 decision names the AMD consequence; whether the class should favour fewer loads per hash for AMD's 64-byte lines is a consensus choice for the project lead (D6) | read-width a451c9935bfb1bc19; coordinator; decision | closed (read-width e752fc7 section 4.1: MH/W and MH per pound per class per card; the AMD gap is the card's random-access rate, no width closes it; the coordinator's 22:30 entry says "near parity per pound" where the table says the 5090 is 2.2x per pound: C18) | -| C12 | The v3 working set on Apple: 1,568 to 1,760 MiB today, 2,592 to 2,784 MiB at the 2 GiB genesis dataset, in unified memory shared with macOS; 2,048 launched warps x 128 KiB scratch | `docs/plans/hot-table.md` section 3 M5 Max row; era-layout section 3 | Apple silicon with 8 GB and 16 GB (the base Mac mini, MacBook Air) | An 8 GB Mac holds the miner's 2.6 GB beside macOS's 3 to 4 GB: it mines today and swaps at the first dataset growth step. The app has no gate on Metal's `recommendedMaxWorkingSetSize`; the site's "Any 4 GB card" has no Apple line. The CPU prover's RAM on a 16 GB Mac is unmeasured (the Settings switch lets it on) | The app reads `recommendedMaxWorkingSetSize` and refuses to mine (with the reason on the Mine tile) when the working set exceeds it; the site says "Mac: 8 GB or more"; the Apple scratch row at 128 KiB is measured in the readwidth table, not launched at 2,048 by assumption | miner UI adbf58b058186a18b; read-width (the Apple row) | in work (read-width 46dcc3e: the Apple scratch footprint by arithmetic is 1.4 GiB at 2,048 x 32 KiB and 1.6 GiB at 2,048 x 128 KiB, 2.4 GiB at 4,096 x 128 KiB; the Metal harness now prints currentAllocatedSize and recommendedMaxWorkingSetSize in its RESULT line, the measured row waits for the Mac measure lock, held by a prover measurement since 20:31Z; the miner UI gates on the arithmetic plus its output buffer until then, mining.gate_reason next cut) | +| C12 | The v3 working set on Apple: 1,568 to 1,760 MiB today, 2,592 to 2,784 MiB at the 2 GiB genesis dataset, in unified memory shared with macOS; 2,048 launched warps x 128 KiB scratch | `docs/plans/hot-table.md` section 3 M5 Max row; era-layout section 3 | Apple silicon with 8 GB and 16 GB (the base Mac mini, MacBook Air) | An 8 GB Mac holds the miner's 2.6 GB beside macOS's 3 to 4 GB: it mines today and swaps at the first dataset growth step. The app has no gate on Metal's `recommendedMaxWorkingSetSize`; the site's "Any 4 GB card" has no Apple line. The CPU prover's RAM on a 16 GB Mac is unmeasured (the Settings switch lets it on) | The app reads `recommendedMaxWorkingSetSize` and refuses to mine (with the reason on the Mine tile) when the working set exceeds it; the site says "Mac: 8 GB or more"; the Apple scratch row at 128 KiB is measured in the readwidth table, not launched at 2,048 by assumption | miner UI adbf58b058186a18b; read-width (the Apple row) | in work (read-width 30ff674: the Apple scratch footprint by arithmetic is 1.4 GiB at 2,048 x 32 KiB and 1.6 GiB at 2,048 x 128 KiB, 2.4 GiB at 4,096 x 128 KiB; the Metal harness now prints currentAllocatedSize and recommendedMaxWorkingSetSize in its RESULT line, the measured row waits for the Mac measure lock, held by a prover measurement since 20:31Z; the miner UI gates on the arithmetic plus its output buffer until then, mining.gate_reason next cut) | | C13 | `/api/supply` `max_supply_ign` 4,000,000,000 against `minted_at_end_ign` about 3,963,000,000 (the ramp withholds about 37 M, the floors the rest); the live tables lack `number`, `tx_count`, `detail` until the observer restarts | `site/api/supply.mjs` 31 to 48; `docs/plans/explorer.md` "Open" | everyone who reads the explorer beside the homepage tile "4B IGN hard cap, ever" (`site/index.html` 386) | The tile says 4 billion and the explorer page says "of 4,000,000,000 by the rule" while the API's own end figure is 3.963 billion: a reader who adds the two columns finds 37 million missing. The explorer page shows "Circulating 0 IGN" on the devnet deployment until the observer restarts on the new code | The tile, the litepaper's supply line and the explorer carry "cap 4,000,000,000; about 3.96 billion ever minted" (the litepaper already says "approached and never reached"); the explorer does not go live before the observer restart lands the columns | explorer a76f60b415859b7b5; wording: the project lead (D7, with D3) | closed (explorer 3e01212: the tile reads "cap 4,000,000,000, 3.96 billion ever minted, x% so far" with the withheld figure on hover; the homepage tile and litepaper wording stay with the project lead, D3 and D7; the zero-circulating premise was sharper than stated, a 42703 failure not a null, now a 503 with the reason, and moot: the live observer restarted on the explorer code at 19:42:50Z, so the live tables carry the columns) | | C14 | The M16 mixer x4 decided into class v3 at 22:00 (coordinator): verifier 1.6 to 4.8 ms per warp against 0.441 ms today (0.87 cold), measurement running on branch ca2-mixer | coordinator's reply 21:5x; `docs/analysis/m16-recompute-attacker-2026-10-05.md` table; spec 09 section 9.8 item 5 | pool operators; every node (the Hetzner seeds, the observer, a 2019-class laptop); the 10 ms verify gate | At x4 one pool core verifies 200 to 600 shares a second instead of 2,270, so one core covers 2,000 to 6,000 members at one share per 10 s instead of 22,000; a block's CPU re-check and the miner's own `cpu re-check` of every found hash cost 4 to 11x; the seeds' small VMs verify every header at that cost; the gate's margin falls from 23x to 2 to 6x, which bounds Counter ASIC 3.0's room | The coordinator is adding the numbers to the ca2-mixer document and the status (said in reply); the reviewer checks at the next sweep that the pool-members-per-core and the seed-VM header-verify rows are there, and that the spec 09 figure 2,270 shares a second per core is re-cut with v3 | coordinator ada8afb62d752b1e2 | in work | | C15 | A full prototype shard peaks at 28.3 GB on sp1-gpu-server 6.8.1 (the sweep, proving agent 22:0x); an empty one 13.9 GB; no knob moves either floor | proving agent's reply; sweep job `memsweep-pc2-pv1` | 24 GB cards (4090, 7900-class if it had a path); the 5090 that mines and proves; the fleet table | A 24 GB card cannot prove a prototype full shard at all, mining or not; a 5090 mining (3.4 GB resident) plus a full shard is 31.7 GB against 31.8 GB, the edge. The 20 GB / 16 GB default rests on the empty-shard number. From H tomorrow the fleet proves v1 shards of 30,000 pgas (about 7 M cycles, a ninth of the prototype shard) whose peak is unmeasured; `proving/fixtures/fees-v1-shards2` and `-shards3` are that shape. The fleet table's "proving-only" rows are 32 GB-card rows until then; the litepaper's "12 GB" (D2) and the evidence row 16 fall with it | The sweep's last row is a v1-budget shard with and without the miner, and the provedefault gates are set from it before 0.3.11 ships default-on; the fleet table labels its rows by the card that fits | proving v1 | closed as measured (proving agent, the S_p curve, app default 440fd59): 32 GB mines and proves today (28.3 GB alone, 30.0 beside the miner); 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner) from DAA 210,000 and nothing before it; 16 GB proves only empty shards alone (13.9 GB), nothing beside the miner (15.7 GB); 12 GB proves nothing on SP1 6.8.1; the default is on at 24 GB or more. Relayed to the rig installer and the HiveOS words sub-agent for their tables; until H tomorrow every prover on the devnet is a 32 GB card | From 28635b165fc6d3f4ba46f33c55db56c14d8b3fe0 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:51:24 +0000 Subject: [PATCH 055/158] Metal worker compiles a class v3 program from the pack a prepare line names; igneum-pow chain_dataset_day seam; pow_genesis_dataset_log2 in the override files main.swift: servePackProgram reads program.h with the packfile.h checks (generator 2 or 3, the class line against the generator, the seed bytes, IGNEUM_SEEDW_INIT against attempt_words, class and era against the line) and compiles program_bound.metal; the program store keys on (seed, class, era); a v3 job with no resident v3 pack program answers need + error; v2 lines unchanged (Swift generation, the variant race); a pack program never races. verify.rs: Epoch::chain_dataset_day(day, class, days_since_genesis, genesis_dataset_log2) and days_since_genesis, the entry the node builds every day cache through (the ca2-mixer growth rule fills the body). Co-Authored-By: Claude Fable 5.1 --- igneum-pow/src/verify.rs | 18 ++- infra/fast-time/override-60x.json | 1 + proto-metal/main.swift | 124 ++++++++++++++---- .../redteam/override-60x-v3.json | 2 +- 4 files changed, 120 insertions(+), 25 deletions(-) diff --git a/igneum-pow/src/verify.rs b/igneum-pow/src/verify.rs index db324e7b..3b93e79c 100644 --- a/igneum-pow/src/verify.rs +++ b/igneum-pow/src/verify.rs @@ -417,6 +417,11 @@ pub struct Epoch { /// Default dataset size: 2^28 words = 1 GiB. pub const DEFAULT_DATASET_LOG2: u32 = 28; +/// Days a day index lies after the network's genesis day (0 for the genesis day and any day before it). +pub fn days_since_genesis(day_index: u64, genesis_day_index: u64) -> u64 { + day_index.saturating_sub(genesis_day_index) +} + impl Epoch { pub fn new(seed: &str, day: &str, mode: DatasetMode, dataset_log2: u32) -> Self { Self { program: generate(seed), dataset: DatasetSource::new(day, mode, dataset_log2) } @@ -467,9 +472,18 @@ impl Epoch { /// construction (the mixer multiplier) and cache size schedule (ca2-mixer); today both classes build the day of /// [`Epoch::from_seed_bytes`], and the engine keys its day caches on `(day, class)` so the two never share one. pub fn chain_dataset(day_bytes: &[u8], class: ProgramClass) -> DatasetSource { - let _ = class; + Self::chain_dataset_day(day_bytes, class, 0, DEFAULT_DATASET_LOG2) + } + + /// [`Epoch::chain_dataset`] with the day's position since genesis and the network's genesis dataset size: the + /// entry the node's engine and the miner's export build every day cache through, so the cache growth schedule + /// of spec 01 section 1.13.3 has one place to act. SEAM (ca2-node, 5 October 2026): the body here builds the + /// genesis-size cache for every day; the ca2-mixer branch fills the growth rule (`growth_doublings`) and the + /// class v3 item construction, keeping this signature. + pub fn chain_dataset_day(day_bytes: &[u8], class: ProgramClass, days_since_genesis: u64, genesis_dataset_log2: u32) -> DatasetSource { + let _ = (class, days_since_genesis); let key = crate::seed::seed_words_from_bytes(day_bytes); - let mut dataset = DatasetSource::from_key(key, DatasetMode::MemoryHard, DEFAULT_DATASET_LOG2); + let mut dataset = DatasetSource::from_key(key, DatasetMode::MemoryHard, genesis_dataset_log2); dataset.key_bytes = day_bytes.to_vec(); dataset } diff --git a/infra/fast-time/override-60x.json b/infra/fast-time/override-60x.json index 7ec2e3b0..b8e77dff 100644 --- a/infra/fast-time/override-60x.json +++ b/infra/fast-time/override-60x.json @@ -55,6 +55,7 @@ "proving_v0_activation_daa": 18446744073709551615, "finality_v3_activation_daa": 18446744073709551615, "program_class_v3_activation_daa": 18446744073709551615, + "pow_genesis_dataset_log2": 28, "fees_v1_activation_daa": 0, "fees": {"pgas": {"version": 1, "cycles_per_pgas": 1000, "intrinsic_pgas_per_tx": 300, "modexp_base": 10, "modexp_per_byte_numer": 1, "modexp_per_byte_denom": 10}, "block_proving_gas_limit": 120000, "shard_proving_gas_budget": 30000, "min_execution_base_fee_wei": 100000000000, "min_proving_base_fee_wei": 10000000000000, "initial_execution_base_fee_wei": 100000000000, "initial_proving_base_fee_wei": 10000000000000, "base_fee_change_denominator": 8} } diff --git a/proto-metal/main.swift b/proto-metal/main.swift index ef1ad029..09e52fa4 100644 --- a/proto-metal/main.swift +++ b/proto-metal/main.swift @@ -2781,13 +2781,32 @@ func blockInitWords(prehash: [UInt8], nonceHi: UInt32) -> [UInt32] { final class ServeProgram { let seedHex: String - let program: Program + /// The Swift-generated program (class v2); nil for a program compiled from a pack (class v3, Counter ASIC 2.0), + /// which has no Swift instruction list and so never races variants + let program: Program? + /// Counter ASIC 2.0: the class ("v2" or "v3") and, for a pack program, its era seed hex and loads per hash + let programClass: String + let eraHex: String + let loadsPerHash: Int private var slot: CompiledHash private let lock = NSLock() /// true until a race ran for this program (an inline compile races after the first job on it) var raceDue = true var raceLine = "" - init(seedHex: String, program: Program, compiled: CompiledHash) { self.seedHex = seedHex; self.program = program; self.slot = compiled } + init(seedHex: String, program: Program, compiled: CompiledHash) { + self.seedHex = seedHex; self.program = program; self.slot = compiled + self.programClass = "v2"; self.eraHex = ""; self.loadsPerHash = program.loadsPerHash + } + init(seedHex: String, packClass: String, eraHex: String, loadsPerHash: Int, compiled: CompiledHash) { + self.seedHex = seedHex; self.program = nil; self.slot = compiled + self.programClass = packClass; self.eraHex = eraHex; self.loadsPerHash = loadsPerHash; self.raceDue = false + } + /// Whether this program is the one a line naming `cls` (and `era`) wants; empty names accept any (class v2 lines). + func matches(cls: String, era: String) -> Bool { + if cls != "" && cls != programClass { return false } + if era != "" && programClass == "v3" && era.lowercased() != eraHex.lowercased() { return false } + return true + } var compiled: CompiledHash { lock.lock(); defer { lock.unlock() }; return slot } func install(_ c: CompiledHash) { lock.lock(); slot = c; lock.unlock() } } @@ -2873,7 +2892,8 @@ func raceProgram(_ gpu: GPU, _ program: ServeProgram, dataset: MTLBuffer, datase // Compile (the base is already compiled) for i in 1.. deadline { entries[i].note = "not compiled: the race budget ran out"; continue } - do { entries[i].compiled = try compileBound(gpu, msl: generateMSL(program.program, datasetLog2: datasetLog2, source: .stored, bound: true, variant: entries[i].v), variant: entries[i].v) } + guard let swiftProgram = program.program else { return "race \(program.seedHex.prefix(16)) skipped: a pack program (class \(program.programClass)) has no variants to race" } + do { entries[i].compiled = try compileBound(gpu, msl: generateMSL(swiftProgram, datasetLog2: datasetLog2, source: .stored, bound: true, variant: entries[i].v), variant: entries[i].v) } catch { entries[i].note = "compile: \(error)".prefix(200).description } } let compileMs = ms(t0, nowNs()) @@ -2926,7 +2946,7 @@ func raceProgram(_ gpu: GPU, _ program: ServeProgram, dataset: MTLBuffer, datase if win != 0, let k = entries[win].compiled { program.install(k) } let total = ms(t0, nowNs()) let os = ProcessInfo.processInfo.operatingSystemVersion - var line = "race \(program.seedHex.prefix(16)) device \(device) driver macos-\(os.majorVersion).\(os.minorVersion).\(os.patchVersion) arch metal loads \(program.program.loadsPerHash) wide \(program.program.wideLoadsPerHash) variants \(entries.count)" + var line = "race \(program.seedHex.prefix(16)) device \(device) driver macos-\(os.majorVersion).\(os.minorVersion).\(os.patchVersion) arch metal loads \(program.loadsPerHash) wide \(program.program?.wideLoadsPerHash ?? 0) variants \(entries.count)" for e in entries { line += e.ok && (e.mhs > 0 || pinnedOnly) ? " \(e.v.name)=\(fmt(e.mhs, 3))/\(e.compiled!.pipeline.maxTotalThreadsPerThreadgroup)t/\(e.v.groupWidth)w" : " \(e.v.name)=-" } let gain = baseMhs > 0 ? (winMhs / baseMhs - 1) * 100 : 0 line += " winner \(entries[win].v.name) \(fmt(winMhs, 3)) base \(fmt(baseMhs, 3)) gain \(gain >= 0 ? "+" : "")\(fmt(gain, 2))% compile \(fmt(compileMs, 0)) bench \(fmt(total - compileMs, 0)) total \(fmt(total, 0)) ms" @@ -2957,14 +2977,14 @@ final class ServeStore { /// The pair of the last job (epoch seed hex, day seed hex); never evicted var current: (String, String)? = nil - func program(_ seedHex: String) -> ServeProgram? { lock.lock(); defer { lock.unlock() }; return programs.first { $0.seedHex == seedHex } } + func program(_ seedHex: String, cls: String = "", era: String = "") -> ServeProgram? { lock.lock(); defer { lock.unlock() }; return programs.first { $0.seedHex == seedHex && $0.matches(cls: cls, era: era) } } func dataset(_ dayHex: String) -> ServeDataset? { lock.lock(); defer { lock.unlock() }; return datasets.first { $0.dayHex == dayHex } } func counts() -> (Int, Int) { lock.lock(); defer { lock.unlock() }; return (programs.count, datasets.count) } /// Adds a program; with more than two resident, the oldest one that is not the current job's goes. func add(_ p: ServeProgram) { lock.lock(); defer { lock.unlock() } - if programs.contains(where: { $0.seedHex == p.seedHex }) { return } + if programs.contains(where: { $0.seedHex == p.seedHex && $0.programClass == p.programClass && $0.eraHex == p.eraHex }) { return } programs.append(p) while programs.count > 2, let i = programs.firstIndex(where: { $0.seedHex != current?.0 && $0.seedHex != p.seedHex }) { programs.remove(at: i) } } @@ -3011,6 +3031,51 @@ func serveProgram(_ gpu: GPU, _ store: ServeStore, seedHex: String, seed: [UInt8 return (sp, ms(t0, nowNs())) } +// Counter ASIC 2.0 (5 October 2026): a class v3 program comes from the pack igneum-miner export-pack / --prepare-packs +// wrote (program_bound.metal, the Rust emitter's text; program.h for the identity), never from the Swift generator. +// The pack is checked the way the one-click workers check it (proto-cuda/nvrtc/packfile.h): IGNEUM_GENERATOR 2 or 3, +// the class line against the generator, the seed bytes against the line's epoch seed, IGNEUM_SEEDW_INIT against the +// words of IGNEUM_PROGRAM_ATTEMPT of that seed, and the class and era against what the line names. +func servePackProgram(_ gpu: GPU, _ store: ServeStore, seedHex: String, seed: [UInt8], dir: String, wantClass: String, wantEra: String) throws -> (ServeProgram, Double) { + if let p = store.program(seedHex, cls: wantClass, era: wantEra) { return (p, 0) } + let t0 = nowNs() + guard let programH = try? String(contentsOfFile: dir + "/program.h", encoding: .utf8) else { throw NSError(domain: "pack", code: 1, userInfo: [NSLocalizedDescriptionKey: "cannot read \(dir)/program.h"]) } + func defineU32(_ name: String) -> UInt32? { + guard let re = try? NSRegularExpression(pattern: "#define \(name) ([0-9a-fA-Fx]+)"), let m = re.firstMatch(in: programH, range: NSRange(programH.startIndex..., in: programH)) else { return nil } + let v = String(programH[Range(m.range(at: 1), in: programH)!]).replacingOccurrences(of: "u", with: "") + return v.hasPrefix("0x") ? UInt32(v.dropFirst(2), radix: 16) : UInt32(v) + } + func defineStr(_ name: String) -> String? { + guard let re = try? NSRegularExpression(pattern: "#define \(name) \"([^\"]*)\""), let m = re.firstMatch(in: programH, range: NSRange(programH.startIndex..., in: programH)) else { return nil } + return String(programH[Range(m.range(at: 1), in: programH)!]) + } + func defineWords(_ name: String) -> [UInt32]? { + guard let re = try? NSRegularExpression(pattern: "#define \(name) \\{([^}]*)\\}"), let m = re.firstMatch(in: programH, range: NSRange(programH.startIndex..., in: programH)) else { return nil } + let body = String(programH[Range(m.range(at: 1), in: programH)!]) + let words = body.split(separator: ",").compactMap { t -> UInt32? in let v = t.trimmingCharacters(in: .whitespaces).replacingOccurrences(of: "u", with: ""); return v.hasPrefix("0x") ? UInt32(v.dropFirst(2), radix: 16) : UInt32(v) } + return words.count == 8 ? words : nil + } + func refuse(_ why: String) -> NSError { NSError(domain: "pack", code: 2, userInfo: [NSLocalizedDescriptionKey: "pack \(dir): \(why)"]) } + let generator = defineU32("IGNEUM_GENERATOR") ?? 1 + guard generator == 2 || generator == 3 else { throw refuse("program pack generator \(generator) is not a generator version this worker runs (2 or 3)") } + let packClass = generator == 3 ? "v3" : "v2" + if let named = defineStr("IGNEUM_PROGRAM_CLASS"), named != packClass { throw refuse("program pack IGNEUM_PROGRAM_CLASS \"\(named)\" does not match IGNEUM_GENERATOR \(generator)") } + let eraHex = defineStr("IGNEUM_ERA_SEED_HEX") ?? "" + if wantClass != "" && wantClass != packClass { throw refuse("program class mismatch: this pack is class \(packClass), the line names class \(wantClass) (export the pack again)") } + if wantEra != "" && packClass == "v3" && wantEra.lowercased() != eraHex.lowercased() { throw refuse("era seed mismatch: this pack was drawn under era \(eraHex.isEmpty ? "(none)" : String(eraHex.prefix(16))), the line names era \(wantEra.prefix(16)) (export the pack again)") } + if let packSeed = defineStr("IGNEUM_SEED_BYTES_HEX"), packSeed.lowercased() != seedHex.lowercased() { throw refuse("the pack is for epoch \(packSeed.prefix(16)), not \(seedHex.prefix(16))") } + let attempt = defineU32("IGNEUM_PROGRAM_ATTEMPT") ?? 0 + guard let seedw = defineWords("IGNEUM_SEEDW_INIT") else { throw refuse("program.h has no IGNEUM_SEEDW_INIT with 8 words") } + let want = attemptWords(seed, attempt) + if want != seedw { throw refuse("program pack and its seeds disagree: IGNEUM_SEEDW_INIT is not attempt \(attempt) of the epoch seed \(seedHex.prefix(16))") } + let loads = Int(defineU32("IGNEUM_LOADS_PER_HASH") ?? 128) + guard let msl = try? String(contentsOfFile: dir + "/program_bound.metal", encoding: .utf8) else { throw refuse("cannot read program_bound.metal") } + let c = try compileBound(gpu, msl: msl) + let sp = ServeProgram(seedHex: seedHex, packClass: packClass, eraHex: eraHex, loadsPerHash: loads, compiled: c) + store.add(sp) + return (sp, ms(t0, nowNs())) +} + // Builds the cache and dataset for a day seed (hex) unless resident. Returns (dataset, build ms). func serveDataset(_ gpu: GPU, _ store: ServeStore, dayHex: String, day: [UInt8], datasetLog2: Int) -> (ServeDataset, Double) { if let d = store.dataset(dayHex) { return (d, 0) } @@ -3036,19 +3101,19 @@ func runServe(_ opts: Options) -> Never { var f = line.split(separator: " ").map(String.init) if f.isEmpty { continue } if f[0] == "quit" { break } - // Counter ASIC 2.0 (5 October 2026): a job or prepare line of a class v3 epoch ends with `class=v3 era=`. - // This worker regenerates programs from the seed with the Swift version 2 generator, so it refuses every - // class v3 line until the integration branch adds generator 3 here (the one-click workers build from packs - // and take v3 packs). - var wantClass = "" + // Counter ASIC 2.0 (5 October 2026): a job or prepare line of a class v3 epoch ends with `class=v3 era=` + // (a class v2 line is the line of before). A class v2 program is generated here from the seed (the Swift + // version 2 generator); a class v3 program is compiled from the pack the prepare line names (servePackProgram), + // so a v3 job on seeds with no prepared v3 pack asks for one (`need`) instead of mining the wrong program. + var wantClass = "", wantEra = "" while let last = f.last, last.hasPrefix("class=") || last.hasPrefix("era=") { - if last.hasPrefix("class=") { wantClass = String(last.dropFirst(6)) } + if last.hasPrefix("class=") { wantClass = String(last.dropFirst(6)) } else { wantEra = String(last.dropFirst(4)) } f.removeLast() } - if wantClass != "" && wantClass != "v2" { + if wantClass != "" && wantClass != "v2" && wantClass != "v3" { let id = f.count > 1 ? f[1] : "0" - if f[0] == "prepare" { emit("prepare-failed \(id) \(f.count > 2 ? f[2] : "0") program class \(wantClass) is not implemented by this worker (Swift generator version 2 only)") } - else { emit("error \(id) program class \(wantClass) is not implemented by this worker (Swift generator version 2 only); use the CUDA or OpenCL worker with a class \(wantClass) pack") } + if f[0] == "prepare" { emit("prepare-failed \(id) \(f.count > 2 ? f[2] : "0") program class \(wantClass) is not one this worker runs (v2 or v3)") } + else { emit("error \(id) program class \(wantClass) is not one this worker runs (v2 or v3)") } continue } if f[0] == "prepare" { @@ -3058,12 +3123,16 @@ func runServe(_ opts: Options) -> Never { emit("prepare-failed \(f[1]) \(f[2]) bad field (epoch_seed 64 hex, day_seed hex)"); continue } let (epochHex, dayHex) = (f[1], f[2]) - let have = (store.program(epochHex) != nil, store.dataset(dayHex) != nil) + let have = (store.program(epochHex, cls: wantClass, era: wantEra) != nil, store.dataset(dayHex) != nil) if have.0 && have.1 { emit("prepared \(epochHex) \(dayHex) 0 program 0 dataset 0 (already resident)"); continue } + let packDir = f.count > 3 ? f[3...].joined(separator: " ") : "" + if wantClass == "v3" && packDir.isEmpty { emit("prepare-failed \(epochHex) \(dayHex) a class v3 program is compiled from a pack: send prepare with a pack directory (igneum-miner --prepare-packs )"); continue } prepareQueue.async { let t0 = nowNs() do { - let (sp, progMs) = try serveProgram(gpu, store, seedHex: epochHex, seed: epochSeed, datasetLog2: datasetLog2) + let (sp, progMs) = wantClass == "v3" + ? try servePackProgram(gpu, store, seedHex: epochHex, seed: epochSeed, dir: packDir, wantClass: wantClass, wantEra: wantEra) + : try serveProgram(gpu, store, seedHex: epochHex, seed: epochSeed, datasetLog2: datasetLog2) let (sd, dsMs) = serveDataset(gpu, store, dayHex: dayHex, day: daySeed, datasetLog2: datasetLog2) // The race: the prepared program against its own dataset, exclusive windows between jobs var raceMs = 0.0 @@ -3073,8 +3142,8 @@ func runServe(_ opts: Options) -> Never { raceMs = ms(r0, nowNs()) } let (np, nd) = store.counts() - emit("prepared \(epochHex) \(dayHex) \(fmt(ms(t0, nowNs()), 1)) program \(fmt(progMs, 1)) dataset \(fmt(dsMs, 1)) race \(fmt(raceMs, 1)) variant \(sp.compiled.variant) loads/hash \(sp.program.loadsPerHash) cache-fill \(fmt(sd.ctx.cacheFillGPUms, 1)) resident \(np) programs \(nd) datasets") - } catch { emit("prepare-failed \(epochHex) \(dayHex) Metal compile failed: \(error)") } + emit("prepared \(epochHex) \(dayHex) \(fmt(ms(t0, nowNs()), 1)) program \(fmt(progMs, 1)) dataset \(fmt(dsMs, 1)) race \(fmt(raceMs, 1)) variant \(sp.compiled.variant) class \(sp.programClass) loads/hash \(sp.loadsPerHash) cache-fill \(fmt(sd.ctx.cacheFillGPUms, 1)) resident \(np) programs \(nd) datasets") + } catch { emit("prepare-failed \(epochHex) \(dayHex) \(wantClass == "v3" ? "" : "Metal compile failed: ")\(error.localizedDescription)") } } continue } @@ -3096,9 +3165,20 @@ func runServe(_ opts: Options) -> Never { let program: ServeProgram let dataset: ServeDataset do { - if store.program(pair.0) == nil || store.dataset(pair.1) == nil { prepareQueue.sync {} } - let (sp, progMs) = try serveProgram(gpu, store, seedHex: pair.0, seed: epochSeed, datasetLog2: datasetLog2) - if progMs > 0 { emit("info program epoch \(pair.0.prefix(16)) loads/hash \(sp.program.loadsPerHash) compiled inline in \(fmt(progMs, 1)) ms (not prepared)") } + if store.program(pair.0, cls: wantClass, era: wantEra) == nil || store.dataset(pair.1) == nil { prepareQueue.sync {} } + let sp: ServeProgram + if wantClass == "v3" { + guard let resident = store.program(pair.0, cls: wantClass, era: wantEra) else { + emit("need \(pair.0) \(pair.1)") + emit("error \(jobId) pack (metal): program class mismatch: no class v3 program for epoch \(pair.0.prefix(16)) is resident (this worker compiles v3 from a pack); send prepare with a pack directory") + continue + } + sp = resident + } else { + let (p, progMs) = try serveProgram(gpu, store, seedHex: pair.0, seed: epochSeed, datasetLog2: datasetLog2) + if progMs > 0 { emit("info program epoch \(pair.0.prefix(16)) loads/hash \(p.loadsPerHash) compiled inline in \(fmt(progMs, 1)) ms (not prepared)") } + sp = p + } let (sd, dsMs) = serveDataset(gpu, store, dayHex: pair.1, day: daySeed, datasetLog2: datasetLog2) if dsMs > 0 { emit("info dataset day \(pair.1) built inline in \(fmt(dsMs, 1)) ms (cache fill \(fmt(sd.ctx.cacheFillGPUms, 1)) ms, build \(fmt(sd.ctx.lastBuildGPUms, 1)) ms GPU; not prepared)") } program = sp; dataset = sd diff --git a/tools/finality-attacks/redteam/override-60x-v3.json b/tools/finality-attacks/redteam/override-60x-v3.json index af6a2ce3..98a1f0f3 100644 --- a/tools/finality-attacks/redteam/override-60x-v3.json +++ b/tools/finality-attacks/redteam/override-60x-v3.json @@ -1 +1 @@ -{"timestamp_deviation_tolerance": 132, "past_median_time_window_size": 27, "difficulty_window_size": 661, "min_difficulty_window_size": 150, "difficulty_rule": "igneum-dual", "coinbase_payload_script_public_key_max_len": 150, "max_coinbase_payload_len": 16384, "max_tx_inputs": 1000, "max_tx_outputs": 1000, "max_signature_script_len": 250000, "max_script_public_key_len": 10000, "mass_per_tx_byte": 1, "mass_per_script_pub_key_byte": 10, "mass_per_sig_op": 1000, "block_mass_limits": {"compute": 500000, "storage": 500000, "transient": 1000000}, "block_lane_limits": {"lanes_per_block": 50, "gas_per_lane": 1000000000}, "storage_mass_parameter": 1000000000000, "deflationary_phase_daa_score": 0, "pre_deflationary_phase_base_subsidy": 50000000000, "skip_proof_of_work": false, "max_block_level": 250, "pruning_proof_m": 1000, "blockrate": {"target_time_per_block": 1000, "ghostdag_k": 18, "past_median_time_sample_rate": 10, "difficulty_sample_rate": 4, "max_block_parents": 10, "mergeset_size_limit": 180, "merge_depth": 60, "finality_depth": 720, "pruning_depth": 13838, "coinbase_maturity": 2}, "pre_crescendo_target_time_per_block": 1000, "crescendo_activation": 0, "genesis_bits": 487587840, "finality": {"checkpoint_interval": 30, "checkpoint_depth": 20, "weight_window": 120, "dust": 5, "presence_window": 1, "aggregators": 8, "equivocation_ban": 120, "min_daa": 120, "aggregator_fallback": 1}, "pow_epoch_blocks": 60, "pow_epoch_lead": 10, "pow_day_ms": 1440000, "finality_v3_activation_daa": 0, "program_class_v3_activation_daa": 18446744073709551615} \ No newline at end of file +{"timestamp_deviation_tolerance": 132, "past_median_time_window_size": 27, "difficulty_window_size": 661, "min_difficulty_window_size": 150, "difficulty_rule": "igneum-dual", "coinbase_payload_script_public_key_max_len": 150, "max_coinbase_payload_len": 16384, "max_tx_inputs": 1000, "max_tx_outputs": 1000, "max_signature_script_len": 250000, "max_script_public_key_len": 10000, "mass_per_tx_byte": 1, "mass_per_script_pub_key_byte": 10, "mass_per_sig_op": 1000, "block_mass_limits": {"compute": 500000, "storage": 500000, "transient": 1000000}, "block_lane_limits": {"lanes_per_block": 50, "gas_per_lane": 1000000000}, "storage_mass_parameter": 1000000000000, "deflationary_phase_daa_score": 0, "pre_deflationary_phase_base_subsidy": 50000000000, "skip_proof_of_work": false, "max_block_level": 250, "pruning_proof_m": 1000, "blockrate": {"target_time_per_block": 1000, "ghostdag_k": 18, "past_median_time_sample_rate": 10, "difficulty_sample_rate": 4, "max_block_parents": 10, "mergeset_size_limit": 180, "merge_depth": 60, "finality_depth": 720, "pruning_depth": 13838, "coinbase_maturity": 2}, "pre_crescendo_target_time_per_block": 1000, "crescendo_activation": 0, "genesis_bits": 487587840, "finality": {"checkpoint_interval": 30, "checkpoint_depth": 20, "weight_window": 120, "dust": 5, "presence_window": 1, "aggregators": 8, "equivocation_ban": 120, "min_daa": 120, "aggregator_fallback": 1}, "pow_epoch_blocks": 60, "pow_epoch_lead": 10, "pow_day_ms": 1440000, "finality_v3_activation_daa": 0, "program_class_v3_activation_daa": 18446744073709551615, "pow_genesis_dataset_log2": 28} \ No newline at end of file From e08909f138d4f33a2d61ed7964b6db14e14db96a Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:53:12 +0000 Subject: [PATCH 056/158] mixer x4 and the cache growth rule (Counter ASIC 2.0, class v3 construction): LoadClass mixer_mult and growth, LoadClass::MX4 (v2 loads, no width roll), memhard::Shape in MixParams, m mixer applications per round with keys round_key(r m + j), Cache::fill_log2, the option C schedule (growth_doublings, cache_log2_words, dataset_log2_words, days_since_genesis) with its test table, day-sized Epoch entries, the three emitters (m loop only for m > 1, v2 text unchanged), program.h and program.json fields, packfile.h mixerMult, packbench and OpenCL host prints, --class mx4 and --days on the CLI; docs/plans/mixer-x4.md design and spec text, docs/analysis/chip-model-v3.md, the 5090 and 9070 XT dataset-build playbooks (measurements and vectors to follow) Co-Authored-By: Claude Fable 5.1 --- docs/analysis/chip-model-v3.md | 86 ++++++++ docs/plans/mixer-x4.md | 151 +++++++++++++ igneum-pow/src/emit.rs | 129 ++++++++--- igneum-pow/src/generator.rs | 138 +++++++++--- igneum-pow/src/lib.rs | 4 +- igneum-pow/src/main.rs | 33 ++- igneum-pow/src/memhard.rs | 273 ++++++++++++++++++++++-- igneum-pow/src/verify.rs | 71 ++++-- proto-cuda/nvrtc/packfile.h | 4 + proto-metal/packbench.swift | 4 +- proto-opencl/host.c | 6 +- relay/playbooks/mixer-x4-5090-bench.ps1 | 63 ++++++ relay/playbooks/mixer-x4-9070-bench.ps1 | 64 ++++++ 13 files changed, 925 insertions(+), 101 deletions(-) create mode 100644 docs/analysis/chip-model-v3.md create mode 100644 docs/plans/mixer-x4.md create mode 100644 relay/playbooks/mixer-x4-5090-bench.ps1 create mode 100644 relay/playbooks/mixer-x4-9070-bench.ps1 diff --git a/docs/analysis/chip-model-v3.md b/docs/analysis/chip-model-v3.md new file mode 100644 index 00000000..8a48ca7a --- /dev/null +++ b/docs/analysis/chip-model-v3.md @@ -0,0 +1,86 @@ +# The on-die-cache recompute chip against the RTX 5090, class v2 and class v3, everything combined + +5 October 2026 (night), Counter ASIC 2.0, worker ca2-mixer. The model is M16's +(`docs/analysis/m16-recompute-attacker-2026-10-05.md`): the strongest chip the plan has priced holds the whole +cache in SRAM and derives every dataset item instead of reading it, so its cost per hash is item derivations, +and its rate at a 50 T op/s integer budget (an RTX 5090's, approximate) is `50 T / (ops per hash)`. Nothing here +is a measurement of a chip; every GPU figure says where it was measured. "Approximate" marks a figure from memory. + +## 1. Inputs + +| Input | Value | Source | +|---|---|---| +| Items per hash | 128 (one item per load, 128 loads per hash, median 128.00 distinct) | spec 01 sections 1.4.2 and 1.8.5; the 20,000-program census | +| Integer operations per mixer application | about 130 | spec 01 section 1.8.4 | +| Mixer applications per item | 9 under v2; 36 under v3 (`m = 4`, `docs/plans/mixer-x4.md`) | `memhard::Shape::mixers_per_item` | +| Integer operations per item | 1,170 (v2); 4,680 (v3) | 9 x 130; 36 x 130 | +| Integer operations per hash | 149,760 (v2, "150,000"); 599,040 (v3, "600,000") | 128 x the above | +| Chip integer budget | 50 T op/s (approximate: 21,760 ALUs at about 2.4 GHz, one 32-bit operation each per clock) | M16 section 3 | +| Fixed-function factor | 3x (approximate, from memory: 2x to 5x is the usual credit for a pipeline with no scheduling or divergence) | M16 section 3 | +| RTX 5090, version 2 programs, measured | 136.1 MH/s (readwidth, tonight, `docs/plans/read-width.md`, pack w4 on PC 2); 139.7 MH/s (M11, 4 October, `docs/bench-log.md`) | this analysis uses tonight's 136.1 as the denominator and quotes both | +| RTX 5090 at w16 (16-byte loads), measured | 139.8 MH/s | readwidth table, tonight (the width stays 4 B: w16 closes nothing) | +| Cache mirror, 256 MiB, N5 headline density | 128 mm^2, $46 per good die (64 mm^2, $21 at the bit-cell lower bound) | `docs/analysis/sram-mirror.md` revision 2, sections 4 and 5 (`ca2-analysis` e6085c6) | +| Cache mirror plus a 96 MB hot table, N5 headline | 175 mm^2, $68 | same, so a hot table costs 0.49 mm^2 and $0.23 per MB (linear, approximate) | +| 512 MiB and 1 GiB mirrors, N5 headline | 255 mm^2 and 510 mm^2; $111 to $306 | same, section 4 (the growth rule's cache at years 4 and 12, priced at today's node) | +| GPU-class die | 750 mm^2 (the equal-silicon comparison) | M16 section 3 | + +## 2. The rows + +Chip rate = 50 T op/s / ops per hash. "Bare" = chip rate / 136.1 MH/s. "With the factor" = bare x 3. "Equal +silicon" = bare x (750 - SRAM) / 750 x 3: the SRAM takes die area the logic does not get, the M16 convention +("minus the area the SRAM takes"). SRAM in mm^2 and dollars at the N5 headline density. + +| Row | Mixer | Ops per hash | Chip rate at 50 T op/s | SRAM the chip holds | mm^2 / $ (N5 headline) | Bare gain against 136.1 MH/s | With the 3x factor | Equal silicon, SRAM deducted, with the factor | +|---|---|---|---|---|---|---|---|---| +| v2 as shipped (the M16 and scratch-soundness row) | x1 | 149,760 | 334 MH/s | 256 MiB | 128 / $46 | 2.45x (2.39x against 139.7) | 7.4x | 6.1x | +| v2 at w16 (not adopted; the chip's cost is items, not bytes: unchanged) | x1 | 149,760 | 334 | 256 MiB | 128 / $46 | 2.39x against 139.8 | 7.2x | 5.9x | +| v3: mixer x4 | x4 | 599,040 | 83.5 MH/s | 256 MiB | 128 / $46 | 0.61x | 1.84x | 1.53x | +| v3 plus a 32 MiB hot table, added form (16 dataset loads and k hot loads) | x4 | 599,040 (a hot load is one SRAM read, no item) | 83.5 | 288 MiB | 144 / $53 | 0.61x or below (owed: the 5090's added-form rate; the hot loads cost it something, the chip nothing) | 1.84x or below | 1.49x | +| v3 plus a 64 MiB hot table, added form | x4 | 599,040 | 83.5 | 320 MiB | 160 / $61 | 0.61x or below (owed) | 1.84x or below | 1.45x | +| v3 at year 4 (cache 512 MiB under option C, dataset 4 GiB), 64 MiB hot table | x4 | 599,040 | 83.5 | 576 MiB | 287 / $130 | 0.61x | 1.84x | 1.14x | +| v3 at year 12 (cache 1 GiB, dataset 8 GiB), 64 MiB hot table | x4 | 599,040 | 83.5 | 1,088 MiB | 542 / $330 | 0.61x | 1.84x | 0.51x | +| v3 with the mixer at x8 instead (the next lever, not adopted) | x8 | 1,198,080 | 41.7 | 256 MiB | 128 / $46 | 0.31x | 0.92x | 0.76x | + +The era draws of spec 1.13.1 cost the chip nothing in this model: the mixer round count is not drawn, the op +weights and fold rotations change the program, not the item derivation, so the chip's ops per hash stand. The +width rule (4-byte loads kept) changes nothing either: w16 would have moved the honest denominator by 2.7% and the +chip's cost not at all. + +Arithmetic, row v3: 36 x 130 = 4,680 ops per item; x 128 = 599,040 per hash; 50 x 10^12 / 599,040 = 83.5 x 10^6 +hashes per second; 83.5 / 136.1 = 0.613; x 3 = 1.84; equal silicon (750 - 128) / 750 = 0.829, x 1.84 = 1.53. +Hot table rows: 32 MiB x 0.49 mm^2 per MB = 16 mm^2, 64 MiB = 32 mm^2 (the 96 MB column of `sram-mirror.md` +scaled linearly); (750 - 144) / 750 = 0.808 and (750 - 160) / 750 = 0.787. Year 4 and 12 rows: the mirror of +`sram-mirror.md` section 4 at N5 for 512 MiB and 1 GiB plus the 64 MiB table, at today's density (the node of +those years is denser by about 1.8x at year 10 on the trend the same file cites; the row is a floor on the area, +not a forecast). + +## 3. The margin, plainly + +The combined headline row reads 1.84x with the 3x factor at an equal integer budget, 1.5x with the SRAM area +deducted. The claim is "under 2x", and the margin is thin: + +- the 3x fixed-function factor is approximate and from memory; at 3.3x the equal-budget row reads 2.0x; +- the denominator is one card's measured rate on one night (136.1 against 139.7 the night before: 2.6% apart); +- the 50 T op/s budget is approximate; a chip at 55 T op/s reads 2.0x; +- the hot table in the added form lowers the honest denominator by whatever the hot loads cost the GPU (owed from + the PC rows), which raises the chip's gain by the same share, 1.84x or more if the hot loads are free, higher if + not; the hot table's only cost to this chip is 16 to 32 mm^2 of die. + +What keeps it under 2x is the mixer, and nothing else in Counter ASIC 2.0 moves this chip (the scratch at any share +gave 2.4x, `docs/analysis/scratch-soundness.md` section 3.4; the hot table taxes the DRAM-only chip, not this one; +the cache growth taxes it only in die area, which is cheap at year 0 and real at year 12). The next levers, in +order: + +1. Mixer x8: 0.18x bare and 0.55x with the factor in the M16 table (0.31x and 0.92x against 136.1 here; the M16 + table's denominator is 229 MH/s); the CPU verifier at 3.3 to 9.6 ms per warp scaled from the version 1 range, + at the edge of the 10 ms gate; the measured v3 row of `docs/plans/mixer-x4.md` section 6 is what to scale from + now, and whether a 2019-class laptop core (unmeasured, O-1.14) passes 10 ms is what decides it. +2. The hot table: adopted or not on the PC rows (`docs/plans/hot-table.md`); in the added form it costs the GPU + nothing it was not already paying in cache misses and the chip die area only, so it is the second lever for the + chip only through area and the first against a DRAM-only chip. + +## 4. What this does not settle + +The items of M16 section 5 stand: the inline kernel on NVIDIA with a 64 MiB cache inside L2 (a measured point +under the "50 T op/s" row) is a PC job not yet run; the time-memory curve (O-1.6) is not drawn; the mixer has had +no cryptanalysis, and a shortcut inside it cuts the 4,680 directly; no chip has been priced beyond its SRAM. diff --git a/docs/plans/mixer-x4.md b/docs/plans/mixer-x4.md new file mode 100644 index 00000000..2635da05 --- /dev/null +++ b/docs/plans/mixer-x4.md @@ -0,0 +1,151 @@ +# Mixer x4 and the cache growth rule: the class v3 dataset construction + +5 October 2026 (night). Counter ASIC 2.0, layer 6 (option C) and ledger M16's lever, decided by the coordinator +under the project lead's delegation at 22:00 UTC (`docs/plans/counter-asic-2-status.md`, "22:00 decided"; the project lead confirms for +the public testnet genesis). Branch `ca2-mixer`. Worker: ca2-mixer (cryptographer's lane). + +What this changes, in one line: under program class v3 every mixer application of the dataset item derivation +becomes four applications with distinct round keys, the eight dependent cache reads per item stay eight, and the +256 MiB cache doubles on the days the dataset doubles (years 4 and 12). Version 2 is byte-identical: the two +pinned packs re-export without a changed byte (section 5). + +## 1. Why this form + +The recompute attacker of `docs/analysis/m16-recompute-attacker-2026-10-05.md` holds the 256 MiB cache on a die +and derives every dataset word instead of reading it: 128 items per hash at about 1,170 integer operations and 8 +dependent cache reads each. Its cost is linear in operations per item; the honest miner pays the mixer once a day +in the dataset build and never per hash; the verifier pays it per item it checks. The multiplier `m` is the one +parameter that moves the attacker and leaves the honest hash rate untouched. + +Two shapes give the attacker 4x the operations: + +| Shape | Mixer applications per item | Dependent cache reads per item | What grows for the verifier | What grows for the chip | What grows for the honest build | +|---|---|---|---|---|---| +| A, chosen: `m = 4` applications per round, 8 rounds | 36 | 8 | the ALU part only; the latency part (8 dependent misses per item) unchanged | integer operations 4x; SRAM bandwidth unchanged (1,024 reads per hash) | 4x the mixer arithmetic, same reads | +| B, alternative: 32 rounds of one application and one read | 33 | 32 | both parts: 4x the dependent misses per item, so about 4x the latency-bound time (spec 1.11: about 8 x 100 ns per item in series without interleaving) | integer operations 3.7x and SRAM bandwidth 4x (4,096 reads per hash, 60 TB/s to match one 5090 at the M16 rate) | 4x the reads too; the GPU build becomes latency-bound at 4x the dependent line fetches | + +Shape A is chosen because the verifier's latency part is the part the 10 ms gate protects (section 1.11: the +distinct items of a unit are derived with their chains interleaved so the 8 misses of each item overlap across up +to 32 items; shape B would make that 32 misses deep). Shape B is implemented nowhere; the rule in the brief: if +shape A's measured verifier time exceeds 4.8 ms per warp on one M5 Max core, measure both and recommend. Section 6 +has the measurement; it is under that bound, so B stays unimplemented. + +## 2. Spec text (replaces 1.8.5 and 1.13.3 under class v3; v2 text unchanged) + +### 1.8.5 Item derivation and dataset mapping + +Item `t` (16 words) under mixer multiplier `m` (`m = 1` for program class v2, `m = 4` for class v3; a class +parameter, `LoadClass::mixer_mult`): + +``` +s[i] = K[i] for i in 0..7 +s[8 + i] = t * MUL[i] + RC[i] for i in 0..7 +for r in 0..7: + for j in 0..m-1: + s = M(s, rk = (r * m + j + 1) * 0x9E3779B9) + a = s[0] AND (2^(C - 4) - 1) cache line index, 2^(C - 4) lines of a 2^C-word cache + s[i] = s[i] XOR cache[line a][i] for i in 0..15 +for j in 0..m-1: + s = M(s, rk = (8 * m + j + 1) * 0x9E3779B9) +item(t) = s +``` + +`M(s, rk)` is the mixer of 1.8.4 with round key `rk`; under `m = 1` the keys are `(r + 1) * 0x9E3779B9` and +`9 * 0x9E3779B9`, the version 2 text exactly. The round keys of the `9 m` applications are the first `9 m` values +of the version 2 key sequence, all distinct (the sequence is `k * 0x9E3779B9` for `k = 1 .. 9 m`, and +`0x9E3779B9` is odd, so no two of the first 2^32 keys coincide). Eight dependent cache reads per item at every +`m` (`ITEM_ROUNDS = 8`, prototype value): the address of read `r` depends on every earlier read. `9 m` mixer +applications, 36 under class v3, about 4,700 integer operations per item (130 per application, 1.8.4). +`dataset[w] = item(w >> 4)[w AND 15]`. A dataset of 2^D words is the prefix of items `0 .. 2^(D-4) - 1`, so an +item has the same value at every dataset size; and a cache of 2^C words is the prefix of segments of every +larger cache (1.8.3 fills segments independently of the cache size), but an item's value depends on `C` through +the line mask, so the item changes on the day the cache doubles. + +Source: `igneum-pow/src/memhard.rs` (`derive_items`, `round_key_mult`, `Shape`), the emitted `mh_item` of +`memhard.h`, `memhard.metal` and `kernel.cl` (`emit.rs`, `emit_memhard_core`: the `m` loop is emitted only for +`m > 1`, so every version 2 pack keeps its text). + +### 1.13.3 Dataset growth (class v3: option (b) with the cache tied to it, "option C") + +Designed: 2 GiB at genesis plus 0.5 GiB per year. The linear schedule in bytes, `G x (1 + 86,400 d / +(4 x 31,536,000)) = G x (1 + d / 1,460)` for the genesis size `G` and the chain day `d` (DAA days since genesis, +section 1.12), doubles at day 1,460 (year 4), quadruples at day 4,380 (year 12), reaches 8x at day 10,220 +(year 28). Rule (Designed, decided 5 October 2026 for class v3): + +``` +doublings(d) = floor(log2(1 + d / 1460)) integer division, then integer log2 +dataset_words(d) = 2^(D_0 + doublings(d)) D_0 = 29 designed (2 GiB), 28 on the devnet (1 GiB); capped at 32 +cache_words(d) = 2^(26 + doublings(d)) 256 MiB, 512 MiB from year 4, 1 GiB from year 12 +``` + +Power-of-two sizes only (option (b)), so every load keeps the `src AND MASK` form of 1.14 item 2 and the cache +line index keeps `s[0] AND mask`. The cache doubles exactly when the dataset doubles ("option C", +`docs/analysis/sram-mirror.md` section 7): the cache's job is to stay above any GPU's last-level cache and that +needs growth; the recompute attacker is priced by the mixer, not by the cache (section 7 below). + +`d` is `day_index(header.timestamp) - day_index(genesis.timestamp)` with `day_index = timestamp_ms / 86,400,000` +(`bind::day_index`, the interim day rule), clamped at 0 (`memhard::days_since_genesis`). Under class v2 nothing +grows: the cache is 2^26 words and the dataset the genesis size on every day. + +| Chain day `d` | Years | `doublings` | Cache words | Cache | Dataset words (devnet `D_0 = 28`) | Dataset (designed `D_0 = 29`) | Verifier cache fill, one M5 Max core (measured at 256 MiB, section 6, scaled linearly) | +|---|---|---|---|---|---|---|---| +| 0 to 1,459 | 0 to 4 | 0 | 2^26 | 256 MiB | 2^28 (1 GiB) | 2 GiB | 0.18 s | +| 1,460 to 4,379 | 4 to 12 | 1 | 2^27 | 512 MiB | 2^29 (2 GiB) | 4 GiB | 0.36 s | +| 4,380 to 10,219 | 12 to 28 | 2 | 2^28 | 1 GiB | 2^30 (4 GiB) | 8 GiB | 0.72 s | +| 10,220 to 21,899 | 28 to 60 | 3 | 2^29 | 2 GiB | 2^31 (8 GiB) | 16 GiB | 1.4 s | +| 21,900 and on | 60 and on | 4 | 2^30 | 4 GiB | 2^32 (16 GiB, the index cap) | 2^32 words, the cap | 2.9 s | + +Test: `memhard::tests::growth_schedule_table` pins every row and the day before each step. The devnet pack +`igneum-devnet-v4-epoch0` is day 20,730 of the Unix count against genesis day 20,729, `d = 1`, so every existing +size and vector stands. + +Consequences for the tiers (the rule of 5 October): a verifier (any node, any pool core) holds 512 MiB from year 4 +and 1 GiB from year 12, and fills it once a day in under a second on one 2026 core (the table); a miner's card +holds the dataset, 4 GiB from year 4 and 8 GiB from year 12 on the designed schedule, so an 8 GB card mines until +year 12 and a 16 GB card until year 28 (the cache is not in the card's working set at hash time: it is built, +the dataset built from it, and dropped). Those dates are the design document's own schedule restated as steps; +option (a) would have faded a 4 GiB card out in year 4 instead of year 4. + +## 3. Interfaces + +| Item | Where | Note | +|---|---|---| +| `LoadClass { mixer_mult: u8, growth: bool }`, `LoadClass::MX4` ("mx4"), `with_mixer(m, growth)`, `v2_loads()`, `takes_width_roll()` | `generator.rs` | a class with v2 loads takes no width roll: its program stream is version 2's draw for draw, so the v3 program of a seed is the v2 program of that seed, only the dataset differs | +| `Shape { mixer_mult, cache_log2_words }`, `Shape::for_class_day(class, d)`, `MixParams.shape`, `Cache::fill_log2(key, log2)`, `round_key_mult(r, j, m)` | `memhard.rs` | `Shape::V2` is version 2 | +| `growth_doublings(d)`, `cache_log2_words(d)`, `dataset_log2_words(D_0, d)`, `days_since_genesis(day, genesis_day)` | `memhard.rs` | the schedule, one function and its two sizes | +| `DatasetSource::{new_shape, from_key_shape, shape}`, `Epoch::new_class_day`, `Epoch::from_seed_bytes_day(epoch, day, label, class, d, D_0)` | `verify.rs` | the day-sized entries; the v2 entries are unchanged and build the v2 shape | +| `IGNEUM_MIXER_MULT`, `IGNEUM_CLASS_MIXER_MULT`, `IGNEUM_CACHE_GROWTH` in program.h; `"mixer_mult"`, `"cache_growth"`, the `"item"` string in program.json | `emit.rs` | written only for a class with `m != 1` or growth, so v2 packs do not change | +| `packfile.h` `mixerMult`; `packbench` and the OpenCL host print the multiplier and the cache size | the three hosts | the kernels carry the construction in their text (one emitter, three dialects); the hosts size the cache from `IGNEUM_CACHE_LOG2_WORDS` already (packbench.swift line 56, host.cu line 65, host.c line 1966) | +| `igneum-pow --class mx4 [--days d]` on every command | `main.rs` | `--days` sizes the cache for a growth class | + +Under the ca2-v3 seam (`ProgramClass::V3`, `V3_CLASS`), the integration sets `V3_CLASS = LoadClass::MX4`; the +chain's day-sized dataset needs the day index, so `Epoch::chain_dataset(day, class)` builds the genesis-size +cache and a `chain_dataset_day(day_bytes, class, d, D_0)` beside it is the growth entry (section 9, owed to the +node agent). + +## 4. Vectors (class v3, `proto-cuda/packs-ca2-mixer/`) + +Filled in section 6 from the exported packs: `mx4-genesis` (seed `igneum-genesis`, day `2026-10-03`, 2^28 words, +2^26-word cache) and `mx4-devnet-epoch0` (the devnet genesis hash as the epoch seed, day bytes of 2026-10-04). + +## 5. The v2 path is byte-identical + +`cargo test --test packs` regenerates every file of `igneum-genesis-mh` and `igneum-devnet-v4-epoch0` from +`program.json` and compares byte for byte (`emitted_sources_match_all_packs`, `export_pack_matches_all_packs`); +section 6 also records a fresh `igneum-pow export` of both packs diffed against the checked-in directories. + +## 6. Measurements + +Filled as they land. Every row names the machine, the date, the command and the lock mode. + +## 7. The chip model + +`docs/analysis/chip-model-v3.md`. + +## 8. What is unverified + +Filled at the end. + +## 9. Owed + +Filled at the end. diff --git a/igneum-pow/src/emit.rs b/igneum-pow/src/emit.rs index a788c25a..1b802481 100644 --- a/igneum-pow/src/emit.rs +++ b/igneum-pow/src/emit.rs @@ -11,8 +11,8 @@ use crate::generator::{Instr, Op, Program, ProgramClass, GENERATOR_VERSION, INSTR_COUNT, ITERATIONS, LOAD_SLOTS}; use crate::memhard::{ - MixParams, CACHE_LINES_PER_SEGMENT, CACHE_LINE_MASK, CACHE_LOG2_WORDS, CACHE_SEGMENTS, CACHE_SEGMENT_LOG2_LINES, - CACHE_TAG, CACHE_WORDS, CHACHA_ROUNDS, CHACHA_SIGMA, ITEM_ROUNDS, + MixParams, Shape, CACHE_LINES_PER_SEGMENT, CACHE_SEGMENT_LOG2_LINES, CACHE_TAG, CHACHA_ROUNDS, CHACHA_SIGMA, + ITEM_ROUNDS, }; use crate::seed::SplitMix64; use crate::verify::{DatasetMode, DatasetSource, Epoch, FOLD_MUL, FOLD_ROT}; @@ -100,12 +100,25 @@ fn class_header_lines(p: &Program) -> String { return String::new(); } let mut s = String::new(); - s.push_str("// Read-width experiment (5 October 2026, docs/plans/read-width.md): NOT the lottery hash. A load of W words reads + if p.class.v2_loads() { + s.push_str("// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item "); - s.push_str("// the W-word-aligned address and folds every word into dst: x = dst ^ w[0]; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x. + s.push_str("// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. "); + } else { + s.push_str("// Read-width experiment (5 October 2026, docs/plans/read-width.md): NOT the lottery hash. A load of W words reads +"); + s.push_str("// the W-word-aligned address and folds every word into dst: x = dst ^ w[0]; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x. +"); + } s.push_str(&format!("#define IGNEUM_LOAD_CLASS {} ", jstr(&p.class.name()))); + if p.class.mixer_mult != 1 || p.class.growth { + s.push_str(&format!("#define IGNEUM_CLASS_MIXER_MULT {} +", p.class.mixer_mult)); + s.push_str(&format!("#define IGNEUM_CACHE_GROWTH {} // 1: cache words = 2^(26 + doublings(day)), doublings = floor(log2(1 + day / 1460)) +", p.class.growth as u8)); + } s.push_str(&format!("#define IGNEUM_LOAD_SLOTS {} ", p.class.load_slots)); s.push_str(&format!("#define IGNEUM_LOAD_MIX {{ {}, {}, {} }} @@ -257,12 +270,19 @@ pub enum LoadSource<'a> { InlineMemhard(&'a MixParams), } -fn log2_segments() -> usize { - CACHE_SEGMENTS.trailing_zeros() as usize +fn log2_segments(shape: &Shape) -> usize { + shape.log2_segments() as usize } -/// The memory-hard core as source text (`emitMemhardCore`). Every parameter is a literal. +/// The memory-hard core as source text (`emitMemhardCore`). Every parameter is a literal: the mixer constants of +/// the day and, from `mp.shape`, the cache size and the mixer multiplier `m`. Under `m = 1` the text is version +/// 2's byte for byte; under `m > 1` the item loop applies `mh_mixer` `m` times per round with the keys +/// `round_key(r m + j)` (spec 01 section 1.8.5 under class v3). pub fn emit_memhard_core(mp: &MixParams, dialect: CoreDialect) -> String { + let shape = &mp.shape; + let m = shape.mixer_mult; + let cache_log2_words = shape.cache_log2_words; + let cache_line_mask = shape.cache_line_mask(); let (u, fn_, cptr, wptr, lptr, lcptr) = match dialect { CoreDialect::Metal => { ("uint", "inline", "device const uint*", "device uint*", "thread uint*", "const thread uint*") @@ -274,18 +294,23 @@ pub fn emit_memhard_core(mp: &MixParams, dialect: CoreDialect) -> String { }; let k = &mp.key; let r = &mp.rot; - let m = &mp.mul; + let mul = &mp.mul; let c = &mp.rc; let mut s = String::with_capacity(6000); s.push_str(&format!( "// Memory-hard dataset core (MEMHARD.md). Cache: 2^{} words in 2^{} segments of {} chained ChaCha{} lines.\n", - CACHE_LOG2_WORDS, - log2_segments(), + cache_log2_words, + log2_segments(shape), CACHE_LINES_PER_SEGMENT, CHACHA_ROUNDS )); - s.push_str("// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.\n"); - s.push_str(&format!("#define MH_CACHE_LINE_MASK {}\n", hex(CACHE_LINE_MASK))); + if m == 1 { + s.push_str("// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.\n"); + } else { + s.push_str(&format!("// Item: 8 rounds of {m} x seed-parameterised mixer + one 64-byte cache read, then {m} x final mixer (class v3, mixer multiplier {m},\n")); + s.push_str("// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals.\n"); + } + s.push_str(&format!("#define MH_CACHE_LINE_MASK {}\n", hex(cache_line_mask))); s.push_str(&format!("#define MH_SEGMENT_LINES {}u\n", CACHE_LINES_PER_SEGMENT)); s.push_str("#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }\n"); s.push_str(&format!( @@ -345,7 +370,7 @@ pub fn emit_memhard_core(mp: &MixParams, dialect: CoreDialect) -> String { s.push_str("// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.\n"); s.push_str(&format!("{fn_} void mh_mixer({lptr} s, {u} rk) {{\n")); for i in 0..16 { - s.push_str(&format!(" s[{i}] = (s[{i}] ^ ({} + rk)) * {};\n", hex(c[i]), hex(m[i]))); + s.push_str(&format!(" s[{i}] = (s[{i}] ^ ({} + rk)) * {};\n", hex(c[i]), hex(mul[i]))); } let col = (0..4).map(|i| format!("{}u", r[i])).collect::>().join(", "); let dia = (4..8).map(|i| format!("{}u", r[i])).collect::>().join(", "); @@ -355,22 +380,39 @@ pub fn emit_memhard_core(mp: &MixParams, dialect: CoreDialect) -> String { s.push_str(&format!(" MH_QR(s[2], s[7], s[8], s[13], {dia}) MH_QR(s[3], s[4], s[9], s[14], {dia})\n")); s.push_str("}\n"); s.push('\n'); - s.push_str(&format!( - "// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); {ITEM_ROUNDS} rounds of mixer + cache line s[0] & mask; final mixer.\n" - )); + if m == 1 { + s.push_str(&format!( + "// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); {ITEM_ROUNDS} rounds of mixer + cache line s[0] & mask; final mixer.\n" + )); + } else { + s.push_str(&format!( + "// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); {ITEM_ROUNDS} rounds of {m} x mixer + cache line s[0] & mask; {m} x final mixer.\n" + )); + } s.push_str(&format!("{fn_} void mh_item({cptr} cache, {u} t, {lptr} s) {{\n")); for i in 0..8 { s.push_str(&format!(" s[{i}] = {};\n", hex(k[i]))); } for i in 0..8 { - s.push_str(&format!(" s[{}] = t * {} + {};\n", 8 + i, hex(m[i]), hex(c[i]))); + s.push_str(&format!(" s[{}] = t * {} + {};\n", 8 + i, hex(mul[i]), hex(c[i]))); } s.push_str(&format!(" for ({u} r = 0u; r < {ITEM_ROUNDS}u; ++r) {{\n")); - s.push_str(" mh_mixer(s, 0x9E3779B9u * (r + 1u));\n"); + if m == 1 { + s.push_str(" mh_mixer(s, 0x9E3779B9u * (r + 1u));\n"); + } else { + s.push_str(&format!(" for ({u} j = 0u; j < {m}u; ++j) mh_mixer(s, 0x9E3779B9u * (r * {m}u + j + 1u));\n")); + } s.push_str(&format!(" {cptr} line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);\n")); s.push_str(&format!(" for ({u} i = 0u; i < 16u; ++i) s[i] ^= line[i];\n")); s.push_str(" }\n"); - s.push_str(&format!(" mh_mixer(s, 0x9E3779B9u * {}u);\n", ITEM_ROUNDS + 1)); + if m == 1 { + s.push_str(&format!(" mh_mixer(s, 0x9E3779B9u * {}u);\n", ITEM_ROUNDS + 1)); + } else { + s.push_str(&format!( + " for ({u} j = 0u; j < {m}u; ++j) mh_mixer(s, 0x9E3779B9u * ({}u + j + 1u));\n", + ITEM_ROUNDS as u32 * m + )); + } s.push_str("}\n"); s.push_str("// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.\n"); s.push_str(&format!( @@ -386,7 +428,7 @@ pub fn metal_memhard(mp: &MixParams) -> String { s.push_str("using namespace metal;\n"); s.push_str(&emit_memhard_core(mp, CoreDialect::Metal)); s.push('\n'); - s.push_str(&format!("// One thread per segment (2^{} threads).\n", log2_segments())); + s.push_str(&format!("// One thread per segment (2^{} threads).\n", log2_segments(&mp.shape))); s.push_str( "kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) {\n", ); @@ -1116,10 +1158,13 @@ pub fn program_header(p: &Program, day: &str, ds: &DatasetSource) -> String { s.push_str(&format!("#define IGNEUM_SEEDW_INIT {{ {} }}\n", join_hex(&p.seed))); if let Some(mp) = memhard { s.push_str(&format!("#define IGNEUM_KEY_INIT {{ {} }}\n", join_hex(&mp.key))); - s.push_str(&format!("#define IGNEUM_CACHE_LOG2_WORDS {CACHE_LOG2_WORDS}\n")); + s.push_str(&format!("#define IGNEUM_CACHE_LOG2_WORDS {}\n", mp.shape.cache_log2_words)); s.push_str(&format!("#define IGNEUM_CACHE_SEGMENT_LOG2_LINES {CACHE_SEGMENT_LOG2_LINES}\n")); - s.push_str(&format!("#define IGNEUM_CACHE_SEGMENTS {CACHE_SEGMENTS}u\n")); + s.push_str(&format!("#define IGNEUM_CACHE_SEGMENTS {}u\n", mp.shape.cache_segments())); s.push_str(&format!("#define IGNEUM_ITEM_ROUNDS {ITEM_ROUNDS}\n")); + if mp.shape.mixer_mult != 1 { + s.push_str(&format!("#define IGNEUM_MIXER_MULT {} // mixer applications per round and after the last read (class v3, docs/plans/mixer-x4.md)\n", mp.shape.mixer_mult)); + } s.push_str(&format!( "#define IGNEUM_MIX_ROT_INIT {{ {} }}\n", mp.rot.iter().map(|r| format!("{r}u")).collect::>().join(", ") @@ -1188,6 +1233,8 @@ pub struct PackVectors { pub cache_last: Vec, /// FNV-1a 64 over the whole cache (memory-hard only) pub cache_fnv: u64, + /// The cache is 2^cache_log2_words words (memory-hard only; 26 under version 2) + pub cache_log2_words: u32, } /// The base nonces of the three vector warps every pack carries. @@ -1249,7 +1296,8 @@ pub fn vectors_header( s.push_str("};\n"); if memhard { s.push_str(&format!( - "// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^{CACHE_LOG2_WORDS} words.\n" + "// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^{} words.\n", + v.cache_log2_words )); s.push_str("static const uint32_t IGNEUM_CACHE_HEAD[16] = {\n"); s.push_str(&format!(" {},\n", join_hex(&v.cache_head[..8]))); @@ -1300,6 +1348,11 @@ pub fn program_json(p: &Program, day: &str, ds: &DatasetSource) -> String { if !p.class.is_v2() { let c = p.width_counts(); s.push_str(&format!(" \"load_class\": {},\n", jstr(&p.class.name()))); + if p.class.mixer_mult != 1 || p.class.growth { + s.push_str(&format!(" \"mixer_mult\": {},\n", p.class.mixer_mult)); + s.push_str(&format!(" \"cache_growth\": {},\n", p.class.growth)); + s.push_str(&format!(" \"mixer\": \"class v3 (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): every mixer application of the item derivation is {} applications with round keys (r * {} + j + 1) * 0x9E3779B9, the 8 dependent cache reads per item unchanged; cache growth rule option C: cache words = 2^(26 + doublings(day)), dataset words = 2^(genesis_log2 + doublings(day)), doublings(day) = floor(log2(1 + day / 1460)) for day = days since genesis\",\n", p.class.mixer_mult, p.class.mixer_mult)); + } s.push_str(&format!(" \"load_slots\": {},\n", p.class.load_slots)); s.push_str(&format!(" \"load_mix_percent_4_16_64\": [{}, {}, {}],\n", p.class.mix[0], p.class.mix[1], p.class.mix[2])); s.push_str(&format!(" \"load_width_counts_4_16_64\": [{}, {}, {}],\n", c[0], c[1], c[2])); @@ -1351,9 +1404,12 @@ pub fn program_json(p: &Program, day: &str, ds: &DatasetSource) -> String { s.push_str(" \"spec\": \"proto-metal/MEMHARD.md\",\n"); s.push_str(&format!(" \"key\": [{}],\n", join_jhex(&mp.key))); s.push_str(" \"key_derivation\": \"the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]\",\n"); + let shape = &mp.shape; s.push_str(&format!( - " \"cache\": {{\"log2_words\": {CACHE_LOG2_WORDS}, \"bytes\": {}, \"line_words\": 16, \"segment_lines\": {CACHE_LINES_PER_SEGMENT}, \"segments\": {CACHE_SEGMENTS}, \"block\": \"ChaCha{CHACHA_ROUNDS} core + feed-forward, rotations 16 12 8 7\", \"sigma\": [{}], \"tag\": [{}], \"chain\": \"in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0\"}},\n", - CACHE_WORDS as u64 * 4, + " \"cache\": {{\"log2_words\": {}, \"bytes\": {}, \"line_words\": 16, \"segment_lines\": {CACHE_LINES_PER_SEGMENT}, \"segments\": {}, \"block\": \"ChaCha{CHACHA_ROUNDS} core + feed-forward, rotations 16 12 8 7\", \"sigma\": [{}], \"tag\": [{}], \"chain\": \"in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0\"}},\n", + shape.cache_log2_words, + shape.cache_words() as u64 * 4, + shape.cache_segments(), join_jhex(&CHACHA_SIGMA), join_jhex(&CACHE_TAG) )); @@ -1364,11 +1420,23 @@ pub fn program_json(p: &Program, day: &str, ds: &DatasetSource) -> String { join_jhex(&mp.rc) )); // The Swift writes jhex(cacheLineMask) here, which breaks the JSON. We write the bare literal. - s.push_str(&format!( - " \"item\": \"s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..{}: s = M_r(s); line = s[0] & 0x{:08x}; s[i] ^= cache[line * 16 + i]; then s = M_{ITEM_ROUNDS}(s); item(t) = s\",\n", - ITEM_ROUNDS - 1, - CACHE_LINE_MASK - )); + if shape.mixer_mult == 1 { + s.push_str(&format!( + " \"item\": \"s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..{}: s = M_r(s); line = s[0] & 0x{:08x}; s[i] ^= cache[line * 16 + i]; then s = M_{ITEM_ROUNDS}(s); item(t) = s\",\n", + ITEM_ROUNDS - 1, + shape.cache_line_mask() + )); + } else { + let m = shape.mixer_mult; + s.push_str(&format!( + " \"mixer_mult\": {m},\n \"item\": \"s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..{}: for j in 0..{}: s = M(s, rk = (r * {m} + j + 1) * 0x9E3779B9); line = s[0] & 0x{:08x}; s[i] ^= cache[line * 16 + i]; then for j in 0..{}: s = M(s, rk = ({} + j + 1) * 0x9E3779B9); item(t) = s\",\n", + ITEM_ROUNDS - 1, + m - 1, + shape.cache_line_mask(), + m - 1, + ITEM_ROUNDS as u32 * m + )); + } s.push_str(" \"word\": \"dataset[w] = item(w >> 4)[w & 15]\"\n"); } else { s.push_str(" \"mode\": \"closed-form\",\n"); @@ -1507,6 +1575,7 @@ pub fn export_pack(epoch: &Epoch, day: &str, source: &str) -> Pack { v.cache_head = w[..16].to_vec(); v.cache_last = w[w.len() - 16..].to_vec(); v.cache_fnv = m.cache.fnv1a64(); + v.cache_log2_words = m.shape().cache_log2_words; } let is_mh = memhard.is_some(); let mut files = vec![ diff --git a/igneum-pow/src/generator.rs b/igneum-pow/src/generator.rs index ce68973e..4658b3c4 100644 --- a/igneum-pow/src/generator.rs +++ b/igneum-pow/src/generator.rs @@ -181,6 +181,13 @@ pub struct LoadClass { /// Variant 5: the scratch per warp in KiB (32 or 128; the whole working set of a card at full occupancy must /// stay under 6 GB, coordinator's cap of 5 October 2026). 0 for every other class. pub scratch_kb: u8, + /// Mixer cost multiplier `m` of the dataset item derivation (Counter ASIC 2.0, M16, decided 5 October 2026 for + /// class v3): every mixer application of spec 01 section 1.8.5 becomes `m` applications with distinct round + /// keys, the 8 dependent cache reads per item unchanged (`memhard::derive_items`). 1 for version 2, 4 for v3. + pub mixer_mult: u8, + /// Cache growth rule, option C (`memhard::growth_doublings`): the cache doubles when the dataset doubles. `false` + /// for version 2 (the cache is 2^26 words on every day), `true` for v3. + pub growth: bool, } /// Scratch geometry (variant 5): 16-byte slots, lane-major, 32 lanes per warp; `scratch_kb` KiB per warp gives @@ -205,20 +212,27 @@ impl LoadClass { impl LoadClass { /// Generator version 2 as adopted on 4 October 2026: 16 loads of one word. The lottery hash. - pub const V2: LoadClass = LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0 }; + pub const V2: LoadClass = + LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 1, growth: false }; + + /// The construction decided for program class v3 on 5 October 2026 (Counter ASIC 2.0, `docs/plans/mixer-x4.md`): + /// version 2 loads (16 slots of one word, no scratch, no width roll, so the program stream is version 2's), the + /// mixer applied 4 times per round, and the cache growth rule. Name "mx4". + pub const MX4: LoadClass = + LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 4, growth: true }; /// A fixed width (1, 4 or 16 words) with `load_slots` loads per program. pub fn fixed(width_words: u8, load_slots: u8) -> LoadClass { let mut mix = [0u8; 3]; let i = WIDTH_WORDS.iter().position(|&w| w == width_words).expect("width must be 1, 4 or 16 words"); mix[i] = 100; - LoadClass { mix, load_slots, scratch: None, scratch_kb: 0 } + LoadClass { mix, load_slots, ..LoadClass::V2 } } /// Per-load width drawn from `mix` (percent for 4, 16, 64 bytes), 16 loads per program. pub fn mixed(mix: [u8; 3]) -> LoadClass { assert_eq!(mix.iter().map(|&m| m as u32).sum::(), 100, "the mix must sum to 100"); - LoadClass { mix, load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0 } + LoadClass { mix, ..LoadClass::V2 } } /// Variant 5: version 2 widths, 16 memory operations of which `k` are scratch read-modify-writes into a @@ -226,11 +240,61 @@ impl LoadClass { pub fn scratch(k: u8, kb: u8) -> LoadClass { assert!(k as usize <= LOAD_SLOTS); assert!(kb.is_power_of_two() && kb <= 128, "scratch per warp must be a power of two up to 128 KiB"); - LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: Some(k), scratch_kb: kb } + LoadClass { scratch: Some(k), scratch_kb: kb, ..LoadClass::V2 } } - /// Parse "p4,p16,p64" or one of the names of [`LoadClass::name`] ("scr4k32": 4 scratch ops, 32 KiB per warp). + /// This class with the mixer multiplier `m` (1, 2, 4, 8 or 16) and the cache growth rule on or off. + pub fn with_mixer(self, mixer_mult: u8, growth: bool) -> LoadClass { + assert!(mixer_mult >= 1 && mixer_mult <= 16 && mixer_mult.is_power_of_two(), "mixer multiplier must be 1, 2, 4, 8 or 16"); + LoadClass { mixer_mult, growth, ..self } + } + + /// The mixer multiplier as the item derivation uses it. + pub fn mixer_mult(&self) -> u32 { + self.mixer_mult as u32 + } + + /// Whether the loads of this class are version 2's: 16 one-word loads, no scratch. Such a class takes no width + /// roll, so its program stream is the version 2 stream draw for draw (the mixer and the cache are properties of + /// the dataset, not of the program). + pub fn v2_loads(&self) -> bool { + self.mix == [100, 0, 0] && self.load_slots as usize == LOAD_SLOTS && self.scratch.is_none() + } + + /// Whether every instruction takes the tenth draw (the width roll): every class whose loads are not version 2's. + pub fn takes_width_roll(&self) -> bool { + !self.v2_loads() + } + + /// Parse "p4,p16,p64" or one of the names of [`LoadClass::name`] ("scr4k32": 4 scratch ops, 32 KiB per warp; + /// "mx4": the v3 construction; a trailing "m" and "g" set the mixer multiplier and the growth rule on any + /// load class, "w16m4g" for example). pub fn parse(s: &str) -> Option { + if s == "mx4" { + return Some(LoadClass::MX4); + } + // the mixer suffix: "...m" then an optional "g" + let (s, growth) = match s.strip_suffix('g') { + Some(base) if base.rsplit_once('m').map(|(_, d)| !d.is_empty() && d.bytes().all(|b| b.is_ascii_digit())).unwrap_or(false) => (base, true), + _ => (s, false), + }; + if let Some((base, digits)) = s.rsplit_once('m') { + if !digits.is_empty() && digits.bytes().all(|b| b.is_ascii_digit()) && !base.is_empty() && !base.ends_with(',') { + let mult: u8 = digits.parse().ok()?; + if mult == 0 || mult > 16 || !mult.is_power_of_two() { + return None; + } + return Some(LoadClass::parse_loads(base)?.with_mixer(mult, growth)); + } + } + if growth { + return None; + } + LoadClass::parse_loads(s) + } + + /// The load part of a class name (no mixer suffix). + fn parse_loads(s: &str) -> Option { if let Some(rest) = s.strip_prefix("scr") { let (k, kb) = rest.split_once('k')?; let k: u8 = k.parse().ok()?; @@ -260,7 +324,7 @@ impl LoadClass { if slots == 0 || slots as usize >= INSTR_COUNT { return None; } - Some(LoadClass { mix, load_slots: slots, scratch: None, scratch_kb: 0 }) + Some(LoadClass { mix, load_slots: slots, ..LoadClass::V2 }) } /// Scratch read-modify-writes per program (0 without a scratch). @@ -272,25 +336,41 @@ impl LoadClass { *self == LoadClass::V2 } - /// "v2", "w4", "w16", "w64", "w64x4", "mix50-35-15", "mix25-50-25x8", "scr4". + /// "v2", "w4", "w16", "w64", "w64x4", "mix50-35-15", "mix25-50-25x8", "scr4k32"; "mx4" for the v3 construction; + /// any other mixer setting appends "m" and, with the growth rule, "g" ("v2m2", "w16m4g"). pub fn name(&self) -> String { if self.is_v2() { return "v2".to_string(); } - if let Some(k) = self.scratch { - return format!("scr{k}k{}", self.scratch_kb); + if *self == LoadClass::MX4 { + return "mx4".to_string(); } - let base = match self.mix { - [100, 0, 0] => "w4".to_string(), - [0, 100, 0] => "w16".to_string(), - [0, 0, 100] => "w64".to_string(), - [a, b, c] => format!("mix{a}-{b}-{c}"), - }; - if self.load_slots as usize == LOAD_SLOTS { - base + let loads = LoadClass { mixer_mult: 1, growth: false, ..*self }; + let base = if loads.is_v2() { + "v2".to_string() + } else if let Some(k) = self.scratch { + format!("scr{k}k{}", self.scratch_kb) } else { - format!("{base}x{}", self.load_slots) + let base = match self.mix { + [100, 0, 0] => "w4".to_string(), + [0, 100, 0] => "w16".to_string(), + [0, 0, 100] => "w64".to_string(), + [a, b, c] => format!("mix{a}-{b}-{c}"), + }; + if self.load_slots as usize == LOAD_SLOTS { + base + } else { + format!("{base}x{}", self.load_slots) + } + }; + let mut s = base; + if self.mixer_mult != 1 || self.growth { + s.push_str(&format!("m{}", self.mixer_mult)); } + if self.growth { + s.push('g'); + } + s } /// The width in words of a load whose width roll (0..99) is `roll`: the first entry of the mix whose cumulative @@ -333,12 +413,11 @@ pub enum ProgramClass { V3, } -/// PLACEHOLDER (ca2-node worker, 5 October 2026): the load class of program class v3 is set to w16 (16 loads of -/// four words, `LoadClass::fixed(4, 16)`) so the node, the miner, the packs and the fast-time gate can be built and -/// run before the Counter ASIC 2.0 measurements decide the width, the per-load mix and the scratch share -/// (`counter-asic-2-rollout.md` section 6). The integration agent on branch ca2-v3 replaces this constant with the -/// decided class; nothing else in the seam names the class, so it is one edit. -pub const V3_CLASS: LoadClass = LoadClass { mix: [0, 100, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0 }; +/// The load class of program class v3, decided 5 October 2026 (Counter ASIC 2.0, `docs/plans/counter-asic-2-status.md` +/// "22:00 decided", `docs/plans/mixer-x4.md`): [`LoadClass::MX4`], version 2 loads (the width stays 4 bytes, the +/// per-load mix and the scratch share are out), the mixer applied 4 times per round and the cache growth rule. The +/// placeholder of the seam (w16) is replaced here; nothing else in the seam names the class. +pub const V3_CLASS: LoadClass = LoadClass::MX4; impl ProgramClass { /// The load class this program class draws from. @@ -494,6 +573,14 @@ pub fn program_id_class(generator: u32, seed: &[u32; 8], attempt: u32, class: &L b.push(k); b.push(class.scratch_kb); } + if class.mixer_mult != 1 || class.growth { + // Counter ASIC 2.0: the mixer multiplier and the growth rule are part of the construction, so a program of + // the same seed under a different mixer carries a different id (under the v3 seam the id is + // program_id(3, seed, attempt) and this branch is not taken) + b.extend_from_slice(b"mixer/"); + b.push(class.mixer_mult); + b.push(class.growth as u8); + } fnv1a64(&b) } @@ -625,7 +712,8 @@ pub fn candidate_from_words_class( let rot = 1 + rng.below(31) as u32; let bit = rng.below(32); let mask = 1u8 << rng.below(5); - let width = if class.is_v2() { 1 } else { class.width_for_roll(rng.below(100)) }; + // Version 2 loads take no width roll, so a mixer class with version 2 loads draws the version 2 program + let width = if class.takes_width_roll() { class.width_for_roll(rng.below(100)) } else { 1 }; let width = if op == Op::Load { width } else { 1 }; if op.is_load() { fresh[src as usize] = false; diff --git a/igneum-pow/src/lib.rs b/igneum-pow/src/lib.rs index 1f95e11a..7cb094d4 100644 --- a/igneum-pow/src/lib.rs +++ b/igneum-pow/src/lib.rs @@ -32,7 +32,7 @@ pub mod verify; pub use bind::{block_init_words, day_bytes, pow256_from_lane, target64_from_le256}; pub use accept::{check as accept_program, AcceptReport, Reject}; -pub use generator::{generate, generate_from_seed_bytes, generate_from_seed_bytes_program_class, Instr, Op, Program, ProgramClass, GENERATOR_VERSION, GENERATOR_VERSION_V3, V3_CLASS}; -pub use memhard::{Cache, MemhardCpu, MixParams}; +pub use generator::{generate, generate_from_seed_bytes, generate_from_seed_bytes_program_class, Instr, LoadClass, Op, Program, ProgramClass, GENERATOR_VERSION, GENERATOR_VERSION_V3, V3_CLASS}; +pub use memhard::{cache_log2_words, dataset_log2_words, days_since_genesis, growth_doublings, Cache, MemhardCpu, MixParams, Shape}; pub use seed::{fnv1a64, seed_words, SplitMix64}; pub use verify::{hash_warp, interpret_warp_init, verify_block, DatasetMode, DatasetSource, Epoch}; diff --git a/igneum-pow/src/main.rs b/igneum-pow/src/main.rs index 1ece8603..a12f225a 100644 --- a/igneum-pow/src/main.rs +++ b/igneum-pow/src/main.rs @@ -13,7 +13,7 @@ use igneum_pow::emit::export_pack; use igneum_pow::generator::LoadClass; -use igneum_pow::memhard::Cache; +use igneum_pow::memhard::{Cache, Shape}; use igneum_pow::seed::day_key; use igneum_pow::verify::{DatasetMode, Epoch, DEFAULT_DATASET_LOG2}; use std::time::Instant; @@ -31,6 +31,8 @@ struct Args { epoch_hex: Option, day_hex: Option, class: LoadClass, + /// Days since genesis for the cache growth rule of a class with `growth` (0: the genesis cache). + days: u64, } fn usage() -> ! { @@ -42,7 +44,8 @@ fn usage() -> ! { \x20 hash-bound --prehash <64 hex> --nonce print the header-bound hash (bind.rs) of one 64-bit nonce\n\ \x20 accept every candidate of the seed (or --epoch-hex) with its acceptance verdict\n\ \x20 show the accepted program, one instruction per line\n\ - \x20 --class C load class (read-width experiment): v2 (default), w4, w16, w64, w64x4, or p4,p16,p64[xN]" + \x20 --class C load class: v2 (default), mx4 (class v3: mixer x4, cache growth), w4, w16, w64, w64x4, p4,p16,p64[xN], m[g]\n\ + \x20 --days N days since genesis for the cache growth rule of a class with it (default 0: the 2^26-word cache)" ); std::process::exit(2) } @@ -61,6 +64,7 @@ fn parse() -> Args { epoch_hex: None, day_hex: None, class: LoadClass::V2, + days: 0, }; let mut it = std::env::args().skip(1); a.cmd = it.next().unwrap_or_else(|| usage()); @@ -78,6 +82,7 @@ fn parse() -> Args { "--epoch-hex" => a.epoch_hex = Some(val()), "--day-hex" => a.day_hex = Some(val()), "--class" => a.class = LoadClass::parse(&val()).unwrap_or_else(|| usage()), + "--days" => a.days = val().parse().unwrap_or_else(|_| usage()), _ => usage(), } } @@ -93,7 +98,7 @@ fn main() { "accept" => accept(&a), "show" => show(&a), "hash" => { - let e = Epoch::new_class(&a.seed, &a.day, mode, a.dataset_log2, a.class); + let e = Epoch::new_class_day(&a.seed, &a.day, mode, a.dataset_log2, a.class, a.days); println!("{:016x}", e.hash(a.nonce as u32)); } "hash-bound" => { @@ -106,7 +111,7 @@ fn main() { let db = igneum_pow::bind::unhex(dh).unwrap_or_else(|| usage()); Epoch::from_seed_bytes_class(&eb, &db, "cli", a.class) } - _ => Epoch::new_class(&a.seed, &a.day, mode, a.dataset_log2, a.class), + _ => Epoch::new_class_day(&a.seed, &a.day, mode, a.dataset_log2, a.class, a.days), }; let init = igneum_pow::bind::block_init_words(&prehash, a.nonce); println!("init words {}", init.iter().map(|w| format!("{w:08x}")).collect::>().join(" ")); @@ -124,24 +129,34 @@ fn bench(a: &Args, mode: DatasetMode) { a.dataset_log2, mode.name() ); + let shape = Shape::for_class_day(&a.class, a.days); if mode == DatasetMode::MemoryHard { // Time the cache fill on its own first (one core), then build the epoch (which fills it again). let t0 = Instant::now(); - let c = Cache::fill(day_key(&a.day)); + let c = Cache::fill_log2(day_key(&a.day), shape.cache_log2_words); let fill_ms = t0.elapsed().as_secs_f64() * 1e3; - println!("cache: fill {fill_ms:.1} ms on one core (2^26 words, 65536 chains of 64 ChaCha12 blocks), FNV-1a 64 {:016x}", c.fnv1a64()); + println!( + "cache: fill {fill_ms:.1} ms on one core (2^{} words, {} MiB, {} chains of 64 ChaCha12 blocks), FNV-1a 64 {:016x}", + shape.cache_log2_words, + shape.cache_words() * 4 / (1 << 20), + c.segments(), + c.fnv1a64() + ); drop(c); } let t0 = Instant::now(); - let e = Epoch::new_class(&a.seed, &a.day, mode, a.dataset_log2, a.class); + let e = Epoch::new_class_day(&a.seed, &a.day, mode, a.dataset_log2, a.class, a.days); let build_ms = t0.elapsed().as_secs_f64() * 1e3; println!( - "program: class {}, {} loads/hash, {} bytes/hash, widths (1,4,16 words) {:?}, {} items/warp, op mix {}; epoch built in {build_ms:.1} ms", + "program: class {}, {} loads/hash, {} bytes/hash, widths (1,4,16 words) {:?}, {} items/warp, mixer x{} ({} mixers/item), cache 2^{} words, op mix {}; epoch built in {build_ms:.1} ms", e.program.class.name(), e.program.loads_per_hash(), e.program.bytes_per_hash(), e.program.width_counts(), e.program.items_per_warp(), + shape.mixer_mult, + shape.mixers_per_item(), + shape.cache_log2_words, e.program.op_mix() ); let bases = [0u32, 4096, 1_000_000]; @@ -175,7 +190,7 @@ fn export(a: &Args, mode: DatasetMode) { let db = igneum_pow::bind::unhex(dh).unwrap_or_else(|| usage()); (Epoch::from_seed_bytes_class(&eb, &db, &format!("igneum-epoch/{eh}/day/{dh}"), a.class), format!("bytes:{dh}")) } - _ => (Epoch::new_class(&a.seed, &a.day, mode, a.dataset_log2, a.class), a.day.clone()), + _ => (Epoch::new_class_day(&a.seed, &a.day, mode, a.dataset_log2, a.class, a.days), a.day.clone()), }; let build_ms = t0.elapsed().as_secs_f64() * 1e3; println!("igneum-pow export {out}"); diff --git a/igneum-pow/src/memhard.rs b/igneum-pow/src/memhard.rs index c6f0727f..b38aba64 100644 --- a/igneum-pow/src/memhard.rs +++ b/igneum-pow/src/memhard.rs @@ -3,12 +3,18 @@ //! ARX-multiply mixer. The verifier holds the cache and never the dataset. //! //! All arithmetic is on u32 modulo 2^32. Rotations are by 1..31 at every call site. +//! +//! Counter ASIC 2.0 (5 October 2026, `docs/plans/mixer-x4.md`, behind the program class): the construction has a +//! [`Shape`], the mixer multiplier `m` and the cache size. Under `m` every mixer application of an item becomes +//! `m` applications with distinct round keys, the 8 dependent cache reads unchanged; the cache doubles when the +//! dataset doubles ([`growth_doublings`]). [`Shape::V2`] (`m = 1`, 2^26 words) is version 2 bit for bit. +use crate::generator::LoadClass; use crate::seed::{day_key, fnv1a64_words, SplitMix64}; pub const CACHE_LOG2_WORDS: usize = 26; pub const CACHE_SEGMENT_LOG2_LINES: usize = 6; -/// 2^26 words = 256 MiB. +/// 2^26 words = 256 MiB (the version 2 cache, and the v3 cache until the first dataset doubling). pub const CACHE_WORDS: usize = 1 << CACHE_LOG2_WORDS; /// 2^22 lines of 16 words. pub const CACHE_LINES: usize = CACHE_WORDS >> 4; @@ -24,6 +30,94 @@ pub const CHACHA_SIGMA: [u32; 4] = [0x61707865, 0x3320646e, 0x79622d32, 0x6b2065 /// "Igne", "umMH". pub const CACHE_TAG: [u32; 2] = [0x49676e65, 0x756d4d48]; +/// The shape of the item derivation and of the cache: the mixer multiplier and the cache size. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct Shape { + /// Mixer applications per round (and after the last read): 1 under version 2, 4 under class v3. + pub mixer_mult: u32, + /// The cache is 2^cache_log2_words words (26 at genesis; 27 and 28 after the dataset doublings of 1.13.3). + pub cache_log2_words: u32, +} + +impl Shape { + /// Version 2: one mixer application per round, a 2^26-word cache. + pub const V2: Shape = Shape { mixer_mult: 1, cache_log2_words: CACHE_LOG2_WORDS as u32 }; + + /// The shape of a load class on day 0 of the chain (and on every day for a class without the growth rule). + pub fn for_class(class: &LoadClass) -> Shape { + Shape::for_class_day(class, 0) + } + + /// The shape of a load class on day `days_since_genesis` of the chain: the class's multiplier, and the cache + /// of [`cache_log2_words`] when the class has the growth rule, else 2^26 words. + pub fn for_class_day(class: &LoadClass, days_since_genesis: u64) -> Shape { + Shape { + mixer_mult: class.mixer_mult(), + cache_log2_words: if class.growth { cache_log2_words(days_since_genesis) } else { CACHE_LOG2_WORDS as u32 }, + } + } + + pub fn is_v2(&self) -> bool { + *self == Shape::V2 + } + pub fn cache_words(&self) -> usize { + 1usize << self.cache_log2_words + } + pub fn cache_lines(&self) -> usize { + self.cache_words() >> 4 + } + pub fn cache_line_mask(&self) -> u32 { + (self.cache_lines() - 1) as u32 + } + pub fn cache_segments(&self) -> usize { + self.cache_lines() >> CACHE_SEGMENT_LOG2_LINES + } + pub fn log2_segments(&self) -> u32 { + self.cache_log2_words - 4 - CACHE_SEGMENT_LOG2_LINES as u32 + } + /// Mixer applications per item: `(ITEM_ROUNDS + 1) x m`. + pub fn mixers_per_item(&self) -> u32 { + (ITEM_ROUNDS as u32 + 1) * self.mixer_mult + } +} + +// -------------------------------------------------------------------------------------------------------------- +// Dataset growth, option C (spec 01 section 1.13.3 option (b) with the cache tied to the dataset's doublings) +// -------------------------------------------------------------------------------------------------------------- + +/// Days per year of the growth schedule: one year = 31,536,000 DAA seconds of 86,400 (spec 01 section 1.13.3). +pub const GROWTH_DAYS_PER_YEAR: u64 = 365; +/// The linear schedule of 1.13.3, 2 GiB at genesis plus 0.5 GiB per year, is `G x (1 + d / 1460)` for the genesis +/// size `G` and the day `d`: it doubles at day 1,460 (year 4), quadruples at day 4,380 (year 12), reaches 8x at +/// day 10,220 (year 28) and 16x at day 21,900 (year 60). +pub const GROWTH_DOUBLING_DAYS: u64 = 4 * GROWTH_DAYS_PER_YEAR; + +/// The number of dataset doublings reached by day `days_since_genesis` of the chain: `floor(log2(1 + d / 1460))`, +/// in integers (`1 + d / 1460` rounded down, then its integer log2, which equals the real log2's floor because a +/// power of two is an integer). 0 until day 1,459; 1 from day 1,460 (year 4); 2 from day 4,380 (year 12). +pub fn growth_doublings(days_since_genesis: u64) -> u32 { + (1 + days_since_genesis / GROWTH_DOUBLING_DAYS).ilog2() +} + +/// The cache size on day `d` under option C: 2^26 words doubled once per dataset doubling (256 MiB, 512 MiB from +/// year 4, 1 GiB from year 12). +pub fn cache_log2_words(days_since_genesis: u64) -> u32 { + CACHE_LOG2_WORDS as u32 + growth_doublings(days_since_genesis) +} + +/// The dataset size on day `d` under option (b) of 1.13.3: the genesis size (2^`genesis_log2_words` words: 28 for +/// the 1 GiB packs and the devnet, 29 for the designed 2 GiB) doubled once per doubling of the linear schedule. The +/// result is capped at 32 (the item index is 32 bits, spec 1.13.3). +pub fn dataset_log2_words(genesis_log2_words: u32, days_since_genesis: u64) -> u32 { + (genesis_log2_words + growth_doublings(days_since_genesis)).min(32) +} + +/// Days since genesis from two day indices of `bind::day_index` (the header's `timestamp_ms / 86,400,000`): the +/// day of the block and the day of the genesis header. A block before the genesis day (clock skew) is day 0. +pub fn days_since_genesis(day_index: u64, genesis_day_index: u64) -> u64 { + day_index.saturating_sub(genesis_day_index) +} + #[inline(always)] fn rotl(x: u32, n: u32) -> u32 { x.rotate_left(n) @@ -66,17 +160,23 @@ pub fn chacha_block(x: &[u32; 16]) -> [u32; 16] { y } -/// Mixer parameters drawn from the day key. Draw order: ROT[0..7] (1..31), MUL[0..15] (odd), RC[0..15]. +/// Mixer parameters drawn from the day key, plus the [`Shape`] the mixer is applied under. Draw order: +/// ROT[0..7] (1..31), MUL[0..15] (odd), RC[0..15]. The shape is not drawn: it is the class's. #[derive(Clone, Debug, PartialEq, Eq)] pub struct MixParams { pub key: [u32; 8], pub rot: [u32; 8], pub mul: [u32; 16], pub rc: [u32; 16], + pub shape: Shape, } impl MixParams { + /// Version 2 shape. pub fn new(key: [u32; 8]) -> Self { + Self::with_shape(key, Shape::V2) + } + pub fn with_shape(key: [u32; 8], shape: Shape) -> Self { let mut rng = SplitMix64::new(key[0] as u64 | ((key[1] as u64) << 32)); let mut rot = [0u32; 8]; let mut mul = [0u32; 16]; @@ -90,7 +190,7 @@ impl MixParams { for c in rc.iter_mut() { *c = rng.next() as u32; } - Self { key, rot, mul, rc } + Self { key, rot, mul, rc, shape } } /// Parameters for a day string: the key is `seed_words("day/" + day)`. pub fn for_day(day: &str) -> Self { @@ -104,6 +204,13 @@ pub fn round_key(r: usize) -> u32 { ((r + 1) as u32).wrapping_mul(0x9E3779B9) } +/// The round key of application `j` (0 <= j < m) of round `r` under multiplier `m`: `round_key(r * m + j)`. For +/// `m = 1` this is `round_key(r)`, version 2's key. +#[inline(always)] +pub fn round_key_mult(r: usize, j: usize, m: usize) -> u32 { + round_key(r * m + j) +} + /// `M_r` on 16 words in place: per word `(s ^ (RC + rk)) * MUL`, then one ChaCha-shaped double round with /// the four column rotations `ROT[0..3]` and the four diagonal rotations `ROT[4..7]`. #[inline(always)] @@ -122,9 +229,11 @@ pub fn mixer(s: &mut [u32; 16], rk: u32, mp: &MixParams) { qr(s, 3, 4, 9, 14, r[4], r[5], r[6], r[7]); } -/// The 256 MiB cache for one day key. +/// The cache for one day key: 2^log2_words words (256 MiB under version 2). pub struct Cache { pub key: [u32; 8], + pub log2_words: u32, + line_mask: u32, words: Vec, } @@ -152,13 +261,22 @@ impl Cache { } } - /// The whole cache on the calling thread: 65,536 chains of 64 ChaCha12 blocks, in segment order. + /// The version 2 cache on the calling thread: 65,536 chains of 64 ChaCha12 blocks, in segment order. pub fn fill(key: [u32; 8]) -> Cache { - let mut words = vec![0u32; CACHE_WORDS]; - for seg in 0..CACHE_SEGMENTS { + Self::fill_log2(key, CACHE_LOG2_WORDS as u32) + } + + /// A cache of 2^`log2_words` words (26, 27 or 28 under the growth rule; smaller sizes for tests): 2^(log2 - 10) + /// independent chains of 64 lines, the same chain function at every size, so a larger cache's first segments + /// are the smaller cache's segments word for word. + pub fn fill_log2(key: [u32; 8], log2_words: u32) -> Cache { + assert!((10..=30).contains(&log2_words), "cache log2 words must be in 10..=30"); + let shape = Shape { mixer_mult: 1, cache_log2_words: log2_words }; + let mut words = vec![0u32; shape.cache_words()]; + for seg in 0..shape.cache_segments() { Self::fill_segment(&mut words, seg, &key); } - Cache { key, words } + Cache { key, log2_words, line_mask: shape.cache_line_mask(), words } } pub fn for_day(day: &str) -> Cache { @@ -170,10 +288,20 @@ impl Cache { &self.words } - /// Cache line `a` (0 <= a < 2^22) as 16 words. + pub fn lines(&self) -> usize { + self.words.len() >> 4 + } + pub fn line_mask(&self) -> u32 { + self.line_mask + } + pub fn segments(&self) -> usize { + self.lines() >> CACHE_SEGMENT_LOG2_LINES + } + + /// Cache line `a` (masked to the cache's lines) as 16 words. #[inline(always)] pub fn line(&self, a: u32) -> &[u32] { - let o = (a & CACHE_LINE_MASK) as usize * 16; + let o = (a & self.line_mask) as usize * 16; &self.words[o..o + 16] } @@ -184,10 +312,13 @@ impl Cache { } /// Derive `ts.len()` items into `out`, all chains interleaved round by round so the cache-line misses of -/// independent items overlap in the memory system (`deriveItems` in the Swift). +/// independent items overlap in the memory system (`deriveItems` in the Swift). Under multiplier `m` +/// (`mp.shape.mixer_mult`) round `r` applies `M` with keys `round_key(r m + j)` for `j = 0 .. m - 1` before its +/// one cache read; the final mixer applies `M` with keys `round_key(8 m + j)`. `m = 1` is version 2. pub fn derive_items(ts: &[u32], mp: &MixParams, cache: &Cache, out: &mut [[u32; 16]]) { let n = ts.len(); debug_assert!(out.len() >= n); + let m = mp.shape.mixer_mult as usize; for k in 0..n { let s = &mut out[k]; let t = ts[k]; @@ -197,9 +328,11 @@ pub fn derive_items(ts: &[u32], mp: &MixParams, cache: &Cache, out: &mut [[u32; } } for r in 0..ITEM_ROUNDS { - let rk = round_key(r); - for s in out[..n].iter_mut() { - mixer(s, rk, mp); + for j in 0..m { + let rk = round_key_mult(r, j, m); + for s in out[..n].iter_mut() { + mixer(s, rk, mp); + } } for s in out[..n].iter_mut() { let line = cache.line(s[0]); @@ -208,9 +341,11 @@ pub fn derive_items(ts: &[u32], mp: &MixParams, cache: &Cache, out: &mut [[u32; } } } - let rk = round_key(ITEM_ROUNDS); - for s in out[..n].iter_mut() { - mixer(s, rk, mp); + for j in 0..m { + let rk = round_key_mult(ITEM_ROUNDS, j, m); + for s in out[..n].iter_mut() { + mixer(s, rk, mp); + } } } @@ -221,7 +356,7 @@ pub fn derive_item(t: u32, mp: &MixParams, cache: &Cache) -> [u32; 16] { out[0] } -/// The CPU verifier's view of the memory-hard dataset: the mixer parameters and the 256 MiB cache. +/// The CPU verifier's view of the memory-hard dataset: the mixer parameters (with the shape) and the cache. pub struct MemhardCpu { pub params: MixParams, pub cache: Cache, @@ -231,12 +366,19 @@ pub struct MemhardCpu { pub const FETCH_MAX: usize = 64; impl MemhardCpu { + /// Version 2 shape. pub fn new(key: [u32; 8]) -> Self { - Self { params: MixParams::new(key), cache: Cache::fill(key) } + Self::with_shape(key, Shape::V2) + } + pub fn with_shape(key: [u32; 8], shape: Shape) -> Self { + Self { params: MixParams::with_shape(key, shape), cache: Cache::fill_log2(key, shape.cache_log2_words) } } pub fn for_day(day: &str) -> Self { Self::new(day_key(day)) } + pub fn shape(&self) -> Shape { + self.params.shape + } /// `dataset[w] = item(w >> 4)[w & 15]`. pub fn word(&self, w: u32) -> u32 { derive_item(w >> 4, &self.params, &self.cache)[(w & 15) as usize] @@ -313,6 +455,7 @@ mod tests { assert_eq!(mp.rc[0], 0xbab68293); assert_eq!(mp.rc[15], 0x31b49ee2); assert!(mp.mul.iter().all(|m| m & 1 == 1)); + assert_eq!(mp.shape, Shape::V2); } #[test] @@ -338,4 +481,96 @@ mod tests { ] ); } + + /// Option C: the schedule table of `docs/plans/mixer-x4.md` (day -> doublings, cache words, dataset words at a + /// 2^28 genesis). The doublings fall at years 4 and 12 exactly, never a day early. + #[test] + fn growth_schedule_table() { + let table: [(u64, u32, u32, u32); 12] = [ + (0, 0, 26, 28), + (1, 0, 26, 28), + (365, 0, 26, 28), + (1_459, 0, 26, 28), + (1_460, 1, 27, 29), + (2_920, 1, 27, 29), + (4_379, 1, 27, 29), + (4_380, 2, 28, 30), + (10_219, 2, 28, 30), + (10_220, 3, 29, 31), + (21_900, 4, 30, 32), + (100_000, 6, 32, 32), + ]; + for (d, k, c, s) in table { + assert_eq!(growth_doublings(d), k, "day {d}"); + assert_eq!(cache_log2_words(d), c, "day {d}"); + assert_eq!(dataset_log2_words(28, d), s, "day {d}"); + } + // the designed 2 GiB genesis: 2^29 words, 2^30 at year 4, 2^31 at year 12 + assert_eq!(dataset_log2_words(29, 0), 29); + assert_eq!(dataset_log2_words(29, 1_460), 30); + assert_eq!(dataset_log2_words(29, 4_380), 31); + // the linear schedule itself: 2 GiB x (1 + d / 1460) crosses 4 GiB at day 1,460 and 8 GiB at day 4,380 + for d in [1_459u64, 1_460, 4_379, 4_380] { + let bytes = 2u64 * (1 << 30) + (1u64 << 29) * d / 365; + let k = (bytes / (2u64 << 30)).ilog2(); + assert_eq!(growth_doublings(d), k, "day {d}: linear {bytes} bytes"); + } + assert_eq!(days_since_genesis(20_730, 20_729), 1); + assert_eq!(days_since_genesis(20_729, 20_729), 0); + assert_eq!(days_since_genesis(20_000, 20_729), 0); + let v2 = Shape::for_class_day(&LoadClass::V2, 100_000); + assert_eq!(v2, Shape::V2); + let v3 = Shape::for_class_day(&LoadClass::MX4, 0); + assert_eq!(v3, Shape { mixer_mult: 4, cache_log2_words: 26 }); + assert_eq!(Shape::for_class_day(&LoadClass::MX4, 1_460).cache_log2_words, 27); + assert_eq!(v3.mixers_per_item(), 36); + assert_eq!(Shape::V2.mixers_per_item(), 9); + assert_eq!(Shape::V2.cache_segments(), CACHE_SEGMENTS); + assert_eq!(Shape::V2.cache_line_mask(), CACHE_LINE_MASK); + assert_eq!(Shape::V2.log2_segments(), 16); + } + + /// The multiplied mixer, restated by hand on a small cache: `m` applications with keys `round_key(r m + j)` + /// before every read, the same 8 reads; `m = 1` is `derive_item` of version 2 word for word; a larger cache's + /// first segments equal the smaller cache's. + #[test] + fn mixer_mult_by_hand() { + let key = day_key("2026-10-03"); + let small = Cache::fill_log2(key, 16); + let big = Cache::fill_log2(key, 18); + assert_eq!(&big.words()[..small.words().len()], small.words()); + assert_eq!(small.segments(), 64); + assert_eq!(small.line_mask(), 4095); + for m in [1u32, 2, 4] { + let mp = MixParams::with_shape(key, Shape { mixer_mult: m, cache_log2_words: 16 }); + for t in [0u32, 1, 12_345, u32::MAX] { + let got = derive_item(t, &mp, &small); + let mut s = [0u32; 16]; + s[..8].copy_from_slice(&key); + for i in 0..8 { + s[8 + i] = t.wrapping_mul(mp.mul[i]).wrapping_add(mp.rc[i]); + } + for r in 0..8usize { + for j in 0..m as usize { + mixer(&mut s, round_key(r * m as usize + j), &mp); + } + let line = small.line(s[0]); + for i in 0..16 { + s[i] ^= line[i]; + } + } + for j in 0..m as usize { + mixer(&mut s, round_key(8 * m as usize + j), &mp); + } + assert_eq!(got, s, "m {m} t {t}"); + } + } + let v2 = MixParams::with_shape(key, Shape { mixer_mult: 1, cache_log2_words: 16 }); + let v3 = MixParams::with_shape(key, Shape { mixer_mult: 4, cache_log2_words: 16 }); + assert_ne!(derive_item(0, &v2, &small), derive_item(0, &v3, &small)); + assert_eq!(round_key_mult(0, 0, 1), round_key(0)); + assert_eq!(round_key_mult(8, 0, 1), round_key(8)); + assert_eq!(round_key_mult(2, 3, 4), round_key(11)); + assert_eq!(round_key_mult(8, 3, 4), round_key(35)); + } } diff --git a/igneum-pow/src/verify.rs b/igneum-pow/src/verify.rs index 3b93e79c..97fbfe94 100644 --- a/igneum-pow/src/verify.rs +++ b/igneum-pow/src/verify.rs @@ -2,7 +2,7 @@ //! calls. Dataset words come from the memory-hard cache (default) or from the closed form (old packs). use crate::generator::{generate, generate_class, Instr, LoadClass, Op, Program, ProgramClass, ITERATIONS, LANES}; -use crate::memhard::MemhardCpu; +use crate::memhard::{MemhardCpu, Shape}; use crate::seed::day_key; /// Read-width experiment (5 October 2026): a `load` of `W` words folds every word into `dst`: @@ -150,21 +150,37 @@ pub struct DatasetSource { impl DatasetSource { /// Build the source for a day. Memory-hard mode fills the 256 MiB cache on the calling thread. pub fn new(day: &str, mode: DatasetMode, log2_words: u32) -> Self { - let mut ds = Self::from_key(day_key(day), mode, log2_words); + Self::new_shape(day, mode, log2_words, Shape::V2) + } + + /// [`DatasetSource::new`] with the construction's shape (mixer multiplier, cache size; Counter ASIC 2.0). + pub fn new_shape(day: &str, mode: DatasetMode, log2_words: u32, shape: Shape) -> Self { + let mut ds = Self::from_key_shape(day_key(day), mode, log2_words, shape); ds.key_bytes = format!("day/{day}").into_bytes(); ds } pub fn from_key(key: [u32; 8], mode: DatasetMode, log2_words: u32) -> Self { + Self::from_key_shape(key, mode, log2_words, Shape::V2) + } + + /// [`DatasetSource::from_key`] with the construction's shape. Memory-hard mode fills a cache of + /// `2^shape.cache_log2_words` words on the calling thread. + pub fn from_key_shape(key: [u32; 8], mode: DatasetMode, log2_words: u32, shape: Shape) -> Self { assert!((4..=32).contains(&log2_words), "dataset log2 must be in 4..=32"); let mask = if log2_words == 32 { u32::MAX } else { (1u32 << log2_words) - 1 }; let dataset = match mode { DatasetMode::ClosedForm => Dataset::ClosedForm { d0: key[0], d1: key[1] }, - DatasetMode::MemoryHard => Dataset::MemoryHard(MemhardCpu::new(key)), + DatasetMode::MemoryHard => Dataset::MemoryHard(MemhardCpu::with_shape(key, shape)), }; Self { log2_words, mask, key, key_bytes: Vec::new(), dataset } } + /// The shape of the memory-hard construction ([`Shape::V2`] for the closed form, which has none). + pub fn shape(&self) -> Shape { + self.memhard().map(|m| m.shape()).unwrap_or(Shape::V2) + } + pub fn mode(&self) -> DatasetMode { match self.dataset { Dataset::ClosedForm { .. } => DatasetMode::ClosedForm, @@ -417,9 +433,10 @@ pub struct Epoch { /// Default dataset size: 2^28 words = 1 GiB. pub const DEFAULT_DATASET_LOG2: u32 = 28; -/// Days a day index lies after the network's genesis day (0 for the genesis day and any day before it). +/// Days a day index lies after the network's genesis day (0 for the genesis day and any day before it). The node's +/// entry; the same function as `memhard::days_since_genesis`. pub fn days_since_genesis(day_index: u64, genesis_day_index: u64) -> u64 { - day_index.saturating_sub(genesis_day_index) + crate::memhard::days_since_genesis(day_index, genesis_day_index) } impl Epoch { @@ -427,9 +444,17 @@ impl Epoch { Self { program: generate(seed), dataset: DatasetSource::new(day, mode, dataset_log2) } } - /// [`Epoch::new`] with a load class (read-width experiment). + /// [`Epoch::new`] with a load class (read-width experiment; Counter ASIC 2.0: the class's mixer multiplier + /// shapes the dataset, the cache is the genesis size since a string day has no day index). pub fn new_class(seed: &str, day: &str, mode: DatasetMode, dataset_log2: u32, class: LoadClass) -> Self { - Self { program: generate_class(seed, class), dataset: DatasetSource::new(day, mode, dataset_log2) } + Self::new_class_day(seed, day, mode, dataset_log2, class, 0) + } + + /// [`Epoch::new_class`] on day `days_since_genesis` of the growth schedule (the cache of + /// `memhard::cache_log2_words` for a class with the growth rule; the dataset size is the caller's). + pub fn new_class_day(seed: &str, day: &str, mode: DatasetMode, dataset_log2: u32, class: LoadClass, days_since_genesis: u64) -> Self { + let shape = Shape::for_class_day(&class, days_since_genesis); + Self { program: generate_class(seed, class), dataset: DatasetSource::new_shape(day, mode, dataset_log2, shape) } } /// The production shape: memory-hard, 1 GiB dataset. @@ -445,11 +470,24 @@ impl Epoch { Self::from_seed_bytes_class(epoch_seed, day_bytes, label, LoadClass::V2) } - /// [`Epoch::from_seed_bytes`] with a load class (read-width experiment). + /// [`Epoch::from_seed_bytes`] with a load class (read-width experiment; Counter ASIC 2.0: the class's mixer + /// multiplier shapes the dataset). Day 0 of the growth schedule: the 2^26-word cache and the 2^28-word dataset, + /// which is every devnet pack and vector. A node past the first doubling calls [`Epoch::from_seed_bytes_day`]. pub fn from_seed_bytes_class(epoch_seed: &[u8], day_bytes: &[u8], label: &str, class: LoadClass) -> Self { + Self::from_seed_bytes_day(epoch_seed, day_bytes, label, class, 0, DEFAULT_DATASET_LOG2) + } + + /// The chain's shape on day `days_since_genesis` (`memhard::days_since_genesis(day_index(header), day_index(genesis))`, + /// the node's two day indices): the program of the class, and under the class's growth rule the cache of + /// `memhard::cache_log2_words(d)` and the dataset of `memhard::dataset_log2_words(genesis_dataset_log2, d)` + /// (the genesis size is 28 for the 1 GiB devnet, 29 for the designed 2 GiB). Without the growth rule the cache + /// is 2^26 words and the dataset `2^genesis_dataset_log2` on every day. + pub fn from_seed_bytes_day(epoch_seed: &[u8], day_bytes: &[u8], label: &str, class: LoadClass, days_since_genesis: u64, genesis_dataset_log2: u32) -> Self { let program = crate::generator::generate_from_seed_bytes_class(label, epoch_seed, class); let key = crate::seed::seed_words_from_bytes(day_bytes); - let mut dataset = DatasetSource::from_key(key, DatasetMode::MemoryHard, DEFAULT_DATASET_LOG2); + let shape = Shape::for_class_day(&class, days_since_genesis); + let dataset_log2 = if class.growth { crate::memhard::dataset_log2_words(genesis_dataset_log2, days_since_genesis) } else { genesis_dataset_log2 }; + let mut dataset = DatasetSource::from_key_shape(key, DatasetMode::MemoryHard, dataset_log2, shape); dataset.key_bytes = day_bytes.to_vec(); Self { program, dataset } } @@ -477,13 +515,18 @@ impl Epoch { /// [`Epoch::chain_dataset`] with the day's position since genesis and the network's genesis dataset size: the /// entry the node's engine and the miner's export build every day cache through, so the cache growth schedule - /// of spec 01 section 1.13.3 has one place to act. SEAM (ca2-node, 5 October 2026): the body here builds the - /// genesis-size cache for every day; the ca2-mixer branch fills the growth rule (`growth_doublings`) and the - /// class v3 item construction, keeping this signature. + /// of spec 01 section 1.13.3 has one place to act (ca2-mixer, 5 October 2026, `docs/plans/mixer-x4.md`): the + /// class's load class gives the mixer multiplier and whether the growth rule applies (`Shape::for_class_day`); + /// under the rule the cache is `2^memhard::cache_log2_words(d)` words and the dataset + /// `2^memhard::dataset_log2_words(genesis_dataset_log2, d)`; without it (class v2) the cache is 2^26 words and + /// the dataset the genesis size on every day. `days_since_genesis` is [`days_since_genesis`] of the block's and + /// the genesis header's day indices. pub fn chain_dataset_day(day_bytes: &[u8], class: ProgramClass, days_since_genesis: u64, genesis_dataset_log2: u32) -> DatasetSource { - let _ = (class, days_since_genesis); + let lc = class.load_class(); + let shape = Shape::for_class_day(&lc, days_since_genesis); + let dataset_log2 = if lc.growth { crate::memhard::dataset_log2_words(genesis_dataset_log2, days_since_genesis) } else { genesis_dataset_log2 }; let key = crate::seed::seed_words_from_bytes(day_bytes); - let mut dataset = DatasetSource::from_key(key, DatasetMode::MemoryHard, genesis_dataset_log2); + let mut dataset = DatasetSource::from_key_shape(key, DatasetMode::MemoryHard, dataset_log2, shape); dataset.key_bytes = day_bytes.to_vec(); dataset } diff --git a/proto-cuda/nvrtc/packfile.h b/proto-cuda/nvrtc/packfile.h index ba5587a9..2a740998 100644 --- a/proto-cuda/nvrtc/packfile.h +++ b/proto-cuda/nvrtc/packfile.h @@ -31,6 +31,9 @@ typedef struct { char loadClass[64]; char programClass[8]; /* IGNEUM_PROGRAM_CLASS: "v2" or "v3" (Counter ASIC 2.0); absent = the generator's class */ char eraHex[65]; /* IGNEUM_ERA_SEED_HEX of a class v3 chain pack; empty otherwise */ + // Counter ASIC 2.0 (5 October 2026): the mixer multiplier of the item derivation (IGNEUM_MIXER_MULT, 1 when absent: + // version 2; 4 under class v3). The emitted memhard.h / kernel.cl carry it in their text; this is for the log lines. + uint32_t mixerMult; // seeds.txt (or program.h): the seeds as the worker protocol carries them char epochHex[65]; char dayHex[PF_HEX_CAP]; @@ -296,6 +299,7 @@ static int pf_load(const char* dir, PfPack* pk, char* err, size_t cap) { pk->persistent = 0; pf_define_u32(prog, "IGNEUM_PERSISTENT_WARPS", &pk->persistent); pk->scratchWordsPerLane = 8192; pf_define_u32(prog, "IGNEUM_SCRATCH_WORDS_PER_LANE", &pk->scratchWordsPerLane); strcpy(pk->loadClass, "v2"); pf_define_str(prog, "IGNEUM_LOAD_CLASS", pk->loadClass, sizeof(pk->loadClass)); + pk->mixerMult = 1; pf_define_u32(prog, "IGNEUM_MIXER_MULT", &pk->mixerMult); if (pf_define_words(prog, "IGNEUM_KEY_INIT", pk->keyw, 8) != 8) { free(prog); return pf_fail(err, cap, "program.h has no IGNEUM_KEY_INIT with 8 words"); } if (!pf_define_str(prog, "IGNEUM_SEED_STRING", pk->seedString, sizeof(pk->seedString))) strncpy(pk->seedString, "(no IGNEUM_SEED_STRING)", sizeof(pk->seedString) - 1); pf_define_str(prog, "IGNEUM_SEED_BYTES_HEX", ehex, sizeof(ehex)); diff --git a/proto-metal/packbench.swift b/proto-metal/packbench.swift index 3110bbe1..b7eda2f3 100644 --- a/proto-metal/packbench.swift +++ b/proto-metal/packbench.swift @@ -61,6 +61,8 @@ let scratchOps = Int(defineU32("IGNEUM_SCRATCH_OPS") ?? 0) let persistent = (defineU32("IGNEUM_PERSISTENT_WARPS") ?? 0) == 1 let scratchWordsPerLane = Int(defineU32("IGNEUM_SCRATCH_WORDS_PER_LANE") ?? 8192) let className = defineStr("IGNEUM_LOAD_CLASS") ?? "v2" +// Counter ASIC 2.0 (5 October 2026): the mixer multiplier of the item derivation, 1 when absent (version 2), 4 under class v3 +let mixerMult = Int(defineU32("IGNEUM_MIXER_MULT") ?? 1) let seedString = defineStr("IGNEUM_SEED_STRING") ?? "?" let programId = defineStr("IGNEUM_PROGRAM_ID") ?? "" @@ -200,7 +202,7 @@ for b in 0..> 20), IGNEUM_ITEM_ROUNDS, IGNEUM_MIXER_MULT); cachePass = setupCache(&dv, di); #else printf("dataset construction: closed-form ds_elem (the original prototype dataset, not memory-hard)\n"); diff --git a/relay/playbooks/mixer-x4-5090-bench.ps1 b/relay/playbooks/mixer-x4-5090-bench.ps1 new file mode 100644 index 00000000..74f4a184 --- /dev/null +++ b/relay/playbooks/mixer-x4-5090-bench.ps1 @@ -0,0 +1,63 @@ +# Igneum run job (bench only): the class v3 dataset construction (mixer x4, docs/plans/mixer-x4.md) on PC 2's RTX 5090 (machine 1ccfe586), 5 October 2026. +# Published as a plain `run` job (NOT --stop-miners): the installed app keeps every other card mining; this script switches +# off ONLY the NVIDIA card in the app through POST api/cards, waits for its worker to stop, runs the fetched +# igneum-worker-cuda.exe on the v2 pack and the two v3 packs of the fetched packs folder (the dataset build time per pack is the +# number this job is for: the worker's own `cache ... dataset ... ms` line, the same line the readwidth round printed), +# and switches the card back on with the settings it had. Every result line starts with RESULT so `node tools/jobs.mjs ` shows them. +# The packs folder of the fetch job: proto-cuda/packs-ca2-mixer/mx4-genesis and mx4-devnet-epoch0 from branch ca2-mixer, plus +# proto-cuda/packs/igneum-genesis-mh copied in as v2-genesis-mh (the version 2 control, same card, same run). +$ErrorActionPreference = 'Continue' +function Say([string] $m) { Write-Host ("[" + (Get-Date -Format 'HH:mm:ss') + "] " + $m) } +$jobs = Split-Path $env:IGNEUM_JOB_DIR +$fetched = Join-Path $jobs 'fetch-mixer-x4-20261005' +$exe = Join-Path $fetched 'igneum-worker-cuda.exe' +$packs = Join-Path $fetched 'packs-ca2-mixer' +if (-not (Test-Path $exe)) { Write-Output "RESULT error worker missing at $exe (the fetch job runs first)"; exit 2 } +if (-not (Test-Path $packs)) { Write-Output "RESULT error packs missing at $packs"; exit 2 } +$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-worker-cuda.exe') } | Select-Object -First 1 +if (-not $inst) { Write-Output 'RESULT error no installed igneum-worker-cuda.exe (the NVRTC DLLs come from there)'; exit 2 } +Get-ChildItem $inst -Filter 'nvrtc*.dll' | Copy-Item -Destination $fetched -Force +Write-Output "RESULT worker $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) with $((Get-ChildItem $fetched -Filter 'nvrtc*.dll').Count) NVRTC DLL(s) from $inst" + +# the app: switch off the NVIDIA card only, remember its settings +$appDir = $env:IGNEUM_APP_DIR +if (-not $appDir) { $appDir = Join-Path $env:LOCALAPPDATA 'igneum\app' } +$urlFile = Join-Path $appDir 'app.url' +$url = $null +if (Test-Path $urlFile) { $url = (Get-Content -LiteralPath $urlFile -Raw).Trim() } +$card = $null +if ($url) { + try { + $st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10 + $card = $st.mining.cards | Where-Object { $_.vendor -eq 'nvidia' } | Select-Object -First 1 + if (-not $card) { $card = $st.cards | Where-Object { $_.vendor -eq 'nvidia' } | Select-Object -First 1 } + } catch { Say ("api/state: " + $_.Exception.Message) } +} +if ($card) { + Write-Output ("RESULT card " + $card.key + " enabled=" + $card.enabled + " identities=" + $card.identities + " power_pct=" + $card.power_pct + " state=" + $card.state) + $body = @{ cards = @(@{ key = $card.key; enabled = $false; identities = [int]$card.identities; power_pct = [int]$card.power_pct }) } | ConvertTo-Json -Depth 5 + try { Invoke-RestMethod -Uri ($url + 'api/cards') -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; Say "card off requested" } catch { Say ("api/cards off: " + $_.Exception.Message) } + $t = 0 + while ($t -lt 90) { + Start-Sleep -Seconds 5; $t += 5 + try { $st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10; $c2 = $st.mining.cards | Where-Object { $_.key -eq $card.key }; if (-not $c2) { $c2 = $st.cards | Where-Object { $_.key -eq $card.key } }; if ($c2 -and $c2.state -eq 'off' -and $c2.pid -eq 0) { break } } catch { } + } + Write-Output ("RESULT card-off after " + $t + " s") + Start-Sleep -Seconds 5 +} else { Write-Output 'RESULT card none-found (the app is not running or has no NVIDIA card); measuring with whatever else runs on the GPU' } + +& nvidia-smi --query-gpu=name,driver_version,power.limit,clocks.sm,clocks.mem,memory.used,temperature.gpu --format=csv,noheader 2>&1 | ForEach-Object { "RESULT gpu-before $_" } +# the probe ran in run-readwidth-5090-20261005 (bench-log, 5 October 2026); this second run is the bench only +foreach ($pk in @('v2-genesis-mh', 'mx4-genesis', 'mx4-devnet-epoch0')) { + $d = Join-Path $packs $pk + Write-Output "RESULT bench $pk start $(Get-Date -Format HH:mm:ss)" + & $exe --bench --pack $d --batches 5 --batch-log2 24 --block-warps 1 2>&1 | ForEach-Object { "RESULT $_" } + & $exe --bench --pack $d --batches 5 --batch-log2 24 --block-warps 8 2>&1 | Where-Object { $_ -match '^RESULT|error|FAIL|dataset' } | ForEach-Object { "RESULT $_" } +} +& nvidia-smi --query-gpu=power.draw,clocks.sm,clocks.mem,memory.used,temperature.gpu --format=csv,noheader 2>&1 | ForEach-Object { "RESULT gpu-after $_" } + +if ($card) { + $body = @{ cards = @(@{ key = $card.key; enabled = [bool]$card.enabled; identities = [int]$card.identities; power_pct = [int]$card.power_pct }) } | ConvertTo-Json -Depth 5 + try { Invoke-RestMethod -Uri ($url + 'api/cards') -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; Write-Output ("RESULT card restored enabled=" + $card.enabled) } catch { Write-Output ("RESULT error card restore: " + $_.Exception.Message) } +} +exit 0 diff --git a/relay/playbooks/mixer-x4-9070-bench.ps1 b/relay/playbooks/mixer-x4-9070-bench.ps1 new file mode 100644 index 00000000..50df5b65 --- /dev/null +++ b/relay/playbooks/mixer-x4-9070-bench.ps1 @@ -0,0 +1,64 @@ +# Igneum run job (bench only): the class v3 dataset construction (mixer x4, docs/plans/mixer-x4.md) on PC 1's RX 9070 XT on the eGPU (machine ae432dc7), 5 October 2026. +# Published as a plain `run` job (NOT --stop-miners): the installed app keeps every other card mining; this script switches +# off ONLY the NVIDIA card in the app through POST api/cards, waits for its worker to stop, runs the fetched +# igneum-worker-opencl.exe on the v2 pack and the two v3 packs of the fetched packs folder (the dataset build time per pack is the +# number this job is for: the worker's own `cache ... dataset ... ms` line, the same line the readwidth round printed), +# and switches the card back on with the settings it had. Every result line starts with RESULT so `node tools/jobs.mjs ` shows them. +# The packs folder of the fetch job: proto-cuda/packs-ca2-mixer/mx4-genesis and mx4-devnet-epoch0 from branch ca2-mixer, plus +# proto-cuda/packs/igneum-genesis-mh copied in as v2-genesis-mh (the version 2 control, same card, same run). +$ErrorActionPreference = 'Continue' +function Say([string] $m) { Write-Host ("[" + (Get-Date -Format 'HH:mm:ss') + "] " + $m) } +$jobs = Split-Path $env:IGNEUM_JOB_DIR +$fetched = Join-Path $jobs 'fetch-mixer-x4-20261005' +$exe = Join-Path $fetched 'igneum-worker-opencl.exe' +$packs = Join-Path $fetched 'packs-ca2-mixer' +if (-not (Test-Path $exe)) { Write-Output "RESULT error worker missing at $exe (the fetch job runs first)"; exit 2 } +if (-not (Test-Path $packs)) { Write-Output "RESULT error packs missing at $packs"; exit 2 } +Write-Output "RESULT worker $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower())" +# the card's OpenCL device index on the current (3683.0) platform, from the worker's own list (the older platform's duplicate is marked dup) +$list = & $exe --list 2>&1 +$list | ForEach-Object { "RESULT list $_" } +$dev = $null +foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1201' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } } +if ($null -eq $dev) { Write-Output 'RESULT error no gfx1201 device in --list'; exit 2 } +Write-Output "RESULT device $dev" + +# the app: switch off the NVIDIA card only, remember its settings +$appDir = $env:IGNEUM_APP_DIR +if (-not $appDir) { $appDir = Join-Path $env:LOCALAPPDATA 'igneum\app' } +$urlFile = Join-Path $appDir 'app.url' +$url = $null +if (Test-Path $urlFile) { $url = (Get-Content -LiteralPath $urlFile -Raw).Trim() } +$card = $null +if ($url) { + try { + $st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10 + $card = $st.mining.cards | Where-Object { ($_.vendor -eq 'amd' -and $_.key -match 'gfx1201') } | Select-Object -First 1 + if (-not $card) { $card = $st.cards | Where-Object { ($_.vendor -eq 'amd' -and $_.key -match 'gfx1201') } | Select-Object -First 1 } + } catch { Say ("api/state: " + $_.Exception.Message) } +} +if ($card) { + Write-Output ("RESULT card " + $card.key + " enabled=" + $card.enabled + " identities=" + $card.identities + " power_pct=" + $card.power_pct + " state=" + $card.state) + $body = @{ cards = @(@{ key = $card.key; enabled = $false; identities = [int]$card.identities; power_pct = [int]$card.power_pct }) } | ConvertTo-Json -Depth 5 + try { Invoke-RestMethod -Uri ($url + 'api/cards') -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; Say "card off requested" } catch { Say ("api/cards off: " + $_.Exception.Message) } + $t = 0 + while ($t -lt 90) { + Start-Sleep -Seconds 5; $t += 5 + try { $st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10; $c2 = $st.mining.cards | Where-Object { $_.key -eq $card.key }; if (-not $c2) { $c2 = $st.cards | Where-Object { $_.key -eq $card.key } }; if ($c2 -and $c2.state -eq 'off' -and $c2.pid -eq 0) { break } } catch { } + } + Write-Output ("RESULT card-off after " + $t + " s") + Start-Sleep -Seconds 5 +} else { Write-Output 'RESULT card none-found (the app is not running or has no NVIDIA card); measuring with whatever else runs on the GPU' } + +# the probe ran in run-readwidth-9070-20261005 (bench-log, 5 October 2026); this second run is the bench only +foreach ($pk in @('v2-genesis-mh', 'mx4-genesis', 'mx4-devnet-epoch0')) { + $d = Join-Path $packs $pk + Write-Output "RESULT bench $pk start $(Get-Date -Format HH:mm:ss)" + & $exe --bench-pack --pack $d --batches 5 --batch-log2 24 --device $dev 2>&1 | ForEach-Object { "RESULT $_" } +} + +if ($card) { + $body = @{ cards = @(@{ key = $card.key; enabled = [bool]$card.enabled; identities = [int]$card.identities; power_pct = [int]$card.power_pct }) } | ConvertTo-Json -Depth 5 + try { Invoke-RestMethod -Uri ($url + 'api/cards') -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; Write-Output ("RESULT card restored enabled=" + $card.enabled) } catch { Write-Output ("RESULT error card restore: " + $_.Exception.Message) } +} +exit 0 From 1aff0b79d16fb55c6cd4892c90fe821338c15d67 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:55:54 +0000 Subject: [PATCH 057/158] Proving v1: the miner-on curve (the adopted shard 22.2 GB and 13.2 s beside the miner; the prototype 30.1 GB): the 24 GB tier measured, the public lines updated Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/src/provedefault.rs | 15 +++++++++++++-- docs/bench-log.md | 2 ++ docs/evidence.md | 2 +- docs/plans/proving-v1.md | 6 +++--- site/litepaper.html | 2 +- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/app/igneum-app/src/provedefault.rs b/app/igneum-app/src/provedefault.rs index 2791c3ab..a124ee0e 100644 --- a/app/igneum-app/src/provedefault.rs +++ b/app/igneum-app/src/provedefault.rs @@ -4,7 +4,7 @@ //! //! | Machine | Default | Why (bench-log 5 October 2026, "proving v1", the S_p curve on the RTX 5090, SP1 6.8.1's GPU prover) | //! |---|---|---| -//! | NVIDIA card with 24 GB or more, mining or not, Windows with WSL2 (Ubuntu-24.04) answering or Linux | on | a full shard at the adopted v1 budget (30,000 pgas, 4.7 M cycles) peaks at 20,434 MiB alone and about 22.1 GB beside the miner (the miner's measured 1.7 GB extra; approximate on a 24 GB card); the prototype shard the devnet proves until its fee switch (6.75 M pgas) peaks at 28,307 MiB alone and 30,039 beside the miner, so until the switch only a 32 GB card proves it and a 24 GB card's prover waits for shards it can hold (the host refuses nothing; a proof that runs out of memory fails and the shard is left) | +//! | NVIDIA card with 24 GB or more, mining or not, Windows with WSL2 (Ubuntu-24.04) answering or Linux | on | a full shard at the adopted v1 budget (30,000 pgas, 4.7 M cycles) peaks at 20,434 MiB alone and 22,210 beside the miner (measured on the 5090; approximate for a 24 GB card's own allocation); the prototype shard the devnet proves until its fee switch (6.75 M pgas) peaks at 28,307 MiB alone and 30,039 beside the miner, so until the switch only a 32 GB card proves it and a 24 GB card's prover waits for shards it can hold (the host refuses nothing; a proof that runs out of memory fails and the shard is left) | //! | NVIDIA card of 16 to 24 GB | off, with the line saying why | the GPU prover's floor is 13,874 MiB for an EMPTY shard, 15,670 beside the miner; a 16 GB card holds no full shard | //! | NVIDIA card under 16 GB | off | 13,874 MiB does not fit; the project lead's 12 GB requirement is open until a prover build with a smaller floor is measured | //! | Windows under 32 GB of RAM | off, with the line saying why | the WSL2 prover held 7.9 GB on a 63 GB PC; a 16 GB PC would swap | @@ -17,7 +17,7 @@ use crate::state::CardState; /// A card that proves, mining or not, needs this much: the adopted v1 shard peaks at 20,434 MiB alone (the S_p curve, -/// 5 October 2026) and about 22.1 GB beside the miner; `nvidia-smi` reports MiB and a 24 GB card reports 24,564, so the +/// 5 October 2026) and 22,210 beside the miner; `nvidia-smi` reports MiB and a 24 GB card reports 24,564, so the /// test is at 23 GB. (The same value for a mining and an idle card: the floor is the GPU server's, not the miner's.) pub const MIN_VRAM_MB_MINING: u64 = 23_552; pub const MIN_VRAM_MB_PROVE_ONLY: u64 = 23_552; @@ -25,6 +25,17 @@ pub const MIN_VRAM_MB_PROVE_ONLY: u64 = 23_552; /// miner), the devnet's shard until its fee switch at DAA 210,000; `nvidia-smi` reports 32,607 for the RTX 5090. pub const VRAM_MB_PROTOTYPE_SHARD: u64 = 31_000; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Decision { + pub on: bool, + /// One plain sentence for the log and the Proving tile. + pub line: String, +} + +fn gb(mb: u64) -> u64 { + (mb + 512) / 1024 +} + /// Windows machines under this much RAM stay off until measured (consequences review C4, 5 October 2026): PC 2 at /// 63 GB had 25.6 GB in use with the WSL2 VM's working set at 7.9 GB while proving; a 16 GB PC would swap. pub const MIN_RAM_MB_WINDOWS: u64 = 31_000; diff --git a/docs/bench-log.md b/docs/bench-log.md index 6eec6de5..df22aa01 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -1684,6 +1684,8 @@ Job `spcurve-stopped-pc2-pv1` (`tools/proving-v1/pc2-sp-curve.ps1`, the miners s The second job (`spcurve-stopped-pc2-pv1b`, the pv1b host whose `--budget` re-plans a fixture, 20:43 to 20:47Z, the same conditions) repeats the points (empty 13,875 MiB 2.1 s; one transfer 13,907 MiB 3.3 s; the v1 shard 20,435 MiB 4.2 s; the prototype shard 28,275 MiB 11.2 s) and adds the re-plans of block 344 (27 M pgas of modexp): at 2.25 M pgas (one transaction, 16 shards a block, 19,987,938 cycles) **28,371 MiB** and 6.6 s; at 4.5 M pgas (7 shards a block, 40,011,108 cycles) 28,307 MiB and 8.5 s; with the 2^25 trace threshold the 2.25 M shard 22,835 MiB and 6.2 s. So the peak is flat at 28.3 GB from 20 M cycles to 60 M (the server's buffers step up between 4.7 M and 20 M cycles and not after), and cutting the prototype shard smaller buys nothing until the v1 size. +The third job (`spcurve-miner-pc2-pv1`, the same points WITH THE MINER RUNNING on the card, 20:49Z on, the live prover off): empty shard 15,585 MiB and 7.5 s; one transfer 15,745 MiB and 12.7 s; **the v1 shard 22,210 MiB and 13.2 s** (20,435 and 4.2 s alone: the miner adds 1.8 GB and 3.1x); the 2.25 M shard 30,049 MiB and 17.9 s; the 4.5 M shard 29,954 MiB and 26.3 s; the prototype shard 30,083 MiB and 33.3 s. So beside the miner the adopted shard needs 22.2 GB: a 24 GB card (24,564 MiB) has 2.3 GB spare for it (the number for a 24 GB card is the 5090's allocation pattern on a 32 GB card, so approximate for the card itself), and the prototype shard needs 30.1 GB, the 32 GB card alone. + Reading, with the miner-on pairs above (empty shard 15,670 MiB, full prototype shard 30,039 MiB). The witness is never the binding term (4.9 to 21.6 KB a shard); the GPU server's working set is: a floor of 13.9 GB for any shard, 20.4 GB at 4.7 M cycles, 28.3 GB at 60 M cycles (23.0 GB with the smallest trace threshold, at the same time). By card: a **12 GB card proves nothing** on this build (the floor is 13.9 GB alone); a **16 GB card proves only empty and near-empty shards, alone** (13.9 GB; 15.7 GB beside the miner leaves nothing for the display); a **24 GB card proves the adopted v1 shard alone** (20.4 GB) and, at the miner's measured 1.7 GB extra, about 22.1 GB beside it (approximate: not measured on a 24 GB card), and never the prototype shard (28.3 GB); a **32 GB card proves the prototype shard beside the miner with 2.5 GB spare** (30.0 of 32.6 GB). The devnet is on the prototype table until H = 210,000 (6 October, about 19:50Z) and on the adopted v1 table (`S_p` 30,000 pgas) after it, so from H the 24 GB tier joins the provers and the shard that binds the memory is the 4.7 M-cycle one. Shards per block at each size: 1 at the prototype `S_p`, 4 at `B_p`; at the v1 budget 1 to 4 (one a block on tonight's chain, 2 to 3 on the txgen blocks). ### Step 4, the rule diff --git a/docs/evidence.md b/docs/evidence.md index 40c3fa8e..138aaefc 100644 --- a/docs/evidence.md +++ b/docs/evidence.md @@ -40,7 +40,7 @@ Versions in the table: `igneum-pow` is the Rust crate at `igneum-pow/Cargo.toml` | 13 | Every node executes the ordered transactions natively and reaches the same state root | Litepaper Proving ("Every node executes ... natively"), Building ("runs on Igneum unchanged") | tested by the team | repo `f5f8c80`, `8dae48b`; fork `devnet-v4` `dc749905`; revm 43.0.3 | `node tools/evm-smoke/smoke.mjs` against a 3-node `igneumd`; `igneum-exec-diff seq.json`; bench-log "execution layer devnet v3" and "devnet-v4 integration" | Simnet, 3 October 2026: 87 of 87 viem checks, state roots identical on 3 nodes at four heights, 57 executed and 19 skipped transactions agree with plain revm, 0 mismatches. Merged node on real proof of work, 4 October 2026: 84 of 85 checks (the miss needs parallel blocks the network did not produce in 36 s), 59 transfers in 10 chain blocks, state roots identical on 3 nodes, `igneum-exec-diff` 0 mismatches over 59 transactions; the live devnet v4 runs this execution layer. Apple M5 Max. The prover is a stub; state is rebuilt from genesis at start; no EVM transaction relay between nodes | none yet | | 14 | Ethereum bytecode runs unchanged, with the documented differences of spec 7.1 | Homepage Build card; litepaper Building | tested by the team | as row 13; fixes `F-exec-A`, `F-exec-B` (spec 7.5) | `tools/evm-smoke/smoke.mjs`: deploy via viem, `increment`, `hashLoop`, `eth_estimateGas`, `eth_getLogs`; `tools/exec-attacks` scenarios 1 and 3; bench-log "execution layer attack fixes" | Deployment, calls, reverts, logs and gas estimates behave as viem expects; chain id 4463; the prototype pgas table gives 0.0095 to 0.028 pgas per gas, below the design's band before calibration, 3 October 2026. 4 October 2026: a transaction that would cross the block's proving budget is refused by the mempool and, if forced in, aborted and charged with its nonce advanced (25 of 25 checks; 30 of 30 malformed cases). Apple M5 Max. The `Prover` precompile, proof records and the shard planner are not in the node | none yet | | 15 | Every block is proven, with the proof landing within about a minute at launch | Homepage stats ("~60 s to a proof"); litepaper Proving; roadmap phase 3 gate | implemented | repo `d7e1f89` (GPU proof), `e01a3cc`, `292e800`, `eedd136` (`proving/igneum-prove`: shard cutter, MPT witnesses, shard and aggregator guests); SP1 6.8.1; spec 7.2, 7.6 | `proving/windows-wsl2` (SETUP-PROVER, PROVE-BLOCK) on the RTX 5090; `igneum-prove-host --mode block` on `proving/fixtures/`; bench-log "proving v0 on the RTX 5090" and "proving: devnet v4 shards" | First GPU proof of an Igneum block, 4 October 2026, RTX 5090 (WSL2, SP1 cuda, mining paused): fixture `block-78-increment` (2 transactions), core proof 1.4 s (7.3 MB, verify 0.221 s), compressed proof 2.7 s (1.27 MB, verify 0.038 s), post-state and receipts roots identical to the node's; 15.7x and 20.6x faster than a loaded M5 Max CPU. The same day on that CPU (load 38 to 47): a three-shard block proved shard by shard and aggregated by recursion, 19 min (1,139 s) end to end, 245 to 337 s per compressed shard proof, every proof verified. What is not there: no proof is produced, carried or checked on the chain (the devnet prover is a stub that signs claims), the proving pool pays nobody (row 21), the block proven is far below one shard, and the 60-second figure remains a design target; the pass mark is the standard in `docs/benchmarks/proving-e2e.md`. Second RTX 5090 run, 4 October 2026 evening (job run-20261004-173115): a full shard at the provisional S_p (6.75 M pgas, 60.8 M cycles) executed in 1.63 s, core proof 8.3 s (18.1 MB), compressed proof 10.9 s (1.27 MB, verify 0.040 s); a two-shard block (13.5 M pgas) proved shard by shard (11.7 s and 10.0 s) and aggregated in 2.2 s, 24 s of GPU stages end to end, every proof verified, six tampered witnesses rejected. The two host defects (an abort after the upload, an idle wait that turned out to be an unbuffered 18 MB proof save through the WSL2 file bridge, 24 minutes) are fixed (ledger P20) 5 October 2026, live devnet with real transactions (bench-log "real transactions, the first non-empty shard proven and paid"): block 72704 shard 0, 29 transfers, 5,800 pgas, proven on PC 2 in 34 s, verified on the Mac in 0.297 s and paid 1.7623 IGN, 53 s after the chain block executed; of about 1,400 blocks in the 20-minute window 36 were proven (the one prover takes the newest shard assigned to it), so "every block" is not yet true; a second content shard (72803, all copies skipped) failed the native-execution veto on the exporter's block structure, fixed with fixtures the same day, the node side pending the 0.3.9 rollout 5 October 2026, evening (bench-log "proving v1"): the aggregated segment record, the chain rule and the unproven rule are implemented behind `proving_v1_activation_daa` (branch proving-v1, not on the devnet before 0.3.11); on the RTX 5090 a chain of 8 consecutive live blocks proved and aggregated by recursion in 135.6 s with the miner on the card (17 s a block, one proof of 1,272,909 bytes attesting all 8, verified in 0.04 s); the 3-node fast-time harness paid a segment record 1.0 s after submission and refused a late one after its deadline (21 checks); the devnet itself, with one prover, carried proofs for 2.4% of blocks over 30 minutes at a block-to-record latency p50 44 s, p99 52 s. The "within about a minute" holds per proven block; "every block" needs 18 mining 5090s or 6 proving-only cards at empty blocks on the measured rates, and the mandatory rule stays off until the share is one | none yet | -| 16 | A 12 GB card proves one shard in about 20 s (WITHDRAWN 5 October 2026: a 24 GB card proves a full shard at the adopted size in 4.3 s; 32 GB mines and proves) | Litepaper Proving ("The proving budget"); roadmap gate 2 | designed | spec 5.1 (Target), 7.6 (`S_p` provisional, 7,500,000 pgas = `B_p` / 4) | `PROVE-SHARD.bat` on the RTX 5090 (pending); the end-to-end standard in `docs/benchmarks/proving-e2e.md`; bench-log "proving: devnet v4 shards" | Measured on a 32 GB card, not yet on a 12 GB card. A shard at the provisional `S_p` is 60.8 M SP1 cycles on the prototype pgas table (9 cycles per pgas, 44 per EVM gas; the modexp entry about 100x its SP1 cost); on an RTX 5090 (4 October 2026 evening, job run-20261004-173115) it executed in 1.63 s and its compressed proof took 10.9 s, verified in 0.040 s, so the 32 GB card is inside the 20 s target with margin. Whether a 12 GB card proves it at all, and in what time, is the next measurement (an RTX 3060 and an RTX 5060 Ti 16 GB are on order). A per-shard time can be met by shrinking the shard, so the project does not use it as a pass mark 5 October 2026, evening (bench-log "proving v1", the S_p curve): measured on the RTX 5090 with SP1 6.8.1's GPU prover, the card to itself, 1-s nvidia-smi samples: an empty shard 13,874 MiB and 2.2 s; a full shard at the ADOPTED v1 budget (30,000 pgas, 4.7 M cycles) 20,434 MiB and 4.3 s; the full prototype shard (6.75 M pgas, 60 M cycles) 28,307 MiB and 10.8 s; beside the miner 15,670 and 30,039 MiB. No environment knob of SP1 moves the 13.9 GB floor and the GPU server has no options of its own, so on this build a 12 GB card proves nothing, a 16 GB card only empty shards, a 24 GB card the adopted full shard alone (about 22 GB beside the miner, approximate) and a 32 GB card the prototype shard beside the miner with 2.5 GB spare. The litepaper line now says so; the 12 GB gate returns when a prover build with a smaller floor is measured on a 12 GB card | none yet | +| 16 | A 12 GB card proves one shard in about 20 s (WITHDRAWN 5 October 2026: a 24 GB card proves a full shard at the adopted size in 4.3 s; 32 GB mines and proves) | Litepaper Proving ("The proving budget"); roadmap gate 2 | designed | spec 5.1 (Target), 7.6 (`S_p` provisional, 7,500,000 pgas = `B_p` / 4) | `PROVE-SHARD.bat` on the RTX 5090 (pending); the end-to-end standard in `docs/benchmarks/proving-e2e.md`; bench-log "proving: devnet v4 shards" | Measured on a 32 GB card, not yet on a 12 GB card. A shard at the provisional `S_p` is 60.8 M SP1 cycles on the prototype pgas table (9 cycles per pgas, 44 per EVM gas; the modexp entry about 100x its SP1 cost); on an RTX 5090 (4 October 2026 evening, job run-20261004-173115) it executed in 1.63 s and its compressed proof took 10.9 s, verified in 0.040 s, so the 32 GB card is inside the 20 s target with margin. Whether a 12 GB card proves it at all, and in what time, is the next measurement (an RTX 3060 and an RTX 5060 Ti 16 GB are on order). A per-shard time can be met by shrinking the shard, so the project does not use it as a pass mark 5 October 2026, evening (bench-log "proving v1", the S_p curve): measured on the RTX 5090 with SP1 6.8.1's GPU prover, the card to itself, 1-s nvidia-smi samples: an empty shard 13,874 MiB and 2.2 s; a full shard at the ADOPTED v1 budget (30,000 pgas, 4.7 M cycles) 20,434 MiB and 4.3 s; the full prototype shard (6.75 M pgas, 60 M cycles) 28,307 MiB and 10.8 s; beside the miner 15,670 and 30,039 MiB. No environment knob of SP1 moves the 13.9 GB floor and the GPU server has no options of its own, so on this build a 12 GB card proves nothing, a 16 GB card only empty shards, a 24 GB card the adopted full shard alone and beside the miner (22,210 MiB measured, 13.2 s) and a 32 GB card the prototype shard beside the miner with 2.5 GB spare. The litepaper line now says so; the 12 GB gate returns when a prover build with a smaller floor is measured on a 12 GB card | none yet | | 17 | The chip resistance target: a chip gains under 2x over a GPU | Litepaper Mining, "What Igneum does not claim"; homepage "no chip can be built for it" | designed | spec 0.2 (Target); O-1.17 | Public benchmark with a leaderboard by card model and a standing bounty, January 2027 (O-1.17); the on-die-SRAM test on the RTX 5090 (R3.5) | A target, not a measurement. Review round 3 priced a recompute chip with the 256 MiB cache on die at about 2.4x, approximate, before the usual chip-versus-GPU integer gain; the design answer (cache larger than any die) is open (spec 1.16) | none yet | | 18 | The chip resistance measurements: the program is random-access bound, not bandwidth bound, and sits beyond a card's on-chip cache | Litepaper Mining ("bound by memory bandwidth", to be corrected), vs RandomX "Measured so far" | tested by the team | repo `aba248d`, `f2a1a64`, `4b95c5e` | RTX 5090 dataset sweep 4 MiB to 1 GiB with `proto-cuda/host.cu`; bench-log "RTX 5090 first run" and "dataset sweep" | At 1 GiB: 228.1 Mhash/s, 23.7 G random loads/s, 94.9 GB/s useful against a 1,638 GB/s dataset fill; inside the 96 MiB L2 (4 and 64 MiB) 1,340 to 1,353 Mhash/s, about 5.8x faster; 104 against 128 loads per hash gives 228 against 185 Mhash/s, proportional. 3 October 2026, RTX 5090, Windows, CUDA 12.8, version 1 programs. Prototype dataset 1 GiB against 2 GB at genesis; a pure random-read microbenchmark (R3 chip designer, attack 2) has not run; the sweep has not been repeated on version 2 | none yet | | 19 | The lottery hash is sound as a hash: uniform output, deterministic, no out-of-bounds read, fuzzed | Litepaper vs RandomX ("Every number above is measured and logged") | tested by the team | repo `c52307e`, `58a5a63`, `b27da39`; `proto-metal/TESTS.md` | `proto-metal/igneum-bench --fuzz --edge --stats --determinism --memcheck`; `--fuzz 2000` on the version 2 generator; `igneum-census`; bench-log "hardening tests", the re-run on the memory-hard dataset, "generator version 2 adopted" | Version 1: 10,200 random programs, 1,305,600 hashes, 0 mismatches; 14 of 14 edge cases; bit frequency within 2.90 sigma, avalanche mean 31.99 to 32.04 of 32; deterministic fingerprint across 5 runs; every dataset read masked, 3 October 2026. Version 2, 4 October 2026: 2,000 random programs through the Metal cross-check, 8,000 warps, 0 mismatches, 128 loads per hash on every program; 20,000-program census, 5.2% rejected (4.1% static, 1.1% dynamic). Apple M5 Max. Statistics are not a security proof; the edge, stats and memcheck sections were not re-run on version 2 (they do not depend on the generator); the seed derivation review (O-1.4) is open; the fuzz set has run on Metal and the CPU only | none yet | diff --git a/docs/plans/proving-v1.md b/docs/plans/proving-v1.md index 91fe1ef0..3f2a72bb 100644 --- a/docs/plans/proving-v1.md +++ b/docs/plans/proving-v1.md @@ -120,13 +120,13 @@ The GPU server of SP1 6.8.1 sets the memory, not the shard: a floor of 13.9 GB f | Card | Alone | Beside the miner | Default (`provedefault.rs`) | |---|---|---|---| -| 32 GB (RTX 5090) | the prototype shard, 28.3 GB, 10.8 s; the v1 shard 20.4 GB, 4.3 s | the prototype shard 30.0 GB, 33 s; the v1 shard about 22 GB (approximate) | on, mine and prove, today | -| 24 GB (RTX 4090, 3090) | the v1 shard 20.4 GB; the prototype shard does NOT fit (28.3 GB) | the v1 shard about 22.1 GB (approximate, not measured on a 24 GB card) | on, mine and prove, with the line "until the devnet's fee switch its shards are the prototype size, which needs 32 GB, so this card proves from the switch on" | +| 32 GB (RTX 5090) | the prototype shard, 28.3 GB, 10.8 s; the v1 shard 20.4 GB, 4.3 s | the prototype shard 30.1 GB, 33 s; the v1 shard 22.2 GB, 13.2 s | on, mine and prove, today | +| 24 GB (RTX 4090, 3090) | the v1 shard 20.4 GB; the prototype shard does NOT fit (28.3 GB) | the v1 shard 22.2 GB measured on the 5090's allocation (2.3 GB spare on a 24 GB card; approximate for the card itself) | on, mine and prove, with the line "until the devnet's fee switch its shards are the prototype size, which needs 32 GB, so this card proves from the switch on" | | 16 GB (RTX 5080, 4080) | an empty shard only (13.9 GB) | nothing (15.7 GB for an empty shard, no room for the display) | off, with the line | | 12 GB (RTX 3060, 4070) | nothing: the floor is 13.9 GB | nothing | off; the project lead's "make sure we can prove on 12 GB cards" is OPEN: it needs a prover build with a smaller floor (an SP1 release or a fork of its GPU server), measured on a 12 GB card; the curve above is the evidence for that ask (D2) | | under 12 GB | nothing | nothing | off, mine only | The three profile numbers the coordinator asked for, as measured: under 9.0 GB does not exist on this build (floor 13.9); under 15.0 GB mine-and-prove does not exist for any full shard (the v1 shard alone is 20.4); the full profile is the 32 GB card. The fleet table's "proving-only" rows therefore read 24 GB cards at the v1 budget and 32 GB cards at the prototype budget. Shards per block at the v1 budget: 1 on tonight's empty chain, 2 to 4 on blocks with transactions (`B_p` 120,000 = 4 x `S_p`); the aggregation count is one per block whatever the shard count (the chained recursion), so the aggregation-cost agent's target is per block. -The second curve job (the pv1b host's `--budget` re-plans of block 344 at 2.25 M and 4.5 M pgas, and the same points with the miner running) refines the 24 GB "beside the miner" number; it does not move a tier. +The re-plans of block 344 at 2.25 M and 4.5 M pgas peak at 28.3 to 28.4 GB alone (the server's buffers step up between 4.7 M and 20 M cycles and are flat to 60 M), so no shard size between the v1 budget and the prototype one changes a tier; with the miner the adopted shard proves 3.1x slower (13.2 s against 4.2 s) and the chained aggregation 9.7 s against 2.5 s: a mining 24 GB card delivers one adopted-size shard plus one aggregation in about 23 s, inside T by 25x. diff --git a/site/litepaper.html b/site/litepaper.html index ae0113a9..ec63d5d3 100644 --- a/site/litepaper.html +++ b/site/litepaper.html @@ -449,7 +449,7 @@ body.all .pager{display:none}

How a block gets proven

Blocks carry transactions only and make no claim about state. Every node executes the ordered transactions natively at once, so users see their transaction land in about a second. The execution is then split into shards of a fixed proving cost. Shards are assigned by lot to eight provers for ten seconds, then open to anyone; there is no bond. Provers run them on consumer cards, and the shard proofs are folded by recursive aggregation into one proof for the block. That proof lands on-chain within about a minute at launch. Because the proof computes the state from the ordered sequence, no node accepts a block with a wrong state root. Full nodes also execute every block natively and reject a proof record whose result differs from their own execution, so a forged proof is a light-client problem and never a chain split. Implemented: the native-execution check on every carried proof record, proving v0 on the devnet (specification section 7). The emergency path for a soundness bug in the proof system is a human one: a new proof-system version is written by people and activates only on miner signalling. Invalid transactions are skipped by rule, the way Kaspa skips conflicting spends.

The proving budget

-

Gas prices execution. Proving cost is a different number, so Igneum meters it separately: every transaction pays in both dimensions, and each block has a proving-cost budget set in consensus from measured prover throughput. A transaction that is cheap to run and expensive to prove pays for what it costs the provers. Measured on 5 October 2026 (an RTX 5090 under SP1 6.8.1's GPU prover, the shard size the chain adopts from its fee switch, 30,000 proving gas, about 4.7 million prover cycles): one full shard proves in 4.3 seconds and needs 20.4 GB of GPU memory with the card to itself, so a 24 GB card proves full shards and a 12 GB or 16 GB card does not on this prover build, whose floor is 13.9 GB for even an empty shard; mining and proving on one card needs 32 GB today (the prototype-size shard beside the miner peaked at 30.0 GB), and 24 GB once the adopted shard size is live (approximate, from the measured 1.7 GB the miner adds). The old 12 GB gate on the roadmap is withdrawn until a prover build with a smaller floor is measured. The first proofs exist: on 4 October 2026 an RTX 5090 proved a small two-transaction block in 1.4 seconds (2.7 seconds compressed), verified in 0.22 and 0.038 seconds, and a laptop CPU proved a three-shard block end to end in 19 minutes. Later that day the same card proved a full shard at the provisional size, 6.75 million prover gas, which executed in 60.8 million cycles: core proof 8.3 seconds, compressed proof 10.9 seconds, verified in 0.040 seconds; a four-shard block took 44.5 seconds of GPU stages end to end. Since 5 October 2026 shards are assigned and proven on the live devnet. The gate asks for a mid-range card, and an RTX 5090 is not one, so the gate stands open. Once the gate is measured, the budget rises by schedule as hardware improves. The proof system is hash-based, which is what runs on consumer cards, and sits behind a versioned interface, so Igneum can adopt a better proof system when one exists by a miner-signalled release, and runs for ever on the current one if none is adopted.

+

Gas prices execution. Proving cost is a different number, so Igneum meters it separately: every transaction pays in both dimensions, and each block has a proving-cost budget set in consensus from measured prover throughput. A transaction that is cheap to run and expensive to prove pays for what it costs the provers. Measured on 5 October 2026 (an RTX 5090 under SP1 6.8.1's GPU prover, the shard size the chain adopts from its fee switch, 30,000 proving gas, about 4.7 million prover cycles): one full shard proves in 4.3 seconds and needs 20.4 GB of GPU memory with the card to itself, so a 24 GB card proves full shards and a 12 GB or 16 GB card does not on this prover build, whose floor is 13.9 GB for even an empty shard; mining and proving on one card needs 32 GB today (the prototype-size shard beside the miner peaked at 30.1 GB) and 24 GB once the adopted shard size is live (22.2 GB measured beside the miner, 13.2 seconds a shard). The old 12 GB gate on the roadmap is withdrawn until a prover build with a smaller floor is measured. The first proofs exist: on 4 October 2026 an RTX 5090 proved a small two-transaction block in 1.4 seconds (2.7 seconds compressed), verified in 0.22 and 0.038 seconds, and a laptop CPU proved a three-shard block end to end in 19 minutes. Later that day the same card proved a full shard at the provisional size, 6.75 million prover gas, which executed in 60.8 million cycles: core proof 8.3 seconds, compressed proof 10.9 seconds, verified in 0.040 seconds; a four-shard block took 44.5 seconds of GPU stages end to end. Since 5 October 2026 shards are assigned and proven on the live devnet. The gate asks for a mid-range card, and an RTX 5090 is not one, so the gate stands open. Once the gate is measured, the budget rises by schedule as hardware improves. The proof system is hash-based, which is what runs on consumer cards, and sits behind a versioned interface, so Igneum can adopt a better proof system when one exists by a miner-signalled release, and runs for ever on the current one if none is adopted.

Proving for everyone else

The same miners accept proving jobs from other chains. Rollups post a job, a miner wins it, proves it, and is paid. The job market is permissionless and is Designed, not yet built. At launch a job is paid on the customer's own chain, in the customer's currency, to a payout contract keyed by miner address, because Igneum cannot yet see Ethereum. Settlement in IGN, with 10% of each fee burned, follows when the proof bridge lets Igneum see the payment, in phase two. The Igneum miner client can also bid on other proving networks and take the best price, where a miner chooses to hold their collateral: Boundless provers post ZKC and Succinct provers stake PROVE (approximate, from their documentation). The proving market is small today. Igneum does not depend on it. We know of no other proof-of-work chain selling proofs to other chains.

From 4ddc9fcf4cd49e72102c0bc6a01cdbe5fa94a1b3 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:56:32 +0000 Subject: [PATCH 058/158] Proving v1: the miner-on curve's 2^25 rows (the prototype shard 24.7 GB and 38.8 s beside the miner) Co-Authored-By: Claude Fable 5.1 --- docs/bench-log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/bench-log.md b/docs/bench-log.md index df22aa01..138064af 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -1684,7 +1684,7 @@ Job `spcurve-stopped-pc2-pv1` (`tools/proving-v1/pc2-sp-curve.ps1`, the miners s The second job (`spcurve-stopped-pc2-pv1b`, the pv1b host whose `--budget` re-plans a fixture, 20:43 to 20:47Z, the same conditions) repeats the points (empty 13,875 MiB 2.1 s; one transfer 13,907 MiB 3.3 s; the v1 shard 20,435 MiB 4.2 s; the prototype shard 28,275 MiB 11.2 s) and adds the re-plans of block 344 (27 M pgas of modexp): at 2.25 M pgas (one transaction, 16 shards a block, 19,987,938 cycles) **28,371 MiB** and 6.6 s; at 4.5 M pgas (7 shards a block, 40,011,108 cycles) 28,307 MiB and 8.5 s; with the 2^25 trace threshold the 2.25 M shard 22,835 MiB and 6.2 s. So the peak is flat at 28.3 GB from 20 M cycles to 60 M (the server's buffers step up between 4.7 M and 20 M cycles and not after), and cutting the prototype shard smaller buys nothing until the v1 size. -The third job (`spcurve-miner-pc2-pv1`, the same points WITH THE MINER RUNNING on the card, 20:49Z on, the live prover off): empty shard 15,585 MiB and 7.5 s; one transfer 15,745 MiB and 12.7 s; **the v1 shard 22,210 MiB and 13.2 s** (20,435 and 4.2 s alone: the miner adds 1.8 GB and 3.1x); the 2.25 M shard 30,049 MiB and 17.9 s; the 4.5 M shard 29,954 MiB and 26.3 s; the prototype shard 30,083 MiB and 33.3 s. So beside the miner the adopted shard needs 22.2 GB: a 24 GB card (24,564 MiB) has 2.3 GB spare for it (the number for a 24 GB card is the 5090's allocation pattern on a 32 GB card, so approximate for the card itself), and the prototype shard needs 30.1 GB, the 32 GB card alone. +The third job (`spcurve-miner-pc2-pv1`, the same points WITH THE MINER RUNNING on the card, 20:49Z on, the live prover off): empty shard 15,585 MiB and 7.5 s; one transfer 15,745 MiB and 12.7 s; **the v1 shard 22,210 MiB and 13.2 s** (20,435 and 4.2 s alone: the miner adds 1.8 GB and 3.1x); the 2.25 M shard 30,049 MiB and 17.9 s; the 4.5 M shard 29,954 MiB and 26.3 s; the prototype shard 30,083 MiB and 33.3 s. With the 2^25 trace threshold beside the miner: the 2.25 M shard 24,642 MiB and 21.5 s, the prototype shard 24,739 MiB and 38.8 s (24.7 GB: over a 24 GB card by the display's share, and 3.6x slower than the card alone). So beside the miner the adopted shard needs 22.2 GB: a 24 GB card (24,564 MiB) has 2.3 GB spare for it (the number for a 24 GB card is the 5090's allocation pattern on a 32 GB card, so approximate for the card itself), and the prototype shard needs 30.1 GB, the 32 GB card alone. Reading, with the miner-on pairs above (empty shard 15,670 MiB, full prototype shard 30,039 MiB). The witness is never the binding term (4.9 to 21.6 KB a shard); the GPU server's working set is: a floor of 13.9 GB for any shard, 20.4 GB at 4.7 M cycles, 28.3 GB at 60 M cycles (23.0 GB with the smallest trace threshold, at the same time). By card: a **12 GB card proves nothing** on this build (the floor is 13.9 GB alone); a **16 GB card proves only empty and near-empty shards, alone** (13.9 GB; 15.7 GB beside the miner leaves nothing for the display); a **24 GB card proves the adopted v1 shard alone** (20.4 GB) and, at the miner's measured 1.7 GB extra, about 22.1 GB beside it (approximate: not measured on a 24 GB card), and never the prototype shard (28.3 GB); a **32 GB card proves the prototype shard beside the miner with 2.5 GB spare** (30.0 of 32.6 GB). The devnet is on the prototype table until H = 210,000 (6 October, about 19:50Z) and on the adopted v1 table (`S_p` 30,000 pgas) after it, so from H the 24 GB tier joins the provers and the shard that binds the memory is the 4.7 M-cycle one. Shards per block at each size: 1 at the prototype `S_p`, 4 at `B_p`; at the v1 budget 1 to 4 (one a block on tonight's chain, 2 to 3 on the txgen blocks). From 46bad306d8c94aee2a6799111316505f589fab1b Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:57:33 +0000 Subject: [PATCH 059/158] Proving v1: the harness passes on the final fork tree (N = 8, 21 checks in 244 s) Co-Authored-By: Claude Fable 5.1 --- docs/bench-log.md | 2 +- docs/plans/proving-v1.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/bench-log.md b/docs/bench-log.md index 138064af..74ebabd0 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -1693,4 +1693,4 @@ Reading, with the miner-on pairs above (empty shard 15,670 MiB, full prototype s | What | Measured | |---|---| | Unit tests | `cargo test --release -p kaspa-consensus-core -p igneum-exec --lib -- proving config::params::tests::override_params_carry_the_proving_v1 config::params::tests::consensus_digest` on this Mac (target `vendor/igneum-node/target-pv1`, 19:09Z): consensus core 13 passed (the segment record round trip, signature and the three nested sections; the credit split; the params switch and the digest that moves only once the switch is set), exec 8 passed (the segment grid and the split; the record checks: alignment, block, chain length, the veto naming the field, the deadline, the window; the chain rule both ways; the unproven restart; the shard side at 90%; the pool offering the segment section). The six full node suites go to PC 2 as a build job when the fleet is back | -| The fast-time 3-node harness (`tools/proving-v1/net.mjs`, 29950+, suffix 956, every node in trust mode, three vmine voters, v0 at DAA 60, v1 at DAA 120, 4 blocks a segment, unproven after 60 DAA, a tenth to the aggregator; fork b177718e built on this Mac) | run 2, 19:13:01Z to 19:16:19Z, under the run lock: PASSED, 21 checks in 197.3 s (`tools/proving-v1/report-2026-10-05.json`). v1 start = chain block 119 on all three nodes; the native statement identical on all three. Known-finished: segment 119..122's fresh-chain record submitted to n1 at t=131.1 s, relayed, verified (trust) and PAID on n0 1.0 s later at chain block 129, 253,611,648,000,000,000 wei = a tenth of the four credits, the same on every node, the payout address holding it. Chain rule: segment 123..126's fresh-chain record refused ("does not chain to segment 119..122 ... proven (record paid at chain block 129)"), the continuing one (chain_len 8) accepted and paid. Known-failed: segment 127..130 left without a record: a fresh-chain record for 131..134 refused while 127..130 was pending ("pending until DAA 191"); at DAA 192 the status read unproven, a late record for 127..130 refused ("unproven: carried after the deadline"), the fresh-chain record for 131..134 accepted and paid with chain_len 4; `segmentsInWindow` proven 3, unproven 1. The shard side: a v1 shard's `shardWei` = 90% of its block's credit. Run 1 (19:10Z) failed in its own tooling (the signer's argument order), fixed | +| The fast-time 3-node harness (`tools/proving-v1/net.mjs`, 29950+, suffix 956, every node in trust mode, three vmine voters, v0 at DAA 60, v1 at DAA 120, 4 blocks a segment, unproven after 60 DAA, a tenth to the aggregator; fork b177718e built on this Mac) | run 2, 19:13:01Z to 19:16:19Z, under the run lock: PASSED, 21 checks in 197.3 s (`tools/proving-v1/report-2026-10-05.json`). v1 start = chain block 119 on all three nodes; the native statement identical on all three. Known-finished: segment 119..122's fresh-chain record submitted to n1 at t=131.1 s, relayed, verified (trust) and PAID on n0 1.0 s later at chain block 129, 253,611,648,000,000,000 wei = a tenth of the four credits, the same on every node, the payout address holding it. Chain rule: segment 123..126's fresh-chain record refused ("does not chain to segment 119..122 ... proven (record paid at chain block 129)"), the continuing one (chain_len 8) accepted and paid. Known-failed: segment 127..130 left without a record: a fresh-chain record for 131..134 refused while 127..130 was pending ("pending until DAA 191"); at DAA 192 the status read unproven, a late record for 127..130 refused ("unproven: carried after the deadline"), the fresh-chain record for 131..134 accepted and paid with chain_len 4; `segmentsInWindow` proven 3, unproven 1. The shard side: a v1 shard's `shardWei` = 90% of its block's credit. Run 1 (19:10Z) failed in its own tooling (the signer's argument order), fixed. Run 3 on the FINAL fork tree (ece42979 on the 0.3.10 commit 21d4c73c, protocol 15, N = 8 both in the params default and `--segment 8`, the fast-time file's four fields), 20:52:41Z to 20:56:45Z: PASSED, 21 checks in 244.4 s (segments of 8: 119..126 paid in 1.0 s after submission, 127..134 refused fresh and paid continuing with chain_len 16, 135..142 left unproven and skipped, 143..150 restarted the chain) | diff --git a/docs/plans/proving-v1.md b/docs/plans/proving-v1.md index 3f2a72bb..ee707b99 100644 --- a/docs/plans/proving-v1.md +++ b/docs/plans/proving-v1.md @@ -38,7 +38,7 @@ that say how many cards cover the chain. | Shards a minute, one 5090 through the app's loop (empty shards) | 1.6 | | Chain of 2 live blocks on the Mac CPU (`--mode chain`) | shard 55.4 and 41.3 s, aggregate 52.0 s then 59.1 s with the previous proof, chain_len 2, final proof 1,272,909 bytes, `verify-segment` 0.032 s | | Unit tests | consensus core 13, exec 8, app 5, all passing on the Mac | -| The harness (3 nodes, fast time, trust mode) | PASSED, 21 checks in 197 s: paid 1.0 s after submit, every node agreeing; the fresh chain refused after a proven segment; the unproven segment skipped after its deadline; shards at 90% | +| The harness (3 nodes, fast time, trust mode) | PASSED, 21 checks in 197 s on b177718e (N = 4) and 244 s on the final tree ece42979 (N = 8): paid 1.0 s after submit, every node agreeing; the fresh chain refused after a proven segment; the unproven segment skipped after its deadline; shards at 90% | | Coverage, 3-min window, the degraded fleet (one card, the Mac verifier down) | 4.7% of blocks proven, on-chain latency p50 39 s | | The chain of 8 live blocks on the 5090, the card also mining (`chain-pc2-pv1c`) | shard 7.3 to 7.7 s, first aggregation 7.9 s, every chained one 9.6 to 9.7 s; N = 2 in 32.6 s, N = 4 in 66.8 s, N = 8 in 135.6 s (17.0 s a block); the final proof 1,272,909 bytes whatever N, the record 586 bytes, `verify-segment` 0.037 to 0.040 s; GPU peak 16,751 MiB with the miner resident. Against 4 October with the miner stopped (aggregate 2.2 s): the miner slows the prover 3 to 4x | | 5090-class cards for 100% at 1 block/s, measured rows | 47 with the loop as it is, 18 through the chain mode on mining cards, 6 (approximate) on proving-only cards, at empty blocks; 14 proving-only at one full shard a block; 45 at `B_p`: the table in the bench log | From f74a0874432ef589e844bc65986649070c9ac717 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:03:27 +0000 Subject: [PATCH 060/158] mixer x4: the two pinned class v3 packs (proto-cuda/packs-ca2-mixer/mx4-genesis and mx4-devnet-epoch0, generator 3 on V3_CLASS = mx4, the devnet pack with era 0's stand-in), tests/packs.rs runs every pinned check over them plus v3_packs_are_the_v2_seeds_under_mixer_x4; igneum-pow --program-class v2|v3 and --era-hex through one epoch_of helper (export, bench, hash, hash-bound); fresh exports of igneum-genesis-mh and igneum-devnet-v4-epoch0 diff clean against the checked-in packs Co-Authored-By: Claude Fable 5.1 --- igneum-pow/src/main.rs | 76 ++-- igneum-pow/tests/packs.rs | 103 ++++- .../mx4-devnet-epoch0/kernel.cl | 279 +++++++++++++ .../mx4-devnet-epoch0/kernel.cu | 164 ++++++++ .../mx4-devnet-epoch0/kernel_bound.cl | 373 ++++++++++++++++++ .../mx4-devnet-epoch0/kernel_bound.cu | 123 ++++++ .../mx4-devnet-epoch0/memhard.h | 109 +++++ .../mx4-devnet-epoch0/memhard.metal | 107 +++++ .../mx4-devnet-epoch0/program.h | 67 ++++ .../mx4-devnet-epoch0/program.json | 133 +++++++ .../mx4-devnet-epoch0/program.metal | 109 +++++ .../mx4-devnet-epoch0/program_bound.metal | 111 ++++++ .../mx4-devnet-epoch0/vectors.h | 57 +++ .../mx4-devnet-epoch0/vectors.json | 36 ++ .../packs-ca2-mixer/mx4-genesis/kernel.cl | 279 +++++++++++++ .../packs-ca2-mixer/mx4-genesis/kernel.cu | 164 ++++++++ .../mx4-genesis/kernel_bound.cl | 373 ++++++++++++++++++ .../mx4-genesis/kernel_bound.cu | 123 ++++++ .../packs-ca2-mixer/mx4-genesis/memhard.h | 109 +++++ .../packs-ca2-mixer/mx4-genesis/memhard.metal | 107 +++++ .../packs-ca2-mixer/mx4-genesis/program.h | 66 ++++ .../packs-ca2-mixer/mx4-genesis/program.json | 132 +++++++ .../packs-ca2-mixer/mx4-genesis/program.metal | 109 +++++ .../mx4-genesis/program_bound.metal | 111 ++++++ .../packs-ca2-mixer/mx4-genesis/vectors.h | 57 +++ .../packs-ca2-mixer/mx4-genesis/vectors.json | 36 ++ 26 files changed, 3481 insertions(+), 32 deletions(-) create mode 100644 proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cl create mode 100644 proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cu create mode 100644 proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.h create mode 100644 proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.metal create mode 100644 proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.h create mode 100644 proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.json create mode 100644 proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.metal create mode 100644 proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program_bound.metal create mode 100644 proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.h create mode 100644 proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.json create mode 100644 proto-cuda/packs-ca2-mixer/mx4-genesis/kernel.cl create mode 100644 proto-cuda/packs-ca2-mixer/mx4-genesis/kernel.cu create mode 100644 proto-cuda/packs-ca2-mixer/mx4-genesis/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-mixer/mx4-genesis/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-mixer/mx4-genesis/memhard.h create mode 100644 proto-cuda/packs-ca2-mixer/mx4-genesis/memhard.metal create mode 100644 proto-cuda/packs-ca2-mixer/mx4-genesis/program.h create mode 100644 proto-cuda/packs-ca2-mixer/mx4-genesis/program.json create mode 100644 proto-cuda/packs-ca2-mixer/mx4-genesis/program.metal create mode 100644 proto-cuda/packs-ca2-mixer/mx4-genesis/program_bound.metal create mode 100644 proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.h create mode 100644 proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.json diff --git a/igneum-pow/src/main.rs b/igneum-pow/src/main.rs index a12f225a..e69d3195 100644 --- a/igneum-pow/src/main.rs +++ b/igneum-pow/src/main.rs @@ -12,10 +12,10 @@ //! on every command selects the load class (default v2, the lottery hash). Nothing in a v2 run changes. use igneum_pow::emit::export_pack; -use igneum_pow::generator::LoadClass; +use igneum_pow::generator::{LoadClass, ProgramClass}; use igneum_pow::memhard::{Cache, Shape}; use igneum_pow::seed::day_key; -use igneum_pow::verify::{DatasetMode, Epoch, DEFAULT_DATASET_LOG2}; +use igneum_pow::verify::{DatasetMode, DatasetSource, Epoch, DEFAULT_DATASET_LOG2}; use std::time::Instant; struct Args { @@ -33,6 +33,10 @@ struct Args { class: LoadClass, /// Days since genesis for the cache growth rule of a class with `growth` (0: the genesis cache). days: u64, + /// The program class (Counter ASIC 2.0 seam): v2 (default) or v3, which draws from V3_CLASS with generator 3. + program_class: Option, + /// The era seed bytes a class v3 chain program records (`--era-hex`). + era_hex: Option, } fn usage() -> ! { @@ -45,7 +49,8 @@ fn usage() -> ! { \x20 accept every candidate of the seed (or --epoch-hex) with its acceptance verdict\n\ \x20 show the accepted program, one instruction per line\n\ \x20 --class C load class: v2 (default), mx4 (class v3: mixer x4, cache growth), w4, w16, w64, w64x4, p4,p16,p64[xN], m[g]\n\ - \x20 --days N days since genesis for the cache growth rule of a class with it (default 0: the 2^26-word cache)" + \x20 --days N days since genesis for the cache growth rule of a class with it (default 0: the 2^26-word cache)\n\ + \x20 --program-class v2|v3 the program class of the seam (v3 = generator 3 on V3_CLASS, the chain's own derivation; --era-hex records the era seed)" ); std::process::exit(2) } @@ -65,6 +70,8 @@ fn parse() -> Args { day_hex: None, class: LoadClass::V2, days: 0, + program_class: None, + era_hex: None, }; let mut it = std::env::args().skip(1); a.cmd = it.next().unwrap_or_else(|| usage()); @@ -83,6 +90,8 @@ fn parse() -> Args { "--day-hex" => a.day_hex = Some(val()), "--class" => a.class = LoadClass::parse(&val()).unwrap_or_else(|| usage()), "--days" => a.days = val().parse().unwrap_or_else(|_| usage()), + "--program-class" => a.program_class = Some(ProgramClass::parse(&val()).unwrap_or_else(|| usage())), + "--era-hex" => a.era_hex = Some(val()), _ => usage(), } } @@ -98,21 +107,14 @@ fn main() { "accept" => accept(&a), "show" => show(&a), "hash" => { - let e = Epoch::new_class_day(&a.seed, &a.day, mode, a.dataset_log2, a.class, a.days); + let (e, _) = epoch_of(&a, mode); println!("{:016x}", e.hash(a.nonce as u32)); } "hash-bound" => { let bytes = igneum_pow::bind::unhex(&a.prehash).unwrap_or_else(|| usage()); let prehash: [u8; 32] = bytes.as_slice().try_into().unwrap_or_else(|_| usage()); // --epoch-hex / --day-hex: the chain's byte seeds (Epoch::from_seed_bytes), as the worker protocol carries them - let e = match (&a.epoch_hex, &a.day_hex) { - (Some(eh), Some(dh)) => { - let eb = igneum_pow::bind::unhex(eh).unwrap_or_else(|| usage()); - let db = igneum_pow::bind::unhex(dh).unwrap_or_else(|| usage()); - Epoch::from_seed_bytes_class(&eb, &db, "cli", a.class) - } - _ => Epoch::new_class_day(&a.seed, &a.day, mode, a.dataset_log2, a.class, a.days), - }; + let (e, _) = epoch_of(&a, mode); let init = igneum_pow::bind::block_init_words(&prehash, a.nonce); println!("init words {}", init.iter().map(|w| format!("{w:08x}")).collect::>().join(" ")); println!("{:016x}", e.hash_bound(&prehash, a.nonce)); @@ -121,6 +123,43 @@ fn main() { } } +/// The epoch every command works on, and the day label for packs. `--epoch-hex`/`--day-hex` give the chain's byte +/// seeds (the day label then names the day bytes); else the string seed and day. `--program-class v3` draws the +/// program through the seam (generator 3 on `V3_CLASS`, the era bytes of `--era-hex` recorded) and sizes the +/// dataset for `--days` through `Epoch::chain_dataset_day`; `--class` is ignored under a program class (the class +/// names the load class). Closed-form mode is only for string seeds under the default class. +fn epoch_of(a: &Args, mode: DatasetMode) -> (Epoch, String) { + let era = a.era_hex.as_ref().map(|h| igneum_pow::bind::unhex(h).unwrap_or_else(|| usage())); + match (&a.epoch_hex, &a.day_hex) { + (Some(eh), Some(dh)) => { + let eb = igneum_pow::bind::unhex(eh).unwrap_or_else(|| usage()); + let db = igneum_pow::bind::unhex(dh).unwrap_or_else(|| usage()); + let label = format!("igneum-epoch/{eh}/day/{dh}"); + let e = match a.program_class { + Some(pc) => Epoch { + program: Epoch::chain_program(&eb, era.as_deref(), pc, &label), + dataset: Epoch::chain_dataset_day(&db, pc, a.days, a.dataset_log2), + }, + None => Epoch::from_seed_bytes_day(&eb, &db, &label, a.class, a.days, a.dataset_log2), + }; + (e, format!("bytes:{dh}")) + } + _ => { + let e = match a.program_class { + Some(pc) => { + let program = igneum_pow::generator::generate_from_seed_bytes_program_class(&a.seed, a.seed.as_bytes(), pc, era.as_deref()); + let lc = pc.load_class(); + let shape = Shape::for_class_day(&lc, a.days); + let log2 = if lc.growth { igneum_pow::memhard::dataset_log2_words(a.dataset_log2, a.days) } else { a.dataset_log2 }; + Epoch { program, dataset: DatasetSource::new_shape(&a.day, mode, log2, shape) } + } + None => Epoch::new_class_day(&a.seed, &a.day, mode, a.dataset_log2, a.class, a.days), + }; + (e, a.day.clone()) + } + } +} + fn bench(a: &Args, mode: DatasetMode) { println!( "igneum-pow bench: seed \"{}\", day \"{}\", dataset 2^{} words ({})", @@ -129,7 +168,7 @@ fn bench(a: &Args, mode: DatasetMode) { a.dataset_log2, mode.name() ); - let shape = Shape::for_class_day(&a.class, a.days); + let shape = Shape::for_class_day(&a.program_class.map(|pc| pc.load_class()).unwrap_or(a.class), a.days); if mode == DatasetMode::MemoryHard { // Time the cache fill on its own first (one core), then build the epoch (which fills it again). let t0 = Instant::now(); @@ -145,7 +184,7 @@ fn bench(a: &Args, mode: DatasetMode) { drop(c); } let t0 = Instant::now(); - let e = Epoch::new_class_day(&a.seed, &a.day, mode, a.dataset_log2, a.class, a.days); + let (e, _) = epoch_of(a, mode); let build_ms = t0.elapsed().as_secs_f64() * 1e3; println!( "program: class {}, {} loads/hash, {} bytes/hash, widths (1,4,16 words) {:?}, {} items/warp, mixer x{} ({} mixers/item), cache 2^{} words, op mix {}; epoch built in {build_ms:.1} ms", @@ -184,14 +223,7 @@ fn export(a: &Args, mode: DatasetMode) { let out = a.out.clone().unwrap_or_else(|| usage()); let t0 = Instant::now(); // --epoch-hex / --day-hex: the chain's byte seeds; the day label then names the day bytes - let (e, day_label) = match (&a.epoch_hex, &a.day_hex) { - (Some(eh), Some(dh)) => { - let eb = igneum_pow::bind::unhex(eh).unwrap_or_else(|| usage()); - let db = igneum_pow::bind::unhex(dh).unwrap_or_else(|| usage()); - (Epoch::from_seed_bytes_class(&eb, &db, &format!("igneum-epoch/{eh}/day/{dh}"), a.class), format!("bytes:{dh}")) - } - _ => (Epoch::new_class_day(&a.seed, &a.day, mode, a.dataset_log2, a.class, a.days), a.day.clone()), - }; + let (e, day_label) = epoch_of(a, mode); let build_ms = t0.elapsed().as_secs_f64() * 1e3; println!("igneum-pow export {out}"); println!( diff --git a/igneum-pow/tests/packs.rs b/igneum-pow/tests/packs.rs index 0bab3d9c..e29d2b56 100644 --- a/igneum-pow/tests/packs.rs +++ b/igneum-pow/tests/packs.rs @@ -5,28 +5,43 @@ //! //! Packs: igneum-genesis-mh and igneum-devnet-v4-epoch0 (memory-hard; the latter from the devnet genesis hash as //! the epoch seed and the day bytes of 2026-10-04), igneum-genesis and igneum-hourly (closed-form dataset, -//! interpreter regression only). +//! interpreter regression only); and, under `proto-cuda/packs-ca2-mixer/`, the class v3 packs mx4-genesis and +//! mx4-devnet-epoch0 (Counter ASIC 2.0, 5 October 2026: generator 3 on `V3_CLASS` = mixer x4 with the cache growth +//! rule, the same seeds and days as the two memory-hard v2 packs, so the v2 program and cache carry over and only +//! the dataset words and the hashes change). use igneum_pow::accept; use igneum_pow::emit::{ cuda_kernel, cuda_kernel_bound, cuda_memhard_header, export_pack, metal_memhard, metal_program, metal_program_bound, opencl_kernel, opencl_kernel_bound, program_header, program_json, LoadSource, }; -use igneum_pow::generator::{generate_from_seed_bytes, Op, GENERATOR_VERSION, LOAD_SLOTS}; -use igneum_pow::memhard::CACHE_WORDS; +use igneum_pow::generator::{generate_from_seed_bytes, generate_from_seed_bytes_program_class, Op, ProgramClass, GENERATOR_VERSION, GENERATOR_VERSION_V3, LOAD_SLOTS, V3_CLASS}; +use igneum_pow::memhard::{Shape, CACHE_WORDS}; use igneum_pow::verify::{DatasetMode, DatasetSource, Epoch}; use serde_json::Value; use std::path::PathBuf; use std::sync::OnceLock; -const PACKS: [&str; 4] = ["igneum-genesis-mh", "igneum-devnet-v4-epoch0", "igneum-genesis", "igneum-hourly"]; +const PACKS: [&str; 6] = + ["igneum-genesis-mh", "igneum-devnet-v4-epoch0", "igneum-genesis", "igneum-hourly", "mx4-genesis", "mx4-devnet-epoch0"]; +/// The class v3 packs (the last two of `PACKS`). +const PACKS_V3: [&str; 2] = ["mx4-genesis", "mx4-devnet-epoch0"]; fn packs_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda/packs") } +/// The directory a pack lives in: the class v3 packs under packs-ca2-mixer, the rest under packs. +fn pack_dir(pack: &str) -> PathBuf { + if pack.starts_with("mx4-") { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda/packs-ca2-mixer").join(pack) + } else { + packs_dir().join(pack) + } +} + fn read(pack: &str, file: &str) -> String { - let p = packs_dir().join(pack).join(file); + let p = pack_dir(pack).join(file); std::fs::read_to_string(&p).unwrap_or_else(|e| panic!("read {}: {e}", p.display())) } @@ -62,9 +77,18 @@ fn epoch(pack: &str) -> &'static Epoch { _ => DatasetMode::ClosedForm, }; let log2 = j["dataset"]["log2_words"].as_u64().unwrap() as u32; - let program = generate_from_seed_bytes(seed, &seed_bytes); + // a class v3 pack: generator 3 on V3_CLASS through the seam, the era bytes it records, the dataset + // in the class's shape on day 0 (the growth rule's genesis cache: every pinned pack is a day-0 size) + let program = match j["generator"].as_u64().unwrap() as u32 { + GENERATOR_VERSION_V3 => { + let era = j.get("era_seed_bytes").map(unhex); + generate_from_seed_bytes_program_class(seed, &seed_bytes, ProgramClass::V3, era.as_deref()) + } + _ => generate_from_seed_bytes(seed, &seed_bytes), + }; + let shape = Shape::for_class(&program.class); let mut dataset = - DatasetSource::from_key(igneum_pow::seed::seed_words_from_bytes(&day_bytes), mode, log2); + DatasetSource::from_key_shape(igneum_pow::seed::seed_words_from_bytes(&day_bytes), mode, log2, shape); dataset.key_bytes = day_bytes; (p.to_string(), Epoch { program, dataset }) }) @@ -81,7 +105,8 @@ fn check_program_json(pack: &str) { let j = json(pack, "program.json"); let p = &epoch(pack).program; assert_eq!(j["format"].as_str().unwrap(), "igneum-program-pack-3"); - assert_eq!(j["generator"].as_u64().unwrap() as u32, GENERATOR_VERSION, "{pack}: generator version"); + assert_eq!(j["generator"].as_u64().unwrap() as u32, p.generator, "{pack}: generator version"); + assert_eq!(p.generator, if PACKS_V3.contains(&pack) { GENERATOR_VERSION_V3 } else { GENERATOR_VERSION }); assert_eq!(j["attempt"].as_u64().unwrap() as u32, p.attempt, "{pack}: attempt"); assert_eq!(hex64(&j["program_id"]), p.program_id(), "{pack}: program id"); let sw: Vec = j["seed_words"].as_array().unwrap().iter().map(hex32).collect(); @@ -129,7 +154,7 @@ fn genesis_program_shape() { #[test] fn mixer_params_match_pack() { - for pack in ["igneum-genesis-mh", "igneum-devnet-v4-epoch0"] { + for pack in ["igneum-genesis-mh", "igneum-devnet-v4-epoch0", "mx4-genesis", "mx4-devnet-epoch0"] { let j = json(pack, "program.json"); let mp = &epoch(pack).dataset.memhard().unwrap().params; let key: Vec = j["dataset"]["key"].as_array().unwrap().iter().map(hex32).collect(); @@ -287,6 +312,64 @@ fn emitted_sources_match_all_packs() { } /// The whole pack as `export` writes it: vectors.json and vectors.h match, and the file list is the full set. +/// The class v3 packs (Counter ASIC 2.0, `docs/plans/mixer-x4.md`): generator 3 on V3_CLASS = mx4; the program of +/// each is the v2 program of the same seed instruction for instruction (v2 loads take no width roll); the cache is +/// the v2 cache (day 0 of the growth rule: 2^26 words, the same FNV-1a 64); the dataset words differ from v2's; +/// program.json, program.h and the emitted memhard core say so; the id carries generator 3. +#[test] +fn v3_packs_are_the_v2_seeds_under_mixer_x4() { + assert_eq!(V3_CLASS.name(), "mx4"); + assert_eq!(V3_CLASS.mixer_mult, 4); + assert!(V3_CLASS.growth); + for (v3, v2) in [("mx4-genesis", "igneum-genesis-mh"), ("mx4-devnet-epoch0", "igneum-devnet-v4-epoch0")] { + let e3 = epoch(v3); + let e2 = epoch(v2); + let j = json(v3, "program.json"); + assert_eq!(j["program_class"].as_str().unwrap(), "v3"); + assert_eq!(j["load_class"].as_str().unwrap(), "mx4"); + assert_eq!(j["mixer_mult"].as_u64().unwrap(), 4); + assert_eq!(j["cache_growth"].as_bool().unwrap(), true); + assert_eq!(j["dataset"]["mixer_mult"].as_u64().unwrap(), 4); + assert_eq!(j["dataset"]["cache"]["log2_words"].as_u64().unwrap(), 26); + assert_eq!(e3.program.generator, GENERATOR_VERSION_V3); + assert_eq!(e3.program.class, V3_CLASS); + assert_eq!(e3.program.instrs, e2.program.instrs, "{v3}: the v2 program under the v3 construction"); + assert_eq!(e3.program.seed, e2.program.seed); + assert_eq!(e3.program.attempt, e2.program.attempt); + assert_ne!(e3.program.program_id(), e2.program.program_id()); + assert_eq!(e3.program.program_id(), igneum_pow::generator::program_id(GENERATOR_VERSION_V3, &e3.program.seed, e3.program.attempt)); + let m3 = e3.dataset.memhard().unwrap(); + let m2 = e2.dataset.memhard().unwrap(); + assert_eq!(m3.shape(), Shape { mixer_mult: 4, cache_log2_words: 26 }); + assert_eq!(m3.cache.fnv1a64(), m2.cache.fnv1a64(), "{v3}: the same cache as v2 on day 0"); + assert_eq!(m3.params.rot, m2.params.rot); + assert_eq!(e3.dataset.log2_words, 28); + assert_ne!(e3.dataset.word(0), e2.dataset.word(0), "{v3}: the dataset words differ"); + assert_ne!(e3.hash_warp(0), e2.hash_warp(0)); + let h = read(v3, "program.h"); + assert!(h.contains("#define IGNEUM_GENERATOR 3\n")); + assert!(h.contains("#define IGNEUM_PROGRAM_CLASS \"v3\"\n")); + assert!(h.contains("#define IGNEUM_MIXER_MULT 4")); + assert!(h.contains("#define IGNEUM_CACHE_GROWTH 1")); + assert!(h.contains("#define IGNEUM_CACHE_LOG2_WORDS 26\n")); + assert!(h.contains("#define IGNEUM_LOAD_CLASS \"mx4\"\n")); + for file in ["memhard.h", "memhard.metal", "kernel.cl"] { + let text = read(v3, file); + assert_eq!(text.matches("j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 4u + j + 1u))").count(), 1, "{v3}/{file}"); + assert_eq!(text.matches("j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u))").count(), 1, "{v3}/{file}"); + } + for file in ["memhard.h", "memhard.metal", "kernel.cl"] { + let text = read(v2, file); + assert_eq!(text.matches("j < 4u").count(), 0, "{v2}/{file}: the v2 text has no multiplier loop"); + } + } + // the devnet v3 pack records era 0's stand-in, the devnet genesis hash + let j = json("mx4-devnet-epoch0", "program.json"); + assert_eq!(unhex(&j["era_seed_bytes"]), unhex(&j["seed_bytes"])); + assert!(read("mx4-devnet-epoch0", "program.h").contains("#define IGNEUM_ERA_SEED_HEX \"edc4fa844da9dc98")); + assert!(json("mx4-genesis", "program.json").get("era_seed_bytes").is_none()); +} + fn check_export(pack: &str) { let e = epoch(pack); let v = json(pack, "vectors.json"); @@ -311,7 +394,7 @@ fn check_export(pack: &str) { expected.extend(["memhard.h", "memhard.metal"]); } assert_eq!(out.files.iter().map(|(n, _)| n.as_str()).collect::>(), expected); - let mut on_disk: Vec = std::fs::read_dir(packs_dir().join(pack)) + let mut on_disk: Vec = std::fs::read_dir(pack_dir(pack)) .unwrap() .map(|d| d.unwrap().file_name().to_string_lossy().to_string()) .filter(|n| !n.starts_with('.')) diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cl b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cl new file mode 100644 index 00000000..a32be499 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cl @@ -0,0 +1,279 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 4 x seed-parameterised mixer + one 64-byte cache read, then 4 x final mixer (class v3, mixer multiplier 4, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 4 x mixer + cache line s[0] & mask; 4 x final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 4u + j + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = mul_hi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = mul_hi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = mul_hi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = mul_hi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = mul_hi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = mul_hi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = mul_hi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = mul_hi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = mul_hi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cu b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cu new file mode 100644 index 00000000..c129edee --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cu @@ -0,0 +1,164 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 4); // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = __umulhi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = __umulhi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = __umulhi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = __umulhi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = __umulhi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = __umulhi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r7, 8); // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = __umulhi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = __umulhi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = __umulhi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cl b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cl new file mode 100644 index 00000000..3ae5ef11 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cl @@ -0,0 +1,373 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 4 x seed-parameterised mixer + one 64-byte cache read, then 4 x final mixer (class v3, mixer multiplier 4, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 4 x mixer + cache line s[0] & mask; 4 x final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 4u + j + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = mul_hi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = mul_hi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = mul_hi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = mul_hi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = mul_hi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = mul_hi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = mul_hi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = mul_hi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = mul_hi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = mul_hi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = mul_hi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = mul_hi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = mul_hi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = mul_hi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = mul_hi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = mul_hi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = mul_hi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = mul_hi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cu b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cu new file mode 100644 index 00000000..9a46b748 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cu @@ -0,0 +1,123 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 4); // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = __umulhi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = __umulhi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = __umulhi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = __umulhi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = __umulhi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = __umulhi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r7, 8); // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = __umulhi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = __umulhi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = __umulhi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.h b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.h new file mode 100644 index 00000000..bbe76f94 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.h @@ -0,0 +1,109 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 4 x seed-parameterised mixer + one 64-byte cache read, then 4 x final mixer (class v3, mixer multiplier 4, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 4 x mixer + cache line s[0] & mask; 4 x final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint32_t r = 0u; r < 8u; ++r) { + for (uint32_t j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 4u + j + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint32_t j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.metal b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.metal new file mode 100644 index 00000000..57022480 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.metal @@ -0,0 +1,107 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 4 x seed-parameterised mixer + one 64-byte cache read, then 4 x final mixer (class v3, mixer multiplier 4, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 4 x mixer + cache line s[0] & mask; 4 x final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 4u + j + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.h b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.h new file mode 100644 index 00000000..a9558ba3 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.h @@ -0,0 +1,67 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_SEED_BYTES_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" +#define IGNEUM_GENERATOR 3 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x73bcbfe8ccf988f1ull +#define IGNEUM_DAY_STRING "bytes:69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY_BYTES_HEX "69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY0 0xceed56d7u +#define IGNEUM_DAY1 0x9ba270d2u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 add=13 mulhi=9 shfl=5 sub=5 mad=4 rotl=4 xor=3 or=2 rotr=2 mul=1" +// Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that +// runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). +#define IGNEUM_PROGRAM_CLASS "v3" +#define IGNEUM_ERA_SEED_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "mx4" +#define IGNEUM_CLASS_MIXER_MULT 4 +#define IGNEUM_CACHE_GROWTH 1 // 1: cache words = 2^(26 + doublings(day)), doublings = floor(log2(1 + day / 1460)) +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u } +#define IGNEUM_KEY_INIT { 0xceed56d7u, 0x9ba270d2u, 0x82caab2du, 0x81ebce0eu, 0x12b6ecf1u, 0xd0f3fd7cu, 0xd872eefeu, 0xc158c7bdu } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIXER_MULT 4 // mixer applications per round and after the last read (class v3, docs/plans/mixer-x4.md) +#define IGNEUM_MIX_ROT_INIT { 17u, 12u, 20u, 23u, 7u, 3u, 27u, 16u } +#define IGNEUM_MIX_MUL_INIT { 0xf351d601u, 0xa3bb398fu, 0xb5a09e35u, 0x7509c9c1u, 0x6bbf31e9u, 0xfc849a79u, 0xded91851u, 0x8d9113d1u, 0x0ff15225u, 0x3a5bdd41u, 0xab533435u, 0xe1c55ad5u, 0xe6d3bd0du, 0x9d9ffbbdu, 0xbb2a3cf3u, 0x50a7c08du } +#define IGNEUM_MIX_RC_INIT { 0xc6892460u, 0x25b7228au, 0xcd515004u, 0x2846527au, 0xa6324241u, 0x36e3ec53u, 0x82961bacu, 0x0f97ba7du, 0xb6f921a9u, 0x3ada24e5u, 0xde20ab91u, 0x5378eeb2u, 0x7d161662u, 0x89353cc1u, 0xb1aa03a2u, 0x788acae6u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.json b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.json new file mode 100644 index 00000000..87a22e6d --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.json @@ -0,0 +1,133 @@ +{ + "format": "igneum-program-pack-3", + "generator": 3, + "attempt": 0, + "program_id": "0x73bcbfe8ccf988f1", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07", + "seed_words": ["0x667d0fbd", "0x7b8e5963", "0x31c67e5e", "0x4529ddc6", "0xef19d6d8", "0xaccf6211", "0xda0aed32", "0xabc6df31"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "program_class": "v3", + "era_seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07", + "load_class": "mx4", + "mixer_mult": 4, + "cache_growth": true, + "mixer": "class v3 (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): every mixer application of the item derivation is 4 applications with round keys (r * 4 + j + 1) * 0x9E3779B9, the 8 dependent cache reads per item unchanged; cache growth rule option C: cache words = 2^(26 + doublings(day)), dataset words = 2^(genesis_log2 + doublings(day)), doublings(day) = floor(log2(1 + day / 1460)) for day = days since genesis", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "op_mix": {"load": 16, "add": 13, "mulhi": 9, "shfl": 5, "sub": 5, "mad": 4, "rotl": 4, "xor": 3, "or": 2, "rotr": 2, "mul": 1}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "day_bytes": "69676e65756d2d6461792ffa50000000000000", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0xceed56d7", + "d1": "0x9ba270d2", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0xceed56d7", "0x9ba270d2", "0x82caab2d", "0x81ebce0e", "0x12b6ecf1", "0xd0f3fd7c", "0xd872eefe", "0xc158c7bd"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [17, 12, 20, 23, 7, 3, 27, 16], "mul": ["0xf351d601", "0xa3bb398f", "0xb5a09e35", "0x7509c9c1", "0x6bbf31e9", "0xfc849a79", "0xded91851", "0x8d9113d1", "0x0ff15225", "0x3a5bdd41", "0xab533435", "0xe1c55ad5", "0xe6d3bd0d", "0x9d9ffbbd", "0xbb2a3cf3", "0x50a7c08d"], "rc": ["0xc6892460", "0x25b7228a", "0xcd515004", "0x2846527a", "0xa6324241", "0x36e3ec53", "0x82961bac", "0x0f97ba7d", "0xb6f921a9", "0x3ada24e5", "0xde20ab91", "0x5378eeb2", "0x7d161662", "0x89353cc1", "0xb1aa03a2", "0x788acae6"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "mixer_mult": 4, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: for j in 0..3: s = M(s, rk = (r * 4 + j + 1) * 0x9E3779B9); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then for j in 0..3: s = M(s, rk = (32 + j + 1) * 0x9E3779B9); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1}, + {"i": 1, "op": "shfl", "dst": 2, "src": 0, "src2": 7, "imm": "0xe3c2f9cb", "imm2": "0xde0bea4e", "rot": 6, "bit": 9, "mask": 4, "width": 1}, + {"i": 2, "op": "add", "dst": 3, "src": 2, "src2": 0, "imm": "0x2cccb6ca", "imm2": "0x642e66db", "rot": 27, "bit": 10, "mask": 2, "width": 1}, + {"i": 3, "op": "rotl", "dst": 0, "src": 2, "src2": 1, "imm": "0xb59e83b2", "imm2": "0x19c26fb9", "rot": 19, "bit": 20, "mask": 4, "width": 1}, + {"i": 4, "op": "rotr", "dst": 7, "src": 6, "src2": 5, "imm": "0x6f055f55", "imm2": "0x550e4ea1", "rot": 31, "bit": 20, "mask": 4, "width": 1}, + {"i": 5, "op": "add", "dst": 7, "src": 4, "src2": 7, "imm": "0xee02465f", "imm2": "0xc1535555", "rot": 31, "bit": 21, "mask": 4, "width": 1}, + {"i": 6, "op": "mulhi", "dst": 1, "src": 7, "src2": 5, "imm": "0x7826a6a7", "imm2": "0x946f7818", "rot": 18, "bit": 21, "mask": 8, "width": 1}, + {"i": 7, "op": "load", "dst": 4, "src": 2, "src2": 0, "imm": "0x5d080878", "imm2": "0xdf885578", "rot": 5, "bit": 18, "mask": 4, "width": 1}, + {"i": 8, "op": "load", "dst": 7, "src": 4, "src2": 1, "imm": "0x875bbb36", "imm2": "0x594a838f", "rot": 24, "bit": 4, "mask": 2, "width": 1}, + {"i": 9, "op": "load", "dst": 0, "src": 3, "src2": 1, "imm": "0xe5e607c2", "imm2": "0xa5cd9f75", "rot": 26, "bit": 28, "mask": 4, "width": 1}, + {"i": 10, "op": "load", "dst": 5, "src": 1, "src2": 3, "imm": "0xea3f7b43", "imm2": "0x10c8d4e7", "rot": 5, "bit": 29, "mask": 8, "width": 1}, + {"i": 11, "op": "load", "dst": 1, "src": 5, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1}, + {"i": 12, "op": "mulhi", "dst": 3, "src": 5, "src2": 7, "imm": "0xbfd5615c", "imm2": "0xd8224ae2", "rot": 21, "bit": 12, "mask": 2, "width": 1}, + {"i": 13, "op": "load", "dst": 1, "src": 3, "src2": 5, "imm": "0x35c07cc5", "imm2": "0xe82db54f", "rot": 7, "bit": 6, "mask": 8, "width": 1}, + {"i": 14, "op": "sub", "dst": 0, "src": 3, "src2": 0, "imm": "0x587ee0f1", "imm2": "0xfd23eefd", "rot": 16, "bit": 21, "mask": 16, "width": 1}, + {"i": 15, "op": "mad", "dst": 5, "src": 1, "src2": 3, "imm": "0x6974dd29", "imm2": "0xc8148960", "rot": 3, "bit": 11, "mask": 2, "width": 1}, + {"i": 16, "op": "mulhi", "dst": 6, "src": 1, "src2": 7, "imm": "0x3072c3c6", "imm2": "0x55ee21f8", "rot": 26, "bit": 1, "mask": 1, "width": 1}, + {"i": 17, "op": "add", "dst": 5, "src": 2, "src2": 2, "imm": "0x697b3d00", "imm2": "0x8b965b57", "rot": 9, "bit": 28, "mask": 1, "width": 1}, + {"i": 18, "op": "mulhi", "dst": 0, "src": 6, "src2": 3, "imm": "0x2910cacb", "imm2": "0x6ac79431", "rot": 7, "bit": 6, "mask": 4, "width": 1}, + {"i": 19, "op": "rotr", "dst": 5, "src": 3, "src2": 0, "imm": "0xed96a94a", "imm2": "0x4c988c10", "rot": 24, "bit": 21, "mask": 8, "width": 1}, + {"i": 20, "op": "mulhi", "dst": 5, "src": 2, "src2": 7, "imm": "0x40d2fc76", "imm2": "0x2f7c7eca", "rot": 13, "bit": 8, "mask": 4, "width": 1}, + {"i": 21, "op": "add", "dst": 1, "src": 0, "src2": 5, "imm": "0xebcf247a", "imm2": "0x6d7e8d05", "rot": 31, "bit": 1, "mask": 16, "width": 1}, + {"i": 22, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1}, + {"i": 23, "op": "mulhi", "dst": 1, "src": 5, "src2": 7, "imm": "0xfdfe72fd", "imm2": "0x735eeb8d", "rot": 30, "bit": 25, "mask": 8, "width": 1}, + {"i": 24, "op": "sub", "dst": 2, "src": 5, "src2": 0, "imm": "0x32cf1258", "imm2": "0xd813deb6", "rot": 30, "bit": 6, "mask": 16, "width": 1}, + {"i": 25, "op": "add", "dst": 7, "src": 4, "src2": 2, "imm": "0x08ffa6c7", "imm2": "0x699ef1bb", "rot": 7, "bit": 2, "mask": 16, "width": 1}, + {"i": 26, "op": "shfl", "dst": 3, "src": 4, "src2": 5, "imm": "0x6f53c70d", "imm2": "0x3357513f", "rot": 3, "bit": 26, "mask": 2, "width": 1}, + {"i": 27, "op": "add", "dst": 7, "src": 1, "src2": 4, "imm": "0xe60fea84", "imm2": "0xb4ead2fb", "rot": 14, "bit": 14, "mask": 1, "width": 1}, + {"i": 28, "op": "add", "dst": 3, "src": 1, "src2": 0, "imm": "0x65c76dab", "imm2": "0x8f30d21d", "rot": 24, "bit": 6, "mask": 1, "width": 1}, + {"i": 29, "op": "load", "dst": 2, "src": 1, "src2": 2, "imm": "0x82fad9a6", "imm2": "0x8c6358db", "rot": 7, "bit": 31, "mask": 2, "width": 1}, + {"i": 30, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x6e947ee0", "imm2": "0xaf9a2dda", "rot": 2, "bit": 30, "mask": 4, "width": 1}, + {"i": 31, "op": "load", "dst": 2, "src": 5, "src2": 1, "imm": "0x608bb7ce", "imm2": "0x4be663db", "rot": 19, "bit": 1, "mask": 16, "width": 1}, + {"i": 32, "op": "shfl", "dst": 1, "src": 7, "src2": 6, "imm": "0x88e52e20", "imm2": "0x77647269", "rot": 20, "bit": 25, "mask": 4, "width": 1}, + {"i": 33, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1}, + {"i": 34, "op": "add", "dst": 4, "src": 2, "src2": 3, "imm": "0x0480debe", "imm2": "0xc7ce690c", "rot": 12, "bit": 21, "mask": 16, "width": 1}, + {"i": 35, "op": "shfl", "dst": 3, "src": 7, "src2": 5, "imm": "0xa73f59de", "imm2": "0x84b9e329", "rot": 21, "bit": 27, "mask": 8, "width": 1}, + {"i": 36, "op": "add", "dst": 7, "src": 1, "src2": 7, "imm": "0xc53b542e", "imm2": "0xe10c2c95", "rot": 21, "bit": 2, "mask": 4, "width": 1}, + {"i": 37, "op": "xor", "dst": 5, "src": 7, "src2": 4, "imm": "0x81cd7b0e", "imm2": "0x21a51823", "rot": 12, "bit": 10, "mask": 2, "width": 1}, + {"i": 38, "op": "or", "dst": 2, "src": 1, "src2": 3, "imm": "0x7894e657", "imm2": "0xf8e4b972", "rot": 18, "bit": 9, "mask": 4, "width": 1}, + {"i": 39, "op": "mulhi", "dst": 1, "src": 0, "src2": 4, "imm": "0xbee8421f", "imm2": "0x070888a8", "rot": 20, "bit": 28, "mask": 4, "width": 1}, + {"i": 40, "op": "rotl", "dst": 6, "src": 1, "src2": 4, "imm": "0x4609857a", "imm2": "0xaeecb156", "rot": 19, "bit": 21, "mask": 1, "width": 1}, + {"i": 41, "op": "mulhi", "dst": 4, "src": 6, "src2": 0, "imm": "0x1a84e1e9", "imm2": "0x9b26bb72", "rot": 28, "bit": 19, "mask": 1, "width": 1}, + {"i": 42, "op": "sub", "dst": 6, "src": 0, "src2": 6, "imm": "0xbf62908e", "imm2": "0xdf03ea88", "rot": 11, "bit": 27, "mask": 16, "width": 1}, + {"i": 43, "op": "shfl", "dst": 6, "src": 3, "src2": 4, "imm": "0xa966241c", "imm2": "0x9c639efa", "rot": 12, "bit": 4, "mask": 4, "width": 1}, + {"i": 44, "op": "load", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1}, + {"i": 45, "op": "xor", "dst": 1, "src": 3, "src2": 6, "imm": "0x006193d0", "imm2": "0xfc2acc3f", "rot": 25, "bit": 1, "mask": 1, "width": 1}, + {"i": 46, "op": "load", "dst": 7, "src": 0, "src2": 6, "imm": "0x4777c4f8", "imm2": "0x3cf0a02f", "rot": 11, "bit": 14, "mask": 4, "width": 1}, + {"i": 47, "op": "load", "dst": 3, "src": 1, "src2": 3, "imm": "0x10692532", "imm2": "0x1a292ea5", "rot": 20, "bit": 23, "mask": 1, "width": 1}, + {"i": 48, "op": "mul", "dst": 5, "src": 3, "src2": 7, "imm": "0xadf5bd13", "imm2": "0xb999de2e", "rot": 23, "bit": 10, "mask": 4, "width": 1}, + {"i": 49, "op": "sub", "dst": 1, "src": 5, "src2": 4, "imm": "0x68ff101e", "imm2": "0xbdaaf46a", "rot": 25, "bit": 23, "mask": 16, "width": 1}, + {"i": 50, "op": "rotl", "dst": 2, "src": 6, "src2": 4, "imm": "0x92d9a412", "imm2": "0x0daf96ea", "rot": 8, "bit": 4, "mask": 4, "width": 1}, + {"i": 51, "op": "add", "dst": 1, "src": 5, "src2": 0, "imm": "0xa900fec4", "imm2": "0x77b9bd43", "rot": 6, "bit": 23, "mask": 2, "width": 1}, + {"i": 52, "op": "load", "dst": 4, "src": 7, "src2": 1, "imm": "0x51392a72", "imm2": "0x99e8bb36", "rot": 11, "bit": 9, "mask": 1, "width": 1}, + {"i": 53, "op": "sub", "dst": 2, "src": 7, "src2": 2, "imm": "0x0ffe2ac7", "imm2": "0x030743df", "rot": 9, "bit": 30, "mask": 16, "width": 1}, + {"i": 54, "op": "xor", "dst": 4, "src": 0, "src2": 4, "imm": "0x9123ff15", "imm2": "0x10c329a7", "rot": 28, "bit": 15, "mask": 2, "width": 1}, + {"i": 55, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1}, + {"i": 56, "op": "load", "dst": 2, "src": 4, "src2": 3, "imm": "0x239de52c", "imm2": "0xf80bae18", "rot": 15, "bit": 24, "mask": 1, "width": 1}, + {"i": 57, "op": "mad", "dst": 0, "src": 1, "src2": 4, "imm": "0x31dede8e", "imm2": "0xd3f619e6", "rot": 29, "bit": 7, "mask": 2, "width": 1}, + {"i": 58, "op": "load", "dst": 3, "src": 5, "src2": 3, "imm": "0x206437d6", "imm2": "0x28d1c290", "rot": 17, "bit": 28, "mask": 4, "width": 1}, + {"i": 59, "op": "or", "dst": 5, "src": 6, "src2": 3, "imm": "0x8fffd674", "imm2": "0x0507903a", "rot": 26, "bit": 27, "mask": 2, "width": 1}, + {"i": 60, "op": "mad", "dst": 6, "src": 5, "src2": 7, "imm": "0xf572bdb9", "imm2": "0xeda2af31", "rot": 21, "bit": 8, "mask": 2, "width": 1}, + {"i": 61, "op": "rotl", "dst": 4, "src": 2, "src2": 7, "imm": "0x84f12ddf", "imm2": "0x81ef22e1", "rot": 28, "bit": 30, "mask": 1, "width": 1}, + {"i": 62, "op": "mulhi", "dst": 5, "src": 0, "src2": 6, "imm": "0xf6bb45ee", "imm2": "0x6bfb632d", "rot": 22, "bit": 0, "mask": 4, "width": 1}, + {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0x50ec702a", "imm2": "0xae6ee96e", "rot": 20, "bit": 25, "mask": 2, "width": 1} + ] +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.metal b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.metal new file mode 100644 index 00000000..b899adec --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r2 = r2 ^ simd_shuffle_xor(r0, (ushort)4); // 1 + r3 = r3 + r2 + select(0x2cccb6cau, 0x642e66dbu, ((sel >> 10u) & 1u) != 0u); // 2 + r0 = rotl_imm(r0, 19u); // 3 + r7 = rotr_var(r7, r6); // 4 + r7 = r7 + r4 + select(0xee02465fu, 0xc1535555u, ((sel >> 21u) & 1u) != 0u); // 5 + r1 = mulhi(r1, r7); // 6 + r4 = r4 ^ dataset[r2 & MASK]; // 7 + r7 = r7 ^ dataset[r4 & MASK]; // 8 + r0 = r0 ^ dataset[r3 & MASK]; // 9 + r5 = r5 ^ dataset[r1 & MASK]; // 10 + r1 = r1 ^ dataset[r5 & MASK]; // 11 + r3 = mulhi(r3, r5); // 12 + r1 = r1 ^ dataset[r3 & MASK]; // 13 + r0 = r0 - r3; // 14 + r5 = r1 * r3 + r5; // 15 + r6 = mulhi(r6, r1); // 16 + r5 = r5 + r2 + select(0x697b3d00u, 0x8b965b57u, ((sel >> 28u) & 1u) != 0u); // 17 + r0 = mulhi(r0, r6); // 18 + r5 = rotr_var(r5, r3); // 19 + r5 = mulhi(r5, r2); // 20 + r1 = r1 + r0 + select(0xebcf247au, 0x6d7e8d05u, ((sel >> 1u) & 1u) != 0u); // 21 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 22 + r1 = mulhi(r1, r5); // 23 + r2 = r2 - r5; // 24 + r7 = r7 + r4 + select(0x08ffa6c7u, 0x699ef1bbu, ((sel >> 2u) & 1u) != 0u); // 25 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 26 + r7 = r7 + r1 + select(0xe60fea84u, 0xb4ead2fbu, ((sel >> 14u) & 1u) != 0u); // 27 + r3 = r3 + r1 + select(0x65c76dabu, 0x8f30d21du, ((sel >> 6u) & 1u) != 0u); // 28 + r2 = r2 ^ dataset[r1 & MASK]; // 29 + r5 = r5 ^ dataset[r7 & MASK]; // 30 + r2 = r2 ^ dataset[r5 & MASK]; // 31 + r1 = r1 ^ simd_shuffle_xor(r7, (ushort)4); // 32 + r4 = r5 * r7 + r4; // 33 + r4 = r4 + r2 + select(0x0480debeu, 0xc7ce690cu, ((sel >> 21u) & 1u) != 0u); // 34 + r3 = r3 ^ simd_shuffle_xor(r7, (ushort)8); // 35 + r7 = r7 + r1 + select(0xc53b542eu, 0xe10c2c95u, ((sel >> 2u) & 1u) != 0u); // 36 + r5 = r5 ^ r7; // 37 + r2 = r2 | r1; // 38 + r1 = mulhi(r1, r0); // 39 + r6 = rotl_imm(r6, 19u); // 40 + r4 = mulhi(r4, r6); // 41 + r6 = r6 - r0; // 42 + r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r4 = r4 ^ dataset[r2 & MASK]; // 44 + r1 = r1 ^ r3; // 45 + r7 = r7 ^ dataset[r0 & MASK]; // 46 + r3 = r3 ^ dataset[r1 & MASK]; // 47 + r5 = r5 * r3; // 48 + r1 = r1 - r5; // 49 + r2 = rotl_imm(r2, 8u); // 50 + r1 = r1 + r5 + select(0xa900fec4u, 0x77b9bd43u, ((sel >> 23u) & 1u) != 0u); // 51 + r4 = r4 ^ dataset[r7 & MASK]; // 52 + r2 = r2 - r7; // 53 + r4 = r4 ^ r0; // 54 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 55 + r2 = r2 ^ dataset[r4 & MASK]; // 56 + r0 = r1 * r4 + r0; // 57 + r3 = r3 ^ dataset[r5 & MASK]; // 58 + r5 = r5 | r6; // 59 + r6 = r5 * r7 + r6; // 60 + r4 = rotl_imm(r4, 28u); // 61 + r5 = mulhi(r5, r0); // 62 + r3 = r3 ^ dataset[r6 & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program_bound.metal b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program_bound.metal new file mode 100644 index 00000000..2f021814 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program_bound.metal @@ -0,0 +1,111 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r2 = r2 ^ simd_shuffle_xor(r0, (ushort)4); // 1 + r3 = r3 + r2 + select(0x2cccb6cau, 0x642e66dbu, ((sel >> 10u) & 1u) != 0u); // 2 + r0 = rotl_imm(r0, 19u); // 3 + r7 = rotr_var(r7, r6); // 4 + r7 = r7 + r4 + select(0xee02465fu, 0xc1535555u, ((sel >> 21u) & 1u) != 0u); // 5 + r1 = mulhi(r1, r7); // 6 + r4 = r4 ^ dataset[r2 & MASK]; // 7 + r7 = r7 ^ dataset[r4 & MASK]; // 8 + r0 = r0 ^ dataset[r3 & MASK]; // 9 + r5 = r5 ^ dataset[r1 & MASK]; // 10 + r1 = r1 ^ dataset[r5 & MASK]; // 11 + r3 = mulhi(r3, r5); // 12 + r1 = r1 ^ dataset[r3 & MASK]; // 13 + r0 = r0 - r3; // 14 + r5 = r1 * r3 + r5; // 15 + r6 = mulhi(r6, r1); // 16 + r5 = r5 + r2 + select(0x697b3d00u, 0x8b965b57u, ((sel >> 28u) & 1u) != 0u); // 17 + r0 = mulhi(r0, r6); // 18 + r5 = rotr_var(r5, r3); // 19 + r5 = mulhi(r5, r2); // 20 + r1 = r1 + r0 + select(0xebcf247au, 0x6d7e8d05u, ((sel >> 1u) & 1u) != 0u); // 21 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 22 + r1 = mulhi(r1, r5); // 23 + r2 = r2 - r5; // 24 + r7 = r7 + r4 + select(0x08ffa6c7u, 0x699ef1bbu, ((sel >> 2u) & 1u) != 0u); // 25 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 26 + r7 = r7 + r1 + select(0xe60fea84u, 0xb4ead2fbu, ((sel >> 14u) & 1u) != 0u); // 27 + r3 = r3 + r1 + select(0x65c76dabu, 0x8f30d21du, ((sel >> 6u) & 1u) != 0u); // 28 + r2 = r2 ^ dataset[r1 & MASK]; // 29 + r5 = r5 ^ dataset[r7 & MASK]; // 30 + r2 = r2 ^ dataset[r5 & MASK]; // 31 + r1 = r1 ^ simd_shuffle_xor(r7, (ushort)4); // 32 + r4 = r5 * r7 + r4; // 33 + r4 = r4 + r2 + select(0x0480debeu, 0xc7ce690cu, ((sel >> 21u) & 1u) != 0u); // 34 + r3 = r3 ^ simd_shuffle_xor(r7, (ushort)8); // 35 + r7 = r7 + r1 + select(0xc53b542eu, 0xe10c2c95u, ((sel >> 2u) & 1u) != 0u); // 36 + r5 = r5 ^ r7; // 37 + r2 = r2 | r1; // 38 + r1 = mulhi(r1, r0); // 39 + r6 = rotl_imm(r6, 19u); // 40 + r4 = mulhi(r4, r6); // 41 + r6 = r6 - r0; // 42 + r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r4 = r4 ^ dataset[r2 & MASK]; // 44 + r1 = r1 ^ r3; // 45 + r7 = r7 ^ dataset[r0 & MASK]; // 46 + r3 = r3 ^ dataset[r1 & MASK]; // 47 + r5 = r5 * r3; // 48 + r1 = r1 - r5; // 49 + r2 = rotl_imm(r2, 8u); // 50 + r1 = r1 + r5 + select(0xa900fec4u, 0x77b9bd43u, ((sel >> 23u) & 1u) != 0u); // 51 + r4 = r4 ^ dataset[r7 & MASK]; // 52 + r2 = r2 - r7; // 53 + r4 = r4 ^ r0; // 54 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 55 + r2 = r2 ^ dataset[r4 & MASK]; // 56 + r0 = r1 * r4 + r0; // 57 + r3 = r3 ^ dataset[r5 & MASK]; // 58 + r5 = r5 | r6; // 59 + r6 = r5 * r7 + r6; // 60 + r4 = rotl_imm(r4, 28u); // 61 + r5 = mulhi(r5, r0); // 62 + r3 = r3 ^ dataset[r6 & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.h b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.h new file mode 100644 index 00000000..106bdc34 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.h @@ -0,0 +1,57 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0x212c6442b51e87aeull, 0xc374795c00839331ull, 0xb6036a220a98f4b3ull, 0xeb8b8013e637367bull, 0xdb5866e9b73930fdull, 0xf3f3d01f46e90333ull, 0x9d913991ab8ed428ull, 0x7ccb1d8fa100a800ull, + 0x3cf45ba44f09a0feull, 0x91acf48ef1a63082ull, 0x6ea46c69fb082f99ull, 0x581f0218977a9d72ull, 0x9a4623a5c62ddf2dull, 0xab6eb5e768f0feb4ull, 0x07b70bdccf8aca12ull, 0xd666311ae5e4311eull, + 0x53114757d669f0a4ull, 0xbd5d6ace87ce2ce4ull, 0xfb712015e8189192ull, 0xa32cec81103e134bull, 0x83f3d18c3289c124ull, 0xfe29f1984b132b3dull, 0xc9ffcf4e3774497aull, 0xac99c9243dc63809ull, + 0xd78a7e8217a32f3cull, 0xab81ad63d242fc31ull, 0x0e4c30b7e00024afull, 0xce014289fff6778dull, 0x64a1292e2a8b4a91ull, 0xd5b8c90e681d7e3aull, 0x06078117673030fdull, 0x51bf77b280173930ull + }, + { // base nonce 4096 + 0x3c797978566b5950ull, 0x7c759e60185b6411ull, 0x199136b84028d653ull, 0x0ad7b60cb722da50ull, 0x93e5fd443b85acc5ull, 0x4a0fadca2f0b7dc6ull, 0x91b5f2478c1fbd4full, 0xe3debc1f1cdc889aull, + 0x9c39b1635a6aec09ull, 0xbeaea0ab408df45aull, 0x080283b6f52a494aull, 0x9acae9a878c16f2eull, 0xc37dc4e098fee807ull, 0xf3daf9ff09a2c0e5ull, 0xfff82c3572c8f362ull, 0xd96414dfa0b4bd51ull, + 0xecf77498c26cc5abull, 0x72945c7a2e52a90cull, 0xe5b6749102690fa4ull, 0x90eddb84d13a2b9full, 0x6e9186a56dc002c1ull, 0xfa2047ec0ae61fe3ull, 0xa8cd4efd68b7eff5ull, 0xd23b650a6345bf73ull, + 0xe1141a33ea09bae1ull, 0x0e9aae812f5d762aull, 0x9ae3811dcc68797full, 0x0a4dab24c1efff6full, 0xa22d476bc42b9064ull, 0xc7a16d6f59414a2eull, 0x5aff92ecae652329ull, 0x96a903eb9a0ca390ull + }, + { // base nonce 1000000 + 0xd5a8da0568df8ee7ull, 0x68cb69c04208285aull, 0xe3e0c57190a809dbull, 0x87ba27d76aa356d5ull, 0x5a30616d4b8bfcf2ull, 0x66f5d2c31997fd24ull, 0x0447779e26a2854aull, 0x1225eb67933b2faeull, + 0xde78d94be53c669bull, 0x02a3fe00eb77c8cdull, 0x4fd029ba4cfc526aull, 0x2ef8ea603a0463d6ull, 0x345cff9b465b6f12ull, 0x03bb80d5a700b7feull, 0xf7da33ca2094ae26ull, 0xb3adaaa8383ad2e9ull, + 0x68d5eafde2924d62ull, 0xeab53a1b039835e0ull, 0xfd04e678835cb646ull, 0xf25b86a707f333c0ull, 0xa3bb24e6981917deull, 0x791d38b29dd6055aull, 0xfd18f01f064bdbcaull, 0xee051af1de1b70f9ull, + 0x246bfe79acd966eaull, 0xbcb8105b8a73efedull, 0x9b7e8c6fcc25fa92ull, 0xa0ffe2d9e4717892ull, 0x22f52e2324035114ull, 0xcdac38103479920dull, 0x49477ffa08ac4a6cull, 0xf8ca84a1a5d78cf5ull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0xafe80d67u, 0xb9fbd029u, 0x6c79f193u, 0x95139ad9u, 0x96310affu, 0x4609f8b1u, 0x75279e63u, 0x28235be1u, + 0x47b17dcbu, 0x718e0ef2u, 0xa52588c8u, 0xa8bf49d5u, 0x19cf243eu, 0x5ec8905eu, 0xa4851f66u, 0xaf9cd9f3u +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xe6a99c7au; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0x57642b58u, 0xc279baddu, 0xcbccbaadu, 0x32cce392u, 0x71fdb4c6u, 0xf5a268ceu, 0x3ca1d676u, 0x7977b03du, 0xb73a6cb1u, 0xe773c5c9u, 0x2533a3f1u, 0xd7c48638u, 0xfc80830au, 0xab3874e9u, 0xc093812eu, 0xb71e49d6u, 0x0e151e7bu, 0x395af161u, 0x7156ef4eu, 0x9202463fu, 0x562e88c8u, 0xb9b0c5b3u, 0xe3f6102fu, 0xe3a94cbbu, 0x9e26c494u, 0x47ace326u, 0xe20ea113u, 0xce4eefa1u, 0x97a41cc9u, 0xa6d51cb5u, 0x71f3df27u, 0x115df551u, 0x20e273b8u, 0x50bf99ffu, 0xc9b1ae13u, 0x342160c6u, 0xc1b8eb36u, 0x9b11737bu, 0x4b73d48fu, 0x455e9b9eu, 0xe5213a1cu, 0xf7049bc3u, 0x0a026f93u, 0x156a8a99u, 0x1029fe6cu, 0x4f29d205u, 0x273315edu, 0x483e85a2u, 0xec802349u, 0x76023cccu, 0xe37e8135u, 0x0881d277u, 0x56ba9c69u, 0x31ad3b78u, 0xc8ad9363u, 0x8647e3c0u, 0x12e78b02u, 0x2d3a1b5fu, 0xa75e9390u, 0xbe0cebd8u, 0x759af86du, 0x9b4bd567u, 0xd272d0aeu, 0x7a18d82eu +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0xebd9055cu, 0x7eaf6b21u, 0x9b610855u, 0x134a8562u, 0xb10059bau, 0x7f459e58u, 0x8f3c7873u, 0x3523e609u, + 0x75b8f4cau, 0x750d31b4u, 0x0dae0782u, 0x26f10015u, 0x7ba87a41u, 0x0efd8543u, 0x691d6368u, 0x8929d967u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x51474edfu, 0xc3cfba93u, 0xf21454cfu, 0x9b79baacu, 0x4d4fce23u, 0xd701dfd5u, 0x37357ab3u, 0x1be693fau, + 0xa701cb3bu, 0x7467c620u, 0x428184e8u, 0xf4010df0u, 0xe33aa88fu, 0xc78d6d62u, 0xae9ba9c0u, 0xf4bba97eu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x448274a57f508cbcull; diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.json b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.json new file mode 100644 index 00000000..5060023f --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.json @@ -0,0 +1,36 @@ +{ + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0x212c6442b51e87ae", "0xc374795c00839331", "0xb6036a220a98f4b3", "0xeb8b8013e637367b", "0xdb5866e9b73930fd", "0xf3f3d01f46e90333", "0x9d913991ab8ed428", "0x7ccb1d8fa100a800", + "0x3cf45ba44f09a0fe", "0x91acf48ef1a63082", "0x6ea46c69fb082f99", "0x581f0218977a9d72", "0x9a4623a5c62ddf2d", "0xab6eb5e768f0feb4", "0x07b70bdccf8aca12", "0xd666311ae5e4311e", + "0x53114757d669f0a4", "0xbd5d6ace87ce2ce4", "0xfb712015e8189192", "0xa32cec81103e134b", "0x83f3d18c3289c124", "0xfe29f1984b132b3d", "0xc9ffcf4e3774497a", "0xac99c9243dc63809", + "0xd78a7e8217a32f3c", "0xab81ad63d242fc31", "0x0e4c30b7e00024af", "0xce014289fff6778d", "0x64a1292e2a8b4a91", "0xd5b8c90e681d7e3a", "0x06078117673030fd", "0x51bf77b280173930" + ]}, + {"base_nonce": 4096, "expected": [ + "0x3c797978566b5950", "0x7c759e60185b6411", "0x199136b84028d653", "0x0ad7b60cb722da50", "0x93e5fd443b85acc5", "0x4a0fadca2f0b7dc6", "0x91b5f2478c1fbd4f", "0xe3debc1f1cdc889a", + "0x9c39b1635a6aec09", "0xbeaea0ab408df45a", "0x080283b6f52a494a", "0x9acae9a878c16f2e", "0xc37dc4e098fee807", "0xf3daf9ff09a2c0e5", "0xfff82c3572c8f362", "0xd96414dfa0b4bd51", + "0xecf77498c26cc5ab", "0x72945c7a2e52a90c", "0xe5b6749102690fa4", "0x90eddb84d13a2b9f", "0x6e9186a56dc002c1", "0xfa2047ec0ae61fe3", "0xa8cd4efd68b7eff5", "0xd23b650a6345bf73", + "0xe1141a33ea09bae1", "0x0e9aae812f5d762a", "0x9ae3811dcc68797f", "0x0a4dab24c1efff6f", "0xa22d476bc42b9064", "0xc7a16d6f59414a2e", "0x5aff92ecae652329", "0x96a903eb9a0ca390" + ]}, + {"base_nonce": 1000000, "expected": [ + "0xd5a8da0568df8ee7", "0x68cb69c04208285a", "0xe3e0c57190a809db", "0x87ba27d76aa356d5", "0x5a30616d4b8bfcf2", "0x66f5d2c31997fd24", "0x0447779e26a2854a", "0x1225eb67933b2fae", + "0xde78d94be53c669b", "0x02a3fe00eb77c8cd", "0x4fd029ba4cfc526a", "0x2ef8ea603a0463d6", "0x345cff9b465b6f12", "0x03bb80d5a700b7fe", "0xf7da33ca2094ae26", "0xb3adaaa8383ad2e9", + "0x68d5eafde2924d62", "0xeab53a1b039835e0", "0xfd04e678835cb646", "0xf25b86a707f333c0", "0xa3bb24e6981917de", "0x791d38b29dd6055a", "0xfd18f01f064bdbca", "0xee051af1de1b70f9", + "0x246bfe79acd966ea", "0xbcb8105b8a73efed", "0x9b7e8c6fcc25fa92", "0xa0ffe2d9e4717892", "0x22f52e2324035114", "0xcdac38103479920d", "0x49477ffa08ac4a6c", "0xf8ca84a1a5d78cf5" + ]} + ], + "dataset_head": ["0xafe80d67", "0xb9fbd029", "0x6c79f193", "0x95139ad9", "0x96310aff", "0x4609f8b1", "0x75279e63", "0x28235be1", "0x47b17dcb", "0x718e0ef2", "0xa52588c8", "0xa8bf49d5", "0x19cf243e", "0x5ec8905e", "0xa4851f66", "0xaf9cd9f3"], + "dataset_last_index": 268435455, + "dataset_last": "0xe6a99c7a", + "dataset_samples": [{"index": 59471966, "value": "0x57642b58"}, {"index": 217795994, "value": "0xc279badd"}, {"index": 208353206, "value": "0xcbccbaad"}, {"index": 42483309, "value": "0x32cce392"}, {"index": 172547758, "value": "0x71fdb4c6"}, {"index": 148076330, "value": "0xf5a268ce"}, {"index": 183853158, "value": "0x3ca1d676"}, {"index": 214389424, "value": "0x7977b03d"}, {"index": 267488061, "value": "0xb73a6cb1"}, {"index": 169781097, "value": "0xe773c5c9"}, {"index": 184093494, "value": "0x2533a3f1"}, {"index": 153880993, "value": "0xd7c48638"}, {"index": 84977930, "value": "0xfc80830a"}, {"index": 46426879, "value": "0xab3874e9"}, {"index": 3093825, "value": "0xc093812e"}, {"index": 225364072, "value": "0xb71e49d6"}, {"index": 44593546, "value": "0x0e151e7b"}, {"index": 260713159, "value": "0x395af161"}, {"index": 168250303, "value": "0x7156ef4e"}, {"index": 52384140, "value": "0x9202463f"}, {"index": 223401610, "value": "0x562e88c8"}, {"index": 45554030, "value": "0xb9b0c5b3"}, {"index": 95410555, "value": "0xe3f6102f"}, {"index": 175039924, "value": "0xe3a94cbb"}, {"index": 79171087, "value": "0x9e26c494"}, {"index": 267580473, "value": "0x47ace326"}, {"index": 24168642, "value": "0xe20ea113"}, {"index": 37981670, "value": "0xce4eefa1"}, {"index": 171551130, "value": "0x97a41cc9"}, {"index": 195559979, "value": "0xa6d51cb5"}, {"index": 204611762, "value": "0x71f3df27"}, {"index": 140997658, "value": "0x115df551"}, {"index": 138925853, "value": "0x20e273b8"}, {"index": 86637313, "value": "0x50bf99ff"}, {"index": 20736778, "value": "0xc9b1ae13"}, {"index": 219665210, "value": "0x342160c6"}, {"index": 160430336, "value": "0xc1b8eb36"}, {"index": 264654675, "value": "0x9b11737b"}, {"index": 8013395, "value": "0x4b73d48f"}, {"index": 228945585, "value": "0x455e9b9e"}, {"index": 213884386, "value": "0xe5213a1c"}, {"index": 104419827, "value": "0xf7049bc3"}, {"index": 44185464, "value": "0x0a026f93"}, {"index": 142737231, "value": "0x156a8a99"}, {"index": 99284897, "value": "0x1029fe6c"}, {"index": 132475900, "value": "0x4f29d205"}, {"index": 61861762, "value": "0x273315ed"}, {"index": 132056166, "value": "0x483e85a2"}, {"index": 262388043, "value": "0xec802349"}, {"index": 91878046, "value": "0x76023ccc"}, {"index": 117353561, "value": "0xe37e8135"}, {"index": 124768597, "value": "0x0881d277"}, {"index": 71352993, "value": "0x56ba9c69"}, {"index": 190698941, "value": "0x31ad3b78"}, {"index": 46055428, "value": "0xc8ad9363"}, {"index": 55281366, "value": "0x8647e3c0"}, {"index": 165145231, "value": "0x12e78b02"}, {"index": 106810753, "value": "0x2d3a1b5f"}, {"index": 171985651, "value": "0xa75e9390"}, {"index": 232085256, "value": "0xbe0cebd8"}, {"index": 159510492, "value": "0x759af86d"}, {"index": 40072060, "value": "0x9b4bd567"}, {"index": 209107596, "value": "0xd272d0ae"}, {"index": 39023794, "value": "0x7a18d82e"}], + "cache_head": ["0xebd9055c", "0x7eaf6b21", "0x9b610855", "0x134a8562", "0xb10059ba", "0x7f459e58", "0x8f3c7873", "0x3523e609", "0x75b8f4ca", "0x750d31b4", "0x0dae0782", "0x26f10015", "0x7ba87a41", "0x0efd8543", "0x691d6368", "0x8929d967"], + "cache_last_line": ["0x51474edf", "0xc3cfba93", "0xf21454cf", "0x9b79baac", "0x4d4fce23", "0xd701dfd5", "0x37357ab3", "0x1be693fa", "0xa701cb3b", "0x7467c620", "0x428184e8", "0xf4010df0", "0xe33aa88f", "0xc78d6d62", "0xae9ba9c0", "0xf4bba97e"], + "cache_fnv1a64": "0x448274a57f508cbc" +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/kernel.cl b/proto-cuda/packs-ca2-mixer/mx4-genesis/kernel.cl new file mode 100644 index 00000000..1a121995 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/kernel.cl @@ -0,0 +1,279 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 4 x seed-parameterised mixer + one 64-byte cache read, then 4 x final mixer (class v3, mixer multiplier 4, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 4 x mixer + cache line s[0] & mask; 4 x final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 4u + j + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ ds[r0 & mask]; // 16 load + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ ds[r1 & mask]; // 34 load + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/kernel.cu b/proto-cuda/packs-ca2-mixer/mx4-genesis/kernel.cu new file mode 100644 index 00000000..e2247231 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/kernel.cu @@ -0,0 +1,164 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ ds[r0 & mask]; // 16 load + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ ds[r1 & mask]; // 34 load + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/kernel_bound.cl b/proto-cuda/packs-ca2-mixer/mx4-genesis/kernel_bound.cl new file mode 100644 index 00000000..68a98d70 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/kernel_bound.cl @@ -0,0 +1,373 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 4 x seed-parameterised mixer + one 64-byte cache read, then 4 x final mixer (class v3, mixer multiplier 4, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 4 x mixer + cache line s[0] & mask; 4 x final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 4u + j + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ ds[r0 & mask]; // 16 load + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ ds[r1 & mask]; // 34 load + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ ds[r0 & mask]; // 16 load + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ ds[r1 & mask]; // 34 load + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/kernel_bound.cu b/proto-cuda/packs-ca2-mixer/mx4-genesis/kernel_bound.cu new file mode 100644 index 00000000..e8f1a9a7 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/kernel_bound.cu @@ -0,0 +1,123 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ ds[r0 & mask]; // 16 load + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ ds[r1 & mask]; // 34 load + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/memhard.h b/proto-cuda/packs-ca2-mixer/mx4-genesis/memhard.h new file mode 100644 index 00000000..4fbf592f --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/memhard.h @@ -0,0 +1,109 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 4 x seed-parameterised mixer + one 64-byte cache read, then 4 x final mixer (class v3, mixer multiplier 4, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 4 x mixer + cache line s[0] & mask; 4 x final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint32_t r = 0u; r < 8u; ++r) { + for (uint32_t j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 4u + j + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint32_t j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/memhard.metal b/proto-cuda/packs-ca2-mixer/mx4-genesis/memhard.metal new file mode 100644 index 00000000..32324e18 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/memhard.metal @@ -0,0 +1,107 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 4 x seed-parameterised mixer + one 64-byte cache read, then 4 x final mixer (class v3, mixer multiplier 4, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 4 x mixer + cache line s[0] & mask; 4 x final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 4u + j + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/program.h b/proto-cuda/packs-ca2-mixer/mx4-genesis/program.h new file mode 100644 index 00000000..f70b83cc --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/program.h @@ -0,0 +1,66 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-genesis" +#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973" +#define IGNEUM_GENERATOR 3 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0xe323b9dcaf283a6full +#define IGNEUM_DAY_STRING "2026-10-03" +#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033" +#define IGNEUM_DAY0 0x3067619fu +#define IGNEUM_DAY1 0x3c269176u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 add=8 shfl=8 xor=6 mad=5 mul=5 mulhi=5 sub=4 rotl=3 rotr=3 or=1" +// Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that +// runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). +#define IGNEUM_PROGRAM_CLASS "v3" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "mx4" +#define IGNEUM_CLASS_MIXER_MULT 4 +#define IGNEUM_CACHE_GROWTH 1 // 1: cache words = 2^(26 + doublings(day)), doublings = floor(log2(1 + day / 1460)) +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u } +#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIXER_MULT 4 // mixer applications per round and after the last read (class v3, docs/plans/mixer-x4.md) +#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u } +#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u } +#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/program.json b/proto-cuda/packs-ca2-mixer/mx4-genesis/program.json new file mode 100644 index 00000000..0971d8d7 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/program.json @@ -0,0 +1,132 @@ +{ + "format": "igneum-program-pack-3", + "generator": 3, + "attempt": 0, + "program_id": "0xe323b9dcaf283a6f", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-genesis", + "seed_bytes": "69676e65756d2d67656e65736973", + "seed_words": ["0x67a9a7be", "0x1a155b25", "0xfddfb732", "0x4b5af2e8", "0xc55caf33", "0xa27c13b7", "0x06628a48", "0x03852469"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "program_class": "v3", + "load_class": "mx4", + "mixer_mult": 4, + "cache_growth": true, + "mixer": "class v3 (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): every mixer application of the item derivation is 4 applications with round keys (r * 4 + j + 1) * 0x9E3779B9, the 8 dependent cache reads per item unchanged; cache growth rule option C: cache words = 2^(26 + doublings(day)), dataset words = 2^(genesis_log2 + doublings(day)), doublings(day) = floor(log2(1 + day / 1460)) for day = days since genesis", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "op_mix": {"load": 16, "add": 8, "shfl": 8, "xor": 6, "mad": 5, "mul": 5, "mulhi": 5, "sub": 4, "rotl": 3, "rotr": 3, "or": 1}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "2026-10-03", + "day_bytes": "6461792f323032362d31302d3033", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0x3067619f", + "d1": "0x3c269176", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "mixer_mult": 4, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: for j in 0..3: s = M(s, rk = (r * 4 + j + 1) * 0x9E3779B9); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then for j in 0..3: s = M(s, rk = (32 + j + 1) * 0x9E3779B9); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "mad", "dst": 2, "src": 3, "src2": 4, "imm": "0xbf7b174d", "imm2": "0x337b762e", "rot": 17, "bit": 2, "mask": 2, "width": 1}, + {"i": 1, "op": "mad", "dst": 2, "src": 1, "src2": 1, "imm": "0xdd04a5da", "imm2": "0x42da7657", "rot": 15, "bit": 30, "mask": 16, "width": 1}, + {"i": 2, "op": "mad", "dst": 2, "src": 3, "src2": 2, "imm": "0x734003fa", "imm2": "0x5bb67700", "rot": 3, "bit": 20, "mask": 1, "width": 1}, + {"i": 3, "op": "xor", "dst": 3, "src": 5, "src2": 5, "imm": "0xc55a1b1c", "imm2": "0xa19720f3", "rot": 7, "bit": 8, "mask": 1, "width": 1}, + {"i": 4, "op": "load", "dst": 7, "src": 2, "src2": 5, "imm": "0xad572dd7", "imm2": "0x9ceb3ea7", "rot": 18, "bit": 30, "mask": 2, "width": 1}, + {"i": 5, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x769a53be", "imm2": "0x80f9067e", "rot": 12, "bit": 22, "mask": 1, "width": 1}, + {"i": 6, "op": "shfl", "dst": 1, "src": 4, "src2": 0, "imm": "0xd3613d88", "imm2": "0x262fb219", "rot": 10, "bit": 30, "mask": 8, "width": 1}, + {"i": 7, "op": "shfl", "dst": 7, "src": 3, "src2": 4, "imm": "0xce38e42f", "imm2": "0xb868b818", "rot": 11, "bit": 8, "mask": 8, "width": 1}, + {"i": 8, "op": "mulhi", "dst": 1, "src": 5, "src2": 2, "imm": "0xa5eebca5", "imm2": "0x5703a72b", "rot": 13, "bit": 13, "mask": 16, "width": 1}, + {"i": 9, "op": "rotr", "dst": 6, "src": 3, "src2": 4, "imm": "0x17a5a9c7", "imm2": "0xdcfb93a1", "rot": 20, "bit": 27, "mask": 2, "width": 1}, + {"i": 10, "op": "or", "dst": 3, "src": 4, "src2": 1, "imm": "0xccb7d785", "imm2": "0xc335364c", "rot": 14, "bit": 12, "mask": 4, "width": 1}, + {"i": 11, "op": "load", "dst": 4, "src": 3, "src2": 2, "imm": "0x88cb9af3", "imm2": "0x4e7dc10d", "rot": 24, "bit": 17, "mask": 4, "width": 1}, + {"i": 12, "op": "mulhi", "dst": 0, "src": 4, "src2": 4, "imm": "0x45374321", "imm2": "0x3cd91989", "rot": 11, "bit": 4, "mask": 2, "width": 1}, + {"i": 13, "op": "add", "dst": 5, "src": 1, "src2": 2, "imm": "0xc7934706", "imm2": "0xd3177981", "rot": 16, "bit": 30, "mask": 2, "width": 1}, + {"i": 14, "op": "load", "dst": 0, "src": 4, "src2": 3, "imm": "0xfd7f56bb", "imm2": "0x65e14f52", "rot": 13, "bit": 22, "mask": 2, "width": 1}, + {"i": 15, "op": "sub", "dst": 2, "src": 4, "src2": 4, "imm": "0x35a80b49", "imm2": "0x060f2d13", "rot": 16, "bit": 20, "mask": 16, "width": 1}, + {"i": 16, "op": "load", "dst": 2, "src": 0, "src2": 2, "imm": "0xae0a32c2", "imm2": "0x4c2a4cfe", "rot": 8, "bit": 31, "mask": 16, "width": 1}, + {"i": 17, "op": "load", "dst": 7, "src": 2, "src2": 6, "imm": "0x82a84cc3", "imm2": "0x21a38d68", "rot": 15, "bit": 21, "mask": 2, "width": 1}, + {"i": 18, "op": "shfl", "dst": 7, "src": 3, "src2": 3, "imm": "0xa3818806", "imm2": "0x8f66b5c8", "rot": 14, "bit": 6, "mask": 4, "width": 1}, + {"i": 19, "op": "mul", "dst": 5, "src": 0, "src2": 1, "imm": "0xa00de107", "imm2": "0x77bfcaa5", "rot": 3, "bit": 10, "mask": 2, "width": 1}, + {"i": 20, "op": "shfl", "dst": 3, "src": 4, "src2": 7, "imm": "0x1d2b8cab", "imm2": "0x80b4f9a2", "rot": 14, "bit": 25, "mask": 2, "width": 1}, + {"i": 21, "op": "shfl", "dst": 2, "src": 4, "src2": 5, "imm": "0x3ac915d2", "imm2": "0x5fba7bc2", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 22, "op": "mulhi", "dst": 6, "src": 2, "src2": 0, "imm": "0xdc3ec8fd", "imm2": "0x599e2fa3", "rot": 22, "bit": 3, "mask": 2, "width": 1}, + {"i": 23, "op": "load", "dst": 6, "src": 1, "src2": 5, "imm": "0x2a6b16d5", "imm2": "0xd73e396f", "rot": 28, "bit": 29, "mask": 2, "width": 1}, + {"i": 24, "op": "mul", "dst": 5, "src": 0, "src2": 7, "imm": "0x376d0223", "imm2": "0xe1c2169a", "rot": 4, "bit": 16, "mask": 16, "width": 1}, + {"i": 25, "op": "rotl", "dst": 5, "src": 7, "src2": 3, "imm": "0x78ad8c60", "imm2": "0x6f5b77d5", "rot": 19, "bit": 11, "mask": 16, "width": 1}, + {"i": 26, "op": "shfl", "dst": 7, "src": 6, "src2": 5, "imm": "0x93915b9f", "imm2": "0x1e61fb6b", "rot": 28, "bit": 23, "mask": 2, "width": 1}, + {"i": 27, "op": "xor", "dst": 0, "src": 5, "src2": 7, "imm": "0x6378fe15", "imm2": "0x66c78f42", "rot": 12, "bit": 31, "mask": 8, "width": 1}, + {"i": 28, "op": "xor", "dst": 0, "src": 4, "src2": 7, "imm": "0x20a57fda", "imm2": "0x088c848e", "rot": 16, "bit": 13, "mask": 4, "width": 1}, + {"i": 29, "op": "sub", "dst": 3, "src": 0, "src2": 2, "imm": "0x49d95fd5", "imm2": "0x1a5f946a", "rot": 6, "bit": 12, "mask": 1, "width": 1}, + {"i": 30, "op": "mul", "dst": 5, "src": 1, "src2": 7, "imm": "0x0a816217", "imm2": "0x405c4f73", "rot": 13, "bit": 27, "mask": 4, "width": 1}, + {"i": 31, "op": "load", "dst": 7, "src": 2, "src2": 2, "imm": "0x09ed045e", "imm2": "0xd69c4715", "rot": 5, "bit": 9, "mask": 2, "width": 1}, + {"i": 32, "op": "load", "dst": 1, "src": 0, "src2": 6, "imm": "0xeb79ea49", "imm2": "0xcc587f5a", "rot": 6, "bit": 8, "mask": 16, "width": 1}, + {"i": 33, "op": "xor", "dst": 5, "src": 6, "src2": 1, "imm": "0x3027401e", "imm2": "0x5f20c27e", "rot": 18, "bit": 9, "mask": 2, "width": 1}, + {"i": 34, "op": "load", "dst": 5, "src": 1, "src2": 3, "imm": "0x0e1cab07", "imm2": "0x09356c5b", "rot": 19, "bit": 31, "mask": 1, "width": 1}, + {"i": 35, "op": "mulhi", "dst": 0, "src": 5, "src2": 2, "imm": "0x90e31357", "imm2": "0xabd32484", "rot": 26, "bit": 5, "mask": 8, "width": 1}, + {"i": 36, "op": "shfl", "dst": 5, "src": 2, "src2": 5, "imm": "0xee9a955f", "imm2": "0x31b3faed", "rot": 8, "bit": 24, "mask": 4, "width": 1}, + {"i": 37, "op": "load", "dst": 7, "src": 0, "src2": 7, "imm": "0x3ba2f832", "imm2": "0x1160dcd3", "rot": 4, "bit": 29, "mask": 1, "width": 1}, + {"i": 38, "op": "add", "dst": 3, "src": 1, "src2": 7, "imm": "0x75ba2fad", "imm2": "0x230c005c", "rot": 4, "bit": 27, "mask": 1, "width": 1}, + {"i": 39, "op": "shfl", "dst": 1, "src": 5, "src2": 3, "imm": "0xdbf37e75", "imm2": "0xb5ac1969", "rot": 30, "bit": 13, "mask": 4, "width": 1}, + {"i": 40, "op": "xor", "dst": 2, "src": 5, "src2": 5, "imm": "0x47f136c5", "imm2": "0x06ce9153", "rot": 19, "bit": 10, "mask": 2, "width": 1}, + {"i": 41, "op": "mad", "dst": 3, "src": 6, "src2": 3, "imm": "0xce13eff8", "imm2": "0x04cc1d55", "rot": 3, "bit": 1, "mask": 4, "width": 1}, + {"i": 42, "op": "sub", "dst": 6, "src": 7, "src2": 1, "imm": "0x6a65ab71", "imm2": "0x8fbc1bcd", "rot": 4, "bit": 1, "mask": 8, "width": 1}, + {"i": 43, "op": "xor", "dst": 7, "src": 0, "src2": 7, "imm": "0xdaeb4928", "imm2": "0xc0423027", "rot": 24, "bit": 11, "mask": 8, "width": 1}, + {"i": 44, "op": "load", "dst": 1, "src": 7, "src2": 7, "imm": "0x778f01c9", "imm2": "0x28cedcea", "rot": 12, "bit": 4, "mask": 16, "width": 1}, + {"i": 45, "op": "mul", "dst": 2, "src": 3, "src2": 2, "imm": "0xf4264f1b", "imm2": "0x0f627d56", "rot": 5, "bit": 28, "mask": 8, "width": 1}, + {"i": 46, "op": "mulhi", "dst": 1, "src": 5, "src2": 1, "imm": "0xffb2147a", "imm2": "0xccde9b05", "rot": 13, "bit": 9, "mask": 2, "width": 1}, + {"i": 47, "op": "sub", "dst": 4, "src": 3, "src2": 5, "imm": "0x73b36234", "imm2": "0x3f5d5997", "rot": 7, "bit": 18, "mask": 2, "width": 1}, + {"i": 48, "op": "rotr", "dst": 2, "src": 6, "src2": 3, "imm": "0x3a4d9aa9", "imm2": "0x212bec7b", "rot": 4, "bit": 29, "mask": 16, "width": 1}, + {"i": 49, "op": "load", "dst": 3, "src": 5, "src2": 2, "imm": "0x626f11df", "imm2": "0x56cd5bfd", "rot": 7, "bit": 1, "mask": 1, "width": 1}, + {"i": 50, "op": "add", "dst": 1, "src": 5, "src2": 6, "imm": "0x81b8bc2c", "imm2": "0x1907970c", "rot": 28, "bit": 7, "mask": 4, "width": 1}, + {"i": 51, "op": "mul", "dst": 0, "src": 2, "src2": 2, "imm": "0xa8848b30", "imm2": "0xef6ac348", "rot": 9, "bit": 15, "mask": 8, "width": 1}, + {"i": 52, "op": "add", "dst": 0, "src": 2, "src2": 0, "imm": "0x4f92b968", "imm2": "0x699fd448", "rot": 22, "bit": 6, "mask": 4, "width": 1}, + {"i": 53, "op": "add", "dst": 1, "src": 0, "src2": 2, "imm": "0x2bb965af", "imm2": "0x77b1520d", "rot": 2, "bit": 12, "mask": 8, "width": 1}, + {"i": 54, "op": "rotl", "dst": 7, "src": 1, "src2": 0, "imm": "0x553e678b", "imm2": "0x3cc8eae0", "rot": 14, "bit": 20, "mask": 2, "width": 1}, + {"i": 55, "op": "add", "dst": 3, "src": 7, "src2": 2, "imm": "0x7b0fe07a", "imm2": "0xa54c55a0", "rot": 10, "bit": 1, "mask": 1, "width": 1}, + {"i": 56, "op": "load", "dst": 6, "src": 7, "src2": 4, "imm": "0x01eba9aa", "imm2": "0x2758c0f7", "rot": 14, "bit": 15, "mask": 4, "width": 1}, + {"i": 57, "op": "rotr", "dst": 1, "src": 5, "src2": 2, "imm": "0x1f5267b3", "imm2": "0x236f5a27", "rot": 2, "bit": 31, "mask": 16, "width": 1}, + {"i": 58, "op": "load", "dst": 5, "src": 4, "src2": 3, "imm": "0xa9954a9b", "imm2": "0x6a54d4e8", "rot": 11, "bit": 10, "mask": 16, "width": 1}, + {"i": 59, "op": "load", "dst": 6, "src": 2, "src2": 4, "imm": "0x9923ff88", "imm2": "0x9357254e", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 60, "op": "mad", "dst": 3, "src": 5, "src2": 0, "imm": "0xc0cc51a6", "imm2": "0x3fd7701b", "rot": 20, "bit": 1, "mask": 4, "width": 1}, + {"i": 61, "op": "add", "dst": 5, "src": 7, "src2": 7, "imm": "0xaf9dd72d", "imm2": "0xad7493e7", "rot": 7, "bit": 31, "mask": 16, "width": 1}, + {"i": 62, "op": "add", "dst": 4, "src": 6, "src2": 2, "imm": "0x89841d87", "imm2": "0x1e07c3d9", "rot": 6, "bit": 27, "mask": 1, "width": 1}, + {"i": 63, "op": "rotl", "dst": 5, "src": 4, "src2": 2, "imm": "0xae210f8d", "imm2": "0x8e499ba4", "rot": 19, "bit": 9, "mask": 1, "width": 1} + ] +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/program.metal b/proto-cuda/packs-ca2-mixer/mx4-genesis/program.metal new file mode 100644 index 00000000..d90be2f7 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/program.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ dataset[r2 & MASK]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ dataset[r0 & MASK]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ dataset[r1 & MASK]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ dataset[r0 & MASK]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ dataset[r1 & MASK]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ dataset[r0 & MASK]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ dataset[r7 & MASK]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ dataset[r2 & MASK]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/program_bound.metal b/proto-cuda/packs-ca2-mixer/mx4-genesis/program_bound.metal new file mode 100644 index 00000000..8fafce71 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/program_bound.metal @@ -0,0 +1,111 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ dataset[r2 & MASK]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ dataset[r0 & MASK]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ dataset[r1 & MASK]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ dataset[r0 & MASK]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ dataset[r1 & MASK]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ dataset[r0 & MASK]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ dataset[r7 & MASK]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ dataset[r2 & MASK]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.h b/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.h new file mode 100644 index 00000000..6ed53fb1 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.h @@ -0,0 +1,57 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0x63acd2d273f475baull, 0xe929c78b34b80d4bull, 0x0b1011cb19982558ull, 0x1457a0df5497aa11ull, 0x957d0f3bb71d98fbull, 0xac16901e6e6f6057ull, 0x8ea1c6279f4b177aull, 0xf28146e60bd08ba9ull, + 0xfe5b8cfe87f8e65bull, 0x49f87240566ace62ull, 0x6ef6d6b7bdea8e41ull, 0x46d9c0dc29a97b9cull, 0x111fe30128db9398ull, 0x66dc39084f0946d4ull, 0x8ee11bdfd35fecf2ull, 0x2861fcfc75db6677ull, + 0x31c7667d4bde8556ull, 0xc5989c48858b4ce0ull, 0x276395e734a9d30dull, 0x84217b41e91368ffull, 0x3604861e34d9f697ull, 0x9f51d8ee16bf3639ull, 0xc89e47bafa84401cull, 0x7ae78c1f10b70e19ull, + 0x0b8c947157a29a48ull, 0xd67192e8cfb43842ull, 0x05a4c6d182c8c675ull, 0x188e2661f3263f2eull, 0xa2df24238f7fea2eull, 0xed69ea7e13ad3a48ull, 0x2d44ae509bab91b8ull, 0xadad61931ea4fb70ull + }, + { // base nonce 4096 + 0xedd508ac57e5699aull, 0x4eefd56d526cdaebull, 0x6c6407d53b9ade77ull, 0x8aaddb277f4d7ea4ull, 0xa1268b3328ec5c7eull, 0x3046863cc08f10b6ull, 0x47fe3bb47491f11cull, 0x9892e81a319f05b0ull, + 0x2c977f8db84667f0ull, 0x3751e42afdf7d37full, 0xf7c4efcd768d3da5ull, 0x79b00b8156bd1981ull, 0x5983f862fb97ee2full, 0xb34e9a2d9e810a26ull, 0x39a71549ca7948b8ull, 0x37c86a24538629b9ull, + 0x4e519f3ad2615633ull, 0xe1393fce43030a86ull, 0x46802bbd8f7913edull, 0x7116a52e1e51c9a0ull, 0x2b966fdbbc23bf85ull, 0x718ec9de9c49f734ull, 0x6d770291c4b420d0ull, 0x9779a328a8bea8b1ull, + 0xc5af3efc143bd2c2ull, 0x0931d55121d12a16ull, 0x8f985babebd420caull, 0x3a4ce5974e9a6e42ull, 0xd2d8644ee07ac277ull, 0x3633f2b2d5ae6edeull, 0xe84b2890e1648233ull, 0x8892f8604733b1e0ull + }, + { // base nonce 1000000 + 0x8b3183778a49f59cull, 0x1831b72a8797e895ull, 0x6288de49326df031ull, 0xdf8d589babc64daaull, 0x3110afa71db82c11ull, 0x3ca14a14afa49b89ull, 0xa14b46a71bcfb2e1ull, 0xf148f5ef363e111bull, + 0xc62d5fb72f53e8adull, 0x8f218b5e9ee8ac70ull, 0xc9683c2cebe6f13eull, 0xc0539e433297efc5ull, 0xf72639c8336113f7ull, 0xedfee376a9fde5d7ull, 0x4d2f3bb8b7c96dc5ull, 0x5a2c68ee48a669d4ull, + 0x0e50f07d454f804full, 0x4c53ea83ac05243eull, 0xd98d409876d1843dull, 0xfec5272f4d75b773ull, 0xbdc1031f6698693aull, 0xb4bc7d47d1e9aabeull, 0x1c7524d48c985b71ull, 0x65ae6f47ef1a0a46ull, + 0x15137619fbf9f945ull, 0x7a73e6324619d0a4ull, 0xcd76884a72b4e50full, 0xcc58c48cf2fabeb9ull, 0x8c7e0c672424b507ull, 0x30dc7c5a2186db58ull, 0xb5322548a60418d6ull, 0x75eae55eba53a506ull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0x61ff2180u, 0x0d4c7e6cu, 0x2177d443u, 0x60df9025u, 0xcf8b2e10u, 0x63675bfbu, 0x25289e58u, 0x9c45dc42u, + 0x2d271c54u, 0x9652369bu, 0x2dd77508u, 0x5921392cu, 0x3afa60eeu, 0xc640ad68u, 0xf2bb56ffu, 0xcfa46438u +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0x5020180eu; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0xde85726du, 0x7cfc31c7u, 0xd3cd5289u, 0x6ebbeef1u, 0x9858413bu, 0xe786f141u, 0x64f13833u, 0xca229d04u, 0xd6e9303eu, 0xac7c7b85u, 0x1c5098bcu, 0xfa0f23c8u, 0xb6fad4fdu, 0xd17f0c5fu, 0xd6b7abc9u, 0xd5ee83ccu, 0x53306f26u, 0x3319d1b6u, 0x3513a069u, 0x9f07da0au, 0xef99d8efu, 0x0ba1c662u, 0x3f18c586u, 0x80727509u, 0xb68425f9u, 0x8a1f253au, 0x40cf9312u, 0x84e16868u, 0x00d870dfu, 0x3a61e8a0u, 0x53f22acau, 0xfbdd07c8u, 0x07932a9du, 0xb22c4cbcu, 0x711e42d3u, 0xa0176126u, 0x6d8823aau, 0x20a0e0b0u, 0x41ba7beau, 0xcaafcc88u, 0x7fdb6389u, 0x06759acau, 0x237fa289u, 0x2f4e42d3u, 0x55c22a13u, 0x005fc019u, 0xf6030945u, 0x17795f6bu, 0x0c816b99u, 0x62ea510du, 0x838557cbu, 0x3546a21fu, 0x804664f0u, 0x266f4dc5u, 0xb7d87ff0u, 0x17ee0753u, 0x30e21993u, 0x464e7559u, 0xdff3b3ceu, 0x3af3bf66u, 0x5552e478u, 0xa42607edu, 0x970cb406u, 0xf44efe28u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u, + 0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du, + 0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull; diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.json b/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.json new file mode 100644 index 00000000..0667ecf1 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.json @@ -0,0 +1,36 @@ +{ + "seed": "igneum-genesis", + "day": "2026-10-03", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0x63acd2d273f475ba", "0xe929c78b34b80d4b", "0x0b1011cb19982558", "0x1457a0df5497aa11", "0x957d0f3bb71d98fb", "0xac16901e6e6f6057", "0x8ea1c6279f4b177a", "0xf28146e60bd08ba9", + "0xfe5b8cfe87f8e65b", "0x49f87240566ace62", "0x6ef6d6b7bdea8e41", "0x46d9c0dc29a97b9c", "0x111fe30128db9398", "0x66dc39084f0946d4", "0x8ee11bdfd35fecf2", "0x2861fcfc75db6677", + "0x31c7667d4bde8556", "0xc5989c48858b4ce0", "0x276395e734a9d30d", "0x84217b41e91368ff", "0x3604861e34d9f697", "0x9f51d8ee16bf3639", "0xc89e47bafa84401c", "0x7ae78c1f10b70e19", + "0x0b8c947157a29a48", "0xd67192e8cfb43842", "0x05a4c6d182c8c675", "0x188e2661f3263f2e", "0xa2df24238f7fea2e", "0xed69ea7e13ad3a48", "0x2d44ae509bab91b8", "0xadad61931ea4fb70" + ]}, + {"base_nonce": 4096, "expected": [ + "0xedd508ac57e5699a", "0x4eefd56d526cdaeb", "0x6c6407d53b9ade77", "0x8aaddb277f4d7ea4", "0xa1268b3328ec5c7e", "0x3046863cc08f10b6", "0x47fe3bb47491f11c", "0x9892e81a319f05b0", + "0x2c977f8db84667f0", "0x3751e42afdf7d37f", "0xf7c4efcd768d3da5", "0x79b00b8156bd1981", "0x5983f862fb97ee2f", "0xb34e9a2d9e810a26", "0x39a71549ca7948b8", "0x37c86a24538629b9", + "0x4e519f3ad2615633", "0xe1393fce43030a86", "0x46802bbd8f7913ed", "0x7116a52e1e51c9a0", "0x2b966fdbbc23bf85", "0x718ec9de9c49f734", "0x6d770291c4b420d0", "0x9779a328a8bea8b1", + "0xc5af3efc143bd2c2", "0x0931d55121d12a16", "0x8f985babebd420ca", "0x3a4ce5974e9a6e42", "0xd2d8644ee07ac277", "0x3633f2b2d5ae6ede", "0xe84b2890e1648233", "0x8892f8604733b1e0" + ]}, + {"base_nonce": 1000000, "expected": [ + "0x8b3183778a49f59c", "0x1831b72a8797e895", "0x6288de49326df031", "0xdf8d589babc64daa", "0x3110afa71db82c11", "0x3ca14a14afa49b89", "0xa14b46a71bcfb2e1", "0xf148f5ef363e111b", + "0xc62d5fb72f53e8ad", "0x8f218b5e9ee8ac70", "0xc9683c2cebe6f13e", "0xc0539e433297efc5", "0xf72639c8336113f7", "0xedfee376a9fde5d7", "0x4d2f3bb8b7c96dc5", "0x5a2c68ee48a669d4", + "0x0e50f07d454f804f", "0x4c53ea83ac05243e", "0xd98d409876d1843d", "0xfec5272f4d75b773", "0xbdc1031f6698693a", "0xb4bc7d47d1e9aabe", "0x1c7524d48c985b71", "0x65ae6f47ef1a0a46", + "0x15137619fbf9f945", "0x7a73e6324619d0a4", "0xcd76884a72b4e50f", "0xcc58c48cf2fabeb9", "0x8c7e0c672424b507", "0x30dc7c5a2186db58", "0xb5322548a60418d6", "0x75eae55eba53a506" + ]} + ], + "dataset_head": ["0x61ff2180", "0x0d4c7e6c", "0x2177d443", "0x60df9025", "0xcf8b2e10", "0x63675bfb", "0x25289e58", "0x9c45dc42", "0x2d271c54", "0x9652369b", "0x2dd77508", "0x5921392c", "0x3afa60ee", "0xc640ad68", "0xf2bb56ff", "0xcfa46438"], + "dataset_last_index": 268435455, + "dataset_last": "0x5020180e", + "dataset_samples": [{"index": 59471966, "value": "0xde85726d"}, {"index": 217795994, "value": "0x7cfc31c7"}, {"index": 208353206, "value": "0xd3cd5289"}, {"index": 42483309, "value": "0x6ebbeef1"}, {"index": 172547758, "value": "0x9858413b"}, {"index": 148076330, "value": "0xe786f141"}, {"index": 183853158, "value": "0x64f13833"}, {"index": 214389424, "value": "0xca229d04"}, {"index": 267488061, "value": "0xd6e9303e"}, {"index": 169781097, "value": "0xac7c7b85"}, {"index": 184093494, "value": "0x1c5098bc"}, {"index": 153880993, "value": "0xfa0f23c8"}, {"index": 84977930, "value": "0xb6fad4fd"}, {"index": 46426879, "value": "0xd17f0c5f"}, {"index": 3093825, "value": "0xd6b7abc9"}, {"index": 225364072, "value": "0xd5ee83cc"}, {"index": 44593546, "value": "0x53306f26"}, {"index": 260713159, "value": "0x3319d1b6"}, {"index": 168250303, "value": "0x3513a069"}, {"index": 52384140, "value": "0x9f07da0a"}, {"index": 223401610, "value": "0xef99d8ef"}, {"index": 45554030, "value": "0x0ba1c662"}, {"index": 95410555, "value": "0x3f18c586"}, {"index": 175039924, "value": "0x80727509"}, {"index": 79171087, "value": "0xb68425f9"}, {"index": 267580473, "value": "0x8a1f253a"}, {"index": 24168642, "value": "0x40cf9312"}, {"index": 37981670, "value": "0x84e16868"}, {"index": 171551130, "value": "0x00d870df"}, {"index": 195559979, "value": "0x3a61e8a0"}, {"index": 204611762, "value": "0x53f22aca"}, {"index": 140997658, "value": "0xfbdd07c8"}, {"index": 138925853, "value": "0x07932a9d"}, {"index": 86637313, "value": "0xb22c4cbc"}, {"index": 20736778, "value": "0x711e42d3"}, {"index": 219665210, "value": "0xa0176126"}, {"index": 160430336, "value": "0x6d8823aa"}, {"index": 264654675, "value": "0x20a0e0b0"}, {"index": 8013395, "value": "0x41ba7bea"}, {"index": 228945585, "value": "0xcaafcc88"}, {"index": 213884386, "value": "0x7fdb6389"}, {"index": 104419827, "value": "0x06759aca"}, {"index": 44185464, "value": "0x237fa289"}, {"index": 142737231, "value": "0x2f4e42d3"}, {"index": 99284897, "value": "0x55c22a13"}, {"index": 132475900, "value": "0x005fc019"}, {"index": 61861762, "value": "0xf6030945"}, {"index": 132056166, "value": "0x17795f6b"}, {"index": 262388043, "value": "0x0c816b99"}, {"index": 91878046, "value": "0x62ea510d"}, {"index": 117353561, "value": "0x838557cb"}, {"index": 124768597, "value": "0x3546a21f"}, {"index": 71352993, "value": "0x804664f0"}, {"index": 190698941, "value": "0x266f4dc5"}, {"index": 46055428, "value": "0xb7d87ff0"}, {"index": 55281366, "value": "0x17ee0753"}, {"index": 165145231, "value": "0x30e21993"}, {"index": 106810753, "value": "0x464e7559"}, {"index": 171985651, "value": "0xdff3b3ce"}, {"index": 232085256, "value": "0x3af3bf66"}, {"index": 159510492, "value": "0x5552e478"}, {"index": 40072060, "value": "0xa42607ed"}, {"index": 209107596, "value": "0x970cb406"}, {"index": 39023794, "value": "0xf44efe28"}], + "cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"], + "cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"], + "cache_fnv1a64": "0x48c4f5bf24166b2e" +} From 5424c30bba3a1e2f2c1f3ae9af9206b81f5b8196 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:03:42 +0000 Subject: [PATCH 061/158] Proving v1: the app's default restores Decision and gb (lost in the tier rewrite), the AMD-only and Apple 'mines and does not prove' line with the CPU prover's measured cost, the plan's AMD/Apple row Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/src/provedefault.rs | 8 ++++++-- docs/plans/proving-v1.md | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/igneum-app/src/provedefault.rs b/app/igneum-app/src/provedefault.rs index a124ee0e..6aebd90d 100644 --- a/app/igneum-app/src/provedefault.rs +++ b/app/igneum-app/src/provedefault.rs @@ -10,6 +10,7 @@ //! | Windows under 32 GB of RAM | off, with the line saying why | the WSL2 prover held 7.9 GB on a 63 GB PC; a 16 GB PC would swap | //! | Windows with a qualifying card but WSL2 silent | off, with the Set up hint | nothing can prove until the distribution exists | //! | Apple silicon | off | the M5 Max CPU took 41 to 55 s for an EMPTY shard's compressed proof under load and 272 s for a 200-pgas shard; a full shard was never under 60 s (bench-log 4 and 5 October 2026) | +//! | AMD-only (no NVIDIA card) | off, "mines and does not prove" | no zkVM proves on an AMD GPU today (docs/analysis/amd-proving.md); the SP1 CPU prover on PC 1 cost 82 to 87 s core plus 199 to 202 s compressed a shard at a 30 GB RSS whatever the shard size (bench-log, "the SP1 CPU prover on PC 1") | //! //! Decided 5 October 2026 (delegated by the project lead: "deploy what is absolute best"), docs/plans/proving-v1.md. The default //! never switches an explicit on back off, and Settings always wins afterwards. @@ -64,6 +65,8 @@ pub fn decide(cards: &[CardState], os: &str, wsl_answers: Option, ram_mb: }; let why = if nvidia.iter().any(|c| c.vram_mb >= 15_872) { "a full shard needs a 24 GB card (measured 20.4 GB on the adopted shard size, 13.9 GB for an empty one); this card is under that, so Settings would switch proving on at your own risk" + } else if nvidia.is_empty() { + "this machine mines and does not prove: no zkVM proves on an AMD GPU today, and the CPU prover costs about 5 minutes a shard at a 30 GB RSS (bench-log, the SP1 CPU prover on PC 1); proving needs an NVIDIA card with 24 GB or more" } else { "no NVIDIA card with 24 GB or more (the GPU prover's floor is 13.9 GB for an empty shard and 20.4 GB for a full one)" }; @@ -130,8 +133,9 @@ mod tests { assert!(!d.on); assert!(d.line.contains("no NVIDIA card with 24 GB or more") && d.line.contains("RTX 3060 12 GB"), "{}", d.line); assert!(!decide(&[card("nvidia", "NVIDIA GeForce RTX 3080", 10_240)], "linux", None, None).on); - assert!(!decide(&[card("amd", "Radeon RX 9070 XT", 16_384)], "linux", None, None).on, "no CUDA prover for AMD yet"); - assert!(decide(&[], "linux", None, None).line.contains("no NVIDIA card")); + let d = decide(&[card("amd", "Radeon RX 9070 XT", 16_384)], "linux", None, None); + assert!(!d.on && d.line.contains("mines and does not prove"), "{}", d.line); + assert!(decide(&[], "linux", None, None).line.contains("mines and does not prove")); } #[test] diff --git a/docs/plans/proving-v1.md b/docs/plans/proving-v1.md index ee707b99..bad75e90 100644 --- a/docs/plans/proving-v1.md +++ b/docs/plans/proving-v1.md @@ -125,6 +125,7 @@ The GPU server of SP1 6.8.1 sets the memory, not the shard: a floor of 13.9 GB f | 16 GB (RTX 5080, 4080) | an empty shard only (13.9 GB) | nothing (15.7 GB for an empty shard, no room for the display) | off, with the line | | 12 GB (RTX 3060, 4070) | nothing: the floor is 13.9 GB | nothing | off; the project lead's "make sure we can prove on 12 GB cards" is OPEN: it needs a prover build with a smaller floor (an SP1 release or a fork of its GPU server), measured on a 12 GB card; the curve above is the evidence for that ask (D2) | | under 12 GB | nothing | nothing | off, mine only | +| AMD-only and Apple machines | nothing on the GPU: no zkVM proves on an AMD GPU today (`docs/analysis/amd-proving.md`, branch amd-prove); the CPU prover is about 5 minutes a shard at a 30 GB RSS whatever the shard size (PC 1, bench-log "the SP1 CPU prover on PC 1") | | off, "mines and does not prove"; the only non-NVIDIA path with a shipped backend is RISC Zero's Metal prover behind the `ProofSystem` seam (a second guest and pinned id, a verifier for both formats, no shared aggregation): an open item, not 0.3.11 | The three profile numbers the coordinator asked for, as measured: under 9.0 GB does not exist on this build (floor 13.9); under 15.0 GB mine-and-prove does not exist for any full shard (the v1 shard alone is 20.4); the full profile is the 32 GB card. The fleet table's "proving-only" rows therefore read 24 GB cards at the v1 budget and 32 GB cards at the prototype budget. Shards per block at the v1 budget: 1 on tonight's empty chain, 2 to 4 on blocks with transactions (`B_p` 120,000 = 4 x `S_p`); the aggregation count is one per block whatever the shard count (the chained recursion), so the aggregation-cost agent's target is per block. From 36c5f13a5f35979336e0147dd5a776d3bafb88b0 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:04:39 +0000 Subject: [PATCH 062/158] fast-time: the proving v1 fields and pow_genesis_dataset_log2 in both override files (the fork's every-field test), README rows; class-v3.mjs reports the build time per epoch; docs/plans/counter-asic-2-node.md (the node side of Counter ASIC 2.0, gate result pending) Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-2-node.md | 97 +++++++++++++++++++ infra/fast-time/README.md | 4 + infra/fast-time/class-v3.mjs | 14 +-- infra/fast-time/override-60x.json | 4 + .../redteam/override-60x-v3.json | 2 +- 5 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 docs/plans/counter-asic-2-node.md diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md new file mode 100644 index 00000000..5eff0634 --- /dev/null +++ b/docs/plans/counter-asic-2-node.md @@ -0,0 +1,97 @@ +# Counter ASIC 2.0: the node side (program class v3 as a height switch) + +5 October 2026, night, worker "ca2-node". Branches: `ca2-v3` (main repository: the igneum-pow seam, the workers, the fast-time gate) and `ca2-v3-node` (the fork, from the 0.3.10 tip 21d4c73c plus pack-loop 05ef0fa3). Plan: `docs/plans/counter-asic-2-rollout.md`; status: `docs/plans/counter-asic-2-status.md`. The shape follows finality v3 (`docs/plans/finality-v3-rollout-devnet.md` section 6): one height switch read from the override file, every node carries the same object before the height. + +Everything below is the SEAM. The class itself (`igneum_pow::V3_CLASS`) is a placeholder, w16 (`LoadClass::fixed(4, 16)`), that the integration branch replaces with the decided width, mix and scratch share; the ca2-era draw and the ca2-mixer item construction fill what `Epoch::from_chain_seeds` calls. Nothing here changes a v2 program, a pinned pack or any live node. + +## 1. What changed, where + +| Piece | What | +|---|---| +| igneum-pow `generator.rs` | `ProgramClass { V2, V3 }`, `V3_CLASS` (placeholder), `GENERATOR_VERSION_V3 = 3`, `generate_from_seed_bytes_program_class(label, seed, class, era)`; `Program::era_bytes`; a v3 program's id is `program_id(3, seed, attempt)` (spec 01 section 1.4.6) | +| igneum-pow `verify.rs` | `Epoch::from_chain_seeds(epoch, day, era, class, label)`, `Epoch::chain_program` (no cache fill), `Epoch::chain_dataset(day, class)` (the one entry the node's day cache goes through; today both classes build the same cache) | +| igneum-pow `emit.rs` | program.h: `IGNEUM_PROGRAM_CLASS "v3"` and `IGNEUM_ERA_SEED_HEX` beside `IGNEUM_GENERATOR 3`; program.json: `program_class`, `era_seed_bytes`; nothing on a v2 pack (`tests/packs.rs` diffs the pinned packs `igneum-genesis-mh` and `igneum-devnet-v4-epoch0`: identical) | +| igneum-pow `packcheck.rs` | `verify_pack_texts_chain` / `verify_pack_dir_chain(dir, epoch, day, want_class, want_era)`: `PackFault::WrongClass` for the wrong class, the wrong era, or a generator other than 2 or 3; `PackIdentity` carries `generator`, `class`, `era_hex` | +| `proto-cuda/nvrtc/packfile.h` | `pf_load` refuses a generator other than 2 or 3 (spec 1.4.5), reads the class (must match the generator) and the era; `pf_pack_class_ok`, `pf_class_token` (the one rule for the `class=` / `era=` tokens) | +| `proto-cuda/nvrtc/worker.cpp`, `proto-opencl/host.c` | a pair's identity includes its class and era when the line names them; right seeds and the wrong class answer `need ` and `error pack : program class mismatch ...`, so the miner prepares the pair from a pack of the right class; a prepare on a wrong-class pack fails in plain words | +| `proto-metal/main.swift` | refuses every `class=v3` line (the Swift generator is version 2; the integration adds 3) | +| fork `consensus/core/src/igneum.rs` | `ProgramClass`, `program_class_for_epoch_at(e, N4, L)`, `program_class_v3_first_epoch_at`, the process-wide activation (`install_program_class_v3_activation`, `program_class_for_epoch`, `program_class_at`), `POW_ERA_BLOCKS`, `POW_ERA_LEAD`, `pow_era_index`, `pow_era_seed_score`; `PowEpochInfo` + `program_class`, `next_program_class`, `program_class_v3_activation_daa`, `era_index`, `era_seed` (serde defaults: v2, never, none) | +| fork `consensus/core/src/config/params.rs` | `program_class_v3_activation_daa` in `Params` and `OverrideParams` (default never on devnet, simnet, mainnet; 0 on the testnet like every other switch), `override_params`, `From`, the digest (unconditionally, right after `finality_v3_activation_daa`), `Params::install_program_class_v3_activation`, `Params::program_class_v3_first_epoch`; tests `override_params_carry_the_program_class_v3_activation`, the digest test's 11th edit, `fast_time_60x_file_is_the_devnet_at_60x` (every field present) | +| fork `kaspad/src/daemon.rs` | `Program class v3 from the override file: active from epoch E (DAA score N4 rounded up to the epoch boundary at 3600*E, epochs of 3600 DAA)`; the activation installed next to the PoW schedule | +| fork `consensus/pow/src/igneum.rs` | `EpochSeeds { epoch, day, class, era }` (+ `EpochSeeds::v2`), day caches keyed on `(day, class)`, the program through `Epoch::chain_program`, the cache through `Epoch::chain_dataset`, `standalone_epoch` through `Epoch::from_chain_seeds`; test `program_class_v3_seeds_hash_their_own_program_over_their_own_cache` | +| fork `consensus/src/pipeline/header_processor/{processor,pre_ghostdag_validation}.rs` | the class of the header's epoch (`program_class_at(daa)`), `HeaderProcessor::era_seed` (the stand-in, memoised per era), `RuleError::EraSeedUnavailable` | +| fork `consensus/src/processes/pruning_proof/igneum_pow.rs` | `seeds_for`: the class of the epoch; era 0 = genesis; a later era is `PruningImportError::MissingEraSeed` (no era witness in the proof format yet) | +| fork `consensus/src/consensus/mod.rs` | `get_pow_epoch_info`: the class of this and the next epoch, the activation, the era index and seed (the era walk memoised once per era per process) | +| fork `rpc/core/src/model/message.rs`, `rpc/grpc/core/proto/rpc.proto` (fields 12 to 16), `rpc/grpc/core/src/convert/message.rs` | `RpcPowEpochInfo` + `program_class` (generator number), `next_program_class`, `program_class_v3_activation_daa`, `era_index`, `era_seed`; an old node's absent fields read as v2, never, none | +| fork `igneum/miner/src/main.rs` | seeds from the template (`seeds_from_info`), the activation installed from the template, the legacy seed walk keys the class on it; `next_pair` takes the next epoch's class; the job and prepare lines end with `class=v3 era=` for a v3 epoch; `seeds.txt` carries `program_class` and `era_seed_hex`; `write_pack_checked` checks class and era (`verify_pack_dir_chain`); the "program and 256 MiB cache ready" lines and `export-pack` print the class and the program id | +| `infra/fast-time/override-60x.json`, `tools/finality-attacks/redteam/override-60x-v3.json`, `infra/fast-time/README.md` | the field at never, the README row | +| `infra/fast-time/class-v3.mjs` | the G4 gate (section 5) | + +## 2. The epoch-boundary rule + +One epoch has one program (spec 01 section 1.12), so the switch keys on the EPOCH: epoch `e` is class v3 when `L * e >= N4` with `L` the live epoch length (`pow_epoch_blocks()`, 3,600 on the devnet, 60 on the fast-time profile). The first v3 epoch is `ceil(N4 / L)`; a height inside an epoch rounds UP to the next boundary and never splits an epoch between two programs. A block's class is a function of its DAA score alone (`program_class_at(daa)`), as its epoch seed is. + +| N4 | L | first v3 epoch | first v3 DAA | the epoch before | +|---|---|---|---|---| +| 150 | 60 | 3 | 180 | epoch 2 (DAA 120 to 179) is v2 to its last block | +| 180 | 60 | 3 | 180 | | +| 181 | 60 | 4 | 240 | | +| 136,000 | 3,600 | 38 | 136,800 | epoch 37 (133,200 to 136,799) is v2 | +| 136,800 | 3,600 | 38 | 136,800 | | +| 0 | any | 0 | 0 | (the testnet) | +| never | any | none | | | + +The unit tests `program_class_switch_rounds_up_to_the_epoch_boundary` (consensus-core) and `override_params_carry_the_program_class_v3_activation` (params) pin these rows and sweep every activation 0..399 at L = 60. + +The digest: the field enters `consensus_digest` unconditionally, so the digest flips the moment a binary carrying the field (at never) runs, exactly as the finality v3 field did. This is intended: every node must carry the object before any node reaches the height, and a node without the field is refused at the handshake (rollout section 1, order step 1). + +## 3. The era stand-in + +`E_n` of spec 04 section 4.4, until the 1-hour VDF is in the node (`docs/plans/era-layout.md` section 2): + +| Era | `E_n` | +|---|---| +| 0 | the genesis block hash | +| n >= 1 | the hash of the last selected-chain block whose DAA score is below `15,552,000 n - 7,200` | + +One function per path: `HeaderProcessor::era_seed` (the header's era from its DAA score, the walk down the selected parents from the header's selected parent, memoised per era in `era_seed_memo`), `Consensus::get_pow_epoch_info` (the same walk from the sink for the template, memoised once per era per process), `ProofSeeds::seeds_for` (era 0 only). The seed block is at least one era lead (7,200 DAA) below any header that uses it, past the merge depth (3,600), so one walk per era per process is sound; the walk itself is up to an era long on the first header of era `n >= 1` (about 15.5 million selected parents), which is why it is memoised and why the VDF should land before era 1 (180 days after genesis). A v2 program never reads the era; the placeholder v3 class does not either (the ca2-era draw will); the era is carried and recorded in the pack so a worker of the wrong era is refused from the first v3 build. + +## 4. The job line, the prepare line, the pack + +| Surface | Class v2 (today) | Class v3 | +|---|---|---| +| job line | `job ` | the same with ` class=v3 era=<64 hex>` at the end | +| prepare line | `prepare []` | the same with ` class=v3 era=<64 hex>` at the end | +| `program.h` | `IGNEUM_GENERATOR 2` | `IGNEUM_GENERATOR 3`, `IGNEUM_PROGRAM_CLASS "v3"`, `IGNEUM_ERA_SEED_HEX "<64 hex>"` | +| `program.json` | `"generator": 2` | `"generator": 3`, `"program_class": "v3"`, `"era_seed_bytes": ""` | +| `seeds.txt` | `epoch_seed_hex`, `day_seed_hex`, `day_index` | plus `program_class v3`, `era_seed_hex ` | +| template `pow_epoch` | `programClass 2` | `programClass 3`, `nextProgramClass`, `programClassV3ActivationDaa`, `eraIndex`, `eraSeed` | + +A v2 line and a v2 pack are byte for byte what the workers read before this branch (the tokens are sent only for a v3 epoch), so a 0.3.10 worker on a 0.3.11 miner mines v2 epochs unchanged and refuses nothing until the switch; by the switch every worker is 0.3.11 (rollout order). + +Refusals: `pf_load` refuses a generator that is not 2 or 3 (`error 0 pack : program pack generator N is not a generator version this worker runs (2 or 3)`, the exit-44 path of 05ef0fa3: the miner rebuilds the pack before the restart). A job of class v3 against a resident v2 pack of the same seeds answers `need ` and `error pack : program class mismatch: this pack is class v2, the job names class v3 (export the pack again)`; the miner's `need` handling prepares the pair again, `write_pack_checked` writes a v3 pack (checked with `verify_pack_dir_chain` before the worker hears of it), and the prepared v3 pair wins over the resident v2 pair because the pair identity now carries the class. The Metal worker answers `error program class v3 is not implemented by this worker` until the Swift generator carries version 3. + +## 5. The fast-time gate (rollout G4) + +`node infra/fast-time/class-v3.mjs [--secs 420] [--activation 150] [--epochs-after 2]` under `tools/lock/with-lock.sh run`: three nodes on `override-60x.json` merged with `genesis_bits` 0x1f010000 (2^16 hashes per block, the CPU difficulty of `sim/difficulty/testnet_v2.py`) and `program_class_v3_activation_daa` 150 (inside epoch 2, so the rounding rule is exercised: the first v3 epoch is 3 at DAA 180); one real CPU miner per node (`--engine igneum-pow`, 1 thread, real lottery-hash solutions, every node verifying the other two); the run ends two epochs after the boundary. + +Result: PENDING (filled by the run; see the status file and the final report). + +## 6. Tests + +| Where | What | State | +|---|---|---| +| igneum-pow `cargo test --release` | 42 unit + 11 pack tests, including `program_classes`, `program_class_and_era_are_checked`, the pinned-pack diffs | pass (Mac, 5 Oct 2026) | +| `proto-cuda/nvrtc/emu/packfile-test.sh` | 13 checks: the attempt rule, the generator rule, a v3 pack with its era, the token matcher | pass (Mac) | +| `host.c`, `worker.cpp` (emulation), `main.swift` | syntax / compile | pass (Mac) | +| fork `cargo test -p kaspa-pow --features igneum-pow` | the engine tests including the v3 one | see the final report | +| fork `cargo test -p kaspa-consensus-core` | the switch rounding, the era clock, the params and digest tests | see the final report | +| PC 2 `build-job.mjs` suites | `kaspa-consensus-core igneum-exec kaspa-pow kaspa-consensus igneum-miner` + `igneum-app` | the coordinator publishes on its go | + +## 7. Unverified, and what is owed + +- The era walk for era >= 1 has never run (the devnet is 180 days from era 1); a pruning-proof sync past era 0 fails with `MissingEraSeed` until an era witness exists in the proof format. +- The Metal worker has no class v3 generator (it regenerates from the seed in Swift): the integration branch adds generator 3 there, or the Mac mines v3 with the OpenCL worker from a pack. +- The GPU workers' class refusal was checked by the C test of `pf_pack_class_ok` and the syntax of both hosts, not by a live worker on a v3 pack: the integration's bit-exact gate (G1) is where a real worker first builds a v3 pack. +- `next_pair` keeps the current era seed for the next epoch; an epoch boundary that is also an era boundary (once per 180 days) would prepare the wrong era, and the job line then names the right one, so the worker refuses the prepared pair and the miner prepares again (one wasted compile, no wrong block). The VDF era seed replaces the stand-in before this matters. +- Wire compatibility: `RpcPowEpochInfo` gained five fields in its Borsh form (wRPC) and five proto fields (gRPC); the gRPC side reads an old node's zeros as v2 / never / none; the Borsh form is versioned by `GetBlockTemplateResponse` (version 2 carries the whole struct), so a 0.3.11 wRPC client against a 0.3.10 node reads short: the miner uses gRPC, the console reads JSON (serde defaults). diff --git a/infra/fast-time/README.md b/infra/fast-time/README.md index fb89f317..10078f7c 100644 --- a/infra/fast-time/README.md +++ b/infra/fast-time/README.md @@ -57,6 +57,10 @@ Time parameters, divided by 60 (devnet value, 60x value): | `difficulty_v2_activation_daa` | never (`18446744073709551615`) | never | a height, not a clock: difficulty rule v2 (4 Oct 2026, `docs/analysis/difficulty-2026-10-04-oscillation.md`) switches on at this DAA score; a test network sets it in its merged file (`sim/difficulty/testnet_v2.py` uses 900) | | `proving_v0_activation_daa` | never | never | a height: proving v0 payouts (spec 7.7) switch on at this DAA score; `tools/proving-v0/run.mjs` sets 60 in its merged file | | `finality_v3_activation_daa` | never | never | a height, not a clock: finality rule v3 (the frozen weight table of ledger F21 and the certificate fold of F22, 4 Oct 2026 evening) applies to checkpoints at or above this DAA score; `tools/finality-attacks/v3.mjs` sets 0 in its merged file | +| `pow_genesis_dataset_log2` | 28 | 28 | a size, not a clock: the dataset at genesis (2^28 words, 1 GiB); the cache growth rule (ca2-mixer) doubles it with the days since the genesis day | +| `proving_v1_activation_daa` | never | never | a height: proving v1 (the fork's proving-v1 branch, 5 Oct 2026) switches on at this DAA score | +| `proving_v1_segment_blocks`, `proving_v1_aggregator_share_bps` | 8, 1,000 | 8, 1,000 | a block count and a share | +| `proving_v1_unproven_daa` | 600 | 10 | a DAA clock (the unproven allowance), divided by 60; the proving agent confirms the value | | `program_class_v3_activation_daa` | never | never | a height, not a clock: the lottery hash draws programs from class v3 (Counter ASIC 2.0, 5 Oct 2026) from the first EPOCH whose start is at or above this DAA score (rounded up to an epoch boundary: at 60 DAA per epoch, 150 means epoch 3 at DAA 180); `infra/fast-time/class-v3.mjs` sets it a few epochs ahead in its merged file | Unchanged, and why: diff --git a/infra/fast-time/class-v3.mjs b/infra/fast-time/class-v3.mjs index 58e0e03c..a4cb213f 100644 --- a/infra/fast-time/class-v3.mjs +++ b/infra/fast-time/class-v3.mjs @@ -155,13 +155,15 @@ const chainBefore = before.filter(b => b.chain).length, chainAfter = after.filte // program ids and classes per epoch from the miners' "program and 256 MiB cache ready" lines const programs = new Map(); // epoch seed -> { class, id, miners: Set } for (const i of [0, 1, 2]) for (const l of minerLog(i)) { - const m = /epoch seed ([0-9a-f]{64}) day (\d+) \(daa (\d+)\): program and 256 MiB cache ready in [\d.]+ ms; class (v\d) program id ([0-9a-f]{16})/.exec(l); + const m = /epoch seed ([0-9a-f]{64}) day (\d+) \(daa (\d+)\): program and 256 MiB cache ready in ([\d.]+) ms; class (v\d) program id ([0-9a-f]{16})/.exec(l); if (!m) continue; - const k = m[1]; const e = programs.get(k) || { seed: k.slice(0, 16), epoch: Math.floor(+m[3] / EPOCH), class: m[4], id: m[5], miners: new Set() }; - if (e.id !== m[5] || e.class !== m[4]) e.disagree = true; - e.miners.add(i); programs.set(k, e); + const k = m[1]; const e = programs.get(k) || { seed: k.slice(0, 16), epoch: Math.floor(+m[3] / EPOCH), class: m[5], id: m[6], miners: new Set(), ready_ms: [] }; + if (e.id !== m[6] || e.class !== m[5]) e.disagree = true; + e.miners.add(i); e.ready_ms.push(+m[4]); programs.set(k, e); } -const programRows = [...programs.values()].sort((a, b) => a.epoch - b.epoch).map(p => ({ epoch: p.epoch, class: p.class, program_id: p.id, seed: p.seed, miners: p.miners.size, disagree: !!p.disagree })); +// ready_ms: the program generation plus the day's cache and dataset build on one CPU core (the first epoch of a day +// pays the cache; under the mixer x4 construction the first v3 epoch's build is the number the rollout asks for) +const programRows = [...programs.values()].sort((a, b) => a.epoch - b.epoch).map(p => ({ epoch: p.epoch, class: p.class, program_id: p.id, seed: p.seed, miners: p.miners.size, disagree: !!p.disagree, ready_ms: p.ready_ms })); const v2Ids = programRows.filter(p => p.class === 'v2').map(p => p.program_id), v3Ids = programRows.filter(p => p.class === 'v3').map(p => p.program_id); // rejections: the miners' submit answers and the nodes' PoW lines @@ -199,7 +201,7 @@ const summary = { sinks, block_counts: counts, tips_per_node: tips, switch_lines: switchLines, samples, }; writeFileSync(`${TMP}/summary.json`, JSON.stringify(summary, null, 2)); -log(`SUMMARY ${pass ? 'PASS' : 'FAIL'}: blocks ${before.length} before / ${after.length} after the boundary at DAA ${BOUNDARY} (chain ${chainBefore} / ${chainAfter}); programs ${programRows.map(p => `e${p.epoch}:${p.class}:${p.program_id}`).join(' ')}; rejected miners ${rejectedMiner.map(r => r.length).join('/')} nodes ${rejectedNode.map(r => r.length).join('/')}; sinks ${sinks.join(' ')} (${checks.sinks_agree ? 'agree' : 'DIFFER'}); block counts ${counts.join('/')}; switch lines ${switchLines.filter(Boolean).length}/3`); +log(`SUMMARY ${pass ? 'PASS' : 'FAIL'}: blocks ${before.length} before / ${after.length} after the boundary at DAA ${BOUNDARY} (chain ${chainBefore} / ${chainAfter}); programs ${programRows.map(p => `e${p.epoch}:${p.class}:${p.program_id}:${Math.max(...p.ready_ms)}ms`).join(' ')}; rejected miners ${rejectedMiner.map(r => r.length).join('/')} nodes ${rejectedNode.map(r => r.length).join('/')}; sinks ${sinks.join(' ')} (${checks.sinks_agree ? 'agree' : 'DIFFER'}); block counts ${counts.join('/')}; switch lines ${switchLines.filter(Boolean).length}/3`); for (const [k, v] of Object.entries(checks)) if (!v) log(`FAILED CHECK ${k}`); log(`summary: ${TMP}/summary.json`); await stopAll(); diff --git a/infra/fast-time/override-60x.json b/infra/fast-time/override-60x.json index b8e77dff..3c9d6cc2 100644 --- a/infra/fast-time/override-60x.json +++ b/infra/fast-time/override-60x.json @@ -56,6 +56,10 @@ "finality_v3_activation_daa": 18446744073709551615, "program_class_v3_activation_daa": 18446744073709551615, "pow_genesis_dataset_log2": 28, + "proving_v1_activation_daa": 18446744073709551615, + "proving_v1_segment_blocks": 8, + "proving_v1_unproven_daa": 10, + "proving_v1_aggregator_share_bps": 1000, "fees_v1_activation_daa": 0, "fees": {"pgas": {"version": 1, "cycles_per_pgas": 1000, "intrinsic_pgas_per_tx": 300, "modexp_base": 10, "modexp_per_byte_numer": 1, "modexp_per_byte_denom": 10}, "block_proving_gas_limit": 120000, "shard_proving_gas_budget": 30000, "min_execution_base_fee_wei": 100000000000, "min_proving_base_fee_wei": 10000000000000, "initial_execution_base_fee_wei": 100000000000, "initial_proving_base_fee_wei": 10000000000000, "base_fee_change_denominator": 8} } diff --git a/tools/finality-attacks/redteam/override-60x-v3.json b/tools/finality-attacks/redteam/override-60x-v3.json index 98a1f0f3..caadb408 100644 --- a/tools/finality-attacks/redteam/override-60x-v3.json +++ b/tools/finality-attacks/redteam/override-60x-v3.json @@ -1 +1 @@ -{"timestamp_deviation_tolerance": 132, "past_median_time_window_size": 27, "difficulty_window_size": 661, "min_difficulty_window_size": 150, "difficulty_rule": "igneum-dual", "coinbase_payload_script_public_key_max_len": 150, "max_coinbase_payload_len": 16384, "max_tx_inputs": 1000, "max_tx_outputs": 1000, "max_signature_script_len": 250000, "max_script_public_key_len": 10000, "mass_per_tx_byte": 1, "mass_per_script_pub_key_byte": 10, "mass_per_sig_op": 1000, "block_mass_limits": {"compute": 500000, "storage": 500000, "transient": 1000000}, "block_lane_limits": {"lanes_per_block": 50, "gas_per_lane": 1000000000}, "storage_mass_parameter": 1000000000000, "deflationary_phase_daa_score": 0, "pre_deflationary_phase_base_subsidy": 50000000000, "skip_proof_of_work": false, "max_block_level": 250, "pruning_proof_m": 1000, "blockrate": {"target_time_per_block": 1000, "ghostdag_k": 18, "past_median_time_sample_rate": 10, "difficulty_sample_rate": 4, "max_block_parents": 10, "mergeset_size_limit": 180, "merge_depth": 60, "finality_depth": 720, "pruning_depth": 13838, "coinbase_maturity": 2}, "pre_crescendo_target_time_per_block": 1000, "crescendo_activation": 0, "genesis_bits": 487587840, "finality": {"checkpoint_interval": 30, "checkpoint_depth": 20, "weight_window": 120, "dust": 5, "presence_window": 1, "aggregators": 8, "equivocation_ban": 120, "min_daa": 120, "aggregator_fallback": 1}, "pow_epoch_blocks": 60, "pow_epoch_lead": 10, "pow_day_ms": 1440000, "finality_v3_activation_daa": 0, "program_class_v3_activation_daa": 18446744073709551615, "pow_genesis_dataset_log2": 28} \ No newline at end of file +{"timestamp_deviation_tolerance": 132, "past_median_time_window_size": 27, "difficulty_window_size": 661, "min_difficulty_window_size": 150, "difficulty_rule": "igneum-dual", "coinbase_payload_script_public_key_max_len": 150, "max_coinbase_payload_len": 16384, "max_tx_inputs": 1000, "max_tx_outputs": 1000, "max_signature_script_len": 250000, "max_script_public_key_len": 10000, "mass_per_tx_byte": 1, "mass_per_script_pub_key_byte": 10, "mass_per_sig_op": 1000, "block_mass_limits": {"compute": 500000, "storage": 500000, "transient": 1000000}, "block_lane_limits": {"lanes_per_block": 50, "gas_per_lane": 1000000000}, "storage_mass_parameter": 1000000000000, "deflationary_phase_daa_score": 0, "pre_deflationary_phase_base_subsidy": 50000000000, "skip_proof_of_work": false, "max_block_level": 250, "pruning_proof_m": 1000, "blockrate": {"target_time_per_block": 1000, "ghostdag_k": 18, "past_median_time_sample_rate": 10, "difficulty_sample_rate": 4, "max_block_parents": 10, "mergeset_size_limit": 180, "merge_depth": 60, "finality_depth": 720, "pruning_depth": 13838, "coinbase_maturity": 2}, "pre_crescendo_target_time_per_block": 1000, "crescendo_activation": 0, "genesis_bits": 487587840, "finality": {"checkpoint_interval": 30, "checkpoint_depth": 20, "weight_window": 120, "dust": 5, "presence_window": 1, "aggregators": 8, "equivocation_ban": 120, "min_daa": 120, "aggregator_fallback": 1}, "pow_epoch_blocks": 60, "pow_epoch_lead": 10, "pow_day_ms": 1440000, "finality_v3_activation_daa": 0, "program_class_v3_activation_daa": 18446744073709551615, "pow_genesis_dataset_log2": 28, "proving_v1_activation_daa": 18446744073709551615, "proving_v1_segment_blocks": 8, "proving_v1_unproven_daa": 10, "proving_v1_aggregator_share_bps": 1000} \ No newline at end of file From 649d37bad0e927b0e050ffb09faeb8500b38f809 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:05:13 +0000 Subject: [PATCH 063/158] fast-time file: proving_v1_unproven_daa 10 (a DAA clock, 600 / 60, as the merged tree carries it; the harness passes its own --unproven) Co-Authored-By: Claude Fable 5.1 --- infra/fast-time/override-60x.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/fast-time/override-60x.json b/infra/fast-time/override-60x.json index 21966fc0..a9a2da61 100644 --- a/infra/fast-time/override-60x.json +++ b/infra/fast-time/override-60x.json @@ -57,7 +57,7 @@ "fees_v1_activation_daa": 0, "proving_v1_activation_daa": 18446744073709551615, "proving_v1_segment_blocks": 8, - "proving_v1_unproven_daa": 60, + "proving_v1_unproven_daa": 10, "proving_v1_aggregator_share_bps": 1000, "fees": {"pgas": {"version": 1, "cycles_per_pgas": 1000, "intrinsic_pgas_per_tx": 300, "modexp_base": 10, "modexp_per_byte_numer": 1, "modexp_per_byte_denom": 10}, "block_proving_gas_limit": 120000, "shard_proving_gas_budget": 30000, "min_execution_base_fee_wei": 100000000000, "min_proving_base_fee_wei": 10000000000000, "initial_execution_base_fee_wei": 100000000000, "initial_proving_base_fee_wei": 10000000000000, "base_fee_change_denominator": 8} } From 2bc198b89d260d3be378ed67c8a8ec01eb03b6d7 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:09:39 +0000 Subject: [PATCH 064/158] Consequences ledger: C2 closed on the miner-on curve; C22 the 30 GB CPU prover against the macOS switch; sweep 5 notes; D2 the two drafts and the 24 GB marker Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 5 ++++- docs/plans/consequences-decisions.md | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 07bd1c90..f13d2e3c 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -9,7 +9,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | # | Number | Source | Tier affected | Consequence | Action | Owner | State | |---|---|---|---|---|---|---|---| | C1 | Fee switch H = 210,000, reached about 19:50Z on 6 October (0.965 blocks/s) | `docs/plans/fee-switch-devnet.md` sections 3 and 7; `vendor/igneum-node-0310/igneum/exec/src/rpc.rs` 849 (no `daaScore`, no `feesV1ActivationDaa` in `igneum_exportSegments`); the app's exporter call without `--fees-v1-activation-daa` (`app/igneum-app/src/prover.rs` 516 to 519) | every prover on the devnet (PC 2, the Mac, any 0.3.10 machine) | From the first chain block at or above H the 0.3.10 node cuts 30,000-pgas shards and meters with the v1 table, but the app's exporter sees a dump without the switch and cuts at 7.5 M with the prototype table: the host refuses the fixture (wrong `S_p`) or the node vetoes the statement. Proving on the devnet goes dark at H and nothing is paid until every prover runs a node whose export carries the switch (the proving-v1 fork does, `vendor/igneum-node-pv1` rpc.rs 961 and 982). `pc2-chain.ps1` fixtures from the live node fail the same way after H | Either 0.3.11 (proving-v1 fork) on every prover before H, or the switch republished at a later H before 19:50Z tomorrow (a digest flip, every node). Recommendation in `consequences-decisions.md` D1 | proving v1 acd4f36bc2c07a4e2, shipper ae892a8b0f78fe31c, coordinator ada8afb62d752b1e2 | in work (proving agent: fork eb32c645 on 21d4c73c carries daaScore and feesV1ActivationDaa, the exporter needs no flag; 0.3.11 on every prover before 16:00Z on 6 October or H moves to tip + 86,400; the line is in the proving plan, the rollout plan and release-0.3.10.md) | -| C2 | 16,751 MiB GPU peak during the chain of 8 with the miner resident (`chain-pc2-pv1c`) | bench-log "proving v1", step 2 chain row | 16 GB cards (RTX 5080, 5060 Ti 16 GB, 4060 Ti 16 GB) | The plan's "a 16 GB card sits 0.4 GB under tonight's peak" is the empty-shard row (15,590 MiB). The chained aggregation adds 1.2 GB and lands at 16.4 GB, over a 16 GB card. So a 16 GB card cannot mine and aggregate on this build; it can at most mine and prove shards, with 0.4 GB spare and no full shard measured | The 16 GB chained-aggregation row goes into the sweep; the aggregator step in `prover.rs aggregate_once` gates on card memory (24 GB with the miner running, else pause the miner on that card for the aggregation); the Proving tile says which role the card runs | proving v1 | in work in part (proving agent: full and empty shard peaks with the miner running measuring now, job memminer-pc2-pv1; aggregate_once gets a card-memory gate tonight, no aggregation from a mining card under 20 GB, the tile names the role; pausing the miner for an aggregation refused for tonight, 0.3.12) | +| C2 | 16,751 MiB GPU peak during the chain of 8 with the miner resident (`chain-pc2-pv1c`) | bench-log "proving v1", step 2 chain row | 16 GB cards (RTX 5080, 5060 Ti 16 GB, 4060 Ti 16 GB) | The plan's "a 16 GB card sits 0.4 GB under tonight's peak" is the empty-shard row (15,590 MiB). The chained aggregation adds 1.2 GB and lands at 16.4 GB, over a 16 GB card. So a 16 GB card cannot mine and aggregate on this build; it can at most mine and prove shards, with 0.4 GB spare and no full shard measured | The 16 GB chained-aggregation row goes into the sweep; the aggregator step in `prover.rs aggregate_once` gates on card memory (24 GB with the miner running, else pause the miner on that card for the aggregation); the Proving tile says which role the card runs | proving v1 | closed as measured (proving agent, spcurve-miner-pc2-pv1 219517f: the adopted shard beside the miner 22,210 MiB and 13.2 s, so a 24 GB card has 2.3 GB spare from the fee switch, the number approximate for the card itself because it is the 5090's allocation pattern; the prototype shard 30.1 GB, the 32 GB card alone; aggregation_card gates on the same memory rule, 23,552 MB either role). Open: the first real 24 GB card measurement, and the public line says "24 GB" from a 32 GB card's pattern (D2 wording) | | C3 | 13,816 MiB GPU peak, prover alone, empty shards (`prover-cost-pc2-pv1`) | bench-log "proving v1", step 1 | 12 GB cards (RTX 3060 12 GB, 4070, 5070), the default-on rule (`provedefault.rs` `MIN_VRAM_MB` 11,776) | On the only measurement the prover by itself exceeds a 12 GB card by 1.5 GB, so the 12 GB default gate switches proving on for cards that cannot run it on this build unless the SP1 knobs bring the peak down. The litepaper's "12 GB or more proves full shards" (`site/litepaper.html` 560) and evidence row 16 rest on the sweep | 0.3.11 does not ship the default-on until the sweep has a row under 11.5 GB for a full v1 shard; if none, the gate moves to 24 GB and the public claim is qualified (D2) | proving v1 (sweep in work); public claim: decision | closed as measured (proving agent: the sweep moves no floor, 13.9 GB for an empty shard, 28.3 GB for a full prototype shard; the default is 20 GB mining / 16 GB prove-only; the 12 GB sentence is false on this build and goes to the project lead as D2 with the curve); the v1-shard row is C15 | | C4 | Host RAM 25,550 MB used of 63,132 on PC 2 with the prover on; the WSL2 VM working set 7,915 MB | bench-log "proving v1", step 1 host RAM row | Windows home miners with 16 GB RAM (the common gaming PC); Macs with 16 GB switching the CPU prover on | The prover default has no RAM floor. On Windows the WSL2 VM alone holds 7.9 GB beside the app, the node and the game-class desktop; a 16 GB machine with proving on by default swaps or kills the node. The app's own RSS (engine, node, verifier) is not separated in the measurement, so no requirement can be stated yet | The sweep job records the app's and the node's working sets beside the VM's; `provedefault` reads total RAM and stays off under 32 GB on Windows until measured; the Proving tile and the miner page state the RAM requirement | proving v1; miner UI adbf58b058186a18b (the tile line) | closed in code (proving v1 c2544be: MIN_RAM_MB_WINDOWS 31,000 in provedefault.rs, the tile line names the 7.9 GB VM on a 63 GB PC; unknown RAM is not a gate; the miner UI help line next cut) | | C5 | The app quit for the 0.3.10 update at 20:01:09Z and aborted the chain job at block 3 ("aborted (the app is quitting)"); `prover.rs` kills the child on quit | bench-log "proving v1" chain run 1; `app/igneum-app/src/prover.rs` 266 to 272 | every prover on every update; with proving v1 the aggregator | Tonight's `update-now` to every app aborts whichever shard each prover has in flight (up to 37 s of work each, no payout, re-assigned to nobody until the window passes). Under proving v1 a restart mid-segment loses the aggregator's chain state: the segment goes unproven after T (600 DAA), the aggregator share of 8 blocks is forfeited to the escrow, and the next record must be fresh-chain. With one aggregator on the devnet every app update costs 10 minutes of unproven segments | The update's safe moment waits for the prover's current proof (as it does for the node); the aggregator persists the last segment proof and resumes the chain after a restart; the Updates section says "waits for the proof in flight". For 0.3.10 tonight the aborted shards are an accepted cost, recorded | proving v1; shipper (tonight's rollout note); miner UI (Updates wording) | refused for tonight by the proving agent (persisting the segment proof and holding the update for a proof in flight are 0.3.12; the cost is in the plan as the rule working as written); the shipper carries the aborted-shard count in release-0.3.10.md section 8; the 20:01:09Z abort was NOT 0.3.10 (shipper: nothing published), see C16 | @@ -36,3 +36,6 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C21 | OTA K2: apps embed `OTA_PUBLIC_KEYS = [K1, K2]` and honour a signed `revoked_keys` list; the rig installer verifies the manifest with ONE key (`OTA_PUBLIC_KEY_HEX`, install-rig.sh 168, igneum-update.sh 2) and knows no revocation; the HiveOS package verifies nothing (no manifest, the override reaches it only by republish) | ota-k2 c722579; packaging/linux; packaging/hive | rigs (both packages), the seeds (if they take the manifest) | The day K1 is lost or revoked and the manifest is signed with K2, every rig on the installer refuses the manifest, stops taking overrides, and is isolated at the next height switch; a leaked K1 keeps signing for rigs, because they carry no revocation list. HiveOS rigs get neither keys nor revocation: a republished package is their only path, and nothing checks who published it | The rig installer carries both public keys and the `revoked_keys` rule in the same form as the app (keys.md section 4, step 3), installed and read from the manifest; the HiveOS README states that the package is unsigned and names the sha256 the Flight Sheet URL should be checked against; keys.md lists the rig and HiveOS paths in its table of what trusts K1 | OTA key af2bb75a5436324d0 (keys.md, the shared verifier form); rig installer a3e7b2b03222f5cff | closed for the rig and the docs (rig installer 88f31d9: OTA_PUBLIC_KEYS [K1, K2 slot] embedded, manifest_check mirrors the app's manifest::check with the revoked_keys record at /var/lib/igneum/updates/revoked.json, tested on three throwaway keys; OTA agent 00fcbb5: keys.md table of every path that trusts K1, the HiveOS README unsigned-archive note); open: the wallet (wallet-v1) still trusts K1 alone, listed in keys.md for its owner; merge note: packaging/hive/README.md is edited on both ota-k2 and hive-words | Sweep 3 (20:46 Mac clock) notes, no new row: the 9070 XT dropped off PC 1's bus at about 20:40 UTC (the second eGPU fault of the day); the coordinator stated the consequences (G1 on the gfx1036 stand-in, the 9070 XT v3 hash-rate and power rows owed, every earlier 9070 XT row stands, the AMD sweep queue item blocked, nobody woken) in its 21:05 and 21:10 entries and the rollout plan 7b. The epoch-length plan (ca2-epoch 4300608) carries its own per-tier table (section 7), including the node tier (one core 100% busy on the VDF at the 600-s floor) and the chain (24% of blocks in difficulty settle at the floor). The proving agent's 440fd59 rewrote the litepaper's two proving-gate sentences and evidence rows 15 and 16 on its branch (D2: the project lead approves the draft); the litepaper's card-lifetime sentence is untouched (D3, D4). +| C22 | The SP1 CPU prover peaks at 29.5 to 30.5 GB RSS whatever the shard size and costs 282 s a shard (PC 1, `cpu-prove-pc1-small2`); no zkVM proves on AMD; the analysis concludes "no CPU tier" | `docs/analysis/amd-proving.md` sections 2, 3, 4a (amd-prove f1d7a7d, merged into ca2-coord) | Macs with 16 or 24 GB (the Settings switch turns the CPU prover on); AMD-only Windows and Linux machines (Settings can switch it on); every tier's expectation of the 20% share | provedefault.rs has a RAM gate for Windows (31,000 MB) and none for macOS or Linux, so a 16 GB Mac that flips the switch runs a 30 GB prover into swap and takes the node down with it (the Mac went down at 1% battery on 4 October; this is the same class of outage from memory). The analysis says the tile must say "about five minutes, paid only when no card proves first" but not that the switch is refused under 32 GB. The public tiers: AMD and Apple miners never see the 20% share on this build (now on the site, 1c8439f) | The CPU-prover switch is refused with the reason on every OS under 32 GB of RAM (the Windows constant generalised: macOS reads hw.memsize, Linux /proc/meminfo), and the tile line carries the 5-minute and 30 GB figures | proving v1 acd4f36bc2c07a4e2 | sent | + +Sweep 5 (21:08 Mac clock) notes: the coordinator applied the public proving line on ca2-coord (1c8439f: index, litepaper, miner page: "an NVIDIA card with 24 GB or more proves; AMD and Apple cards mine; a prover for them lands when a zkVM ships one") and the proving agent rewrote the litepaper's two gate sentences on proving-v1 (440fd59): two drafts of overlapping public sentences on two unpushed branches, both for the project lead (D2, D8); the integrator takes one. The measure-lock convoy (a0c3d13: a dead holder, two waiters, cargo tests re-acquiring build slots) is stated by the coordinator with a next-cut task. The S_p CPU shard job was dropped on the 312-s small-shard number (stated). GitHub Actions outage: the 0.3.10 installer builds on PC 1 (stated, 21:01). diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index 7bc99a4a..589d1ce4 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -5,7 +5,7 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | # | Row | What needs deciding | Recommendation | Why | |---|---|---|---|---| | D1 | C1 | The fee switch lands at H = 210,000 about 19:50Z on 6 October, and the 0.3.10 node's export does not carry the switch, so every app prover's shards are refused or vetoed from H. Move H, or race 0.3.11 onto every prover first | If 0.3.11 (with the proving-v1 fork, whose export carries `daaScore` and `feesV1ActivationDaa`) is not on every prover by 16:00Z on 6 October, republish the override with H = tip + 86,400 rounded to the next thousand, re-read the digest on a scratch node, every node in one sweep (the fee-switch plan's own rule for a later H). The coordinator holds the devnet delegation; this note is so the morning does not find proving dark | A dark proving pool on the devnet costs nothing on chain (the escrow keeps it) but every coverage, latency and fleet number measured after H is void | -| D2 | C3 | The litepaper says "12 GB or more proves full shards"; the only measurement puts the prover alone at 13.8 GB on a 32 GB card | The sweep and the S_p curve are in (proving agent, 5 October late): no knob moves the floor; 12 GB proves nothing on SP1 6.8.1, 16 GB proves only empty shards alone, 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner), 32 GB proves everything. Change the sentence to "24 GB or more proves; 32 GB proves and mines on one card" and evidence row 16 to tested-by-the-team on those rows; the 12 GB figure returns only if a smaller GPU server or a smaller shard measures under 12 GB. The proving agent has drafted the two replacement sentences on its branch (app 440fd59, litepaper and evidence rows 15 and 16); nothing is pushed, so the project lead approves or rewrites the draft rather than starting from the measurement | A public number that the first 3060 owner disproves is the FUD the ledger exists to prevent | +| D2 | C3 | The litepaper says "12 GB or more proves full shards"; the only measurement puts the prover alone at 13.8 GB on a 32 GB card | The sweep and the S_p curve are in (proving agent, 5 October late): no knob moves the floor; 12 GB proves nothing on SP1 6.8.1, 16 GB proves only empty shards alone, 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner), 32 GB proves everything. Change the sentence to "24 GB or more proves; 32 GB proves and mines on one card" and evidence row 16 to tested-by-the-team on those rows; the 12 GB figure returns only if a smaller GPU server or a smaller shard measures under 12 GB. The proving agent has drafted the two replacement sentences on its branch (app 440fd59, litepaper and evidence rows 15 and 16); nothing is pushed, so the project lead approves or rewrites the draft rather than starting from the measurement. Two drafts exist: the proving agent's litepaper sentences (proving-v1 440fd59) and the coordinator's site, litepaper and miner-page line (ca2-coord 1c8439f); the integrator keeps one. One marker is owed on either: the 24 GB figure is the 5090's allocation pattern on a 32 GB card (22.2 GB beside the miner), not a measurement on a 24 GB card, and evidence rule 3 wants that said until a 4090 or a 5080-class 24 GB card runs it | A public number that the first 3060 owner disproves is the FUD the ledger exists to prevent | | D3 | C8, C13 | The homepage says "Any 4 GB card" and the litepaper says a 4 GB card mines for about four years and an 8 GB card for more than a decade; the tile says "4B hard cap" while the rule mints about 3.96 billion | Replace with the lifetime table's numbers once the sub-agent lands it: "4 GB cards mine at launch; 8 GB for about six years; 12 GB for about fourteen; the dataset grows half a gigabyte a year" and "cap 4 billion, about 3.96 billion ever minted" | The schedule is public and the arithmetic is one line; a reader will do it | | D4 | C9, C8 | Index mapping at gate 1, now with the lifetime table (`docs/analysis/card-lifetime-2026-10-05.md`): (a) multiply-shift, continuous growth: 4 GB cards out at 1.0 to 1.5 years, 8 GB at 6.3 to 7.5, 12 GB at 12 to 13.5, Apple 8 GB at 2.6 to 3.4; (b) power-of-two steps at years 4, 12, 28, 60: 4 GB to year 4, 8 GB to year 12, 12 GB to year 28 (the cache freed after the build, decided by the coordinator at 22:50), each tier ending on a step day | (b), with the step calendar published on the miners page from day one (the years 4, 12, 28 and 60 named beside the tiers), and the 1 GiB vectors kept. Revised from (a) at 23:0x: the table shows (b) is the only mapping under which the litepaper's "4 GB about four years, 8 GB more than a decade" holds, and a step day known twelve years ahead is a schedule, not an event; the coordinator recommends the same | Under (a) both public sentences are wrong today by 2.5 to 5 years; under (b) they hold and the cliffs are dated | | D5 | C10 | The site's "under 2x" chip claim: the scratch layer leaves the on-die-cache recompute chip at 2.4x at every share; the mixer multiplier x2 brings it to 1.2x at twice the CPU verify cost (about 0.9 ms per warp, half the pool members per core) | Carry the mixer x2 into class v3 on the devnet tonight (the coordinator's gates apply, the verify time per warp measured) and keep the claim; if the coordinator judges it outside the delegation, qualify the claim in level 3 until the project lead says | The claim is the project's first public sentence on chips; it is either true by a measured lever or it is marked | From 2f6c7138b6f82f0c7b48cedc2e6446f7e2786821 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:12:13 +0000 Subject: [PATCH 065/158] evidence and litepaper: the 24 GB tier is measured on the 32 GB card's allocation, not yet on a 24 GB card (consequences C22) Co-Authored-By: Claude Fable 5.1 --- docs/evidence.md | 2 +- site/litepaper.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/evidence.md b/docs/evidence.md index 138aaefc..5748cd2f 100644 --- a/docs/evidence.md +++ b/docs/evidence.md @@ -40,7 +40,7 @@ Versions in the table: `igneum-pow` is the Rust crate at `igneum-pow/Cargo.toml` | 13 | Every node executes the ordered transactions natively and reaches the same state root | Litepaper Proving ("Every node executes ... natively"), Building ("runs on Igneum unchanged") | tested by the team | repo `f5f8c80`, `8dae48b`; fork `devnet-v4` `dc749905`; revm 43.0.3 | `node tools/evm-smoke/smoke.mjs` against a 3-node `igneumd`; `igneum-exec-diff seq.json`; bench-log "execution layer devnet v3" and "devnet-v4 integration" | Simnet, 3 October 2026: 87 of 87 viem checks, state roots identical on 3 nodes at four heights, 57 executed and 19 skipped transactions agree with plain revm, 0 mismatches. Merged node on real proof of work, 4 October 2026: 84 of 85 checks (the miss needs parallel blocks the network did not produce in 36 s), 59 transfers in 10 chain blocks, state roots identical on 3 nodes, `igneum-exec-diff` 0 mismatches over 59 transactions; the live devnet v4 runs this execution layer. Apple M5 Max. The prover is a stub; state is rebuilt from genesis at start; no EVM transaction relay between nodes | none yet | | 14 | Ethereum bytecode runs unchanged, with the documented differences of spec 7.1 | Homepage Build card; litepaper Building | tested by the team | as row 13; fixes `F-exec-A`, `F-exec-B` (spec 7.5) | `tools/evm-smoke/smoke.mjs`: deploy via viem, `increment`, `hashLoop`, `eth_estimateGas`, `eth_getLogs`; `tools/exec-attacks` scenarios 1 and 3; bench-log "execution layer attack fixes" | Deployment, calls, reverts, logs and gas estimates behave as viem expects; chain id 4463; the prototype pgas table gives 0.0095 to 0.028 pgas per gas, below the design's band before calibration, 3 October 2026. 4 October 2026: a transaction that would cross the block's proving budget is refused by the mempool and, if forced in, aborted and charged with its nonce advanced (25 of 25 checks; 30 of 30 malformed cases). Apple M5 Max. The `Prover` precompile, proof records and the shard planner are not in the node | none yet | | 15 | Every block is proven, with the proof landing within about a minute at launch | Homepage stats ("~60 s to a proof"); litepaper Proving; roadmap phase 3 gate | implemented | repo `d7e1f89` (GPU proof), `e01a3cc`, `292e800`, `eedd136` (`proving/igneum-prove`: shard cutter, MPT witnesses, shard and aggregator guests); SP1 6.8.1; spec 7.2, 7.6 | `proving/windows-wsl2` (SETUP-PROVER, PROVE-BLOCK) on the RTX 5090; `igneum-prove-host --mode block` on `proving/fixtures/`; bench-log "proving v0 on the RTX 5090" and "proving: devnet v4 shards" | First GPU proof of an Igneum block, 4 October 2026, RTX 5090 (WSL2, SP1 cuda, mining paused): fixture `block-78-increment` (2 transactions), core proof 1.4 s (7.3 MB, verify 0.221 s), compressed proof 2.7 s (1.27 MB, verify 0.038 s), post-state and receipts roots identical to the node's; 15.7x and 20.6x faster than a loaded M5 Max CPU. The same day on that CPU (load 38 to 47): a three-shard block proved shard by shard and aggregated by recursion, 19 min (1,139 s) end to end, 245 to 337 s per compressed shard proof, every proof verified. What is not there: no proof is produced, carried or checked on the chain (the devnet prover is a stub that signs claims), the proving pool pays nobody (row 21), the block proven is far below one shard, and the 60-second figure remains a design target; the pass mark is the standard in `docs/benchmarks/proving-e2e.md`. Second RTX 5090 run, 4 October 2026 evening (job run-20261004-173115): a full shard at the provisional S_p (6.75 M pgas, 60.8 M cycles) executed in 1.63 s, core proof 8.3 s (18.1 MB), compressed proof 10.9 s (1.27 MB, verify 0.040 s); a two-shard block (13.5 M pgas) proved shard by shard (11.7 s and 10.0 s) and aggregated in 2.2 s, 24 s of GPU stages end to end, every proof verified, six tampered witnesses rejected. The two host defects (an abort after the upload, an idle wait that turned out to be an unbuffered 18 MB proof save through the WSL2 file bridge, 24 minutes) are fixed (ledger P20) 5 October 2026, live devnet with real transactions (bench-log "real transactions, the first non-empty shard proven and paid"): block 72704 shard 0, 29 transfers, 5,800 pgas, proven on PC 2 in 34 s, verified on the Mac in 0.297 s and paid 1.7623 IGN, 53 s after the chain block executed; of about 1,400 blocks in the 20-minute window 36 were proven (the one prover takes the newest shard assigned to it), so "every block" is not yet true; a second content shard (72803, all copies skipped) failed the native-execution veto on the exporter's block structure, fixed with fixtures the same day, the node side pending the 0.3.9 rollout 5 October 2026, evening (bench-log "proving v1"): the aggregated segment record, the chain rule and the unproven rule are implemented behind `proving_v1_activation_daa` (branch proving-v1, not on the devnet before 0.3.11); on the RTX 5090 a chain of 8 consecutive live blocks proved and aggregated by recursion in 135.6 s with the miner on the card (17 s a block, one proof of 1,272,909 bytes attesting all 8, verified in 0.04 s); the 3-node fast-time harness paid a segment record 1.0 s after submission and refused a late one after its deadline (21 checks); the devnet itself, with one prover, carried proofs for 2.4% of blocks over 30 minutes at a block-to-record latency p50 44 s, p99 52 s. The "within about a minute" holds per proven block; "every block" needs 18 mining 5090s or 6 proving-only cards at empty blocks on the measured rates, and the mandatory rule stays off until the share is one | none yet | -| 16 | A 12 GB card proves one shard in about 20 s (WITHDRAWN 5 October 2026: a 24 GB card proves a full shard at the adopted size in 4.3 s; 32 GB mines and proves) | Litepaper Proving ("The proving budget"); roadmap gate 2 | designed | spec 5.1 (Target), 7.6 (`S_p` provisional, 7,500,000 pgas = `B_p` / 4) | `PROVE-SHARD.bat` on the RTX 5090 (pending); the end-to-end standard in `docs/benchmarks/proving-e2e.md`; bench-log "proving: devnet v4 shards" | Measured on a 32 GB card, not yet on a 12 GB card. A shard at the provisional `S_p` is 60.8 M SP1 cycles on the prototype pgas table (9 cycles per pgas, 44 per EVM gas; the modexp entry about 100x its SP1 cost); on an RTX 5090 (4 October 2026 evening, job run-20261004-173115) it executed in 1.63 s and its compressed proof took 10.9 s, verified in 0.040 s, so the 32 GB card is inside the 20 s target with margin. Whether a 12 GB card proves it at all, and in what time, is the next measurement (an RTX 3060 and an RTX 5060 Ti 16 GB are on order). A per-shard time can be met by shrinking the shard, so the project does not use it as a pass mark 5 October 2026, evening (bench-log "proving v1", the S_p curve): measured on the RTX 5090 with SP1 6.8.1's GPU prover, the card to itself, 1-s nvidia-smi samples: an empty shard 13,874 MiB and 2.2 s; a full shard at the ADOPTED v1 budget (30,000 pgas, 4.7 M cycles) 20,434 MiB and 4.3 s; the full prototype shard (6.75 M pgas, 60 M cycles) 28,307 MiB and 10.8 s; beside the miner 15,670 and 30,039 MiB. No environment knob of SP1 moves the 13.9 GB floor and the GPU server has no options of its own, so on this build a 12 GB card proves nothing, a 16 GB card only empty shards, a 24 GB card the adopted full shard alone and beside the miner (22,210 MiB measured, 13.2 s) and a 32 GB card the prototype shard beside the miner with 2.5 GB spare. The litepaper line now says so; the 12 GB gate returns when a prover build with a smaller floor is measured on a 12 GB card | none yet | +| 16 | A 12 GB card proves one shard in about 20 s (WITHDRAWN 5 October 2026: a 24 GB card proves a full shard at the adopted size in 4.3 s; 32 GB mines and proves) | Litepaper Proving ("The proving budget"); roadmap gate 2 | designed | spec 5.1 (Target), 7.6 (`S_p` provisional, 7,500,000 pgas = `B_p` / 4) | `PROVE-SHARD.bat` on the RTX 5090 (pending); the end-to-end standard in `docs/benchmarks/proving-e2e.md`; bench-log "proving: devnet v4 shards" | Measured on a 32 GB card, not yet on a 12 GB card. A shard at the provisional `S_p` is 60.8 M SP1 cycles on the prototype pgas table (9 cycles per pgas, 44 per EVM gas; the modexp entry about 100x its SP1 cost); on an RTX 5090 (4 October 2026 evening, job run-20261004-173115) it executed in 1.63 s and its compressed proof took 10.9 s, verified in 0.040 s, so the 32 GB card is inside the 20 s target with margin. Whether a 12 GB card proves it at all, and in what time, is the next measurement (an RTX 3060 and an RTX 5060 Ti 16 GB are on order). A per-shard time can be met by shrinking the shard, so the project does not use it as a pass mark 5 October 2026, evening (bench-log "proving v1", the S_p curve): measured on the RTX 5090 with SP1 6.8.1's GPU prover, the card to itself, 1-s nvidia-smi samples: an empty shard 13,874 MiB and 2.2 s; a full shard at the ADOPTED v1 budget (30,000 pgas, 4.7 M cycles) 20,434 MiB and 4.3 s; the full prototype shard (6.75 M pgas, 60 M cycles) 28,307 MiB and 10.8 s; beside the miner 15,670 and 30,039 MiB. No environment knob of SP1 moves the 13.9 GB floor and the GPU server has no options of its own, so on this build a 12 GB card proves nothing, a 16 GB card only empty shards, a 24 GB card the adopted full shard alone and beside the miner (22,210 MiB and 13.2 s, measured on the 32 GB card: the 5090's allocation pattern, not yet a run on a 24 GB card) and a 32 GB card the prototype shard beside the miner with 2.5 GB spare. The litepaper line now says so; the 12 GB gate returns when a prover build with a smaller floor is measured on a 12 GB card | none yet | | 17 | The chip resistance target: a chip gains under 2x over a GPU | Litepaper Mining, "What Igneum does not claim"; homepage "no chip can be built for it" | designed | spec 0.2 (Target); O-1.17 | Public benchmark with a leaderboard by card model and a standing bounty, January 2027 (O-1.17); the on-die-SRAM test on the RTX 5090 (R3.5) | A target, not a measurement. Review round 3 priced a recompute chip with the 256 MiB cache on die at about 2.4x, approximate, before the usual chip-versus-GPU integer gain; the design answer (cache larger than any die) is open (spec 1.16) | none yet | | 18 | The chip resistance measurements: the program is random-access bound, not bandwidth bound, and sits beyond a card's on-chip cache | Litepaper Mining ("bound by memory bandwidth", to be corrected), vs RandomX "Measured so far" | tested by the team | repo `aba248d`, `f2a1a64`, `4b95c5e` | RTX 5090 dataset sweep 4 MiB to 1 GiB with `proto-cuda/host.cu`; bench-log "RTX 5090 first run" and "dataset sweep" | At 1 GiB: 228.1 Mhash/s, 23.7 G random loads/s, 94.9 GB/s useful against a 1,638 GB/s dataset fill; inside the 96 MiB L2 (4 and 64 MiB) 1,340 to 1,353 Mhash/s, about 5.8x faster; 104 against 128 loads per hash gives 228 against 185 Mhash/s, proportional. 3 October 2026, RTX 5090, Windows, CUDA 12.8, version 1 programs. Prototype dataset 1 GiB against 2 GB at genesis; a pure random-read microbenchmark (R3 chip designer, attack 2) has not run; the sweep has not been repeated on version 2 | none yet | | 19 | The lottery hash is sound as a hash: uniform output, deterministic, no out-of-bounds read, fuzzed | Litepaper vs RandomX ("Every number above is measured and logged") | tested by the team | repo `c52307e`, `58a5a63`, `b27da39`; `proto-metal/TESTS.md` | `proto-metal/igneum-bench --fuzz --edge --stats --determinism --memcheck`; `--fuzz 2000` on the version 2 generator; `igneum-census`; bench-log "hardening tests", the re-run on the memory-hard dataset, "generator version 2 adopted" | Version 1: 10,200 random programs, 1,305,600 hashes, 0 mismatches; 14 of 14 edge cases; bit frequency within 2.90 sigma, avalanche mean 31.99 to 32.04 of 32; deterministic fingerprint across 5 runs; every dataset read masked, 3 October 2026. Version 2, 4 October 2026: 2,000 random programs through the Metal cross-check, 8,000 warps, 0 mismatches, 128 loads per hash on every program; 20,000-program census, 5.2% rejected (4.1% static, 1.1% dynamic). Apple M5 Max. Statistics are not a security proof; the edge, stats and memcheck sections were not re-run on version 2 (they do not depend on the generator); the seed derivation review (O-1.4) is open; the fuzz set has run on Metal and the CPU only | none yet | diff --git a/site/litepaper.html b/site/litepaper.html index ec63d5d3..e1514a56 100644 --- a/site/litepaper.html +++ b/site/litepaper.html @@ -449,7 +449,7 @@ body.all .pager{display:none}

How a block gets proven

Blocks carry transactions only and make no claim about state. Every node executes the ordered transactions natively at once, so users see their transaction land in about a second. The execution is then split into shards of a fixed proving cost. Shards are assigned by lot to eight provers for ten seconds, then open to anyone; there is no bond. Provers run them on consumer cards, and the shard proofs are folded by recursive aggregation into one proof for the block. That proof lands on-chain within about a minute at launch. Because the proof computes the state from the ordered sequence, no node accepts a block with a wrong state root. Full nodes also execute every block natively and reject a proof record whose result differs from their own execution, so a forged proof is a light-client problem and never a chain split. Implemented: the native-execution check on every carried proof record, proving v0 on the devnet (specification section 7). The emergency path for a soundness bug in the proof system is a human one: a new proof-system version is written by people and activates only on miner signalling. Invalid transactions are skipped by rule, the way Kaspa skips conflicting spends.

The proving budget

-

Gas prices execution. Proving cost is a different number, so Igneum meters it separately: every transaction pays in both dimensions, and each block has a proving-cost budget set in consensus from measured prover throughput. A transaction that is cheap to run and expensive to prove pays for what it costs the provers. Measured on 5 October 2026 (an RTX 5090 under SP1 6.8.1's GPU prover, the shard size the chain adopts from its fee switch, 30,000 proving gas, about 4.7 million prover cycles): one full shard proves in 4.3 seconds and needs 20.4 GB of GPU memory with the card to itself, so a 24 GB card proves full shards and a 12 GB or 16 GB card does not on this prover build, whose floor is 13.9 GB for even an empty shard; mining and proving on one card needs 32 GB today (the prototype-size shard beside the miner peaked at 30.1 GB) and 24 GB once the adopted shard size is live (22.2 GB measured beside the miner, 13.2 seconds a shard). The old 12 GB gate on the roadmap is withdrawn until a prover build with a smaller floor is measured. The first proofs exist: on 4 October 2026 an RTX 5090 proved a small two-transaction block in 1.4 seconds (2.7 seconds compressed), verified in 0.22 and 0.038 seconds, and a laptop CPU proved a three-shard block end to end in 19 minutes. Later that day the same card proved a full shard at the provisional size, 6.75 million prover gas, which executed in 60.8 million cycles: core proof 8.3 seconds, compressed proof 10.9 seconds, verified in 0.040 seconds; a four-shard block took 44.5 seconds of GPU stages end to end. Since 5 October 2026 shards are assigned and proven on the live devnet. The gate asks for a mid-range card, and an RTX 5090 is not one, so the gate stands open. Once the gate is measured, the budget rises by schedule as hardware improves. The proof system is hash-based, which is what runs on consumer cards, and sits behind a versioned interface, so Igneum can adopt a better proof system when one exists by a miner-signalled release, and runs for ever on the current one if none is adopted.

+

Gas prices execution. Proving cost is a different number, so Igneum meters it separately: every transaction pays in both dimensions, and each block has a proving-cost budget set in consensus from measured prover throughput. A transaction that is cheap to run and expensive to prove pays for what it costs the provers. Measured on 5 October 2026 (an RTX 5090 under SP1 6.8.1's GPU prover, the shard size the chain adopts from its fee switch, 30,000 proving gas, about 4.7 million prover cycles): one full shard proves in 4.3 seconds and needs 20.4 GB of GPU memory with the card to itself, so a 24 GB card proves full shards and a 12 GB or 16 GB card does not on this prover build, whose floor is 13.9 GB for even an empty shard; mining and proving on one card needs 32 GB today (the prototype-size shard beside the miner peaked at 30.1 GB) and 24 GB once the adopted shard size is live (22.2 GB beside the miner, 13.2 seconds a shard, measured on the 32 GB card; a 24 GB card has not run it yet). The old 12 GB gate on the roadmap is withdrawn until a prover build with a smaller floor is measured. The first proofs exist: on 4 October 2026 an RTX 5090 proved a small two-transaction block in 1.4 seconds (2.7 seconds compressed), verified in 0.22 and 0.038 seconds, and a laptop CPU proved a three-shard block end to end in 19 minutes. Later that day the same card proved a full shard at the provisional size, 6.75 million prover gas, which executed in 60.8 million cycles: core proof 8.3 seconds, compressed proof 10.9 seconds, verified in 0.040 seconds; a four-shard block took 44.5 seconds of GPU stages end to end. Since 5 October 2026 shards are assigned and proven on the live devnet. The gate asks for a mid-range card, and an RTX 5090 is not one, so the gate stands open. Once the gate is measured, the budget rises by schedule as hardware improves. The proof system is hash-based, which is what runs on consumer cards, and sits behind a versioned interface, so Igneum can adopt a better proof system when one exists by a miner-signalled release, and runs for ever on the current one if none is adopted.

Proving for everyone else

The same miners accept proving jobs from other chains. Rollups post a job, a miner wins it, proves it, and is paid. The job market is permissionless and is Designed, not yet built. At launch a job is paid on the customer's own chain, in the customer's currency, to a payout contract keyed by miner address, because Igneum cannot yet see Ethereum. Settlement in IGN, with 10% of each fee burned, follows when the proof bridge lets Igneum see the payment, in phase two. The Igneum miner client can also bid on other proving networks and take the best price, where a miner chooses to hold their collateral: Boundless provers post ZKC and Succinct provers stake PROVE (approximate, from their documentation). The proving market is small today. Igneum does not depend on it. We know of no other proof-of-work chain selling proofs to other chains.

@@ -557,7 +557,7 @@ body.all .pager{display:none}

The honest bear-market case rests on cost. A miner's card is already running and the power is often domestic, so Igneum miners' marginal cost in the proving market is close to power, which is an edge over data-centre provers and nothing more.

Hardware

-

The dataset starts at 2 GB and grows by half a gigabyte a year, so a 4 GB card mines for about four years and an 8 GB card for more than a decade, approximate. 24 GB proves full shards and 32 GB mines and proves on one card, measured 5 October 2026 on this prover build (a 12 GB card does not prove on it: the GPU prover's floor is 13.9 GB). NVIDIA and AMD both work, because the mining program is generated for the architecture both share and the proof system is hash-based. Apple's chips are GPUs with unified memory, so Macs mine too, at about a fifth of a flagship card: Measured, 26.7 against 123 million hashes a second, an Apple M5 Max beside an RTX 5090 on the live devnet, 4 October 2026. A Mac is a poor miner per dollar. There is no CPU mining lane, on purpose, because CPU mining is what botnets farm. Nodes, wallets and exchanges need no GPU at all.

+

The dataset starts at 2 GB and grows by half a gigabyte a year, so a 4 GB card mines for about four years and an 8 GB card for more than a decade, approximate. 24 GB proves full shards (measured on a 32 GB card's allocation; a 24 GB card has not run it yet) and 32 GB mines and proves on one card, measured 5 October 2026 on this prover build (a 12 GB card does not prove on it: the GPU prover's floor is 13.9 GB). NVIDIA and AMD both work, because the mining program is generated for the architecture both share and the proof system is hash-based. Apple's chips are GPUs with unified memory, so Macs mine too, at about a fifth of a flagship card: Measured, 26.7 against 123 million hashes a second, an Apple M5 Max beside an RTX 5090 on the live devnet, 4 October 2026. A Mac is a poor miner per dollar. There is no CPU mining lane, on purpose, because CPU mining is what botnets farm. Nodes, wallets and exchanges need no GPU at all.

What a miner's hour looks like

The card hashes the lottery continuously. When the client sees a shard or an external job it can win, it switches the card to proving for a few seconds, posts the proof, and goes back to hashing. The client does the switching and the miner sees one balance.

The protocol carries no fee: no dev fund, no cut to any team. Ember, the miner software, takes an optional 1% dev fee, the way other GPU miners do. One block template in 100 is requested with the dev address instead of yours, by a counter, not a random draw, so it is exactly 1 in 100 and anyone can check it from the source or from the chain. One flag turns it off (--dev-fee 0, a switch in the app, a line in the HiveOS config). The miner prints the fee and the address when it starts. Any other client is welcome.

From 37fa956ca539f15dbea49353881d686e122366fd Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:12:23 +0000 Subject: [PATCH 066/158] Consequences ledger: C22 taken in full by the proving agent Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index f13d2e3c..91616cb4 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -36,6 +36,6 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C21 | OTA K2: apps embed `OTA_PUBLIC_KEYS = [K1, K2]` and honour a signed `revoked_keys` list; the rig installer verifies the manifest with ONE key (`OTA_PUBLIC_KEY_HEX`, install-rig.sh 168, igneum-update.sh 2) and knows no revocation; the HiveOS package verifies nothing (no manifest, the override reaches it only by republish) | ota-k2 c722579; packaging/linux; packaging/hive | rigs (both packages), the seeds (if they take the manifest) | The day K1 is lost or revoked and the manifest is signed with K2, every rig on the installer refuses the manifest, stops taking overrides, and is isolated at the next height switch; a leaked K1 keeps signing for rigs, because they carry no revocation list. HiveOS rigs get neither keys nor revocation: a republished package is their only path, and nothing checks who published it | The rig installer carries both public keys and the `revoked_keys` rule in the same form as the app (keys.md section 4, step 3), installed and read from the manifest; the HiveOS README states that the package is unsigned and names the sha256 the Flight Sheet URL should be checked against; keys.md lists the rig and HiveOS paths in its table of what trusts K1 | OTA key af2bb75a5436324d0 (keys.md, the shared verifier form); rig installer a3e7b2b03222f5cff | closed for the rig and the docs (rig installer 88f31d9: OTA_PUBLIC_KEYS [K1, K2 slot] embedded, manifest_check mirrors the app's manifest::check with the revoked_keys record at /var/lib/igneum/updates/revoked.json, tested on three throwaway keys; OTA agent 00fcbb5: keys.md table of every path that trusts K1, the HiveOS README unsigned-archive note); open: the wallet (wallet-v1) still trusts K1 alone, listed in keys.md for its owner; merge note: packaging/hive/README.md is edited on both ota-k2 and hive-words | Sweep 3 (20:46 Mac clock) notes, no new row: the 9070 XT dropped off PC 1's bus at about 20:40 UTC (the second eGPU fault of the day); the coordinator stated the consequences (G1 on the gfx1036 stand-in, the 9070 XT v3 hash-rate and power rows owed, every earlier 9070 XT row stands, the AMD sweep queue item blocked, nobody woken) in its 21:05 and 21:10 entries and the rollout plan 7b. The epoch-length plan (ca2-epoch 4300608) carries its own per-tier table (section 7), including the node tier (one core 100% busy on the VDF at the 600-s floor) and the chain (24% of blocks in difficulty settle at the floor). The proving agent's 440fd59 rewrote the litepaper's two proving-gate sentences and evidence rows 15 and 16 on its branch (D2: the project lead approves the draft); the litepaper's card-lifetime sentence is untouched (D3, D4). -| C22 | The SP1 CPU prover peaks at 29.5 to 30.5 GB RSS whatever the shard size and costs 282 s a shard (PC 1, `cpu-prove-pc1-small2`); no zkVM proves on AMD; the analysis concludes "no CPU tier" | `docs/analysis/amd-proving.md` sections 2, 3, 4a (amd-prove f1d7a7d, merged into ca2-coord) | Macs with 16 or 24 GB (the Settings switch turns the CPU prover on); AMD-only Windows and Linux machines (Settings can switch it on); every tier's expectation of the 20% share | provedefault.rs has a RAM gate for Windows (31,000 MB) and none for macOS or Linux, so a 16 GB Mac that flips the switch runs a 30 GB prover into swap and takes the node down with it (the Mac went down at 1% battery on 4 October; this is the same class of outage from memory). The analysis says the tile must say "about five minutes, paid only when no card proves first" but not that the switch is refused under 32 GB. The public tiers: AMD and Apple miners never see the 20% share on this build (now on the site, 1c8439f) | The CPU-prover switch is refused with the reason on every OS under 32 GB of RAM (the Windows constant generalised: macOS reads hw.memsize, Linux /proc/meminfo), and the tile line carries the 5-minute and 30 GB figures | proving v1 acd4f36bc2c07a4e2 | sent | +| C22 | The SP1 CPU prover peaks at 29.5 to 30.5 GB RSS whatever the shard size and costs 282 s a shard (PC 1, `cpu-prove-pc1-small2`); no zkVM proves on AMD; the analysis concludes "no CPU tier" | `docs/analysis/amd-proving.md` sections 2, 3, 4a (amd-prove f1d7a7d, merged into ca2-coord) | Macs with 16 or 24 GB (the Settings switch turns the CPU prover on); AMD-only Windows and Linux machines (Settings can switch it on); every tier's expectation of the 20% share | provedefault.rs has a RAM gate for Windows (31,000 MB) and none for macOS or Linux, so a 16 GB Mac that flips the switch runs a 30 GB prover into swap and takes the node down with it (the Mac went down at 1% battery on 4 October; this is the same class of outage from memory). The analysis says the tile must say "about five minutes, paid only when no card proves first" but not that the switch is refused under 32 GB. The public tiers: AMD and Apple miners never see the 20% share on this build (now on the site, 1c8439f) | The CPU-prover switch is refused with the reason on every OS under 32 GB of RAM (the Windows constant generalised: macOS reads hw.memsize, Linux /proc/meminfo), and the tile line carries the 5-minute and 30 GB figures | proving v1 acd4f36bc2c07a4e2 | taken in full (proving agent: the prover loop refuses the CPU path on every OS under 32 GB, Settings cannot bypass it, with the line naming the machine's RAM; the tile line on CPU machines carries the 5-minute, 30 GB, paid-only-if-no-card figures; lands in the next app commit after the gate-test build; the "measured on a 32 GB card, not yet on a 24 GB card" marker is in evidence row 16, both litepaper sentences and the plan) | Sweep 5 (21:08 Mac clock) notes: the coordinator applied the public proving line on ca2-coord (1c8439f: index, litepaper, miner page: "an NVIDIA card with 24 GB or more proves; AMD and Apple cards mine; a prover for them lands when a zkVM ships one") and the proving agent rewrote the litepaper's two gate sentences on proving-v1 (440fd59): two drafts of overlapping public sentences on two unpushed branches, both for the project lead (D2, D8); the integrator takes one. The measure-lock convoy (a0c3d13: a dead holder, two waiters, cargo tests re-acquiring build slots) is stated by the coordinator with a next-cut task. The S_p CPU shard job was dropped on the 312-s small-shard number (stated). GitHub Actions outage: the 0.3.10 installer builds on PC 1 (stated, 21:01). From d7c28ec695a4441b7e9f9bac5c92a49ce37890c5 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:12:37 +0000 Subject: [PATCH 067/158] prover loop: the CPU path is refused under 32 GB of RAM on every OS with the measured reason, and its tile line names the cost (consequences C22) Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/src/prover.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/igneum-app/src/prover.rs b/app/igneum-app/src/prover.rs index 69cb4d39..7799e7df 100644 --- a/app/igneum-app/src/prover.rs +++ b/app/igneum-app/src/prover.rs @@ -341,6 +341,7 @@ fn loop_forever(shared: Arc, bin_dir: PathBuf) { let mut attempted: HashSet<(String, u32)> = HashSet::new(); let mut attempted_segments: HashSet = HashSet::new(); let mut tools: Option = None; + let ram_mb = crate::detect::total_ram_mb(); let mut last_probe = Instant::now() - Duration::from_secs(600); let mut submitted: Vec<(u64, String, u32, u128)> = Vec::new(); let mut last_verifier_read = Instant::now() - Duration::from_secs(600); @@ -407,6 +408,20 @@ fn loop_forever(shared: Arc, bin_dir: PathBuf) { } } let Some(t) = tools.as_ref() else { continue }; + // consequences review C22 (5 October 2026): the SP1 CPU prover takes 29.5 to 30.5 GB of RSS and about five + // minutes a shard whatever the shard size (PC 1, bench-log "the SP1 CPU prover on PC 1"); on a machine under + // 32 GB it would swap the node out, so the CPU path is refused here, Settings or not, with the reason + if !t.cuda { + if let Some(ram) = ram_mb { + if ram < crate::provedefault::MIN_RAM_MB_WINDOWS { + set(&shared, |p| { + p.status = "off".into(); + p.message = format!("the CPU prover needs 32 GB of RAM (30 GB measured on PC 1); this machine has {} GB, so proving stays off here", (ram + 512) / 1024); + }); + continue; + } + } + } set(&shared, |p| { p.enabled = true; p.available = true; @@ -520,7 +535,7 @@ fn loop_forever(shared: Arc, bin_dir: PathBuf) { if !ok || !fixture.exists() { return Err(format!("exporter: {}", out.lines().rev().find(|l| !l.trim().is_empty()).unwrap_or("failed"))); } - set(&shared, |p| p.message = if t.cuda { "proving on the GPU".into() } else { "proving on the CPU (slow)".into() }); + set(&shared, |p| p.message = if t.cuda { "proving on the GPU".into() } else { "CPU prover: about five minutes a shard, 30 GB of RAM, paid only when no card proves first".into() }); let prover_env = if t.cuda { "cuda" } else { "cpu" }; let (ok, out) = run_tool(&shared, t, &t.host, &[fix_p, "--mode".into(), "compressed".into(), "--shard".into(), w.shard.to_string(), "--prover".into(), payout.clone(), "--out".into(), res_p], &[("SP1_PROVER", prover_env), ("RUST_LOG", "off")], Duration::from_secs(3 * 3600), &dir.join(format!("prove-{}-{}.log", w.number, w.shard))); if !ok || !results.exists() { From b52c5f4cda30629231cc1ab02a5a78e60bb50616 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:17:11 +0000 Subject: [PATCH 068/158] Consequences ledger: C19 taken by ca2-mixer, the 8x corrected to under 4x pending the run Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 91616cb4..33e2709f 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -31,7 +31,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | # | Number | Source | Tier affected | Consequence | Action | Owner | State | |---|---|---|---|---|---|---|---| | C18 | 5090 0.072 against 9070 XT 0.032 MH/s per pound at list prices (2.2x), 4.9x per watt | read-width.md 4.1 | AMD home miners; the public level 3 copy | The coordinator's 22:30 status entry says "near parity per pound"; the table it cites says 2.2x. The level 3 page is written from the status | The status and level 3 carry the table's figure (2.2x per pound, 4.9x per watt, 7.5x in rate); "near parity" is struck | coordinator | closed (coordinator 6b07776: the status, the rollout plan and the public copy carry 2.2x per pound, 4.9x per watt, 7.5x in rate) | -| C19 | Mixer x4 into class v3: CPU verify 1.6 to 4.8 ms per warp against 0.604 today; pruning depth 108,000 DAA s (spec 02) | status 22:00; M16 table; spec 02 line 17 | every node (the three testnet seeds on small Hetzner VMs, the observer, a laptop node); IBD; pools | A new node verifies every header in the pruning window on one core: 108,000 x 4.8 ms = 8.6 min at x4 against 1.1 min today (a 30-s block-time budget at 1 block/s is unaffected: 4.8 ms per block is 0.5% of a core). Every miner's own `cpu re-check` of a found hash and every pool share verification cost the same 8x; the 10 ms gate (ledger M9) keeps 2x of margin at the slow end, which is what Counter ASIC 3.0 has left to spend. On the 2019-class laptop core the evidence table names (rule 3) the figure is unmeasured and may pass 10 ms | The ca2-mixer document carries: ms per warp on the M5 Max core AND a scaled 2019-class figure (marked approximate), the pruning-window IBD minutes per tier, pool shares per core per second, and the gate margin left for 3.0; the seeds' header-verify load is checked in the testnet go checklist | ca2-mixer af345b1e2c541ffbb; coordinator | in work (coordinator: with ca2-mixer; the seeds' header-verify load into testnet-go.md) | +| C19 | Mixer x4 into class v3: CPU verify 1.6 to 4.8 ms per warp against 0.604 today; pruning depth 108,000 DAA s (spec 02) | status 22:00; M16 table; spec 02 line 17 | every node (the three testnet seeds on small Hetzner VMs, the observer, a laptop node); IBD; pools | A new node verifies every header in the pruning window on one core: 108,000 x 4.8 ms = 8.6 min at x4 against 1.1 min today (a 30-s block-time budget at 1 block/s is unaffected: 4.8 ms per block is 0.5% of a core). Every miner's own `cpu re-check` of a found hash and every pool share verification cost the same 8x; the 10 ms gate (ledger M9) keeps 2x of margin at the slow end, which is what Counter ASIC 3.0 has left to spend. On the 2019-class laptop core the evidence table names (rule 3) the figure is unmeasured and may pass 10 ms | The ca2-mixer document carries: ms per warp on the M5 Max core AND a scaled 2019-class figure (marked approximate), the pruning-window IBD minutes per tier, pool shares per core per second, and the gate margin left for 3.0; the seeds' header-verify load is checked in the testnet go checklist | ca2-mixer af345b1e2c541ffbb; coordinator | taken in full (ca2-mixer, docs/plans/mixer-x4.md section 6.5: measured ms per warp v2 and v3 on the M5 Max core, a 2019-class figure scaled and marked approximate, IBD minutes per tier over the 108,000-header window, pool shares per core and members per core v2 and v3 so spec 09's 2,270 is re-cut, the gate margin in ms; the timing run queued behind the Mac measure lock). Correction taken: the x4 multiplies only the ALU part of the verifier, the 8 dependent cache misses per item are unchanged, so the measured ratio is under 4x, not the 8x this row assumed from the M16 range; the row's minutes and share counts are upper bounds until the run | | C20 | Layer 9: the epoch length as an era parameter, 600 to 7,200 DAA s (10 min to 2 h), base 3,600 | status 22:30 and 23:00 | rigs (HiveOS and the rig installer), Macs, pools, the seed path | Both rig miners run `--exit-on-seed-change` and re-export the pack on exit 42 (h-run.sh 56 to 61, igneum-miner.sh 67 to 78): at a 10-minute epoch every card's miner restarts six times an hour with a pack export each time, and the restart gap is lost hashing; the app's prepare-ahead path does not restart. The Mac fleet's prepare pause (35 s an hour at one epoch an hour, bench-log M11) becomes 3.5 min an hour, 6%. The 10-minute seed VDF (spec 04) equals the shortest epoch, so the seed for epoch n+1 is known only as epoch n starts, which is the compile-ahead window the agent must measure per card (the 5090 compiled in 1,285 ms, the 9070 XT unmeasured). A pool's `job` cadence and the dev-fee counter are unaffected | The epoch-length document carries a per-tier row: rig restart cost per epoch length (and the fix: prepare-ahead in the rig scripts, no exit 42 path), the Mac pause share, the compile-ahead margin per card at 600 DAA s against the VDF; the rig installer removes `--exit-on-seed-change` in favour of the prepare path before layer 9 can draw a short epoch | ca2-epoch a32a3ece66c02417a; rig installer | closed with a correction (ca2-epoch 4300608, epoch-length.md sections 6.3, 7, 9): the rig scripts pass --prepare-packs as well, so a worker with prepare support swaps in place and exit 42 is the fallback on a prepare MISS (the loaded iGPU missed 2 of 10, M11), not a restart every epoch; the risk at 600 s is one restart plus export plus inline compile per missed boundary; the Mac race pause is 6.3% of a 600-s epoch (race default off); the program is known a full epoch ahead at every length (lead and T_epoch fixed, option A); the 9070 XT compile is OWED (no prepared line from gfx1201 in any upload); the rig installer (88f31d9) confirms the rig already takes the prepare path: exit 42 fires only when a worker's ready line lacks "prepare 1", and both shipped workers answer it; documented in its README, no code change | | C21 | OTA K2: apps embed `OTA_PUBLIC_KEYS = [K1, K2]` and honour a signed `revoked_keys` list; the rig installer verifies the manifest with ONE key (`OTA_PUBLIC_KEY_HEX`, install-rig.sh 168, igneum-update.sh 2) and knows no revocation; the HiveOS package verifies nothing (no manifest, the override reaches it only by republish) | ota-k2 c722579; packaging/linux; packaging/hive | rigs (both packages), the seeds (if they take the manifest) | The day K1 is lost or revoked and the manifest is signed with K2, every rig on the installer refuses the manifest, stops taking overrides, and is isolated at the next height switch; a leaked K1 keeps signing for rigs, because they carry no revocation list. HiveOS rigs get neither keys nor revocation: a republished package is their only path, and nothing checks who published it | The rig installer carries both public keys and the `revoked_keys` rule in the same form as the app (keys.md section 4, step 3), installed and read from the manifest; the HiveOS README states that the package is unsigned and names the sha256 the Flight Sheet URL should be checked against; keys.md lists the rig and HiveOS paths in its table of what trusts K1 | OTA key af2bb75a5436324d0 (keys.md, the shared verifier form); rig installer a3e7b2b03222f5cff | closed for the rig and the docs (rig installer 88f31d9: OTA_PUBLIC_KEYS [K1, K2 slot] embedded, manifest_check mirrors the app's manifest::check with the revoked_keys record at /var/lib/igneum/updates/revoked.json, tested on three throwaway keys; OTA agent 00fcbb5: keys.md table of every path that trusts K1, the HiveOS README unsigned-archive note); open: the wallet (wallet-v1) still trusts K1 alone, listed in keys.md for its owner; merge note: packaging/hive/README.md is edited on both ota-k2 and hive-words | From 7053d206a15552c5380646c4ba437c2907965f9d Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:25:09 +0000 Subject: [PATCH 069/158] counter-asic-2-node.md: the (program id, era seed) identity, the measured digest flip, the Mac suite results Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-2-node.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index 5eff0634..ce4fba83 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -43,7 +43,7 @@ One epoch has one program (spec 01 section 1.12), so the switch keys on the EPOC The unit tests `program_class_switch_rounds_up_to_the_epoch_boundary` (consensus-core) and `override_params_carry_the_program_class_v3_activation` (params) pin these rows and sweep every activation 0..399 at L = 60. -The digest: the field enters `consensus_digest` unconditionally, so the digest flips the moment a binary carrying the field (at never) runs, exactly as the finality v3 field did. This is intended: every node must carry the object before any node reaches the height, and a node without the field is refused at the handshake (rollout section 1, order step 1). +The digest: the field (and `pow_genesis_dataset_log2`) enters `consensus_digest` unconditionally, so the digest flips the moment a binary carrying the field (at never) runs, exactly as the finality v3 field did. This is intended: every node must carry the object before any node reaches the height, and a node without the field is refused at the handshake (rollout section 1, order step 1). Measured: the devnet digest with no override file moves from `9409dedac4bf9f0f...` (0.3.10) to `c562d70e1428c9789823cc40067623b4767f7c555ce7ff4ea11c1498f013ef6c` (0.3.11; the pinned value of `consensus_digest_keeps_the_0_3_5_value_until_the_fee_switch_is_set`), which is the expected digest of a scratch node at the publish. ## 3. The era stand-in @@ -67,6 +67,8 @@ One function per path: `HeaderProcessor::era_seed` (the header's era from its DA | `seeds.txt` | `epoch_seed_hex`, `day_seed_hex`, `day_index` | plus `program_class v3`, `era_seed_hex ` | | template `pow_epoch` | `programClass 2` | `programClass 3`, `nextProgramClass`, `programClassV3ActivationDaa`, `eraIndex`, `eraSeed` | +A v3 program's identity is the pair (program id, era seed): the id covers the generator, the seed words and the attempt (spec 01 section 1.4.6, unchanged), so every era of one epoch seed shares one id, and the era seed, carried by the pack (`IGNEUM_ERA_SEED_HEX`) and the job line (`era=`), tells them apart. The workers and `packcheck` compare both. + A v2 line and a v2 pack are byte for byte what the workers read before this branch (the tokens are sent only for a v3 epoch), so a 0.3.10 worker on a 0.3.11 miner mines v2 epochs unchanged and refuses nothing until the switch; by the switch every worker is 0.3.11 (rollout order). Refusals: `pf_load` refuses a generator that is not 2 or 3 (`error 0 pack : program pack generator N is not a generator version this worker runs (2 or 3)`, the exit-44 path of 05ef0fa3: the miner rebuilds the pack before the restart). A job of class v3 against a resident v2 pack of the same seeds answers `need ` and `error pack : program class mismatch: this pack is class v2, the job names class v3 (export the pack again)`; the miner's `need` handling prepares the pair again, `write_pack_checked` writes a v3 pack (checked with `verify_pack_dir_chain` before the worker hears of it), and the prepared v3 pair wins over the resident v2 pair because the pair identity now carries the class. The Metal worker answers `error program class v3 is not implemented by this worker` until the Swift generator carries version 3. @@ -84,8 +86,8 @@ Result: PENDING (filled by the run; see the status file and the final report). | igneum-pow `cargo test --release` | 42 unit + 11 pack tests, including `program_classes`, `program_class_and_era_are_checked`, the pinned-pack diffs | pass (Mac, 5 Oct 2026) | | `proto-cuda/nvrtc/emu/packfile-test.sh` | 13 checks: the attempt rule, the generator rule, a v3 pack with its era, the token matcher | pass (Mac) | | `host.c`, `worker.cpp` (emulation), `main.swift` | syntax / compile | pass (Mac) | -| fork `cargo test -p kaspa-pow --features igneum-pow` | the engine tests including the v3 one | see the final report | -| fork `cargo test -p kaspa-consensus-core` | the switch rounding, the era clock, the params and digest tests | see the final report | +| fork `cargo test -p kaspa-pow --features igneum-pow` | 14 engine tests including `program_class_v3_seeds_hash_their_own_program_over_their_own_cache` | pass (Mac, fork 79bd8e10) | +| fork `cargo test -p kaspa-consensus-core` | 108 + 7: the switch rounding, the era clock, the params, digest and fast-time file tests | pass (Mac, fork 79bd8e10) | | PC 2 `build-job.mjs` suites | `kaspa-consensus-core igneum-exec kaspa-pow kaspa-consensus igneum-miner` + `igneum-app` | the coordinator publishes on its go | ## 7. Unverified, and what is owed From 48c13b5f1e98519a0a567711c2850ab25c8efa1a Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:30:25 +0000 Subject: [PATCH 070/158] Consequences ledger round 3: C23 the mixer's daily build on the iGPU tier, C24 the bash-body-in-PowerShell class, C25 tuning priors under the signing key; D5 re-cut to the measured chip row Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 8 ++++++++ docs/plans/consequences-decisions.md | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 33e2709f..0e17e478 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -39,3 +39,11 @@ Sweep 3 (20:46 Mac clock) notes, no new row: the 9070 XT dropped off PC 1's bus | C22 | The SP1 CPU prover peaks at 29.5 to 30.5 GB RSS whatever the shard size and costs 282 s a shard (PC 1, `cpu-prove-pc1-small2`); no zkVM proves on AMD; the analysis concludes "no CPU tier" | `docs/analysis/amd-proving.md` sections 2, 3, 4a (amd-prove f1d7a7d, merged into ca2-coord) | Macs with 16 or 24 GB (the Settings switch turns the CPU prover on); AMD-only Windows and Linux machines (Settings can switch it on); every tier's expectation of the 20% share | provedefault.rs has a RAM gate for Windows (31,000 MB) and none for macOS or Linux, so a 16 GB Mac that flips the switch runs a 30 GB prover into swap and takes the node down with it (the Mac went down at 1% battery on 4 October; this is the same class of outage from memory). The analysis says the tile must say "about five minutes, paid only when no card proves first" but not that the switch is refused under 32 GB. The public tiers: AMD and Apple miners never see the 20% share on this build (now on the site, 1c8439f) | The CPU-prover switch is refused with the reason on every OS under 32 GB of RAM (the Windows constant generalised: macOS reads hw.memsize, Linux /proc/meminfo), and the tile line carries the 5-minute and 30 GB figures | proving v1 acd4f36bc2c07a4e2 | taken in full (proving agent: the prover loop refuses the CPU path on every OS under 32 GB, Settings cannot bypass it, with the line naming the machine's RAM; the tile line on CPU machines carries the 5-minute, 30 GB, paid-only-if-no-card figures; lands in the next app commit after the gate-test build; the "measured on a 32 GB card, not yet on a 24 GB card" marker is in evidence row 16, both litepaper sentences and the plan) | Sweep 5 (21:08 Mac clock) notes: the coordinator applied the public proving line on ca2-coord (1c8439f: index, litepaper, miner page: "an NVIDIA card with 24 GB or more proves; AMD and Apple cards mine; a prover for them lands when a zkVM ships one") and the proving agent rewrote the litepaper's two gate sentences on proving-v1 (440fd59): two drafts of overlapping public sentences on two unpushed branches, both for the project lead (D2, D8); the integrator takes one. The measure-lock convoy (a0c3d13: a dead holder, two waiters, cargo tests re-acquiring build slots) is stated by the coordinator with a next-cut task. The S_p CPU shard job was dropped on the 312-s small-shard number (stated). GitHub Actions outage: the 0.3.10 installer builds on PC 1 (stated, 21:01). + +## Round 3 (21:30 to 22:00 Mac clock) + +| # | Number | Source | Tier affected | Consequence | Action | Owner | State | +|---|---|---|---|---|---|---|---| +| C23 | Mixer x4 or x8: the 5090's daily dataset build 13.4 ms at x1 (54 ms at x4, about 107 ms at x8); the integrated gfx1036 builds the dataset per PREPARE, 7 to 12 s at x1 and 55 to 124 s under CPU load (epoch-length.md section 7); the x8 rule: "the daily build under 1 s on every card we own" | status 21:25 and 21:27; epoch-length.md section 7 | integrated GPUs (the iGPU tier), 8 GB cards (about a tenth of a 5090's rate), rigs with one weak card | The x8 rule names "every card we own": the gfx1036 is one, and at x8 its per-prepare build is 56 to 96 s per epoch (7 to 16 min under load), so it misses every epoch boundary and falls to the exit-42 path; at x4 it is 28 to 48 s per hour (0.8 to 1.3%) or 4 to 8 min under load. An 8 GB discrete card scales at about a tenth of the 5090: about 1 s at x8, on the rule's edge. The per-day dataset reuse in the worker (owed in epoch-length.md section 9) is what makes the mixer cheap for the iGPU tier; without it the mixer multiplies a per-epoch cost | The mixer decision names the gfx1036 and an 8 GB-class scaled row beside the 5090, M5 Max and 9070 XT in the "under 1 s" check, and the per-day dataset reuse in the worker lands before (or with) class v3, or the iGPU tier is stated as "mines v3 with a restart per epoch" on the level 3 page | ca2-mixer af345b1e2c541ffbb; coordinator | sent | +| C24 | Two PowerShell job scripts lost a quote inside an inline bash body tonight: amd-prove's awk program (cpu-prove-pc1-small, exit 0 with nothing proved) and the 0.3.10 installer's `bash -c` string (fb-installer-pc1-3, exit 2 in 4 s); amd-prove added `tools/amd-prove/check-job-bash.sh` (bash -n on its own scripts' bash bodies) | amd-proving.md section 2; status 21:28 | every PC job; the morning's rollouts | The class (CLAUDE.md, 5 October: fix the class the same day, add a check that fails when the shape comes back) is "a bash body inside a PowerShell job string"; the check exists for one agent's scripts and did not cover the shipper's, which failed the same way an hour later | A repo-wide CI check: every `.ps1` under relay/playbooks and tools that carries a bash body (`wsl ... bash -c`, `bash -lc`, here-strings fed to bash) has that body extracted and passed through `bash -n`; the shipper's rule (the WSL part as a file run with `bash `) written in packaging/README-ship.md as the convention | sub-agent (bounded, no owner); coordinator told | in work | +| C25 | Ember Tune: the signed manifest carries per-card-model tuning priors (power limit and core clock) that a new card applies and confirms in two steps; K1 signs it | ember-tune.md sections 4 and 5; bench-log Ember Tune entry | every NVIDIA and AMD card on the app; the keys | The manifest now sets clocks and power limits on every user's card, so the signing key's blast radius grew: a signed prior can underclock the fleet or push a card model to its power ceiling. The plan's clamps (inside power.min_limit / max_limit and clocks.max.gr, the confirm step, a faulted step reverted) are the bound; keys.md's "what K1 signs" table (ota-k2 00fcbb5) predates the priors and does not list them | ember-tune.md section 5 states the bound in one line (a prior can never set a value outside the card's own reported limits, and never a memory clock), with the test that proves it; keys.md's table gains the tuning priors under K1 with that bound | Ember Tune a855dcc4bd05e0615; OTA key agent (the table row) | sent | diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index 589d1ce4..d7ecdb2e 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -8,7 +8,7 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | D2 | C3 | The litepaper says "12 GB or more proves full shards"; the only measurement puts the prover alone at 13.8 GB on a 32 GB card | The sweep and the S_p curve are in (proving agent, 5 October late): no knob moves the floor; 12 GB proves nothing on SP1 6.8.1, 16 GB proves only empty shards alone, 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner), 32 GB proves everything. Change the sentence to "24 GB or more proves; 32 GB proves and mines on one card" and evidence row 16 to tested-by-the-team on those rows; the 12 GB figure returns only if a smaller GPU server or a smaller shard measures under 12 GB. The proving agent has drafted the two replacement sentences on its branch (app 440fd59, litepaper and evidence rows 15 and 16); nothing is pushed, so the project lead approves or rewrites the draft rather than starting from the measurement. Two drafts exist: the proving agent's litepaper sentences (proving-v1 440fd59) and the coordinator's site, litepaper and miner-page line (ca2-coord 1c8439f); the integrator keeps one. One marker is owed on either: the 24 GB figure is the 5090's allocation pattern on a 32 GB card (22.2 GB beside the miner), not a measurement on a 24 GB card, and evidence rule 3 wants that said until a 4090 or a 5080-class 24 GB card runs it | A public number that the first 3060 owner disproves is the FUD the ledger exists to prevent | | D3 | C8, C13 | The homepage says "Any 4 GB card" and the litepaper says a 4 GB card mines for about four years and an 8 GB card for more than a decade; the tile says "4B hard cap" while the rule mints about 3.96 billion | Replace with the lifetime table's numbers once the sub-agent lands it: "4 GB cards mine at launch; 8 GB for about six years; 12 GB for about fourteen; the dataset grows half a gigabyte a year" and "cap 4 billion, about 3.96 billion ever minted" | The schedule is public and the arithmetic is one line; a reader will do it | | D4 | C9, C8 | Index mapping at gate 1, now with the lifetime table (`docs/analysis/card-lifetime-2026-10-05.md`): (a) multiply-shift, continuous growth: 4 GB cards out at 1.0 to 1.5 years, 8 GB at 6.3 to 7.5, 12 GB at 12 to 13.5, Apple 8 GB at 2.6 to 3.4; (b) power-of-two steps at years 4, 12, 28, 60: 4 GB to year 4, 8 GB to year 12, 12 GB to year 28 (the cache freed after the build, decided by the coordinator at 22:50), each tier ending on a step day | (b), with the step calendar published on the miners page from day one (the years 4, 12, 28 and 60 named beside the tiers), and the 1 GiB vectors kept. Revised from (a) at 23:0x: the table shows (b) is the only mapping under which the litepaper's "4 GB about four years, 8 GB more than a decade" holds, and a step day known twelve years ahead is a schedule, not an event; the coordinator recommends the same | Under (a) both public sentences are wrong today by 2.5 to 5 years; under (b) they hold and the cliffs are dated | -| D5 | C10 | The site's "under 2x" chip claim: the scratch layer leaves the on-die-cache recompute chip at 2.4x at every share; the mixer multiplier x2 brings it to 1.2x at twice the CPU verify cost (about 0.9 ms per warp, half the pool members per core) | Carry the mixer x2 into class v3 on the devnet tonight (the coordinator's gates apply, the verify time per warp measured) and keep the claim; if the coordinator judges it outside the delegation, qualify the claim in level 3 until the project lead says | The claim is the project's first public sentence on chips; it is either true by a measured lever or it is marked | +| D5 | C10 | The site's "under 2x" chip claim: the scratch layer leaves the on-die-cache recompute chip at 2.4x at every share; the mixer multiplier x2 brings it to 1.2x at twice the CPU verify cost (about 0.9 ms per warp, half the pool members per core) | Overtaken by the coordinator's delegated decisions (21:50 to 21:28 Mac clock): the public claim was qualified, the mixer x4 is in class v3 (chip row 0.61x bare, 1.84x with a 3x fixed-function factor, 1.53x at equal silicon with the mirror deducted: "under 2x" holds on the equal-silicon convention and is thin), and x8 is built and measured beside it (0.92x with the factor, from the M16 table); x8 goes in if the verify stays under 10 ms and the daily build under 1 s on every card we own (C23 asks that the iGPU tier be named in that rule). For the project lead: the public sentence after tonight reads "under 2x at equal silicon, measured against the strongest chip we can model" with the convention named, or it does not read "under 2x" | The claim is the project's first public sentence on chips; it is either true by a measured lever or it is marked | | D6 | C11 | Whether class v3 should favour AMD (fewer, wider loads per hash) at a cost to the 5090's latency-bound share, or accept that AMD cards mine at about a seventh of a 5090 and 4.5x the electricity per hash | Accept it for v3 and say so on the miners page ("NVIDIA first; AMD mines at a lower rate per watt on this class"); open the AMD question as a Counter ASIC 3.0 item with its own measurement | Tonight's rule was the project lead's and the 5090 margin is the anti-chip argument; AMD's position is a public-copy question, not a gate | | D7 | C13 | Same as D3's supply wording | with D3 | | | D8 | C7 | The miner page says "One click: install, start, the card mines and proves" (site/miner.html 7, 13, 21; site/index.html 484). On HiveOS and the rig installer a rig mines only until a Linux prover build is published, and on the app a 12 GB card mines only, a 16 GB card proves with the miner paused, 20 GB and up does both (the sweep of 5 October, before the v1-shard row) | Qualify the sentence on the miner page and the homepage card: "the card mines; 24 GB cards prove too, 32 GB does both at once; rigs mine until the Linux prover ships". The same page's "a visible 1% software fee you can switch off" becomes "solo mining carries a 1% software fee you can switch off; in a pool the pool's own fee is the only one" once pool-v0 ships (the pool agent fixed the rule: no software dev fee in pool mode) | The sentence is the product's first promise and tonight's measurement bounds it by card | From ac55f878e1a498ff42623a9fcfd212f15018cf52 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:31:11 +0000 Subject: [PATCH 071/158] Consequences ledger: C23 taken by the coordinator, C24 in work with the no-skip rule Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 0e17e478..7cf7be65 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -44,6 +44,6 @@ Sweep 5 (21:08 Mac clock) notes: the coordinator applied the public proving line | # | Number | Source | Tier affected | Consequence | Action | Owner | State | |---|---|---|---|---|---|---|---| -| C23 | Mixer x4 or x8: the 5090's daily dataset build 13.4 ms at x1 (54 ms at x4, about 107 ms at x8); the integrated gfx1036 builds the dataset per PREPARE, 7 to 12 s at x1 and 55 to 124 s under CPU load (epoch-length.md section 7); the x8 rule: "the daily build under 1 s on every card we own" | status 21:25 and 21:27; epoch-length.md section 7 | integrated GPUs (the iGPU tier), 8 GB cards (about a tenth of a 5090's rate), rigs with one weak card | The x8 rule names "every card we own": the gfx1036 is one, and at x8 its per-prepare build is 56 to 96 s per epoch (7 to 16 min under load), so it misses every epoch boundary and falls to the exit-42 path; at x4 it is 28 to 48 s per hour (0.8 to 1.3%) or 4 to 8 min under load. An 8 GB discrete card scales at about a tenth of the 5090: about 1 s at x8, on the rule's edge. The per-day dataset reuse in the worker (owed in epoch-length.md section 9) is what makes the mixer cheap for the iGPU tier; without it the mixer multiplies a per-epoch cost | The mixer decision names the gfx1036 and an 8 GB-class scaled row beside the 5090, M5 Max and 9070 XT in the "under 1 s" check, and the per-day dataset reuse in the worker lands before (or with) class v3, or the iGPU tier is stated as "mines v3 with a restart per epoch" on the level 3 page | ca2-mixer af345b1e2c541ffbb; coordinator | sent | -| C24 | Two PowerShell job scripts lost a quote inside an inline bash body tonight: amd-prove's awk program (cpu-prove-pc1-small, exit 0 with nothing proved) and the 0.3.10 installer's `bash -c` string (fb-installer-pc1-3, exit 2 in 4 s); amd-prove added `tools/amd-prove/check-job-bash.sh` (bash -n on its own scripts' bash bodies) | amd-proving.md section 2; status 21:28 | every PC job; the morning's rollouts | The class (CLAUDE.md, 5 October: fix the class the same day, add a check that fails when the shape comes back) is "a bash body inside a PowerShell job string"; the check exists for one agent's scripts and did not cover the shipper's, which failed the same way an hour later | A repo-wide CI check: every `.ps1` under relay/playbooks and tools that carries a bash body (`wsl ... bash -c`, `bash -lc`, here-strings fed to bash) has that body extracted and passed through `bash -n`; the shipper's rule (the WSL part as a file run with `bash `) written in packaging/README-ship.md as the convention | sub-agent (bounded, no owner); coordinator told | in work | +| C23 | Mixer x4 or x8: the 5090's daily dataset build 13.4 ms at x1 (54 ms at x4, about 107 ms at x8); the integrated gfx1036 builds the dataset per PREPARE, 7 to 12 s at x1 and 55 to 124 s under CPU load (epoch-length.md section 7); the x8 rule: "the daily build under 1 s on every card we own" | status 21:25 and 21:27; epoch-length.md section 7 | integrated GPUs (the iGPU tier), 8 GB cards (about a tenth of a 5090's rate), rigs with one weak card | The x8 rule names "every card we own": the gfx1036 is one, and at x8 its per-prepare build is 56 to 96 s per epoch (7 to 16 min under load), so it misses every epoch boundary and falls to the exit-42 path; at x4 it is 28 to 48 s per hour (0.8 to 1.3%) or 4 to 8 min under load. An 8 GB discrete card scales at about a tenth of the 5090: about 1 s at x8, on the rule's edge. The per-day dataset reuse in the worker (owed in epoch-length.md section 9) is what makes the mixer cheap for the iGPU tier; without it the mixer multiplies a per-epoch cost | The mixer decision names the gfx1036 and an 8 GB-class scaled row beside the 5090, M5 Max and 9070 XT in the "under 1 s" check, and the per-day dataset reuse in the worker lands before (or with) class v3, or the iGPU tier is stated as "mines v3 with a restart per epoch" on the level 3 page | ca2-mixer af345b1e2c541ffbb; coordinator | taken (coordinator: the x8 table carries the gfx1036 row and a scaled 8 GB-class row; the under-1-s rule applies to the discrete cards' daily build; for the integrated tier either the per-day dataset reuse in the three workers lands with class v3, asked of the mixer and node agents as a bounded change tonight, or the level 3 page says the iGPU tier mines v3 with a restart per epoch; recorded with the x4/x8 choice) | +| C24 | Two PowerShell job scripts lost a quote inside an inline bash body tonight: amd-prove's awk program (cpu-prove-pc1-small, exit 0 with nothing proved) and the 0.3.10 installer's `bash -c` string (fb-installer-pc1-3, exit 2 in 4 s); amd-prove added `tools/amd-prove/check-job-bash.sh` (bash -n on its own scripts' bash bodies) | amd-proving.md section 2; status 21:28 | every PC job; the morning's rollouts | The class (CLAUDE.md, 5 October: fix the class the same day, add a check that fails when the shape comes back) is "a bash body inside a PowerShell job string"; the check exists for one agent's scripts and did not cover the shipper's, which failed the same way an hour later | A repo-wide CI check: every `.ps1` under relay/playbooks and tools that carries a bash body (`wsl ... bash -c`, `bash -lc`, here-strings fed to bash) has that body extracted and passed through `bash -n`; the shipper's rule (the WSL part as a file run with `bash `) written in packaging/README-ship.md as the convention | sub-agent (bounded, no owner); coordinator told | in work (sub-agent bash-body-check; the coordinator: no collision with PC 1, the shipper moved its WSL part to a file for attempt 4, the repro agent added a PowerShell 5.1 parse gate for bench/; the check fails on an unextractable body rather than skipping it) | | C25 | Ember Tune: the signed manifest carries per-card-model tuning priors (power limit and core clock) that a new card applies and confirms in two steps; K1 signs it | ember-tune.md sections 4 and 5; bench-log Ember Tune entry | every NVIDIA and AMD card on the app; the keys | The manifest now sets clocks and power limits on every user's card, so the signing key's blast radius grew: a signed prior can underclock the fleet or push a card model to its power ceiling. The plan's clamps (inside power.min_limit / max_limit and clocks.max.gr, the confirm step, a faulted step reverted) are the bound; keys.md's "what K1 signs" table (ota-k2 00fcbb5) predates the priors and does not list them | ember-tune.md section 5 states the bound in one line (a prior can never set a value outside the card's own reported limits, and never a memory clock), with the test that proves it; keys.md's table gains the tuning priors under K1 with that bound | Ember Tune a855dcc4bd05e0615; OTA key agent (the table row) | sent | From 1df7ae9b089d38abdb0b7c9593a00d3f01f8a9a4 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:31:24 +0000 Subject: [PATCH 072/158] Consequences ledger: C25 closed (Ember Tune 5d7ced9) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 7cf7be65..03bc6edf 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -46,4 +46,4 @@ Sweep 5 (21:08 Mac clock) notes: the coordinator applied the public proving line |---|---|---|---|---|---|---|---| | C23 | Mixer x4 or x8: the 5090's daily dataset build 13.4 ms at x1 (54 ms at x4, about 107 ms at x8); the integrated gfx1036 builds the dataset per PREPARE, 7 to 12 s at x1 and 55 to 124 s under CPU load (epoch-length.md section 7); the x8 rule: "the daily build under 1 s on every card we own" | status 21:25 and 21:27; epoch-length.md section 7 | integrated GPUs (the iGPU tier), 8 GB cards (about a tenth of a 5090's rate), rigs with one weak card | The x8 rule names "every card we own": the gfx1036 is one, and at x8 its per-prepare build is 56 to 96 s per epoch (7 to 16 min under load), so it misses every epoch boundary and falls to the exit-42 path; at x4 it is 28 to 48 s per hour (0.8 to 1.3%) or 4 to 8 min under load. An 8 GB discrete card scales at about a tenth of the 5090: about 1 s at x8, on the rule's edge. The per-day dataset reuse in the worker (owed in epoch-length.md section 9) is what makes the mixer cheap for the iGPU tier; without it the mixer multiplies a per-epoch cost | The mixer decision names the gfx1036 and an 8 GB-class scaled row beside the 5090, M5 Max and 9070 XT in the "under 1 s" check, and the per-day dataset reuse in the worker lands before (or with) class v3, or the iGPU tier is stated as "mines v3 with a restart per epoch" on the level 3 page | ca2-mixer af345b1e2c541ffbb; coordinator | taken (coordinator: the x8 table carries the gfx1036 row and a scaled 8 GB-class row; the under-1-s rule applies to the discrete cards' daily build; for the integrated tier either the per-day dataset reuse in the three workers lands with class v3, asked of the mixer and node agents as a bounded change tonight, or the level 3 page says the iGPU tier mines v3 with a restart per epoch; recorded with the x4/x8 choice) | | C24 | Two PowerShell job scripts lost a quote inside an inline bash body tonight: amd-prove's awk program (cpu-prove-pc1-small, exit 0 with nothing proved) and the 0.3.10 installer's `bash -c` string (fb-installer-pc1-3, exit 2 in 4 s); amd-prove added `tools/amd-prove/check-job-bash.sh` (bash -n on its own scripts' bash bodies) | amd-proving.md section 2; status 21:28 | every PC job; the morning's rollouts | The class (CLAUDE.md, 5 October: fix the class the same day, add a check that fails when the shape comes back) is "a bash body inside a PowerShell job string"; the check exists for one agent's scripts and did not cover the shipper's, which failed the same way an hour later | A repo-wide CI check: every `.ps1` under relay/playbooks and tools that carries a bash body (`wsl ... bash -c`, `bash -lc`, here-strings fed to bash) has that body extracted and passed through `bash -n`; the shipper's rule (the WSL part as a file run with `bash `) written in packaging/README-ship.md as the convention | sub-agent (bounded, no owner); coordinator told | in work (sub-agent bash-body-check; the coordinator: no collision with PC 1, the shipper moved its WSL part to a file for attempt 4, the repro agent added a PowerShell 5.1 parse gate for bench/; the check fails on an unextractable body rather than skipping it) | -| C25 | Ember Tune: the signed manifest carries per-card-model tuning priors (power limit and core clock) that a new card applies and confirms in two steps; K1 signs it | ember-tune.md sections 4 and 5; bench-log Ember Tune entry | every NVIDIA and AMD card on the app; the keys | The manifest now sets clocks and power limits on every user's card, so the signing key's blast radius grew: a signed prior can underclock the fleet or push a card model to its power ceiling. The plan's clamps (inside power.min_limit / max_limit and clocks.max.gr, the confirm step, a faulted step reverted) are the bound; keys.md's "what K1 signs" table (ota-k2 00fcbb5) predates the priors and does not list them | ember-tune.md section 5 states the bound in one line (a prior can never set a value outside the card's own reported limits, and never a memory clock), with the test that proves it; keys.md's table gains the tuning priors under K1 with that bound | Ember Tune a855dcc4bd05e0615; OTA key agent (the table row) | sent | +| C25 | Ember Tune: the signed manifest carries per-card-model tuning priors (power limit and core clock) that a new card applies and confirms in two steps; K1 signs it | ember-tune.md sections 4 and 5; bench-log Ember Tune entry | every NVIDIA and AMD card on the app; the keys | The manifest now sets clocks and power limits on every user's card, so the signing key's blast radius grew: a signed prior can underclock the fleet or push a card model to its power ceiling. The plan's clamps (inside power.min_limit / max_limit and clocks.max.gr, the confirm step, a faulted step reverted) are the bound; keys.md's "what K1 signs" table (ota-k2 00fcbb5) predates the priors and does not list them | ember-tune.md section 5 states the bound in one line (a prior can never set a value outside the card's own reported limits, and never a memory clock), with the test that proves it; keys.md's table gains the tuning priors under K1 with that bound | Ember Tune a855dcc4bd05e0615; OTA key agent (the table row) | closed (Ember Tune 5d7ced9: the rule as a row in section 5 with the two tests named, a prior of 9,000 MHz at 30% clamps to 3,090 MHz at 50%, a bad prior costs one confirm step per card; the OTA agent has the keys.md note: tuning priors and the kill switch under K1 with that bound) | From 144fe8ba1e1023cd60aa13a8f48d0e1560526340 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:32:09 +0000 Subject: [PATCH 073/158] fast-time: the override file is merged as text, never through JSON.parse (a never height, 18446744073709551615, becomes 1.8446744073709552e+19 and the node refuses the file; first gate run 5 October 2026 21:30Z); class-v3.mjs and simnet.mjs Co-Authored-By: Claude Fable 5.1 --- infra/fast-time/class-v3.mjs | 19 +++++++++++++++---- infra/fast-time/simnet.mjs | 4 +++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/infra/fast-time/class-v3.mjs b/infra/fast-time/class-v3.mjs index a4cb213f..69fd8f68 100644 --- a/infra/fast-time/class-v3.mjs +++ b/infra/fast-time/class-v3.mjs @@ -41,12 +41,23 @@ const sleep = (ms) => new Promise(r => setTimeout(r, ms)); for (const b of [IGNEUMD, CPU_MINER]) if (!existsSync(b)) { console.error(`missing ${b}`); process.exit(2); } rmSync(TMP, { recursive: true, force: true }); mkdirSync(TMP, { recursive: true }); -const base = JSON.parse(readFileSync(FILE, 'utf8')); -const EPOCH = base.pow_epoch_blocks; +// The file is merged as TEXT, never through JSON.parse: a `never` height is 18446744073709551615, which JavaScript +// rounds to 1.8446744073709552e+19 and the node refuses ("expected u64"; the first gate run, 5 October 2026 21:30Z). +// The merged fields are appended after the file's last field; a field already in the file is removed first. +const baseText = readFileSync(FILE, 'utf8'); +const field = (name) => { const m = new RegExp(`"${name}":\\s*([0-9]+)`).exec(baseText); return m ? +m[1] : undefined; }; +const EPOCH = field('pow_epoch_blocks'); +const DAY_MS = field('pow_day_ms'); const FIRST_V3_EPOCH = Math.ceil(ACTIVATION / EPOCH); const BOUNDARY = FIRST_V3_EPOCH * EPOCH; const override = `${TMP}/override.json`; -writeFileSync(override, JSON.stringify({ ...base, genesis_bits: GENESIS_BITS, skip_proof_of_work: false, program_class_v3_activation_daa: ACTIVATION })); +export function mergeOverrideText(text, fields) { + let out = text; + for (const k of Object.keys(fields)) out = out.replace(new RegExp(`\\s*"${k}":\\s*[^,}\\n]+,?`), ''); + const extra = Object.entries(fields).map(([k, v]) => `"${k}": ${typeof v === 'string' ? JSON.stringify(v) : v}`).join(', '); + return out.replace(/,?\s*}\s*$/, `,\n ${extra}\n}\n`); +} +writeFileSync(override, mergeOverrideText(baseText, { genesis_bits: GENESIS_BITS, skip_proof_of_work: false, program_class_v3_activation_daa: ACTIVATION })); log(`activation ${ACTIVATION} at ${EPOCH} DAA per epoch: the first v3 epoch is ${FIRST_V3_EPOCH} (DAA ${BOUNDARY}); run ${SECS} s, CPU genesis bits 0x${GENESIS_BITS.toString(16)}`); class Node { @@ -99,7 +110,7 @@ log(`n0 PoW schedule: ${n0.grepLog(/PoW schedule from the override file/).map(l log(`n0 digest: ${n0.grepLog(/Consensus params digest/).map(l => l.replace(/^.*?digest: /, '').slice(0, 16)).join(' ')}`); // one real CPU miner per node, 1 thread, 2^16 expected hashes per block at genesis -nodes.forEach((n, i) => miner(CPU_MINER, ['mine', n.grpc, String(THREADS), String(SECS), `cpu${i}`, '--engine', 'igneum-pow', '--payout-label', `cpu${i}`, '--status-secs', '30', '--no-vote'], `cpu${i}`, { IGNEUM_POW_DAY_MS: String(base.pow_day_ms) })); +nodes.forEach((n, i) => miner(CPU_MINER, ['mine', n.grpc, String(THREADS), String(SECS), `cpu${i}`, '--engine', 'igneum-pow', '--payout-label', `cpu${i}`, '--status-secs', '30', '--no-vote'], `cpu${i}`, { IGNEUM_POW_DAY_MS: String(DAY_MS) })); const pay = devAddress('fast-time-v3'); const epochs = new Map(); // epoch index -> { class, firstSeenDaa, at } diff --git a/infra/fast-time/simnet.mjs b/infra/fast-time/simnet.mjs index f70fc7d5..0f715652 100644 --- a/infra/fast-time/simnet.mjs +++ b/infra/fast-time/simnet.mjs @@ -35,7 +35,9 @@ for (const b of [IGNEUMD, VMINE]) if (!existsSync(b)) { console.error(`missing $ rmSync(TMP, { recursive: true, force: true }); mkdirSync(TMP, { recursive: true }); const override = `${TMP}/override.json`; -writeFileSync(override, JSON.stringify({ ...JSON.parse(readFileSync(FILE, 'utf8')), skip_proof_of_work: true })); +// merged as text, not through JSON.parse: a `never` height (18446744073709551615) does not survive a JavaScript number +// (the class-v3.mjs gate run of 5 October 2026 21:30Z: "invalid type: floating point 1.8446744073709552e+19") +writeFileSync(override, readFileSync(FILE, 'utf8').replace(/\s*"skip_proof_of_work":\s*[^,}\n]+,?/, '').replace(/,?\s*}\s*$/, ',\n "skip_proof_of_work": true\n}\n')); class Node { constructor(i, connect = []) { From 9b5451f66a8c95710d9ebc7fc75d8e25c6940df9 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:33:00 +0000 Subject: [PATCH 074/158] class-v3.mjs: node 2 connects to node 0 only (--connect takes one address); a thrown start stops the nodes Co-Authored-By: Claude Fable 5.1 --- infra/fast-time/class-v3.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/infra/fast-time/class-v3.mjs b/infra/fast-time/class-v3.mjs index 69fd8f68..ca0af9df 100644 --- a/infra/fast-time/class-v3.mjs +++ b/infra/fast-time/class-v3.mjs @@ -94,13 +94,14 @@ async function stopAll() { for (const p of started) { try { p.kill('SIGKILL'); } catch { } } } process.on('SIGINT', async () => { await stopAll(); process.exit(130); }); +process.on('unhandledRejection', async (e) => { log(`FAILED: ${e?.stack || e}`); await stopAll(); process.exit(3); }); // a thrown start leaves no node behind const minerLog = (i) => { try { return readFileSync(`${TMP}/cpu${i}.log`, 'utf8').split('\n'); } catch { return []; } }; const t0 = Date.now(); const since = () => ((Date.now() - t0) / 1000).toFixed(1); const n0 = await new Node(0).start(); const n1 = await new Node(1, [`127.0.0.1:${n0.p2pPort}`]).start(); -const n2 = await new Node(2, [`127.0.0.1:${n0.p2pPort}`, `127.0.0.1:${n1.p2pPort}`]).start(); +const n2 = await new Node(2, [`127.0.0.1:${n0.p2pPort}`]).start(); // --connect takes one address const nodes = [n0, n1, n2]; for (const n of nodes) { const line = n.grepLog(/Program class v3 from the override file/)[0]; From 5c0c196331b537baffb5210545ce714b8f4758a4 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:36:08 +0000 Subject: [PATCH 075/158] Consequences ledger: C23 taken by ca2-mixer too Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 03bc6edf..da1cc37e 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -44,6 +44,6 @@ Sweep 5 (21:08 Mac clock) notes: the coordinator applied the public proving line | # | Number | Source | Tier affected | Consequence | Action | Owner | State | |---|---|---|---|---|---|---|---| -| C23 | Mixer x4 or x8: the 5090's daily dataset build 13.4 ms at x1 (54 ms at x4, about 107 ms at x8); the integrated gfx1036 builds the dataset per PREPARE, 7 to 12 s at x1 and 55 to 124 s under CPU load (epoch-length.md section 7); the x8 rule: "the daily build under 1 s on every card we own" | status 21:25 and 21:27; epoch-length.md section 7 | integrated GPUs (the iGPU tier), 8 GB cards (about a tenth of a 5090's rate), rigs with one weak card | The x8 rule names "every card we own": the gfx1036 is one, and at x8 its per-prepare build is 56 to 96 s per epoch (7 to 16 min under load), so it misses every epoch boundary and falls to the exit-42 path; at x4 it is 28 to 48 s per hour (0.8 to 1.3%) or 4 to 8 min under load. An 8 GB discrete card scales at about a tenth of the 5090: about 1 s at x8, on the rule's edge. The per-day dataset reuse in the worker (owed in epoch-length.md section 9) is what makes the mixer cheap for the iGPU tier; without it the mixer multiplies a per-epoch cost | The mixer decision names the gfx1036 and an 8 GB-class scaled row beside the 5090, M5 Max and 9070 XT in the "under 1 s" check, and the per-day dataset reuse in the worker lands before (or with) class v3, or the iGPU tier is stated as "mines v3 with a restart per epoch" on the level 3 page | ca2-mixer af345b1e2c541ffbb; coordinator | taken (coordinator: the x8 table carries the gfx1036 row and a scaled 8 GB-class row; the under-1-s rule applies to the discrete cards' daily build; for the integrated tier either the per-day dataset reuse in the three workers lands with class v3, asked of the mixer and node agents as a bounded change tonight, or the level 3 page says the iGPU tier mines v3 with a restart per epoch; recorded with the x4/x8 choice) | +| C23 | Mixer x4 or x8: the 5090's daily dataset build 13.4 ms at x1 (54 ms at x4, about 107 ms at x8); the integrated gfx1036 builds the dataset per PREPARE, 7 to 12 s at x1 and 55 to 124 s under CPU load (epoch-length.md section 7); the x8 rule: "the daily build under 1 s on every card we own" | status 21:25 and 21:27; epoch-length.md section 7 | integrated GPUs (the iGPU tier), 8 GB cards (about a tenth of a 5090's rate), rigs with one weak card | The x8 rule names "every card we own": the gfx1036 is one, and at x8 its per-prepare build is 56 to 96 s per epoch (7 to 16 min under load), so it misses every epoch boundary and falls to the exit-42 path; at x4 it is 28 to 48 s per hour (0.8 to 1.3%) or 4 to 8 min under load. An 8 GB discrete card scales at about a tenth of the 5090: about 1 s at x8, on the rule's edge. The per-day dataset reuse in the worker (owed in epoch-length.md section 9) is what makes the mixer cheap for the iGPU tier; without it the mixer multiplies a per-epoch cost | The mixer decision names the gfx1036 and an 8 GB-class scaled row beside the 5090, M5 Max and 9070 XT in the "under 1 s" check, and the per-day dataset reuse in the worker lands before (or with) class v3, or the iGPU tier is stated as "mines v3 with a restart per epoch" on the level 3 page | ca2-mixer af345b1e2c541ffbb; coordinator | taken (coordinator and ca2-mixer, mixer-x4.md build-time table: the x8 table carries the gfx1036 row and a scaled 8 GB-class row; the under-1-s rule applies to the discrete cards' daily build; for the integrated tier either the per-day dataset reuse in the three workers lands with class v3, asked of the mixer and node agents as a bounded change tonight, or the level 3 page says the iGPU tier mines v3 with a restart per epoch; recorded with the x4/x8 choice) | | C24 | Two PowerShell job scripts lost a quote inside an inline bash body tonight: amd-prove's awk program (cpu-prove-pc1-small, exit 0 with nothing proved) and the 0.3.10 installer's `bash -c` string (fb-installer-pc1-3, exit 2 in 4 s); amd-prove added `tools/amd-prove/check-job-bash.sh` (bash -n on its own scripts' bash bodies) | amd-proving.md section 2; status 21:28 | every PC job; the morning's rollouts | The class (CLAUDE.md, 5 October: fix the class the same day, add a check that fails when the shape comes back) is "a bash body inside a PowerShell job string"; the check exists for one agent's scripts and did not cover the shipper's, which failed the same way an hour later | A repo-wide CI check: every `.ps1` under relay/playbooks and tools that carries a bash body (`wsl ... bash -c`, `bash -lc`, here-strings fed to bash) has that body extracted and passed through `bash -n`; the shipper's rule (the WSL part as a file run with `bash `) written in packaging/README-ship.md as the convention | sub-agent (bounded, no owner); coordinator told | in work (sub-agent bash-body-check; the coordinator: no collision with PC 1, the shipper moved its WSL part to a file for attempt 4, the repro agent added a PowerShell 5.1 parse gate for bench/; the check fails on an unextractable body rather than skipping it) | | C25 | Ember Tune: the signed manifest carries per-card-model tuning priors (power limit and core clock) that a new card applies and confirms in two steps; K1 signs it | ember-tune.md sections 4 and 5; bench-log Ember Tune entry | every NVIDIA and AMD card on the app; the keys | The manifest now sets clocks and power limits on every user's card, so the signing key's blast radius grew: a signed prior can underclock the fleet or push a card model to its power ceiling. The plan's clamps (inside power.min_limit / max_limit and clocks.max.gr, the confirm step, a faulted step reverted) are the bound; keys.md's "what K1 signs" table (ota-k2 00fcbb5) predates the priors and does not list them | ember-tune.md section 5 states the bound in one line (a prior can never set a value outside the card's own reported limits, and never a memory clock), with the test that proves it; keys.md's table gains the tuning priors under K1 with that bound | Ember Tune a855dcc4bd05e0615; OTA key agent (the table row) | closed (Ember Tune 5d7ced9: the rule as a row in section 5 with the two tests named, a prior of 9,000 MHz at 30% clamps to 3,090 MHz at 50%, a bad prior costs one confirm step per card; the OTA agent has the keys.md note: tuning priors and the kill switch under K1 with that bound) | From 08db83b2eb761fc0674c537d341f0d54431e0ccf Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:38:37 +0000 Subject: [PATCH 076/158] counter-asic-2-node.md: gate G4 run 1 PASS on the mixer-x4 class (181 / 124 blocks across the boundary, 0 rejected, 0 forks, 3 switch lines, v2 and v3 program ids), the summary JSON Co-Authored-By: Claude Fable 5.1 --- .../class-v3-20261005-2133Z-mx4.json | 381 ++++++++++++++++++ docs/plans/counter-asic-2-node.md | 17 +- 2 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 docs/plans/counter-asic-2-gate/class-v3-20261005-2133Z-mx4.json diff --git a/docs/plans/counter-asic-2-gate/class-v3-20261005-2133Z-mx4.json b/docs/plans/counter-asic-2-gate/class-v3-20261005-2133Z-mx4.json new file mode 100644 index 00000000..5b667847 --- /dev/null +++ b/docs/plans/counter-asic-2-gate/class-v3-20261005-2133Z-mx4.json @@ -0,0 +1,381 @@ +{ + "pass": true, + "checks": { + "switch_line_on_every_node": true, + "switch_line_names_the_rounded_epoch": true, + "template_switched_at_the_first_v3_epoch": true, + "blocks_before_the_boundary": true, + "blocks_after_the_boundary": true, + "v2_and_v3_programs_seen": true, + "program_ids_differ_across_the_switch": true, + "miners_agree_on_every_program": true, + "zero_rejected_by_miners": true, + "zero_rejected_by_nodes": true, + "sinks_agree": true, + "block_counts_agree": true + }, + "activation": 150, + "epoch_blocks": 60, + "first_v3_epoch": 3, + "boundary_daa": 180, + "secs": 480, + "threads": 1, + "node": "/Users/joshm/Projects/igneum-wt-ca2-v3/vendor/igneum-node-ca2/target-ca2/release/igneumd", + "miner": "/Users/joshm/Projects/igneum-wt-ca2-v3/vendor/igneum-node-ca2/target-ca2/release/igneum-miner", + "genesis_bits": "0x1f010000", + "template_switch": { + "epoch": 3, + "daa": 180, + "at": 168 + }, + "run_ended_at_s": 298.3, + "final_daa": 300, + "blocks": { + "total": 305, + "before_boundary": 181, + "after_boundary": 124, + "chain_before": 176, + "chain_after": 123 + }, + "programs": [ + { + "epoch": 0, + "class": "v2", + "program_id": "8f8806638d59850f", + "seed": "234e082d653dc69d", + "miners": 3, + "disagree": false, + "ready_ms": [ + 219, + 235, + 232 + ] + }, + { + "epoch": 1, + "class": "v2", + "program_id": "fd9562df32a68313", + "seed": "9b36731951ad7fb3", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + }, + { + "epoch": 2, + "class": "v2", + "program_id": "1ae6d90ab299154c", + "seed": "ce88239dfe686691", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + }, + { + "epoch": 3, + "class": "v3", + "program_id": "5d0dedd9fd9e29a1", + "seed": "f177ab855facbeb4", + "miners": 3, + "disagree": false, + "ready_ms": [ + 185, + 179, + 183, + 177, + 183, + 177 + ] + }, + { + "epoch": 4, + "class": "v3", + "program_id": "e81808dcdb02ce05", + "seed": "bcbc22513d3b73a2", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + }, + { + "epoch": 5, + "class": "v3", + "program_id": "06aff9c1d33e7a13", + "seed": "8d6755bc0eb15465", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + } + ], + "accepted_per_miner": [ + 97, + 103, + 104 + ], + "rejected_by_miners": [ + 0, + 0, + 0 + ], + "rejected_by_nodes": [ + 0, + 0, + 0 + ], + "rejected_lines": [], + "sinks": [ + "082fd39ba65df2ff", + "082fd39ba65df2ff", + "082fd39ba65df2ff" + ], + "block_counts": [ + 304, + 304, + 304 + ], + "tips_per_node": [ + 1, + 1, + 1 + ], + "switch_lines": [ + "Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)" + ], + "samples": [ + { + "t": 4.7, + "daa": 0, + "epoch": 0, + "class": 2, + "nodes": [ + "0/234e082d", + "0/234e082d", + "0/234e082d" + ] + }, + { + "t": 19.7, + "daa": 11, + "epoch": 0, + "class": 2, + "nodes": [ + "11/c036063c", + "11/c036063c", + "11/c036063c" + ] + }, + { + "t": 34.7, + "daa": 34, + "epoch": 0, + "class": 2, + "nodes": [ + "34/62ae0e8e", + "34/62ae0e8e", + "34/62ae0e8e" + ] + }, + { + "t": 49.8, + "daa": 53, + "epoch": 0, + "class": 2, + "nodes": [ + "53/c21d7e86", + "53/c21d7e86", + "53/c21d7e86" + ] + }, + { + "t": 64.8, + "daa": 70, + "epoch": 1, + "class": 2, + "nodes": [ + "70/9cb940da", + "70/9cb940da", + "70/9cb940da" + ] + }, + { + "t": 79.8, + "daa": 88, + "epoch": 1, + "class": 2, + "nodes": [ + "88/c84b8514", + "88/c84b8514", + "88/c84b8514" + ] + }, + { + "t": 94.9, + "daa": 103, + "epoch": 1, + "class": 2, + "nodes": [ + "103/1e2dfed3", + "103/1e2dfed3", + "103/1e2dfed3" + ] + }, + { + "t": 109.9, + "daa": 111, + "epoch": 1, + "class": 2, + "nodes": [ + "111/aa7ea4cd", + "111/aa7ea4cd", + "111/aa7ea4cd" + ] + }, + { + "t": 124.9, + "daa": 139, + "epoch": 2, + "class": 2, + "nodes": [ + "139/d52be92d", + "139/d52be92d", + "139/d52be92d" + ] + }, + { + "t": 139.9, + "daa": 153, + "epoch": 2, + "class": 2, + "nodes": [ + "153/02a4323f", + "153/02a4323f", + "153/02a4323f" + ] + }, + { + "t": 155, + "daa": 165, + "epoch": 2, + "class": 2, + "nodes": [ + "165/f2e38b3d", + "165/f2e38b3d", + "165/f2e38b3d" + ] + }, + { + "t": 170, + "daa": 183, + "epoch": 3, + "class": 3, + "nodes": [ + "183/98f2ca9d", + "183/98f2ca9d", + "183/98f2ca9d" + ] + }, + { + "t": 185.1, + "daa": 195, + "epoch": 3, + "class": 3, + "nodes": [ + "195/eecbf07e", + "195/eecbf07e", + "195/eecbf07e" + ] + }, + { + "t": 200.1, + "daa": 205, + "epoch": 3, + "class": 3, + "nodes": [ + "205/ad4a2646", + "205/ad4a2646", + "205/ad4a2646" + ] + }, + { + "t": 215.1, + "daa": 221, + "epoch": 3, + "class": 3, + "nodes": [ + "221/f8626ffd", + "221/f8626ffd", + "221/f8626ffd" + ] + }, + { + "t": 230.2, + "daa": 233, + "epoch": 3, + "class": 3, + "nodes": [ + "233/d94e1815", + "233/d94e1815", + "233/d94e1815" + ] + }, + { + "t": 245.2, + "daa": 251, + "epoch": 4, + "class": 3, + "nodes": [ + "251/f724fd9e", + "251/f724fd9e", + "251/f724fd9e" + ] + }, + { + "t": 260.2, + "daa": 266, + "epoch": 4, + "class": 3, + "nodes": [ + "266/b91936fd", + "266/b91936fd", + "266/b91936fd" + ] + }, + { + "t": 275.2, + "daa": 276, + "epoch": 4, + "class": 3, + "nodes": [ + "276/7c8f720d", + "276/7c8f720d", + "276/7c8f720d" + ] + }, + { + "t": 290.3, + "daa": 297, + "epoch": 4, + "class": 3, + "nodes": [ + "297/63eb2574", + "297/63eb2574", + "297/63eb2574" + ] + } + ] +} \ No newline at end of file diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index ce4fba83..0ce5abef 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -77,7 +77,22 @@ Refusals: `pf_load` refuses a generator that is not 2 or 3 (`error 0 pack : `node infra/fast-time/class-v3.mjs [--secs 420] [--activation 150] [--epochs-after 2]` under `tools/lock/with-lock.sh run`: three nodes on `override-60x.json` merged with `genesis_bits` 0x1f010000 (2^16 hashes per block, the CPU difficulty of `sim/difficulty/testnet_v2.py`) and `program_class_v3_activation_daa` 150 (inside epoch 2, so the rounding rule is exercised: the first v3 epoch is 3 at DAA 180); one real CPU miner per node (`--engine igneum-pow`, 1 thread, real lottery-hash solutions, every node verifying the other two); the run ends two epochs after the boundary. -Result: PENDING (filled by the run; see the status file and the final report). +### Result, run 1 (5 October 2026, 21:33:04Z to 21:38:02Z, Apple M5 Max shared with other agents' builds) + +Binaries: fork ca2-v3-node 79bd8e10 and igneum-pow at ca2-v3 66eeba3 (the mixer-x4 class, `V3_CLASS` = MX4, before the era and hot-table fields), `target-ca2/release`, built on the Mac under the build lock. Summary: `docs/plans/counter-asic-2-gate/class-v3-20261005-2133Z-mx4.json`. PASS: every check true. + +| Check | Measured | +|---|---| +| Switch line on every node | 3 of 3: `Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)` | +| Digest, all three nodes | `0186df7d0834d054...` (the 60x profile with the CPU bits and the switch) | +| Template class per epoch | epochs 0 to 2 class 2 (`nextProgramClass` 3 from epoch 2), epochs 3 to 5 class 3; the switch seen at DAA 180, 168.0 s wall | +| Blocks before / after the boundary (node 0's DAG) | 181 / 124 (selected chain 176 / 123), 305 in all | +| Program id per epoch, all three miners agreeing | e0 v2 `8f8806638d59850f`, e1 v2 `fd9562df32a68313`, e2 v2 `1ae6d90ab299154c`, e3 v3 `5d0dedd9fd9e29a1`, e4 v3 `e81808dcdb02ce05`, e5 v3 `06aff9c1d33e7a13`; no v2 id reappears under v3 | +| Rejected blocks | miners 0 / 0 / 0 (97, 103, 104 accepted); nodes 0 / 0 / 0 `PoW rejected` lines | +| Forks | sinks `082fd39ba65df2ff` on all three nodes, block counts 304 / 304 / 304, one tip each | +| Program and cache ready, one CPU core | a new `(day, class)` cache: v2 epoch 0 219 to 235 ms, the first v3 epoch 177 to 185 ms (two per miner: the 24-minute day rolled at DAA 190); a program swap inside a day 2 ms | + +The mixer x4 build-time number the rollout asks for is not visible here: the CPU miner derives dataset words on demand from the cache (no dataset build), so the x4 cost lands on the GPU workers' dataset build, measured by the ca2-mixer playbooks on the PCs. Run 2, on the composed class (era and hot fields), follows the era and cache merges. ## 6. Tests From a5533177d3d60a38a2dde1c1b68521f60f06d9cd Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:38:41 +0000 Subject: [PATCH 077/158] CI: bash bodies in PowerShell jobs pass bash -n (the lost-quote class) Twice on 5 October 2026 a PowerShell job script carried a bash body inside a string, a quote was lost on the way through PowerShell, and bash refused the body: pc1-cpu-prove.ps1 (first version) reported exit 0 having done nothing, the 0.3.10 installer job failed in 4 s. tools/amd-prove/check-job-bash.sh covered only its own here-string. tools/ci/bash-body-check.sh reads every *.ps1 under relay/playbooks/ and tools/, finds each bash body however it is handed over (bash -c "...", bash -lc '...', bash -c $var, a + concatenation in parentheses, the Start-Process argument list, a here-string written to a file that is later run with bash), unescapes it the way PowerShell would (backtick escapes and "" in double-quoted strings, '' in single-quoted strings, here-strings verbatim; $var left as-is, a $(...) subexpression replaced by ${PS_SUBEXPR}), and runs bash -n on it. One line per body with the file line of the error. A body it sees but cannot read is "unextractable body" and fails too: a skip would be a hole in the class check. bash 3.2 compatible; python3 for the extractor. --self-test runs three fixtures under tools/ci/fixtures/: the correct shapes (8 bodies, must pass), the lost quotes (the awk apostrophe, a dropped closing quote in a literal and in a variable; must fail with the line), and three unreadable bodies (must fail). Wired into ci.yml next to the copied-sources check, self-test first. The current tree: 7 inline bodies in 3 playbooks, all parse. packaging/README-ship.md: the job-script rule (body to a file, bash ). Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 2 + packaging/README-ship.md | 12 + tools/ci/bash-body-check.sh | 402 ++++++++++++++++++ tools/ci/fixtures/bash-body-lost-quote.ps1 | 24 ++ tools/ci/fixtures/bash-body-ok.ps1 | 53 +++ tools/ci/fixtures/bash-body-unextractable.ps1 | 14 + 6 files changed, 507 insertions(+) create mode 100755 tools/ci/bash-body-check.sh create mode 100644 tools/ci/fixtures/bash-body-lost-quote.ps1 create mode 100644 tools/ci/fixtures/bash-body-ok.ps1 create mode 100644 tools/ci/fixtures/bash-body-unextractable.ps1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9879a98b..1da84358 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,6 +67,8 @@ jobs: run: bash tools/ci/no-conflict-markers.sh - name: copied sources are re-stamped before a build run: bash tools/ci/copied-sources-check.sh + - name: bash bodies in PowerShell job scripts pass bash -n, the lost-quote class (self-test first, then the tree) + run: bash tools/ci/bash-body-check.sh --self-test && bash tools/ci/bash-body-check.sh - name: pinned guest programs match their manifest and are built only by pin-guests.sh run: bash tools/ci/pinned-guests-check.sh - name: no secret file names and no 64-hex secrets in the tree (self-test first, then the tree) diff --git a/packaging/README-ship.md b/packaging/README-ship.md index f9498b75..3da24790 100644 --- a/packaging/README-ship.md +++ b/packaging/README-ship.md @@ -106,3 +106,15 @@ from the repository root (the project's root directory is `site`), but the norma (`igneum-relay`) and the downloads folder (`igneum-dl`) are the other two projects in the `igneum` team; both deploy by CLI from their own folders (`relay/README.md`, `packaging/ota/README.md`). CLAUDE.md still says the site sits in the [other-business] team and deploys with `--scope [other-business]` from `site/`: that was true on 3 October and is not now. + +## Job scripts (5 October 2026, the lost-quote class) + +A bash body in a PowerShell job (`relay/playbooks/*.ps1`, `tools/**/*.ps1`) is written to a file and run with +`bash `, never inline. That is the 0.3.6 cut's rule. Twice on 5 October a body travelled inside a PowerShell +string, a quote was lost on the way through PowerShell, bash refused the whole body, and the job either reported exit 0 +having done nothing (`tools/amd-prove/pc1-cpu-prove.ps1`, first version: an apostrophe inside a single-quoted awk +program) or failed in 4 s (the 0.3.10 installer job). The file shape keeps the body readable by `bash -n` before it runs; +`pc1-cpu-prove.ps1` does that in the job itself. `tools/ci/bash-body-check.sh` fails CI on an inline body that does not +parse: it finds every body handed to bash (`bash -c "..."`, `bash -lc $var`, a `+` concatenation, a here-string written to a +file and run), unescapes it the way PowerShell would, and runs `bash -n` on it. A body it sees but cannot read fails too. +`--self-test` shows it firing on the fixtures under `tools/ci/fixtures/`. diff --git a/tools/ci/bash-body-check.sh b/tools/ci/bash-body-check.sh new file mode 100755 index 00000000..385b3366 --- /dev/null +++ b/tools/ci/bash-body-check.sh @@ -0,0 +1,402 @@ +#!/usr/bin/env bash +# The lost-quote class (5 October 2026, twice in one night): a PowerShell job script carries a bash body inside a +# string, a quote is lost on the way through PowerShell, bash refuses the whole body, and the job either reports +# exit 0 having done nothing (tools/amd-prove/pc1-cpu-prove.ps1, first version: an apostrophe inside a single-quoted +# awk program) or fails in 4 s (the 0.3.10 installer job). Rule (the 0.3.6 cut): a bash body in a PowerShell job is +# written to a file and run with `bash `, never inline. This check reads every *.ps1 under relay/playbooks/ and +# tools/, finds each bash body however it is handed over (`wsl ... bash -c "..."`, `bash -lc '...'`, `bash -c $var`, +# a `+` concatenation in parentheses, a here-string written to a file that is later run with bash), unescapes it the +# way PowerShell would (backtick escapes and "" in double-quoted strings, '' in single-quoted strings, here-strings +# verbatim; `$var` interpolation left as-is, a PowerShell `$(...)` subexpression replaced by `${PS_SUBEXPR}`; in a `+` +# concatenation a variable becomes `${name}`), and runs `bash -n` on it. One line per body: file, line, ok or the +# bash -n error. A body the extractor sees but cannot read (`bash -c` with an argument shape it does not parse, or a +# variable with no literal assignment above) is "unextractable body" and FAILS: a skip would be a hole in the check. +# Exit 1 on any failure. +# +# Usage: tools/ci/bash-body-check.sh # the tree (tools/ci/fixtures/ left out) +# tools/ci/bash-body-check.sh ... # named files +# tools/ci/bash-body-check.sh --self-test # must fire on the lost-quote and unextractable fixtures under +# # tools/ci/fixtures/ and stay quiet on the correct one +# Needs python3 (the extractor) and bash (the parser). Runs on this Mac's bash 3.2. +set -uo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)" +REPO="$(cd "$HERE/../.." && pwd)" + +# ---- the extractor: python3 reads the ps1 files, writes one body per file under $1, prints an index to stdout ---- +# index line: idx file line kind offset status detail +# offset: file line = offset + body line (bash -n reports body lines); status: body | unextractable +extract() { + python3 - "$@" <<'PY' +import sys, os, re + +out_dir = sys.argv[1] +files = sys.argv[2:] +SUBEXPR = '${PS_SUBEXPR}' +BT = chr(96) +BT_MAP = {'n': '\n', 't': '\t', 'r': '\r', '0': '\0', 'a': '\a', 'b': '\b', 'f': '\f', 'v': '\v', 'e': '\x1b'} + +def skip_subexpr(s, i): + """s[i] == '(' of a PowerShell $( ... ); returns the index after the matching ')'.""" + depth = 0 + n = len(s) + while i < n: + c = s[i] + if c == '(': + depth += 1; i += 1 + elif c == ')': + depth -= 1; i += 1 + if depth == 0: return i + elif c == "'": + i += 1 + while i < n: + if s[i] == "'": + if i + 1 < n and s[i+1] == "'": i += 2; continue + i += 1; break + i += 1 + elif c == '"': + i = skip_dq(s, i + 1) + else: + i += 1 + return n + +def skip_dq(s, i): + """s[i] is the first char inside a double-quoted string; returns the index after the closing quote.""" + n = len(s) + while i < n: + c = s[i] + if c == BT: i += 2; continue + if c == '"': + if i + 1 < n and s[i+1] == '"': i += 2; continue + return i + 1 + if c == '$' and i + 1 < n and s[i+1] == '(': + i = skip_subexpr(s, i + 1); continue + i += 1 + return n + +def decode_dq(raw, here): + """A double-quoted string or here-string the way PowerShell reads it, with $var left as-is.""" + o = []; i = 0; n = len(raw) + while i < n: + c = raw[i] + if c == BT and i + 1 < n: + o.append(BT_MAP.get(raw[i+1], raw[i+1])); i += 2; continue + if c == '"' and not here and i + 1 < n and raw[i+1] == '"': + o.append('"'); i += 2; continue + if c == '$' and i + 1 < n and raw[i+1] == '(': + o.append(SUBEXPR); i = skip_subexpr(raw, i + 1); continue + o.append(c); i += 1 + return ''.join(o) + +def tokenize(text): + """PowerShell tokens: ('str', value, line, sub) | ('var', name, line, None) | ('word', text, line, None) | + ('op', char, line, None) | ('nl', '', line, None). Comments are dropped, a trailing backtick joins lines.""" + toks = []; i = 0; n = len(text); line = 1 + SPECIAL = set(' \t\r\n()[]{},;|=+\'"') + while i < n: + c = text[i] + if c == '\n': + toks.append(('nl', '', line, None)); line += 1; i += 1; continue + if c in ' \t\r': i += 1; continue + if c == BT and i + 1 < n and text[i+1] in '\r\n': + i += 1 + while i < n and text[i] == '\r': i += 1 + if i < n and text[i] == '\n': line += 1; i += 1 + continue + if text.startswith('<#', i): + j = text.find('#>', i + 2) + if j < 0: j = n + line += text.count('\n', i, j); i = j + 2; continue + if c == '#': + j = text.find('\n', i) + i = n if j < 0 else j; continue + if text.startswith('@"', i) or text.startswith("@'", i): + q = text[i+1] + j = i + 2 + while j < n and text[j] == '\r': j += 1 + if j < n and text[j] == '\n': + start = j + 1 + m = re.compile(r'(?:^|\n)' + re.escape(q + '@')).search(text, start - 1) + if m: + end = m.start() if text[m.start()] == '\n' else m.start() + raw = text[start:end] + sub = 'hdq' if q == '"' else 'hsq' + val = decode_dq(raw, True) if q == '"' else raw + toks.append(('str', val, line, sub)) + line += text.count('\n', i, m.end()); i = m.end(); continue + # not a here-string after all: fall through as an operator + toks.append(('op', '@', line, None)); i += 1; continue + if c == "'": + j = i + 1; o = [] + while j < n: + if text[j] == "'": + if j + 1 < n and text[j+1] == "'": o.append("'"); j += 2; continue + break + o.append(text[j]); j += 1 + toks.append(('str', ''.join(o), line, 'sq')) + line += text.count('\n', i, j); i = j + 1; continue + if c == '"': + j = skip_dq(text, i + 1) + raw = text[i+1:j-1] if j <= n and text[j-1] == '"' else text[i+1:j] + toks.append(('str', decode_dq(raw, False), line, 'dq')) + line += text.count('\n', i, j); i = j; continue + if c == '$': + if i + 1 < n and text[i+1] == '(': + j = skip_subexpr(text, i + 1) + toks.append(('subexpr', text[i:j], line, None)) + line += text.count('\n', i, j); i = j; continue + if i + 1 < n and text[i+1] == '{': + j = text.find('}', i) + if j < 0: j = n - 1 + toks.append(('var', text[i+2:j], line, None)); i = j + 1; continue + m = re.compile(r'\$([A-Za-z_][A-Za-z0-9_]*(?::[A-Za-z_][A-Za-z0-9_]*)?)').match(text, i) + if m: + toks.append(('var', m.group(1), line, None)); i = m.end(); continue + toks.append(('op', '$', line, None)); i += 1; continue + if c in SPECIAL: + toks.append(('op', c, line, None)); i += 1; continue + j = i + while j < n and text[j] not in SPECIAL: j += 1 + toks.append(('word', text[i:j], line, None)); i = j + toks.append(('nl', '', line, None)) + return toks + +def is_bash_word(t): + v = t[1] + return t[0] in ('word', 'str') and (v in ('bash', 'bash.exe') or v.endswith('/bin/bash') or v.endswith('\\bash.exe')) + +STOP_OPS = set('|;{})') + +def parse_concat(toks, i, assigns, stop_at_nl): + """Reads str|var (+ str|var)* from toks[i]; returns (value, kind, next_index, problem). kind: lit | here | concat.""" + parts = []; kinds = []; problem = None + want_operand = True + while i < len(toks): + t = toks[i] + if t[0] == 'nl': + if stop_at_nl and not want_operand: break + if want_operand and parts: i += 1; continue # a newline after '+' continues the expression + if not parts: break + i += 1; continue + if want_operand: + if t[0] == 'str': + parts.append(t[1]); kinds.append('here' if t[3] in ('hdq', 'hsq') else 'lit'); want_operand = False; i += 1; continue + if t[0] == 'var': + parts.append('${' + t[1] + '}'); kinds.append('var'); want_operand = False; i += 1; continue + problem = 'expression token %r at line %d' % (t[1] or t[0], t[2]); break + else: + if t[0] == 'op' and t[1] == '+': want_operand = True; i += 1; continue + if t[0] == 'op' and t[1] in STOP_OPS: break + if t[0] == 'word' and t[1].startswith('-'): problem = 'operator %s at line %d' % (t[1], t[2]); break + if t[0] in ('word', 'var', 'str', 'subexpr', 'op'): problem = 'expression token %r at line %d' % (t[1] or t[0], t[2]); break + break + if want_operand and parts and not problem: problem = 'expression ends after +' + if not parts and not problem: problem = 'no string' + if problem: return None, None, i, problem + if len(parts) == 1: kind = kinds[0] + else: kind = 'concat' + return ''.join(parts), kind, i, None + +def collect_assignments(toks): + """$name = at statement start. {name: [(line, value|None, kind, problem)]}""" + a = {} + for i, t in enumerate(toks): + if t[0] != 'var': continue + prev = toks[i-1] if i > 0 else None + if prev is not None and not (prev[0] == 'nl' or (prev[0] == 'op' and prev[1] in ';{')): continue + if i + 1 >= len(toks) or toks[i+1] != ('op', '=', toks[i+1][2], None): continue + val, kind, _, problem = parse_concat(toks, i + 2, a, True) + a.setdefault(t[1], []).append((t[2], val, kind, problem)) + return a + +def lookup(assigns, name, line): + best = None + for (l, val, kind, problem) in assigns.get(name, []): + if l < line: best = (l, val, kind, problem) + return best + +def bash_args(toks, i): + """Arguments after a bash word: list of (token-or-expr, index). An expr is ('expr', value, line, kind, problem).""" + args = [] + while i < len(toks): + t = toks[i] + if t[0] == 'nl': break + if t[0] == 'op' and t[1] in STOP_OPS: break + if t[0] == 'op' and t[1] == ',': i += 1; continue + if t[0] == 'word' and re.match(r'^[0-9]*>', t[1]): break + if t[0] == 'op' and t[1] == '(': + val, kind, j, problem = parse_concat(toks, i + 1, None, False) + # skip to the matching ')' + depth = 1; k = i + 1 + while k < len(toks) and depth > 0: + if toks[k][0] == 'op' and toks[k][1] == '(': depth += 1 + elif toks[k][0] == 'op' and toks[k][1] == ')': depth -= 1 + k += 1 + if problem is None and j < k - 1: problem = 'more than strings and + inside the parentheses' + names = set(x[1] for x in toks[i+1:k] if x[0] == 'var') + args.append((('expr', val, t[2], kind, problem, names), i)); i = k; continue + if t[0] == 'op' and t[1] == ')': break + args.append((t, i)); i += 1 + return args + +def value_of(arg, assigns): + """(value, def_line, kind, problem) for an argument token.""" + t = arg + if t[0] == 'str': return t[1], t[2], ('here' if t[3] in ('hdq', 'hsq') else 'lit'), None + if t[0] == 'expr': return t[1], t[2], t[3], t[4] + if t[0] == 'var': + a = lookup(assigns, t[1], t[2]) + if a is None: return None, t[2], 'var', '$%s has no literal assignment above line %d' % (t[1], t[2]) + l, val, kind, problem = a + if problem: return None, l, 'var', '$%s = (line %d) is not a string: %s' % (t[1], l, problem) + return val, l, kind, None + if t[0] == 'subexpr': return None, t[2], 'subexpr', 'a $(...) subexpression' + return None, t[2], t[0], 'token %r' % t[1] + +def option_value(arg): + if arg[0] in ('word', 'str'): return arg[1] + return None + +def find_bodies(path, toks): + assigns = collect_assignments(toks) + bodies = [] # (line, kind, value, offset, problem, def_line) + seen = set() + inv = [] # (index, args) of every bash invocation + for i, t in enumerate(toks): + if not is_bash_word(t): continue + args = bash_args(toks, i + 1) + inv.append((i, args)) + want_body = False; body_done = False; skip_next = False + for (a, ai) in args: + if body_done: break + if skip_next: skip_next = False; continue + if not want_body: + ov = option_value(a) + if ov is not None and re.match(r'^-[A-Za-z]+$', ov): + if 'c' in ov: want_body = True + if ov[-1] in ('o', 'O'): skip_next = True + continue + if ov is not None and ov.startswith('--'): continue + if ov is not None and re.match(r'^\+[A-Za-z]+$', ov): + if ov[-1] == 'O': skip_next = True + continue + break # a script file: not an inline body + val, dline, kind, problem = value_of(a, assigns) + body_done = True + how = 'bash -c ' + ('$' + a[1] if a[0] == 'var' else ('(...)' if a[0] == 'expr' else 'literal')) + if problem: bodies.append((t[2], how, None, 0, problem, dline)) + else: + offset = dline if kind == 'here' else dline - 1 + key = (dline, how) + if key in seen: continue + seen.add(key); bodies.append((t[2], how, val, offset, None, dline)) + if want_body and not body_done: + bodies.append((t[2], 'bash -c', None, 0, 'no body argument after -c', t[2])) + # here-strings (and other string variables) written to a file that is later run with bash + WRITERS = ('WriteAllText', 'WriteAllLines', 'Set-Content', 'Out-File', 'Add-Content') + lines_tokens = {} + for t in toks: lines_tokens.setdefault(t[2], []).append(t) + for name, lst in assigns.items(): + for (l, val, kind, problem) in lst: + if val is None: continue + for wl, lt in lines_tokens.items(): + if wl <= l: continue + if not any(x[0] == 'word' and any(w in x[1] for w in WRITERS) for x in lt): continue + if not any(x[0] == 'var' and x[1] == name for x in lt): continue + paths = set(x[1] for x in lt if x[0] == 'var' and x[1] != name) + sh_literal = any(x[0] == 'str' and x[1].endswith('.sh') for x in lt) + # a variable derived from the path ($w = WslPath $bashFile) names the same file + for al in sorted(lines_tokens): + if al <= wl: continue + alt = lines_tokens[al] + if len(alt) > 1 and alt[0][0] == 'var' and alt[1] == ('op', '=', al, None): + if any(x[0] == 'var' and x[1] in paths for x in alt[1:]): paths.add(alt[0][1]) + run = False + for (bi, args) in inv: + if toks[bi][2] <= wl: continue + for (a, ai) in args: + if a[0] == 'var' and a[1] in paths: run = True + if a[0] == 'expr' and (a[5] & paths): run = True + if a[0] == 'str' and a[1].endswith('.sh') and sh_literal: run = True + if run: break + if not run: continue + how = '$%s written at line %d and run with bash' % (name, wl) + key = (l, how) + if key in seen: continue + seen.add(key) + bodies.append((l, how, val, l if kind == 'here' else l - 1, None, l)) + break + bodies.sort(key=lambda b: (b[5], b[0])) + return bodies + +idx = 0 +for path in files: + try: + text = open(path, encoding='utf-8', errors='replace').read() + except OSError as e: + print('\t'.join(['0', path, '0', 'read', '0', 'unextractable', str(e)])); continue + toks = tokenize(text) + for (line, how, val, offset, problem, dline) in find_bodies(path, toks): + idx += 1 + if problem: + print('\t'.join([str(idx), path, str(line), how, '0', 'unextractable', problem])) + else: + with open(os.path.join(out_dir, '%d.body' % idx), 'w', encoding='utf-8') as f: f.write(val + '\n') + print('\t'.join([str(idx), path, str(dline), how, str(offset), 'body', ''])) +PY +} + +# ---- the check over a list of files; prints one line per body, returns 1 on any failure ---- +check_files() { + local T; T="$(mktemp -d)" + local rc=0 n=0 index + index="$T/index.tsv" + if ! extract "$T" "$@" > "$index"; then echo "FAIL extractor error"; rm -rf "$T"; return 1; fi + while IFS="$(printf '\t')" read -r idx file line how offset status detail; do + [ -n "$idx" ] || continue + n=$((n + 1)) + if [ "$status" = "unextractable" ]; then + echo "FAIL $file:$line unextractable body ($how): $detail"; rc=1; continue + fi + local err + if err="$(bash -n "$T/$idx.body" 2>&1)"; then + echo "ok $file:$line ($how)" + else + # bash prints ": line N: message"; map N to the file line + err="$(printf '%s\n' "$err" | sed -e "s#^$T/$idx.body: ##" | awk -v off="$offset" '{ if (match($0, /^line [0-9]+:/)) { n = substr($0, 6, RLENGTH - 6) + 0; sub(/^line [0-9]+:/, "file line " (n + off) " (body line " n "):") } print }' | tr '\n' ' ')" + echo "FAIL $file:$line ($how): $err"; rc=1 + fi + done < "$index" + rm -rf "$T" + echo "bash-body-check: $n bodies in $# files, $( [ $rc = 0 ] && echo 'all parse' || echo 'FAILURES above')" + return $rc +} + +if [ "${1:-}" = "--self-test" ]; then + F="$HERE/fixtures" + ok_out="$(check_files "$F/bash-body-ok.ps1" 2>&1)"; ok_rc=$? + bad_out="$(check_files "$F/bash-body-lost-quote.ps1" 2>&1)"; bad_rc=$? + unx_out="$(check_files "$F/bash-body-unextractable.ps1" 2>&1)"; unx_rc=$? + echo "self-test correct fixture: rc $ok_rc"; printf '%s\n' "$ok_out" | sed 's/^/ /' + echo "self-test lost-quote fixture: rc $bad_rc"; printf '%s\n' "$bad_out" | sed 's/^/ /' + echo "self-test unextractable fixture: rc $unx_rc"; printf '%s\n' "$unx_out" | sed 's/^/ /' + [ $ok_rc -eq 0 ] || { echo "SELF-TEST FAILED: the correct fixture was flagged"; exit 1; } + [ "$(printf '%s\n' "$ok_out" | grep -c '^ok ')" -eq 8 ] || { echo "SELF-TEST FAILED: the correct fixture has 8 bodies, a different number was found"; exit 1; } + [ $bad_rc -ne 0 ] || { echo "SELF-TEST FAILED: the lost-quote fixture passed"; exit 1; } + for want in 'bash-body-lost-quote.ps1:9 ' 'bash-body-lost-quote.ps1:18 ' 'bash-body-lost-quote.ps1:20 '; do + printf '%s\n' "$bad_out" | grep -q "^FAIL .*$want" || { echo "SELF-TEST FAILED: the lost quote at $want was not reported"; exit 1; } + done + [ "$(printf '%s\n' "$bad_out" | grep -c '^ok ')" -eq 1 ] || { echo "SELF-TEST FAILED: the one correct body in the lost-quote fixture was not reported ok"; exit 1; } + [ $unx_rc -ne 0 ] || { echo "SELF-TEST FAILED: the unextractable fixture passed"; exit 1; } + [ "$(printf '%s\n' "$unx_out" | grep -c 'unextractable body')" -eq 3 ] || { echo "SELF-TEST FAILED: 3 unextractable bodies expected"; exit 1; } + echo "self-test passed: bash -n fires on the lost quotes, the unreadable bodies fail, the correct bodies pass" + exit 0 +fi + +if [ $# -gt 0 ]; then + check_files "$@"; exit $? +fi +cd "$REPO" +files=$(git ls-files 'relay/playbooks/**' 'tools/**' | grep -E '\.ps1$' | grep -v '^tools/ci/fixtures/' || true) +if [ -z "$files" ]; then echo "bash-body-check: no .ps1 files under relay/playbooks/ or tools/"; exit 0; fi +# shellcheck disable=SC2086 +check_files $files diff --git a/tools/ci/fixtures/bash-body-lost-quote.ps1 b/tools/ci/fixtures/bash-body-lost-quote.ps1 new file mode 100644 index 00000000..c01e7373 --- /dev/null +++ b/tools/ci/fixtures/bash-body-lost-quote.ps1 @@ -0,0 +1,24 @@ +# Fixture for tools/ci/bash-body-check.sh --self-test: the lost-quote class of 5 October 2026. Three bodies do not +# parse (the here-string at line 9, the literal at line 18, the variable at line 20); one does (line 23). The check +# must report the three with the bash -n error and exit 1. Never run; a stand-in for a job script. +$ErrorActionPreference = 'Continue' +$distro = 'Ubuntu-24.04' +$job = Join-Path $env:TEMP 'igneum-fixture' +$jobW = '/mnt/c/igneum-fixture' +# 1. pc1-cpu-prove.ps1, first version: an apostrophe inside a single-quoted awk program ends the quote (body line 3) +$bash = @" +set -uo pipefail +for FX in a b; do + awk -v fx="`$FX" '/Maximum resident set size/ { print "RESULT " fx " the job's peak kb " `$NF }' "$jobW/results/`$FX-time.txt" +done +"@ +$bashFile = Join-Path $job 'body.sh' +[IO.File]::WriteAllText($bashFile, ($bash -replace "`r`n", "`n")) +& wsl.exe -d $distro -u root -- bash (WslPath $bashFile) 2>&1 | ForEach-Object { "$_" } +& wsl.exe -d $distro -- bash -c 'echo "installer started; ls /opt/igneum' 2>&1 | ForEach-Object { "$_" } +# 3. the 0.3.10 installer job: a closing quote dropped on the way through PowerShell +$cmd = "cd /opt/igneum && echo `"started && ./run.sh" +& wsl.exe -d $distro -- bash -lc $cmd 2>&1 | ForEach-Object { "$_" } +# 4. a correct body beside them, reported ok +& wsl.exe -d $distro -- bash -lc 'id; uname -r' 2>&1 | ForEach-Object { "$_" } +exit 0 diff --git a/tools/ci/fixtures/bash-body-ok.ps1 b/tools/ci/fixtures/bash-body-ok.ps1 new file mode 100644 index 00000000..ff7062d9 --- /dev/null +++ b/tools/ci/fixtures/bash-body-ok.ps1 @@ -0,0 +1,53 @@ +# Fixture for tools/ci/bash-body-check.sh --self-test: every way a job script hands bash a body, all of them +# correct. The check must report 8 bodies ok and exit 0. Never run; a stand-in for a job script. +$ErrorActionPreference = 'Continue' +$distro = 'Ubuntu-24.04' +$job = Join-Path $env:TEMP 'igneum-fixture' +$pkgW = '/mnt/c/igneum-fixture/pkg' +$FIXTURES = 'block-56-transfers-3shards block-78-increment' +function WslPath($p) { '/mnt/c' + ($p.Substring(2) -replace '\\', '/') } +# 1. a double-quoted here-string written to a file and run with bash (the pc1-cpu-prove.ps1 shape, fixed version) +$bash = @" +set -uo pipefail +export PATH="`$HOME/.cargo/bin:`$PATH"; [ -f "`$HOME/.cargo/env" ] && . "`$HOME/.cargo/env" +stamp() { date -u +%Y-%m-%dT%H:%M:%SZ; } +PKG='$pkgW'; DEST="`$HOME/igneum-prove-cpu" +echo "RESULT wsl `$(stamp) cores `$(nproc) ram_total_mb `$(free -m | awk '/Mem:/ {print `$2}')" +for FX in $FIXTURES; do + awk -v fx="`$FX" '/Maximum resident set size/ {r=`$NF} END {print "RESULT " fx " max_rss_kb=" r}' "`$DEST/`$FX-time.txt" + echo "RESULT `$FX prove end `$(stamp) exit `${PIPESTATUS[0]}" +done +"@ +$bashFile = Join-Path $job 'cpu-prove.sh' +[IO.File]::WriteAllText($bashFile, ($bash -replace "`r`n", "`n"), (New-Object System.Text.UTF8Encoding $false)) +$chk = (& wsl.exe -d $distro -u root -- bash -n (WslPath $bashFile) 2>&1); if ($LASTEXITCODE -ne 0) { "RESULT syntax FAILED: $chk"; exit 1 } +& wsl.exe -d $distro -u root -- bash (WslPath $bashFile) 2>&1 | ForEach-Object { "$_" } +# 2. a single-quoted literal after -lc (the wsl-setup.ps1 shape) +& wsl.exe -d $distro -- bash -lc 'id; uname -r; nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null || echo "no GPU visible inside WSL"' 2>&1 | ForEach-Object { "$_" } +# 3. a double-quoted string in a variable, interpolation left as-is (the prover-setup.ps1 shape) +$linuxDir = '/mnt/c/igneum-prove/igneum-prove-wsl2' +$cmd = "echo igneum | sudo -S -v 2>/dev/null; sudo -n true || echo 'sudo still asks for a password'; cd $linuxDir && bash ./setup-wsl.sh" +& wsl.exe -d $distro -u igneum -- bash -lc $cmd 2>&1 | ForEach-Object { "$_" } +# 4. single-quoted pieces joined with + over three lines (the wsl-setup.ps1 shape) +$mk = 'id igneum >/dev/null 2>&1 || (useradd -m -s /bin/bash igneum && echo igneum:igneum | chpasswd); ' + + 'echo "igneum ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/igneum && chmod 440 /etc/sudoers.d/igneum; ' + + 'printf "[user]\ndefault=igneum\n" > /etc/wsl.conf; id igneum' +& wsl.exe -d $distro -u root -- bash -c $mk 2>&1 | ForEach-Object { "$_" } +# 5. a concatenation in parentheses with a variable in the middle (the shard-test.ps1 shape) +& wsl.exe -d $distro -u igneum -- bash -lc ("sudo -n true 2>/dev/null || echo 'sudo asks for a password'; cd " + $linuxDir + " && bash ./setup-wsl.sh") 2>&1 | ForEach-Object { "$_" } +# 6. the Start-Process argument list, -c as its own quoted argument +$p = Start-Process -FilePath 'wsl.exe' -ArgumentList @('-d', $distro, '--', 'bash', '-c', 'pkill -f igneum-prove-host; pkill -f prove-shard.sh; true') -NoNewWindow -PassThru +# 7. a single-quoted here-string piped to Set-Content, the file then run by a derived path +$body = @' +set -euo pipefail +cd "$HOME/igneum" && ./igneum-miner --help | sed 's/^/RESULT help /' +'@ +$sh = Join-Path $job 'body.sh' +$body | Set-Content -Path $sh -Encoding ascii +$shW = WslPath $sh +& wsl.exe -d $distro -- bash $shW 2>&1 | ForEach-Object { "$_" } +# 8. -ec with a doubled quote and backtick escapes in a double-quoted string +& wsl.exe -d $distro -- bash -ec "echo ""started""; printf '%s`n' `"done`"" 2>&1 | ForEach-Object { "$_" } +# not bodies: a script file by path, and bash -n on a file +& wsl.exe -d $distro -u igneum -- bash /mnt/c/igneum-prove/igneum-prove-wsl2/prove-block.sh fixture core 2>&1 | ForEach-Object { "$_" } +exit 0 diff --git a/tools/ci/fixtures/bash-body-unextractable.ps1 b/tools/ci/fixtures/bash-body-unextractable.ps1 new file mode 100644 index 00000000..d6c8a9b0 --- /dev/null +++ b/tools/ci/fixtures/bash-body-unextractable.ps1 @@ -0,0 +1,14 @@ +# Fixture for tools/ci/bash-body-check.sh --self-test: three bash -c bodies the extractor sees but cannot read. +# Each must be reported as "unextractable body" and the check must exit 1: a skip would be a hole in the class check +# (the coordinator's rule, 5 October 2026). Never run; a stand-in for a job script. +$ErrorActionPreference = 'Continue' +$distro = 'Ubuntu-24.04' +# 1. the body comes from a command, not a string +$cmd = (Get-Content 'C:\igneum\body.txt') -join '; ' +& wsl.exe -d $distro -- bash -c $cmd 2>&1 | ForEach-Object { "$_" } +# 2. a format operator inside the parentheses +$tpl = 'cd {0} && ./run.sh' +& wsl.exe -d $distro -- bash -c ($tpl -f '/opt/igneum') 2>&1 | ForEach-Object { "$_" } +# 3. a variable with no assignment above +& wsl.exe -d $distro -- bash -lc $fromElsewhere 2>&1 | ForEach-Object { "$_" } +exit 0 From b3d15a73040ada34642757da84134a537f242dfe Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:39:11 +0000 Subject: [PATCH 078/158] counter-asic-2-node.md: the two owed items (gate run 2 on the composed class, per-day dataset reuse in the CUDA and OpenCL workers) Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-2-node.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index 0ce5abef..7f93f4af 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -111,4 +111,6 @@ The mixer x4 build-time number the rollout asks for is not visible here: the CPU - The Metal worker has no class v3 generator (it regenerates from the seed in Swift): the integration branch adds generator 3 there, or the Mac mines v3 with the OpenCL worker from a pack. - The GPU workers' class refusal was checked by the C test of `pf_pack_class_ok` and the syntax of both hosts, not by a live worker on a v3 pack: the integration's bit-exact gate (G1) is where a real worker first builds a v3 pack. - `next_pair` keeps the current era seed for the next epoch; an epoch boundary that is also an era boundary (once per 180 days) would prepare the wrong era, and the job line then names the right one, so the worker refuses the prepared pair and the miner prepares again (one wasted compile, no wrong block). The VDF era seed replaces the stand-in before this matters. +- Owed: gate run 2 on the composed class (ca2-era and ca2-cache merged onto the mixer tip; both branches still base on 464d6e1 at 22:40Z, before the mixer, so the era agent's rebased commit comes first), then a rebuild of `target-ca2/release` and `class-v3.mjs` again. +- Owed (0.3.12, coordinator's ask of 5 October 2026 22:20Z): per-day dataset reuse in the CUDA and OpenCL workers (a `Day` object shared by consecutive pairs, the cache freed after the build); the Metal worker already keys datasets by day. Until then the iGPU tier mines v3 with a dataset rebuild per epoch on those two workers. - Wire compatibility: `RpcPowEpochInfo` gained five fields in its Borsh form (wRPC) and five proto fields (gRPC); the gRPC side reads an old node's zeros as v2 / never / none; the Borsh form is versioned by `GetBlockTemplateResponse` (version 2 carries the whole struct), so a 0.3.11 wRPC client against a 0.3.10 node reads short: the miner uses gRPC, the console reads JSON (serde defaults). From 15589715212ffbaf603d728a55623f01c42258ab Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:25:50 +0000 Subject: [PATCH 079/158] era layout (Counter ASIC 2.0 layers 4 and 8) behind the class flag, on the ca2-v3 seam: the 7-draw era stream from E_n (stride, interleave, width pinned at 4 B), per-site window draws (dataset, half, quarter at a 256 MiB floor), the strided windowed load address in the interpreter, the acceptance mirror and the three emitters, the interleaved dataset layout riding with the program (memhard::Layout, mh_t/mh_j/mh_addr, Epoch::dataset_word), V3_CLASS with the era drawn inside by generate_from_seed_bytes_program_class, --era / --era-widths on the CLI, six class v3 era packs (packs-ca2-era), tests, the design doc, host.cu/host.c deriving host words through the pack's mh_word, emu/test-layout.sh, the PC 1 playbook Squashed from six commits (tag ca2-era-pre-squash) for one merge. Co-Authored-By: Claude Fable 5.1 --- docs/bench-log.md | 47 +++ docs/plans/era-layout.md | 177 +++++++++ docs/plans/read-width.md | 109 +++++ igneum-pow/src/accept.rs | 5 +- igneum-pow/src/emit.rs | 352 ++++++++++++---- igneum-pow/src/generator.rs | 318 ++++++++++++++- igneum-pow/src/main.rs | 95 ++++- igneum-pow/src/memhard.rs | 128 +++++- igneum-pow/src/verify.rs | 153 ++++++- igneum-pow/tests/packs.rs | 264 +++++++++++- proto-cuda/emu/test-layout.sh | 28 ++ proto-cuda/host.cu | 8 +- proto-cuda/packs-ca2-era/era-0/kernel.cl | 281 +++++++++++++ proto-cuda/packs-ca2-era/era-0/kernel.cu | 163 ++++++++ .../packs-ca2-era/era-0/kernel_bound.cl | 375 ++++++++++++++++++ .../packs-ca2-era/era-0/kernel_bound.cu | 123 ++++++ proto-cuda/packs-ca2-era/era-0/memhard.h | 112 ++++++ proto-cuda/packs-ca2-era/era-0/memhard.metal | 109 +++++ proto-cuda/packs-ca2-era/era-0/program.h | 76 ++++ proto-cuda/packs-ca2-era/era-0/program.json | 143 +++++++ proto-cuda/packs-ca2-era/era-0/program.metal | 109 +++++ .../packs-ca2-era/era-0/program_bound.metal | 111 ++++++ proto-cuda/packs-ca2-era/era-0/seeds.txt | 5 + proto-cuda/packs-ca2-era/era-0/vectors.h | 57 +++ proto-cuda/packs-ca2-era/era-0/vectors.json | 36 ++ proto-cuda/packs-ca2-era/era-1/kernel.cl | 281 +++++++++++++ proto-cuda/packs-ca2-era/era-1/kernel.cu | 163 ++++++++ .../packs-ca2-era/era-1/kernel_bound.cl | 375 ++++++++++++++++++ .../packs-ca2-era/era-1/kernel_bound.cu | 123 ++++++ proto-cuda/packs-ca2-era/era-1/memhard.h | 112 ++++++ proto-cuda/packs-ca2-era/era-1/memhard.metal | 109 +++++ proto-cuda/packs-ca2-era/era-1/program.h | 76 ++++ proto-cuda/packs-ca2-era/era-1/program.json | 143 +++++++ proto-cuda/packs-ca2-era/era-1/program.metal | 109 +++++ .../packs-ca2-era/era-1/program_bound.metal | 111 ++++++ proto-cuda/packs-ca2-era/era-1/seeds.txt | 5 + proto-cuda/packs-ca2-era/era-1/vectors.h | 57 +++ proto-cuda/packs-ca2-era/era-1/vectors.json | 36 ++ proto-cuda/packs-ca2-era/era-2/kernel.cl | 281 +++++++++++++ proto-cuda/packs-ca2-era/era-2/kernel.cu | 163 ++++++++ .../packs-ca2-era/era-2/kernel_bound.cl | 375 ++++++++++++++++++ .../packs-ca2-era/era-2/kernel_bound.cu | 123 ++++++ proto-cuda/packs-ca2-era/era-2/memhard.h | 112 ++++++ proto-cuda/packs-ca2-era/era-2/memhard.metal | 109 +++++ proto-cuda/packs-ca2-era/era-2/program.h | 76 ++++ proto-cuda/packs-ca2-era/era-2/program.json | 143 +++++++ proto-cuda/packs-ca2-era/era-2/program.metal | 109 +++++ .../packs-ca2-era/era-2/program_bound.metal | 111 ++++++ proto-cuda/packs-ca2-era/era-2/seeds.txt | 5 + proto-cuda/packs-ca2-era/era-2/vectors.h | 57 +++ proto-cuda/packs-ca2-era/era-2/vectors.json | 36 ++ proto-cuda/packs-ca2-era/era-3/kernel.cl | 281 +++++++++++++ proto-cuda/packs-ca2-era/era-3/kernel.cu | 163 ++++++++ .../packs-ca2-era/era-3/kernel_bound.cl | 375 ++++++++++++++++++ .../packs-ca2-era/era-3/kernel_bound.cu | 123 ++++++ proto-cuda/packs-ca2-era/era-3/memhard.h | 112 ++++++ proto-cuda/packs-ca2-era/era-3/memhard.metal | 109 +++++ proto-cuda/packs-ca2-era/era-3/program.h | 76 ++++ proto-cuda/packs-ca2-era/era-3/program.json | 143 +++++++ proto-cuda/packs-ca2-era/era-3/program.metal | 109 +++++ .../packs-ca2-era/era-3/program_bound.metal | 111 ++++++ proto-cuda/packs-ca2-era/era-3/seeds.txt | 5 + proto-cuda/packs-ca2-era/era-3/vectors.h | 57 +++ proto-cuda/packs-ca2-era/era-3/vectors.json | 36 ++ proto-cuda/packs-ca2-era/era-4/kernel.cl | 281 +++++++++++++ proto-cuda/packs-ca2-era/era-4/kernel.cu | 163 ++++++++ .../packs-ca2-era/era-4/kernel_bound.cl | 375 ++++++++++++++++++ .../packs-ca2-era/era-4/kernel_bound.cu | 123 ++++++ proto-cuda/packs-ca2-era/era-4/memhard.h | 112 ++++++ proto-cuda/packs-ca2-era/era-4/memhard.metal | 109 +++++ proto-cuda/packs-ca2-era/era-4/program.h | 76 ++++ proto-cuda/packs-ca2-era/era-4/program.json | 143 +++++++ proto-cuda/packs-ca2-era/era-4/program.metal | 109 +++++ .../packs-ca2-era/era-4/program_bound.metal | 111 ++++++ proto-cuda/packs-ca2-era/era-4/seeds.txt | 5 + proto-cuda/packs-ca2-era/era-4/vectors.h | 57 +++ proto-cuda/packs-ca2-era/era-4/vectors.json | 36 ++ proto-cuda/packs-ca2-era/era-5/kernel.cl | 281 +++++++++++++ proto-cuda/packs-ca2-era/era-5/kernel.cu | 163 ++++++++ .../packs-ca2-era/era-5/kernel_bound.cl | 375 ++++++++++++++++++ .../packs-ca2-era/era-5/kernel_bound.cu | 123 ++++++ proto-cuda/packs-ca2-era/era-5/memhard.h | 112 ++++++ proto-cuda/packs-ca2-era/era-5/memhard.metal | 109 +++++ proto-cuda/packs-ca2-era/era-5/program.h | 76 ++++ proto-cuda/packs-ca2-era/era-5/program.json | 143 +++++++ proto-cuda/packs-ca2-era/era-5/program.metal | 109 +++++ .../packs-ca2-era/era-5/program_bound.metal | 111 ++++++ proto-cuda/packs-ca2-era/era-5/seeds.txt | 5 + proto-cuda/packs-ca2-era/era-5/vectors.h | 57 +++ proto-cuda/packs-ca2-era/era-5/vectors.json | 36 ++ .../mx4-devnet-epoch0/kernel.cl | 137 +++---- .../mx4-devnet-epoch0/kernel.cu | 129 +++--- .../mx4-devnet-epoch0/kernel_bound.cl | 263 ++++++------ .../mx4-devnet-epoch0/kernel_bound.cu | 126 +++--- .../mx4-devnet-epoch0/memhard.h | 8 +- .../mx4-devnet-epoch0/memhard.metal | 11 +- .../mx4-devnet-epoch0/program.h | 16 +- .../mx4-devnet-epoch0/program.json | 146 ++++--- .../mx4-devnet-epoch0/program.metal | 126 +++--- .../mx4-devnet-epoch0/program_bound.metal | 126 +++--- .../mx4-devnet-epoch0/vectors.h | 30 +- .../mx4-devnet-epoch0/vectors.json | 28 +- proto-cuda/packs-readwidth/scr0k32/kernel.cl | 12 +- .../packs-readwidth/scr0k32/kernel_bound.cl | 26 +- proto-cuda/packs-readwidth/scr2k128/kernel.cl | 12 +- .../packs-readwidth/scr2k128/kernel_bound.cl | 26 +- proto-cuda/packs-readwidth/scr2k32/kernel.cl | 12 +- .../packs-readwidth/scr2k32/kernel_bound.cl | 26 +- proto-cuda/packs-readwidth/scr4k128/kernel.cl | 12 +- .../packs-readwidth/scr4k128/kernel_bound.cl | 26 +- proto-cuda/packs-readwidth/scr4k32/kernel.cl | 12 +- .../packs-readwidth/scr4k32/kernel_bound.cl | 26 +- proto-cuda/packs-readwidth/scr8k128/kernel.cl | 12 +- .../packs-readwidth/scr8k128/kernel_bound.cl | 26 +- proto-cuda/packs-readwidth/scr8k32/kernel.cl | 12 +- .../packs-readwidth/scr8k32/kernel_bound.cl | 26 +- proto-opencl/host.c | 6 +- relay/playbooks/ca2-era-pc1.ps1 | 93 +++++ 118 files changed, 12573 insertions(+), 822 deletions(-) create mode 100644 docs/plans/era-layout.md create mode 100644 docs/plans/read-width.md create mode 100755 proto-cuda/emu/test-layout.sh create mode 100644 proto-cuda/packs-ca2-era/era-0/kernel.cl create mode 100644 proto-cuda/packs-ca2-era/era-0/kernel.cu create mode 100644 proto-cuda/packs-ca2-era/era-0/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-era/era-0/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-era/era-0/memhard.h create mode 100644 proto-cuda/packs-ca2-era/era-0/memhard.metal create mode 100644 proto-cuda/packs-ca2-era/era-0/program.h create mode 100644 proto-cuda/packs-ca2-era/era-0/program.json create mode 100644 proto-cuda/packs-ca2-era/era-0/program.metal create mode 100644 proto-cuda/packs-ca2-era/era-0/program_bound.metal create mode 100644 proto-cuda/packs-ca2-era/era-0/seeds.txt create mode 100644 proto-cuda/packs-ca2-era/era-0/vectors.h create mode 100644 proto-cuda/packs-ca2-era/era-0/vectors.json create mode 100644 proto-cuda/packs-ca2-era/era-1/kernel.cl create mode 100644 proto-cuda/packs-ca2-era/era-1/kernel.cu create mode 100644 proto-cuda/packs-ca2-era/era-1/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-era/era-1/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-era/era-1/memhard.h create mode 100644 proto-cuda/packs-ca2-era/era-1/memhard.metal create mode 100644 proto-cuda/packs-ca2-era/era-1/program.h create mode 100644 proto-cuda/packs-ca2-era/era-1/program.json create mode 100644 proto-cuda/packs-ca2-era/era-1/program.metal create mode 100644 proto-cuda/packs-ca2-era/era-1/program_bound.metal create mode 100644 proto-cuda/packs-ca2-era/era-1/seeds.txt create mode 100644 proto-cuda/packs-ca2-era/era-1/vectors.h create mode 100644 proto-cuda/packs-ca2-era/era-1/vectors.json create mode 100644 proto-cuda/packs-ca2-era/era-2/kernel.cl create mode 100644 proto-cuda/packs-ca2-era/era-2/kernel.cu create mode 100644 proto-cuda/packs-ca2-era/era-2/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-era/era-2/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-era/era-2/memhard.h create mode 100644 proto-cuda/packs-ca2-era/era-2/memhard.metal create mode 100644 proto-cuda/packs-ca2-era/era-2/program.h create mode 100644 proto-cuda/packs-ca2-era/era-2/program.json create mode 100644 proto-cuda/packs-ca2-era/era-2/program.metal create mode 100644 proto-cuda/packs-ca2-era/era-2/program_bound.metal create mode 100644 proto-cuda/packs-ca2-era/era-2/seeds.txt create mode 100644 proto-cuda/packs-ca2-era/era-2/vectors.h create mode 100644 proto-cuda/packs-ca2-era/era-2/vectors.json create mode 100644 proto-cuda/packs-ca2-era/era-3/kernel.cl create mode 100644 proto-cuda/packs-ca2-era/era-3/kernel.cu create mode 100644 proto-cuda/packs-ca2-era/era-3/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-era/era-3/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-era/era-3/memhard.h create mode 100644 proto-cuda/packs-ca2-era/era-3/memhard.metal create mode 100644 proto-cuda/packs-ca2-era/era-3/program.h create mode 100644 proto-cuda/packs-ca2-era/era-3/program.json create mode 100644 proto-cuda/packs-ca2-era/era-3/program.metal create mode 100644 proto-cuda/packs-ca2-era/era-3/program_bound.metal create mode 100644 proto-cuda/packs-ca2-era/era-3/seeds.txt create mode 100644 proto-cuda/packs-ca2-era/era-3/vectors.h create mode 100644 proto-cuda/packs-ca2-era/era-3/vectors.json create mode 100644 proto-cuda/packs-ca2-era/era-4/kernel.cl create mode 100644 proto-cuda/packs-ca2-era/era-4/kernel.cu create mode 100644 proto-cuda/packs-ca2-era/era-4/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-era/era-4/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-era/era-4/memhard.h create mode 100644 proto-cuda/packs-ca2-era/era-4/memhard.metal create mode 100644 proto-cuda/packs-ca2-era/era-4/program.h create mode 100644 proto-cuda/packs-ca2-era/era-4/program.json create mode 100644 proto-cuda/packs-ca2-era/era-4/program.metal create mode 100644 proto-cuda/packs-ca2-era/era-4/program_bound.metal create mode 100644 proto-cuda/packs-ca2-era/era-4/seeds.txt create mode 100644 proto-cuda/packs-ca2-era/era-4/vectors.h create mode 100644 proto-cuda/packs-ca2-era/era-4/vectors.json create mode 100644 proto-cuda/packs-ca2-era/era-5/kernel.cl create mode 100644 proto-cuda/packs-ca2-era/era-5/kernel.cu create mode 100644 proto-cuda/packs-ca2-era/era-5/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-era/era-5/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-era/era-5/memhard.h create mode 100644 proto-cuda/packs-ca2-era/era-5/memhard.metal create mode 100644 proto-cuda/packs-ca2-era/era-5/program.h create mode 100644 proto-cuda/packs-ca2-era/era-5/program.json create mode 100644 proto-cuda/packs-ca2-era/era-5/program.metal create mode 100644 proto-cuda/packs-ca2-era/era-5/program_bound.metal create mode 100644 proto-cuda/packs-ca2-era/era-5/seeds.txt create mode 100644 proto-cuda/packs-ca2-era/era-5/vectors.h create mode 100644 proto-cuda/packs-ca2-era/era-5/vectors.json create mode 100644 relay/playbooks/ca2-era-pc1.ps1 diff --git a/docs/bench-log.md b/docs/bench-log.md index 9c0171fc..d9bb12a0 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -1524,3 +1524,50 @@ What is measured: one BLS12-381 aggregate signature over 16 summed G1 keys plus | on, split 90 s | v3 | 0 / 2 | none / 3 | 278 / 265 | apart | none | 3 on n0 | 2 (n0 reconnected 6 s after the heal, A's chain at about 58 DAA, inside the table) | Reading (the NEW finding, ledger C4). With the module off GHOSTDAG alone converges on the heavier chain and the losing side's records re-determine (F24 works when the chain moves). With the module on the overlay holds during the split (A, with 30% of the frozen table, locks nothing; B locks 7 and 8) and then fails at the heal in the shipped node: B's certificates for blocks off n0's chain are "kept pending until the chain decides (no lock at this index)", n0's chain never decides because GHOSTDAG keeps its heavier tip and nothing turns the certificate into a fork-choice constraint, and once n0's last lock (index 7, DAA 209) is one window old (DAA 329) the frozen table stops applying on A's chain ("no frozen table (no lock on this chain inside the window)"), A's two keys are 100% of A's own window (B's post-cut blocks are red there) and n0 locks 10, 11, 12 alone; B's certificates for 10 and 11 then log CONFLICTING on n0 (n0 log, 17:27:04 to 17:29:54 BST). A finality fork from a 96-s honest partition, no attacker, table intact at the heal; the 150-s run and the v2 control end the same way. The spec's fork choice ("GHOSTDAG among tips through all certified checkpoints", 3.5) is therefore implemented only for certificates over blocks already on the node's chain. Fix named in the ledger entry: verify an off-chain certificate against the table at its own block and let it constrain fork choice (a certificate-driven reorg), then re-determine. Raw: `scratchpad fud-a/c4-results-*.md`, node logs `c4-on90-tmp/`, `c4-v2-control-tmp/`. + +## 5 October 2026 (night), read width of the lottery hash: 4, 16 and 64-byte loads, a per-load mix, a written scratch; three cards (gate 1 experiment, cryptographer) + +Branch `readwidth` (commits 019b014, b970dda, 4badcee, a9e002c, d0018cf and the entry commit); plan and recommendation in `docs/plans/read-width.md`. Nothing here changes consensus: every class sits behind `igneum-pow --class` and the default class is generator version 2 byte for byte (`igneum-pow/tests/packs.rs` passes on the four pinned packs after every commit). Question (the project lead, after "the 9070 XT on the eGPU" above): would wider reads keep the latency-bound random-access property while closing the vendor gap. Additions from the coordinator: a per-load width drawn from an era-fixed mix, and a written per-warp scratch (measurement only, no soundness claim). + +**What a class does** (`igneum-pow/src/generator.rs` `LoadClass`, `verify::fold_words`, the three emitters): a load of W words reads the W-word-aligned address `(src AND MASK) AND NOT (W - 1)` and folds every word into `dst` (`x = dst ^ w0; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]`); W = 1 is the lottery hash exactly (`w4` = pack `bcc1248b10cc90f2`). A mix class draws W per load with one extra `below(100)` roll per instruction. A scratch class `scrk` turns `k` of the 16 memory slots into read-modify-writes of a 16-byte slot of the lane's share of a `kb` KiB per-warp scratch (kernels run persistent warps, one per block or work-group; a slot reads as a seed-and-base fill until the unit writes it, behind a per-unit tag). Program ids carry the class. Dependent chain and 32-lane unit unchanged. + +**Correctness**: 23 packs (`proto-cuda/packs-readwidth/`, Rust CPU reference vectors). Every pack passed its three vector units and the cache and dataset checks on Metal (M5 Max, `proto-metal/packbench`), Apple OpenCL (`--bench-pack`), the RTX 5090 (NVRTC, `igneum-worker-cuda --bench`) and, the 16 width and mix packs, the RX 9070 XT (`igneum-worker-opencl --bench-pack`); the 2^24 batch fingerprints agree across all four runtimes on every pack (for example w16 `e7c890445b47af60`, w64 `836e56e7d496e980`, mixB-2 `a18ac73098c76007`). The clang CUDA emulation (w16, w64, w64x4, mixA-0, mixB-0: 3 of 3 units standalone and 2 of 2 in batch at 2 warps per block) and the clang OpenCL emulation (the same five plus scr2k32 and scr8k128, sub-group 32 and, width packs, wave64 with sub-group shuffles) pass with equal fingerprints per configuration. Acceptance rule on the classes: 60 candidates per class, rejection 0 to 14 of 60 (w16 and w64 as v2; the mixes the same; the scratch classes' distinct-address bound now covers dataset loads only, since a 64-slot lane scratch repeats slots by design). CPU verifier (M5 Max, one core, avg of 50 units, `igneum-pow bench --class`): v2 0.604 ms, w16 0.610, w64 0.630, w64x4 0.160, mix50-35-15 0.620, mix25-50-25 0.614, scr0k32 0.600 (1.004 on a loaded re-run), scr2k32 0.657, scr4k32 0.458, scr8k32 0.317, scr2k128 0.535, scr4k128 0.458, scr8k128 0.311; per hash divide by 32. The wide reads cost the verifier nothing (a lane's words lie in one item); scratch ops replace item derivations and make it cheaper. + +**Probes** (`--memprobe`, dependent random reads at 1024 MiB, G reads/s, best over lanes in flight; 4 B = the hash's pattern; the 5090 and 9070 XT with the card off in the app, the Mac through Apple OpenCL under a load average of 5 to 10): + +| Card | 4 B chase | 16 B | 64 B | 64 B as GB/s | coalesced stream GB/s (rated) | integer chain | +|---|---|---|---|---|---|---| +| RTX 5090 (PC 2, CUDA) | 17.5 to 18.2 | 18.0 to 19.9 | 9.1 to 15.7 (9.1 at 4 M lanes) | 584 | 1,579 (1,792) | 39.0 T op/s | +| RX 9070 XT (PC 1, eGPU, OpenCL) | 2.42 to 2.66 | 2.43 to 2.73 | 2.47 to 2.87 | 158 | 636 (640) | 6.2 T op/s | +| Apple M5 Max (Apple OpenCL, approximate) | 3.50 | 3.51 | 3.51 | 225 | 522 | | + +Reading: on the 9070 XT and the M5 Max a 64-byte dependent read costs exactly what a 4-byte one costs (the line is fetched either way); on the 5090 a 64-byte read costs about two 4-byte reads (two 32-byte sectors) and the 64 B chase at full occupancy sits at 584 GB/s, a third of the stream. + +**Hash rates** (5 timed dispatches of 2^24 nonces after a warm-up; Metal and the 9070 XT by device time, the 5090 by wall time around the stream sync; the PC cards switched off in the app for the run and restored, PC 1's 5090 and the integrated chip kept mining; the Mac under other agents' builds, load 4 to 9, so its absolute numbers carry that; the share = measured / (the card's probe ceiling at the class's widths / loads per hash)): + +| Class | dataset B/hash | RTX 5090 MH/s (share) | RX 9070 XT MH/s (share) | M5 Max Metal MH/s (share) | 5090 / 9070 | +|---|---|---|---|---|---| +| v2 (w4, the lottery hash) | 512 | 136.1 (0.96) | 18.15 (0.87) | 27.74 (1.01) | 7.5x | +| w16 | 2,048 | 139.8 (0.90) | 17.90 (0.84) | 28.26 (1.03) | 7.8x | +| w64 | 8,192 | 71.9 (0.58) | 17.59 (0.78) | 28.27 (1.03) | 4.1x | +| w64x4 (32 loads) | 2,048 | 275.3 (0.56) | 75.19 (0.84) | 109.7 (1.00) | 3.7x | +| mix50-35-15, 6 programs: min / median / max (spread of median) | 1,664 to 3,680 | 99.5 / 114.2 / 121.0 (18.8%) | 17.45 / 18.76 / 18.83 (7.4%) | 25.36 / 27.26 / 28.43 (11.3%) | 6.1x | +| mix25-50-25, 6 programs | 2,240 to 5,024 | 95.9 / 107.3 / 119.8 (22.3%) | 17.84 / 18.45 / 18.85 (5.5%) | 23.21 / 24.68 / 25.21 (8.1%) | 5.8x | + +Scratch (variant 5; N persistent warps; 5090: 2,048 warps launched against a resident capacity of 4,080 = 24 blocks/SM x 1 warp/block x 170 SMs at `--block-warps 1`, the occupancy query unchanged by the allocation (24 before and after); Metal: 2,048 to 16,384 warps swept, best shown; arena = N x per-warp size; the whole working set = 1 GiB dataset + 256 MiB cache + 128 MiB output + arena, under 2 GB on every row): + +| Class (k of 16 slots, KiB per warp) | scratch ops/hash | dataset B/hash | RTX 5090 MH/s (vs scr0, share) | M5 Max Metal MH/s (vs scr0) | RX 9070 XT MH/s | 5090 arena / working set | +|---|---|---|---|---|---|---| +| scr0k32 (control, persistent loop, no RMW) | 0 | 512 | 139.1 (0, 0.98) | 28.25 (0) | 17.88 (control, 0.86) | 64 MiB / 1.4 GiB | +| scr2k32 (12.5%) | 16 | 448 | 114.4 (-18%, 0.80) | 26.14 (-7%) | 14.65 (-18%) | 64 MiB / 1.4 GiB | +| scr4k32 (25%) | 32 | 384 | 109.8 (-21%, 0.76) | 31.74 (+12%) | 14.00 (-22%) | 64 MiB / 1.4 GiB | +| scr8k32 (50%) | 64 | 256 | 122.1 (-12%, 0.82) | 49.08 (+74%) | 14.17 (-21%) | 64 MiB / 1.4 GiB | +| scr2k128 (12.5%) | 16 | 448 | 110.1 (-21%, 0.77) | 26.24 (-7%) | 14.07 (-21%) | 256 MiB / 1.6 GiB | +| scr4k128 (25%) | 32 | 384 | 98.0 (-30%, 0.68) | 28.08 (-1%) | 13.14 (-27%) | 256 MiB / 1.6 GiB | +| scr8k128 (50%) | 64 | 256 | 72.8 (-48%, 0.49) | 35.44 (+25%) | 12.03 (-33%) | 256 MiB / 1.6 GiB | + +The 9070 XT rows are 2,048 persistent warps (4,096 within 1 percent), arena 64 MiB at 32 KiB and 256 MiB at 128 KiB, working set 1.4 and 1.6 GiB; its control (17.88, the persistent loop) equals its v2 rate (18.15) within 2 percent, and every RMW share costs it 18 to 33 percent: on AMD a scratch op is a dependent 16-byte read plus a write into a region the 64 MB Infinity Cache does not hold for 2,048 warps, so it is memory work there as on the 5090, not the cached op it is on Apple. Apple OpenCL on the same scratch packs (wall time, `--bench-pack --warps 2048`): scr0k32 27.85, scr2k32 28.58, scr4k32 32.43, scr8k32 47.93, scr2k128 25.67, scr4k128 27.24, scr8k128 32.93 MH/s, the Metal shape within 4 percent, fingerprints equal. Bytes moved per scratch op: 16 read + 16 written (the tag word included); per hash at 50 percent, 1,024 read + 1,024 written beside 256 of dataset reads. The 5090 at 4,096 launched warps (above its 4,080 resident) lost 2 to 26 percent (scr8k32 90.0 MH/s), so the rows above are the in-capacity launch. + +**Readings.** (1) Same count, wider: the vendor gap does not move at 16 B (7.8x) because on the 9070 XT a 4-byte read already costs a 64-byte line and on the 5090 a 16-byte read costs one 32-byte sector, the same as 4 bytes: the memory systems do identical work, only the fold's input grows. At 64 B the gap closes to 4.1x, entirely by the 5090 losing half its rate (its share falls to 0.58 and its DRAM traffic reaches 589 GB/s, 37 percent of the stream: bandwidth, not latency, bounds it), while the 9070 XT and the M5 Max do not move. (2) Fewer, wider (w64x4): 3.7x, but every card runs 4x faster because the dependent chain is 32 loads long instead of 128; the 5090 sits at a 0.56 share (bandwidth), so a chip with more bandwidth per dollar than a GPU gains, which is the Ethash shape the design avoids. (3) The mix: the hour-to-hour spread is 7 to 22 percent of the median per card (the 5090 the widest, because its 64-byte loads are the expensive ones and their count per program runs 2 to 8 of 16); the programs with many 64-byte loads (mixA-3, mixA-5, mixB-2) are the slow hours on the 5090 and the fast ones nowhere. (4) The scratch: on the 5090 every RMW share costs 12 to 48 percent against the persistent control, the 32 KiB arena less than the 128 KiB one (the smaller arena, 64 MiB over 2,048 warps, sits inside the 96 MB L2); on the M5 Max the 32 KiB rows are FASTER than the control (+12 and +74 percent at 25 and 50 percent), because the arena (128 MiB over 4,096 warps) lives in the chip's caches and a scratch op is cheaper than a dataset read, so replacing dataset loads raises the rate: the scratch at these sizes is not memory work on Apple and is partly cached on NVIDIA. The chip row for these variants comes from the ca2-soundness branch; what this entry gives is the GPU cost and the share. (5) Latency-bound shares: v2 0.87 to 1.01 on the three cards, w16 0.84 to 1.03, w64 0.58 (5090) and 0.78 (9070 XT); the Mac's shares above 1 are an Apple OpenCL probe under load against a Metal rate. + +Jobs: `run-readwidth-5090-20261005` and `run-readwidth-9070-20261005` (probes; the packs refused for their string seeds, fixed in a9e002c), `run-readwidth-5090-20261005c`, `run-readwidth-9070-20261005c` (benches), `run-readwidth-9070-scratch-20261005d` (the scratch packs after the `__local` fix d0018cf, AMD's compiler requires the exchange buffer at the kernel's outermost scope); read back with `node tools/jobs.mjs --all`. Mac commands and logs: `docs/plans/read-width.md` section 3. The worker exes for the jobs: `proto-cuda/nvrtc/build-windows.sh` on this branch (mingw), sha256 of the CUDA one `6f46336f...defe1`. diff --git a/docs/plans/era-layout.md b/docs/plans/era-layout.md new file mode 100644 index 00000000..661d9e02 --- /dev/null +++ b/docs/plans/era-layout.md @@ -0,0 +1,177 @@ +# Era layout: table layout and working set drawn per era and per program (Counter ASIC 2.0, layers 4 and 8) + +5 October 2026, branch `ca2-era`, worker "ca2-era". Status: Designed and Implemented behind the class flag (`LoadClass::era`, not the lottery hash); nothing here changes the default generator, the pinned packs or any live program. Measured sections are marked as such; everything else is design. + +Plan: `docs/plans/counter-asic-2.md`, layers 4 ("table layout drawn per era: item size, stride, interleave") and 8 ("working-set size drawn per program"). The era seed is `E_n` of spec 04 section 4.4. Confirmed on 5 October 2026 by grep over `igneum-pow/src` on branches master, readwidth and opencl-rdna4-telemetry: no era draw existed in code before this branch (`igneum-era`, `EraParams`, `era_seed`: no match). + +## 1. What is drawn, and from what + +Two streams, nothing else: + +| Stream | Seeded from | Draws | Sets | +|---|---|---|---| +| Era stream | `seed_words_from_bytes("igneum-era/" \|\| E_n)` words 0 and 1 (spec 01 section 1.13.1 wrote `n_le64 \|\| E_n`; the index is dropped here because `E_n` already commits to `n` through the VDF input of section 4.4 step 2, and the node's seam hands the generator the era bytes alone: `Epoch::from_chain_seeds(epoch, day, era, class, label)`, branch ca2-v3) | 7 per era, fixed | the load width `W`, the stride `(M, R)`, the interleave `pos[0..3]` | +| Program stream | the epoch seed words as today (spec 01 section 1.3.3) | 11 per instruction instead of 9: the 9 of version 2, then 2 window draws (a class whose loads are not version 2's takes the read-width width roll between them, ca2-v3's rule) | per load site: the window shrink `k_off` and its offset `o` | + +The era parameters change the class of every program of the era. The program stream does not see the era parameters (two eras with the same epoch seed draw the same instruction list and the same windows, and differ in width, stride and layout); this is what makes the six era packs below a controlled comparison. + +### 1.1 Era draw (proposed spec text for section 1.13.1, replacing its parameter table) + +One SplitMix64 stream `S` seeded with `lo = words[0] | (words[1] << 32)` of `seed_words_from_bytes("igneum-era/" || E_n)`. Seven draws, in this order, whether or not a value is used: + +1. `W = allowed[below(|allowed|)]`: the width in words of every dataset load of the era, drawn from the genesis-fixed ascending set `allowed`, a subset of {1, 4, 16} (4, 16 or 64 bytes). A set of one element pins the width; the draw is still consumed. The set is `{1}` (4 bytes, v2's load): the read-width decision of 5 October 2026 (`docs/plans/read-width.md`, "keep v2; w16 the only width that passes the rules and closes nothing") and the adoption rule of the same evening (a draw that changes the bytes per hash changes the rate; the six-era hash-rate spread must stay under 5 percent per card). The set is recorded in every pack (`IGNEUM_ERA_ALLOWED_WIDTHS`, `program.json` `era.allowed_widths`) and enters the program id. The code keeps the draw general so the set can be widened at genesis without a new derivation. +2. `M = low32(next()) OR 1`: the stride multiplier, odd, so `x -> x * M` is a bijection on 32-bit words. +3. `R = 1 + below(31)`: the stride rotation, in 1..31 (never 0: spec 01 section 1.14 item 3). +4. to 7. `r_i = next()` for `i` in 0..3: the interleave draws. Let `b = log2(W)` (0, 2 or 4) and `free = 4 - b`. Let `c = [b, b + 1, ..., 15]` (16 - b candidates). For `i` in `0..free`: `j = i + (r_i mod (16 - b - i))`, swap `c[i]` and `c[j]`. The interleave is `pos = [0, ..., b - 1] ++ sort(c[0..free])`, four ascending bit positions in 0..15. Draws `r_free..r_3` are consumed and ignored. + +The era parameters are `(W, M, R, pos)`. Era 0 of the devnet packs is listed in section 5. + +### 1.2 Dataset mapping with the interleave (replaces the last sentence of section 1.8.5) + +Word `w` of the dataset holds word `j(w)` of item `t(w)`, where `j(w)` is the 4-bit number whose bit `i` is bit `pos[i]` of `w`, and `t(w)` is `w` with bits `pos[0..3]` removed (the remaining bits in order). With `pos = [0, 1, 2, 3]` this is today's `dataset[w] = item(w >> 4)[w AND 15]`, byte for byte. + +Properties kept: + +- An item has the same value at every dataset size (item derivation is untouched), and because every `pos[i] < 16`, `dataset[w]` is the same at every dataset size of at least 2^16 words. The 1 GiB vectors of an era remain valid for words below 2^28 at any larger size, as today. +- The low `b = log2(W)` positions are 0..b-1, so the `W` words of one aligned load lie in one item (`t` is the same for all of them and `j` runs `j0 .. j0 + W - 1`). The verifier derives one item per lane per load, as today: the 4,096-item bound of section 1.11 holds (16 loads x 8 iterations x 32 lanes, whatever the width). +- The dataset build writes 16 words of one item to 16 addresses `w(t, j)` (a scatter of 4-byte writes instead of one 64-byte line when `pos != [0, 1, 2, 3]`). This is the only GPU cost of the interleave and is paid once per day; section 6 measures it. + +What the interleave does and does not buy. A chip that hard-wires today's layout (64-byte items, a 64-byte line per item) reads the wrong 15 words with every word once the era draws another layout; the layout changes every 180 days inside rules fixed at genesis. A chip whose address decoder can permute 28 address lines under firmware control pays nothing for it. The honest claim is the first sentence only. The stride below is the same kind of lever: two integer operations per load on a chip, nothing on a GPU. + +### 1.3 Load address (replaces "a load reads one 4-byte word at `src AND MASK`" in section 1.5 for era programs) + +For a load site with window draws `(k_off, o)` (section 1.4), a dataset of `2^D` words (`MASK = 2^D - 1`), and register value `x`: + +``` +k = min(k_off, D - 26) (0 when D <= 26) +y = rotl(x * M, R) +idx = ((y AND (MASK >> k)) OR ((o AND (2^k - 1)) << (D - k))) AND MASK +base = idx AND NOT (W - 1) (W words from base are folded as verify::fold_words) +``` + +Uniformity: `x * M` with `M` odd and `rotl` are bijections of the 32-bit word, so `y` is uniform when `x` is; `y AND (MASK >> k)` is uniform on the window; the offset picks which of the `2^k` aligned windows. Branch-free, integer only, three operations before the mask (multiply, rotate, and-or) against one today. The emitted text has one form per dialect, checkable by text search (section 1.14 item 2): CUDA and OpenCL `ds[((rotl_imm(rN * 0x........u, Ru) & 0x........u) | 0x........u) & mask]`, Metal the same with `dataset[` and `& MASK]`. + +### 1.4 Window draw per load site (layer 8; proposed text for section 1.4.3) + +After the nine draws of version 2 (and the width roll, for a class whose loads are not version 2's), every instruction takes two more draws, used only on a load slot: + +``` +k_off = below(3) window = the dataset, a half or a quarter of it (2^(D - k_off) words) +o = low32(next()) AND (2^k_off - 1) which aligned window +``` + +Bounds: the window never goes below `2^26` words (256 MiB; `k = min(k_off, D - 26)` in 1.3), which exceeds the largest on-chip cache of any card in the benchmark (the RTX 5090's 96 MiB L2, the RX 9070 XT's 64 MB Infinity Cache, vendor figures), and never above the dataset. At the prototype dataset (2^28) the windows are 1 GiB, 512 MiB and 256 MiB; at the genesis dataset (2^29) 2 GiB, 1 GiB and 512 MiB. The dataset grows by the step schedule recommended to the project lead (spec 01 section 1.13.3 option (b), `docs/analysis/card-lifetime-2026-10-05.md`: power-of-two steps, 4 GiB at year 4, 8 GiB at year 12, 16 GiB at year 28, 32 GiB at year 60, every index `AND MASK`), so the window ceiling follows the steps and the floor stays the genesis constant 2^26 words; nothing in the address of 1.3 needs a range reduction. A program has 16 load sites and so up to 16 windows; the set a program reads is their union (section 7 computes its distribution). The verifier bound is unchanged (1.2). + +Why per load site and not per program: a per-program window of a quarter of the dataset would hand a 256 MiB SRAM mirror a third of the hours at the prototype size. Sixteen sites with drawn offsets cover the dataset with high probability (section 7), so the mirror a chip would need is the whole dataset in every hour, and the hour-to-hour variation lands on the memory design (which quarter, which half, how many distinct windows), not on its size. + +### 1.5 Program stream (replaces "592 draws per program" in section 1.4.3 for era programs) + +16 slot draws, then 64 x 11 = 704: 720 draws per program (768 and 784 for a class with the width roll). On the chain an era program is a class v3 program (branch ca2-v3's seam: `ProgramClass::V3`, generator version 3): its id is `program_id(3, seed words, attempt)` as that branch defines it, and the era it was drawn under is identified beside the id by `IGNEUM_ERA_SEED_HEX` (`packcheck::verify_pack_dir_chain` refuses a pack whose era is not the job's), so the pair (id, era seed) names the program. The experiment classes that are not class v3 (`--class --era ...`) carry the era inside the class id instead: `FNV-1a-64("igneum-program-rw/" || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots || "era/" || allowed[3] || W || M_le32 || R || pos[4])`. The class name is the base name with `-era` (`w4-era401998a5`). + +### 1.5.1 The seam (branch ca2-v3, kept as its signatures stand) + +`V3_CLASS` is the base class of class v3 (16 loads of 4 bytes, the lottery hash's load; the era rides inside it), `V3_ALLOWED = [1]` its width set. `generate_from_seed_bytes_program_class(label, seed, V3, Some(era))` draws `LoadClass::era(V3_CLASS, era, &V3_ALLOWED)` and stamps generator 3; without era bytes (a template before the era is known) the bare `V3_CLASS` stands. `Epoch::chain_dataset(day, class)` is unchanged: the layout of 1.2 is a property of the program (`program.class.layout()`), applied by the interpreter and by `Epoch::dataset_word`, so the day's cache is shared by every era of a day and the engine keys its caches on `(day, class)` as before. + +### 1.6 Acceptance + +The rule of section 1.4.6 is unchanged in its tests. Its interpreter mirrors 1.3 at the rule's constant `D = 28` (`idx` as above with `MASK = 0x0fffffff`), as `igneum-pow/src/accept.rs` does. The distinct-address bound counts dataset loads as the read-width branch defines it. + +## 2. The era seed on the devnet (stand-in for `E_n`) + +Until the 1-hour VDF of section 4.4 is in the node, in the shape of the epoch seed's stand-in (`docs/fork-divergence.md` "Epoch seed"): + +| Era | `E_n` | +|---|---| +| 0 | the genesis block hash (32 bytes) | +| n >= 1 | the hash of the last selected-chain block whose DAA score is below `15,552,000 n - 7,200` (the 2-hour lead of section 4.4 step 1) | + +The era of a block is `floor(DAA score / 15,552,000)`, a function of the header alone. `E_n` for `n >= 1` is known 7,200 DAA seconds before the era starts, which covers the 1-hour VDF when it arrives and the kernel compile and dataset rebuild now. Test seeds for packs and tests: `E_n` = the 32 bytes (little-endian words) of `seed_words_from_bytes("igneum-era-test/")` (`igneum-pow ... --era igneum-era-test/`, the number only names the pack); raw bytes with `--era :<64 hex>`. + +## 3. Memory budget (the project lead, 5 October 2026: under 6 GB on an 8 GB card) + +The era layout adds no resident memory: a window is a mask and an offset in the kernel text, the interleave is address arithmetic, the stride is two operations. The whole working set on a card, every item from this branch and the others: + +| Item | Bytes | Who | +|---|---|---| +| Dataset (prototype) | 1 GiB | existing | +| Cache (256 MiB, resident only while the day's dataset is built, then free; the decided reading, confirmed by the coordinator on 5 October 2026, `docs/analysis/card-lifetime-2026-10-05.md` carries the per-tier working set with the cache freed as the best case) | 256 MiB peak | existing | +| Layer 5 hot table | the ca2-cache worker's figure | not this branch | +| Scratch per resident warp (read-width variant 5, 32 or 128 KiB per warp) | 2,048 warps x 128 KiB = 256 MiB at most | readwidth branch | +| Output and read-back buffers | 2^24 nonces x 8 B = 128 MiB per dispatch | existing harness | +| Era windows, stride, interleave | 0 | this branch | + +The era window never exceeds the dataset, so it never grows the footprint. + +## 4. Implementation (behind the flag) + +| Piece | Where | What | +|---|---|---| +| `EraParams`, `era_draw`, `LoadClass::era` | `igneum-pow/src/generator.rs` | the 7-draw era stream of 1.1; the class carries `era: Option` beside `mix`, `load_slots`, `scratch`, `scratch_kb`; the two window draws per instruction (`Instr::win`, `Instr::off`); the program id of 1.5 | +| `Layout` | `igneum-pow/src/memhard.rs` | `split(w) -> (t, j)`, `join(t, j) -> w`, `LINEAR = [0, 1, 2, 3]`; `MemhardCpu` and `DatasetSource` carry it | +| `load_index` | `igneum-pow/src/verify.rs` | the address of 1.3, shared by the interpreter and the acceptance mirror | +| Emitters | `igneum-pow/src/emit.rs` | the one load form of 1.3 in Metal, CUDA and OpenCL C; `mh_word`, the three `igneum_build` kernels and the Metal build kernel with `mh_t`, `mh_j`, `mh_addr` when the layout is not linear; `IGNEUM_ERA_*` in `program.h`, an `"era"` object in `program.json` | +| CLI | `igneum-pow/src/main.rs` | `--era ` and `--era-widths 4,16,64` (one width pins) on every command | +| Tests | `igneum-pow/src/*.rs`, `igneum-pow/tests/packs.rs` | the draw is deterministic and within bounds; `split`/`join` are inverse and the dataset is a prefix at every size; six era programs pass the generator contract and the acceptance rule; vectors round-trip; the pinned packs are byte-identical; the six era packs match the emitters and every load has the form of 1.3 | + +The default class is untouched: `LoadClass::V2` has `era: None`, every emitter branch on `era` keeps today's text, and `tests/packs.rs` diffs the pinned packs (`igneum-genesis-mh`, `igneum-devnet-v4-epoch0`) against the emitters as before. Section 6 records the diff of a fresh export against the checked-in files. + +## 5. The six era packs + +`proto-cuda/packs-ca2-era/era-`, `n` in 0..5: the devnet's 32-byte epoch seed `edc4fa84...fb07` and day bytes `igneum-day/20730` (the seeds of the pinned pack `igneum-devnet-v4-epoch0`, which is the v2 baseline with the same program seed), dataset 2^28 words, era seed `igneum-era-test/`, width pinned at 4 bytes (`--era-widths 4`, the default). The program seed is held fixed so that the six packs differ in the era parameters only (section 1); each carries `seeds.txt` for the one-click workers. Every pack is attempt 1 (attempt 0 of this seed is rejected under the era class: 12 draws per instruction give a different stream from v2's). The drawn parameters (`igneum-pow show --epoch-hex edc4... --era igneum-era-test/`, 5 October 2026): + +| Pack | Class | Era seed `E_n` (first 16 hex) | W (bytes) | M | R | pos | +|---|---|---|---|---|---|---| +| era-0 | w4-erab2ed8a89 | 5e0587f455a86e91 | 4 | 0x625e5ab3 | 19 | 0, 2, 10, 15 | +| era-1 | w4-era676a17fc | df57136f2ad5f410 | 4 | 0xb2a9d70d | 6 | 1, 3, 8, 13 | +| era-2 | w4-era843155d7 | 7f450623297a954f | 4 | 0x2b4a5b97 | 28 | 1, 3, 4, 8 | +| era-3 | w4-erad6367bfe | 8bffdd3366b9c3ff | 4 | 0x27ea7eff | 30 | 2, 3, 8, 13 | +| era-4 | w4-era4488f3ed | e593fc1d48475c88 | 4 | 0x4d38603d | 10 | 2, 9, 13, 15 | +| era-5 | w4-eraf897c84e | ff87ad96a1b53f36 | 4 | 0x03ac37ad | 22 | 0, 2, 10, 13 | + +All six are class v3 packs (generator 3, `IGNEUM_PROGRAM_CLASS "v3"`, `IGNEUM_ERA_SEED_HEX`), attempt 0, program id `73bcbfe8ccf988f1` in every pack (the seam's `program_id(3, seed, attempt)`; the era seed beside it names the program), the era layout over version 2's item construction (mixer x1, the genesis cache) so that the v2 baseline pack is the same dataset; the chain's class v3 composes the same draw over `LoadClass::MX4` (mixer x4, growth), and the integration re-exports these packs on it after the PC rows. The era-seed-to-pack assignment above is from `program.h` of each pack; the test-seed numbering is only the pack name. + +The windows are a property of the program, so they are the same in all six packs (site:shrink:offset): `1:2:3 4:2:2 6:0:0 10:1:0 12:1:1 20:2:0 27:1:0 30:0:0 35:0:0 40:0:0 41:0:0 43:2:3 45:1:0 52:0:0 53:2:2 54:0:0`: 7 sites read the whole dataset, 5 a half, 4 a quarter; the union is the whole dataset. + +## 6. Measurements + +Pending at the time of this commit; each table below says the machine, the date, the harness and the command when filled. + +### 6.1 Byte-identical default path + +### 6.2 Bit-exactness on the Mac (Metal, Apple OpenCL, CUDA emulation) + +### 6.3 Hash rate per era on the M5 Max (Metal) and the CPU verifier + +### 6.4 PCs (RTX 5090 CUDA, RX 9070 XT OpenCL): prepared, waiting for the go + +## 7. The chip-model line per draw + +From `docs/analysis/m16-recompute-attacker-2026-10-05.md` and the random-read ceilings of `docs/bench-log.md` ("the 9070 XT on the eGPU": 9070 XT 2.42 to 2.68 G loads/s at 1 GiB, 5090 16.4 to 18.0, M5 Max 3.41 to 3.49; every random 4-byte read costs AMD a 64-byte line): + +| Quantity | Formula | +|---|---| +| Bytes read per hash | 128 loads x W bytes | +| Distinct 64-byte lines per hash | 128 (one line per load at every W up to 64 bytes; the census's 120 to 128 distinct addresses per hash) | +| SRAM a chip needs to mirror what the hash reads | the union of the program's 16 windows (a distribution over programs; section 7.1) | +| Latency-bound share | measured rate / (the card's 4-byte random-read ceiling / 128) | + +The latency-bound share uses the 4-byte ceiling for every width because the 9070 XT line probe showed the same count per second for 4-byte and 64-byte random reads; the 5090's 16-byte and 64-byte ceilings are the read-width branch's measurement, cited when they land. + +### 7.1 Union of windows per program + +Filled from a CPU census over programs (section 6). + +## 7.2 Found on the way (harness defects, both fixed on this branch) + +| Where | Defect | Fix | Checked | +|---|---|---|---| +| `proto-cuda/nvrtc/packfile.h` (the one-click workers) | the seed words were re-derived from the bare epoch seed, so every pack of attempt 1 or higher was refused ("the epoch seed bytes do not give the pack's IGNEUM_SEEDW_INIT"); 5.14 percent of epochs under v2, all six era packs, and the epoch 34 fleet outage of 18:23Z on 5 October 2026 (branch pack-loop af983a7, which this branch takes: `pf_program_words`) | the pack-loop derivation merged over the readwidth packfile (class fields and string seeds kept) | the devnet pack (attempt 0) loads, the six era packs (attempt 1) load, a copy of era-0 with the attempt tampered to 0 is refused on the re-derivation (section 6) | +| `proto-cuda/host.cu`, `proto-opencl/host.c` | the host-side dataset word was `mh_item(w >> 4)[w AND 15]`, the harness's own copy of the linear layout; under an interleaved layout the "64 random points vs host derivation" check failed while the Mac samples and the vectors passed | `host_ds_word` calls the pack's `mh_word` (memhard.h), which carries the layout | `proto-cuda/emu/test-layout.sh`: the CUDA emulation on era-1 (interleaved) and the devnet pack (linear) must pass the random-point check; it failed on era-1, era-3 and era-5 before the fix (section 6) | + +## 8. What is unverified + +- Everything in section 6 marked pending. +- The 1-hour VDF does not exist; the devnet stand-in of section 2 is a proposal. +- The interleave's value against a chip with a programmable address decoder is nil (1.2); the claim is limited to hard-wired layouts. +- The window floor of 2^26 words is set by the 5090's L2 (96 MiB) and the 9070 XT's Infinity Cache (64 MB, vendor figures); a future card with a larger cache moves the floor, which is a genesis constant. +- No cryptanalysis of the stride (a multiply and a rotate before the mask); it is a bijection, so the address distribution is that of the register value, as today. diff --git a/docs/plans/read-width.md b/docs/plans/read-width.md new file mode 100644 index 00000000..60a4d3da --- /dev/null +++ b/docs/plans/read-width.md @@ -0,0 +1,109 @@ +# Read width of the lottery hash: 4, 16 and 64-byte loads, a per-load mix, and a written scratch (gate 1 experiment) + +5 October 2026. Branch `readwidth` (worktree `../igneum-wt-readwidth`), commits 019b014 and b970dda plus the measurement commit. Nothing here changes consensus, the live generator, the pinned vectors or a shipped binary: every class sits behind `--class` in `igneum-pow` and the default class is generator version 2 byte for byte (`igneum-pow/tests/packs.rs` still compares the four pinned packs against the emitters). Numbers and a recommendation; the decision is the project lead's. + +## 1. The question + +The bench-log entry "the 9070 XT on the eGPU" (5 October 2026) found the hash bound by dependent random 4-byte reads over the 1 GiB dataset, 128 per hash: the RX 9070 XT finishes 2.4 to 2.7 G such reads a second (18 MH/s), the RTX 5090 16 to 18 G (127 MH/s), the M5 Max 3.45 G (23 to 28 MH/s). AMD fetches a 64-byte line per 4-byte read, so 94 percent of its memory traffic is unused; NVIDIA fetches a 32-byte sector and its 96 MB L2 catches a share. the project lead's question: would wider reads keep the chip-resistance property (latency-bound, random access) while closing the vendor gap? Two additions from the coordinator: a per-load width drawn from an era-fixed mix so no chip is built for one width, and a written per-warp scratch so part of the memory work cannot be mirrored into read-only SRAM. + +## 2. What was built (all behind the flag) + +| Class (`--class`) | Loads per hash | What a load does | Dataset bytes per hash | +|---|---|---|---| +| `v2` (= `w4`, the lottery hash) | 128 | `dst ^= dataset[src & MASK]`, one 4-byte word | 512 | +| `w16` | 128 | the 16-byte-aligned group of 4 words at `src & MASK`, every word folded into `dst` | 2,048 | +| `w64` | 128 | the 64-byte-aligned item (16 words), every word folded | 8,192 | +| `w64x4` | 32 (4 load slots) | as `w64`; the same bytes per hash as 512 loads of 4 bytes | 2,048 | +| `mix50-35-15` | 128 | per load, width 4, 16 or 64 bytes drawn from the program stream with probabilities 50/35/15 | 1,664 to 3,680 over the six programs measured (expected 2,202) | +| `mix25-50-25` | 128 | the same with 25/50/25 | 2,240 to 5,024 (expected 3,200) | +| `scrk` | 128 memory operations | `k` of the 16 slots are scratch read-modify-writes into a `kb` KiB per-warp scratch (16-byte slots, lane-major); the other `16 - k` are 4-byte loads | 4 x (16 - k) x 8 reads plus 16 B read and 16 B written per scratch op | + +The fold. A load of W words reads the W-word-aligned address `b = (src AND MASK) AND NOT (W - 1)` and sets `x = dst XOR w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) XOR w[j]; dst = x` (`verify::fold_words`, mirrored in the three kernel dialects). The rotate-multiply between the words makes the fold state-dependent: two different lines give two different maps of `dst` (the multiply by an odd constant is not xor-linear), so no function of the line alone can stand in for it, and a dataset of pre-folded lines cannot replace the dataset. The dependent chain is unchanged: the next load's address comes from a register that the fold wrote. Width 1 is the lottery hash's xor of one word, so `w4` is the pinned program `bcc1248b10cc90f2` exactly. + +The per-load draw. Every class other than `v2` takes one extra draw per instruction (`below(100)`, the width roll, consumed on every slot so the stream stays uniform) after the nine draws of spec 01 section 1.4.3; on a load slot the width is the first entry of the mix whose cumulative weight exceeds the roll. The program id of a class is `FNV-1a-64("igneum-program-rw/" || 2 || seed words || attempt || mix[3] || slots [|| "scratch/" k kb])`, so no class program can pass for a version 2 program. The acceptance rule of 1.4.6 runs unchanged on the aligned addresses (lane-constant sites and the distinct-address bound, scaled to the dataset loads per hash). + +The scratch (variant 5, measurement only). The kernel runs N persistent warps (one per block or work-group of 32); warp `w` owns scratch `w` and runs units `w, w + N, ...` of the launch. A scratch op reads the lane's 16-byte slot `src AND (slots - 1)`: three data words behind a per-unit tag; a slot whose tag is not this unit's reads as its fill `splitmix32(((base + lane) XOR seed[j]) + slot x 0x9e3779b1 + (j + 1) x 0x85ebca77)`, the three words are folded into `dst` as above, and the slot is rewritten `(tag, x XOR w1, rotl(x, 7) XOR w2, x + w0)`. The CPU verifier holds the touched slots of one unit (at most 32 x k x 8) and nothing else. The scratch is per lane (a 32 KiB warp scratch is 64 slots per lane, 128 KiB is 256), so two lanes never race on a slot and the result is a function of (program, day, unit) alone; the GPU's tags make the lazy fill exact as long as a tag is not reused within the arena's history (the salt advances per unit; it wraps after 2^32 units, a measurement caveat, not a design). + +## 3. Method + +| Step | Command (every figure in the bench log carries its command) | +|---|---| +| Packs | `igneum-pow export --seed --class --out proto-cuda/packs-readwidth/` (23 packs; the six mix seeds per mix are `igneum-readwidth/A/` and `/B/` with attempt 0 accepted, `A/4` skipped: rejected at attempt 0) | +| CPU verifier | `igneum-pow bench --seed igneum-genesis --class --warps 50` (M5 Max, one core; load average 4 to 9 from other agents' builds during the run) | +| Metal | `proto-metal/packbench --pack --batches 5 --batch-log2 24 --group 256 [--warps N]` (new harness: runs the pack's own text; vectors, cache FNV, dataset words, 2^24 fingerprint, MH/s by GPU time) under the measure lock | +| Apple OpenCL | `proto-opencl/igneum-bench-cl-rw --bench-pack --pack --batches 5 --batch-log2 24` and `--memprobe` (Apple's OpenCL, a correctness check and an approximate rate) | +| Emulators | `proto-cuda/emu/emu.sh ../packs-readwidth/

--batch-log2 13 --batches 1 --block-warps 2` (the CUDA text as C++); `proto-opencl/emu/emu.sh ../packs-readwidth/

0 32 --sg 32` and `1 64 --sg 64` (the OpenCL text, wave32 and wave64) | +| RTX 5090 | job `run-readwidth-5090-20261005` on PC 2 (`relay/playbooks/readwidth-5090.ps1`): the NVIDIA card switched off in the app through `POST app.url/api/cards` and restored after; `igneum-worker-cuda --memprobe`, then `--bench --pack

--batches 5 --batch-log2 24` per pack (NVRTC, the pack's own text, vectors through the bound kernel, 2^24 fingerprint) | +| RX 9070 XT | job `run-readwidth-9070-20261005` on PC 1 (`relay/playbooks/readwidth-9070.ps1`): only the gfx1201 card switched off; `igneum-worker-opencl --device D --memprobe`, then `--bench-pack --pack --batches 5 --batch-log2 24` per pack | + +Latency-bound share = measured MH/s x loads per hash / the card's dependent-read ceiling for that width from its own probe at 1024 MiB (for a mix, the harmonic combination of the widths' ceilings weighted by the program's width counts). A share near 1 means the hash runs at the card's random-access limit, the property the design wants; a share well under 1 means something else bounds it (bandwidth, ALU, occupancy). + +## 4. Results (full tables with commands in `docs/bench-log.md`, "read width of the lottery hash") + +Probe ceilings at 1024 MiB (G dependent reads/s): RTX 5090 4 B 17.5, 16 B 18.0, 64 B 9.1 (584 GB/s), stream 1,579 GB/s; RX 9070 XT 4 B 2.42, 16 B 2.43, 64 B 2.47 (158 GB/s), stream 636; M5 Max (Apple OpenCL, approximate) 3.50 / 3.51 / 3.51, stream 522. + +| Class | dataset B/hash | RTX 5090 MH/s (latency-bound share) | RX 9070 XT (share) | M5 Max Metal (share) | 5090 / 9070 | DRAM bytes moved per hash, NVIDIA 32 B sector / AMD 64 B line | CPU verify ms per unit | +|---|---|---|---|---|---|---|---| +| v2 = w4 (today) | 512 | 136.1 (0.96) | 18.15 (0.87) | 27.74 (1.01) | 7.5x | 4,096 / 8,192 | 0.604 | +| w16 | 2,048 | 139.8 (0.90) | 17.90 (0.84) | 28.26 (1.03) | 7.8x | 4,096 / 8,192 | 0.610 | +| w64 | 8,192 | 71.9 (0.58) | 17.59 (0.78) | 28.27 (1.03) | 4.1x | 8,192 / 8,192 | 0.630 | +| w64x4 (32 loads) | 2,048 | 275.3 (0.56) | 75.19 (0.84) | 109.7 (1.00) | 3.7x | 2,048 / 2,048 | 0.160 | +| mix50-35-15 (6 programs, min / median / max) | 1,664 to 3,680 | 99.5 / 114.2 / 121.0, spread 18.8% | 17.45 / 18.76 / 18.83, 7.4% | 25.36 / 27.26 / 28.43, 11.3% | 6.1x | 5,939 / 8,192 expected | 0.620 | +| mix25-50-25 (6 programs) | 2,240 to 5,024 | 95.9 / 107.3 / 119.8, 22.3% | 17.84 / 18.45 / 18.85, 5.5% | 23.21 / 24.68 / 25.21, 8.1% | 5.8x | 7,168 / 8,192 expected | 0.614 | + +Scratch, variant 5 (N persistent warps; GPU cost against the persistent control scr0k32; working set = 1 GiB + 256 MiB + 128 MiB output + N x size): + +| Class | RMW share | dataset B/hash | scratch B/hash read + written | RTX 5090 MH/s, 2,048 warps of 4,080 resident (vs control, share) | M5 Max Metal (vs control) | RX 9070 XT, 4,096 warps | working set 5090 / 9070 / Mac | +|---|---|---|---|---|---|---|---| +| scr0k32 | 0 | 512 | 0 | 139.1 (control, 0.98) | 28.25 (control) | 17.88 (control, 0.86) | 1.4 GiB / 1.5 GiB / 1.5 GiB | +| scr2k32 | 12.5% | 448 | 256 + 256 | 114.4 (-18%, 0.80) | 26.14 (-7%) | 14.65 (-18%) | same | +| scr4k32 | 25% | 384 | 512 + 512 | 109.8 (-21%, 0.76) | 31.74 (+12%) | 14.00 (-22%) | same | +| scr8k32 | 50% | 256 | 1,024 + 1,024 | 122.1 (-12%, 0.82) | 49.08 (+74%) | 14.17 (-21%) | same | +| scr2k128 | 12.5% | 448 | 256 + 256 | 110.1 (-21%, 0.77) | 26.24 (-7%) | 14.07 (-21%) | 1.6 GiB / 1.9 GiB / 1.9 GiB | +| scr4k128 | 25% | 384 | 512 + 512 | 98.0 (-30%, 0.68) | 28.08 (-1%) | 13.14 (-27%) | same | +| scr8k128 | 50% | 256 | 1,024 + 1,024 | 72.8 (-48%, 0.49) | 35.44 (+25%) | 12.03 (-33%) | same | + +Resident warps and the cap: the 5090 holds 4,080 warps at one warp per block (24 blocks per SM x 170 SMs; 8,160 at 8 warps per block), so 128 KiB each is 510 MiB and the whole working set 1.9 GiB; a 1 MB scratch would have been 4.0 GiB at this geometry and 10.6 GiB at the 64-warp-per-SM figure, which is why the cap moved the size to the tens of kilobytes. The occupancy query returned 24 blocks per SM before and after the arena allocation: the allocation did not change it. The 9070 XT's OpenCL runtime has no occupancy query; 4,096 persistent warps were launched (64 per compute unit over 64 CUs, approximate) and the arena is 128 MiB at 32 KiB, 512 MiB at 128 KiB. The Mac's residency is not reported; 2,048 to 16,384 warps were swept and the best row kept. + +Chip model, re-run with the measured widths (the M16 arithmetic of `docs/analysis/m16-recompute-attacker-2026-10-05.md`; the on-die-cache recompute chip's row per scratch variant is the ca2-soundness branch's, as agreed with the Counter ASIC 2.0 coordinator): + +| Class | what a chip with its own DRAM controller gains over the GPU's memory system | what a chip with on-die SRAM gains | +|---|---|---| +| v2 | the GPU fetches 8 to 16x the bytes it uses (AMD 64 B, NVIDIA 32 B per 4 B); a chip fetching 32 B bursts moves 4,096 B per hash, the 5090's figure, so nothing over NVIDIA and 2x over AMD in traffic, none in latency (the chain is 128 dependent DRAM latencies on either) | the recompute attacker of M16: 150,000 integer ops per hash against the 256 MiB cache; 2.4x at equal silicon before a fixed-function factor (unchanged by the width) | +| w16 | the same: 4,096 / 8,192 bytes moved, 2,048 used; traffic efficiency 50 percent on NVIDIA, 25 on AMD | unchanged: the fold uses every byte, so the chip recomputes 128 items per hash exactly as before; the SRAM mirror of the read-only dataset (1 GiB) stays out of reach | +| w64 | every byte moved is used on both vendors (8,192 moved, 8,192 used); the 5090 is bandwidth-bound at 589 GB/s, so a chip with HBM3 class bandwidth (several TB/s, approximate) is bandwidth-advantaged: the Ethash shape | unchanged in op count; but the chain of 128 loads now moves 8 KB, so a chip's advantage shifts from latency to bandwidth per dollar, which is the wrong direction for the design's 2x target | +| w64x4 | 2,048 moved and used; 32 latencies per hash; every card 4x faster; a bandwidth-rich chip gains as above | 32 items per hash: the recompute attacker's op count falls 4x (37,500 per hash), so the M16 gain rises 4x: fails the 2x target by arithmetic | +| mixes | between v2 and w64 per program; the chip cannot be built for one width, but the GPU pays the 64-byte hours (the 5090 loses up to 27 percent in a heavy hour) | as v2 per item; the recompute attacker is indifferent to the width | +| scratch | a chip must provide writable memory for N units in flight: 32 KiB x N at the GPU's geometry (128 MiB at 4,080), against the 256 MiB read-only cache it could mirror into SRAM (54 to 83 mm^2 at a leading node, the coordinator's figure, approximate); but a unit touches at most k x 8 x 32 slots (4 KiB at 50 percent), the fill is a function and the tags are per unit, so a chip need only hold the touched set per unit in flight (the soundness caveat below) | the dataset reads replaced by scratch ops are reads the chip no longer has to serve from the 1 GiB; at 50 percent the recompute attacker computes 64 items instead of 128 | + +Soundness (variant 5, measurement only, as instructed; the chip row is the ca2-soundness branch's, a465881: the on-die-cache recompute chip's gain is 2.4x at 0, 12.5, 25 and 50 percent, replaced or added, 32 or 128 KB, so the scratch does not move it): the per-unit scratch starts from a fill that any implementation can compute, and a unit writes at most `k x 8` slots per lane; an implementation that keeps only the touched slots of each unit in flight (the CPU verifier does exactly this) needs 16 B x touched slots, not the nominal arena, so the "real memory a chip must provide" is bounded by units in flight x touched slots, not by N x 32 KiB. The variant forces memory that is written, which SRAM can hold as well as DRAM; it does not force memory that is large. A written region that outlives the unit (state carried across units) would, and the CPU verifier could not replay it. This is the finding, not a recommendation. + +## 5. Recommendation (the decision is the project lead's) + +the project lead's rules, as passed by the coordinator: width = the widest read that keeps every card latency-bound (achieved within 90 percent of the probe ceiling at that width) with margin on the 5090 (bytes per hash x rate under a third of the 1,579 GB/s stream); the mix is in only if the six-program spread is under 5 percent per card; the scratch share is the smallest at which the chip model's gain falls under 1.5x at the lowest GPU cost within the 6 GB cap. + +| Variant | Verdict under the rules | Numbers | +|---|---|---| +| w16 (16-byte loads, 128 per hash) | PASSES the rules: shares 0.90 / 0.84 / 1.03 (the 9070 XT's 0.84 equals its v2 share of 0.87 within noise: the card is at its ceiling in both), 286 GB/s on the 5090 = 18 percent of the stream. It does NOT close the vendor gap (7.8x against 7.5x), because the memory systems already move a sector or a line per load; it changes what the fold consumes, nothing the DRAM does | the only width row that passes; a no-cost change in rate (+2.7 percent 5090, -1.4 percent 9070 XT, +1.9 percent M5 Max) | +| w64 | FAILS: 5090 share 0.58, 37 percent of the stream; closes the gap to 4.1x only by making the 5090 bandwidth-bound | | +| w64x4 | FAILS: shares 0.56 / 0.84 / 1.00, the recompute gain rises 4x | | +| mix 50/35/15 and 25/50/25 | OUT: spreads 18.8 and 22.3 percent on the 5090, 7.4 and 5.5 on the 9070 XT, 11.3 and 8.1 on the M5 Max, all over 5 percent; a chip is not built for a width anyway (see the model: the width does not change the recompute attacker) | | +| scratch | OUT: every share costs the 5090 12 to 48 percent and the 9070 XT 18 to 33 percent, and raises the M5 Max's rate (the arena is cached there); the soundness branch's chip row (ca2-soundness a465881, the on-die-cache recompute chip) stays at 2.4x at every share, 32 or 128 KB, because the verifier resets the scratch per unit and the live state is the hash's own read-modify-writes, which a chip keeps in 80 to 320 B per lane; under the rule the share is 0. The rows stay as the measurement that decided it | | + +Recommendation: keep 128 loads per hash and 4 bytes per load (v2) for the devnet; if a width change is wanted for the fold's sake (every byte of the sector consumed, which removes the "94 percent waste" statement from the AMD entry without changing what the card does), w16 is the one that passes every rule and costs nothing measurable, and it is the only width worth a vector re-cut. The AMD gap is a random-access gap (2.4 G against 17.5 G dependent reads per second at 1 GiB on the cards we own), and no read width closes it without turning the 5090 bandwidth-bound; the levers that act on the gap are the ones outside this experiment (the AMD card's memory path, and the dataset size against the 5090's 96 MB L2 share, which the probe's 64 MiB rows show at 9 G reads/s against 2.4 at 1 GiB). The per-load mix is out on stability; the scratch is out on GPU cost and on the soundness caveat. + +## 6. What w16 would change if adopted (not done; the decision is the project lead's) + +| Where | Change | +|---|---| +| `docs/spec/01-lottery-hash.md` 1.4.1 | `load`: `dst = fold(dst, dataset[b .. b + 4))`, `b = (src AND MASK) AND NOT 3`, with the fold written out; 1.4.3 unchanged (no width draw for a fixed width); 1.4.6 unchanged (the aligned address is the address the rule sees) | +| 1.5 | "A load reads one 4-byte word" becomes 16 bytes aligned; the single-form text-search rule of 1.14 item 2 becomes the wide form; the item size (64 B) and `dataset[w] = item(w >> 4)[w AND 15]` unchanged | +| 1.11 | unchanged in count (4,096 items per unit; the verifier derives the same items) | +| 1.15, 1.17 | every vector re-cut (new program ids: the class enters the id or the generator version steps to 3); the four pinned packs replaced; the conformance fuzz re-run on Metal, CUDA and OpenCL (this branch's 23 packs and the three emulators are the template) | +| Litepaper, Mining ("random reads over a multi-gigabyte dataset") and the vs-RandomX "128 dataset addresses" rows | "128 reads of 16 bytes"; `site/bench.html` sector arithmetic (32 B per 4 B) becomes 32 B per 16 B | +| Workers | no host change: the kernel text carries the loads; `proto-cuda/host.cu`'s static mask check (`TESTS.md` section 5) learns the wide form | +| Cost on the 5090 | none measured (+2.7 percent); on the 9070 XT -1.4 percent; verifier +1 percent | + +## 7. Files + +`igneum-pow/src/{generator,verify,accept,emit,memhard,main}.rs` (the classes, behind `--class`), `proto-cuda/packs-readwidth/` (23 packs), `proto-metal/packbench.swift` (Metal from a pack's files), `proto-opencl/host.c` (`--bench-pack`, `--warps`, the 16-byte probe row, the scratch arguments), `proto-cuda/nvrtc/worker.cpp` (`--bench`, `--memprobe`, the scratch arena), `proto-cuda/nvrtc/packfile.h` (class fields; string-seed packs), `proto-cuda/emu/cuda_runtime.h` and `proto-opencl/emu/{emu_opencl.h,emu_main.cpp}` (vector types, the persistent launch), `relay/playbooks/readwidth-*.ps1` (the PC jobs: the card under test off in the app and restored, never the other card). diff --git a/igneum-pow/src/accept.rs b/igneum-pow/src/accept.rs index aca1702e..7a5352ee 100644 --- a/igneum-pow/src/accept.rs +++ b/igneum-pow/src/accept.rs @@ -16,7 +16,7 @@ use crate::generator::{Instr, Op, Program, INSTR_COUNT, ITERATIONS, LANES}; use crate::seed::{fnv1a64, SplitMix64}; -use crate::verify::{dataset_elem, fold_words, splitmix32, ScratchModel}; +use crate::verify::{dataset_elem, fold_words, load_index, splitmix32, ScratchModel}; /// Units (32-lane warps) the dynamic test interprets. pub const ACCEPT_UNITS: usize = 64; @@ -193,6 +193,7 @@ fn run_unit(p: &Program, unit: usize, base: u32, acc: &mut Acc, lane_addrs: &mut let mut nload = 0usize; let mut scratch = if p.has_scratch() { Some(ScratchModel::new(p.class.scratch_slots_per_lane())) } else { None }; let slot_mask = p.class.scratch_slot_mask(); + let era = p.class.era; for it in 0..ITERATIONS { let sel = r[0]; for (k, ins) in p.instrs.iter().enumerate() { @@ -285,7 +286,7 @@ fn run_unit(p: &Program, unit: usize, base: u32, acc: &mut Acc, lane_addrs: &mut let width = ins.width as usize; let align = !(ins.width as u32 - 1); for lane in 0..LANES { - idx[lane] = (r[a][lane] & mask) & align; + idx[lane] = load_index(era.as_ref(), ins, r[a][lane], mask, ACCEPT_DATASET_LOG2) & align; } if idx.iter().all(|&x| x == idx[0]) { return Err(Reject::LaneConstantSite { iteration: it as u8, instr: k as u8, unit: unit as u8 }); diff --git a/igneum-pow/src/emit.rs b/igneum-pow/src/emit.rs index 1b802481..ef9410ef 100644 --- a/igneum-pow/src/emit.rs +++ b/igneum-pow/src/emit.rs @@ -9,13 +9,90 @@ //! One deliberate difference from the Swift: `program_json` writes the cache line mask inside the `"item"` string //! as a bare `0x003fffff`. The Swift writes it quoted (`jhex`), which is not valid JSON. -use crate::generator::{Instr, Op, Program, ProgramClass, GENERATOR_VERSION, INSTR_COUNT, ITERATIONS, LOAD_SLOTS}; +use crate::generator::{EraParams, Instr, Op, Program, ProgramClass, GENERATOR_VERSION, INSTR_COUNT, ITERATIONS, LOAD_SLOTS}; use crate::memhard::{ - MixParams, Shape, CACHE_LINES_PER_SEGMENT, CACHE_SEGMENT_LOG2_LINES, CACHE_TAG, CHACHA_ROUNDS, CHACHA_SIGMA, + Layout, MixParams, Shape, CACHE_LINES_PER_SEGMENT, CACHE_SEGMENT_LOG2_LINES, CACHE_TAG, CHACHA_ROUNDS, CHACHA_SIGMA, ITEM_ROUNDS, }; use crate::seed::SplitMix64; -use crate::verify::{DatasetMode, DatasetSource, Epoch, FOLD_MUL, FOLD_ROT}; +use crate::verify::{window, DatasetMode, DatasetSource, Epoch, DEFAULT_DATASET_LOG2, FOLD_MUL, FOLD_ROT}; + +/// The index expression of a dataset load (era layout, `docs/plans/era-layout.md` section 1.3). For every class +/// without an era it is the lottery hash's `rN & MASK`; for an era program it is the one form +/// `((rotl_imm(rN * M, R) & WM) | OFF) & MASK` with the site's window constants at the pack's dataset size. +fn load_index_expr(dialect: CoreDialect, era: Option<&EraParams>, ins: &Instr, a: &str, dataset_log2: u32) -> String { + let mask_name = match dialect { + CoreDialect::Metal => "MASK", + _ => "mask", + }; + match era { + None => format!("{a} & {mask_name}"), + Some(e) => { + let (wm, off) = window(ins, mask_for(dataset_log2), dataset_log2); + format!("((rotl_imm({a} * {}, {}u) & {}) | {}) & {mask_name}", hex(e.stride_mul), e.stride_rot, hex(wm), hex(off)) + } + } +} + +/// The era lines of program.h (empty without an era). +fn era_header_lines(p: &Program) -> String { + let Some(e) = p.class.era else { return String::new() }; + let mut s = String::new(); + s.push_str("// Era layout (5 October 2026, docs/plans/era-layout.md): NOT the lottery hash. Every dataset load reads\n"); + s.push_str("// idx = ((rotl(src * STRIDE_MUL, STRIDE_ROT) & window mask) | window offset) & MASK; the window of a load site is the\n"); + s.push_str("// dataset, a half or a quarter of it (IGNEUM_ERA_WINDOWS: site:shrink:offset); dataset word w holds word j(w) of item\n"); + s.push_str("// t(w) with j's bits at the INTERLEAVE positions (memhard.h: mh_t, mh_j, mh_addr).\n"); + s.push_str(&format!("#define IGNEUM_ERA_LABEL {}\n", jstr(&e.label()))); + s.push_str(&format!("#define IGNEUM_ERA_SEED_WORDS {{ {} }}\n", join_hex(&e.words))); + s.push_str(&format!("#define IGNEUM_ERA_ALLOWED_WIDTHS {{ {}, {}, {} }} // words, ascending, 0 = unused; one entry pins the width\n", e.allowed[0], e.allowed[1], e.allowed[2])); + s.push_str(&format!("#define IGNEUM_ERA_WIDTH_WORDS {}\n", e.width_words)); + s.push_str(&format!("#define IGNEUM_ERA_STRIDE_MUL {}\n", hex(e.stride_mul))); + s.push_str(&format!("#define IGNEUM_ERA_STRIDE_ROT {}\n", e.stride_rot)); + s.push_str(&format!("#define IGNEUM_ERA_INTERLEAVE {{ {}, {}, {}, {} }}\n", e.pos[0], e.pos[1], e.pos[2], e.pos[3])); + s.push_str(&format!("#define IGNEUM_ERA_WINDOWS {}\n", jstr(&era_windows(p)))); + s +} + +/// "site:shrink:offset" for every load site of an era program, space separated. +fn era_windows(p: &Program) -> String { + p.instrs + .iter() + .enumerate() + .filter(|(_, i)| i.op == Op::Load) + .map(|(k, i)| format!("{k}:{}:{}", i.win, i.off)) + .collect::>() + .join(" ") +} + +/// The layout helpers of the memory-hard core for a non-linear layout: `mh_j(w)`, `mh_t(w)` and `mh_addr(t, j)` +/// (`Layout::split` and `Layout::join` as text). Empty for the linear layout, so the pinned packs do not change. +fn layout_helpers(layout: Layout, u: &str, fn_: &str) -> String { + if layout.is_linear() { + return String::new(); + } + let p = layout.pos; + let low = |q: u8| hex(((1u64 << q) - 1) as u32); + let mut s = String::new(); + s.push_str(&format!( + "// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions {} {} {} {} of w.\n", + p[0], p[1], p[2], p[3] + )); + s.push_str(&format!( + "{fn_} {u} mh_j({u} w) {{ return ((w >> {}u) & 1u) | (((w >> {}u) & 1u) << 1) | (((w >> {}u) & 1u) << 2) | (((w >> {}u) & 1u) << 3); }}\n", + p[0], p[1], p[2], p[3] + )); + s.push_str(&format!("{fn_} {u} mh_t({u} w) {{")); + for &q in p.iter().rev() { + s.push_str(&format!(" w = (w & {}) | ((w >> {}u) << {}u);", low(q), q + 1, q)); + } + s.push_str(" return w; }\n"); + s.push_str(&format!("{fn_} {u} mh_addr({u} t, {u} j) {{ {u} w = t;")); + for (i, &q) in p.iter().enumerate() { + s.push_str(&format!(" w = ((w >> {}u) << {}u) | (w & {}) | (((j >> {}u) & 1u) << {}u);", q, q + 1, low(q), i, q)); + } + s.push_str(" return w; }\n"); + s +} /// Where the words of a wide load come from (read-width experiment). #[derive(Clone, Copy, PartialEq, Eq)] @@ -32,16 +109,16 @@ enum WideSource { /// address aligned down to `width` words, folded into `dst` as `verify::fold_words`. The emitted text is the /// same shape in the three dialects: the vector loads differ (`uint4` pointer on Metal and CUDA, `vload4` on /// OpenCL C 1.2). For `width == 1` the caller emits the lottery hash's one-word form instead. -fn wide_load_stmt(dialect: CoreDialect, d: &str, a: &str, width: u8, src: WideSource, closed: Option<(u32, u32)>) -> String { +fn wide_load_stmt(dialect: CoreDialect, d: &str, idx: &str, width: u8, src: WideSource, closed: Option<(u32, u32)>) -> String { debug_assert!(width == 4 || width == 16); - let (u, mask, base_ptr) = match dialect { - CoreDialect::Metal => ("uint", "MASK", "dataset"), - CoreDialect::Cuda => ("uint32_t", "mask", "ds"), - CoreDialect::OpenCl => ("uint", "mask", "ds"), + let (u, base_ptr) = match dialect { + CoreDialect::Metal => ("uint", "dataset"), + CoreDialect::Cuda => ("uint32_t", "ds"), + CoreDialect::OpenCl => ("uint", "ds"), }; let vectors = width as usize / 4; let mut s = String::with_capacity(400); - s.push_str(&format!("{{ {u} b_ = ({a} & {mask}) & ~{}u; ", width as u32 - 1)); + s.push_str(&format!("{{ {u} b_ = ({idx}) & ~{}u; ", width as u32 - 1)); match src { WideSource::Stored => match dialect { CoreDialect::Metal => s.push_str(&format!("device const uint4* l_ = (device const uint4*)({base_ptr} + b_); ")), @@ -49,7 +126,7 @@ fn wide_load_stmt(dialect: CoreDialect, d: &str, a: &str, width: u8, src: WideSo CoreDialect::OpenCl => {} }, WideSource::InlineClosed => {} - WideSource::InlineMemhard => s.push_str("uint s_[16]; mh_item(cache, b_ >> 4u, s_); "), + WideSource::InlineMemhard => s.push_str("uint s_[16]; mh_item(cache, mh_t(b_), s_); "), } let word = |j: usize| -> String { match src { @@ -58,7 +135,7 @@ fn wide_load_stmt(dialect: CoreDialect, d: &str, a: &str, width: u8, src: WideSo let (d0, d1) = closed.expect("closed-form words need d0, d1"); format!("ds_elem(b_ + {j}u, {}, {})", hex(d0), hex(d1)) } - WideSource::InlineMemhard => format!("s_[(b_ & 15u) + {j}u]"), + WideSource::InlineMemhard => format!("s_[mh_j(b_) + {j}u]"), } }; if src == WideSource::Stored { @@ -187,6 +264,14 @@ fn scratch_stmt(dialect: CoreDialect, d: &str, a: &str, slot_mask: u32) -> Strin /// lottery hash's text is unchanged: `gid` is the unit's first output index plus the lane. The host MUST launch /// `groups` as a multiple of N (a uniform trip count: the OpenCL local-memory exchange carries a barrier). fn persistent_prologue(dialect: CoreDialect, words_per_lane: usize) -> String { + let (a, b) = persistent_prologue_parts(dialect, words_per_lane); + a + &b +} + +/// The prologue in two parts: the warp's identity and arena, then the unit loop. OpenCL C requires a `__local` +/// variable at the outermost scope of the kernel (AMD's compiler enforces it, 5 October 2026, round 3 on the +/// 9070 XT), so the OpenCL kernels declare the exchange buffer between the two parts. +fn persistent_prologue_parts(dialect: CoreDialect, words_per_lane: usize) -> (String, String) { let (u, tid, nthreads, ptr) = match dialect { CoreDialect::Metal => ("uint", "tid", "nthreads", "device uint*"), CoreDialect::Cuda => ("uint32_t", "(blockIdx.x * blockDim.x + threadIdx.x)", "(gridDim.x * blockDim.x)", "uint32_t*"), @@ -197,11 +282,12 @@ fn persistent_prologue(dialect: CoreDialect, words_per_lane: usize) -> String { s.push_str(&format!(" {u} warp_ = {tid} >> 5;\n")); s.push_str(&format!(" {u} nwarps_ = {nthreads} >> 5;\n")); s.push_str(&format!(" {ptr} arena = scratch + ((size_t)warp_ * 32u + lane) * {words_per_lane}u;\n")); - s.push_str(&format!(" for ({u} g_ = warp_; g_ < groups; g_ += nwarps_) {{\n")); - s.push_str(&format!(" {u} gid = g_ * 32u + lane;\n")); - s.push_str(&format!(" {u} gbase = baseNonce + g_ * 32u;\n")); - s.push_str(&format!(" {u} tag = salt + g_;\n")); - s + let mut l = String::new(); + l.push_str(&format!(" for ({u} g_ = warp_; g_ < groups; g_ += nwarps_) {{\n")); + l.push_str(&format!(" {u} gid = g_ * 32u + lane;\n")); + l.push_str(&format!(" {u} gbase = baseNonce + g_ * 32u;\n")); + l.push_str(&format!(" {u} tag = salt + g_;\n")); + (s, l) } /// The scratch lines of program.h (variant 5). @@ -274,11 +360,14 @@ fn log2_segments(shape: &Shape) -> usize { shape.log2_segments() as usize } -/// The memory-hard core as source text (`emitMemhardCore`). Every parameter is a literal: the mixer constants of -/// the day and, from `mp.shape`, the cache size and the mixer multiplier `m`. Under `m = 1` the text is version -/// 2's byte for byte; under `m > 1` the item loop applies `mh_mixer` `m` times per round with the keys -/// `round_key(r m + j)` (spec 01 section 1.8.5 under class v3). +/// The memory-hard core as source text (`emitMemhardCore`). Every parameter is a literal. Linear layout. pub fn emit_memhard_core(mp: &MixParams, dialect: CoreDialect) -> String { + emit_memhard_core_layout(mp, dialect, Layout::LINEAR) +} + +/// [`emit_memhard_core`] with the dataset layout: with a non-linear layout `mh_word` and the build kernels go +/// through `mh_t`, `mh_j` and `mh_addr` (era layout); with the linear layout the text is unchanged. +pub fn emit_memhard_core_layout(mp: &MixParams, dialect: CoreDialect, layout: Layout) -> String { let shape = &mp.shape; let m = shape.mixer_mult; let cache_log2_words = shape.cache_log2_words; @@ -414,19 +503,52 @@ pub fn emit_memhard_core(mp: &MixParams, dialect: CoreDialect) -> String { )); } s.push_str("}\n"); - s.push_str("// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.\n"); - s.push_str(&format!( - "{fn_} {u} mh_word({cptr} cache, {u} w) {{ {u} s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }}\n" - )); + if layout.is_linear() { + s.push_str("// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.\n"); + s.push_str(&format!( + "{fn_} {u} mh_word({cptr} cache, {u} w) {{ {u} s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }}\n" + )); + } else { + s.push_str(&layout_helpers(layout, u, fn_)); + s.push_str("// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w).\n"); + s.push_str(&format!( + "{fn_} {u} mh_word({cptr} cache, {u} w) {{ {u} s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; }}\n" + )); + } s } +/// The dataset store of one item in a build kernel: `d[i] = s[i]` at `ds + t * 16` for the linear layout, else the +/// scatter `ds[mh_addr(t, i)] = s[i]` (era layout). +fn build_store(layout: Layout, dialect: CoreDialect, ds: &str, t: &str) -> String { + let (u, wptr, cast) = match dialect { + CoreDialect::Metal => ("uint", "device uint*", ""), + CoreDialect::Cuda => ("uint32_t", "uint32_t*", "(size_t)"), + CoreDialect::OpenCl => ("uint", "__global uint*", "(ulong)"), + }; + if layout.is_linear() { + match dialect { + CoreDialect::Metal => format!(" {wptr} d = {ds} + {t} * 16u;\n for ({u} i = 0u; i < 16u; ++i) d[i] = s[i];\n"), + CoreDialect::Cuda => format!(" {wptr} d = {ds} + (size_t){t} * 16u;\n for ({u} i = 0u; i < 16u; ++i) d[i] = s[i];\n"), + CoreDialect::OpenCl => format!(" {wptr} d = {ds} + ((ulong){t} * 16u);\n for ({u} i = 0u; i < 16u; ++i) d[i] = s[i];\n"), + } + } else { + let indent = if dialect == CoreDialect::Metal { " " } else { " " }; + format!("{indent}for ({u} i = 0u; i < 16u; ++i) {ds}[{cast}mh_addr({t}, i)] = s[i];\n") + } +} + /// Metal library with the cache fill and dataset build kernels for one day key (`memhardMSL`, memhard.metal). pub fn metal_memhard(mp: &MixParams) -> String { + metal_memhard_layout(mp, Layout::LINEAR) +} + +/// [`metal_memhard`] with the dataset layout (era layout). +pub fn metal_memhard_layout(mp: &MixParams, layout: Layout) -> String { let mut s = String::new(); s.push_str("#include \n"); s.push_str("using namespace metal;\n"); - s.push_str(&emit_memhard_core(mp, CoreDialect::Metal)); + s.push_str(&emit_memhard_core_layout(mp, CoreDialect::Metal, layout)); s.push('\n'); s.push_str(&format!("// One thread per segment (2^{} threads).\n", log2_segments(&mp.shape))); s.push_str( @@ -441,8 +563,7 @@ pub fn metal_memhard(mp: &MixParams) -> String { s.push_str(" uint gid [[thread_position_in_grid]]) {\n"); s.push_str(" uint s[16];\n"); s.push_str(" mh_item(cache, gid, s);\n"); - s.push_str(" device uint* d = dataset + gid * 16u;\n"); - s.push_str(" for (uint i = 0u; i < 16u; ++i) d[i] = s[i];\n"); + s.push_str(&build_store(layout, CoreDialect::Metal, "dataset", "gid")); s.push_str("}\n"); s } @@ -487,7 +608,7 @@ fn metal_program_impl(p: &Program, dataset_log2: u32, source: LoadSource, bound: } let mut buffer0 = "device const uint* dataset [[buffer(0)]]"; if let LoadSource::InlineMemhard(mp) = &source { - s.push_str(&emit_memhard_core(mp, CoreDialect::Metal)); + s.push_str(&emit_memhard_core_layout(mp, CoreDialect::Metal, p.class.layout())); s.push('\n'); buffer0 = "device const uint* cache [[buffer(0)]]"; } @@ -528,11 +649,12 @@ fn metal_program_impl(p: &Program, dataset_log2: u32, source: LoadSource, bound: )); } s.push_str(&format!("\n for (uint it = 0u; it < {ITERATIONS}u; ++it) {{\n uint sel = r0;\n")); - let word_index = |a: &str, wide: bool| -> String { + let era = p.class.era; + let word_index = |a: &str, wide: bool, ins: &Instr| -> String { if wide { format!("(simd_broadcast({a}, 0) & WMASK) + lane") } else { - format!("{a} & MASK") + load_index_expr(CoreDialect::Metal, era.as_ref(), ins, a, dataset_log2) } }; let fetch = |idx: String| -> String { @@ -568,10 +690,10 @@ fn metal_program_impl(p: &Program, dataset_log2: u32, source: LoadSource, bound: LoadSource::InlineClosed(d0, d1) => (WideSource::InlineClosed, Some((*d0, *d1))), LoadSource::InlineMemhard(_) => (WideSource::InlineMemhard, None), }; - wide_load_stmt(CoreDialect::Metal, &d, &a, ins.width, src, closed) + wide_load_stmt(CoreDialect::Metal, &d, &word_index(&a, false, ins), ins.width, src, closed) } - Op::Load => format!("{d} = {d} ^ {};", fetch(word_index(&a, false))), - Op::WLoad => format!("{d} = {d} ^ {};", fetch(word_index(&a, true))), + Op::Load => format!("{d} = {d} ^ {};", fetch(word_index(&a, false, ins))), + Op::WLoad => format!("{d} = {d} ^ {};", fetch(word_index(&a, true, ins))), Op::Scratch => scratch_stmt(CoreDialect::Metal, &d, &a, p.class.scratch_slot_mask()), }; s.push_str(&format!(" {line} // {k}\n")); @@ -611,8 +733,9 @@ fn init_line(p: &Program, u: &str, i: usize) -> String { } /// The instruction lines of the CUDA hash kernel body (shared by `igneum_hash` and `igneum_hash_bound`). -fn cuda_instr_lines(p: &Program) -> String { +fn cuda_instr_lines(p: &Program, dataset_log2: u32) -> String { let mut s = String::with_capacity(6000); + let era = p.class.era; for (k, ins) in p.instrs.iter().enumerate() { let d = format!("r{}", ins.dst); let a = format!("r{}", ins.src); @@ -634,8 +757,10 @@ fn cuda_instr_lines(p: &Program) -> String { Op::Rotr => format!("{d} = rotr_var({d}, {a});"), Op::Mad => format!("{d} = {a} * {b} + {d};"), Op::Shfl => format!("{d} = {d} ^ __shfl_xor_sync(0xffffffffu, {a}, {});", ins.mask), - Op::Load if load_width(ins) > 1 => wide_load_stmt(CoreDialect::Cuda, &d, &a, ins.width, WideSource::Stored, None), - Op::Load => format!("{d} = {d} ^ ds[{a} & mask];"), + Op::Load if load_width(ins) > 1 => { + wide_load_stmt(CoreDialect::Cuda, &d, &load_index_expr(CoreDialect::Cuda, era.as_ref(), ins, &a, dataset_log2), ins.width, WideSource::Stored, None) + } + Op::Load => format!("{d} = {d} ^ ds[{}];", load_index_expr(CoreDialect::Cuda, era.as_ref(), ins, &a, dataset_log2)), Op::WLoad => format!("{d} = {d} ^ ds[(__shfl_sync(0xffffffffu, {a}, 0) & wmask) + lane];"), Op::Scratch => scratch_stmt(CoreDialect::Cuda, &d, &a, p.class.scratch_slot_mask()), }; @@ -646,6 +771,13 @@ fn cuda_instr_lines(p: &Program) -> String { /// The CUDA kernel (`generateCUDA`, kernel.cu). `memhard` is `None` for a closed-form pack. pub fn cuda_kernel(p: &Program, memhard: Option<&MixParams>) -> String { + cuda_kernel_at(p, memhard, DEFAULT_DATASET_LOG2) +} + +/// [`cuda_kernel`] at a dataset size (an era program's window constants are literals of the pack's size; every +/// other class ignores it). +pub fn cuda_kernel_at(p: &Program, memhard: Option<&MixParams>, dataset_log2: u32) -> String { + let layout = p.class.layout(); let mut s = String::with_capacity(9000); s.push_str(&generated_by(&p.seed_string)); s.push_str( @@ -696,8 +828,7 @@ pub fn cuda_kernel(p: &Program, memhard: Option<&MixParams>) -> String { s.push_str(" if (t < nItems) {\n"); s.push_str(" uint32_t s[16];\n"); s.push_str(" mh_item(cache, t, s);\n"); - s.push_str(" uint32_t* d = ds + (size_t)t * 16u;\n"); - s.push_str(" for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i];\n"); + s.push_str(&build_store(layout, CoreDialect::Cuda, "ds", "t")); s.push_str(" }\n"); s.push_str("}\n"); s.push('\n'); @@ -722,7 +853,7 @@ pub fn cuda_kernel(p: &Program, memhard: Option<&MixParams>) -> String { s.push_str(&init_line(p, "uint32_t", i)); } s.push_str(&format!("\n for (uint32_t it = 0u; it < {ITERATIONS}u; ++it) {{\n uint32_t sel = r0;\n")); - s.push_str(&cuda_instr_lines(p)); + s.push_str(&cuda_instr_lines(p, dataset_log2)); s.push_str(" }\n"); s.push_str(" uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u);\n"); s.push_str(" uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u);\n"); @@ -798,6 +929,11 @@ pub fn cuda_kernel(p: &Program, memhard: Option<&MixParams>) -> String { /// `I` arrive by value in `IgneumInitWords` (`bind::block_init_words`), the instruction text is that of /// `igneum_hash`. Declarations for the host are at the top of the file. pub fn cuda_kernel_bound(p: &Program, memhard: Option<&MixParams>) -> String { + cuda_kernel_bound_at(p, memhard, DEFAULT_DATASET_LOG2) +} + +/// [`cuda_kernel_bound`] at a dataset size (see [`cuda_kernel_at`]). +pub fn cuda_kernel_bound_at(p: &Program, memhard: Option<&MixParams>, dataset_log2: u32) -> String { let mut s = String::with_capacity(9000); s.push_str(&generated_by(&p.seed_string)); s.push_str( @@ -847,7 +983,7 @@ pub fn cuda_kernel_bound(p: &Program, memhard: Option<&MixParams>) -> String { )); } s.push_str(&format!("\n for (uint32_t it = 0u; it < {ITERATIONS}u; ++it) {{\n uint32_t sel = r0;\n")); - s.push_str(&cuda_instr_lines(p)); + s.push_str(&cuda_instr_lines(p, dataset_log2)); s.push_str(" }\n"); s.push_str(" uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u);\n"); s.push_str(" uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u);\n"); @@ -891,8 +1027,9 @@ pub fn cuda_kernel_bound(p: &Program, memhard: Option<&MixParams>) -> String { } /// The instruction lines of the OpenCL hash kernel body (shared by `igneum_hash` and `igneum_hash_bound`). -fn opencl_instr_lines(p: &Program) -> String { +fn opencl_instr_lines(p: &Program, dataset_log2: u32) -> String { let mut s = String::with_capacity(6000); + let era = p.class.era; for (k, ins) in p.instrs.iter().enumerate() { let d = format!("r{}", ins.dst); let a = format!("r{}", ins.src); @@ -913,8 +1050,10 @@ fn opencl_instr_lines(p: &Program) -> String { Op::Rotr => format!("{d} = rotr_var({d}, {a});"), Op::Mad => format!("{d} = {a} * {b} + {d};"), Op::Shfl => format!("{{ uint t_; IGNEUM_SHFL_XOR(t_, {a}, {}u); {d} = {d} ^ t_; }}", ins.mask), - Op::Load if load_width(ins) > 1 => wide_load_stmt(CoreDialect::OpenCl, &d, &a, ins.width, WideSource::Stored, None), - Op::Load => format!("{d} = {d} ^ ds[{a} & mask];"), + Op::Load if load_width(ins) > 1 => { + wide_load_stmt(CoreDialect::OpenCl, &d, &load_index_expr(CoreDialect::OpenCl, era.as_ref(), ins, &a, dataset_log2), ins.width, WideSource::Stored, None) + } + Op::Load => format!("{d} = {d} ^ ds[{}];", load_index_expr(CoreDialect::OpenCl, era.as_ref(), ins, &a, dataset_log2)), Op::WLoad => format!("{{ uint t_; IGNEUM_BCAST0(t_, {a}); {d} = {d} ^ ds[(t_ & wmask) + lane]; }}"), Op::Scratch => scratch_stmt(CoreDialect::OpenCl, &d, &a, p.class.scratch_slot_mask()), }; @@ -927,28 +1066,48 @@ fn opencl_instr_lines(p: &Program) -> String { /// fifth argument (`__global const uint* initw`, 8 words, `bind::block_init_words`). One source file so the serve /// mode of proto-opencl/host.c builds cache fill, dataset build and the bound hash from it at runtime. pub fn opencl_kernel_bound(p: &Program, memhard: Option<&MixParams>) -> String { - let mut s = opencl_kernel(p, memhard); + opencl_kernel_bound_at(p, memhard, DEFAULT_DATASET_LOG2) +} + +/// [`opencl_kernel_bound`] at a dataset size (see [`cuda_kernel_at`]). +pub fn opencl_kernel_bound_at(p: &Program, memhard: Option<&MixParams>, dataset_log2: u32) -> String { + let mut s = opencl_kernel_at(p, memhard, dataset_log2); s.push('\n'); s.push_str( "// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash.\n", ); let scratch_args = if p.has_scratch() { ", __global uint* scratch, uint groups, uint salt" } else { "" }; s.push_str(&format!("IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw{scratch_args}) {{\n")); + let (setup, unit_loop) = persistent_prologue_parts(CoreDialect::OpenCl, p.class.scratch_words_per_lane()); if p.has_scratch() { - s.push_str(&persistent_prologue(CoreDialect::OpenCl, p.class.scratch_words_per_lane())); + s.push_str(&setup); } else { s.push_str(" uint gid = (uint)get_global_id(0);\n"); } s.push_str(" uint lid = (uint)get_local_id(0);\n"); - s.push_str(" uint nonce = baseNonce + gid;\n"); - s.push_str(" uint r0, r1, r2, r3, r4, r5, r6, r7;\n"); - s.push_str(" uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7];\n"); - s.push_str("#if IGNEUM_EXCHANGE == 0\n"); - s.push_str(" IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);\n"); - s.push_str(" uint xk = 0u;\n"); - s.push_str("#else\n"); - s.push_str(" (void)lid;\n"); - s.push_str("#endif\n"); + if p.has_scratch() { + // the __local exchange buffer must sit at the kernel's outermost scope: declare it, then open the unit loop + s.push_str("#if IGNEUM_EXCHANGE == 0\n"); + s.push_str(" IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);\n"); + s.push_str(" uint xk = 0u;\n"); + s.push_str("#else\n"); + s.push_str(" (void)lid;\n"); + s.push_str("#endif\n"); + s.push_str(&unit_loop); + s.push_str(" uint nonce = baseNonce + gid;\n"); + s.push_str(" uint r0, r1, r2, r3, r4, r5, r6, r7;\n"); + s.push_str(" uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7];\n"); + } else { + s.push_str(" uint nonce = baseNonce + gid;\n"); + s.push_str(" uint r0, r1, r2, r3, r4, r5, r6, r7;\n"); + s.push_str(" uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7];\n"); + s.push_str("#if IGNEUM_EXCHANGE == 0\n"); + s.push_str(" IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);\n"); + s.push_str(" uint xk = 0u;\n"); + s.push_str("#else\n"); + s.push_str(" (void)lid;\n"); + s.push_str("#endif\n"); + } if p.has_wide() { s.push_str(" uint lane = lid & 31u;\n uint wmask = mask & ~31u;\n"); } @@ -960,7 +1119,7 @@ pub fn opencl_kernel_bound(p: &Program, memhard: Option<&MixParams>) -> String { )); } s.push_str(&format!("\n for (uint it = 0u; it < {ITERATIONS}u; ++it) {{\n uint sel = r0;\n")); - s.push_str(&opencl_instr_lines(p)); + s.push_str(&opencl_instr_lines(p, dataset_log2)); s.push_str(" }\n"); s.push_str(" uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u);\n"); s.push_str(" uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u);\n"); @@ -974,6 +1133,12 @@ pub fn opencl_kernel_bound(p: &Program, memhard: Option<&MixParams>) -> String { /// The OpenCL C 1.2 kernel (`generateOpenCL`, kernel.cl). pub fn opencl_kernel(p: &Program, memhard: Option<&MixParams>) -> String { + opencl_kernel_at(p, memhard, DEFAULT_DATASET_LOG2) +} + +/// [`opencl_kernel`] at a dataset size (see [`cuda_kernel_at`]). +pub fn opencl_kernel_at(p: &Program, memhard: Option<&MixParams>, dataset_log2: u32) -> String { + let layout = p.class.layout(); let mut s = String::with_capacity(14000); s.push_str(&generated_by(&p.seed_string)); s.push_str("// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).\n"); @@ -1034,7 +1199,7 @@ pub fn opencl_kernel(p: &Program, memhard: Option<&MixParams>) -> String { s.push_str(DS_ELEM_BODY); s.push('\n'); if let Some(mp) = memhard { - s.push_str(&emit_memhard_core(mp, CoreDialect::OpenCl)); + s.push_str(&emit_memhard_core_layout(mp, CoreDialect::OpenCl, layout)); s.push('\n'); s.push_str("// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.\n"); s.push_str("// The same constants as memhard.h in this pack (one emitter, three dialects).\n"); @@ -1047,8 +1212,7 @@ pub fn opencl_kernel(p: &Program, memhard: Option<&MixParams>) -> String { s.push_str(" if (t < nItems) {\n"); s.push_str(" uint s[16];\n"); s.push_str(" mh_item(cache, t, s);\n"); - s.push_str(" __global uint* d = ds + ((ulong)t * 16u);\n"); - s.push_str(" for (uint i = 0u; i < 16u; ++i) d[i] = s[i];\n"); + s.push_str(&build_store(layout, CoreDialect::OpenCl, "ds", "t")); s.push_str(" }\n"); s.push_str("}\n"); s.push('\n'); @@ -1066,20 +1230,33 @@ pub fn opencl_kernel(p: &Program, memhard: Option<&MixParams>) -> String { s.push_str(&scratch_prelude(p, CoreDialect::OpenCl)); let scratch_args = if p.has_scratch() { ", __global uint* scratch, uint groups, uint salt" } else { "" }; s.push_str(&format!("IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask{scratch_args}) {{\n")); + let (setup, unit_loop) = persistent_prologue_parts(CoreDialect::OpenCl, p.class.scratch_words_per_lane()); if p.has_scratch() { - s.push_str(&persistent_prologue(CoreDialect::OpenCl, p.class.scratch_words_per_lane())); + s.push_str(&setup); } else { s.push_str(" uint gid = (uint)get_global_id(0);\n"); } s.push_str(" uint lid = (uint)get_local_id(0);\n"); - s.push_str(" uint nonce = baseNonce + gid;\n"); - s.push_str(" uint r0, r1, r2, r3, r4, r5, r6, r7;\n"); - s.push_str("#if IGNEUM_EXCHANGE == 0\n"); - s.push_str(" IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);\n"); - s.push_str(" uint xk = 0u;\n"); - s.push_str("#else\n"); - s.push_str(" (void)lid;\n"); - s.push_str("#endif\n"); + if p.has_scratch() { + s.push_str("#if IGNEUM_EXCHANGE == 0\n"); + s.push_str(" IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);\n"); + s.push_str(" uint xk = 0u;\n"); + s.push_str("#else\n"); + s.push_str(" (void)lid;\n"); + s.push_str("#endif\n"); + s.push_str(&unit_loop); + s.push_str(" uint nonce = baseNonce + gid;\n"); + s.push_str(" uint r0, r1, r2, r3, r4, r5, r6, r7;\n"); + } else { + s.push_str(" uint nonce = baseNonce + gid;\n"); + s.push_str(" uint r0, r1, r2, r3, r4, r5, r6, r7;\n"); + s.push_str("#if IGNEUM_EXCHANGE == 0\n"); + s.push_str(" IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);\n"); + s.push_str(" uint xk = 0u;\n"); + s.push_str("#else\n"); + s.push_str(" (void)lid;\n"); + s.push_str("#endif\n"); + } if p.has_wide() { s.push_str(" uint lane = lid & 31u;\n uint wmask = mask & ~31u;\n"); } @@ -1087,7 +1264,7 @@ pub fn opencl_kernel(p: &Program, memhard: Option<&MixParams>) -> String { s.push_str(&init_line(p, "uint", i)); } s.push_str(&format!("\n for (uint it = 0u; it < {ITERATIONS}u; ++it) {{\n uint sel = r0;\n")); - s.push_str(&opencl_instr_lines(p)); + s.push_str(&opencl_instr_lines(p, dataset_log2)); s.push_str(" }\n"); s.push_str(" uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u);\n"); s.push_str(" uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u);\n"); @@ -1152,6 +1329,7 @@ pub fn program_header(p: &Program, day: &str, ds: &DatasetSource) -> String { s.push_str(&program_class_header_lines(p)); s.push_str(&class_header_lines(p)); s.push_str(&scratch_header_lines(p)); + s.push_str(&era_header_lines(p)); s.push_str("// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h)\n"); s.push_str(&format!("#define IGNEUM_DATASET_MODE {}\n", if memhard.is_some() { 1 } else { 0 })); s.push('\n'); @@ -1213,7 +1391,7 @@ pub fn cuda_memhard_header(p: &Program, mp: &MixParams) -> String { s.push_str("#else\n"); s.push_str("#define IGNEUM_HD static inline\n"); s.push_str("#endif\n"); - s.push_str(&emit_memhard_core(mp, CoreDialect::Cuda)); + s.push_str(&emit_memhard_core_layout(mp, CoreDialect::Cuda, p.class.layout())); s } @@ -1363,6 +1541,22 @@ pub fn program_json(p: &Program, day: &str, ds: &DatasetSource) -> String { s.push_str(&format!(" \"scratch\": \"variant 5 (measurement only): persistent warps; a {kb} KiB scratch per warp of {slots} 16-byte slots per lane (lane-major); slot = src & 0x{smask:x}; a slot reads as its fill (scratch_fill(seed words, unit base nonce, lane, slot, j) = splitmix32(((base + lane) ^ seed[j]) + slot * 0x9e3779b1 + (j + 1) * 0x85ebca77), j in 0..2) until the unit writes it; read w0 w1 w2 (behind a per-unit tag on the GPU), x = fold(dst, w0, w1, w2), dst = x, rewrite (x ^ w1, rotl(x, 7) ^ w2, x + w0)\",\n", kb = p.class.scratch_kb, slots = p.class.scratch_slots_per_lane(), smask = p.class.scratch_slot_mask())); } s.push_str(&format!(" \"wide_load\": \"read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, {FOLD_ROT}) * 0x{FOLD_MUL:08x}) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots\",\n")); + if let Some(e) = p.class.era { + s.push_str(" \"era\": {\n"); + s.push_str(&format!(" \"label\": {},\n", jstr(&e.label()))); + s.push_str(&format!(" \"seed_words\": [{}],\n", join_jhex(&e.words))); + s.push_str(" \"draw\": \"docs/plans/era-layout.md 1.1: SplitMix64 seeded with seed_words[0] | seed_words[1] << 32 of seed_words_from_bytes('igneum-era/' || n_le64 || E_n); width = allowed[below(|allowed|)], stride_mul = low32(next()) | 1, stride_rot = 1 + below(31), then four next() draws for a partial Fisher-Yates over positions log2(W)..15 of which 4 - log2(W) are used\",\n"); + s.push_str(&format!(" \"allowed_widths\": [{}],\n", e.allowed_set().iter().map(|w| w.to_string()).collect::>().join(", "))); + s.push_str(&format!(" \"width_words\": {},\n", e.width_words)); + s.push_str(&format!(" \"stride_mul\": {},\n", jhex(e.stride_mul))); + s.push_str(&format!(" \"stride_rot\": {},\n", e.stride_rot)); + s.push_str(&format!(" \"interleave\": [{}, {}, {}, {}],\n", e.pos[0], e.pos[1], e.pos[2], e.pos[3])); + s.push_str(" \"address\": \"y = rotl(src * stride_mul, stride_rot); k = min(win, D - 26); idx = ((y & (mask >> k)) | ((off & (2^k - 1)) << (D - k))) & mask; a wide load aligns idx down to W words\",\n"); + s.push_str(" \"windows\": \"per instruction, after the width roll: win = below(3), off = low32(next()) & (2^win - 1); used on a load slot (the instruction's win and off fields)\",\n"); + s.push_str(" \"dataset_word\": \"dataset[w] = item(t(w))[j(w)]: j(w) gathers the bits of w at the interleave positions, t(w) is w with those bits removed\",\n"); + s.push_str(" \"program_id_suffix\": \"'era/' || allowed[3] || width_words || stride_mul_le32 || stride_rot_le32 || interleave[4]\"\n"); + s.push_str(" },\n"); + } } s.push_str(&format!( " \"op_mix\": {{{}}},\n", @@ -1447,8 +1641,9 @@ pub fn program_json(p: &Program, day: &str, ds: &DatasetSource) -> String { let n = p.instrs.len(); for (k, ins) in p.instrs.iter().enumerate() { if !p.class.is_v2() { + let era_fields = if p.class.era.is_some() { format!(", \"win\": {}, \"off\": {}", ins.win, ins.off) } else { String::new() }; s.push_str(&format!( - " {{\"i\": {k}, \"op\": {}, \"dst\": {}, \"src\": {}, \"src2\": {}, \"imm\": {}, \"imm2\": {}, \"rot\": {}, \"bit\": {}, \"mask\": {}, \"width\": {}}}", + " {{\"i\": {k}, \"op\": {}, \"dst\": {}, \"src\": {}, \"src2\": {}, \"imm\": {}, \"imm2\": {}, \"rot\": {}, \"bit\": {}, \"mask\": {}, \"width\": {}{era_fields}}}", jstr(ins.op.name()), ins.dst, ins.src, @@ -1563,13 +1758,14 @@ pub fn export_pack(epoch: &Epoch, day: &str, source: &str) -> Pack { let memhard = ds.memhard().map(|m| &m.params); let bases = PACK_VECTOR_BASES.to_vec(); let outs: Vec<[u64; 32]> = bases.iter().map(|&b| epoch.hash_warp(b)).collect(); + // the self-test words under the program's layout (era layout; linear for every other class) let mut v = PackVectors { - head: (0..16).map(|i| ds.word(i)).collect(), - last: ds.word(mask), + head: (0..16).map(|i| epoch.dataset_word(i)).collect(), + last: epoch.dataset_word(mask), sample_idx: sample_indices(mask), ..Default::default() }; - v.sample_val = v.sample_idx.iter().map(|&i| ds.word(i)).collect(); + v.sample_val = v.sample_idx.iter().map(|&i| epoch.dataset_word(i)).collect(); if let Some(m) = ds.memhard() { let w = m.cache.words(); v.cache_head = w[..16].to_vec(); @@ -1581,19 +1777,19 @@ pub fn export_pack(epoch: &Epoch, day: &str, source: &str) -> Pack { let mut files = vec![ ("program.json".to_string(), program_json(p, day, ds)), ("vectors.json".to_string(), vectors_json(p, day, ds.log2_words, &bases, &outs, &v, mask, source, is_mh)), - ("kernel.cu".to_string(), cuda_kernel(p, memhard)), - ("kernel.cl".to_string(), opencl_kernel(p, memhard)), + ("kernel.cu".to_string(), cuda_kernel_at(p, memhard, ds.log2_words)), + ("kernel.cl".to_string(), opencl_kernel_at(p, memhard, ds.log2_words)), ("program.h".to_string(), program_header(p, day, ds)), ("vectors.h".to_string(), vectors_header(p, &bases, &outs, &v, mask, source, is_mh)), ("program.metal".to_string(), metal_program(p, ds.log2_words, LoadSource::Stored)), // Header-bound kernels (3 October 2026, bind.rs): new files, the seven above are unchanged. ("program_bound.metal".to_string(), metal_program_bound(p, ds.log2_words)), - ("kernel_bound.cu".to_string(), cuda_kernel_bound(p, memhard)), - ("kernel_bound.cl".to_string(), opencl_kernel_bound(p, memhard)), + ("kernel_bound.cu".to_string(), cuda_kernel_bound_at(p, memhard, ds.log2_words)), + ("kernel_bound.cl".to_string(), opencl_kernel_bound_at(p, memhard, ds.log2_words)), ]; if let Some(mp) = memhard { files.push(("memhard.h".to_string(), cuda_memhard_header(p, mp))); - files.push(("memhard.metal".to_string(), metal_memhard(mp))); + files.push(("memhard.metal".to_string(), metal_memhard_layout(mp, p.class.layout()))); } Pack { files, bases, outs, vectors: v } } diff --git a/igneum-pow/src/generator.rs b/igneum-pow/src/generator.rs index 4658b3c4..7d5bbac4 100644 --- a/igneum-pow/src/generator.rs +++ b/igneum-pow/src/generator.rs @@ -19,6 +19,12 @@ //! kept as [`generate_v1`] for the census tool and the lever measurements of `proto-metal/MEMHARD.md`. Its //! programs are not the lottery hash and no pack or vector of version 1 is current. //! +//! Era layout (5 October 2026, Counter ASIC 2.0 layers 4 and 8, `docs/plans/era-layout.md`; NOT the lottery hash, +//! behind [`LoadClass::era`]): [`EraParams`] drawn from the era seed `E_n` by [`era_draw`] (the load width, a stride +//! multiplier and rotation, the interleave of item words over the dataset), and per load site two more draws (a +//! window of the dataset: a half, a quarter or all of it, at a drawn offset). The load address is +//! [`crate::verify::load_index`]. An era class takes 12 draws per instruction, so its stream differs from version 2. +//! //! Read-width experiment (5 October 2026, gate 1, `docs/plans/read-width.md`; NOT the lottery hash, behind //! [`LoadClass`]): a program class whose `load` reads `W` bytes (4, 16 or 64: 1, 4 or 16 words, aligned to `W`) //! and folds every word into `dst` (`verify::fold_words`), with the width fixed per class or drawn per load from @@ -27,7 +33,7 @@ //! version 2 and its program id carries the class. use crate::accept::{check, Reject}; -use crate::seed::{fnv1a64, program_rng, seed_words_from_bytes}; +use crate::seed::{fnv1a64, program_rng, seed_words_from_bytes, SplitMix64}; /// Iterations of the instruction list per hash. pub const ITERATIONS: usize = 8; @@ -141,6 +147,11 @@ pub struct Instr { /// Words read by a `load`: 1 (the lottery hash, 4 bytes), 4 or 16 (the read-width experiment). 1 on every /// other op. pub width: u8, + /// Era layout, layer 8: the window shrink of this load site, 0..2 (the dataset, a half, a quarter). 0 on every + /// op of every other class. + pub win: u8, + /// Era layout, layer 8: which aligned window, below `2^win`. 0 on every op of every other class. + pub off: u8, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -188,6 +199,121 @@ pub struct LoadClass { /// Cache growth rule, option C (`memhard::growth_doublings`): the cache doubles when the dataset doubles. `false` /// for version 2 (the cache is 2^26 words on every day), `true` for v3. pub growth: bool, + /// Era layout (`docs/plans/era-layout.md`): `Some` turns on the strided, windowed load address and the + /// interleaved dataset mapping with the parameters drawn from the era seed. `None` for every other class. + pub era: Option, +} + +/// The parameters one era draws from its seed `E_n` (`docs/plans/era-layout.md` section 1.1, the proposed text of +/// spec 01 section 1.13.1). `Copy` so the class stays `Copy`; the eight words of the era stream's seed and the era +/// index are carried so a pack can say where the draw came from. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct EraParams { + /// `seed_words_from_bytes("igneum-era/" || E_n)` (`E_n` commits to the era index through the VDF input of spec + /// 04 section 4.4 step 2, so the index does not enter the draw). + pub words: [u32; 8], + /// The genesis-fixed set the width is drawn from, ascending, zero-padded (`[1, 0, 0]` pins 4 bytes: the + /// read-width decision of 5 October 2026, v2's 128 x 4 B stays). + pub allowed: [u8; 3], + /// Layer 4, item size: the words one load folds (1, 4 or 16), drawn from the allowed set. + pub width_words: u8, + /// Layer 4, stride: `y = rotl(x * stride_mul, stride_rot)`; the multiplier is odd, the rotation in 1..31. + pub stride_mul: u32, + pub stride_rot: u32, + /// Layer 4, interleave: the four ascending bit positions (0..15) of the word-within-item bits in the word + /// index; the first `log2(width_words)` are `0..`, so one aligned load stays inside one item. + pub pos: [u8; 4], +} + +/// Domain tag of the era stream seed. +pub const ERA_TAG: &[u8] = b"igneum-era/"; + +impl EraParams { + /// `seed_words_from_bytes("igneum-era/" || era_bytes)`. + pub fn stream_words(era_bytes: &[u8]) -> [u32; 8] { + let mut b = Vec::with_capacity(ERA_TAG.len() + era_bytes.len()); + b.extend_from_slice(ERA_TAG); + b.extend_from_slice(era_bytes); + seed_words_from_bytes(&b) + } + + /// The short label of the era in class names and pack lines: the first stream word as hex. + pub fn label(&self) -> String { + format!("{:08x}", self.words[0]) + } + + /// The era bytes of a test seed string: the 32 bytes (little-endian words) of `seed_words_from_bytes(s)`. + pub fn test_era_bytes(s: &str) -> [u8; 32] { + let w = seed_words_from_bytes(s.as_bytes()); + let mut out = [0u8; 32]; + for (i, x) in w.iter().enumerate() { + out[i * 4..i * 4 + 4].copy_from_slice(&x.to_le_bytes()); + } + out + } + + /// The dataset layout this era's loads and build use. + pub fn layout(&self) -> crate::memhard::Layout { + crate::memhard::Layout { pos: self.pos } + } + + /// The allowed widths as a slice (the non-zero entries). + pub fn allowed_set(&self) -> Vec { + self.allowed.iter().copied().filter(|&w| w != 0).collect() + } + + /// The bytes that enter the program id after `"era/"`: the allowed set, width, multiplier, rotation, positions. + pub fn id_bytes(&self) -> Vec { + let mut b = Vec::with_capacity(3 + 1 + 4 + 4 + 4); + b.extend_from_slice(&self.allowed); + b.push(self.width_words); + b.extend_from_slice(&self.stride_mul.to_le_bytes()); + b.extend_from_slice(&self.stride_rot.to_le_bytes()); + b.extend_from_slice(&self.pos); + b + } +} + +/// The era draw (`docs/plans/era-layout.md` section 1.1): seven draws from one SplitMix64 stream seeded with words 0 +/// and 1 of [`EraParams::stream_words`], in this order: the width from `allowed` (ascending, a non-empty subset of +/// [`WIDTH_WORDS`]; one element pins it, the draw is still consumed), the odd stride multiplier, the stride rotation +/// in 1..31, then four draws for the interleave (a partial Fisher-Yates over the candidate positions `log2(W)..15`, +/// `4 - log2(W)` of them used, the rest consumed). +pub fn era_draw(era_bytes: &[u8], allowed: &[u8]) -> EraParams { + assert!(!allowed.is_empty() && allowed.len() <= 3, "the allowed width set has 1 to 3 entries"); + for (i, &w) in allowed.iter().enumerate() { + assert!(WIDTH_WORDS.contains(&w), "allowed width {w} is not 1, 4 or 16 words"); + assert!(i == 0 || allowed[i - 1] < w, "the allowed width set is ascending"); + } + let words = EraParams::stream_words(era_bytes); + let mut s = SplitMix64::new(words[0] as u64 | ((words[1] as u64) << 32)); + let width_words = allowed[s.below(allowed.len() as u64) as usize]; + let stride_mul = (s.next() as u32) | 1; + let stride_rot = 1 + s.below(31) as u32; + let b = width_words.trailing_zeros() as usize; // 0, 2 or 4 + let free = 4 - b; + let mut c: Vec = (b as u8..16).collect(); + let mut r = [0u64; 4]; + for x in r.iter_mut() { + *x = s.next(); + } + for i in 0..free { + let n = c.len() - i; + let j = i + (r[i] % n as u64) as usize; + c.swap(i, j); + } + let mut chosen: Vec = c[..free].to_vec(); + chosen.sort_unstable(); + let mut pos = [0u8; 4]; + for i in 0..b { + pos[i] = i as u8; + } + for (i, p) in chosen.iter().enumerate() { + pos[b + i] = *p; + } + let mut al = [0u8; 3]; + al[..allowed.len()].copy_from_slice(allowed); + EraParams { words, allowed: al, width_words, stride_mul, stride_rot, pos } } /// Scratch geometry (variant 5): 16-byte slots, lane-major, 32 lanes per warp; `scratch_kb` KiB per warp gives @@ -213,13 +339,40 @@ impl LoadClass { impl LoadClass { /// Generator version 2 as adopted on 4 October 2026: 16 loads of one word. The lottery hash. pub const V2: LoadClass = - LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 1, growth: false }; + LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 1, growth: false, era: None }; /// The construction decided for program class v3 on 5 October 2026 (Counter ASIC 2.0, `docs/plans/mixer-x4.md`): /// version 2 loads (16 slots of one word, no scratch, no width roll, so the program stream is version 2's), the /// mixer applied 4 times per round, and the cache growth rule. Name "mx4". pub const MX4: LoadClass = - LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 4, growth: true }; + LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 4, growth: true, era: None }; + + /// The era class over `base` (`docs/plans/era-layout.md`): the parameters drawn by [`era_draw`]; when `allowed` + /// has more than one width the drawn width becomes the class mix (every load that width), otherwise the base + /// class's mix stands (the width rule of the read-width branch, pinned) and the era's `width_words` is the widest + /// width that mix can draw. + pub fn era(base: LoadClass, era_bytes: &[u8], allowed: &[u8]) -> LoadClass { + let mut e = era_draw(era_bytes, allowed); + let mut c = base; + if allowed.len() > 1 { + let i = WIDTH_WORDS.iter().position(|&w| w == e.width_words).unwrap(); + c.mix = [0, 0, 0]; + c.mix[i] = 100; + } else { + let widest = (0..3).rev().find(|&i| c.mix[i] > 0).map(|i| WIDTH_WORDS[i]).unwrap_or(1); + if widest != e.width_words { + // the interleave must keep the widest load inside one item: redraw the positions for that width + e = era_draw(era_bytes, &[widest]); + } + } + c.era = Some(e); + c + } + + /// The dataset layout of this class ([`crate::memhard::Layout::LINEAR`] without an era). + pub fn layout(&self) -> crate::memhard::Layout { + self.era.map(|e| e.layout()).unwrap_or(crate::memhard::Layout::LINEAR) + } /// A fixed width (1, 4 or 16 words) with `load_slots` loads per program. pub fn fixed(width_words: u8, load_slots: u8) -> LoadClass { @@ -338,7 +491,13 @@ impl LoadClass { /// "v2", "w4", "w16", "w64", "w64x4", "mix50-35-15", "mix25-50-25x8", "scr4k32"; "mx4" for the v3 construction; /// any other mixer setting appends "m" and, with the growth rule, "g" ("v2m2", "w16m4g"). + /// An era class is the base name with "-era" appended ("w4-era401998a5", "mx4-era..."). pub fn name(&self) -> String { + if let Some(e) = self.era { + let base = LoadClass { era: None, ..*self }; + let base_name = if base.is_v2() { "w4".to_string() } else { base.name() }; + return format!("{base_name}-era{}", e.label()); + } if self.is_v2() { return "v2".to_string(); } @@ -417,7 +576,23 @@ pub enum ProgramClass { /// "22:00 decided", `docs/plans/mixer-x4.md`): [`LoadClass::MX4`], version 2 loads (the width stays 4 bytes, the /// per-load mix and the scratch share are out), the mixer applied 4 times per round and the cache growth rule. The /// placeholder of the seam (w16) is replaced here; nothing else in the seam names the class. -pub const V3_CLASS: LoadClass = LoadClass::MX4; +/// Composed on 5 October 2026 (branch ca2-era): the era layout of `docs/plans/era-layout.md` is drawn inside this class by +/// [`generate_from_seed_bytes_program_class`] (`LoadClass::era(V3_CLASS, era, &V3_ALLOWED)`); here `era` is `None`. +pub const V3_CLASS: LoadClass = LoadClass { era: None, ..LoadClass::MX4 }; + +/// The width set class v3's era draw chooses from: 4 bytes only (the read-width decision of 5 October 2026; the +/// draw is consumed, so widening the set at genesis keeps the derivation). +pub const V3_ALLOWED: [u8; 1] = [1]; + +/// The program of an era class (generator version 3): `base` with the era parameters drawn from `era_bytes` +/// over the width set `allowed`, generator 3 stamped and the era bytes recorded (`docs/plans/era-layout.md`). +/// The chain's path is this with `base = V3_CLASS` and `allowed = V3_ALLOWED`. +pub fn generate_era(seed_string: &str, seed_bytes: &[u8], base: LoadClass, era_bytes: &[u8], allowed: &[u8]) -> Program { + let mut p = generate_from_seed_bytes_class(seed_string, seed_bytes, LoadClass::era(base, era_bytes, allowed)); + p.generator = GENERATOR_VERSION_V3; + p.era_bytes = Some(era_bytes.to_vec()); + p +} impl ProgramClass { /// The load class this program class draws from. @@ -581,6 +756,10 @@ pub fn program_id_class(generator: u32, seed: &[u32; 8], attempt: u32, class: &L b.push(class.mixer_mult); b.push(class.growth as u8); } + if let Some(e) = class.era { + b.extend_from_slice(b"era/"); + b.extend_from_slice(&e.id_bytes()); + } fnv1a64(&b) } @@ -715,11 +894,23 @@ pub fn candidate_from_words_class( // Version 2 loads take no width roll, so a mixer class with version 2 loads draws the version 2 program let width = if class.takes_width_roll() { class.width_for_roll(rng.below(100)) } else { 1 }; let width = if op == Op::Load { width } else { 1 }; + // Era layout, layer 8: two window draws per instruction (drawn on every slot, used on a load slot). + let (win, off) = if class.era.is_some() { + let k = rng.below(3) as u8; + let o = (rng.next() as u32 & ((1u32 << k) - 1)) as u8; + if op == Op::Load { + (k, o) + } else { + (0, 0) + } + } else { + (0, 0) + }; if op.is_load() { fresh[src as usize] = false; } fresh[dst as usize] = true; - instrs.push(Instr { op, dst: dst as u8, src: src as u8, src2: b as u8, imm, imm2, rot, bit: bit as u8, mask, width }); + instrs.push(Instr { op, dst: dst as u8, src: src as u8, src2: b as u8, imm, imm2, rot, bit: bit as u8, mask, width, win, off }); } Program { seed_string: seed_string.to_string(), @@ -793,6 +984,11 @@ pub fn generate_from_seed_bytes_class(seed_string: &str, seed_bytes: &[u8], clas /// [`generate_from_seed_bytes`] exactly; class v3 draws from [`V3_CLASS`] and stamps generator version 3 on the /// program, so its packs and its id say generator 3 (spec 01 sections 1.4.5 and 1.4.6). pub fn generate_from_seed_bytes_program_class(seed_string: &str, seed_bytes: &[u8], class: ProgramClass, era_bytes: Option<&[u8]>) -> Program { + // Class v3 with an era seed: the era layout (docs/plans/era-layout.md) drawn from the era bytes inside V3_CLASS. + // Without era bytes (a template before the era is known) the bare V3_CLASS stands. + if let (ProgramClass::V3, Some(era)) = (class, era_bytes) { + return generate_era(seed_string, seed_bytes, V3_CLASS, era, &V3_ALLOWED); + } let mut p = generate_from_seed_bytes_class(seed_string, seed_bytes, class.load_class()); p.generator = class.generator_version(); // The era is a property of class v3 programs; a version 2 program never records one, so the pinned v2 packs @@ -908,7 +1104,7 @@ pub fn generate_v1_from_words(seed_string: &str, seed: [u32; 8], cfg: &Generator if op == Op::Load && bit * 100 < cfg.wide_frac * 32 { op = Op::WLoad; } - instrs.push(Instr { op, dst: dst as u8, src: a as u8, src2: b as u8, imm, imm2, rot, bit: bit as u8, mask, width: 1 }); + instrs.push(Instr { op, dst: dst as u8, src: a as u8, src2: b as u8, imm, imm2, rot, bit: bit as u8, mask, width: 1, win: 0, off: 0 }); } Program { seed_string: seed_string.to_string(), @@ -1114,10 +1310,14 @@ mod tests { assert_eq!(v3.generator, GENERATOR_VERSION_V3); assert_eq!(v3.era_bytes.as_deref(), Some(&[7u8; 32][..])); let v3_no_era = generate_from_seed_bytes_program_class("igneum-genesis", b"igneum-genesis", ProgramClass::V3, None); - assert_eq!(v3_no_era.instrs, v3.instrs, "the placeholder class does not read the era"); - assert_eq!(v3_no_era.program_id(), v3.program_id()); + assert_ne!(v3_no_era.instrs, v3.instrs, "the era class takes two more draws per instruction"); + assert_eq!(v3_no_era.class, V3_CLASS); assert_eq!(v3.program_class(), ProgramClass::V3); - assert_eq!(v3.class, V3_CLASS); + assert_eq!(LoadClass { era: None, ..v3.class }, V3_CLASS, "the era rides inside V3_CLASS"); + assert_eq!(v3.class, LoadClass::era(V3_CLASS, &[7u8; 32], &V3_ALLOWED)); + assert_eq!(v3.class.era.unwrap().width_words, 1); + assert_eq!(v3.class.layout(), v3.class.era.unwrap().layout()); + assert_ne!(generate_from_seed_bytes_program_class("igneum-genesis", b"igneum-genesis", ProgramClass::V3, Some(&[8u8; 32])).class, v3.class); assert!(check(&v3).is_ok()); assert_eq!(v3.program_id(), program_id(GENERATOR_VERSION_V3, &v3.seed, v3.attempt)); assert_ne!(v3.program_id(), program_id(GENERATOR_VERSION, &v3.seed, v3.attempt)); @@ -1134,6 +1334,106 @@ mod tests { assert_eq!(ProgramClass::V2.load_class(), LoadClass::V2); } + /// Era layout: the draw is deterministic, within bounds, and the six test eras are pinned; an era program has + /// 16 loads with window draws in bounds and nothing drawn on ALU slots; the class names and program ids separate + /// the eras from each other and from every other class. + #[test] + fn era_draw_deterministic_and_bounded() { + let all = [1u8, 4, 16]; + for n in 0..200u64 { + let eb = EraParams::test_era_bytes(&format!("igneum-era-test/{n}")); + let e = era_draw(&eb, &all); + assert_eq!(e, era_draw(&eb, &all)); + assert_eq!(e.words, EraParams::stream_words(&eb)); + assert!(all.contains(&e.width_words)); + assert_eq!(e.stride_mul & 1, 1); + assert!((1..=31).contains(&e.stride_rot)); + assert!(e.layout().is_valid(), "{:?}", e.pos); + let b = e.width_words.trailing_zeros() as usize; + for i in 0..b { + assert_eq!(e.pos[i], i as u8, "the low positions are the identity for width {}", e.width_words); + } + // a pinned set consumes the draw and keeps the stride draws in step + let pinned = era_draw(&eb, &[1]); + assert_eq!(pinned.width_words, 1); + assert_eq!((pinned.stride_mul, pinned.stride_rot), (e.stride_mul, e.stride_rot)); + assert_ne!(era_draw(&EraParams::test_era_bytes(&format!("igneum-era-test/{}", n + 1)), &all).words, e.words); + } + // the six test eras pinned at 4 bytes (docs/plans/era-layout.md section 5): ERA_VECTORS + for (n, mul, rot, pos) in ERA_VECTORS { + let e = era_draw(&EraParams::test_era_bytes(&format!("igneum-era-test/{n}")), &V3_ALLOWED); + assert_eq!((e.width_words, e.stride_mul, e.stride_rot, e.pos), (1, mul, rot, pos), "era test seed {n}"); + } + // era programs + let mut ids = std::collections::HashSet::new(); + ids.insert(candidate("igneum-genesis", b"igneum-genesis", 0).program_id()); + ids.insert(candidate_class("igneum-genesis", b"igneum-genesis", 0, LoadClass::fixed(16, 16)).program_id()); + for n in 0..6u64 { + let eb = EraParams::test_era_bytes(&format!("igneum-era-test/{n}")); + let c = LoadClass::era(LoadClass::V2, &eb, &all); + let e = c.era.unwrap(); + assert_eq!(c.mix.iter().position(|&m| m == 100).map(|i| WIDTH_WORDS[i]), Some(e.width_words)); + let base = match e.width_words { + 1 => "w4", + 4 => "w16", + _ => "w64", + }; + assert_eq!(c.name(), format!("{base}-era{}", e.label())); + let p = candidate_class("igneum-genesis", b"igneum-genesis", 0, c); + assert_eq!(p.class, c); + assert_eq!(p.loads_per_hash(), 128); + assert_eq!(p.bytes_per_hash(), 128 * 4 * e.width_words as usize); + assert_ne!(p.instrs[0].op, Op::Load); + for ins in &p.instrs { + assert_ne!(ins.dst, ins.src); + assert!((1..=31).contains(&ins.rot)); + if ins.op == Op::Load { + assert_eq!(ins.width, e.width_words); + assert!(ins.win <= 2 && (ins.off as u32) < (1u32 << ins.win), "win {} off {}", ins.win, ins.off); + } else { + assert_eq!((ins.width, ins.win, ins.off), (1, 0, 0)); + } + } + assert!(ids.insert(p.program_id()), "era {n}: program id collides"); + // the pinned form keeps the base mix and redraws the interleave for the base's widest width + let pinned = LoadClass::era(LoadClass::mixed([50, 35, 15]), &eb, &[1]); + assert_eq!(pinned.mix, [50, 35, 15]); + assert_eq!(pinned.era.unwrap().width_words, 16); + assert_eq!(pinned.era.unwrap().pos, [0, 1, 2, 3]); + assert!(ids.insert(candidate_class("igneum-genesis", b"igneum-genesis", 0, pinned).program_id())); + } + // the window draws take two more draws per instruction: the stream differs from the read-width class + let w64 = candidate_class("igneum-genesis", b"igneum-genesis", 0, LoadClass::fixed(16, 16)); + let era0 = candidate_class("igneum-genesis", b"igneum-genesis", 0, LoadClass::era(LoadClass::V2, &EraParams::test_era_bytes("igneum-era-test/0"), &all)); + assert_ne!(w64.instrs, era0.instrs); + } + + /// The era draws of the six test seeds at the 4-byte width: (test seed, stride multiplier, rotation, positions), + /// as `igneum-pow show --era igneum-era-test/` printed them on 5 October 2026 (docs/plans/era-layout.md 5). + const ERA_VECTORS: [(u64, u32, u32, [u8; 4]); 6] = [ + (0, 0x625e5ab3, 19, [0, 2, 10, 15]), + (1, 0xb2a9d70d, 6, [1, 3, 8, 13]), + (2, 0x2b4a5b97, 28, [1, 3, 4, 8]), + (3, 0x27ea7eff, 30, [2, 3, 8, 13]), + (4, 0x4d38603d, 10, [2, 9, 13, 15]), + (5, 0x03ac37ad, 22, [0, 2, 10, 13]), + ]; + + /// The six test eras generate accepted programs through the chain's class v3 path (the acceptance rule with the + /// era address mirror); generator 3, the era bytes recorded, the class V3_CLASS with the era inside. + #[test] + fn era_programs_are_accepted() { + for n in 0..6u64 { + let eb = EraParams::test_era_bytes(&format!("igneum-era-test/{n}")); + let p = generate_from_seed_bytes_program_class("igneum-genesis", b"igneum-genesis", ProgramClass::V3, Some(&eb)); + assert!(check(&p).is_ok(), "era {n}"); + assert!(p.attempt < MAX_ATTEMPTS); + assert_eq!(p.generator, GENERATOR_VERSION_V3); + assert_eq!(p.era_bytes.as_deref(), Some(&eb[..])); + assert_eq!(p, generate_era("igneum-genesis", b"igneum-genesis", V3_CLASS, &eb, &V3_ALLOWED)); + } + } + #[test] fn generate_returns_an_accepted_program() { let p = generate("igneum-genesis"); diff --git a/igneum-pow/src/main.rs b/igneum-pow/src/main.rs index e69d3195..714ed336 100644 --- a/igneum-pow/src/main.rs +++ b/igneum-pow/src/main.rs @@ -10,6 +10,13 @@ //! //! Read-width experiment (5 October 2026, docs/plans/read-width.md): `--class v2|w4|w16|w64|w64x4|p4,p16,p64[xN]` //! on every command selects the load class (default v2, the lottery hash). Nothing in a v2 run changes. +//! +//! Era layout (5 October 2026, docs/plans/era-layout.md): `--era igneum-era-test/` (a test era seed: the 32 bytes +//! of seed_words_from_bytes of the string, era index n) or `--era :<64 hex>` (the chain's 32-byte era seed E_n) +//! turns the chosen class into its era class; `--era-widths 4` (default: the read-width decision of 5 October 2026 +//! keeps v2's 4-byte load) is the allowed width set the era draws from; `4,16,64` lets the era draw the width. + +use igneum_pow::generator::{EraParams, GENERATOR_VERSION_V3}; use igneum_pow::emit::export_pack; use igneum_pow::generator::{LoadClass, ProgramClass}; @@ -37,6 +44,44 @@ struct Args { program_class: Option, /// The era seed bytes a class v3 chain program records (`--era-hex`). era_hex: Option, + /// Era layout: `--era igneum-era-test/` or `--era :<64 hex>` composes the era class over `--class` with + /// generator 3 and the era bytes recorded (the measurement packs: v2's mixer under the era layout). + era: Option<(u64, Vec, String)>, + era_widths: Vec, +} + +/// `igneum-era-test/` or `:<64 hex>` -> (index, 32 era bytes, label). +fn parse_era(s: &str) -> Option<(u64, Vec, String)> { + if let Some(n) = s.strip_prefix("igneum-era-test/") { + let index: u64 = n.parse().ok()?; + return Some((index, EraParams::test_era_bytes(s).to_vec(), s.to_string())); + } + let (n, hex) = s.split_once(':')?; + let index: u64 = n.parse().ok()?; + let bytes = igneum_pow::bind::unhex(hex)?; + if bytes.len() != 32 { + return None; + } + Some((index, bytes, format!("igneum-era/{index}/{hex}"))) +} + +/// "4,16,64" (bytes) -> ascending words. +fn parse_widths(s: &str) -> Option> { + let mut v: Vec = s + .split(',') + .map(|x| match x.trim() { + "4" => Some(1u8), + "16" => Some(4), + "64" => Some(16), + _ => None, + }) + .collect::>>()?; + v.sort_unstable(); + v.dedup(); + if v.is_empty() { + return None; + } + Some(v) } fn usage() -> ! { @@ -50,7 +95,9 @@ fn usage() -> ! { \x20 show the accepted program, one instruction per line\n\ \x20 --class C load class: v2 (default), mx4 (class v3: mixer x4, cache growth), w4, w16, w64, w64x4, p4,p16,p64[xN], m[g]\n\ \x20 --days N days since genesis for the cache growth rule of a class with it (default 0: the 2^26-word cache)\n\ - \x20 --program-class v2|v3 the program class of the seam (v3 = generator 3 on V3_CLASS, the chain's own derivation; --era-hex records the era seed)" + \x20 --program-class v2|v3 the program class of the seam (v3 = generator 3 on V3_CLASS, the chain's own derivation; --era-hex records the era seed)\n\ + \x20 --era E era layout over --class: igneum-era-test/ or :<64 hex> (the 32-byte era seed E_n)\n\ + \x20 --era-widths 4[,16,64] the width set the era draws from, in bytes (default 4: pinned; more lets the era draw it)" ); std::process::exit(2) } @@ -72,6 +119,8 @@ fn parse() -> Args { days: 0, program_class: None, era_hex: None, + era: None, + era_widths: vec![1], }; let mut it = std::env::args().skip(1); a.cmd = it.next().unwrap_or_else(|| usage()); @@ -92,12 +141,26 @@ fn parse() -> Args { "--days" => a.days = val().parse().unwrap_or_else(|_| usage()), "--program-class" => a.program_class = Some(ProgramClass::parse(&val()).unwrap_or_else(|| usage())), "--era-hex" => a.era_hex = Some(val()), + "--era" => a.era = Some(parse_era(&val()).unwrap_or_else(|| usage())), + "--era-widths" => a.era_widths = parse_widths(&val()).unwrap_or_else(|| usage()), _ => usage(), } } + if let Some((_, bytes, _)) = &a.era { + a.class = LoadClass::era(a.class, bytes, &a.era_widths); + } a } +/// An era program is a class v3 program: generator 3 and the era bytes recorded (what the chain's +/// `Epoch::from_chain_seeds` does); the pack then carries IGNEUM_PROGRAM_CLASS "v3" and IGNEUM_ERA_SEED_HEX. +fn stamp_era(e: &mut Epoch, a: &Args) { + if let Some((_, bytes, _)) = &a.era { + e.program.generator = GENERATOR_VERSION_V3; + e.program.era_bytes = Some(bytes.clone()); + } +} + fn main() { let a = parse(); let mode = if a.closed_form { DatasetMode::ClosedForm } else { DatasetMode::MemoryHard }; @@ -129,6 +192,12 @@ fn main() { /// dataset for `--days` through `Epoch::chain_dataset_day`; `--class` is ignored under a program class (the class /// names the load class). Closed-form mode is only for string seeds under the default class. fn epoch_of(a: &Args, mode: DatasetMode) -> (Epoch, String) { + let (mut e, label) = epoch_of_class(a, mode); + stamp_era(&mut e, a); + (e, label) +} + +fn epoch_of_class(a: &Args, mode: DatasetMode) -> (Epoch, String) { let era = a.era_hex.as_ref().map(|h| igneum_pow::bind::unhex(h).unwrap_or_else(|| usage())); match (&a.epoch_hex, &a.day_hex) { (Some(eh), Some(dh)) => { @@ -291,7 +360,11 @@ fn accept(a: &Args) { fn show(a: &Args) { let (label, bytes) = seed_bytes_of(a); - let p = igneum_pow::generator::generate_from_seed_bytes_class(&label, &bytes, a.class); + let mut p = igneum_pow::generator::generate_from_seed_bytes_class(&label, &bytes, a.class); + if let Some((_, eb, _)) = &a.era { + p.generator = GENERATOR_VERSION_V3; + p.era_bytes = Some(eb.clone()); + } println!( "seed \"{}\" generator v{} class {} attempt {} program id {:016x} seed words {}", p.seed_string, @@ -302,6 +375,24 @@ fn show(a: &Args) { p.seed.iter().map(|w| format!("{w:08x}")).collect::>().join(" ") ); println!("op mix {} loads/hash {} bytes/hash {}", p.op_mix(), p.loads_per_hash(), p.bytes_per_hash()); + if let Some(e) = p.class.era { + println!( + "era {} ({}): width {} B, stride mul {:#010x} rot {}, interleave {:?}, windows (site:shrink:offset) {}", + e.label(), + a.era.as_ref().map(|x| x.2.as_str()).unwrap_or("?"), + e.width_words as u32 * 4, + e.stride_mul, + e.stride_rot, + e.pos, + p.instrs + .iter() + .enumerate() + .filter(|(_, i)| i.op == igneum_pow::generator::Op::Load) + .map(|(k, i)| format!("{k}:{}:{}", i.win, i.off)) + .collect::>() + .join(" ") + ); + } for (k, i) in p.instrs.iter().enumerate() { println!( "{k:2}: {:5} dst={} src={} src2={} imm={:#010x} imm2={:#010x} rot={} bit={} mask={}{}", diff --git a/igneum-pow/src/memhard.rs b/igneum-pow/src/memhard.rs index b38aba64..dd3cdf92 100644 --- a/igneum-pow/src/memhard.rs +++ b/igneum-pow/src/memhard.rs @@ -198,6 +198,71 @@ impl MixParams { } } +/// The dataset layout (era layout, `docs/plans/era-layout.md` section 1.2): word `w` of the dataset holds word +/// `j(w)` of item `t(w)`, where `j(w)` gathers the four bits of `w` at the ascending positions `pos` and `t(w)` is +/// `w` with those bits removed. [`Layout::LINEAR`] (`pos = [0, 1, 2, 3]`) is `dataset[w] = item(w >> 4)[w & 15]`, +/// the lottery hash's mapping. Every position is below 16, so the mapping is the same at every dataset size of +/// at least 2^16 words and an item keeps its value at every size. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct Layout { + pub pos: [u8; 4], +} + +impl Layout { + pub const LINEAR: Layout = Layout { pos: [0, 1, 2, 3] }; + + pub fn is_linear(&self) -> bool { + self.pos == [0, 1, 2, 3] + } + + /// Positions ascending, distinct, below 16. + pub fn is_valid(&self) -> bool { + self.pos.iter().all(|&p| p < 16) && (1..4).all(|i| self.pos[i] > self.pos[i - 1]) + } + + /// `(t, j)` of word index `w`. + #[inline(always)] + pub fn split(&self, w: u32) -> (u32, u32) { + if self.is_linear() { + return (w >> 4, w & 15); + } + let mut j = 0u32; + for (i, &p) in self.pos.iter().enumerate() { + j |= ((w >> p) & 1) << i; + } + // remove the highest position first so the lower ones stay where they are + let mut t = w; + for &p in self.pos.iter().rev() { + let p = p as u32; + let low = (1u32 << p) - 1; + t = (t & low) | ((t >> (p + 1)) << p); + } + (t, j) + } + + /// The word index of word `j` of item `t`: the inverse of [`Layout::split`]. + #[inline(always)] + pub fn join(&self, t: u32, j: u32) -> u32 { + if self.is_linear() { + return (t << 4) | (j & 15); + } + // insert the lowest position first: every later position counts the bit just inserted + let mut w = t; + for (i, &p) in self.pos.iter().enumerate() { + let p = p as u32; + let low = (1u32 << p) - 1; + w = ((w >> p) << (p + 1)) | (w & low) | (((j >> i) & 1) << p); + } + w + } +} + +impl Default for Layout { + fn default() -> Self { + Layout::LINEAR + } +} + /// Round key `(r + 1) * 0x9E3779B9` mod 2^32. #[inline(always)] pub fn round_key(r: usize) -> u32 { @@ -379,20 +444,28 @@ impl MemhardCpu { pub fn shape(&self) -> Shape { self.params.shape } - /// `dataset[w] = item(w >> 4)[w & 15]`. + /// `dataset[w] = item(w >> 4)[w & 15]` (the linear layout). pub fn word(&self, w: u32) -> u32 { - derive_item(w >> 4, &self.params, &self.cache)[(w & 15) as usize] + self.word_at(Layout::LINEAR, w) + } + /// `dataset[w] = item(t(w))[j(w)]` under `layout` (era layout; the layout is the program's, the cache the + /// day's, so one cache serves every era of a day). + pub fn word_at(&self, layout: Layout, w: u32) -> u32 { + let (t, j) = layout.split(w); + derive_item(t, &self.params, &self.cache)[j as usize] } /// `out[k] = dataset[idx[k]]` for every k, `idx.len() <= FETCH_MAX`. Equal items are derived once. /// Returns the number of distinct items derived. - pub fn fetch(&self, idx: &[u32], out: &mut [u32]) -> usize { + pub fn fetch(&self, idx: &[u32], out: &mut [u32], layout: Layout) -> usize { let n = idx.len(); assert!(n <= FETCH_MAX && out.len() >= n); let mut uniq = [0u32; FETCH_MAX]; let mut slot = [0u8; FETCH_MAX]; + let mut word = [0u8; FETCH_MAX]; let mut u = 0usize; for k in 0..n { - let t = idx[k] >> 4; + let (t, j) = layout.split(idx[k]); + word[k] = j as u8; let found = uniq[..u].iter().position(|&x| x == t); let j = match found { Some(j) => j, @@ -407,20 +480,24 @@ impl MemhardCpu { let mut items = [[0u32; 16]; FETCH_MAX]; derive_items(&uniq[..u], &self.params, &self.cache, &mut items); for k in 0..n { - out[k] = items[slot[k] as usize][(idx[k] & 15) as usize]; + out[k] = items[slot[k] as usize][word[k] as usize]; } u } /// `out[k][j] = dataset[base[k] + j]` for `j < width` (read-width experiment): `base[k]` is aligned to `width` - /// words, so every lane's words lie in one item, derived once per distinct item. Returns the distinct items. - pub fn fetch_wide(&self, base: &[u32], width: usize, out: &mut [[u32; 16]]) -> usize { + /// words and the layout's low `log2(width)` positions are the identity, so every lane's words lie in one item + /// at consecutive word offsets, derived once per distinct item. Returns the distinct items. + pub fn fetch_wide(&self, base: &[u32], width: usize, out: &mut [[u32; 16]], layout: Layout) -> usize { let n = base.len(); assert!(n <= FETCH_MAX && out.len() >= n && width <= 16); + debug_assert!((0..width.trailing_zeros() as usize).all(|i| layout.pos[i] == i as u8), "a wide load needs the identity on its low positions"); let mut uniq = [0u32; FETCH_MAX]; let mut slot = [0u8; FETCH_MAX]; + let mut word = [0u8; FETCH_MAX]; let mut u = 0usize; for k in 0..n { - let t = base[k] >> 4; + let (t, j0) = layout.split(base[k]); + word[k] = j0 as u8; let j = match uniq[..u].iter().position(|&x| x == t) { Some(j) => j, None => { @@ -434,7 +511,7 @@ impl MemhardCpu { let mut items = [[0u32; 16]; FETCH_MAX]; derive_items(&uniq[..u], &self.params, &self.cache, &mut items); for k in 0..n { - let o = (base[k] & 15) as usize; + let o = word[k] as usize; out[k][..width].copy_from_slice(&items[slot[k] as usize][o..o + width]); } u @@ -458,6 +535,39 @@ mod tests { assert_eq!(mp.shape, Shape::V2); } + /// Era layout: split and join are inverse, the linear layout is today's mapping, and an interleaved layout + /// keeps every position below 16 so the mapping is the same at every size of at least 2^16 words. + #[test] + fn layout_split_join() { + let lin = Layout::LINEAR; + assert!(lin.is_linear() && lin.is_valid()); + for w in [0u32, 1, 15, 16, 17, 0x0fff_ffff, 0xffff_ffff] { + assert_eq!(lin.split(w), (w >> 4, w & 15)); + assert_eq!(lin.join(w >> 4, w & 15), w); + } + let l = Layout { pos: [0, 1, 7, 12] }; + assert!(!l.is_linear() && l.is_valid()); + for w in [0u32, 1, 2, 3, 4, 127, 128, 129, 4095, 4096, 0x0fff_ffff, 0x1234_5678, 0xffff_ffff] { + let (t, j) = l.split(w); + assert!(j < 16); + assert_eq!(l.join(t, j), w, "w {w:#x}"); + } + // bits: j0 = bit 0, j1 = bit 1, j2 = bit 7, j3 = bit 12; t = the other 28 bits in order + assert_eq!(l.split(0b1_0000_0000_0000), (0, 8)); + assert_eq!(l.split(1 << 7), (0, 4)); + assert_eq!(l.split(0b100), (1, 0)); + // every t in 0..2^(D-4) appears exactly once among w < 2^D (D = 16), with every j + let mut seen = vec![0u32; 1 << 12]; + for w in 0..(1u32 << 16) { + let (t, j) = l.split(w); + seen[t as usize] |= 1 << j; + } + assert!(seen.iter().all(|&s| s == 0xffff)); + assert!(!Layout { pos: [0, 1, 1, 5] }.is_valid()); + assert!(!Layout { pos: [0, 1, 2, 16] }.is_valid()); + assert!(!Layout { pos: [1, 0, 2, 3] }.is_valid()); + } + #[test] fn chacha_block_is_a_permutation_plus_feedforward() { let x = [1u32; 16]; diff --git a/igneum-pow/src/verify.rs b/igneum-pow/src/verify.rs index 97fbfe94..940590c3 100644 --- a/igneum-pow/src/verify.rs +++ b/igneum-pow/src/verify.rs @@ -1,10 +1,36 @@ //! The CPU reference interpreter for one 32-lane warp (`cpuWarpTraced` in the Swift) and the API the node //! calls. Dataset words come from the memory-hard cache (default) or from the closed form (old packs). -use crate::generator::{generate, generate_class, Instr, LoadClass, Op, Program, ProgramClass, ITERATIONS, LANES}; -use crate::memhard::{MemhardCpu, Shape}; +use crate::generator::{generate, generate_class, EraParams, Instr, LoadClass, Op, Program, ProgramClass, ITERATIONS, LANES}; +use crate::memhard::{Layout, MemhardCpu, Shape}; use crate::seed::day_key; +/// The load address of an era program (`docs/plans/era-layout.md` section 1.3): `y = rotl(x * M, R)`, then the +/// window of the load site, `k = min(win, D - 26)` (0 when `D <= 26`), `idx = ((y & (MASK >> k)) | ((off & +/// (2^k - 1)) << (D - k))) & MASK`. For every other class `idx = x & MASK`, the lottery hash's address. `mask` is +/// `2^D - 1`. The acceptance mirror calls this at the rule's constant `D = 28`. +#[inline(always)] +pub fn load_index(era: Option<&EraParams>, ins: &Instr, x: u32, mask: u32, log2: u32) -> u32 { + match era { + None => x & mask, + Some(e) => { + let (wm, off) = window(ins, mask, log2); + let y = x.wrapping_mul(e.stride_mul).rotate_left(e.stride_rot); + ((y & wm) | off) & mask + } + } +} + +/// The window of a load site at a dataset of `2^log2` words: `(window mask, offset)` such that +/// `idx = (y & window mask) | offset` lies in the site's aligned window of `2^(log2 - k)` words. +#[inline(always)] +pub fn window(ins: &Instr, mask: u32, log2: u32) -> (u32, u32) { + let k = (ins.win as u32).min(log2.saturating_sub(26)); + let wm = mask >> k; + let off = ((ins.off as u32) & ((1u32 << k) - 1)) << (log2 - k); + (wm, off) +} + /// Read-width experiment (5 October 2026): a `load` of `W` words folds every word into `dst`: /// `x = dst XOR w[0]; for j in 1..W: x = (rotl(x, FOLD_ROT) * FOLD_MUL) XOR w[j]; dst = x`. For `W = 1` this is the /// lottery hash's `dst XOR dataset[...]`. The fold is state-dependent (the rotate-multiply sits between the words), @@ -195,18 +221,23 @@ impl DatasetSource { } } - /// `dataset[w & mask]`. + /// `dataset[w & mask]` under the linear layout (the lottery hash). pub fn word(&self, w: u32) -> u32 { + self.word_at(Layout::LINEAR, w) + } + + /// `dataset[w & mask]` under a program's layout (era layout). The closed form has no items and ignores it. + pub fn word_at(&self, layout: Layout, w: u32) -> u32 { let w = w & self.mask; match &self.dataset { Dataset::ClosedForm { d0, d1 } => dataset_elem(w, *d0, *d1), - Dataset::MemoryHard(m) => m.word(w), + Dataset::MemoryHard(m) => m.word_at(layout, w), } } /// `out[k] = dataset[idx[k]]`; indices are already masked. Returns items derived (0 for the closed form). #[inline] - fn fetch(&self, idx: &[u32; LANES], out: &mut [u32; LANES]) -> usize { + fn fetch(&self, idx: &[u32; LANES], out: &mut [u32; LANES], layout: Layout) -> usize { match &self.dataset { Dataset::ClosedForm { d0, d1 } => { for k in 0..LANES { @@ -214,14 +245,14 @@ impl DatasetSource { } 0 } - Dataset::MemoryHard(m) => m.fetch(idx, out), + Dataset::MemoryHard(m) => m.fetch(idx, out, layout), } } /// `out[k][j] = dataset[base[k] + j]` for `j < width`; bases are masked and aligned to `width` words /// (`width` 4 or 16, so a lane's words lie in one item). Returns items derived (0 for the closed form). #[inline] - fn fetch_wide(&self, base: &[u32; LANES], width: usize, out: &mut [[u32; 16]; LANES]) -> usize { + fn fetch_wide(&self, base: &[u32; LANES], width: usize, out: &mut [[u32; 16]; LANES], layout: Layout) -> usize { match &self.dataset { Dataset::ClosedForm { d0, d1 } => { for k in 0..LANES { @@ -231,7 +262,7 @@ impl DatasetSource { } 0 } - Dataset::MemoryHard(m) => m.fetch_wide(base, width, out), + Dataset::MemoryHard(m) => m.fetch_wide(base, width, out, layout), } } } @@ -260,6 +291,9 @@ pub fn interpret_warp(program: &Program, base_nonce: u32, ds: &DatasetSource) -> /// a block uses `I = bind::block_init_words(H, nonce)`. pub fn interpret_warp_init(program: &Program, seed: &[u32; 8], base_nonce: u32, ds: &DatasetSource) -> WarpResult { let mask = ds.mask; + let log2 = ds.log2_words; + let era = program.class.era; + let layout = program.class.layout(); let mut r = [[0u32; LANES]; 8]; for lane in 0..LANES { let nonce = base_nonce.wrapping_add(lane as u32); @@ -278,7 +312,7 @@ pub fn interpret_warp_init(program: &Program, seed: &[u32; 8], base_nonce: u32, for _ in 0..ITERATIONS { let sel = r[0]; for ins in &program.instrs { - step(ins, &mut r, &sel, mask, ds, &mut idx, &mut val, &mut items_derived); + step(ins, &mut r, &sel, mask, log2, era.as_ref(), layout, ds, &mut idx, &mut val, &mut items_derived); if ins.op == Op::Scratch { let m = scratch.as_mut().expect("a scratch op needs a scratch class"); let (d, a) = (ins.dst as usize, ins.src as usize); @@ -305,6 +339,9 @@ fn step( r: &mut [[u32; LANES]; 8], sel: &[u32; LANES], mask: u32, + log2: u32, + era: Option<&EraParams>, + layout: Layout, ds: &DatasetSource, idx: &mut [u32; LANES], val: &mut [u32; LANES], @@ -380,9 +417,9 @@ fn step( } Op::Load if ins.width == 1 => { for lane in 0..LANES { - idx[lane] = r[a][lane] & mask; + idx[lane] = load_index(era, ins, r[a][lane], mask, log2); } - *items_derived += ds.fetch(idx, val); + *items_derived += ds.fetch(idx, val, layout); for lane in 0..LANES { r[d][lane] ^= val[lane]; } @@ -392,10 +429,10 @@ fn step( let width = ins.width as usize; let align = !(ins.width as u32 - 1); for lane in 0..LANES { - idx[lane] = (r[a][lane] & mask) & align; + idx[lane] = load_index(era, ins, r[a][lane], mask, log2) & align; } let mut vals = [[0u32; 16]; LANES]; - *items_derived += ds.fetch_wide(idx, width, &mut vals); + *items_derived += ds.fetch_wide(idx, width, &mut vals, layout); for lane in 0..LANES { r[d][lane] = fold_words(r[d][lane], &vals[lane][..width]); } @@ -409,7 +446,7 @@ fn step( for lane in 0..LANES { idx[lane] = base + lane as u32; } - *items_derived += ds.fetch(idx, val); + *items_derived += ds.fetch(idx, val, Layout::LINEAR); for lane in 0..LANES { r[d][lane] ^= val[lane]; } @@ -457,6 +494,11 @@ impl Epoch { Self { program: generate_class(seed, class), dataset: DatasetSource::new_shape(day, mode, dataset_log2, shape) } } + /// `dataset[w]` as this epoch's program reads it: under the program's layout (era layout; linear for v2). + pub fn dataset_word(&self, w: u32) -> u32 { + self.dataset.word_at(self.program.class.layout(), w) + } + /// The production shape: memory-hard, 1 GiB dataset. pub fn memory_hard(seed: &str, day: &str) -> Self { Self::new(seed, day, DatasetMode::MemoryHard, DEFAULT_DATASET_LOG2) @@ -587,10 +629,10 @@ mod tests { let ds = DatasetSource::new("2026-10-03", DatasetMode::MemoryHard, 20); let mut base = [0u32; LANES]; for (k, b) in base.iter_mut().enumerate() { - *b = ((k as u32 * 0x9E37_79B1) & ds.mask) & !15; + *b = ((k as u32).wrapping_mul(0x9E37_79B1) & ds.mask) & !15; } let mut out = [[0u32; 16]; LANES]; - let items = ds.fetch_wide(&base, 16, &mut out); + let items = ds.fetch_wide(&base, 16, &mut out, Layout::LINEAR); assert!(items >= 1 && items <= LANES); for k in 0..LANES { for j in 0..16 { @@ -601,7 +643,7 @@ mod tests { for b in base4.iter_mut() { *b += 8; } - let items4 = ds.fetch_wide(&base4, 4, &mut out); + let items4 = ds.fetch_wide(&base4, 4, &mut out, Layout::LINEAR); assert_eq!(items4, items); for k in 0..LANES { for j in 0..4 { @@ -632,6 +674,83 @@ mod tests { assert_eq!(e.hash_warp(0), e.hash_warp(0)); } + /// Era layout: the load address stays inside the site's window and below the mask at every dataset size (the + /// window floor of 2^26 words clamps the shrink), the interleaved memory-hard dataset reads item(t(w))[j(w)] and + /// is the same prefix at 2^20 and 2^22 words, the wide fetch agrees word for word, and an era epoch hashes + /// deterministically through the interpreter and the single-nonce API. + #[test] + fn era_windows_layout_and_epochs() { + let eb = EraParams::test_era_bytes("igneum-era-test/1"); + let c = LoadClass::era(LoadClass::V2, &eb, &[1]); + let e = c.era.unwrap(); + let mut ins = Instr { op: Op::Load, dst: 0, src: 1, src2: 0, imm: 0, imm2: 0, rot: 1, bit: 0, mask: 1, width: 1, win: 2, off: 3 }; + let mut s = crate::seed::SplitMix64::new(7); + for log2 in [20u32, 26, 27, 28, 29] { + let mask = (1u64 << log2) as u32 - 1; + let k = ins.win.min(log2.saturating_sub(26) as u8) as u32; + for _ in 0..1000 { + let x = s.next() as u32; + let idx = load_index(Some(&e), &ins, x, mask, log2); + assert!(idx <= mask); + let (wm, off) = window(&ins, mask, log2); + assert_eq!(idx & !wm, off, "log2 {log2}"); + assert_eq!(wm, mask >> k); + assert_eq!(idx, ((x.wrapping_mul(e.stride_mul).rotate_left(e.stride_rot) & wm) | off) & mask); + } + } + ins.win = 0; + assert_eq!(load_index(None, &ins, 0xdead_beef, 0x0fff_ffff, 28), 0xdead_beef & 0x0fff_ffff); + // the interleaved dataset: one day cache, the layout per program + let l = e.layout(); + assert_eq!(l.pos, [1, 3, 8, 13]); + let small = DatasetSource::new("2026-10-03", DatasetMode::MemoryHard, 20); + let big = DatasetSource::new("2026-10-03", DatasetMode::MemoryHard, 22); + let m = small.memhard().unwrap(); + for w in [0u32, 1, 4, 5, 255, 256, 4095, 8192, 0x0f_ffff] { + let (t, j) = l.split(w); + assert_eq!(small.word_at(l, w), crate::memhard::derive_item(t, &m.params, &m.cache)[j as usize], "w {w}"); + assert_eq!(small.word_at(l, w), big.word_at(l, w), "prefix at w {w}"); + assert_eq!(small.word(w), small.word_at(Layout::LINEAR, w)); + } + let mut idx = [0u32; LANES]; + for (k, i) in idx.iter_mut().enumerate() { + *i = (k as u32).wrapping_mul(0x9E37_79B1) & small.mask; + } + let mut out = [0u32; LANES]; + small.fetch(&idx, &mut out, l); + for k in 0..LANES { + assert_eq!(out[k], small.word_at(l, idx[k])); + } + // a 16-byte era: the wide fetch keeps a lane's four words in one item + let eb3 = EraParams::test_era_bytes("igneum-era-test/3"); + let c3 = LoadClass::era(LoadClass::fixed(4, 16), &eb3, &[4]); + let l3 = c3.layout(); + assert_eq!(l3.pos[..2], [0, 1]); + let mut base = [0u32; LANES]; + for (k, b) in base.iter_mut().enumerate() { + *b = ((k as u32).wrapping_mul(0x9E37_79B1) & small.mask) & !3; + } + let mut wide = [[0u32; 16]; LANES]; + small.fetch_wide(&base, 4, &mut wide, l3); + for k in 0..LANES { + for j in 0..4 { + assert_eq!(wide[k][j], small.word_at(l3, base[k] + j as u32), "lane {k} word {j}"); + } + } + // era epochs hash deterministically, differ per era, and the single-nonce API agrees with the warp + let mut seen = std::collections::HashSet::new(); + for (n, c) in [(1u64, c), (3, c3)] { + let ep = Epoch::new_class("igneum-genesis", "2026-10-03", DatasetMode::MemoryHard, 20, c); + assert_eq!(ep.dataset_word(5), ep.dataset.word_at(c.layout(), 5)); + let a = ep.hash_warp(64); + assert_eq!(a, ep.hash_warp(64)); + assert_eq!(ep.hash(64 + 5), a[5]); + assert!(seen.insert(a[0]), "era {n}"); + let closed = Epoch::new_class("igneum-genesis", "2026-10-03", DatasetMode::ClosedForm, 20, c); + assert_ne!(closed.hash_warp(64), a); + } + } + #[test] fn wide_class_epochs_hash() { for name in ["w16", "w64x4", "50,35,15"] { diff --git a/igneum-pow/tests/packs.rs b/igneum-pow/tests/packs.rs index e29d2b56..54cf2692 100644 --- a/igneum-pow/tests/packs.rs +++ b/igneum-pow/tests/packs.rs @@ -15,7 +15,7 @@ use igneum_pow::emit::{ cuda_kernel, cuda_kernel_bound, cuda_memhard_header, export_pack, metal_memhard, metal_program, metal_program_bound, opencl_kernel, opencl_kernel_bound, program_header, program_json, LoadSource, }; -use igneum_pow::generator::{generate_from_seed_bytes, generate_from_seed_bytes_program_class, Op, ProgramClass, GENERATOR_VERSION, GENERATOR_VERSION_V3, LOAD_SLOTS, V3_CLASS}; +use igneum_pow::generator::{generate_from_seed_bytes, generate_from_seed_bytes_program_class, LoadClass, Op, ProgramClass, GENERATOR_VERSION, GENERATOR_VERSION_V3, LOAD_SLOTS, V3_CLASS}; use igneum_pow::memhard::{Shape, CACHE_WORDS}; use igneum_pow::verify::{DatasetMode, DatasetSource, Epoch}; use serde_json::Value; @@ -191,19 +191,21 @@ fn cache_matches_vectors() { fn check_dataset_words(pack: &str) { let v = json(pack, "vectors.json"); - let ds = &epoch(pack).dataset; + let e = epoch(pack); + let ds = &e.dataset; + // a pack's self-test words are read under its program's layout (the era layout; linear for every v2 pack) let head: Vec = v["dataset_head"].as_array().unwrap().iter().map(hex32).collect(); for (i, h) in head.iter().enumerate() { - assert_eq!(ds.word(i as u32), *h, "{pack}: dataset[{i}]"); + assert_eq!(e.dataset_word(i as u32), *h, "{pack}: dataset[{i}]"); } let last_index = v["dataset_last_index"].as_u64().unwrap() as u32; assert_eq!(last_index, ds.mask); - assert_eq!(ds.word(last_index), hex32(&v["dataset_last"]), "{pack}: dataset[MASK]"); + assert_eq!(e.dataset_word(last_index), hex32(&v["dataset_last"]), "{pack}: dataset[MASK]"); let samples = v["dataset_samples"].as_array().unwrap(); assert_eq!(samples.len(), 64); for s in samples { let idx = s["index"].as_u64().unwrap() as u32; - assert_eq!(ds.word(idx), hex32(&s["value"]), "{pack}: dataset[{idx}]"); + assert_eq!(e.dataset_word(idx), hex32(&s["value"]), "{pack}: dataset[{idx}]"); } } @@ -286,12 +288,15 @@ fn check_sources(pack: &str) { assert_same_text(pack, "program.h", &program_header(p, &day, &e.dataset)); if let Some(mp) = mp { assert_same_text(pack, "memhard.h", &cuda_memhard_header(p, mp)); - assert_same_text(pack, "memhard.metal", &metal_memhard(mp)); + assert_same_text(pack, "memhard.metal", &igneum_pow::emit::metal_memhard_layout(mp, p.class.layout())); } let got = program_json(p, &day, &e.dataset); assert_same_text(pack, "program.json", &got); let _: Value = serde_json::from_str(&got).expect("program.json is valid JSON"); - // Every load in every emitted hash kernel has the masked form, and there are exactly 16 of them. + // Every load in every emitted hash kernel has the masked form, and there are exactly 16 of them (a class with + // the era layout inside has the era form instead: `((rotl_imm(rN * M, R) & WM) | OFF) & mask`, checked by + // era_emitted_sources_match_and_loads_have_the_era_form over the era packs, and here by the same count). + let era_load = if p.class.era.is_some() { "((rotl_imm(r" } else { "" }; for (file, load, masked) in [ ("kernel.cu", "ds[r", " & mask]"), ("kernel_bound.cu", "ds[r", " & mask]"), @@ -299,6 +304,7 @@ fn check_sources(pack: &str) { ("program_bound.metal", "dataset[r", " & MASK]"), ] { let text = read(pack, file); + let load = if era_load.is_empty() { load } else { era_load }; assert_eq!(text.matches(load).count(), LOAD_SLOTS, "{pack}/{file}: 16 loads"); assert_eq!(text.matches(masked).count(), LOAD_SLOTS, "{pack}/{file}: 16 masked loads"); } @@ -326,14 +332,23 @@ fn v3_packs_are_the_v2_seeds_under_mixer_x4() { let e2 = epoch(v2); let j = json(v3, "program.json"); assert_eq!(j["program_class"].as_str().unwrap(), "v3"); - assert_eq!(j["load_class"].as_str().unwrap(), "mx4"); + assert!(j["load_class"].as_str().unwrap().starts_with("mx4"), "{v3}: mx4, or mx4 with the era inside"); assert_eq!(j["mixer_mult"].as_u64().unwrap(), 4); assert_eq!(j["cache_growth"].as_bool().unwrap(), true); assert_eq!(j["dataset"]["mixer_mult"].as_u64().unwrap(), 4); assert_eq!(j["dataset"]["cache"]["log2_words"].as_u64().unwrap(), 26); assert_eq!(e3.program.generator, GENERATOR_VERSION_V3); - assert_eq!(e3.program.class, V3_CLASS); - assert_eq!(e3.program.instrs, e2.program.instrs, "{v3}: the v2 program under the v3 construction"); + if e3.program.era_bytes.is_some() { + // the era layout composed into class v3 (docs/plans/era-layout.md, 5 October 2026): a chain pack carries an + // era, so its class is V3_CLASS with the era drawn inside and its stream takes two window draws per + // instruction; the v2 program carries over only in the seed, the attempt and the day + assert_eq!(LoadClass { era: None, ..e3.program.class }, V3_CLASS, "{v3}: the composed class"); + assert!(e3.program.class.era.is_some()); + assert_ne!(e3.program.instrs, e2.program.instrs, "{v3}: the era windows change the stream"); + } else { + assert_eq!(e3.program.class, V3_CLASS); + assert_eq!(e3.program.instrs, e2.program.instrs, "{v3}: the v2 program under the v3 construction"); + } assert_eq!(e3.program.seed, e2.program.seed); assert_eq!(e3.program.attempt, e2.program.attempt); assert_ne!(e3.program.program_id(), e2.program.program_id()); @@ -352,7 +367,7 @@ fn v3_packs_are_the_v2_seeds_under_mixer_x4() { assert!(h.contains("#define IGNEUM_MIXER_MULT 4")); assert!(h.contains("#define IGNEUM_CACHE_GROWTH 1")); assert!(h.contains("#define IGNEUM_CACHE_LOG2_WORDS 26\n")); - assert!(h.contains("#define IGNEUM_LOAD_CLASS \"mx4\"\n")); + assert!(h.contains("#define IGNEUM_LOAD_CLASS \"mx4"), "mx4, or mx4 with the era inside"); for file in ["memhard.h", "memhard.metal", "kernel.cl"] { let text = read(v3, file); assert_eq!(text.matches("j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 4u + j + 1u))").count(), 1, "{v3}/{file}"); @@ -433,3 +448,230 @@ fn devnet_pack_is_the_chain_derivation() { assert_eq!(e.program.instrs, epoch("igneum-devnet-v4-epoch0").program.instrs); assert_eq!(e.hash_warp(0), epoch("igneum-devnet-v4-epoch0").hash_warp(0)); } + +// --------------------------------------------------------------------------------------------------------- +// Era layout packs (5 October 2026, docs/plans/era-layout.md): proto-cuda/packs-ca2-era/era-, n in 0..5, the +// devnet epoch seed and day bytes under the era class of test era seed igneum-era-test/, the width pinned at +// 4 bytes (allowed_widths in program.json). Checked like the pinned packs, plus the one load form of 1.3 by text search. +// --------------------------------------------------------------------------------------------------------- + +use igneum_pow::generator::{generate_era, EraParams, V3_ALLOWED}; + +const ERA_PACKS: [&str; 6] = ["era-0", "era-1", "era-2", "era-3", "era-4", "era-5"]; + +fn era_packs_dir() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda/packs-ca2-era") +} + +fn era_read(pack: &str, file: &str) -> String { + let p = era_packs_dir().join(pack).join(file); + std::fs::read_to_string(&p).unwrap_or_else(|e| panic!("read {}: {e}", p.display())) +} + +fn era_json(pack: &str, file: &str) -> Value { + serde_json::from_str(&era_read(pack, file)).unwrap_or_else(|e| panic!("{pack}/{file}: {e}")) +} + +/// The era class of a pack: the era bytes from program.json (`era_seed_bytes`, the chain's `E_n`; the test seed +/// `igneum-era-test/` of the pack's number gives the same bytes), the allowed set from `era.allowed_widths` +/// (class v3's `V3_ALLOWED`); the pack's recorded stream words and draw must be the class's. +fn era_class(pack: &str) -> LoadClass { + let j = era_json(pack, "program.json"); + let n: u64 = pack.trim_start_matches("era-").parse().unwrap(); + let eb = unhex(&j["era_seed_bytes"]); + assert_eq!(eb, EraParams::test_era_bytes(&format!("igneum-era-test/{n}")).to_vec(), "{pack}: the era bytes of test seed {n}"); + let allowed: Vec = j["era"]["allowed_widths"].as_array().unwrap().iter().map(|v| v.as_u64().unwrap() as u8).collect(); + assert_eq!(allowed, V3_ALLOWED.to_vec(), "{pack}: class v3's width set"); + // the measurement packs of 5 October 2026: the era layout over version 2's construction (mixer x1, the genesis + // cache), generator 3 and the era bytes recorded; the chain's class v3 composes the same draw over LoadClass::MX4 + // (generator tests era_programs_are_accepted and program_classes), and the integration re-exports these packs + let c = LoadClass::era(LoadClass::V2, &eb, &allowed); + let e = c.era.unwrap(); + let words: Vec = j["era"]["seed_words"].as_array().unwrap().iter().map(hex32).collect(); + assert_eq!(e.words.to_vec(), words, "{pack}: era seed words"); + assert_eq!(e.width_words as u64, j["era"]["width_words"].as_u64().unwrap(), "{pack}: width"); + assert_eq!(e.stride_mul, hex32(&j["era"]["stride_mul"]), "{pack}: stride mul"); + assert_eq!(e.stride_rot as u64, j["era"]["stride_rot"].as_u64().unwrap(), "{pack}: stride rot"); + let pos: Vec = j["era"]["interleave"].as_array().unwrap().iter().map(|v| v.as_u64().unwrap() as u8).collect(); + assert_eq!(e.pos.to_vec(), pos, "{pack}: interleave"); + c +} + +fn era_epoch(pack: &str) -> &'static Epoch { + static E: OnceLock> = OnceLock::new(); + let all = E.get_or_init(|| { + ERA_PACKS + .iter() + .map(|p| { + let j = era_json(p, "program.json"); + let seed = j["seed"].as_str().unwrap(); + let seed_bytes = unhex(&j["seed_bytes"]); + let day_bytes = unhex(&j["dataset"]["day_bytes"]); + assert_eq!(j["dataset_mode"].as_str().unwrap(), "memory-hard"); + let log2 = j["dataset"]["log2_words"].as_u64().unwrap() as u32; + let class = era_class(p); + assert_eq!(log2, igneum_pow::verify::DEFAULT_DATASET_LOG2); + let eb = unhex(&j["era_seed_bytes"]); + let program = generate_era(seed, &seed_bytes, LoadClass::V2, &eb, &V3_ALLOWED); + assert_eq!(program.class, class, "{p}: the pack's era class"); + assert_eq!(program.generator, GENERATOR_VERSION_V3); + assert_eq!(program.era_bytes.as_deref(), Some(&eb[..])); + let mut dataset = DatasetSource::from_key(igneum_pow::seed::seed_words_from_bytes(&day_bytes), DatasetMode::MemoryHard, log2); + dataset.key_bytes = day_bytes; + (p.to_string(), Epoch { program, dataset }) + }) + .collect() + }); + &all.iter().find(|(n, _)| n == pack).unwrap().1 +} + +/// program.json of an era pack: generator, attempt, id, class, every instruction with width, win and off, and the +/// program passes the acceptance rule. +#[test] +fn era_program_json_matches() { + for pack in ERA_PACKS { + let j = era_json(pack, "program.json"); + let p = &era_epoch(pack).program; + assert_eq!(j["generator"].as_u64().unwrap() as u32, GENERATOR_VERSION_V3, "{pack}: a class v3 pack"); + assert_eq!(j["program_class"].as_str().unwrap(), "v3"); + assert_eq!(j["attempt"].as_u64().unwrap() as u32, p.attempt, "{pack}: attempt"); + assert_eq!(hex64(&j["program_id"]), p.program_id(), "{pack}: program id"); + assert_eq!(j["load_class"].as_str().unwrap(), p.class.name(), "{pack}: class"); + assert_eq!(j["bytes_per_hash"].as_u64().unwrap() as usize, p.bytes_per_hash()); + assert_eq!(p.loads_per_hash(), 8 * LOAD_SLOTS); + assert!(accept::check(p).is_ok(), "{pack}: acceptance"); + let instrs = j["instructions"].as_array().unwrap(); + assert_eq!(instrs.len(), p.instrs.len()); + for (k, (ins, ji)) in p.instrs.iter().zip(instrs).enumerate() { + assert_eq!(Op::from_name(ji["op"].as_str().unwrap()).unwrap(), ins.op, "{pack} #{k} op"); + assert_eq!(ji["dst"].as_u64().unwrap(), ins.dst as u64); + assert_eq!(ji["src"].as_u64().unwrap(), ins.src as u64); + assert_eq!(hex32(&ji["imm"]), ins.imm); + assert_eq!(ji["width"].as_u64().unwrap(), ins.width as u64, "{pack} #{k} width"); + assert_eq!(ji["win"].as_u64().unwrap(), ins.win as u64, "{pack} #{k} win"); + assert_eq!(ji["off"].as_u64().unwrap(), ins.off as u64, "{pack} #{k} off"); + if ins.op == Op::Load { + assert_eq!(ins.width, p.class.era.unwrap().width_words); + assert!(ins.win <= 2 && (ins.off as u32) < (1u32 << ins.win)); + } + } + } +} + +/// The six era packs are the same program seed under six draws: the instruction lists agree, the widths and layouts +/// follow the draw, and the dataset words differ from the linear layout exactly when the interleave is not linear. +#[test] +fn era_packs_share_the_program_and_differ_in_layout() { + let linear = &epoch("igneum-devnet-v4-epoch0").dataset; + for pack in ERA_PACKS { + let e = era_epoch(pack); + assert_eq!(e.program.seed_bytes, epoch("igneum-devnet-v4-epoch0").program.seed_bytes, "{pack}: the devnet seed"); + let strip = |p: &igneum_pow::generator::Program| { + p.instrs.iter().map(|i| (i.op, i.dst, i.src, i.src2, i.imm, i.imm2, i.rot, i.bit, i.mask, i.win, i.off)).collect::>() + }; + assert_eq!(strip(&e.program), strip(&era_epoch("era-0").program), "{pack}: same stream as era-0"); + let l = e.program.class.layout(); + let same_at_1 = (0..64u32).all(|w| e.dataset_word(w * 977 + 1) == linear.word(w * 977 + 1)); + assert_eq!(same_at_1, l.is_linear(), "{pack}: layout {:?}", l.pos); + assert_eq!(e.dataset_word(0), linear.word(0), "{pack}: word 0 is item 0 word 0 in every layout"); + // the chain's shared day cache serves every era: the day's dataset source is the pinned pack's, bit for bit + assert_eq!(e.dataset.key, linear.key); + assert_eq!(e.dataset.memhard().unwrap().cache.fnv1a64(), linear.memhard().unwrap().cache.fnv1a64()); + } +} + +#[test] +fn era_dataset_words_and_vectors_match() { + for pack in ERA_PACKS { + let v = era_json(pack, "vectors.json"); + let e = era_epoch(pack); + let ds = &e.dataset; + let head: Vec = v["dataset_head"].as_array().unwrap().iter().map(hex32).collect(); + for (i, h) in head.iter().enumerate() { + assert_eq!(e.dataset_word(i as u32), *h, "{pack}: dataset[{i}]"); + } + assert_eq!(e.dataset_word(ds.mask), hex32(&v["dataset_last"]), "{pack}: dataset[MASK]"); + for s in v["dataset_samples"].as_array().unwrap() { + let idx = s["index"].as_u64().unwrap() as u32; + assert_eq!(e.dataset_word(idx), hex32(&s["value"]), "{pack}: dataset[{idx}]"); + } + assert_eq!(ds.memhard().unwrap().cache.fnv1a64(), hex64(&v["cache_fnv1a64"])); + let mut n = 0; + for w in v["warps"].as_array().unwrap() { + let base = w["base_nonce"].as_u64().unwrap() as u32; + let expected: Vec = w["expected"].as_array().unwrap().iter().map(hex64).collect(); + let got = e.hash_warp(base); + for lane in 0..32 { + assert_eq!(got[lane], expected[lane], "{pack}: base {base} lane {lane}"); + n += 1; + } + assert_eq!(e.hash(base + 7), expected[7]); + } + assert_eq!(n, 96, "{pack}"); + } +} + +/// Every emitted file of every era pack matches the emitters byte for byte, the export reproduces vectors.json and +/// vectors.h, and every dataset load in every hash kernel has the one era form (no plain `ds[rN & mask]` remains). +#[test] +fn era_emitted_sources_match_and_loads_have_the_era_form() { + for pack in ERA_PACKS { + let e = era_epoch(pack); + let day = era_json(pack, "program.json")["dataset"]["day"].as_str().unwrap().to_string(); + let source = era_json(pack, "vectors.json")["source"].as_str().unwrap().to_string(); + let out = export_pack(e, &day, &source); + for (name, text) in &out.files { + let want = era_read(pack, name); + assert!(text == &want, "{pack}/{name} differs from the emitter"); + } + let mut on_disk: Vec = std::fs::read_dir(era_packs_dir().join(pack)) + .unwrap() + .map(|d| d.unwrap().file_name().to_string_lossy().to_string()) + .filter(|n| !n.starts_with('.') && n != "seeds.txt") + .collect(); + on_disk.sort(); + let mut want: Vec = out.files.iter().map(|(n, _)| n.clone()).collect(); + want.sort(); + assert_eq!(on_disk, want, "{pack}: the pack holds the export's files and seeds.txt only"); + let era = e.program.class.era.unwrap(); + let mul = format!("0x{:08x}u", era.stride_mul); + for (file, mask) in [ + ("kernel.cu", "mask"), + ("kernel_bound.cu", "mask"), + ("kernel.cl", "mask"), + ("kernel_bound.cl", "mask"), + ("program.metal", "MASK"), + ("program_bound.metal", "MASK"), + ] { + let text = era_read(pack, file); + let kernels = if file.starts_with("kernel_bound") || file == "kernel.cu" || file == "kernel.cl" || file.starts_with("program") { 1 } else { 1 }; + // kernel_bound.cl carries igneum_hash and igneum_hash_bound: two kernels + let kernels = if file == "kernel_bound.cl" { 2 } else { kernels }; + let era_form: usize = text + .lines() + .filter(|l| l.contains("rotl_imm(r") && l.contains(&format!(" * {mul}, {}u) & ", era.stride_rot)) && l.contains(&format!(") & {mask}"))) + .filter(|l| l.contains("ds[") || l.contains("dataset[") || l.contains("b_ = ")) + .count(); + assert_eq!(era_form, LOAD_SLOTS * kernels, "{pack}/{file}: {} loads of the era form", LOAD_SLOTS * kernels); + let plain = text.lines().filter(|l| l.contains("ds[r") || l.contains("dataset[r")).count(); + assert_eq!(plain, 0, "{pack}/{file}: a load without the era form"); + } + // the layout helpers appear exactly when the layout is not linear + let mh = era_read(pack, "memhard.h"); + assert_eq!(mh.contains("mh_addr("), !era.layout().is_linear(), "{pack}: memhard.h layout helpers"); + } +} + +/// An era pack's dataset is a prefix at every size of at least 2^16 words: the 2^20-word source gives the pack's +/// words below 2^20. +#[test] +fn era_dataset_is_a_prefix_at_smaller_sizes() { + for pack in ["era-1", "era-3"] { + let e = era_epoch(pack); + let small = DatasetSource::from_key(e.dataset.key, DatasetMode::MemoryHard, 20); + let l = e.program.class.layout(); + for w in [0u32, 1, 2, 3, 16, 255, 4096, 65_535, 65_536, 0x000f_ffff] { + assert_eq!(small.word_at(l, w), e.dataset_word(w), "{pack}: w {w}"); + } + } +} diff --git a/proto-cuda/emu/test-layout.sh b/proto-cuda/emu/test-layout.sh new file mode 100755 index 00000000..5a4a198b --- /dev/null +++ b/proto-cuda/emu/test-layout.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# The host-side dataset derivation of the harnesses under a non-linear item-to-word layout (era layout, +# docs/plans/era-layout.md 1.2): proto-cuda/host.cu (and proto-opencl/host.c, the same function) derive dataset words +# on the host through the pack's own mh_word, which carries the layout. Until 5 October 2026 both derived +# mh_item(w >> 4)[w & 15] and the "64 random points vs host derivation" check failed on every interleaved pack while +# the Mac's samples and the vectors passed (the known-failed case, docs/bench-log.md, era layout entry). This script +# runs the CUDA CPU emulation on an interleaved era pack and a linear one and requires OVERALL: PASS on both; it is +# the test that fails on the old derivation. Usage: emu/test-layout.sh [pack dir ...] +set -euo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)" +CUDA_DIR="$(cd "$HERE/.." && pwd)" +PACKS=("$@") +[ ${#PACKS[@]} -gt 0 ] || PACKS=("$CUDA_DIR/packs-ca2-era/era-1" "$CUDA_DIR/packs/igneum-devnet-v4-epoch0") +CXX="${CXX:-c++}" +for P in "${PACKS[@]}"; do + grep -q "mh_addr(" "$P/memhard.h" && kind=interleaved || kind=linear + BUILD="$HERE/build-layout-$(basename "$P")" + mkdir -p "$BUILD" + cp "$CUDA_DIR/host.cu" "$BUILD/host_emu.cpp" + sed -E 's/([A-Za-z_0-9]+)<<<([^,]+), ([^>]+)>>>\(/emu_launch(\1, \2, \3, /' "$P/kernel.cu" > "$BUILD/kernel_emu.cpp" + sed -E 's/([A-Za-z_0-9]+)<<<([^,]+), ([^>]+)>>>\(/emu_launch(\1, \2, \3, /' "$P/kernel_bound.cu" > "$BUILD/kernel_bound_emu.cpp" + "$CXX" -std=c++17 -O2 -w -I "$HERE" -I "$P" -o "$BUILD/igneum-emu" "$BUILD/host_emu.cpp" "$BUILD/kernel_emu.cpp" -DIGNEUM_BOUND "$BUILD/kernel_bound_emu.cpp" "$HERE/shim.cpp" -pthread + out="$("$BUILD/igneum-emu" --batch-log2 13 --batches 1 2>&1)" + echo "$out" | grep -E "dataset self-test|OVERALL" | sed "s|^|$(basename "$P") ($kind): |" + echo "$out" | grep -q "^OVERALL: PASS" || { echo "FAIL: $P ($kind layout) did not pass the emulated harness"; exit 1; } + echo "$out" | grep -q "64 random points vs host derivation PASS" || { echo "FAIL: $P ($kind layout): the host derivation disagrees with the pack's layout"; exit 1; } +done +echo "PASS: the host derivation follows the pack's layout on ${#PACKS[@]} pack(s)" diff --git a/proto-cuda/host.cu b/proto-cuda/host.cu index d31ce617..dc445fd6 100644 --- a/proto-cuda/host.cu +++ b/proto-cuda/host.cu @@ -115,11 +115,11 @@ static bool setupCache() { return gCachePass; } -// dataset[w] derived on the host from the host cache, exactly as proto-metal's verifier does it. +// dataset[w] derived on the host from the host cache through the pack's own mh_word (memhard.h), which carries the +// pack's item-to-word layout (era layout, 5 October 2026: the harness's former w >> 4 / w & 15 failed the random +// points of every interleaved pack while the Mac samples and vectors passed). static uint32_t host_ds_word(uint32_t w) { - uint32_t s[16]; - mh_item(hCache.data(), w >> 4u, s); - return s[w & 15u]; + return mh_word(hCache.data(), w); } #endif diff --git a/proto-cuda/packs-ca2-era/era-0/kernel.cl b/proto-cuda/packs-ca2-era/era-0/kernel.cl new file mode 100644 index 00000000..15563f3d --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/kernel.cl @@ -0,0 +1,281 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 10 15 of w. +static inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 10u) & 1u) << 2) | (((w >> 15u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00007fffu) | ((w >> 16u) << 15u); w = (w & 0x000003ffu) | ((w >> 11u) << 10u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 10u) << 11u) | (w & 0x000003ffu) | (((j >> 2u) & 1u) << 10u); w = ((w >> 15u) << 16u) | (w & 0x00007fffu) | (((j >> 3u) & 1u) << 15u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-era/era-0/kernel.cu b/proto-cuda/packs-ca2-era/era-0/kernel.cu new file mode 100644 index 00000000..616d7959 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/kernel.cu @@ -0,0 +1,163 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + for (uint32_t i = 0u; i < 16u; ++i) ds[(size_t)mh_addr(t, i)] = s[i]; + } +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-era/era-0/kernel_bound.cl b/proto-cuda/packs-ca2-era/era-0/kernel_bound.cl new file mode 100644 index 00000000..1536c23a --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/kernel_bound.cl @@ -0,0 +1,375 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 10 15 of w. +static inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 10u) & 1u) << 2) | (((w >> 15u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00007fffu) | ((w >> 16u) << 15u); w = (w & 0x000003ffu) | ((w >> 11u) << 10u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 10u) << 11u) | (w & 0x000003ffu) | (((j >> 2u) & 1u) << 10u); w = ((w >> 15u) << 16u) | (w & 0x00007fffu) | (((j >> 3u) & 1u) << 15u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-0/kernel_bound.cu b/proto-cuda/packs-ca2-era/era-0/kernel_bound.cu new file mode 100644 index 00000000..a8131602 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/kernel_bound.cu @@ -0,0 +1,123 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-era/era-0/memhard.h b/proto-cuda/packs-ca2-era/era-0/memhard.h new file mode 100644 index 00000000..d9a81e10 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/memhard.h @@ -0,0 +1,112 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 10 15 of w. +IGNEUM_HD uint32_t mh_j(uint32_t w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 10u) & 1u) << 2) | (((w >> 15u) & 1u) << 3); } +IGNEUM_HD uint32_t mh_t(uint32_t w) { w = (w & 0x00007fffu) | ((w >> 16u) << 15u); w = (w & 0x000003ffu) | ((w >> 11u) << 10u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +IGNEUM_HD uint32_t mh_addr(uint32_t t, uint32_t j) { uint32_t w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 10u) << 11u) | (w & 0x000003ffu) | (((j >> 2u) & 1u) << 10u); w = ((w >> 15u) << 16u) | (w & 0x00007fffu) | (((j >> 3u) & 1u) << 15u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } diff --git a/proto-cuda/packs-ca2-era/era-0/memhard.metal b/proto-cuda/packs-ca2-era/era-0/memhard.metal new file mode 100644 index 00000000..c8f3982d --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/memhard.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 10 15 of w. +inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 10u) & 1u) << 2) | (((w >> 15u) & 1u) << 3); } +inline uint mh_t(uint w) { w = (w & 0x00007fffu) | ((w >> 16u) << 15u); w = (w & 0x000003ffu) | ((w >> 11u) << 10u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 10u) << 11u) | (w & 0x000003ffu) | (((j >> 2u) & 1u) << 10u); w = ((w >> 15u) << 16u) | (w & 0x00007fffu) | (((j >> 3u) & 1u) << 15u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + for (uint i = 0u; i < 16u; ++i) dataset[mh_addr(gid, i)] = s[i]; +} diff --git a/proto-cuda/packs-ca2-era/era-0/program.h b/proto-cuda/packs-ca2-era/era-0/program.h new file mode 100644 index 00000000..59ceaea5 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/program.h @@ -0,0 +1,76 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_SEED_BYTES_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" +#define IGNEUM_GENERATOR 3 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x73bcbfe8ccf988f1ull +#define IGNEUM_DAY_STRING "bytes:69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY_BYTES_HEX "69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY0 0xceed56d7u +#define IGNEUM_DAY1 0x9ba270d2u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 add=15 shfl=6 mad=4 or=4 rotl=4 rotr=4 xor=4 mulhi=3 mul=2 sub=2" +// Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that +// runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). +#define IGNEUM_PROGRAM_CLASS "v3" +#define IGNEUM_ERA_SEED_HEX "8bffdd3366b9c3ffe89c1231e91538d531cfa717307df5c48ccba43096e17212" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "w4-erab2ed8a89" +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Era layout (5 October 2026, docs/plans/era-layout.md): NOT the lottery hash. Every dataset load reads +// idx = ((rotl(src * STRIDE_MUL, STRIDE_ROT) & window mask) | window offset) & MASK; the window of a load site is the +// dataset, a half or a quarter of it (IGNEUM_ERA_WINDOWS: site:shrink:offset); dataset word w holds word j(w) of item +// t(w) with j's bits at the INTERLEAVE positions (memhard.h: mh_t, mh_j, mh_addr). +#define IGNEUM_ERA_LABEL "b2ed8a89" +#define IGNEUM_ERA_SEED_WORDS { 0xb2ed8a89u, 0xb023f2bau, 0x6bbf405eu, 0x98153cddu, 0x49428e54u, 0xfbfa65eeu, 0xbcb76d0du, 0x2f509891u } +#define IGNEUM_ERA_ALLOWED_WIDTHS { 1, 0, 0 } // words, ascending, 0 = unused; one entry pins the width +#define IGNEUM_ERA_WIDTH_WORDS 1 +#define IGNEUM_ERA_STRIDE_MUL 0x625e5ab3u +#define IGNEUM_ERA_STRIDE_ROT 19 +#define IGNEUM_ERA_INTERLEAVE { 0, 2, 10, 15 } +#define IGNEUM_ERA_WINDOWS "7:2:1 8:1:1 9:1:1 10:1:1 11:0:0 13:1:1 29:0:0 30:2:2 31:1:1 44:1:1 46:2:0 47:0:0 52:0:0 56:0:0 58:2:0 63:1:1" +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u } +#define IGNEUM_KEY_INIT { 0xceed56d7u, 0x9ba270d2u, 0x82caab2du, 0x81ebce0eu, 0x12b6ecf1u, 0xd0f3fd7cu, 0xd872eefeu, 0xc158c7bdu } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 17u, 12u, 20u, 23u, 7u, 3u, 27u, 16u } +#define IGNEUM_MIX_MUL_INIT { 0xf351d601u, 0xa3bb398fu, 0xb5a09e35u, 0x7509c9c1u, 0x6bbf31e9u, 0xfc849a79u, 0xded91851u, 0x8d9113d1u, 0x0ff15225u, 0x3a5bdd41u, 0xab533435u, 0xe1c55ad5u, 0xe6d3bd0du, 0x9d9ffbbdu, 0xbb2a3cf3u, 0x50a7c08du } +#define IGNEUM_MIX_RC_INIT { 0xc6892460u, 0x25b7228au, 0xcd515004u, 0x2846527au, 0xa6324241u, 0x36e3ec53u, 0x82961bacu, 0x0f97ba7du, 0xb6f921a9u, 0x3ada24e5u, 0xde20ab91u, 0x5378eeb2u, 0x7d161662u, 0x89353cc1u, 0xb1aa03a2u, 0x788acae6u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-era/era-0/program.json b/proto-cuda/packs-ca2-era/era-0/program.json new file mode 100644 index 00000000..3fb0c5ac --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/program.json @@ -0,0 +1,143 @@ +{ + "format": "igneum-program-pack-3", + "generator": 3, + "attempt": 0, + "program_id": "0x73bcbfe8ccf988f1", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07", + "seed_words": ["0x667d0fbd", "0x7b8e5963", "0x31c67e5e", "0x4529ddc6", "0xef19d6d8", "0xaccf6211", "0xda0aed32", "0xabc6df31"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "program_class": "v3", + "era_seed_bytes": "8bffdd3366b9c3ffe89c1231e91538d531cfa717307df5c48ccba43096e17212", + "load_class": "w4-erab2ed8a89", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "era": { + "label": "b2ed8a89", + "seed_words": ["0xb2ed8a89", "0xb023f2ba", "0x6bbf405e", "0x98153cdd", "0x49428e54", "0xfbfa65ee", "0xbcb76d0d", "0x2f509891"], + "draw": "docs/plans/era-layout.md 1.1: SplitMix64 seeded with seed_words[0] | seed_words[1] << 32 of seed_words_from_bytes('igneum-era/' || n_le64 || E_n); width = allowed[below(|allowed|)], stride_mul = low32(next()) | 1, stride_rot = 1 + below(31), then four next() draws for a partial Fisher-Yates over positions log2(W)..15 of which 4 - log2(W) are used", + "allowed_widths": [1], + "width_words": 1, + "stride_mul": "0x625e5ab3", + "stride_rot": 19, + "interleave": [0, 2, 10, 15], + "address": "y = rotl(src * stride_mul, stride_rot); k = min(win, D - 26); idx = ((y & (mask >> k)) | ((off & (2^k - 1)) << (D - k))) & mask; a wide load aligns idx down to W words", + "windows": "per instruction, after the width roll: win = below(3), off = low32(next()) & (2^win - 1); used on a load slot (the instruction's win and off fields)", + "dataset_word": "dataset[w] = item(t(w))[j(w)]: j(w) gathers the bits of w at the interleave positions, t(w) is w with those bits removed", + "program_id_suffix": "'era/' || allowed[3] || width_words || stride_mul_le32 || stride_rot_le32 || interleave[4]" + }, + "op_mix": {"load": 16, "add": 15, "shfl": 6, "mad": 4, "or": 4, "rotl": 4, "rotr": 4, "xor": 4, "mulhi": 3, "mul": 2, "sub": 2}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "day_bytes": "69676e65756d2d6461792ffa50000000000000", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0xceed56d7", + "d1": "0x9ba270d2", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0xceed56d7", "0x9ba270d2", "0x82caab2d", "0x81ebce0e", "0x12b6ecf1", "0xd0f3fd7c", "0xd872eefe", "0xc158c7bd"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [17, 12, 20, 23, 7, 3, 27, 16], "mul": ["0xf351d601", "0xa3bb398f", "0xb5a09e35", "0x7509c9c1", "0x6bbf31e9", "0xfc849a79", "0xded91851", "0x8d9113d1", "0x0ff15225", "0x3a5bdd41", "0xab533435", "0xe1c55ad5", "0xe6d3bd0d", "0x9d9ffbbd", "0xbb2a3cf3", "0x50a7c08d"], "rc": ["0xc6892460", "0x25b7228a", "0xcd515004", "0x2846527a", "0xa6324241", "0x36e3ec53", "0x82961bac", "0x0f97ba7d", "0xb6f921a9", "0x3ada24e5", "0xde20ab91", "0x5378eeb2", "0x7d161662", "0x89353cc1", "0xb1aa03a2", "0x788acae6"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 1, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xbaab6229", "imm2": "0xed861989", "rot": 26, "bit": 22, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 2, "op": "shfl", "dst": 3, "src": 6, "src2": 2, "imm": "0x5b623116", "imm2": "0xff12e5b2", "rot": 12, "bit": 24, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 3, "op": "sub", "dst": 4, "src": 1, "src2": 1, "imm": "0xe99741c7", "imm2": "0xf5fa5009", "rot": 1, "bit": 21, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 4, "op": "mad", "dst": 2, "src": 0, "src2": 4, "imm": "0x673c2157", "imm2": "0xee02465f", "rot": 20, "bit": 22, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 5, "op": "rotl", "dst": 4, "src": 7, "src2": 7, "imm": "0x946f7818", "imm2": "0x45d3399e", "rot": 9, "bit": 2, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 6, "op": "rotr", "dst": 0, "src": 2, "src2": 4, "imm": "0x5f6a0ed2", "imm2": "0x7043a636", "rot": 19, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 7, "op": "load", "dst": 6, "src": 7, "src2": 1, "imm": "0x5c61dcf7", "imm2": "0x7466aa40", "rot": 19, "bit": 9, "mask": 2, "width": 1, "win": 2, "off": 1}, + {"i": 8, "op": "load", "dst": 1, "src": 4, "src2": 5, "imm": "0x85668475", "imm2": "0xdb8cc483", "rot": 29, "bit": 7, "mask": 4, "width": 1, "win": 1, "off": 1}, + {"i": 9, "op": "load", "dst": 1, "src": 2, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 10, "op": "load", "dst": 7, "src": 0, "src2": 2, "imm": "0xe075297c", "imm2": "0x5779c44c", "rot": 10, "bit": 22, "mask": 2, "width": 1, "win": 1, "off": 1}, + {"i": 11, "op": "load", "dst": 7, "src": 1, "src2": 6, "imm": "0x65aa4311", "imm2": "0x4fe48ea9", "rot": 15, "bit": 9, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 12, "op": "mul", "dst": 5, "src": 4, "src2": 2, "imm": "0x1383d3ad", "imm2": "0xf3094b29", "rot": 8, "bit": 9, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 13, "op": "load", "dst": 7, "src": 6, "src2": 2, "imm": "0xed8a496f", "imm2": "0x3072c3c6", "rot": 28, "bit": 19, "mask": 8, "width": 1, "win": 1, "off": 1}, + {"i": 14, "op": "shfl", "dst": 6, "src": 5, "src2": 0, "imm": "0x8b965b57", "imm2": "0xcfeca6c1", "rot": 12, "bit": 27, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 15, "op": "shfl", "dst": 3, "src": 5, "src2": 3, "imm": "0x877c7586", "imm2": "0xa9cb2a03", "rot": 2, "bit": 29, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 16, "op": "or", "dst": 2, "src": 7, "src2": 3, "imm": "0xb740221a", "imm2": "0x89d38d6d", "rot": 6, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 17, "op": "rotr", "dst": 0, "src": 6, "src2": 1, "imm": "0x26f3ad8a", "imm2": "0x27256f15", "rot": 18, "bit": 5, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 18, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 19, "op": "rotl", "dst": 7, "src": 0, "src2": 5, "imm": "0x849ae6ee", "imm2": "0x02b358f9", "rot": 24, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 20, "op": "add", "dst": 6, "src": 7, "src2": 6, "imm": "0x52334d12", "imm2": "0x8c9f0ef8", "rot": 11, "bit": 23, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 21, "op": "or", "dst": 2, "src": 6, "src2": 5, "imm": "0xb1871e63", "imm2": "0xb2e40191", "rot": 5, "bit": 13, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 22, "op": "mad", "dst": 6, "src": 5, "src2": 4, "imm": "0x97df29e4", "imm2": "0xe60fea84", "rot": 11, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 23, "op": "or", "dst": 1, "src": 0, "src2": 3, "imm": "0x8f30d21d", "imm2": "0x2df685a0", "rot": 31, "bit": 0, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 24, "op": "xor", "dst": 6, "src": 1, "src2": 2, "imm": "0xd2c4025f", "imm2": "0x5269eb4d", "rot": 31, "bit": 21, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 25, "op": "add", "dst": 2, "src": 6, "src2": 5, "imm": "0x659fc3d3", "imm2": "0x9cec0e12", "rot": 6, "bit": 17, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 26, "op": "rotr", "dst": 7, "src": 0, "src2": 1, "imm": "0xd89ef484", "imm2": "0x20be3846", "rot": 12, "bit": 9, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 27, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 28, "op": "mad", "dst": 3, "src": 2, "src2": 4, "imm": "0xc5c46d76", "imm2": "0x700044b5", "rot": 22, "bit": 10, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 29, "op": "load", "dst": 1, "src": 4, "src2": 3, "imm": "0x0fbaf177", "imm2": "0xfff4f2ed", "rot": 20, "bit": 31, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 30, "op": "load", "dst": 2, "src": 3, "src2": 0, "imm": "0xe90eb2e5", "imm2": "0xb0f9eb79", "rot": 27, "bit": 14, "mask": 4, "width": 1, "win": 2, "off": 2}, + {"i": 31, "op": "load", "dst": 1, "src": 5, "src2": 2, "imm": "0x97ba3fc3", "imm2": "0x7894e657", "rot": 3, "bit": 30, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 32, "op": "add", "dst": 7, "src": 2, "src2": 7, "imm": "0x070888a8", "imm2": "0xe403240e", "rot": 2, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 33, "op": "add", "dst": 2, "src": 0, "src2": 5, "imm": "0xf2e46d55", "imm2": "0x29701828", "rot": 31, "bit": 28, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 34, "op": "add", "dst": 2, "src": 3, "src2": 0, "imm": "0x58f75b87", "imm2": "0x343b7aee", "rot": 12, "bit": 14, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 35, "op": "mulhi", "dst": 2, "src": 5, "src2": 6, "imm": "0x5892a9e6", "imm2": "0xc9824c94", "rot": 19, "bit": 26, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 36, "op": "xor", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 37, "op": "mul", "dst": 6, "src": 5, "src2": 7, "imm": "0xccf564a5", "imm2": "0x873ad101", "rot": 7, "bit": 11, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 38, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xcac8f06d", "imm2": "0x6b97c683", "rot": 18, "bit": 28, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 39, "op": "add", "dst": 7, "src": 2, "src2": 4, "imm": "0xb8180e9d", "imm2": "0x32bbd117", "rot": 23, "bit": 19, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 40, "op": "rotr", "dst": 2, "src": 3, "src2": 0, "imm": "0x2d6070bc", "imm2": "0x68ff101e", "rot": 13, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 41, "op": "sub", "dst": 7, "src": 0, "src2": 2, "imm": "0x0daf96ea", "imm2": "0x36f37be1", "rot": 5, "bit": 0, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 42, "op": "add", "dst": 4, "src": 3, "src2": 6, "imm": "0x6ced15b7", "imm2": "0x6df7aed4", "rot": 19, "bit": 4, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 43, "op": "shfl", "dst": 7, "src": 3, "src2": 5, "imm": "0x8ace05f3", "imm2": "0xd378ec12", "rot": 23, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 44, "op": "load", "dst": 0, "src": 7, "src2": 4, "imm": "0xb0607786", "imm2": "0xc4acabbc", "rot": 13, "bit": 7, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 45, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 46, "op": "load", "dst": 3, "src": 1, "src2": 0, "imm": "0x63cc1e4e", "imm2": "0xa1be8118", "rot": 12, "bit": 6, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 47, "op": "load", "dst": 6, "src": 3, "src2": 7, "imm": "0x353f1d79", "imm2": "0x3b2e7456", "rot": 18, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 48, "op": "mulhi", "dst": 4, "src": 2, "src2": 7, "imm": "0x00d8a3cd", "imm2": "0x231866d2", "rot": 21, "bit": 20, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 49, "op": "add", "dst": 5, "src": 0, "src2": 2, "imm": "0xa8bae6df", "imm2": "0xf572bdb9", "rot": 14, "bit": 7, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 50, "op": "shfl", "dst": 0, "src": 7, "src2": 7, "imm": "0x81ef22e1", "imm2": "0x74438fc5", "rot": 28, "bit": 18, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 51, "op": "add", "dst": 6, "src": 0, "src2": 6, "imm": "0x383b9260", "imm2": "0x11e17c61", "rot": 12, "bit": 19, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 52, "op": "load", "dst": 5, "src": 2, "src2": 2, "imm": "0xfb84f451", "imm2": "0x11cd863e", "rot": 21, "bit": 20, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 53, "op": "rotl", "dst": 6, "src": 5, "src2": 4, "imm": "0xb1a7db6b", "imm2": "0x76686b9b", "rot": 12, "bit": 4, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 54, "op": "rotl", "dst": 3, "src": 6, "src2": 3, "imm": "0x6f981f52", "imm2": "0xd99aeba2", "rot": 12, "bit": 27, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 55, "op": "add", "dst": 2, "src": 1, "src2": 2, "imm": "0xac6be8e3", "imm2": "0x18d67dbb", "rot": 26, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 56, "op": "load", "dst": 1, "src": 4, "src2": 0, "imm": "0x7e7f6a00", "imm2": "0x6f0747da", "rot": 25, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 57, "op": "add", "dst": 5, "src": 0, "src2": 4, "imm": "0xf03673fe", "imm2": "0xa75cd60d", "rot": 16, "bit": 12, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 58, "op": "load", "dst": 5, "src": 0, "src2": 2, "imm": "0x227f94a6", "imm2": "0x0e8344f9", "rot": 20, "bit": 10, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 59, "op": "add", "dst": 1, "src": 4, "src2": 3, "imm": "0xdecd4794", "imm2": "0x8dfb96bb", "rot": 21, "bit": 7, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 60, "op": "mulhi", "dst": 3, "src": 2, "src2": 2, "imm": "0x0dd268e0", "imm2": "0x53034ca9", "rot": 1, "bit": 8, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 61, "op": "or", "dst": 6, "src": 4, "src2": 7, "imm": "0x3a45a321", "imm2": "0x9bc59a5f", "rot": 25, "bit": 11, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 62, "op": "shfl", "dst": 5, "src": 4, "src2": 2, "imm": "0x8f229cc1", "imm2": "0xcaac64a2", "rot": 17, "bit": 13, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0xb2574178", "imm2": "0xcbcc798d", "rot": 28, "bit": 0, "mask": 16, "width": 1, "win": 1, "off": 1} + ] +} diff --git a/proto-cuda/packs-ca2-era/era-0/program.metal b/proto-cuda/packs-ca2-era/era-0/program.metal new file mode 100644 index 00000000..9461d097 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/program.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-0/program_bound.metal b/proto-cuda/packs-ca2-era/era-0/program_bound.metal new file mode 100644 index 00000000..a0e6978d --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/program_bound.metal @@ -0,0 +1,111 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x625e5ab3u, 19u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x625e5ab3u, 19u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x625e5ab3u, 19u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-0/seeds.txt b/proto-cuda/packs-ca2-era/era-0/seeds.txt new file mode 100644 index 00000000..ff7b31ee --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/seeds.txt @@ -0,0 +1,5 @@ +epoch_seed_hex edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 +day_seed_hex 69676e65756d2d6461792ffa50000000000000 +epoch_index 0 +day_index 20730 +daa_score 0 diff --git a/proto-cuda/packs-ca2-era/era-0/vectors.h b/proto-cuda/packs-ca2-era/era-0/vectors.h new file mode 100644 index 00000000..ece33cb6 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/vectors.h @@ -0,0 +1,57 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0xa32ad2dd06264089ull, 0x854fea26361763aaull, 0xd958908d36ee63b5ull, 0x33641fe988c44ad9ull, 0xb5290b7892dbc7baull, 0xd815f329e6f806c0ull, 0x9fb28ff3fc29e39cull, 0xdbc28a5d8bf42ab4ull, + 0x06f955808ff71bb9ull, 0x1ec10fff7bcdeaddull, 0x1acbc289cddd83cdull, 0x395e1564cda10137ull, 0x8a2aac2aa5b190d2ull, 0x499b8401e1f3e412ull, 0x9e2320319c85fc43ull, 0x66ba9661bc421ad6ull, + 0x3ba69971f1c9d8bdull, 0x00d7a64d460bfefeull, 0xc0f6487a1fbc5978ull, 0x1a261ac8673e1146ull, 0xf5a95a7caaaafa0aull, 0x7bbdabd107842744ull, 0x49dd3bf9b77a16cfull, 0xf7026cc441495a05ull, + 0x49fc48c1cba2be91ull, 0x84c2fc11de378cf9ull, 0x96de9e40c9da551cull, 0x3d20f01e363aaf61ull, 0xef36922a5fcf96caull, 0x31f1b0c4b0b42aafull, 0x33c6406d516d5992ull, 0x847af3a4248a972full + }, + { // base nonce 4096 + 0x2039f40a61003341ull, 0x8238174df3761142ull, 0x32a72a04c4ca4a3cull, 0x16aa0fd3f65da6b3ull, 0x502bf9db9bd98782ull, 0x109f60bc6167ff30ull, 0x3873c59792d6b552ull, 0xea98fba68fc32149ull, + 0x56a8440169cb0f83ull, 0x7362edc3a7c98252ull, 0x8f393a7ad9f4ff40ull, 0x6b3cb3a1e0b02453ull, 0xdcbdc40aedfa06c0ull, 0x2bc381227147a2f3ull, 0x1570ef95b0c8e31full, 0xf3ca64d0f9ca8c91ull, + 0x1c767a63cd6a0bf8ull, 0x8e1a13a1237a948bull, 0xff2241b0b53b3efdull, 0x95949c528978b1fbull, 0xce3519499b78db7eull, 0x56a0fcecaaa62096ull, 0x0a62c2bddc7041b2ull, 0x5ec463510d31d7c0ull, + 0x2fc74273ed47b17full, 0xedc5740b0c5c191eull, 0xe62f737c106216cbull, 0x185df496808afd70ull, 0x29070790e64a0bc7ull, 0xc0ddaeac8b22b14bull, 0x302982837b6d96b4ull, 0x523972727aa906b5ull + }, + { // base nonce 1000000 + 0x3527292a5f4afb4eull, 0x8c4128a03d946030ull, 0xb5b598dd18eb207dull, 0x9f1b21b5bf9dce8full, 0xa791a53fa1a4733eull, 0x12d2f9629aaca3b8ull, 0x400e844a00ba254cull, 0x1e9f98bee0272859ull, + 0x9983064542aabb54ull, 0x017873e17fab719cull, 0xf3e747e327968d2bull, 0x562380f8016b9ca7ull, 0x3f5007c1ec21b4a8ull, 0x5612489676e34c2bull, 0x97e13b52bc125dabull, 0xe15cf6d8d12f9b00ull, + 0xd8ba508db7b12919ull, 0x2e16d66db0bd1837ull, 0x0026e228553d7b18ull, 0x0c6d22f5018958ddull, 0xe76da566fc36580dull, 0x7824813a77faff44ull, 0x663811c5504ff04dull, 0x75efd5493353d65aull, + 0x926b72ebe963caa2ull, 0x392723945438b76dull, 0x4df61663eb2bfe05ull, 0x9a305821ba9ceb59ull, 0x317f4a6ac3f65a88ull, 0xdbae9b884f6829a9ull, 0x97cc8c8866b1a0cbull, 0x5db142b6d7777df1ull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0x3dd50b1fu, 0x48edec90u, 0x93123701u, 0x3a7d2407u, 0x5119540eu, 0x657ac748u, 0x3358ef2du, 0x09e2105cu, + 0xcdd88b45u, 0xb03a0ac0u, 0x2c37c6b0u, 0x10e2c6a5u, 0x25e2929cu, 0x4317cabdu, 0x5fa0cdccu, 0x6178ee52u +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xf7b7180eu; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0x1faef64au, 0x068e4a54u, 0x24551c62u, 0x1234f539u, 0xc15100f5u, 0x2f53b6b0u, 0xbab5fb76u, 0x06edf897u, 0xb0880fc6u, 0xccfddc4du, 0xf640c0f4u, 0xf145a054u, 0xa2425e8fu, 0x0a218118u, 0xd310512au, 0xb612d7ffu, 0x9a290b4du, 0xaf0227edu, 0x81b9c33cu, 0x5f352daeu, 0x46889c3du, 0x8ddc5d17u, 0x368b4a6cu, 0x16acb57au, 0x103f86c1u, 0x7144efafu, 0x87cd0dc1u, 0x1d9132b1u, 0x28b0d87cu, 0x01c44610u, 0x908b6bacu, 0x9e127820u, 0x9e695a6fu, 0xfbb80109u, 0x39ad9ba2u, 0x0c867f51u, 0xac1e69e5u, 0xf16f763du, 0x2de60ff0u, 0xfabaf858u, 0x53a35511u, 0x249e753fu, 0x88f84300u, 0xd79db283u, 0x7ac395cau, 0x5df23d8bu, 0xe3f98318u, 0x33ad1217u, 0x2dd19e3fu, 0xf1a97b54u, 0xb1fabd1eu, 0x1bc27b43u, 0xb208c2e5u, 0x1f109813u, 0xccb6a56bu, 0x64b0398eu, 0x9ac3815eu, 0x14970614u, 0x6139a686u, 0x6a5eeda3u, 0xbb567051u, 0xd2cd3e4du, 0x08c46214u, 0xc15f2c97u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0xebd9055cu, 0x7eaf6b21u, 0x9b610855u, 0x134a8562u, 0xb10059bau, 0x7f459e58u, 0x8f3c7873u, 0x3523e609u, + 0x75b8f4cau, 0x750d31b4u, 0x0dae0782u, 0x26f10015u, 0x7ba87a41u, 0x0efd8543u, 0x691d6368u, 0x8929d967u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x51474edfu, 0xc3cfba93u, 0xf21454cfu, 0x9b79baacu, 0x4d4fce23u, 0xd701dfd5u, 0x37357ab3u, 0x1be693fau, + 0xa701cb3bu, 0x7467c620u, 0x428184e8u, 0xf4010df0u, 0xe33aa88fu, 0xc78d6d62u, 0xae9ba9c0u, 0xf4bba97eu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x448274a57f508cbcull; diff --git a/proto-cuda/packs-ca2-era/era-0/vectors.json b/proto-cuda/packs-ca2-era/era-0/vectors.json new file mode 100644 index 00000000..acf72996 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-0/vectors.json @@ -0,0 +1,36 @@ +{ + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0xa32ad2dd06264089", "0x854fea26361763aa", "0xd958908d36ee63b5", "0x33641fe988c44ad9", "0xb5290b7892dbc7ba", "0xd815f329e6f806c0", "0x9fb28ff3fc29e39c", "0xdbc28a5d8bf42ab4", + "0x06f955808ff71bb9", "0x1ec10fff7bcdeadd", "0x1acbc289cddd83cd", "0x395e1564cda10137", "0x8a2aac2aa5b190d2", "0x499b8401e1f3e412", "0x9e2320319c85fc43", "0x66ba9661bc421ad6", + "0x3ba69971f1c9d8bd", "0x00d7a64d460bfefe", "0xc0f6487a1fbc5978", "0x1a261ac8673e1146", "0xf5a95a7caaaafa0a", "0x7bbdabd107842744", "0x49dd3bf9b77a16cf", "0xf7026cc441495a05", + "0x49fc48c1cba2be91", "0x84c2fc11de378cf9", "0x96de9e40c9da551c", "0x3d20f01e363aaf61", "0xef36922a5fcf96ca", "0x31f1b0c4b0b42aaf", "0x33c6406d516d5992", "0x847af3a4248a972f" + ]}, + {"base_nonce": 4096, "expected": [ + "0x2039f40a61003341", "0x8238174df3761142", "0x32a72a04c4ca4a3c", "0x16aa0fd3f65da6b3", "0x502bf9db9bd98782", "0x109f60bc6167ff30", "0x3873c59792d6b552", "0xea98fba68fc32149", + "0x56a8440169cb0f83", "0x7362edc3a7c98252", "0x8f393a7ad9f4ff40", "0x6b3cb3a1e0b02453", "0xdcbdc40aedfa06c0", "0x2bc381227147a2f3", "0x1570ef95b0c8e31f", "0xf3ca64d0f9ca8c91", + "0x1c767a63cd6a0bf8", "0x8e1a13a1237a948b", "0xff2241b0b53b3efd", "0x95949c528978b1fb", "0xce3519499b78db7e", "0x56a0fcecaaa62096", "0x0a62c2bddc7041b2", "0x5ec463510d31d7c0", + "0x2fc74273ed47b17f", "0xedc5740b0c5c191e", "0xe62f737c106216cb", "0x185df496808afd70", "0x29070790e64a0bc7", "0xc0ddaeac8b22b14b", "0x302982837b6d96b4", "0x523972727aa906b5" + ]}, + {"base_nonce": 1000000, "expected": [ + "0x3527292a5f4afb4e", "0x8c4128a03d946030", "0xb5b598dd18eb207d", "0x9f1b21b5bf9dce8f", "0xa791a53fa1a4733e", "0x12d2f9629aaca3b8", "0x400e844a00ba254c", "0x1e9f98bee0272859", + "0x9983064542aabb54", "0x017873e17fab719c", "0xf3e747e327968d2b", "0x562380f8016b9ca7", "0x3f5007c1ec21b4a8", "0x5612489676e34c2b", "0x97e13b52bc125dab", "0xe15cf6d8d12f9b00", + "0xd8ba508db7b12919", "0x2e16d66db0bd1837", "0x0026e228553d7b18", "0x0c6d22f5018958dd", "0xe76da566fc36580d", "0x7824813a77faff44", "0x663811c5504ff04d", "0x75efd5493353d65a", + "0x926b72ebe963caa2", "0x392723945438b76d", "0x4df61663eb2bfe05", "0x9a305821ba9ceb59", "0x317f4a6ac3f65a88", "0xdbae9b884f6829a9", "0x97cc8c8866b1a0cb", "0x5db142b6d7777df1" + ]} + ], + "dataset_head": ["0x3dd50b1f", "0x48edec90", "0x93123701", "0x3a7d2407", "0x5119540e", "0x657ac748", "0x3358ef2d", "0x09e2105c", "0xcdd88b45", "0xb03a0ac0", "0x2c37c6b0", "0x10e2c6a5", "0x25e2929c", "0x4317cabd", "0x5fa0cdcc", "0x6178ee52"], + "dataset_last_index": 268435455, + "dataset_last": "0xf7b7180e", + "dataset_samples": [{"index": 59471966, "value": "0x1faef64a"}, {"index": 217795994, "value": "0x068e4a54"}, {"index": 208353206, "value": "0x24551c62"}, {"index": 42483309, "value": "0x1234f539"}, {"index": 172547758, "value": "0xc15100f5"}, {"index": 148076330, "value": "0x2f53b6b0"}, {"index": 183853158, "value": "0xbab5fb76"}, {"index": 214389424, "value": "0x06edf897"}, {"index": 267488061, "value": "0xb0880fc6"}, {"index": 169781097, "value": "0xccfddc4d"}, {"index": 184093494, "value": "0xf640c0f4"}, {"index": 153880993, "value": "0xf145a054"}, {"index": 84977930, "value": "0xa2425e8f"}, {"index": 46426879, "value": "0x0a218118"}, {"index": 3093825, "value": "0xd310512a"}, {"index": 225364072, "value": "0xb612d7ff"}, {"index": 44593546, "value": "0x9a290b4d"}, {"index": 260713159, "value": "0xaf0227ed"}, {"index": 168250303, "value": "0x81b9c33c"}, {"index": 52384140, "value": "0x5f352dae"}, {"index": 223401610, "value": "0x46889c3d"}, {"index": 45554030, "value": "0x8ddc5d17"}, {"index": 95410555, "value": "0x368b4a6c"}, {"index": 175039924, "value": "0x16acb57a"}, {"index": 79171087, "value": "0x103f86c1"}, {"index": 267580473, "value": "0x7144efaf"}, {"index": 24168642, "value": "0x87cd0dc1"}, {"index": 37981670, "value": "0x1d9132b1"}, {"index": 171551130, "value": "0x28b0d87c"}, {"index": 195559979, "value": "0x01c44610"}, {"index": 204611762, "value": "0x908b6bac"}, {"index": 140997658, "value": "0x9e127820"}, {"index": 138925853, "value": "0x9e695a6f"}, {"index": 86637313, "value": "0xfbb80109"}, {"index": 20736778, "value": "0x39ad9ba2"}, {"index": 219665210, "value": "0x0c867f51"}, {"index": 160430336, "value": "0xac1e69e5"}, {"index": 264654675, "value": "0xf16f763d"}, {"index": 8013395, "value": "0x2de60ff0"}, {"index": 228945585, "value": "0xfabaf858"}, {"index": 213884386, "value": "0x53a35511"}, {"index": 104419827, "value": "0x249e753f"}, {"index": 44185464, "value": "0x88f84300"}, {"index": 142737231, "value": "0xd79db283"}, {"index": 99284897, "value": "0x7ac395ca"}, {"index": 132475900, "value": "0x5df23d8b"}, {"index": 61861762, "value": "0xe3f98318"}, {"index": 132056166, "value": "0x33ad1217"}, {"index": 262388043, "value": "0x2dd19e3f"}, {"index": 91878046, "value": "0xf1a97b54"}, {"index": 117353561, "value": "0xb1fabd1e"}, {"index": 124768597, "value": "0x1bc27b43"}, {"index": 71352993, "value": "0xb208c2e5"}, {"index": 190698941, "value": "0x1f109813"}, {"index": 46055428, "value": "0xccb6a56b"}, {"index": 55281366, "value": "0x64b0398e"}, {"index": 165145231, "value": "0x9ac3815e"}, {"index": 106810753, "value": "0x14970614"}, {"index": 171985651, "value": "0x6139a686"}, {"index": 232085256, "value": "0x6a5eeda3"}, {"index": 159510492, "value": "0xbb567051"}, {"index": 40072060, "value": "0xd2cd3e4d"}, {"index": 209107596, "value": "0x08c46214"}, {"index": 39023794, "value": "0xc15f2c97"}], + "cache_head": ["0xebd9055c", "0x7eaf6b21", "0x9b610855", "0x134a8562", "0xb10059ba", "0x7f459e58", "0x8f3c7873", "0x3523e609", "0x75b8f4ca", "0x750d31b4", "0x0dae0782", "0x26f10015", "0x7ba87a41", "0x0efd8543", "0x691d6368", "0x8929d967"], + "cache_last_line": ["0x51474edf", "0xc3cfba93", "0xf21454cf", "0x9b79baac", "0x4d4fce23", "0xd701dfd5", "0x37357ab3", "0x1be693fa", "0xa701cb3b", "0x7467c620", "0x428184e8", "0xf4010df0", "0xe33aa88f", "0xc78d6d62", "0xae9ba9c0", "0xf4bba97e"], + "cache_fnv1a64": "0x448274a57f508cbc" +} diff --git a/proto-cuda/packs-ca2-era/era-1/kernel.cl b/proto-cuda/packs-ca2-era/era-1/kernel.cl new file mode 100644 index 00000000..f8af3e5b --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/kernel.cl @@ -0,0 +1,281 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 1 3 8 13 of w. +static inline uint mh_j(uint w) { return ((w >> 1u) & 1u) | (((w >> 3u) & 1u) << 1) | (((w >> 8u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000000ffu) | ((w >> 9u) << 8u); w = (w & 0x00000007u) | ((w >> 4u) << 3u); w = (w & 0x00000001u) | ((w >> 2u) << 1u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 1u) << 2u) | (w & 0x00000001u) | (((j >> 0u) & 1u) << 1u); w = ((w >> 3u) << 4u) | (w & 0x00000007u) | (((j >> 1u) & 1u) << 3u); w = ((w >> 8u) << 9u) | (w & 0x000000ffu) | (((j >> 2u) & 1u) << 8u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-era/era-1/kernel.cu b/proto-cuda/packs-ca2-era/era-1/kernel.cu new file mode 100644 index 00000000..774b1485 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/kernel.cu @@ -0,0 +1,163 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + for (uint32_t i = 0u; i < 16u; ++i) ds[(size_t)mh_addr(t, i)] = s[i]; + } +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-era/era-1/kernel_bound.cl b/proto-cuda/packs-ca2-era/era-1/kernel_bound.cl new file mode 100644 index 00000000..bb2edf5d --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/kernel_bound.cl @@ -0,0 +1,375 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 1 3 8 13 of w. +static inline uint mh_j(uint w) { return ((w >> 1u) & 1u) | (((w >> 3u) & 1u) << 1) | (((w >> 8u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000000ffu) | ((w >> 9u) << 8u); w = (w & 0x00000007u) | ((w >> 4u) << 3u); w = (w & 0x00000001u) | ((w >> 2u) << 1u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 1u) << 2u) | (w & 0x00000001u) | (((j >> 0u) & 1u) << 1u); w = ((w >> 3u) << 4u) | (w & 0x00000007u) | (((j >> 1u) & 1u) << 3u); w = ((w >> 8u) << 9u) | (w & 0x000000ffu) | (((j >> 2u) & 1u) << 8u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-1/kernel_bound.cu b/proto-cuda/packs-ca2-era/era-1/kernel_bound.cu new file mode 100644 index 00000000..db4db42e --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/kernel_bound.cu @@ -0,0 +1,123 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-era/era-1/memhard.h b/proto-cuda/packs-ca2-era/era-1/memhard.h new file mode 100644 index 00000000..77b9a403 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/memhard.h @@ -0,0 +1,112 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 1 3 8 13 of w. +IGNEUM_HD uint32_t mh_j(uint32_t w) { return ((w >> 1u) & 1u) | (((w >> 3u) & 1u) << 1) | (((w >> 8u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +IGNEUM_HD uint32_t mh_t(uint32_t w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000000ffu) | ((w >> 9u) << 8u); w = (w & 0x00000007u) | ((w >> 4u) << 3u); w = (w & 0x00000001u) | ((w >> 2u) << 1u); return w; } +IGNEUM_HD uint32_t mh_addr(uint32_t t, uint32_t j) { uint32_t w = t; w = ((w >> 1u) << 2u) | (w & 0x00000001u) | (((j >> 0u) & 1u) << 1u); w = ((w >> 3u) << 4u) | (w & 0x00000007u) | (((j >> 1u) & 1u) << 3u); w = ((w >> 8u) << 9u) | (w & 0x000000ffu) | (((j >> 2u) & 1u) << 8u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } diff --git a/proto-cuda/packs-ca2-era/era-1/memhard.metal b/proto-cuda/packs-ca2-era/era-1/memhard.metal new file mode 100644 index 00000000..3c203084 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/memhard.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 1 3 8 13 of w. +inline uint mh_j(uint w) { return ((w >> 1u) & 1u) | (((w >> 3u) & 1u) << 1) | (((w >> 8u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000000ffu) | ((w >> 9u) << 8u); w = (w & 0x00000007u) | ((w >> 4u) << 3u); w = (w & 0x00000001u) | ((w >> 2u) << 1u); return w; } +inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 1u) << 2u) | (w & 0x00000001u) | (((j >> 0u) & 1u) << 1u); w = ((w >> 3u) << 4u) | (w & 0x00000007u) | (((j >> 1u) & 1u) << 3u); w = ((w >> 8u) << 9u) | (w & 0x000000ffu) | (((j >> 2u) & 1u) << 8u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + for (uint i = 0u; i < 16u; ++i) dataset[mh_addr(gid, i)] = s[i]; +} diff --git a/proto-cuda/packs-ca2-era/era-1/program.h b/proto-cuda/packs-ca2-era/era-1/program.h new file mode 100644 index 00000000..03a56397 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/program.h @@ -0,0 +1,76 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_SEED_BYTES_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" +#define IGNEUM_GENERATOR 3 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x73bcbfe8ccf988f1ull +#define IGNEUM_DAY_STRING "bytes:69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY_BYTES_HEX "69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY0 0xceed56d7u +#define IGNEUM_DAY1 0x9ba270d2u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 add=15 shfl=6 mad=4 or=4 rotl=4 rotr=4 xor=4 mulhi=3 mul=2 sub=2" +// Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that +// runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). +#define IGNEUM_PROGRAM_CLASS "v3" +#define IGNEUM_ERA_SEED_HEX "ff87ad96a1b53f367a95d5ca123bab64211bd9aa57fc7ad3c5e2d496c20138d4" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "w4-era676a17fc" +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Era layout (5 October 2026, docs/plans/era-layout.md): NOT the lottery hash. Every dataset load reads +// idx = ((rotl(src * STRIDE_MUL, STRIDE_ROT) & window mask) | window offset) & MASK; the window of a load site is the +// dataset, a half or a quarter of it (IGNEUM_ERA_WINDOWS: site:shrink:offset); dataset word w holds word j(w) of item +// t(w) with j's bits at the INTERLEAVE positions (memhard.h: mh_t, mh_j, mh_addr). +#define IGNEUM_ERA_LABEL "676a17fc" +#define IGNEUM_ERA_SEED_WORDS { 0x676a17fcu, 0x60bc956eu, 0x3e9865f4u, 0x68ae9e61u, 0xc0b3f442u, 0xaf406bebu, 0xb6126b9au, 0xaa30959bu } +#define IGNEUM_ERA_ALLOWED_WIDTHS { 1, 0, 0 } // words, ascending, 0 = unused; one entry pins the width +#define IGNEUM_ERA_WIDTH_WORDS 1 +#define IGNEUM_ERA_STRIDE_MUL 0xb2a9d70du +#define IGNEUM_ERA_STRIDE_ROT 6 +#define IGNEUM_ERA_INTERLEAVE { 1, 3, 8, 13 } +#define IGNEUM_ERA_WINDOWS "7:2:1 8:1:1 9:1:1 10:1:1 11:0:0 13:1:1 29:0:0 30:2:2 31:1:1 44:1:1 46:2:0 47:0:0 52:0:0 56:0:0 58:2:0 63:1:1" +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u } +#define IGNEUM_KEY_INIT { 0xceed56d7u, 0x9ba270d2u, 0x82caab2du, 0x81ebce0eu, 0x12b6ecf1u, 0xd0f3fd7cu, 0xd872eefeu, 0xc158c7bdu } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 17u, 12u, 20u, 23u, 7u, 3u, 27u, 16u } +#define IGNEUM_MIX_MUL_INIT { 0xf351d601u, 0xa3bb398fu, 0xb5a09e35u, 0x7509c9c1u, 0x6bbf31e9u, 0xfc849a79u, 0xded91851u, 0x8d9113d1u, 0x0ff15225u, 0x3a5bdd41u, 0xab533435u, 0xe1c55ad5u, 0xe6d3bd0du, 0x9d9ffbbdu, 0xbb2a3cf3u, 0x50a7c08du } +#define IGNEUM_MIX_RC_INIT { 0xc6892460u, 0x25b7228au, 0xcd515004u, 0x2846527au, 0xa6324241u, 0x36e3ec53u, 0x82961bacu, 0x0f97ba7du, 0xb6f921a9u, 0x3ada24e5u, 0xde20ab91u, 0x5378eeb2u, 0x7d161662u, 0x89353cc1u, 0xb1aa03a2u, 0x788acae6u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-era/era-1/program.json b/proto-cuda/packs-ca2-era/era-1/program.json new file mode 100644 index 00000000..caa8b25b --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/program.json @@ -0,0 +1,143 @@ +{ + "format": "igneum-program-pack-3", + "generator": 3, + "attempt": 0, + "program_id": "0x73bcbfe8ccf988f1", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07", + "seed_words": ["0x667d0fbd", "0x7b8e5963", "0x31c67e5e", "0x4529ddc6", "0xef19d6d8", "0xaccf6211", "0xda0aed32", "0xabc6df31"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "program_class": "v3", + "era_seed_bytes": "ff87ad96a1b53f367a95d5ca123bab64211bd9aa57fc7ad3c5e2d496c20138d4", + "load_class": "w4-era676a17fc", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "era": { + "label": "676a17fc", + "seed_words": ["0x676a17fc", "0x60bc956e", "0x3e9865f4", "0x68ae9e61", "0xc0b3f442", "0xaf406beb", "0xb6126b9a", "0xaa30959b"], + "draw": "docs/plans/era-layout.md 1.1: SplitMix64 seeded with seed_words[0] | seed_words[1] << 32 of seed_words_from_bytes('igneum-era/' || n_le64 || E_n); width = allowed[below(|allowed|)], stride_mul = low32(next()) | 1, stride_rot = 1 + below(31), then four next() draws for a partial Fisher-Yates over positions log2(W)..15 of which 4 - log2(W) are used", + "allowed_widths": [1], + "width_words": 1, + "stride_mul": "0xb2a9d70d", + "stride_rot": 6, + "interleave": [1, 3, 8, 13], + "address": "y = rotl(src * stride_mul, stride_rot); k = min(win, D - 26); idx = ((y & (mask >> k)) | ((off & (2^k - 1)) << (D - k))) & mask; a wide load aligns idx down to W words", + "windows": "per instruction, after the width roll: win = below(3), off = low32(next()) & (2^win - 1); used on a load slot (the instruction's win and off fields)", + "dataset_word": "dataset[w] = item(t(w))[j(w)]: j(w) gathers the bits of w at the interleave positions, t(w) is w with those bits removed", + "program_id_suffix": "'era/' || allowed[3] || width_words || stride_mul_le32 || stride_rot_le32 || interleave[4]" + }, + "op_mix": {"load": 16, "add": 15, "shfl": 6, "mad": 4, "or": 4, "rotl": 4, "rotr": 4, "xor": 4, "mulhi": 3, "mul": 2, "sub": 2}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "day_bytes": "69676e65756d2d6461792ffa50000000000000", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0xceed56d7", + "d1": "0x9ba270d2", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0xceed56d7", "0x9ba270d2", "0x82caab2d", "0x81ebce0e", "0x12b6ecf1", "0xd0f3fd7c", "0xd872eefe", "0xc158c7bd"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [17, 12, 20, 23, 7, 3, 27, 16], "mul": ["0xf351d601", "0xa3bb398f", "0xb5a09e35", "0x7509c9c1", "0x6bbf31e9", "0xfc849a79", "0xded91851", "0x8d9113d1", "0x0ff15225", "0x3a5bdd41", "0xab533435", "0xe1c55ad5", "0xe6d3bd0d", "0x9d9ffbbd", "0xbb2a3cf3", "0x50a7c08d"], "rc": ["0xc6892460", "0x25b7228a", "0xcd515004", "0x2846527a", "0xa6324241", "0x36e3ec53", "0x82961bac", "0x0f97ba7d", "0xb6f921a9", "0x3ada24e5", "0xde20ab91", "0x5378eeb2", "0x7d161662", "0x89353cc1", "0xb1aa03a2", "0x788acae6"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 1, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xbaab6229", "imm2": "0xed861989", "rot": 26, "bit": 22, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 2, "op": "shfl", "dst": 3, "src": 6, "src2": 2, "imm": "0x5b623116", "imm2": "0xff12e5b2", "rot": 12, "bit": 24, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 3, "op": "sub", "dst": 4, "src": 1, "src2": 1, "imm": "0xe99741c7", "imm2": "0xf5fa5009", "rot": 1, "bit": 21, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 4, "op": "mad", "dst": 2, "src": 0, "src2": 4, "imm": "0x673c2157", "imm2": "0xee02465f", "rot": 20, "bit": 22, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 5, "op": "rotl", "dst": 4, "src": 7, "src2": 7, "imm": "0x946f7818", "imm2": "0x45d3399e", "rot": 9, "bit": 2, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 6, "op": "rotr", "dst": 0, "src": 2, "src2": 4, "imm": "0x5f6a0ed2", "imm2": "0x7043a636", "rot": 19, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 7, "op": "load", "dst": 6, "src": 7, "src2": 1, "imm": "0x5c61dcf7", "imm2": "0x7466aa40", "rot": 19, "bit": 9, "mask": 2, "width": 1, "win": 2, "off": 1}, + {"i": 8, "op": "load", "dst": 1, "src": 4, "src2": 5, "imm": "0x85668475", "imm2": "0xdb8cc483", "rot": 29, "bit": 7, "mask": 4, "width": 1, "win": 1, "off": 1}, + {"i": 9, "op": "load", "dst": 1, "src": 2, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 10, "op": "load", "dst": 7, "src": 0, "src2": 2, "imm": "0xe075297c", "imm2": "0x5779c44c", "rot": 10, "bit": 22, "mask": 2, "width": 1, "win": 1, "off": 1}, + {"i": 11, "op": "load", "dst": 7, "src": 1, "src2": 6, "imm": "0x65aa4311", "imm2": "0x4fe48ea9", "rot": 15, "bit": 9, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 12, "op": "mul", "dst": 5, "src": 4, "src2": 2, "imm": "0x1383d3ad", "imm2": "0xf3094b29", "rot": 8, "bit": 9, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 13, "op": "load", "dst": 7, "src": 6, "src2": 2, "imm": "0xed8a496f", "imm2": "0x3072c3c6", "rot": 28, "bit": 19, "mask": 8, "width": 1, "win": 1, "off": 1}, + {"i": 14, "op": "shfl", "dst": 6, "src": 5, "src2": 0, "imm": "0x8b965b57", "imm2": "0xcfeca6c1", "rot": 12, "bit": 27, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 15, "op": "shfl", "dst": 3, "src": 5, "src2": 3, "imm": "0x877c7586", "imm2": "0xa9cb2a03", "rot": 2, "bit": 29, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 16, "op": "or", "dst": 2, "src": 7, "src2": 3, "imm": "0xb740221a", "imm2": "0x89d38d6d", "rot": 6, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 17, "op": "rotr", "dst": 0, "src": 6, "src2": 1, "imm": "0x26f3ad8a", "imm2": "0x27256f15", "rot": 18, "bit": 5, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 18, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 19, "op": "rotl", "dst": 7, "src": 0, "src2": 5, "imm": "0x849ae6ee", "imm2": "0x02b358f9", "rot": 24, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 20, "op": "add", "dst": 6, "src": 7, "src2": 6, "imm": "0x52334d12", "imm2": "0x8c9f0ef8", "rot": 11, "bit": 23, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 21, "op": "or", "dst": 2, "src": 6, "src2": 5, "imm": "0xb1871e63", "imm2": "0xb2e40191", "rot": 5, "bit": 13, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 22, "op": "mad", "dst": 6, "src": 5, "src2": 4, "imm": "0x97df29e4", "imm2": "0xe60fea84", "rot": 11, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 23, "op": "or", "dst": 1, "src": 0, "src2": 3, "imm": "0x8f30d21d", "imm2": "0x2df685a0", "rot": 31, "bit": 0, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 24, "op": "xor", "dst": 6, "src": 1, "src2": 2, "imm": "0xd2c4025f", "imm2": "0x5269eb4d", "rot": 31, "bit": 21, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 25, "op": "add", "dst": 2, "src": 6, "src2": 5, "imm": "0x659fc3d3", "imm2": "0x9cec0e12", "rot": 6, "bit": 17, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 26, "op": "rotr", "dst": 7, "src": 0, "src2": 1, "imm": "0xd89ef484", "imm2": "0x20be3846", "rot": 12, "bit": 9, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 27, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 28, "op": "mad", "dst": 3, "src": 2, "src2": 4, "imm": "0xc5c46d76", "imm2": "0x700044b5", "rot": 22, "bit": 10, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 29, "op": "load", "dst": 1, "src": 4, "src2": 3, "imm": "0x0fbaf177", "imm2": "0xfff4f2ed", "rot": 20, "bit": 31, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 30, "op": "load", "dst": 2, "src": 3, "src2": 0, "imm": "0xe90eb2e5", "imm2": "0xb0f9eb79", "rot": 27, "bit": 14, "mask": 4, "width": 1, "win": 2, "off": 2}, + {"i": 31, "op": "load", "dst": 1, "src": 5, "src2": 2, "imm": "0x97ba3fc3", "imm2": "0x7894e657", "rot": 3, "bit": 30, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 32, "op": "add", "dst": 7, "src": 2, "src2": 7, "imm": "0x070888a8", "imm2": "0xe403240e", "rot": 2, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 33, "op": "add", "dst": 2, "src": 0, "src2": 5, "imm": "0xf2e46d55", "imm2": "0x29701828", "rot": 31, "bit": 28, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 34, "op": "add", "dst": 2, "src": 3, "src2": 0, "imm": "0x58f75b87", "imm2": "0x343b7aee", "rot": 12, "bit": 14, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 35, "op": "mulhi", "dst": 2, "src": 5, "src2": 6, "imm": "0x5892a9e6", "imm2": "0xc9824c94", "rot": 19, "bit": 26, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 36, "op": "xor", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 37, "op": "mul", "dst": 6, "src": 5, "src2": 7, "imm": "0xccf564a5", "imm2": "0x873ad101", "rot": 7, "bit": 11, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 38, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xcac8f06d", "imm2": "0x6b97c683", "rot": 18, "bit": 28, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 39, "op": "add", "dst": 7, "src": 2, "src2": 4, "imm": "0xb8180e9d", "imm2": "0x32bbd117", "rot": 23, "bit": 19, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 40, "op": "rotr", "dst": 2, "src": 3, "src2": 0, "imm": "0x2d6070bc", "imm2": "0x68ff101e", "rot": 13, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 41, "op": "sub", "dst": 7, "src": 0, "src2": 2, "imm": "0x0daf96ea", "imm2": "0x36f37be1", "rot": 5, "bit": 0, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 42, "op": "add", "dst": 4, "src": 3, "src2": 6, "imm": "0x6ced15b7", "imm2": "0x6df7aed4", "rot": 19, "bit": 4, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 43, "op": "shfl", "dst": 7, "src": 3, "src2": 5, "imm": "0x8ace05f3", "imm2": "0xd378ec12", "rot": 23, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 44, "op": "load", "dst": 0, "src": 7, "src2": 4, "imm": "0xb0607786", "imm2": "0xc4acabbc", "rot": 13, "bit": 7, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 45, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 46, "op": "load", "dst": 3, "src": 1, "src2": 0, "imm": "0x63cc1e4e", "imm2": "0xa1be8118", "rot": 12, "bit": 6, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 47, "op": "load", "dst": 6, "src": 3, "src2": 7, "imm": "0x353f1d79", "imm2": "0x3b2e7456", "rot": 18, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 48, "op": "mulhi", "dst": 4, "src": 2, "src2": 7, "imm": "0x00d8a3cd", "imm2": "0x231866d2", "rot": 21, "bit": 20, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 49, "op": "add", "dst": 5, "src": 0, "src2": 2, "imm": "0xa8bae6df", "imm2": "0xf572bdb9", "rot": 14, "bit": 7, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 50, "op": "shfl", "dst": 0, "src": 7, "src2": 7, "imm": "0x81ef22e1", "imm2": "0x74438fc5", "rot": 28, "bit": 18, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 51, "op": "add", "dst": 6, "src": 0, "src2": 6, "imm": "0x383b9260", "imm2": "0x11e17c61", "rot": 12, "bit": 19, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 52, "op": "load", "dst": 5, "src": 2, "src2": 2, "imm": "0xfb84f451", "imm2": "0x11cd863e", "rot": 21, "bit": 20, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 53, "op": "rotl", "dst": 6, "src": 5, "src2": 4, "imm": "0xb1a7db6b", "imm2": "0x76686b9b", "rot": 12, "bit": 4, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 54, "op": "rotl", "dst": 3, "src": 6, "src2": 3, "imm": "0x6f981f52", "imm2": "0xd99aeba2", "rot": 12, "bit": 27, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 55, "op": "add", "dst": 2, "src": 1, "src2": 2, "imm": "0xac6be8e3", "imm2": "0x18d67dbb", "rot": 26, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 56, "op": "load", "dst": 1, "src": 4, "src2": 0, "imm": "0x7e7f6a00", "imm2": "0x6f0747da", "rot": 25, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 57, "op": "add", "dst": 5, "src": 0, "src2": 4, "imm": "0xf03673fe", "imm2": "0xa75cd60d", "rot": 16, "bit": 12, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 58, "op": "load", "dst": 5, "src": 0, "src2": 2, "imm": "0x227f94a6", "imm2": "0x0e8344f9", "rot": 20, "bit": 10, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 59, "op": "add", "dst": 1, "src": 4, "src2": 3, "imm": "0xdecd4794", "imm2": "0x8dfb96bb", "rot": 21, "bit": 7, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 60, "op": "mulhi", "dst": 3, "src": 2, "src2": 2, "imm": "0x0dd268e0", "imm2": "0x53034ca9", "rot": 1, "bit": 8, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 61, "op": "or", "dst": 6, "src": 4, "src2": 7, "imm": "0x3a45a321", "imm2": "0x9bc59a5f", "rot": 25, "bit": 11, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 62, "op": "shfl", "dst": 5, "src": 4, "src2": 2, "imm": "0x8f229cc1", "imm2": "0xcaac64a2", "rot": 17, "bit": 13, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0xb2574178", "imm2": "0xcbcc798d", "rot": 28, "bit": 0, "mask": 16, "width": 1, "win": 1, "off": 1} + ] +} diff --git a/proto-cuda/packs-ca2-era/era-1/program.metal b/proto-cuda/packs-ca2-era/era-1/program.metal new file mode 100644 index 00000000..91da1a9c --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/program.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-1/program_bound.metal b/proto-cuda/packs-ca2-era/era-1/program_bound.metal new file mode 100644 index 00000000..b5272166 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/program_bound.metal @@ -0,0 +1,111 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0xb2a9d70du, 6u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0xb2a9d70du, 6u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0xb2a9d70du, 6u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-1/seeds.txt b/proto-cuda/packs-ca2-era/era-1/seeds.txt new file mode 100644 index 00000000..ff7b31ee --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/seeds.txt @@ -0,0 +1,5 @@ +epoch_seed_hex edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 +day_seed_hex 69676e65756d2d6461792ffa50000000000000 +epoch_index 0 +day_index 20730 +daa_score 0 diff --git a/proto-cuda/packs-ca2-era/era-1/vectors.h b/proto-cuda/packs-ca2-era/era-1/vectors.h new file mode 100644 index 00000000..8d14f358 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/vectors.h @@ -0,0 +1,57 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0xdabe038c15c539f6ull, 0xda2a12ac4857cae1ull, 0x2768199f161cd4efull, 0x28e44246d90e9cffull, 0xa6e49bc2e7b5da30ull, 0x9797d70a66935020ull, 0x4820804005effffeull, 0x88662fd15b9fe6e0ull, + 0xd7ffe7abc6bed954ull, 0x29baff8fcb44fa53ull, 0x5a090c72603a9301ull, 0x3c81aea5dac7be1eull, 0xfb1a9cc02b40b817ull, 0x3d0e8e32741a6091ull, 0xf7fd20269e5402beull, 0x89b31407de8924b6ull, + 0xaae6fe83cc322e25ull, 0x076d6b1d7de1f125ull, 0xb4aa525302008bdfull, 0x860c033cd41fcd1dull, 0x20d77bcc09e08aa7ull, 0xe75793c696a71f74ull, 0xca05dc63ec4f5b6eull, 0x0c07b473ff504716ull, + 0x8318ca28bfe8d189ull, 0xae11050d0adc5a87ull, 0x3b899c2da4ce3239ull, 0x815c563707ff1e0cull, 0x783799ce7b282d84ull, 0xc96488bfb2156fc9ull, 0x65d30aace55b4d9full, 0x0051015324bc1753ull + }, + { // base nonce 4096 + 0x09635fc8b43e490aull, 0x2cde2ee9fb2a0428ull, 0x7efadc8593c3b2ccull, 0x902068a0e0355bc3ull, 0xc3af55591a6b9a4cull, 0xb36f5ef5bef69733ull, 0x32d797b26f3ff603ull, 0x80e0374e701bb677ull, + 0x08db70d60ac78f51ull, 0x23d6ee5b06233744ull, 0x2a4de99fd6f150e5ull, 0x5727b4c1f0db5162ull, 0x191bafa761798bacull, 0x94e47312bbf20681ull, 0xee9da67774b46e6aull, 0x1465ae37760b4e08ull, + 0x2d96a0f805f370dcull, 0xc1a761ea03e0d500ull, 0x06317cfdbfcdc10eull, 0xef71c2112b4897dfull, 0x6e364b602701201dull, 0xdd0cfb3a907838ceull, 0x569e82cb5acd3801ull, 0x56723edf79db59fbull, + 0x5826c8add612ef45ull, 0xc3fdce113372a83dull, 0x632b40402f52ff52ull, 0x443994dfc952a6abull, 0x5ffda84776dcf8eeull, 0x79fe9a93648e18dfull, 0x266faea56ca3ba5dull, 0x0277609022e6adc0ull + }, + { // base nonce 1000000 + 0x689af011faee80c1ull, 0x4e62e84a6a805665ull, 0x8828e37e049d9739ull, 0xaf733761893000dbull, 0xdf9aa4524c6c9075ull, 0x34af22f30b9827c9ull, 0xe7ae4db768b3bef8ull, 0x83bb23a187bf66c9ull, + 0xad82b133ca30a3e5ull, 0x5a4c4ae80d988fd2ull, 0xbfc7229dca4c05bdull, 0xce783e7c7636ca10ull, 0xb18c78869943c79cull, 0x1023948ca66770cbull, 0x84dcd256dbf5e9dcull, 0x2e9ad8e9c0bcd6e4ull, + 0xf2d3c29d300046bdull, 0x10713b9bd25e3583ull, 0x9a1fa2385a63b827ull, 0xb31ae3aeeaa46884ull, 0xb16e8a6c82c7bd1aull, 0xfed6df91f0f3cfb4ull, 0x864d1f1b8727aed8ull, 0x89e9ac61e6362617ull, + 0x70a1df41038533bbull, 0x3d10c42046a6413bull, 0x31023c3566190ecfull, 0x2294dc505d480256ull, 0x24ff4fb403755cc7ull, 0x50ed84b9b4720072ull, 0x783690abb3addc85ull, 0x12eabf30075edb7bull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0x3dd50b1fu, 0x93123701u, 0x48edec90u, 0x3a7d2407u, 0xcdd88b45u, 0x2c37c6b0u, 0xb03a0ac0u, 0x10e2c6a5u, + 0x5119540eu, 0x3358ef2du, 0x657ac748u, 0x09e2105cu, 0x25e2929cu, 0x5fa0cdccu, 0x4317cabdu, 0x6178ee52u +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xf7b7180eu; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0xf6e00c89u, 0x370d4c24u, 0x09260b8cu, 0x16208e9du, 0x4028c489u, 0x1386ca6bu, 0x88bf905cu, 0x88f76874u, 0xac034e2du, 0x88e89b31u, 0x22deecc6u, 0xd6022e44u, 0x4b18c60eu, 0x8c761ec7u, 0x7079d93du, 0x79341b4cu, 0x5b93bcc4u, 0xc659d408u, 0xd5883003u, 0xe6b03742u, 0x016da861u, 0xbbec95bfu, 0xc1647d24u, 0x88b20f49u, 0xcbcb1898u, 0x05d9a8aeu, 0x2bfaeba0u, 0x12094029u, 0xd2a03583u, 0xf8bc77efu, 0xeb7dd210u, 0x251ecc1du, 0xa8d8a48bu, 0x26c9e599u, 0x12927937u, 0x536d595eu, 0x1f437e50u, 0x9b52077au, 0x876dce0cu, 0x65092ed9u, 0xf89bbfddu, 0xf20ee5b8u, 0xb4205b97u, 0xd79db283u, 0x65869dcfu, 0xd6c0f997u, 0xa9effec4u, 0xd192a75au, 0x73d9e395u, 0x2f043a85u, 0xdc0c3fddu, 0x60b1d49au, 0xf5da472eu, 0x83f8e3d3u, 0xdb881ba6u, 0x2f00a5dcu, 0xbe7f358eu, 0x68a25926u, 0xf5852ad5u, 0xa731ea98u, 0xbb567051u, 0xa1b64724u, 0x1d8896b6u, 0x81ef09f4u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0xebd9055cu, 0x7eaf6b21u, 0x9b610855u, 0x134a8562u, 0xb10059bau, 0x7f459e58u, 0x8f3c7873u, 0x3523e609u, + 0x75b8f4cau, 0x750d31b4u, 0x0dae0782u, 0x26f10015u, 0x7ba87a41u, 0x0efd8543u, 0x691d6368u, 0x8929d967u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x51474edfu, 0xc3cfba93u, 0xf21454cfu, 0x9b79baacu, 0x4d4fce23u, 0xd701dfd5u, 0x37357ab3u, 0x1be693fau, + 0xa701cb3bu, 0x7467c620u, 0x428184e8u, 0xf4010df0u, 0xe33aa88fu, 0xc78d6d62u, 0xae9ba9c0u, 0xf4bba97eu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x448274a57f508cbcull; diff --git a/proto-cuda/packs-ca2-era/era-1/vectors.json b/proto-cuda/packs-ca2-era/era-1/vectors.json new file mode 100644 index 00000000..97694f44 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-1/vectors.json @@ -0,0 +1,36 @@ +{ + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0xdabe038c15c539f6", "0xda2a12ac4857cae1", "0x2768199f161cd4ef", "0x28e44246d90e9cff", "0xa6e49bc2e7b5da30", "0x9797d70a66935020", "0x4820804005effffe", "0x88662fd15b9fe6e0", + "0xd7ffe7abc6bed954", "0x29baff8fcb44fa53", "0x5a090c72603a9301", "0x3c81aea5dac7be1e", "0xfb1a9cc02b40b817", "0x3d0e8e32741a6091", "0xf7fd20269e5402be", "0x89b31407de8924b6", + "0xaae6fe83cc322e25", "0x076d6b1d7de1f125", "0xb4aa525302008bdf", "0x860c033cd41fcd1d", "0x20d77bcc09e08aa7", "0xe75793c696a71f74", "0xca05dc63ec4f5b6e", "0x0c07b473ff504716", + "0x8318ca28bfe8d189", "0xae11050d0adc5a87", "0x3b899c2da4ce3239", "0x815c563707ff1e0c", "0x783799ce7b282d84", "0xc96488bfb2156fc9", "0x65d30aace55b4d9f", "0x0051015324bc1753" + ]}, + {"base_nonce": 4096, "expected": [ + "0x09635fc8b43e490a", "0x2cde2ee9fb2a0428", "0x7efadc8593c3b2cc", "0x902068a0e0355bc3", "0xc3af55591a6b9a4c", "0xb36f5ef5bef69733", "0x32d797b26f3ff603", "0x80e0374e701bb677", + "0x08db70d60ac78f51", "0x23d6ee5b06233744", "0x2a4de99fd6f150e5", "0x5727b4c1f0db5162", "0x191bafa761798bac", "0x94e47312bbf20681", "0xee9da67774b46e6a", "0x1465ae37760b4e08", + "0x2d96a0f805f370dc", "0xc1a761ea03e0d500", "0x06317cfdbfcdc10e", "0xef71c2112b4897df", "0x6e364b602701201d", "0xdd0cfb3a907838ce", "0x569e82cb5acd3801", "0x56723edf79db59fb", + "0x5826c8add612ef45", "0xc3fdce113372a83d", "0x632b40402f52ff52", "0x443994dfc952a6ab", "0x5ffda84776dcf8ee", "0x79fe9a93648e18df", "0x266faea56ca3ba5d", "0x0277609022e6adc0" + ]}, + {"base_nonce": 1000000, "expected": [ + "0x689af011faee80c1", "0x4e62e84a6a805665", "0x8828e37e049d9739", "0xaf733761893000db", "0xdf9aa4524c6c9075", "0x34af22f30b9827c9", "0xe7ae4db768b3bef8", "0x83bb23a187bf66c9", + "0xad82b133ca30a3e5", "0x5a4c4ae80d988fd2", "0xbfc7229dca4c05bd", "0xce783e7c7636ca10", "0xb18c78869943c79c", "0x1023948ca66770cb", "0x84dcd256dbf5e9dc", "0x2e9ad8e9c0bcd6e4", + "0xf2d3c29d300046bd", "0x10713b9bd25e3583", "0x9a1fa2385a63b827", "0xb31ae3aeeaa46884", "0xb16e8a6c82c7bd1a", "0xfed6df91f0f3cfb4", "0x864d1f1b8727aed8", "0x89e9ac61e6362617", + "0x70a1df41038533bb", "0x3d10c42046a6413b", "0x31023c3566190ecf", "0x2294dc505d480256", "0x24ff4fb403755cc7", "0x50ed84b9b4720072", "0x783690abb3addc85", "0x12eabf30075edb7b" + ]} + ], + "dataset_head": ["0x3dd50b1f", "0x93123701", "0x48edec90", "0x3a7d2407", "0xcdd88b45", "0x2c37c6b0", "0xb03a0ac0", "0x10e2c6a5", "0x5119540e", "0x3358ef2d", "0x657ac748", "0x09e2105c", "0x25e2929c", "0x5fa0cdcc", "0x4317cabd", "0x6178ee52"], + "dataset_last_index": 268435455, + "dataset_last": "0xf7b7180e", + "dataset_samples": [{"index": 59471966, "value": "0xf6e00c89"}, {"index": 217795994, "value": "0x370d4c24"}, {"index": 208353206, "value": "0x09260b8c"}, {"index": 42483309, "value": "0x16208e9d"}, {"index": 172547758, "value": "0x4028c489"}, {"index": 148076330, "value": "0x1386ca6b"}, {"index": 183853158, "value": "0x88bf905c"}, {"index": 214389424, "value": "0x88f76874"}, {"index": 267488061, "value": "0xac034e2d"}, {"index": 169781097, "value": "0x88e89b31"}, {"index": 184093494, "value": "0x22deecc6"}, {"index": 153880993, "value": "0xd6022e44"}, {"index": 84977930, "value": "0x4b18c60e"}, {"index": 46426879, "value": "0x8c761ec7"}, {"index": 3093825, "value": "0x7079d93d"}, {"index": 225364072, "value": "0x79341b4c"}, {"index": 44593546, "value": "0x5b93bcc4"}, {"index": 260713159, "value": "0xc659d408"}, {"index": 168250303, "value": "0xd5883003"}, {"index": 52384140, "value": "0xe6b03742"}, {"index": 223401610, "value": "0x016da861"}, {"index": 45554030, "value": "0xbbec95bf"}, {"index": 95410555, "value": "0xc1647d24"}, {"index": 175039924, "value": "0x88b20f49"}, {"index": 79171087, "value": "0xcbcb1898"}, {"index": 267580473, "value": "0x05d9a8ae"}, {"index": 24168642, "value": "0x2bfaeba0"}, {"index": 37981670, "value": "0x12094029"}, {"index": 171551130, "value": "0xd2a03583"}, {"index": 195559979, "value": "0xf8bc77ef"}, {"index": 204611762, "value": "0xeb7dd210"}, {"index": 140997658, "value": "0x251ecc1d"}, {"index": 138925853, "value": "0xa8d8a48b"}, {"index": 86637313, "value": "0x26c9e599"}, {"index": 20736778, "value": "0x12927937"}, {"index": 219665210, "value": "0x536d595e"}, {"index": 160430336, "value": "0x1f437e50"}, {"index": 264654675, "value": "0x9b52077a"}, {"index": 8013395, "value": "0x876dce0c"}, {"index": 228945585, "value": "0x65092ed9"}, {"index": 213884386, "value": "0xf89bbfdd"}, {"index": 104419827, "value": "0xf20ee5b8"}, {"index": 44185464, "value": "0xb4205b97"}, {"index": 142737231, "value": "0xd79db283"}, {"index": 99284897, "value": "0x65869dcf"}, {"index": 132475900, "value": "0xd6c0f997"}, {"index": 61861762, "value": "0xa9effec4"}, {"index": 132056166, "value": "0xd192a75a"}, {"index": 262388043, "value": "0x73d9e395"}, {"index": 91878046, "value": "0x2f043a85"}, {"index": 117353561, "value": "0xdc0c3fdd"}, {"index": 124768597, "value": "0x60b1d49a"}, {"index": 71352993, "value": "0xf5da472e"}, {"index": 190698941, "value": "0x83f8e3d3"}, {"index": 46055428, "value": "0xdb881ba6"}, {"index": 55281366, "value": "0x2f00a5dc"}, {"index": 165145231, "value": "0xbe7f358e"}, {"index": 106810753, "value": "0x68a25926"}, {"index": 171985651, "value": "0xf5852ad5"}, {"index": 232085256, "value": "0xa731ea98"}, {"index": 159510492, "value": "0xbb567051"}, {"index": 40072060, "value": "0xa1b64724"}, {"index": 209107596, "value": "0x1d8896b6"}, {"index": 39023794, "value": "0x81ef09f4"}], + "cache_head": ["0xebd9055c", "0x7eaf6b21", "0x9b610855", "0x134a8562", "0xb10059ba", "0x7f459e58", "0x8f3c7873", "0x3523e609", "0x75b8f4ca", "0x750d31b4", "0x0dae0782", "0x26f10015", "0x7ba87a41", "0x0efd8543", "0x691d6368", "0x8929d967"], + "cache_last_line": ["0x51474edf", "0xc3cfba93", "0xf21454cf", "0x9b79baac", "0x4d4fce23", "0xd701dfd5", "0x37357ab3", "0x1be693fa", "0xa701cb3b", "0x7467c620", "0x428184e8", "0xf4010df0", "0xe33aa88f", "0xc78d6d62", "0xae9ba9c0", "0xf4bba97e"], + "cache_fnv1a64": "0x448274a57f508cbc" +} diff --git a/proto-cuda/packs-ca2-era/era-2/kernel.cl b/proto-cuda/packs-ca2-era/era-2/kernel.cl new file mode 100644 index 00000000..a9c8a60a --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/kernel.cl @@ -0,0 +1,281 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 1 3 4 8 of w. +static inline uint mh_j(uint w) { return ((w >> 1u) & 1u) | (((w >> 3u) & 1u) << 1) | (((w >> 4u) & 1u) << 2) | (((w >> 8u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x000000ffu) | ((w >> 9u) << 8u); w = (w & 0x0000000fu) | ((w >> 5u) << 4u); w = (w & 0x00000007u) | ((w >> 4u) << 3u); w = (w & 0x00000001u) | ((w >> 2u) << 1u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 1u) << 2u) | (w & 0x00000001u) | (((j >> 0u) & 1u) << 1u); w = ((w >> 3u) << 4u) | (w & 0x00000007u) | (((j >> 1u) & 1u) << 3u); w = ((w >> 4u) << 5u) | (w & 0x0000000fu) | (((j >> 2u) & 1u) << 4u); w = ((w >> 8u) << 9u) | (w & 0x000000ffu) | (((j >> 3u) & 1u) << 8u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-era/era-2/kernel.cu b/proto-cuda/packs-ca2-era/era-2/kernel.cu new file mode 100644 index 00000000..fc256e4e --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/kernel.cu @@ -0,0 +1,163 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + for (uint32_t i = 0u; i < 16u; ++i) ds[(size_t)mh_addr(t, i)] = s[i]; + } +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-era/era-2/kernel_bound.cl b/proto-cuda/packs-ca2-era/era-2/kernel_bound.cl new file mode 100644 index 00000000..16920dd4 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/kernel_bound.cl @@ -0,0 +1,375 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 1 3 4 8 of w. +static inline uint mh_j(uint w) { return ((w >> 1u) & 1u) | (((w >> 3u) & 1u) << 1) | (((w >> 4u) & 1u) << 2) | (((w >> 8u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x000000ffu) | ((w >> 9u) << 8u); w = (w & 0x0000000fu) | ((w >> 5u) << 4u); w = (w & 0x00000007u) | ((w >> 4u) << 3u); w = (w & 0x00000001u) | ((w >> 2u) << 1u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 1u) << 2u) | (w & 0x00000001u) | (((j >> 0u) & 1u) << 1u); w = ((w >> 3u) << 4u) | (w & 0x00000007u) | (((j >> 1u) & 1u) << 3u); w = ((w >> 4u) << 5u) | (w & 0x0000000fu) | (((j >> 2u) & 1u) << 4u); w = ((w >> 8u) << 9u) | (w & 0x000000ffu) | (((j >> 3u) & 1u) << 8u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-2/kernel_bound.cu b/proto-cuda/packs-ca2-era/era-2/kernel_bound.cu new file mode 100644 index 00000000..15a6a258 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/kernel_bound.cu @@ -0,0 +1,123 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-era/era-2/memhard.h b/proto-cuda/packs-ca2-era/era-2/memhard.h new file mode 100644 index 00000000..d684ab3c --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/memhard.h @@ -0,0 +1,112 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 1 3 4 8 of w. +IGNEUM_HD uint32_t mh_j(uint32_t w) { return ((w >> 1u) & 1u) | (((w >> 3u) & 1u) << 1) | (((w >> 4u) & 1u) << 2) | (((w >> 8u) & 1u) << 3); } +IGNEUM_HD uint32_t mh_t(uint32_t w) { w = (w & 0x000000ffu) | ((w >> 9u) << 8u); w = (w & 0x0000000fu) | ((w >> 5u) << 4u); w = (w & 0x00000007u) | ((w >> 4u) << 3u); w = (w & 0x00000001u) | ((w >> 2u) << 1u); return w; } +IGNEUM_HD uint32_t mh_addr(uint32_t t, uint32_t j) { uint32_t w = t; w = ((w >> 1u) << 2u) | (w & 0x00000001u) | (((j >> 0u) & 1u) << 1u); w = ((w >> 3u) << 4u) | (w & 0x00000007u) | (((j >> 1u) & 1u) << 3u); w = ((w >> 4u) << 5u) | (w & 0x0000000fu) | (((j >> 2u) & 1u) << 4u); w = ((w >> 8u) << 9u) | (w & 0x000000ffu) | (((j >> 3u) & 1u) << 8u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } diff --git a/proto-cuda/packs-ca2-era/era-2/memhard.metal b/proto-cuda/packs-ca2-era/era-2/memhard.metal new file mode 100644 index 00000000..ba332bd4 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/memhard.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 1 3 4 8 of w. +inline uint mh_j(uint w) { return ((w >> 1u) & 1u) | (((w >> 3u) & 1u) << 1) | (((w >> 4u) & 1u) << 2) | (((w >> 8u) & 1u) << 3); } +inline uint mh_t(uint w) { w = (w & 0x000000ffu) | ((w >> 9u) << 8u); w = (w & 0x0000000fu) | ((w >> 5u) << 4u); w = (w & 0x00000007u) | ((w >> 4u) << 3u); w = (w & 0x00000001u) | ((w >> 2u) << 1u); return w; } +inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 1u) << 2u) | (w & 0x00000001u) | (((j >> 0u) & 1u) << 1u); w = ((w >> 3u) << 4u) | (w & 0x00000007u) | (((j >> 1u) & 1u) << 3u); w = ((w >> 4u) << 5u) | (w & 0x0000000fu) | (((j >> 2u) & 1u) << 4u); w = ((w >> 8u) << 9u) | (w & 0x000000ffu) | (((j >> 3u) & 1u) << 8u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + for (uint i = 0u; i < 16u; ++i) dataset[mh_addr(gid, i)] = s[i]; +} diff --git a/proto-cuda/packs-ca2-era/era-2/program.h b/proto-cuda/packs-ca2-era/era-2/program.h new file mode 100644 index 00000000..8d33908e --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/program.h @@ -0,0 +1,76 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_SEED_BYTES_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" +#define IGNEUM_GENERATOR 3 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x73bcbfe8ccf988f1ull +#define IGNEUM_DAY_STRING "bytes:69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY_BYTES_HEX "69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY0 0xceed56d7u +#define IGNEUM_DAY1 0x9ba270d2u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 add=15 shfl=6 mad=4 or=4 rotl=4 rotr=4 xor=4 mulhi=3 mul=2 sub=2" +// Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that +// runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). +#define IGNEUM_PROGRAM_CLASS "v3" +#define IGNEUM_ERA_SEED_HEX "df57136f2ad5f410e6145023090eea148c1342ccef5a5f541afa590be7e44745" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "w4-era843155d7" +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Era layout (5 October 2026, docs/plans/era-layout.md): NOT the lottery hash. Every dataset load reads +// idx = ((rotl(src * STRIDE_MUL, STRIDE_ROT) & window mask) | window offset) & MASK; the window of a load site is the +// dataset, a half or a quarter of it (IGNEUM_ERA_WINDOWS: site:shrink:offset); dataset word w holds word j(w) of item +// t(w) with j's bits at the INTERLEAVE positions (memhard.h: mh_t, mh_j, mh_addr). +#define IGNEUM_ERA_LABEL "843155d7" +#define IGNEUM_ERA_SEED_WORDS { 0x843155d7u, 0x8fb2bbb8u, 0x3af89788u, 0xc80fe6f2u, 0xb265c95eu, 0x001f1648u, 0x236e8759u, 0x6cada520u } +#define IGNEUM_ERA_ALLOWED_WIDTHS { 1, 0, 0 } // words, ascending, 0 = unused; one entry pins the width +#define IGNEUM_ERA_WIDTH_WORDS 1 +#define IGNEUM_ERA_STRIDE_MUL 0x2b4a5b97u +#define IGNEUM_ERA_STRIDE_ROT 28 +#define IGNEUM_ERA_INTERLEAVE { 1, 3, 4, 8 } +#define IGNEUM_ERA_WINDOWS "7:2:1 8:1:1 9:1:1 10:1:1 11:0:0 13:1:1 29:0:0 30:2:2 31:1:1 44:1:1 46:2:0 47:0:0 52:0:0 56:0:0 58:2:0 63:1:1" +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u } +#define IGNEUM_KEY_INIT { 0xceed56d7u, 0x9ba270d2u, 0x82caab2du, 0x81ebce0eu, 0x12b6ecf1u, 0xd0f3fd7cu, 0xd872eefeu, 0xc158c7bdu } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 17u, 12u, 20u, 23u, 7u, 3u, 27u, 16u } +#define IGNEUM_MIX_MUL_INIT { 0xf351d601u, 0xa3bb398fu, 0xb5a09e35u, 0x7509c9c1u, 0x6bbf31e9u, 0xfc849a79u, 0xded91851u, 0x8d9113d1u, 0x0ff15225u, 0x3a5bdd41u, 0xab533435u, 0xe1c55ad5u, 0xe6d3bd0du, 0x9d9ffbbdu, 0xbb2a3cf3u, 0x50a7c08du } +#define IGNEUM_MIX_RC_INIT { 0xc6892460u, 0x25b7228au, 0xcd515004u, 0x2846527au, 0xa6324241u, 0x36e3ec53u, 0x82961bacu, 0x0f97ba7du, 0xb6f921a9u, 0x3ada24e5u, 0xde20ab91u, 0x5378eeb2u, 0x7d161662u, 0x89353cc1u, 0xb1aa03a2u, 0x788acae6u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-era/era-2/program.json b/proto-cuda/packs-ca2-era/era-2/program.json new file mode 100644 index 00000000..69a544ce --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/program.json @@ -0,0 +1,143 @@ +{ + "format": "igneum-program-pack-3", + "generator": 3, + "attempt": 0, + "program_id": "0x73bcbfe8ccf988f1", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07", + "seed_words": ["0x667d0fbd", "0x7b8e5963", "0x31c67e5e", "0x4529ddc6", "0xef19d6d8", "0xaccf6211", "0xda0aed32", "0xabc6df31"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "program_class": "v3", + "era_seed_bytes": "df57136f2ad5f410e6145023090eea148c1342ccef5a5f541afa590be7e44745", + "load_class": "w4-era843155d7", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "era": { + "label": "843155d7", + "seed_words": ["0x843155d7", "0x8fb2bbb8", "0x3af89788", "0xc80fe6f2", "0xb265c95e", "0x001f1648", "0x236e8759", "0x6cada520"], + "draw": "docs/plans/era-layout.md 1.1: SplitMix64 seeded with seed_words[0] | seed_words[1] << 32 of seed_words_from_bytes('igneum-era/' || n_le64 || E_n); width = allowed[below(|allowed|)], stride_mul = low32(next()) | 1, stride_rot = 1 + below(31), then four next() draws for a partial Fisher-Yates over positions log2(W)..15 of which 4 - log2(W) are used", + "allowed_widths": [1], + "width_words": 1, + "stride_mul": "0x2b4a5b97", + "stride_rot": 28, + "interleave": [1, 3, 4, 8], + "address": "y = rotl(src * stride_mul, stride_rot); k = min(win, D - 26); idx = ((y & (mask >> k)) | ((off & (2^k - 1)) << (D - k))) & mask; a wide load aligns idx down to W words", + "windows": "per instruction, after the width roll: win = below(3), off = low32(next()) & (2^win - 1); used on a load slot (the instruction's win and off fields)", + "dataset_word": "dataset[w] = item(t(w))[j(w)]: j(w) gathers the bits of w at the interleave positions, t(w) is w with those bits removed", + "program_id_suffix": "'era/' || allowed[3] || width_words || stride_mul_le32 || stride_rot_le32 || interleave[4]" + }, + "op_mix": {"load": 16, "add": 15, "shfl": 6, "mad": 4, "or": 4, "rotl": 4, "rotr": 4, "xor": 4, "mulhi": 3, "mul": 2, "sub": 2}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "day_bytes": "69676e65756d2d6461792ffa50000000000000", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0xceed56d7", + "d1": "0x9ba270d2", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0xceed56d7", "0x9ba270d2", "0x82caab2d", "0x81ebce0e", "0x12b6ecf1", "0xd0f3fd7c", "0xd872eefe", "0xc158c7bd"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [17, 12, 20, 23, 7, 3, 27, 16], "mul": ["0xf351d601", "0xa3bb398f", "0xb5a09e35", "0x7509c9c1", "0x6bbf31e9", "0xfc849a79", "0xded91851", "0x8d9113d1", "0x0ff15225", "0x3a5bdd41", "0xab533435", "0xe1c55ad5", "0xe6d3bd0d", "0x9d9ffbbd", "0xbb2a3cf3", "0x50a7c08d"], "rc": ["0xc6892460", "0x25b7228a", "0xcd515004", "0x2846527a", "0xa6324241", "0x36e3ec53", "0x82961bac", "0x0f97ba7d", "0xb6f921a9", "0x3ada24e5", "0xde20ab91", "0x5378eeb2", "0x7d161662", "0x89353cc1", "0xb1aa03a2", "0x788acae6"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 1, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xbaab6229", "imm2": "0xed861989", "rot": 26, "bit": 22, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 2, "op": "shfl", "dst": 3, "src": 6, "src2": 2, "imm": "0x5b623116", "imm2": "0xff12e5b2", "rot": 12, "bit": 24, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 3, "op": "sub", "dst": 4, "src": 1, "src2": 1, "imm": "0xe99741c7", "imm2": "0xf5fa5009", "rot": 1, "bit": 21, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 4, "op": "mad", "dst": 2, "src": 0, "src2": 4, "imm": "0x673c2157", "imm2": "0xee02465f", "rot": 20, "bit": 22, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 5, "op": "rotl", "dst": 4, "src": 7, "src2": 7, "imm": "0x946f7818", "imm2": "0x45d3399e", "rot": 9, "bit": 2, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 6, "op": "rotr", "dst": 0, "src": 2, "src2": 4, "imm": "0x5f6a0ed2", "imm2": "0x7043a636", "rot": 19, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 7, "op": "load", "dst": 6, "src": 7, "src2": 1, "imm": "0x5c61dcf7", "imm2": "0x7466aa40", "rot": 19, "bit": 9, "mask": 2, "width": 1, "win": 2, "off": 1}, + {"i": 8, "op": "load", "dst": 1, "src": 4, "src2": 5, "imm": "0x85668475", "imm2": "0xdb8cc483", "rot": 29, "bit": 7, "mask": 4, "width": 1, "win": 1, "off": 1}, + {"i": 9, "op": "load", "dst": 1, "src": 2, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 10, "op": "load", "dst": 7, "src": 0, "src2": 2, "imm": "0xe075297c", "imm2": "0x5779c44c", "rot": 10, "bit": 22, "mask": 2, "width": 1, "win": 1, "off": 1}, + {"i": 11, "op": "load", "dst": 7, "src": 1, "src2": 6, "imm": "0x65aa4311", "imm2": "0x4fe48ea9", "rot": 15, "bit": 9, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 12, "op": "mul", "dst": 5, "src": 4, "src2": 2, "imm": "0x1383d3ad", "imm2": "0xf3094b29", "rot": 8, "bit": 9, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 13, "op": "load", "dst": 7, "src": 6, "src2": 2, "imm": "0xed8a496f", "imm2": "0x3072c3c6", "rot": 28, "bit": 19, "mask": 8, "width": 1, "win": 1, "off": 1}, + {"i": 14, "op": "shfl", "dst": 6, "src": 5, "src2": 0, "imm": "0x8b965b57", "imm2": "0xcfeca6c1", "rot": 12, "bit": 27, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 15, "op": "shfl", "dst": 3, "src": 5, "src2": 3, "imm": "0x877c7586", "imm2": "0xa9cb2a03", "rot": 2, "bit": 29, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 16, "op": "or", "dst": 2, "src": 7, "src2": 3, "imm": "0xb740221a", "imm2": "0x89d38d6d", "rot": 6, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 17, "op": "rotr", "dst": 0, "src": 6, "src2": 1, "imm": "0x26f3ad8a", "imm2": "0x27256f15", "rot": 18, "bit": 5, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 18, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 19, "op": "rotl", "dst": 7, "src": 0, "src2": 5, "imm": "0x849ae6ee", "imm2": "0x02b358f9", "rot": 24, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 20, "op": "add", "dst": 6, "src": 7, "src2": 6, "imm": "0x52334d12", "imm2": "0x8c9f0ef8", "rot": 11, "bit": 23, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 21, "op": "or", "dst": 2, "src": 6, "src2": 5, "imm": "0xb1871e63", "imm2": "0xb2e40191", "rot": 5, "bit": 13, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 22, "op": "mad", "dst": 6, "src": 5, "src2": 4, "imm": "0x97df29e4", "imm2": "0xe60fea84", "rot": 11, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 23, "op": "or", "dst": 1, "src": 0, "src2": 3, "imm": "0x8f30d21d", "imm2": "0x2df685a0", "rot": 31, "bit": 0, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 24, "op": "xor", "dst": 6, "src": 1, "src2": 2, "imm": "0xd2c4025f", "imm2": "0x5269eb4d", "rot": 31, "bit": 21, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 25, "op": "add", "dst": 2, "src": 6, "src2": 5, "imm": "0x659fc3d3", "imm2": "0x9cec0e12", "rot": 6, "bit": 17, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 26, "op": "rotr", "dst": 7, "src": 0, "src2": 1, "imm": "0xd89ef484", "imm2": "0x20be3846", "rot": 12, "bit": 9, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 27, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 28, "op": "mad", "dst": 3, "src": 2, "src2": 4, "imm": "0xc5c46d76", "imm2": "0x700044b5", "rot": 22, "bit": 10, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 29, "op": "load", "dst": 1, "src": 4, "src2": 3, "imm": "0x0fbaf177", "imm2": "0xfff4f2ed", "rot": 20, "bit": 31, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 30, "op": "load", "dst": 2, "src": 3, "src2": 0, "imm": "0xe90eb2e5", "imm2": "0xb0f9eb79", "rot": 27, "bit": 14, "mask": 4, "width": 1, "win": 2, "off": 2}, + {"i": 31, "op": "load", "dst": 1, "src": 5, "src2": 2, "imm": "0x97ba3fc3", "imm2": "0x7894e657", "rot": 3, "bit": 30, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 32, "op": "add", "dst": 7, "src": 2, "src2": 7, "imm": "0x070888a8", "imm2": "0xe403240e", "rot": 2, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 33, "op": "add", "dst": 2, "src": 0, "src2": 5, "imm": "0xf2e46d55", "imm2": "0x29701828", "rot": 31, "bit": 28, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 34, "op": "add", "dst": 2, "src": 3, "src2": 0, "imm": "0x58f75b87", "imm2": "0x343b7aee", "rot": 12, "bit": 14, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 35, "op": "mulhi", "dst": 2, "src": 5, "src2": 6, "imm": "0x5892a9e6", "imm2": "0xc9824c94", "rot": 19, "bit": 26, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 36, "op": "xor", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 37, "op": "mul", "dst": 6, "src": 5, "src2": 7, "imm": "0xccf564a5", "imm2": "0x873ad101", "rot": 7, "bit": 11, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 38, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xcac8f06d", "imm2": "0x6b97c683", "rot": 18, "bit": 28, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 39, "op": "add", "dst": 7, "src": 2, "src2": 4, "imm": "0xb8180e9d", "imm2": "0x32bbd117", "rot": 23, "bit": 19, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 40, "op": "rotr", "dst": 2, "src": 3, "src2": 0, "imm": "0x2d6070bc", "imm2": "0x68ff101e", "rot": 13, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 41, "op": "sub", "dst": 7, "src": 0, "src2": 2, "imm": "0x0daf96ea", "imm2": "0x36f37be1", "rot": 5, "bit": 0, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 42, "op": "add", "dst": 4, "src": 3, "src2": 6, "imm": "0x6ced15b7", "imm2": "0x6df7aed4", "rot": 19, "bit": 4, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 43, "op": "shfl", "dst": 7, "src": 3, "src2": 5, "imm": "0x8ace05f3", "imm2": "0xd378ec12", "rot": 23, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 44, "op": "load", "dst": 0, "src": 7, "src2": 4, "imm": "0xb0607786", "imm2": "0xc4acabbc", "rot": 13, "bit": 7, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 45, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 46, "op": "load", "dst": 3, "src": 1, "src2": 0, "imm": "0x63cc1e4e", "imm2": "0xa1be8118", "rot": 12, "bit": 6, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 47, "op": "load", "dst": 6, "src": 3, "src2": 7, "imm": "0x353f1d79", "imm2": "0x3b2e7456", "rot": 18, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 48, "op": "mulhi", "dst": 4, "src": 2, "src2": 7, "imm": "0x00d8a3cd", "imm2": "0x231866d2", "rot": 21, "bit": 20, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 49, "op": "add", "dst": 5, "src": 0, "src2": 2, "imm": "0xa8bae6df", "imm2": "0xf572bdb9", "rot": 14, "bit": 7, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 50, "op": "shfl", "dst": 0, "src": 7, "src2": 7, "imm": "0x81ef22e1", "imm2": "0x74438fc5", "rot": 28, "bit": 18, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 51, "op": "add", "dst": 6, "src": 0, "src2": 6, "imm": "0x383b9260", "imm2": "0x11e17c61", "rot": 12, "bit": 19, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 52, "op": "load", "dst": 5, "src": 2, "src2": 2, "imm": "0xfb84f451", "imm2": "0x11cd863e", "rot": 21, "bit": 20, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 53, "op": "rotl", "dst": 6, "src": 5, "src2": 4, "imm": "0xb1a7db6b", "imm2": "0x76686b9b", "rot": 12, "bit": 4, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 54, "op": "rotl", "dst": 3, "src": 6, "src2": 3, "imm": "0x6f981f52", "imm2": "0xd99aeba2", "rot": 12, "bit": 27, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 55, "op": "add", "dst": 2, "src": 1, "src2": 2, "imm": "0xac6be8e3", "imm2": "0x18d67dbb", "rot": 26, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 56, "op": "load", "dst": 1, "src": 4, "src2": 0, "imm": "0x7e7f6a00", "imm2": "0x6f0747da", "rot": 25, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 57, "op": "add", "dst": 5, "src": 0, "src2": 4, "imm": "0xf03673fe", "imm2": "0xa75cd60d", "rot": 16, "bit": 12, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 58, "op": "load", "dst": 5, "src": 0, "src2": 2, "imm": "0x227f94a6", "imm2": "0x0e8344f9", "rot": 20, "bit": 10, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 59, "op": "add", "dst": 1, "src": 4, "src2": 3, "imm": "0xdecd4794", "imm2": "0x8dfb96bb", "rot": 21, "bit": 7, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 60, "op": "mulhi", "dst": 3, "src": 2, "src2": 2, "imm": "0x0dd268e0", "imm2": "0x53034ca9", "rot": 1, "bit": 8, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 61, "op": "or", "dst": 6, "src": 4, "src2": 7, "imm": "0x3a45a321", "imm2": "0x9bc59a5f", "rot": 25, "bit": 11, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 62, "op": "shfl", "dst": 5, "src": 4, "src2": 2, "imm": "0x8f229cc1", "imm2": "0xcaac64a2", "rot": 17, "bit": 13, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0xb2574178", "imm2": "0xcbcc798d", "rot": 28, "bit": 0, "mask": 16, "width": 1, "win": 1, "off": 1} + ] +} diff --git a/proto-cuda/packs-ca2-era/era-2/program.metal b/proto-cuda/packs-ca2-era/era-2/program.metal new file mode 100644 index 00000000..88ecd097 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/program.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-2/program_bound.metal b/proto-cuda/packs-ca2-era/era-2/program_bound.metal new file mode 100644 index 00000000..3a25bc69 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/program_bound.metal @@ -0,0 +1,111 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x2b4a5b97u, 28u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x2b4a5b97u, 28u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x2b4a5b97u, 28u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-2/seeds.txt b/proto-cuda/packs-ca2-era/era-2/seeds.txt new file mode 100644 index 00000000..ff7b31ee --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/seeds.txt @@ -0,0 +1,5 @@ +epoch_seed_hex edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 +day_seed_hex 69676e65756d2d6461792ffa50000000000000 +epoch_index 0 +day_index 20730 +daa_score 0 diff --git a/proto-cuda/packs-ca2-era/era-2/vectors.h b/proto-cuda/packs-ca2-era/era-2/vectors.h new file mode 100644 index 00000000..5392d6f9 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/vectors.h @@ -0,0 +1,57 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0x2d96e3f8e9ad42e2ull, 0x0d36523b17ba1058ull, 0x899e2a6df6991c3dull, 0xdb382aa991ff18d6ull, 0xc896fe7b624c7722ull, 0x932cfc8892d24216ull, 0xd9ef4047f797129bull, 0x63fb054ee8cfe1d2ull, + 0xbcbd287cfd7e12a9ull, 0x68e41482f26203c2ull, 0x5c368b15e9ec2bf5ull, 0x83484f0946bfda39ull, 0xc82c1aeadba75e3cull, 0x023cbca13fae681dull, 0x000fcab814a56dd9ull, 0x432082a86895e2a7ull, + 0x635fb930f3cf454bull, 0x1104c1b855a17655ull, 0x2fd53820b88956b2ull, 0xc415d67f6420191dull, 0xeaf6853e1c859f5dull, 0x95dfdf66b21e0518ull, 0x7d67b2b734243bfeull, 0xb142ae8e4a4b5ca2ull, + 0x71ec0aff12d4c515ull, 0x0b2207c2c51fc2faull, 0xbc3957dbbf51d260ull, 0x74ea737eebe8a3e4ull, 0xd49fd6ea590200e9ull, 0xd5096fd4e83b8fa1ull, 0x999b099025a53907ull, 0xa5692c50b79eac9dull + }, + { // base nonce 4096 + 0x9d4b6aaa8c292525ull, 0xb2ad4ab3941b3a3full, 0x17988da38edbf912ull, 0x566b31d7cceae84aull, 0x132f77e29f4e6d40ull, 0xbdc098ecac64b3dcull, 0x1670a7bfbc675d88ull, 0xda3a9960471e44bbull, + 0x887e9fda5ec82271ull, 0x6137f598a67fc0d4ull, 0xd77b98d7d2025d26ull, 0x96b472c7560a777dull, 0x0c98d0ff42f589f0ull, 0x4f0f15c9e4eefef2ull, 0x6bae44572933ab20ull, 0x2772c0266bdd5308ull, + 0x2fdb9e92d13d07baull, 0x6fe9239589ed122aull, 0x7e3955e5a91916fdull, 0x966d21685073c9b8ull, 0xa050194eb84c4104ull, 0xf7e095dae1d70633ull, 0x4f49616da323736bull, 0x1fcc532045f01cd4ull, + 0x69549d64913199fcull, 0x6684a1b257c011b0ull, 0xff318d60e8970b9aull, 0x1429c63759eb426dull, 0x3f470be3f4d4815aull, 0x6e1d6c6b91b984e2ull, 0xda2dd7f31fea85efull, 0xb3944abe38dae250ull + }, + { // base nonce 1000000 + 0xd549a905f9121657ull, 0xd863810b66483e86ull, 0x65d735cf2e5447afull, 0x9cda91c71e0f5790ull, 0x682c4a1be66d2444ull, 0xcb64852ca144ffe1ull, 0x8e72112d5db27544ull, 0x1a9e6bb74ea3e563ull, + 0x63c279f228faf1bcull, 0xf7b77f8435da26a2ull, 0x3c28bd5b463df46full, 0xd2cdd0e9e968d2bdull, 0x46eae240fc0c3f01ull, 0xbdd4c4e525220d3cull, 0x345178054b1eac9dull, 0x312f541e08ab8fe9ull, + 0x0cbc6f22e65887adull, 0xf9a0d018f9ce95cfull, 0x994d29e263550764ull, 0x27a37ad3487c4018ull, 0xcdb7645aa05dac6aull, 0x8578f1e945156d7full, 0x8e135ab725d23599ull, 0xfa902aebc88a620cull, + 0x5bdb23834f693ba8ull, 0xae53782ddf331358ull, 0x2304fcfaad2616fcull, 0x21e9216f6c0b14a6ull, 0xa88e90dc82b85f66ull, 0xba5aa73b369dd894ull, 0x97e37652c487cc5eull, 0x36cb34ed0c8ce81full + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0x3dd50b1fu, 0x93123701u, 0x48edec90u, 0x3a7d2407u, 0xcdd88b45u, 0x2c37c6b0u, 0xb03a0ac0u, 0x10e2c6a5u, + 0x5119540eu, 0x3358ef2du, 0x657ac748u, 0x09e2105cu, 0x25e2929cu, 0x5fa0cdccu, 0x4317cabdu, 0x6178ee52u +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xf7b7180eu; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0x88717593u, 0x79f4c2ebu, 0xa55c10fau, 0x1e48deedu, 0x825505dfu, 0x056e4b2au, 0x51c5cdc5u, 0x9133ff90u, 0xf3e1f630u, 0x8c0f4bcau, 0x13c805d6u, 0x7cd9cc39u, 0x54d3ad95u, 0x3142a64bu, 0xf690ebf9u, 0x5f05ecf5u, 0x708fd94du, 0x57e3a213u, 0xb60de9a6u, 0xc3cacc1fu, 0x72a0a173u, 0x2a13f6c7u, 0xfb5b4620u, 0xeae5f6c3u, 0x38e0ceb1u, 0x103d3fa4u, 0xcb03f2aeu, 0x562969ccu, 0xf1349e23u, 0x3fa57b5du, 0x4fb463b9u, 0xb2b70250u, 0x7f689f8cu, 0x27e5f93eu, 0xbaeafce2u, 0xaf3d3776u, 0x084c3724u, 0xffe6a16cu, 0xd4d0d18du, 0x5d78da4eu, 0xf0bd37c6u, 0x261f1c0du, 0x7bfc0ff6u, 0x08e5373bu, 0xd23a038cu, 0x124d74c0u, 0xdd74caceu, 0x5ab7c9bau, 0xc05d2942u, 0x2d1e2f73u, 0x52a27429u, 0x0c828d10u, 0x7514e589u, 0xdb8ca7d2u, 0xdb881ba6u, 0x0ce95e1du, 0x3c93a15eu, 0x73ec90f8u, 0xe47554edu, 0x496959f3u, 0xebf27832u, 0x11f5885eu, 0x69a1ff20u, 0x804542bdu +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0xebd9055cu, 0x7eaf6b21u, 0x9b610855u, 0x134a8562u, 0xb10059bau, 0x7f459e58u, 0x8f3c7873u, 0x3523e609u, + 0x75b8f4cau, 0x750d31b4u, 0x0dae0782u, 0x26f10015u, 0x7ba87a41u, 0x0efd8543u, 0x691d6368u, 0x8929d967u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x51474edfu, 0xc3cfba93u, 0xf21454cfu, 0x9b79baacu, 0x4d4fce23u, 0xd701dfd5u, 0x37357ab3u, 0x1be693fau, + 0xa701cb3bu, 0x7467c620u, 0x428184e8u, 0xf4010df0u, 0xe33aa88fu, 0xc78d6d62u, 0xae9ba9c0u, 0xf4bba97eu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x448274a57f508cbcull; diff --git a/proto-cuda/packs-ca2-era/era-2/vectors.json b/proto-cuda/packs-ca2-era/era-2/vectors.json new file mode 100644 index 00000000..4305145f --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-2/vectors.json @@ -0,0 +1,36 @@ +{ + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0x2d96e3f8e9ad42e2", "0x0d36523b17ba1058", "0x899e2a6df6991c3d", "0xdb382aa991ff18d6", "0xc896fe7b624c7722", "0x932cfc8892d24216", "0xd9ef4047f797129b", "0x63fb054ee8cfe1d2", + "0xbcbd287cfd7e12a9", "0x68e41482f26203c2", "0x5c368b15e9ec2bf5", "0x83484f0946bfda39", "0xc82c1aeadba75e3c", "0x023cbca13fae681d", "0x000fcab814a56dd9", "0x432082a86895e2a7", + "0x635fb930f3cf454b", "0x1104c1b855a17655", "0x2fd53820b88956b2", "0xc415d67f6420191d", "0xeaf6853e1c859f5d", "0x95dfdf66b21e0518", "0x7d67b2b734243bfe", "0xb142ae8e4a4b5ca2", + "0x71ec0aff12d4c515", "0x0b2207c2c51fc2fa", "0xbc3957dbbf51d260", "0x74ea737eebe8a3e4", "0xd49fd6ea590200e9", "0xd5096fd4e83b8fa1", "0x999b099025a53907", "0xa5692c50b79eac9d" + ]}, + {"base_nonce": 4096, "expected": [ + "0x9d4b6aaa8c292525", "0xb2ad4ab3941b3a3f", "0x17988da38edbf912", "0x566b31d7cceae84a", "0x132f77e29f4e6d40", "0xbdc098ecac64b3dc", "0x1670a7bfbc675d88", "0xda3a9960471e44bb", + "0x887e9fda5ec82271", "0x6137f598a67fc0d4", "0xd77b98d7d2025d26", "0x96b472c7560a777d", "0x0c98d0ff42f589f0", "0x4f0f15c9e4eefef2", "0x6bae44572933ab20", "0x2772c0266bdd5308", + "0x2fdb9e92d13d07ba", "0x6fe9239589ed122a", "0x7e3955e5a91916fd", "0x966d21685073c9b8", "0xa050194eb84c4104", "0xf7e095dae1d70633", "0x4f49616da323736b", "0x1fcc532045f01cd4", + "0x69549d64913199fc", "0x6684a1b257c011b0", "0xff318d60e8970b9a", "0x1429c63759eb426d", "0x3f470be3f4d4815a", "0x6e1d6c6b91b984e2", "0xda2dd7f31fea85ef", "0xb3944abe38dae250" + ]}, + {"base_nonce": 1000000, "expected": [ + "0xd549a905f9121657", "0xd863810b66483e86", "0x65d735cf2e5447af", "0x9cda91c71e0f5790", "0x682c4a1be66d2444", "0xcb64852ca144ffe1", "0x8e72112d5db27544", "0x1a9e6bb74ea3e563", + "0x63c279f228faf1bc", "0xf7b77f8435da26a2", "0x3c28bd5b463df46f", "0xd2cdd0e9e968d2bd", "0x46eae240fc0c3f01", "0xbdd4c4e525220d3c", "0x345178054b1eac9d", "0x312f541e08ab8fe9", + "0x0cbc6f22e65887ad", "0xf9a0d018f9ce95cf", "0x994d29e263550764", "0x27a37ad3487c4018", "0xcdb7645aa05dac6a", "0x8578f1e945156d7f", "0x8e135ab725d23599", "0xfa902aebc88a620c", + "0x5bdb23834f693ba8", "0xae53782ddf331358", "0x2304fcfaad2616fc", "0x21e9216f6c0b14a6", "0xa88e90dc82b85f66", "0xba5aa73b369dd894", "0x97e37652c487cc5e", "0x36cb34ed0c8ce81f" + ]} + ], + "dataset_head": ["0x3dd50b1f", "0x93123701", "0x48edec90", "0x3a7d2407", "0xcdd88b45", "0x2c37c6b0", "0xb03a0ac0", "0x10e2c6a5", "0x5119540e", "0x3358ef2d", "0x657ac748", "0x09e2105c", "0x25e2929c", "0x5fa0cdcc", "0x4317cabd", "0x6178ee52"], + "dataset_last_index": 268435455, + "dataset_last": "0xf7b7180e", + "dataset_samples": [{"index": 59471966, "value": "0x88717593"}, {"index": 217795994, "value": "0x79f4c2eb"}, {"index": 208353206, "value": "0xa55c10fa"}, {"index": 42483309, "value": "0x1e48deed"}, {"index": 172547758, "value": "0x825505df"}, {"index": 148076330, "value": "0x056e4b2a"}, {"index": 183853158, "value": "0x51c5cdc5"}, {"index": 214389424, "value": "0x9133ff90"}, {"index": 267488061, "value": "0xf3e1f630"}, {"index": 169781097, "value": "0x8c0f4bca"}, {"index": 184093494, "value": "0x13c805d6"}, {"index": 153880993, "value": "0x7cd9cc39"}, {"index": 84977930, "value": "0x54d3ad95"}, {"index": 46426879, "value": "0x3142a64b"}, {"index": 3093825, "value": "0xf690ebf9"}, {"index": 225364072, "value": "0x5f05ecf5"}, {"index": 44593546, "value": "0x708fd94d"}, {"index": 260713159, "value": "0x57e3a213"}, {"index": 168250303, "value": "0xb60de9a6"}, {"index": 52384140, "value": "0xc3cacc1f"}, {"index": 223401610, "value": "0x72a0a173"}, {"index": 45554030, "value": "0x2a13f6c7"}, {"index": 95410555, "value": "0xfb5b4620"}, {"index": 175039924, "value": "0xeae5f6c3"}, {"index": 79171087, "value": "0x38e0ceb1"}, {"index": 267580473, "value": "0x103d3fa4"}, {"index": 24168642, "value": "0xcb03f2ae"}, {"index": 37981670, "value": "0x562969cc"}, {"index": 171551130, "value": "0xf1349e23"}, {"index": 195559979, "value": "0x3fa57b5d"}, {"index": 204611762, "value": "0x4fb463b9"}, {"index": 140997658, "value": "0xb2b70250"}, {"index": 138925853, "value": "0x7f689f8c"}, {"index": 86637313, "value": "0x27e5f93e"}, {"index": 20736778, "value": "0xbaeafce2"}, {"index": 219665210, "value": "0xaf3d3776"}, {"index": 160430336, "value": "0x084c3724"}, {"index": 264654675, "value": "0xffe6a16c"}, {"index": 8013395, "value": "0xd4d0d18d"}, {"index": 228945585, "value": "0x5d78da4e"}, {"index": 213884386, "value": "0xf0bd37c6"}, {"index": 104419827, "value": "0x261f1c0d"}, {"index": 44185464, "value": "0x7bfc0ff6"}, {"index": 142737231, "value": "0x08e5373b"}, {"index": 99284897, "value": "0xd23a038c"}, {"index": 132475900, "value": "0x124d74c0"}, {"index": 61861762, "value": "0xdd74cace"}, {"index": 132056166, "value": "0x5ab7c9ba"}, {"index": 262388043, "value": "0xc05d2942"}, {"index": 91878046, "value": "0x2d1e2f73"}, {"index": 117353561, "value": "0x52a27429"}, {"index": 124768597, "value": "0x0c828d10"}, {"index": 71352993, "value": "0x7514e589"}, {"index": 190698941, "value": "0xdb8ca7d2"}, {"index": 46055428, "value": "0xdb881ba6"}, {"index": 55281366, "value": "0x0ce95e1d"}, {"index": 165145231, "value": "0x3c93a15e"}, {"index": 106810753, "value": "0x73ec90f8"}, {"index": 171985651, "value": "0xe47554ed"}, {"index": 232085256, "value": "0x496959f3"}, {"index": 159510492, "value": "0xebf27832"}, {"index": 40072060, "value": "0x11f5885e"}, {"index": 209107596, "value": "0x69a1ff20"}, {"index": 39023794, "value": "0x804542bd"}], + "cache_head": ["0xebd9055c", "0x7eaf6b21", "0x9b610855", "0x134a8562", "0xb10059ba", "0x7f459e58", "0x8f3c7873", "0x3523e609", "0x75b8f4ca", "0x750d31b4", "0x0dae0782", "0x26f10015", "0x7ba87a41", "0x0efd8543", "0x691d6368", "0x8929d967"], + "cache_last_line": ["0x51474edf", "0xc3cfba93", "0xf21454cf", "0x9b79baac", "0x4d4fce23", "0xd701dfd5", "0x37357ab3", "0x1be693fa", "0xa701cb3b", "0x7467c620", "0x428184e8", "0xf4010df0", "0xe33aa88f", "0xc78d6d62", "0xae9ba9c0", "0xf4bba97e"], + "cache_fnv1a64": "0x448274a57f508cbc" +} diff --git a/proto-cuda/packs-ca2-era/era-3/kernel.cl b/proto-cuda/packs-ca2-era/era-3/kernel.cl new file mode 100644 index 00000000..5c0c8486 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/kernel.cl @@ -0,0 +1,281 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 2 3 8 13 of w. +static inline uint mh_j(uint w) { return ((w >> 2u) & 1u) | (((w >> 3u) & 1u) << 1) | (((w >> 8u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000000ffu) | ((w >> 9u) << 8u); w = (w & 0x00000007u) | ((w >> 4u) << 3u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 0u) & 1u) << 2u); w = ((w >> 3u) << 4u) | (w & 0x00000007u) | (((j >> 1u) & 1u) << 3u); w = ((w >> 8u) << 9u) | (w & 0x000000ffu) | (((j >> 2u) & 1u) << 8u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-era/era-3/kernel.cu b/proto-cuda/packs-ca2-era/era-3/kernel.cu new file mode 100644 index 00000000..4c78ac25 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/kernel.cu @@ -0,0 +1,163 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + for (uint32_t i = 0u; i < 16u; ++i) ds[(size_t)mh_addr(t, i)] = s[i]; + } +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-era/era-3/kernel_bound.cl b/proto-cuda/packs-ca2-era/era-3/kernel_bound.cl new file mode 100644 index 00000000..590222ee --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/kernel_bound.cl @@ -0,0 +1,375 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 2 3 8 13 of w. +static inline uint mh_j(uint w) { return ((w >> 2u) & 1u) | (((w >> 3u) & 1u) << 1) | (((w >> 8u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000000ffu) | ((w >> 9u) << 8u); w = (w & 0x00000007u) | ((w >> 4u) << 3u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 0u) & 1u) << 2u); w = ((w >> 3u) << 4u) | (w & 0x00000007u) | (((j >> 1u) & 1u) << 3u); w = ((w >> 8u) << 9u) | (w & 0x000000ffu) | (((j >> 2u) & 1u) << 8u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-3/kernel_bound.cu b/proto-cuda/packs-ca2-era/era-3/kernel_bound.cu new file mode 100644 index 00000000..2efc3535 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/kernel_bound.cu @@ -0,0 +1,123 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-era/era-3/memhard.h b/proto-cuda/packs-ca2-era/era-3/memhard.h new file mode 100644 index 00000000..c9542dcc --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/memhard.h @@ -0,0 +1,112 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 2 3 8 13 of w. +IGNEUM_HD uint32_t mh_j(uint32_t w) { return ((w >> 2u) & 1u) | (((w >> 3u) & 1u) << 1) | (((w >> 8u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +IGNEUM_HD uint32_t mh_t(uint32_t w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000000ffu) | ((w >> 9u) << 8u); w = (w & 0x00000007u) | ((w >> 4u) << 3u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); return w; } +IGNEUM_HD uint32_t mh_addr(uint32_t t, uint32_t j) { uint32_t w = t; w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 0u) & 1u) << 2u); w = ((w >> 3u) << 4u) | (w & 0x00000007u) | (((j >> 1u) & 1u) << 3u); w = ((w >> 8u) << 9u) | (w & 0x000000ffu) | (((j >> 2u) & 1u) << 8u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } diff --git a/proto-cuda/packs-ca2-era/era-3/memhard.metal b/proto-cuda/packs-ca2-era/era-3/memhard.metal new file mode 100644 index 00000000..51fcf31c --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/memhard.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 2 3 8 13 of w. +inline uint mh_j(uint w) { return ((w >> 2u) & 1u) | (((w >> 3u) & 1u) << 1) | (((w >> 8u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000000ffu) | ((w >> 9u) << 8u); w = (w & 0x00000007u) | ((w >> 4u) << 3u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); return w; } +inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 0u) & 1u) << 2u); w = ((w >> 3u) << 4u) | (w & 0x00000007u) | (((j >> 1u) & 1u) << 3u); w = ((w >> 8u) << 9u) | (w & 0x000000ffu) | (((j >> 2u) & 1u) << 8u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + for (uint i = 0u; i < 16u; ++i) dataset[mh_addr(gid, i)] = s[i]; +} diff --git a/proto-cuda/packs-ca2-era/era-3/program.h b/proto-cuda/packs-ca2-era/era-3/program.h new file mode 100644 index 00000000..c53a957f --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/program.h @@ -0,0 +1,76 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_SEED_BYTES_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" +#define IGNEUM_GENERATOR 3 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x73bcbfe8ccf988f1ull +#define IGNEUM_DAY_STRING "bytes:69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY_BYTES_HEX "69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY0 0xceed56d7u +#define IGNEUM_DAY1 0x9ba270d2u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 add=15 shfl=6 mad=4 or=4 rotl=4 rotr=4 xor=4 mulhi=3 mul=2 sub=2" +// Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that +// runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). +#define IGNEUM_PROGRAM_CLASS "v3" +#define IGNEUM_ERA_SEED_HEX "e593fc1d48475c88e456632f6aa0a752a5fdbca3c33022afe37680e44b7dfd11" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "w4-erad6367bfe" +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Era layout (5 October 2026, docs/plans/era-layout.md): NOT the lottery hash. Every dataset load reads +// idx = ((rotl(src * STRIDE_MUL, STRIDE_ROT) & window mask) | window offset) & MASK; the window of a load site is the +// dataset, a half or a quarter of it (IGNEUM_ERA_WINDOWS: site:shrink:offset); dataset word w holds word j(w) of item +// t(w) with j's bits at the INTERLEAVE positions (memhard.h: mh_t, mh_j, mh_addr). +#define IGNEUM_ERA_LABEL "d6367bfe" +#define IGNEUM_ERA_SEED_WORDS { 0xd6367bfeu, 0x8bee0097u, 0x6c3c1b37u, 0x5dc9cefcu, 0x50b52d88u, 0x03833326u, 0x28ca58f9u, 0xa6e3b5c0u } +#define IGNEUM_ERA_ALLOWED_WIDTHS { 1, 0, 0 } // words, ascending, 0 = unused; one entry pins the width +#define IGNEUM_ERA_WIDTH_WORDS 1 +#define IGNEUM_ERA_STRIDE_MUL 0x27ea7effu +#define IGNEUM_ERA_STRIDE_ROT 30 +#define IGNEUM_ERA_INTERLEAVE { 2, 3, 8, 13 } +#define IGNEUM_ERA_WINDOWS "7:2:1 8:1:1 9:1:1 10:1:1 11:0:0 13:1:1 29:0:0 30:2:2 31:1:1 44:1:1 46:2:0 47:0:0 52:0:0 56:0:0 58:2:0 63:1:1" +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u } +#define IGNEUM_KEY_INIT { 0xceed56d7u, 0x9ba270d2u, 0x82caab2du, 0x81ebce0eu, 0x12b6ecf1u, 0xd0f3fd7cu, 0xd872eefeu, 0xc158c7bdu } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 17u, 12u, 20u, 23u, 7u, 3u, 27u, 16u } +#define IGNEUM_MIX_MUL_INIT { 0xf351d601u, 0xa3bb398fu, 0xb5a09e35u, 0x7509c9c1u, 0x6bbf31e9u, 0xfc849a79u, 0xded91851u, 0x8d9113d1u, 0x0ff15225u, 0x3a5bdd41u, 0xab533435u, 0xe1c55ad5u, 0xe6d3bd0du, 0x9d9ffbbdu, 0xbb2a3cf3u, 0x50a7c08du } +#define IGNEUM_MIX_RC_INIT { 0xc6892460u, 0x25b7228au, 0xcd515004u, 0x2846527au, 0xa6324241u, 0x36e3ec53u, 0x82961bacu, 0x0f97ba7du, 0xb6f921a9u, 0x3ada24e5u, 0xde20ab91u, 0x5378eeb2u, 0x7d161662u, 0x89353cc1u, 0xb1aa03a2u, 0x788acae6u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-era/era-3/program.json b/proto-cuda/packs-ca2-era/era-3/program.json new file mode 100644 index 00000000..b50a9ba8 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/program.json @@ -0,0 +1,143 @@ +{ + "format": "igneum-program-pack-3", + "generator": 3, + "attempt": 0, + "program_id": "0x73bcbfe8ccf988f1", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07", + "seed_words": ["0x667d0fbd", "0x7b8e5963", "0x31c67e5e", "0x4529ddc6", "0xef19d6d8", "0xaccf6211", "0xda0aed32", "0xabc6df31"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "program_class": "v3", + "era_seed_bytes": "e593fc1d48475c88e456632f6aa0a752a5fdbca3c33022afe37680e44b7dfd11", + "load_class": "w4-erad6367bfe", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "era": { + "label": "d6367bfe", + "seed_words": ["0xd6367bfe", "0x8bee0097", "0x6c3c1b37", "0x5dc9cefc", "0x50b52d88", "0x03833326", "0x28ca58f9", "0xa6e3b5c0"], + "draw": "docs/plans/era-layout.md 1.1: SplitMix64 seeded with seed_words[0] | seed_words[1] << 32 of seed_words_from_bytes('igneum-era/' || n_le64 || E_n); width = allowed[below(|allowed|)], stride_mul = low32(next()) | 1, stride_rot = 1 + below(31), then four next() draws for a partial Fisher-Yates over positions log2(W)..15 of which 4 - log2(W) are used", + "allowed_widths": [1], + "width_words": 1, + "stride_mul": "0x27ea7eff", + "stride_rot": 30, + "interleave": [2, 3, 8, 13], + "address": "y = rotl(src * stride_mul, stride_rot); k = min(win, D - 26); idx = ((y & (mask >> k)) | ((off & (2^k - 1)) << (D - k))) & mask; a wide load aligns idx down to W words", + "windows": "per instruction, after the width roll: win = below(3), off = low32(next()) & (2^win - 1); used on a load slot (the instruction's win and off fields)", + "dataset_word": "dataset[w] = item(t(w))[j(w)]: j(w) gathers the bits of w at the interleave positions, t(w) is w with those bits removed", + "program_id_suffix": "'era/' || allowed[3] || width_words || stride_mul_le32 || stride_rot_le32 || interleave[4]" + }, + "op_mix": {"load": 16, "add": 15, "shfl": 6, "mad": 4, "or": 4, "rotl": 4, "rotr": 4, "xor": 4, "mulhi": 3, "mul": 2, "sub": 2}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "day_bytes": "69676e65756d2d6461792ffa50000000000000", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0xceed56d7", + "d1": "0x9ba270d2", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0xceed56d7", "0x9ba270d2", "0x82caab2d", "0x81ebce0e", "0x12b6ecf1", "0xd0f3fd7c", "0xd872eefe", "0xc158c7bd"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [17, 12, 20, 23, 7, 3, 27, 16], "mul": ["0xf351d601", "0xa3bb398f", "0xb5a09e35", "0x7509c9c1", "0x6bbf31e9", "0xfc849a79", "0xded91851", "0x8d9113d1", "0x0ff15225", "0x3a5bdd41", "0xab533435", "0xe1c55ad5", "0xe6d3bd0d", "0x9d9ffbbd", "0xbb2a3cf3", "0x50a7c08d"], "rc": ["0xc6892460", "0x25b7228a", "0xcd515004", "0x2846527a", "0xa6324241", "0x36e3ec53", "0x82961bac", "0x0f97ba7d", "0xb6f921a9", "0x3ada24e5", "0xde20ab91", "0x5378eeb2", "0x7d161662", "0x89353cc1", "0xb1aa03a2", "0x788acae6"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 1, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xbaab6229", "imm2": "0xed861989", "rot": 26, "bit": 22, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 2, "op": "shfl", "dst": 3, "src": 6, "src2": 2, "imm": "0x5b623116", "imm2": "0xff12e5b2", "rot": 12, "bit": 24, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 3, "op": "sub", "dst": 4, "src": 1, "src2": 1, "imm": "0xe99741c7", "imm2": "0xf5fa5009", "rot": 1, "bit": 21, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 4, "op": "mad", "dst": 2, "src": 0, "src2": 4, "imm": "0x673c2157", "imm2": "0xee02465f", "rot": 20, "bit": 22, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 5, "op": "rotl", "dst": 4, "src": 7, "src2": 7, "imm": "0x946f7818", "imm2": "0x45d3399e", "rot": 9, "bit": 2, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 6, "op": "rotr", "dst": 0, "src": 2, "src2": 4, "imm": "0x5f6a0ed2", "imm2": "0x7043a636", "rot": 19, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 7, "op": "load", "dst": 6, "src": 7, "src2": 1, "imm": "0x5c61dcf7", "imm2": "0x7466aa40", "rot": 19, "bit": 9, "mask": 2, "width": 1, "win": 2, "off": 1}, + {"i": 8, "op": "load", "dst": 1, "src": 4, "src2": 5, "imm": "0x85668475", "imm2": "0xdb8cc483", "rot": 29, "bit": 7, "mask": 4, "width": 1, "win": 1, "off": 1}, + {"i": 9, "op": "load", "dst": 1, "src": 2, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 10, "op": "load", "dst": 7, "src": 0, "src2": 2, "imm": "0xe075297c", "imm2": "0x5779c44c", "rot": 10, "bit": 22, "mask": 2, "width": 1, "win": 1, "off": 1}, + {"i": 11, "op": "load", "dst": 7, "src": 1, "src2": 6, "imm": "0x65aa4311", "imm2": "0x4fe48ea9", "rot": 15, "bit": 9, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 12, "op": "mul", "dst": 5, "src": 4, "src2": 2, "imm": "0x1383d3ad", "imm2": "0xf3094b29", "rot": 8, "bit": 9, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 13, "op": "load", "dst": 7, "src": 6, "src2": 2, "imm": "0xed8a496f", "imm2": "0x3072c3c6", "rot": 28, "bit": 19, "mask": 8, "width": 1, "win": 1, "off": 1}, + {"i": 14, "op": "shfl", "dst": 6, "src": 5, "src2": 0, "imm": "0x8b965b57", "imm2": "0xcfeca6c1", "rot": 12, "bit": 27, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 15, "op": "shfl", "dst": 3, "src": 5, "src2": 3, "imm": "0x877c7586", "imm2": "0xa9cb2a03", "rot": 2, "bit": 29, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 16, "op": "or", "dst": 2, "src": 7, "src2": 3, "imm": "0xb740221a", "imm2": "0x89d38d6d", "rot": 6, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 17, "op": "rotr", "dst": 0, "src": 6, "src2": 1, "imm": "0x26f3ad8a", "imm2": "0x27256f15", "rot": 18, "bit": 5, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 18, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 19, "op": "rotl", "dst": 7, "src": 0, "src2": 5, "imm": "0x849ae6ee", "imm2": "0x02b358f9", "rot": 24, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 20, "op": "add", "dst": 6, "src": 7, "src2": 6, "imm": "0x52334d12", "imm2": "0x8c9f0ef8", "rot": 11, "bit": 23, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 21, "op": "or", "dst": 2, "src": 6, "src2": 5, "imm": "0xb1871e63", "imm2": "0xb2e40191", "rot": 5, "bit": 13, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 22, "op": "mad", "dst": 6, "src": 5, "src2": 4, "imm": "0x97df29e4", "imm2": "0xe60fea84", "rot": 11, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 23, "op": "or", "dst": 1, "src": 0, "src2": 3, "imm": "0x8f30d21d", "imm2": "0x2df685a0", "rot": 31, "bit": 0, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 24, "op": "xor", "dst": 6, "src": 1, "src2": 2, "imm": "0xd2c4025f", "imm2": "0x5269eb4d", "rot": 31, "bit": 21, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 25, "op": "add", "dst": 2, "src": 6, "src2": 5, "imm": "0x659fc3d3", "imm2": "0x9cec0e12", "rot": 6, "bit": 17, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 26, "op": "rotr", "dst": 7, "src": 0, "src2": 1, "imm": "0xd89ef484", "imm2": "0x20be3846", "rot": 12, "bit": 9, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 27, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 28, "op": "mad", "dst": 3, "src": 2, "src2": 4, "imm": "0xc5c46d76", "imm2": "0x700044b5", "rot": 22, "bit": 10, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 29, "op": "load", "dst": 1, "src": 4, "src2": 3, "imm": "0x0fbaf177", "imm2": "0xfff4f2ed", "rot": 20, "bit": 31, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 30, "op": "load", "dst": 2, "src": 3, "src2": 0, "imm": "0xe90eb2e5", "imm2": "0xb0f9eb79", "rot": 27, "bit": 14, "mask": 4, "width": 1, "win": 2, "off": 2}, + {"i": 31, "op": "load", "dst": 1, "src": 5, "src2": 2, "imm": "0x97ba3fc3", "imm2": "0x7894e657", "rot": 3, "bit": 30, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 32, "op": "add", "dst": 7, "src": 2, "src2": 7, "imm": "0x070888a8", "imm2": "0xe403240e", "rot": 2, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 33, "op": "add", "dst": 2, "src": 0, "src2": 5, "imm": "0xf2e46d55", "imm2": "0x29701828", "rot": 31, "bit": 28, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 34, "op": "add", "dst": 2, "src": 3, "src2": 0, "imm": "0x58f75b87", "imm2": "0x343b7aee", "rot": 12, "bit": 14, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 35, "op": "mulhi", "dst": 2, "src": 5, "src2": 6, "imm": "0x5892a9e6", "imm2": "0xc9824c94", "rot": 19, "bit": 26, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 36, "op": "xor", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 37, "op": "mul", "dst": 6, "src": 5, "src2": 7, "imm": "0xccf564a5", "imm2": "0x873ad101", "rot": 7, "bit": 11, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 38, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xcac8f06d", "imm2": "0x6b97c683", "rot": 18, "bit": 28, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 39, "op": "add", "dst": 7, "src": 2, "src2": 4, "imm": "0xb8180e9d", "imm2": "0x32bbd117", "rot": 23, "bit": 19, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 40, "op": "rotr", "dst": 2, "src": 3, "src2": 0, "imm": "0x2d6070bc", "imm2": "0x68ff101e", "rot": 13, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 41, "op": "sub", "dst": 7, "src": 0, "src2": 2, "imm": "0x0daf96ea", "imm2": "0x36f37be1", "rot": 5, "bit": 0, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 42, "op": "add", "dst": 4, "src": 3, "src2": 6, "imm": "0x6ced15b7", "imm2": "0x6df7aed4", "rot": 19, "bit": 4, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 43, "op": "shfl", "dst": 7, "src": 3, "src2": 5, "imm": "0x8ace05f3", "imm2": "0xd378ec12", "rot": 23, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 44, "op": "load", "dst": 0, "src": 7, "src2": 4, "imm": "0xb0607786", "imm2": "0xc4acabbc", "rot": 13, "bit": 7, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 45, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 46, "op": "load", "dst": 3, "src": 1, "src2": 0, "imm": "0x63cc1e4e", "imm2": "0xa1be8118", "rot": 12, "bit": 6, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 47, "op": "load", "dst": 6, "src": 3, "src2": 7, "imm": "0x353f1d79", "imm2": "0x3b2e7456", "rot": 18, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 48, "op": "mulhi", "dst": 4, "src": 2, "src2": 7, "imm": "0x00d8a3cd", "imm2": "0x231866d2", "rot": 21, "bit": 20, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 49, "op": "add", "dst": 5, "src": 0, "src2": 2, "imm": "0xa8bae6df", "imm2": "0xf572bdb9", "rot": 14, "bit": 7, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 50, "op": "shfl", "dst": 0, "src": 7, "src2": 7, "imm": "0x81ef22e1", "imm2": "0x74438fc5", "rot": 28, "bit": 18, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 51, "op": "add", "dst": 6, "src": 0, "src2": 6, "imm": "0x383b9260", "imm2": "0x11e17c61", "rot": 12, "bit": 19, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 52, "op": "load", "dst": 5, "src": 2, "src2": 2, "imm": "0xfb84f451", "imm2": "0x11cd863e", "rot": 21, "bit": 20, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 53, "op": "rotl", "dst": 6, "src": 5, "src2": 4, "imm": "0xb1a7db6b", "imm2": "0x76686b9b", "rot": 12, "bit": 4, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 54, "op": "rotl", "dst": 3, "src": 6, "src2": 3, "imm": "0x6f981f52", "imm2": "0xd99aeba2", "rot": 12, "bit": 27, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 55, "op": "add", "dst": 2, "src": 1, "src2": 2, "imm": "0xac6be8e3", "imm2": "0x18d67dbb", "rot": 26, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 56, "op": "load", "dst": 1, "src": 4, "src2": 0, "imm": "0x7e7f6a00", "imm2": "0x6f0747da", "rot": 25, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 57, "op": "add", "dst": 5, "src": 0, "src2": 4, "imm": "0xf03673fe", "imm2": "0xa75cd60d", "rot": 16, "bit": 12, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 58, "op": "load", "dst": 5, "src": 0, "src2": 2, "imm": "0x227f94a6", "imm2": "0x0e8344f9", "rot": 20, "bit": 10, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 59, "op": "add", "dst": 1, "src": 4, "src2": 3, "imm": "0xdecd4794", "imm2": "0x8dfb96bb", "rot": 21, "bit": 7, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 60, "op": "mulhi", "dst": 3, "src": 2, "src2": 2, "imm": "0x0dd268e0", "imm2": "0x53034ca9", "rot": 1, "bit": 8, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 61, "op": "or", "dst": 6, "src": 4, "src2": 7, "imm": "0x3a45a321", "imm2": "0x9bc59a5f", "rot": 25, "bit": 11, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 62, "op": "shfl", "dst": 5, "src": 4, "src2": 2, "imm": "0x8f229cc1", "imm2": "0xcaac64a2", "rot": 17, "bit": 13, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0xb2574178", "imm2": "0xcbcc798d", "rot": 28, "bit": 0, "mask": 16, "width": 1, "win": 1, "off": 1} + ] +} diff --git a/proto-cuda/packs-ca2-era/era-3/program.metal b/proto-cuda/packs-ca2-era/era-3/program.metal new file mode 100644 index 00000000..dd57d5bc --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/program.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-3/program_bound.metal b/proto-cuda/packs-ca2-era/era-3/program_bound.metal new file mode 100644 index 00000000..a8cdb6c1 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/program_bound.metal @@ -0,0 +1,111 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x27ea7effu, 30u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x27ea7effu, 30u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x27ea7effu, 30u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-3/seeds.txt b/proto-cuda/packs-ca2-era/era-3/seeds.txt new file mode 100644 index 00000000..ff7b31ee --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/seeds.txt @@ -0,0 +1,5 @@ +epoch_seed_hex edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 +day_seed_hex 69676e65756d2d6461792ffa50000000000000 +epoch_index 0 +day_index 20730 +daa_score 0 diff --git a/proto-cuda/packs-ca2-era/era-3/vectors.h b/proto-cuda/packs-ca2-era/era-3/vectors.h new file mode 100644 index 00000000..71f2d9d8 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/vectors.h @@ -0,0 +1,57 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0x482d9ca50850d913ull, 0x4ad212a8109dbf80ull, 0xfc22f1d2859b72dbull, 0x1b122111c880daddull, 0x590c0ed07d26f3beull, 0x14f9ccb1fe4a0aa0ull, 0xb9aa46ddbf3358f4ull, 0x7a2e7a0758a139dcull, + 0x4ca0c6f57fc6a6f8ull, 0xa9303e49a53c55cfull, 0xd0eef090a205c2daull, 0xf916463a1fe71f3dull, 0xece100319c1ba260ull, 0x0d765a753bab6621ull, 0x7a8b38b0cd86e711ull, 0x6330fa995f69eac6ull, + 0x31630dc8222e4cf3ull, 0x1ab386a5c4bfd048ull, 0xd1849ab5d7ccc035ull, 0x7e42d0b071e213b7ull, 0xc61d482f91f73a00ull, 0xb759a48c18732abcull, 0x7cb720597cea45f4ull, 0x9aa782a7d7153267ull, + 0x3928c8ca30daa819ull, 0xf6624ec425bf44b6ull, 0xb92b06f50f5fca62ull, 0xb7ac77af13b44e09ull, 0x9bd4a23ed9a8d186ull, 0xed157fa7db51abdfull, 0x169dae4f9708b9b0ull, 0x3928f1c5bb85b8ffull + }, + { // base nonce 4096 + 0x68d28d84a9cacd37ull, 0x3de7667c504d33b1ull, 0x9cd9cbd429ecccbfull, 0xce28296aba64ec41ull, 0x7f25bfb162361f07ull, 0x305c8fcecda70a88ull, 0x4d26391bf3d1c1f0ull, 0x230a57b46fde607full, + 0x35482e4e9ae6e874ull, 0x87f3bdd2250cb21eull, 0x71a6a482132c6938ull, 0x3c853fb23317a129ull, 0x5932d928549c095aull, 0xe90460bc189cdb34ull, 0x6dbe852db160461aull, 0x2abd73567e773ab6ull, + 0x97be9fa85c3805ffull, 0xf7d138416c3ac1ccull, 0x888b84f204d9305eull, 0x92800629cdb2d056ull, 0x2cb98fc7e6f8b22bull, 0x085ba0e520fa1df6ull, 0x9b0ee619aa074133ull, 0xd92649b972786f58ull, + 0x94e56fdfade518caull, 0xcae28f9eec9d5be5ull, 0x2e715314eaa160b6ull, 0xd3faf570104ca532ull, 0xeeecb2834a0e878bull, 0x9e978602b88d0a38ull, 0x3536c9dd7062e398ull, 0x5974adef999d72a2ull + }, + { // base nonce 1000000 + 0x86c2830118bf5d18ull, 0x0ca010e1f65efea9ull, 0x0e065db2ff5d7177ull, 0xb6c5de9453893f86ull, 0x4b2bca8ca7ff4dacull, 0x875e9e4b09262995ull, 0xfe1c3a11b0dc75abull, 0x9f83ac0ba031e6dfull, + 0x5c4e3154cdf0d3beull, 0x812fd5f5be4159e6ull, 0xcbf87d1772e7a740ull, 0x251c1bb3b31e0de1ull, 0xba117c2b253c2d04ull, 0xf42a27fffa765e8aull, 0xb654d4c021ced921ull, 0x63fe5bc570bd9a6dull, + 0xe3729cacfa2738b9ull, 0x0686bf7f029dd650ull, 0xa4da20856fbb382eull, 0xcacd5c3aa30da86cull, 0x7b47735366038a7aull, 0xe0b5603c8652702eull, 0x62b834fee790c2c5ull, 0x4c979e00c086c371ull, + 0x9a94911a7eaa32d6ull, 0x377a5edc37bfb69full, 0xecebe1aab4f07982ull, 0xdd2bba556d4d8996ull, 0xb6e0be464e82691cull, 0xe4cc7f9840d754d9ull, 0x8e31fb423c108e46ull, 0x8164e7242a952c96ull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0x3dd50b1fu, 0x93123701u, 0xcdd88b45u, 0x2c37c6b0u, 0x48edec90u, 0x3a7d2407u, 0xb03a0ac0u, 0x10e2c6a5u, + 0x5119540eu, 0x3358ef2du, 0x25e2929cu, 0x5fa0cdccu, 0x657ac748u, 0x09e2105cu, 0x4317cabdu, 0x6178ee52u +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xf7b7180eu; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0xf6e00c89u, 0xe206f72bu, 0x09260b8cu, 0x662c6558u, 0x4028c489u, 0xbccb5ab0u, 0x88bf905cu, 0x88f76874u, 0xea529b56u, 0x88e89b31u, 0x22deecc6u, 0xd6022e44u, 0x7a7923c9u, 0x8c761ec7u, 0x7079d93du, 0x79341b4cu, 0x560640c9u, 0xc659d408u, 0xd5883003u, 0x95e70092u, 0xbb0552dbu, 0xbbec95bfu, 0x1efacd50u, 0x8d4c6036u, 0xcbcb1898u, 0x05d9a8aeu, 0x0e5af59bu, 0x12094029u, 0xd6132e60u, 0xffc32f3eu, 0x3bae4991u, 0xbf6d57a0u, 0x7f03491eu, 0x26c9e599u, 0xa0c9ecc9u, 0xae96f173u, 0x1f437e50u, 0x1f641c65u, 0x30d7028au, 0x65092ed9u, 0xe390c42bu, 0x8f89bb6bu, 0xb4205b97u, 0xd79db283u, 0x65869dcfu, 0x4ac02d55u, 0x5f1b8dc3u, 0xd192a75au, 0xcca97073u, 0x2f043a85u, 0xdc0c3fddu, 0x19b12a30u, 0xf5da472eu, 0xbf7dd652u, 0xec1dc918u, 0x2f00a5dcu, 0xbe7f358eu, 0x68a25926u, 0x817ae6c9u, 0xa731ea98u, 0x6af869dau, 0xb84e7e41u, 0xd3519f00u, 0x3e6c426au +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0xebd9055cu, 0x7eaf6b21u, 0x9b610855u, 0x134a8562u, 0xb10059bau, 0x7f459e58u, 0x8f3c7873u, 0x3523e609u, + 0x75b8f4cau, 0x750d31b4u, 0x0dae0782u, 0x26f10015u, 0x7ba87a41u, 0x0efd8543u, 0x691d6368u, 0x8929d967u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x51474edfu, 0xc3cfba93u, 0xf21454cfu, 0x9b79baacu, 0x4d4fce23u, 0xd701dfd5u, 0x37357ab3u, 0x1be693fau, + 0xa701cb3bu, 0x7467c620u, 0x428184e8u, 0xf4010df0u, 0xe33aa88fu, 0xc78d6d62u, 0xae9ba9c0u, 0xf4bba97eu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x448274a57f508cbcull; diff --git a/proto-cuda/packs-ca2-era/era-3/vectors.json b/proto-cuda/packs-ca2-era/era-3/vectors.json new file mode 100644 index 00000000..a3123c12 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-3/vectors.json @@ -0,0 +1,36 @@ +{ + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0x482d9ca50850d913", "0x4ad212a8109dbf80", "0xfc22f1d2859b72db", "0x1b122111c880dadd", "0x590c0ed07d26f3be", "0x14f9ccb1fe4a0aa0", "0xb9aa46ddbf3358f4", "0x7a2e7a0758a139dc", + "0x4ca0c6f57fc6a6f8", "0xa9303e49a53c55cf", "0xd0eef090a205c2da", "0xf916463a1fe71f3d", "0xece100319c1ba260", "0x0d765a753bab6621", "0x7a8b38b0cd86e711", "0x6330fa995f69eac6", + "0x31630dc8222e4cf3", "0x1ab386a5c4bfd048", "0xd1849ab5d7ccc035", "0x7e42d0b071e213b7", "0xc61d482f91f73a00", "0xb759a48c18732abc", "0x7cb720597cea45f4", "0x9aa782a7d7153267", + "0x3928c8ca30daa819", "0xf6624ec425bf44b6", "0xb92b06f50f5fca62", "0xb7ac77af13b44e09", "0x9bd4a23ed9a8d186", "0xed157fa7db51abdf", "0x169dae4f9708b9b0", "0x3928f1c5bb85b8ff" + ]}, + {"base_nonce": 4096, "expected": [ + "0x68d28d84a9cacd37", "0x3de7667c504d33b1", "0x9cd9cbd429ecccbf", "0xce28296aba64ec41", "0x7f25bfb162361f07", "0x305c8fcecda70a88", "0x4d26391bf3d1c1f0", "0x230a57b46fde607f", + "0x35482e4e9ae6e874", "0x87f3bdd2250cb21e", "0x71a6a482132c6938", "0x3c853fb23317a129", "0x5932d928549c095a", "0xe90460bc189cdb34", "0x6dbe852db160461a", "0x2abd73567e773ab6", + "0x97be9fa85c3805ff", "0xf7d138416c3ac1cc", "0x888b84f204d9305e", "0x92800629cdb2d056", "0x2cb98fc7e6f8b22b", "0x085ba0e520fa1df6", "0x9b0ee619aa074133", "0xd92649b972786f58", + "0x94e56fdfade518ca", "0xcae28f9eec9d5be5", "0x2e715314eaa160b6", "0xd3faf570104ca532", "0xeeecb2834a0e878b", "0x9e978602b88d0a38", "0x3536c9dd7062e398", "0x5974adef999d72a2" + ]}, + {"base_nonce": 1000000, "expected": [ + "0x86c2830118bf5d18", "0x0ca010e1f65efea9", "0x0e065db2ff5d7177", "0xb6c5de9453893f86", "0x4b2bca8ca7ff4dac", "0x875e9e4b09262995", "0xfe1c3a11b0dc75ab", "0x9f83ac0ba031e6df", + "0x5c4e3154cdf0d3be", "0x812fd5f5be4159e6", "0xcbf87d1772e7a740", "0x251c1bb3b31e0de1", "0xba117c2b253c2d04", "0xf42a27fffa765e8a", "0xb654d4c021ced921", "0x63fe5bc570bd9a6d", + "0xe3729cacfa2738b9", "0x0686bf7f029dd650", "0xa4da20856fbb382e", "0xcacd5c3aa30da86c", "0x7b47735366038a7a", "0xe0b5603c8652702e", "0x62b834fee790c2c5", "0x4c979e00c086c371", + "0x9a94911a7eaa32d6", "0x377a5edc37bfb69f", "0xecebe1aab4f07982", "0xdd2bba556d4d8996", "0xb6e0be464e82691c", "0xe4cc7f9840d754d9", "0x8e31fb423c108e46", "0x8164e7242a952c96" + ]} + ], + "dataset_head": ["0x3dd50b1f", "0x93123701", "0xcdd88b45", "0x2c37c6b0", "0x48edec90", "0x3a7d2407", "0xb03a0ac0", "0x10e2c6a5", "0x5119540e", "0x3358ef2d", "0x25e2929c", "0x5fa0cdcc", "0x657ac748", "0x09e2105c", "0x4317cabd", "0x6178ee52"], + "dataset_last_index": 268435455, + "dataset_last": "0xf7b7180e", + "dataset_samples": [{"index": 59471966, "value": "0xf6e00c89"}, {"index": 217795994, "value": "0xe206f72b"}, {"index": 208353206, "value": "0x09260b8c"}, {"index": 42483309, "value": "0x662c6558"}, {"index": 172547758, "value": "0x4028c489"}, {"index": 148076330, "value": "0xbccb5ab0"}, {"index": 183853158, "value": "0x88bf905c"}, {"index": 214389424, "value": "0x88f76874"}, {"index": 267488061, "value": "0xea529b56"}, {"index": 169781097, "value": "0x88e89b31"}, {"index": 184093494, "value": "0x22deecc6"}, {"index": 153880993, "value": "0xd6022e44"}, {"index": 84977930, "value": "0x7a7923c9"}, {"index": 46426879, "value": "0x8c761ec7"}, {"index": 3093825, "value": "0x7079d93d"}, {"index": 225364072, "value": "0x79341b4c"}, {"index": 44593546, "value": "0x560640c9"}, {"index": 260713159, "value": "0xc659d408"}, {"index": 168250303, "value": "0xd5883003"}, {"index": 52384140, "value": "0x95e70092"}, {"index": 223401610, "value": "0xbb0552db"}, {"index": 45554030, "value": "0xbbec95bf"}, {"index": 95410555, "value": "0x1efacd50"}, {"index": 175039924, "value": "0x8d4c6036"}, {"index": 79171087, "value": "0xcbcb1898"}, {"index": 267580473, "value": "0x05d9a8ae"}, {"index": 24168642, "value": "0x0e5af59b"}, {"index": 37981670, "value": "0x12094029"}, {"index": 171551130, "value": "0xd6132e60"}, {"index": 195559979, "value": "0xffc32f3e"}, {"index": 204611762, "value": "0x3bae4991"}, {"index": 140997658, "value": "0xbf6d57a0"}, {"index": 138925853, "value": "0x7f03491e"}, {"index": 86637313, "value": "0x26c9e599"}, {"index": 20736778, "value": "0xa0c9ecc9"}, {"index": 219665210, "value": "0xae96f173"}, {"index": 160430336, "value": "0x1f437e50"}, {"index": 264654675, "value": "0x1f641c65"}, {"index": 8013395, "value": "0x30d7028a"}, {"index": 228945585, "value": "0x65092ed9"}, {"index": 213884386, "value": "0xe390c42b"}, {"index": 104419827, "value": "0x8f89bb6b"}, {"index": 44185464, "value": "0xb4205b97"}, {"index": 142737231, "value": "0xd79db283"}, {"index": 99284897, "value": "0x65869dcf"}, {"index": 132475900, "value": "0x4ac02d55"}, {"index": 61861762, "value": "0x5f1b8dc3"}, {"index": 132056166, "value": "0xd192a75a"}, {"index": 262388043, "value": "0xcca97073"}, {"index": 91878046, "value": "0x2f043a85"}, {"index": 117353561, "value": "0xdc0c3fdd"}, {"index": 124768597, "value": "0x19b12a30"}, {"index": 71352993, "value": "0xf5da472e"}, {"index": 190698941, "value": "0xbf7dd652"}, {"index": 46055428, "value": "0xec1dc918"}, {"index": 55281366, "value": "0x2f00a5dc"}, {"index": 165145231, "value": "0xbe7f358e"}, {"index": 106810753, "value": "0x68a25926"}, {"index": 171985651, "value": "0x817ae6c9"}, {"index": 232085256, "value": "0xa731ea98"}, {"index": 159510492, "value": "0x6af869da"}, {"index": 40072060, "value": "0xb84e7e41"}, {"index": 209107596, "value": "0xd3519f00"}, {"index": 39023794, "value": "0x3e6c426a"}], + "cache_head": ["0xebd9055c", "0x7eaf6b21", "0x9b610855", "0x134a8562", "0xb10059ba", "0x7f459e58", "0x8f3c7873", "0x3523e609", "0x75b8f4ca", "0x750d31b4", "0x0dae0782", "0x26f10015", "0x7ba87a41", "0x0efd8543", "0x691d6368", "0x8929d967"], + "cache_last_line": ["0x51474edf", "0xc3cfba93", "0xf21454cf", "0x9b79baac", "0x4d4fce23", "0xd701dfd5", "0x37357ab3", "0x1be693fa", "0xa701cb3b", "0x7467c620", "0x428184e8", "0xf4010df0", "0xe33aa88f", "0xc78d6d62", "0xae9ba9c0", "0xf4bba97e"], + "cache_fnv1a64": "0x448274a57f508cbc" +} diff --git a/proto-cuda/packs-ca2-era/era-4/kernel.cl b/proto-cuda/packs-ca2-era/era-4/kernel.cl new file mode 100644 index 00000000..f63a2b9c --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/kernel.cl @@ -0,0 +1,281 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 2 9 13 15 of w. +static inline uint mh_j(uint w) { return ((w >> 2u) & 1u) | (((w >> 9u) & 1u) << 1) | (((w >> 13u) & 1u) << 2) | (((w >> 15u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00007fffu) | ((w >> 16u) << 15u); w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000001ffu) | ((w >> 10u) << 9u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 0u) & 1u) << 2u); w = ((w >> 9u) << 10u) | (w & 0x000001ffu) | (((j >> 1u) & 1u) << 9u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 2u) & 1u) << 13u); w = ((w >> 15u) << 16u) | (w & 0x00007fffu) | (((j >> 3u) & 1u) << 15u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-era/era-4/kernel.cu b/proto-cuda/packs-ca2-era/era-4/kernel.cu new file mode 100644 index 00000000..97d49363 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/kernel.cu @@ -0,0 +1,163 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + for (uint32_t i = 0u; i < 16u; ++i) ds[(size_t)mh_addr(t, i)] = s[i]; + } +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-era/era-4/kernel_bound.cl b/proto-cuda/packs-ca2-era/era-4/kernel_bound.cl new file mode 100644 index 00000000..90dafcf9 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/kernel_bound.cl @@ -0,0 +1,375 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 2 9 13 15 of w. +static inline uint mh_j(uint w) { return ((w >> 2u) & 1u) | (((w >> 9u) & 1u) << 1) | (((w >> 13u) & 1u) << 2) | (((w >> 15u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00007fffu) | ((w >> 16u) << 15u); w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000001ffu) | ((w >> 10u) << 9u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 0u) & 1u) << 2u); w = ((w >> 9u) << 10u) | (w & 0x000001ffu) | (((j >> 1u) & 1u) << 9u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 2u) & 1u) << 13u); w = ((w >> 15u) << 16u) | (w & 0x00007fffu) | (((j >> 3u) & 1u) << 15u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-4/kernel_bound.cu b/proto-cuda/packs-ca2-era/era-4/kernel_bound.cu new file mode 100644 index 00000000..17246ebe --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/kernel_bound.cu @@ -0,0 +1,123 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-era/era-4/memhard.h b/proto-cuda/packs-ca2-era/era-4/memhard.h new file mode 100644 index 00000000..edcde333 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/memhard.h @@ -0,0 +1,112 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 2 9 13 15 of w. +IGNEUM_HD uint32_t mh_j(uint32_t w) { return ((w >> 2u) & 1u) | (((w >> 9u) & 1u) << 1) | (((w >> 13u) & 1u) << 2) | (((w >> 15u) & 1u) << 3); } +IGNEUM_HD uint32_t mh_t(uint32_t w) { w = (w & 0x00007fffu) | ((w >> 16u) << 15u); w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000001ffu) | ((w >> 10u) << 9u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); return w; } +IGNEUM_HD uint32_t mh_addr(uint32_t t, uint32_t j) { uint32_t w = t; w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 0u) & 1u) << 2u); w = ((w >> 9u) << 10u) | (w & 0x000001ffu) | (((j >> 1u) & 1u) << 9u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 2u) & 1u) << 13u); w = ((w >> 15u) << 16u) | (w & 0x00007fffu) | (((j >> 3u) & 1u) << 15u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } diff --git a/proto-cuda/packs-ca2-era/era-4/memhard.metal b/proto-cuda/packs-ca2-era/era-4/memhard.metal new file mode 100644 index 00000000..8e553b2c --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/memhard.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 2 9 13 15 of w. +inline uint mh_j(uint w) { return ((w >> 2u) & 1u) | (((w >> 9u) & 1u) << 1) | (((w >> 13u) & 1u) << 2) | (((w >> 15u) & 1u) << 3); } +inline uint mh_t(uint w) { w = (w & 0x00007fffu) | ((w >> 16u) << 15u); w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000001ffu) | ((w >> 10u) << 9u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); return w; } +inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 0u) & 1u) << 2u); w = ((w >> 9u) << 10u) | (w & 0x000001ffu) | (((j >> 1u) & 1u) << 9u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 2u) & 1u) << 13u); w = ((w >> 15u) << 16u) | (w & 0x00007fffu) | (((j >> 3u) & 1u) << 15u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + for (uint i = 0u; i < 16u; ++i) dataset[mh_addr(gid, i)] = s[i]; +} diff --git a/proto-cuda/packs-ca2-era/era-4/program.h b/proto-cuda/packs-ca2-era/era-4/program.h new file mode 100644 index 00000000..5e197494 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/program.h @@ -0,0 +1,76 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_SEED_BYTES_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" +#define IGNEUM_GENERATOR 3 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x73bcbfe8ccf988f1ull +#define IGNEUM_DAY_STRING "bytes:69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY_BYTES_HEX "69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY0 0xceed56d7u +#define IGNEUM_DAY1 0x9ba270d2u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 add=15 shfl=6 mad=4 or=4 rotl=4 rotr=4 xor=4 mulhi=3 mul=2 sub=2" +// Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that +// runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). +#define IGNEUM_PROGRAM_CLASS "v3" +#define IGNEUM_ERA_SEED_HEX "5e0587f455a86e91e4990f5c481a34cca0044d3f3ae519adcae58ddc82885d31" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "w4-era4488f3ed" +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Era layout (5 October 2026, docs/plans/era-layout.md): NOT the lottery hash. Every dataset load reads +// idx = ((rotl(src * STRIDE_MUL, STRIDE_ROT) & window mask) | window offset) & MASK; the window of a load site is the +// dataset, a half or a quarter of it (IGNEUM_ERA_WINDOWS: site:shrink:offset); dataset word w holds word j(w) of item +// t(w) with j's bits at the INTERLEAVE positions (memhard.h: mh_t, mh_j, mh_addr). +#define IGNEUM_ERA_LABEL "4488f3ed" +#define IGNEUM_ERA_SEED_WORDS { 0x4488f3edu, 0x3cf22d2au, 0xb3e8271eu, 0x55754277u, 0xc2b1c4c7u, 0x8e627302u, 0x584d5acdu, 0xc31dd01du } +#define IGNEUM_ERA_ALLOWED_WIDTHS { 1, 0, 0 } // words, ascending, 0 = unused; one entry pins the width +#define IGNEUM_ERA_WIDTH_WORDS 1 +#define IGNEUM_ERA_STRIDE_MUL 0x4d38603du +#define IGNEUM_ERA_STRIDE_ROT 10 +#define IGNEUM_ERA_INTERLEAVE { 2, 9, 13, 15 } +#define IGNEUM_ERA_WINDOWS "7:2:1 8:1:1 9:1:1 10:1:1 11:0:0 13:1:1 29:0:0 30:2:2 31:1:1 44:1:1 46:2:0 47:0:0 52:0:0 56:0:0 58:2:0 63:1:1" +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u } +#define IGNEUM_KEY_INIT { 0xceed56d7u, 0x9ba270d2u, 0x82caab2du, 0x81ebce0eu, 0x12b6ecf1u, 0xd0f3fd7cu, 0xd872eefeu, 0xc158c7bdu } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 17u, 12u, 20u, 23u, 7u, 3u, 27u, 16u } +#define IGNEUM_MIX_MUL_INIT { 0xf351d601u, 0xa3bb398fu, 0xb5a09e35u, 0x7509c9c1u, 0x6bbf31e9u, 0xfc849a79u, 0xded91851u, 0x8d9113d1u, 0x0ff15225u, 0x3a5bdd41u, 0xab533435u, 0xe1c55ad5u, 0xe6d3bd0du, 0x9d9ffbbdu, 0xbb2a3cf3u, 0x50a7c08du } +#define IGNEUM_MIX_RC_INIT { 0xc6892460u, 0x25b7228au, 0xcd515004u, 0x2846527au, 0xa6324241u, 0x36e3ec53u, 0x82961bacu, 0x0f97ba7du, 0xb6f921a9u, 0x3ada24e5u, 0xde20ab91u, 0x5378eeb2u, 0x7d161662u, 0x89353cc1u, 0xb1aa03a2u, 0x788acae6u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-era/era-4/program.json b/proto-cuda/packs-ca2-era/era-4/program.json new file mode 100644 index 00000000..7740a7c1 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/program.json @@ -0,0 +1,143 @@ +{ + "format": "igneum-program-pack-3", + "generator": 3, + "attempt": 0, + "program_id": "0x73bcbfe8ccf988f1", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07", + "seed_words": ["0x667d0fbd", "0x7b8e5963", "0x31c67e5e", "0x4529ddc6", "0xef19d6d8", "0xaccf6211", "0xda0aed32", "0xabc6df31"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "program_class": "v3", + "era_seed_bytes": "5e0587f455a86e91e4990f5c481a34cca0044d3f3ae519adcae58ddc82885d31", + "load_class": "w4-era4488f3ed", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "era": { + "label": "4488f3ed", + "seed_words": ["0x4488f3ed", "0x3cf22d2a", "0xb3e8271e", "0x55754277", "0xc2b1c4c7", "0x8e627302", "0x584d5acd", "0xc31dd01d"], + "draw": "docs/plans/era-layout.md 1.1: SplitMix64 seeded with seed_words[0] | seed_words[1] << 32 of seed_words_from_bytes('igneum-era/' || n_le64 || E_n); width = allowed[below(|allowed|)], stride_mul = low32(next()) | 1, stride_rot = 1 + below(31), then four next() draws for a partial Fisher-Yates over positions log2(W)..15 of which 4 - log2(W) are used", + "allowed_widths": [1], + "width_words": 1, + "stride_mul": "0x4d38603d", + "stride_rot": 10, + "interleave": [2, 9, 13, 15], + "address": "y = rotl(src * stride_mul, stride_rot); k = min(win, D - 26); idx = ((y & (mask >> k)) | ((off & (2^k - 1)) << (D - k))) & mask; a wide load aligns idx down to W words", + "windows": "per instruction, after the width roll: win = below(3), off = low32(next()) & (2^win - 1); used on a load slot (the instruction's win and off fields)", + "dataset_word": "dataset[w] = item(t(w))[j(w)]: j(w) gathers the bits of w at the interleave positions, t(w) is w with those bits removed", + "program_id_suffix": "'era/' || allowed[3] || width_words || stride_mul_le32 || stride_rot_le32 || interleave[4]" + }, + "op_mix": {"load": 16, "add": 15, "shfl": 6, "mad": 4, "or": 4, "rotl": 4, "rotr": 4, "xor": 4, "mulhi": 3, "mul": 2, "sub": 2}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "day_bytes": "69676e65756d2d6461792ffa50000000000000", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0xceed56d7", + "d1": "0x9ba270d2", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0xceed56d7", "0x9ba270d2", "0x82caab2d", "0x81ebce0e", "0x12b6ecf1", "0xd0f3fd7c", "0xd872eefe", "0xc158c7bd"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [17, 12, 20, 23, 7, 3, 27, 16], "mul": ["0xf351d601", "0xa3bb398f", "0xb5a09e35", "0x7509c9c1", "0x6bbf31e9", "0xfc849a79", "0xded91851", "0x8d9113d1", "0x0ff15225", "0x3a5bdd41", "0xab533435", "0xe1c55ad5", "0xe6d3bd0d", "0x9d9ffbbd", "0xbb2a3cf3", "0x50a7c08d"], "rc": ["0xc6892460", "0x25b7228a", "0xcd515004", "0x2846527a", "0xa6324241", "0x36e3ec53", "0x82961bac", "0x0f97ba7d", "0xb6f921a9", "0x3ada24e5", "0xde20ab91", "0x5378eeb2", "0x7d161662", "0x89353cc1", "0xb1aa03a2", "0x788acae6"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 1, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xbaab6229", "imm2": "0xed861989", "rot": 26, "bit": 22, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 2, "op": "shfl", "dst": 3, "src": 6, "src2": 2, "imm": "0x5b623116", "imm2": "0xff12e5b2", "rot": 12, "bit": 24, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 3, "op": "sub", "dst": 4, "src": 1, "src2": 1, "imm": "0xe99741c7", "imm2": "0xf5fa5009", "rot": 1, "bit": 21, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 4, "op": "mad", "dst": 2, "src": 0, "src2": 4, "imm": "0x673c2157", "imm2": "0xee02465f", "rot": 20, "bit": 22, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 5, "op": "rotl", "dst": 4, "src": 7, "src2": 7, "imm": "0x946f7818", "imm2": "0x45d3399e", "rot": 9, "bit": 2, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 6, "op": "rotr", "dst": 0, "src": 2, "src2": 4, "imm": "0x5f6a0ed2", "imm2": "0x7043a636", "rot": 19, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 7, "op": "load", "dst": 6, "src": 7, "src2": 1, "imm": "0x5c61dcf7", "imm2": "0x7466aa40", "rot": 19, "bit": 9, "mask": 2, "width": 1, "win": 2, "off": 1}, + {"i": 8, "op": "load", "dst": 1, "src": 4, "src2": 5, "imm": "0x85668475", "imm2": "0xdb8cc483", "rot": 29, "bit": 7, "mask": 4, "width": 1, "win": 1, "off": 1}, + {"i": 9, "op": "load", "dst": 1, "src": 2, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 10, "op": "load", "dst": 7, "src": 0, "src2": 2, "imm": "0xe075297c", "imm2": "0x5779c44c", "rot": 10, "bit": 22, "mask": 2, "width": 1, "win": 1, "off": 1}, + {"i": 11, "op": "load", "dst": 7, "src": 1, "src2": 6, "imm": "0x65aa4311", "imm2": "0x4fe48ea9", "rot": 15, "bit": 9, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 12, "op": "mul", "dst": 5, "src": 4, "src2": 2, "imm": "0x1383d3ad", "imm2": "0xf3094b29", "rot": 8, "bit": 9, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 13, "op": "load", "dst": 7, "src": 6, "src2": 2, "imm": "0xed8a496f", "imm2": "0x3072c3c6", "rot": 28, "bit": 19, "mask": 8, "width": 1, "win": 1, "off": 1}, + {"i": 14, "op": "shfl", "dst": 6, "src": 5, "src2": 0, "imm": "0x8b965b57", "imm2": "0xcfeca6c1", "rot": 12, "bit": 27, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 15, "op": "shfl", "dst": 3, "src": 5, "src2": 3, "imm": "0x877c7586", "imm2": "0xa9cb2a03", "rot": 2, "bit": 29, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 16, "op": "or", "dst": 2, "src": 7, "src2": 3, "imm": "0xb740221a", "imm2": "0x89d38d6d", "rot": 6, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 17, "op": "rotr", "dst": 0, "src": 6, "src2": 1, "imm": "0x26f3ad8a", "imm2": "0x27256f15", "rot": 18, "bit": 5, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 18, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 19, "op": "rotl", "dst": 7, "src": 0, "src2": 5, "imm": "0x849ae6ee", "imm2": "0x02b358f9", "rot": 24, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 20, "op": "add", "dst": 6, "src": 7, "src2": 6, "imm": "0x52334d12", "imm2": "0x8c9f0ef8", "rot": 11, "bit": 23, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 21, "op": "or", "dst": 2, "src": 6, "src2": 5, "imm": "0xb1871e63", "imm2": "0xb2e40191", "rot": 5, "bit": 13, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 22, "op": "mad", "dst": 6, "src": 5, "src2": 4, "imm": "0x97df29e4", "imm2": "0xe60fea84", "rot": 11, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 23, "op": "or", "dst": 1, "src": 0, "src2": 3, "imm": "0x8f30d21d", "imm2": "0x2df685a0", "rot": 31, "bit": 0, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 24, "op": "xor", "dst": 6, "src": 1, "src2": 2, "imm": "0xd2c4025f", "imm2": "0x5269eb4d", "rot": 31, "bit": 21, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 25, "op": "add", "dst": 2, "src": 6, "src2": 5, "imm": "0x659fc3d3", "imm2": "0x9cec0e12", "rot": 6, "bit": 17, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 26, "op": "rotr", "dst": 7, "src": 0, "src2": 1, "imm": "0xd89ef484", "imm2": "0x20be3846", "rot": 12, "bit": 9, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 27, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 28, "op": "mad", "dst": 3, "src": 2, "src2": 4, "imm": "0xc5c46d76", "imm2": "0x700044b5", "rot": 22, "bit": 10, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 29, "op": "load", "dst": 1, "src": 4, "src2": 3, "imm": "0x0fbaf177", "imm2": "0xfff4f2ed", "rot": 20, "bit": 31, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 30, "op": "load", "dst": 2, "src": 3, "src2": 0, "imm": "0xe90eb2e5", "imm2": "0xb0f9eb79", "rot": 27, "bit": 14, "mask": 4, "width": 1, "win": 2, "off": 2}, + {"i": 31, "op": "load", "dst": 1, "src": 5, "src2": 2, "imm": "0x97ba3fc3", "imm2": "0x7894e657", "rot": 3, "bit": 30, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 32, "op": "add", "dst": 7, "src": 2, "src2": 7, "imm": "0x070888a8", "imm2": "0xe403240e", "rot": 2, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 33, "op": "add", "dst": 2, "src": 0, "src2": 5, "imm": "0xf2e46d55", "imm2": "0x29701828", "rot": 31, "bit": 28, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 34, "op": "add", "dst": 2, "src": 3, "src2": 0, "imm": "0x58f75b87", "imm2": "0x343b7aee", "rot": 12, "bit": 14, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 35, "op": "mulhi", "dst": 2, "src": 5, "src2": 6, "imm": "0x5892a9e6", "imm2": "0xc9824c94", "rot": 19, "bit": 26, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 36, "op": "xor", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 37, "op": "mul", "dst": 6, "src": 5, "src2": 7, "imm": "0xccf564a5", "imm2": "0x873ad101", "rot": 7, "bit": 11, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 38, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xcac8f06d", "imm2": "0x6b97c683", "rot": 18, "bit": 28, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 39, "op": "add", "dst": 7, "src": 2, "src2": 4, "imm": "0xb8180e9d", "imm2": "0x32bbd117", "rot": 23, "bit": 19, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 40, "op": "rotr", "dst": 2, "src": 3, "src2": 0, "imm": "0x2d6070bc", "imm2": "0x68ff101e", "rot": 13, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 41, "op": "sub", "dst": 7, "src": 0, "src2": 2, "imm": "0x0daf96ea", "imm2": "0x36f37be1", "rot": 5, "bit": 0, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 42, "op": "add", "dst": 4, "src": 3, "src2": 6, "imm": "0x6ced15b7", "imm2": "0x6df7aed4", "rot": 19, "bit": 4, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 43, "op": "shfl", "dst": 7, "src": 3, "src2": 5, "imm": "0x8ace05f3", "imm2": "0xd378ec12", "rot": 23, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 44, "op": "load", "dst": 0, "src": 7, "src2": 4, "imm": "0xb0607786", "imm2": "0xc4acabbc", "rot": 13, "bit": 7, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 45, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 46, "op": "load", "dst": 3, "src": 1, "src2": 0, "imm": "0x63cc1e4e", "imm2": "0xa1be8118", "rot": 12, "bit": 6, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 47, "op": "load", "dst": 6, "src": 3, "src2": 7, "imm": "0x353f1d79", "imm2": "0x3b2e7456", "rot": 18, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 48, "op": "mulhi", "dst": 4, "src": 2, "src2": 7, "imm": "0x00d8a3cd", "imm2": "0x231866d2", "rot": 21, "bit": 20, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 49, "op": "add", "dst": 5, "src": 0, "src2": 2, "imm": "0xa8bae6df", "imm2": "0xf572bdb9", "rot": 14, "bit": 7, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 50, "op": "shfl", "dst": 0, "src": 7, "src2": 7, "imm": "0x81ef22e1", "imm2": "0x74438fc5", "rot": 28, "bit": 18, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 51, "op": "add", "dst": 6, "src": 0, "src2": 6, "imm": "0x383b9260", "imm2": "0x11e17c61", "rot": 12, "bit": 19, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 52, "op": "load", "dst": 5, "src": 2, "src2": 2, "imm": "0xfb84f451", "imm2": "0x11cd863e", "rot": 21, "bit": 20, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 53, "op": "rotl", "dst": 6, "src": 5, "src2": 4, "imm": "0xb1a7db6b", "imm2": "0x76686b9b", "rot": 12, "bit": 4, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 54, "op": "rotl", "dst": 3, "src": 6, "src2": 3, "imm": "0x6f981f52", "imm2": "0xd99aeba2", "rot": 12, "bit": 27, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 55, "op": "add", "dst": 2, "src": 1, "src2": 2, "imm": "0xac6be8e3", "imm2": "0x18d67dbb", "rot": 26, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 56, "op": "load", "dst": 1, "src": 4, "src2": 0, "imm": "0x7e7f6a00", "imm2": "0x6f0747da", "rot": 25, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 57, "op": "add", "dst": 5, "src": 0, "src2": 4, "imm": "0xf03673fe", "imm2": "0xa75cd60d", "rot": 16, "bit": 12, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 58, "op": "load", "dst": 5, "src": 0, "src2": 2, "imm": "0x227f94a6", "imm2": "0x0e8344f9", "rot": 20, "bit": 10, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 59, "op": "add", "dst": 1, "src": 4, "src2": 3, "imm": "0xdecd4794", "imm2": "0x8dfb96bb", "rot": 21, "bit": 7, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 60, "op": "mulhi", "dst": 3, "src": 2, "src2": 2, "imm": "0x0dd268e0", "imm2": "0x53034ca9", "rot": 1, "bit": 8, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 61, "op": "or", "dst": 6, "src": 4, "src2": 7, "imm": "0x3a45a321", "imm2": "0x9bc59a5f", "rot": 25, "bit": 11, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 62, "op": "shfl", "dst": 5, "src": 4, "src2": 2, "imm": "0x8f229cc1", "imm2": "0xcaac64a2", "rot": 17, "bit": 13, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0xb2574178", "imm2": "0xcbcc798d", "rot": 28, "bit": 0, "mask": 16, "width": 1, "win": 1, "off": 1} + ] +} diff --git a/proto-cuda/packs-ca2-era/era-4/program.metal b/proto-cuda/packs-ca2-era/era-4/program.metal new file mode 100644 index 00000000..decb0d37 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/program.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-4/program_bound.metal b/proto-cuda/packs-ca2-era/era-4/program_bound.metal new file mode 100644 index 00000000..cbb26498 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/program_bound.metal @@ -0,0 +1,111 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x4d38603du, 10u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x4d38603du, 10u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x4d38603du, 10u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-4/seeds.txt b/proto-cuda/packs-ca2-era/era-4/seeds.txt new file mode 100644 index 00000000..ff7b31ee --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/seeds.txt @@ -0,0 +1,5 @@ +epoch_seed_hex edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 +day_seed_hex 69676e65756d2d6461792ffa50000000000000 +epoch_index 0 +day_index 20730 +daa_score 0 diff --git a/proto-cuda/packs-ca2-era/era-4/vectors.h b/proto-cuda/packs-ca2-era/era-4/vectors.h new file mode 100644 index 00000000..1f68a0f7 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/vectors.h @@ -0,0 +1,57 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0x8e32500ee72aa1f8ull, 0x710f1522fd52e28eull, 0x62fd41712fd5bd3eull, 0x3837ee0ed93c4015ull, 0x0338e814e0e8a4faull, 0xaab705ee7514af71ull, 0x4a3e143121ed41a6ull, 0xe843b01bee434ef6ull, + 0x4f9974ee1f331bc6ull, 0x1e089106d4d04581ull, 0x8a986409754a08b8ull, 0x54bf0b893560e130ull, 0x1346ddc8074de7ddull, 0x5fdc54666289e864ull, 0xe9ed965ed22a9ea1ull, 0x7915337fc840db87ull, + 0xaab900aea6b7c2cfull, 0x81d768fa492b1cf2ull, 0xfd62bcf1021629dfull, 0x6280dc76e5125eddull, 0x17055f35fc9a4ed3ull, 0xead760de60ae3e84ull, 0x14ea3ac017d95d57ull, 0x5f09e499a0096d83ull, + 0xdf7c24624de53e02ull, 0x10e39b014ea6d6c6ull, 0xfcfac36ecf54fb8cull, 0x8b510ae1be613404ull, 0xa9a07c9f9fa58801ull, 0xba88c22112b80fd0ull, 0x673c5bf0fbb48d35ull, 0xeef7f536b1e0a810ull + }, + { // base nonce 4096 + 0x88df895d8d8539ffull, 0x052fb10eab66d76dull, 0x534663c60e7de2e5ull, 0xc76cb6763d97b1beull, 0x24c28c52fdbc5c86ull, 0x46a9b9ee877a5764ull, 0xcc000e068d59a8e0ull, 0x7e634381ee2664e7ull, + 0x0e8d46d76bb5f04aull, 0x2a7caab33aaf260full, 0x11805382c57768fcull, 0x1050d656bfd66847ull, 0x6dac200ef67b302full, 0xfcebdae4acc594c2ull, 0x59ddcc381267cfb0ull, 0x15e9e31cd7347f92ull, + 0xad4fb91f9c31a9d1ull, 0x01bdb6cd682f2d3aull, 0xd94caecd87d9bc10ull, 0x3bc7dd917ded4591ull, 0x1dc97ba9f06c33cbull, 0xb503609bab3d4397ull, 0xa6001b284f75c5e4ull, 0x56981fe3d9beddccull, + 0xc1e3cf75a47fd944ull, 0x0d44d93176a1ab11ull, 0x31d24d65e0706657ull, 0xff7363ff80342341ull, 0x8060c156f3d360beull, 0xa75837c717e7eadaull, 0x86f2a525a3cf8d13ull, 0xac0d7a25d491c048ull + }, + { // base nonce 1000000 + 0x77e307c1d3c4a45dull, 0xc46db2c1181e7d29ull, 0x60d540ae23190d93ull, 0x03a6005b827a3b1eull, 0x549bac8bd1c1e201ull, 0x780dfc346d705778ull, 0xcfcbebfdbb68e6f4ull, 0x3c5ed8998530360dull, + 0x2b88fe019717d013ull, 0xb70795cd85b32d67ull, 0x6decdbef0be191d7ull, 0x8f22242400f2cb78ull, 0xbbd28d1419989b8aull, 0xe16cffacddfb8292ull, 0xd8248122cb40a2a3ull, 0x2522a2642fae7251ull, + 0xfeeea1da8b23ad1bull, 0x3be311612545a761ull, 0x4ddb4b56a8275591ull, 0x7cb9edbe2572ab8bull, 0xe9a8ab8f94c06690ull, 0xd853e9b7bc76760dull, 0x03b2b843379ca0e4ull, 0x1954de4a7d95cb4full, + 0xad25e818a9aa212bull, 0x14be913efb5627feull, 0x2006c5d0f1ee99feull, 0xb1ce75c39bd2f557ull, 0xcd2f842898802fecull, 0xf44486cc9aa01340ull, 0x09a89237e25250eeull, 0xb342a4880eb580b3ull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0x3dd50b1fu, 0x93123701u, 0xcdd88b45u, 0x2c37c6b0u, 0x48edec90u, 0x3a7d2407u, 0xb03a0ac0u, 0x10e2c6a5u, + 0xf46429c0u, 0x4b1f3b3au, 0xd69dcaffu, 0xa48808fdu, 0xc8b2db13u, 0xc555587fu, 0x53042758u, 0x1a71bda6u +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xf7b7180eu; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0x7f292d0cu, 0x73dd96cfu, 0x202b4ad2u, 0xf6d8038fu, 0x7835659bu, 0x03a3a00du, 0x022d1e04u, 0x89957f84u, 0xe3451874u, 0xc82651dau, 0x29759e8cu, 0xa7a8cca3u, 0x45098d58u, 0x60d618eau, 0xf42d0e2cu, 0xc3546ab2u, 0xa7de6336u, 0xd592fdf4u, 0x083d78d1u, 0x64bd8824u, 0x8a66c547u, 0x8bc97d55u, 0x1411385du, 0x485f6fc0u, 0xc8142e6cu, 0xd8c1b7ebu, 0x6671b76bu, 0x63d4b24eu, 0xd622091bu, 0xa6821c04u, 0x8c2949d6u, 0x6b064395u, 0x5fa8e992u, 0xf49195b8u, 0x990254beu, 0xc5e6169du, 0xfff9c259u, 0x0e3c45dau, 0x42d69754u, 0xe23a1172u, 0x6d7d42e0u, 0xc30766efu, 0xe8abd145u, 0x11348b7cu, 0x837ccca1u, 0xe65b14c7u, 0x550f2d75u, 0xfedbcd25u, 0xb7430a6au, 0x5bbbdc45u, 0x2f81bc7du, 0xebbd5147u, 0xe991ee42u, 0x74d6d9abu, 0xc0eb32eau, 0x589a8daau, 0xd50d6a45u, 0x3f0ec2ccu, 0x31377e5eu, 0xe22a256fu, 0x52e144d0u, 0xa3fd1811u, 0xd3be259du, 0xc9bbc553u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0xebd9055cu, 0x7eaf6b21u, 0x9b610855u, 0x134a8562u, 0xb10059bau, 0x7f459e58u, 0x8f3c7873u, 0x3523e609u, + 0x75b8f4cau, 0x750d31b4u, 0x0dae0782u, 0x26f10015u, 0x7ba87a41u, 0x0efd8543u, 0x691d6368u, 0x8929d967u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x51474edfu, 0xc3cfba93u, 0xf21454cfu, 0x9b79baacu, 0x4d4fce23u, 0xd701dfd5u, 0x37357ab3u, 0x1be693fau, + 0xa701cb3bu, 0x7467c620u, 0x428184e8u, 0xf4010df0u, 0xe33aa88fu, 0xc78d6d62u, 0xae9ba9c0u, 0xf4bba97eu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x448274a57f508cbcull; diff --git a/proto-cuda/packs-ca2-era/era-4/vectors.json b/proto-cuda/packs-ca2-era/era-4/vectors.json new file mode 100644 index 00000000..16420165 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-4/vectors.json @@ -0,0 +1,36 @@ +{ + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0x8e32500ee72aa1f8", "0x710f1522fd52e28e", "0x62fd41712fd5bd3e", "0x3837ee0ed93c4015", "0x0338e814e0e8a4fa", "0xaab705ee7514af71", "0x4a3e143121ed41a6", "0xe843b01bee434ef6", + "0x4f9974ee1f331bc6", "0x1e089106d4d04581", "0x8a986409754a08b8", "0x54bf0b893560e130", "0x1346ddc8074de7dd", "0x5fdc54666289e864", "0xe9ed965ed22a9ea1", "0x7915337fc840db87", + "0xaab900aea6b7c2cf", "0x81d768fa492b1cf2", "0xfd62bcf1021629df", "0x6280dc76e5125edd", "0x17055f35fc9a4ed3", "0xead760de60ae3e84", "0x14ea3ac017d95d57", "0x5f09e499a0096d83", + "0xdf7c24624de53e02", "0x10e39b014ea6d6c6", "0xfcfac36ecf54fb8c", "0x8b510ae1be613404", "0xa9a07c9f9fa58801", "0xba88c22112b80fd0", "0x673c5bf0fbb48d35", "0xeef7f536b1e0a810" + ]}, + {"base_nonce": 4096, "expected": [ + "0x88df895d8d8539ff", "0x052fb10eab66d76d", "0x534663c60e7de2e5", "0xc76cb6763d97b1be", "0x24c28c52fdbc5c86", "0x46a9b9ee877a5764", "0xcc000e068d59a8e0", "0x7e634381ee2664e7", + "0x0e8d46d76bb5f04a", "0x2a7caab33aaf260f", "0x11805382c57768fc", "0x1050d656bfd66847", "0x6dac200ef67b302f", "0xfcebdae4acc594c2", "0x59ddcc381267cfb0", "0x15e9e31cd7347f92", + "0xad4fb91f9c31a9d1", "0x01bdb6cd682f2d3a", "0xd94caecd87d9bc10", "0x3bc7dd917ded4591", "0x1dc97ba9f06c33cb", "0xb503609bab3d4397", "0xa6001b284f75c5e4", "0x56981fe3d9beddcc", + "0xc1e3cf75a47fd944", "0x0d44d93176a1ab11", "0x31d24d65e0706657", "0xff7363ff80342341", "0x8060c156f3d360be", "0xa75837c717e7eada", "0x86f2a525a3cf8d13", "0xac0d7a25d491c048" + ]}, + {"base_nonce": 1000000, "expected": [ + "0x77e307c1d3c4a45d", "0xc46db2c1181e7d29", "0x60d540ae23190d93", "0x03a6005b827a3b1e", "0x549bac8bd1c1e201", "0x780dfc346d705778", "0xcfcbebfdbb68e6f4", "0x3c5ed8998530360d", + "0x2b88fe019717d013", "0xb70795cd85b32d67", "0x6decdbef0be191d7", "0x8f22242400f2cb78", "0xbbd28d1419989b8a", "0xe16cffacddfb8292", "0xd8248122cb40a2a3", "0x2522a2642fae7251", + "0xfeeea1da8b23ad1b", "0x3be311612545a761", "0x4ddb4b56a8275591", "0x7cb9edbe2572ab8b", "0xe9a8ab8f94c06690", "0xd853e9b7bc76760d", "0x03b2b843379ca0e4", "0x1954de4a7d95cb4f", + "0xad25e818a9aa212b", "0x14be913efb5627fe", "0x2006c5d0f1ee99fe", "0xb1ce75c39bd2f557", "0xcd2f842898802fec", "0xf44486cc9aa01340", "0x09a89237e25250ee", "0xb342a4880eb580b3" + ]} + ], + "dataset_head": ["0x3dd50b1f", "0x93123701", "0xcdd88b45", "0x2c37c6b0", "0x48edec90", "0x3a7d2407", "0xb03a0ac0", "0x10e2c6a5", "0xf46429c0", "0x4b1f3b3a", "0xd69dcaff", "0xa48808fd", "0xc8b2db13", "0xc555587f", "0x53042758", "0x1a71bda6"], + "dataset_last_index": 268435455, + "dataset_last": "0xf7b7180e", + "dataset_samples": [{"index": 59471966, "value": "0x7f292d0c"}, {"index": 217795994, "value": "0x73dd96cf"}, {"index": 208353206, "value": "0x202b4ad2"}, {"index": 42483309, "value": "0xf6d8038f"}, {"index": 172547758, "value": "0x7835659b"}, {"index": 148076330, "value": "0x03a3a00d"}, {"index": 183853158, "value": "0x022d1e04"}, {"index": 214389424, "value": "0x89957f84"}, {"index": 267488061, "value": "0xe3451874"}, {"index": 169781097, "value": "0xc82651da"}, {"index": 184093494, "value": "0x29759e8c"}, {"index": 153880993, "value": "0xa7a8cca3"}, {"index": 84977930, "value": "0x45098d58"}, {"index": 46426879, "value": "0x60d618ea"}, {"index": 3093825, "value": "0xf42d0e2c"}, {"index": 225364072, "value": "0xc3546ab2"}, {"index": 44593546, "value": "0xa7de6336"}, {"index": 260713159, "value": "0xd592fdf4"}, {"index": 168250303, "value": "0x083d78d1"}, {"index": 52384140, "value": "0x64bd8824"}, {"index": 223401610, "value": "0x8a66c547"}, {"index": 45554030, "value": "0x8bc97d55"}, {"index": 95410555, "value": "0x1411385d"}, {"index": 175039924, "value": "0x485f6fc0"}, {"index": 79171087, "value": "0xc8142e6c"}, {"index": 267580473, "value": "0xd8c1b7eb"}, {"index": 24168642, "value": "0x6671b76b"}, {"index": 37981670, "value": "0x63d4b24e"}, {"index": 171551130, "value": "0xd622091b"}, {"index": 195559979, "value": "0xa6821c04"}, {"index": 204611762, "value": "0x8c2949d6"}, {"index": 140997658, "value": "0x6b064395"}, {"index": 138925853, "value": "0x5fa8e992"}, {"index": 86637313, "value": "0xf49195b8"}, {"index": 20736778, "value": "0x990254be"}, {"index": 219665210, "value": "0xc5e6169d"}, {"index": 160430336, "value": "0xfff9c259"}, {"index": 264654675, "value": "0x0e3c45da"}, {"index": 8013395, "value": "0x42d69754"}, {"index": 228945585, "value": "0xe23a1172"}, {"index": 213884386, "value": "0x6d7d42e0"}, {"index": 104419827, "value": "0xc30766ef"}, {"index": 44185464, "value": "0xe8abd145"}, {"index": 142737231, "value": "0x11348b7c"}, {"index": 99284897, "value": "0x837ccca1"}, {"index": 132475900, "value": "0xe65b14c7"}, {"index": 61861762, "value": "0x550f2d75"}, {"index": 132056166, "value": "0xfedbcd25"}, {"index": 262388043, "value": "0xb7430a6a"}, {"index": 91878046, "value": "0x5bbbdc45"}, {"index": 117353561, "value": "0x2f81bc7d"}, {"index": 124768597, "value": "0xebbd5147"}, {"index": 71352993, "value": "0xe991ee42"}, {"index": 190698941, "value": "0x74d6d9ab"}, {"index": 46055428, "value": "0xc0eb32ea"}, {"index": 55281366, "value": "0x589a8daa"}, {"index": 165145231, "value": "0xd50d6a45"}, {"index": 106810753, "value": "0x3f0ec2cc"}, {"index": 171985651, "value": "0x31377e5e"}, {"index": 232085256, "value": "0xe22a256f"}, {"index": 159510492, "value": "0x52e144d0"}, {"index": 40072060, "value": "0xa3fd1811"}, {"index": 209107596, "value": "0xd3be259d"}, {"index": 39023794, "value": "0xc9bbc553"}], + "cache_head": ["0xebd9055c", "0x7eaf6b21", "0x9b610855", "0x134a8562", "0xb10059ba", "0x7f459e58", "0x8f3c7873", "0x3523e609", "0x75b8f4ca", "0x750d31b4", "0x0dae0782", "0x26f10015", "0x7ba87a41", "0x0efd8543", "0x691d6368", "0x8929d967"], + "cache_last_line": ["0x51474edf", "0xc3cfba93", "0xf21454cf", "0x9b79baac", "0x4d4fce23", "0xd701dfd5", "0x37357ab3", "0x1be693fa", "0xa701cb3b", "0x7467c620", "0x428184e8", "0xf4010df0", "0xe33aa88f", "0xc78d6d62", "0xae9ba9c0", "0xf4bba97e"], + "cache_fnv1a64": "0x448274a57f508cbc" +} diff --git a/proto-cuda/packs-ca2-era/era-5/kernel.cl b/proto-cuda/packs-ca2-era/era-5/kernel.cl new file mode 100644 index 00000000..c5ccba30 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/kernel.cl @@ -0,0 +1,281 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 10 13 of w. +static inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 10u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000003ffu) | ((w >> 11u) << 10u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 10u) << 11u) | (w & 0x000003ffu) | (((j >> 2u) & 1u) << 10u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-era/era-5/kernel.cu b/proto-cuda/packs-ca2-era/era-5/kernel.cu new file mode 100644 index 00000000..fd4fdd83 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/kernel.cu @@ -0,0 +1,163 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + for (uint32_t i = 0u; i < 16u; ++i) ds[(size_t)mh_addr(t, i)] = s[i]; + } +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-era/era-5/kernel_bound.cl b/proto-cuda/packs-ca2-era/era-5/kernel_bound.cl new file mode 100644 index 00000000..44b16538 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/kernel_bound.cl @@ -0,0 +1,375 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 10 13 of w. +static inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 10u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000003ffu) | ((w >> 11u) << 10u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 10u) << 11u) | (w & 0x000003ffu) | (((j >> 2u) & 1u) << 10u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-5/kernel_bound.cu b/proto-cuda/packs-ca2-era/era-5/kernel_bound.cu new file mode 100644 index 00000000..ab4546ad --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/kernel_bound.cu @@ -0,0 +1,123 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-era/era-5/memhard.h b/proto-cuda/packs-ca2-era/era-5/memhard.h new file mode 100644 index 00000000..aa6d5585 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/memhard.h @@ -0,0 +1,112 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 10 13 of w. +IGNEUM_HD uint32_t mh_j(uint32_t w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 10u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +IGNEUM_HD uint32_t mh_t(uint32_t w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000003ffu) | ((w >> 11u) << 10u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +IGNEUM_HD uint32_t mh_addr(uint32_t t, uint32_t j) { uint32_t w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 10u) << 11u) | (w & 0x000003ffu) | (((j >> 2u) & 1u) << 10u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } diff --git a/proto-cuda/packs-ca2-era/era-5/memhard.metal b/proto-cuda/packs-ca2-era/era-5/memhard.metal new file mode 100644 index 00000000..45980977 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/memhard.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 10 13 of w. +inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 10u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x000003ffu) | ((w >> 11u) << 10u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 10u) << 11u) | (w & 0x000003ffu) | (((j >> 2u) & 1u) << 10u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + for (uint i = 0u; i < 16u; ++i) dataset[mh_addr(gid, i)] = s[i]; +} diff --git a/proto-cuda/packs-ca2-era/era-5/program.h b/proto-cuda/packs-ca2-era/era-5/program.h new file mode 100644 index 00000000..b3a4969d --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/program.h @@ -0,0 +1,76 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_SEED_BYTES_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" +#define IGNEUM_GENERATOR 3 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x73bcbfe8ccf988f1ull +#define IGNEUM_DAY_STRING "bytes:69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY_BYTES_HEX "69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY0 0xceed56d7u +#define IGNEUM_DAY1 0x9ba270d2u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 add=15 shfl=6 mad=4 or=4 rotl=4 rotr=4 xor=4 mulhi=3 mul=2 sub=2" +// Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that +// runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). +#define IGNEUM_PROGRAM_CLASS "v3" +#define IGNEUM_ERA_SEED_HEX "7f450623297a954f493ca08abcdfe7142bb753900abcee38256266007b4b48b7" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "w4-eraf897c84e" +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Era layout (5 October 2026, docs/plans/era-layout.md): NOT the lottery hash. Every dataset load reads +// idx = ((rotl(src * STRIDE_MUL, STRIDE_ROT) & window mask) | window offset) & MASK; the window of a load site is the +// dataset, a half or a quarter of it (IGNEUM_ERA_WINDOWS: site:shrink:offset); dataset word w holds word j(w) of item +// t(w) with j's bits at the INTERLEAVE positions (memhard.h: mh_t, mh_j, mh_addr). +#define IGNEUM_ERA_LABEL "f897c84e" +#define IGNEUM_ERA_SEED_WORDS { 0xf897c84eu, 0xa4296ce5u, 0x3fb7ee16u, 0xd2354d94u, 0x82937d5fu, 0xe8f3ff88u, 0xe41af6f4u, 0x4f10d757u } +#define IGNEUM_ERA_ALLOWED_WIDTHS { 1, 0, 0 } // words, ascending, 0 = unused; one entry pins the width +#define IGNEUM_ERA_WIDTH_WORDS 1 +#define IGNEUM_ERA_STRIDE_MUL 0x03ac37adu +#define IGNEUM_ERA_STRIDE_ROT 22 +#define IGNEUM_ERA_INTERLEAVE { 0, 2, 10, 13 } +#define IGNEUM_ERA_WINDOWS "7:2:1 8:1:1 9:1:1 10:1:1 11:0:0 13:1:1 29:0:0 30:2:2 31:1:1 44:1:1 46:2:0 47:0:0 52:0:0 56:0:0 58:2:0 63:1:1" +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u } +#define IGNEUM_KEY_INIT { 0xceed56d7u, 0x9ba270d2u, 0x82caab2du, 0x81ebce0eu, 0x12b6ecf1u, 0xd0f3fd7cu, 0xd872eefeu, 0xc158c7bdu } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 17u, 12u, 20u, 23u, 7u, 3u, 27u, 16u } +#define IGNEUM_MIX_MUL_INIT { 0xf351d601u, 0xa3bb398fu, 0xb5a09e35u, 0x7509c9c1u, 0x6bbf31e9u, 0xfc849a79u, 0xded91851u, 0x8d9113d1u, 0x0ff15225u, 0x3a5bdd41u, 0xab533435u, 0xe1c55ad5u, 0xe6d3bd0du, 0x9d9ffbbdu, 0xbb2a3cf3u, 0x50a7c08du } +#define IGNEUM_MIX_RC_INIT { 0xc6892460u, 0x25b7228au, 0xcd515004u, 0x2846527au, 0xa6324241u, 0x36e3ec53u, 0x82961bacu, 0x0f97ba7du, 0xb6f921a9u, 0x3ada24e5u, 0xde20ab91u, 0x5378eeb2u, 0x7d161662u, 0x89353cc1u, 0xb1aa03a2u, 0x788acae6u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-era/era-5/program.json b/proto-cuda/packs-ca2-era/era-5/program.json new file mode 100644 index 00000000..9e52af8e --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/program.json @@ -0,0 +1,143 @@ +{ + "format": "igneum-program-pack-3", + "generator": 3, + "attempt": 0, + "program_id": "0x73bcbfe8ccf988f1", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07", + "seed_words": ["0x667d0fbd", "0x7b8e5963", "0x31c67e5e", "0x4529ddc6", "0xef19d6d8", "0xaccf6211", "0xda0aed32", "0xabc6df31"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "program_class": "v3", + "era_seed_bytes": "7f450623297a954f493ca08abcdfe7142bb753900abcee38256266007b4b48b7", + "load_class": "w4-eraf897c84e", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "era": { + "label": "f897c84e", + "seed_words": ["0xf897c84e", "0xa4296ce5", "0x3fb7ee16", "0xd2354d94", "0x82937d5f", "0xe8f3ff88", "0xe41af6f4", "0x4f10d757"], + "draw": "docs/plans/era-layout.md 1.1: SplitMix64 seeded with seed_words[0] | seed_words[1] << 32 of seed_words_from_bytes('igneum-era/' || n_le64 || E_n); width = allowed[below(|allowed|)], stride_mul = low32(next()) | 1, stride_rot = 1 + below(31), then four next() draws for a partial Fisher-Yates over positions log2(W)..15 of which 4 - log2(W) are used", + "allowed_widths": [1], + "width_words": 1, + "stride_mul": "0x03ac37ad", + "stride_rot": 22, + "interleave": [0, 2, 10, 13], + "address": "y = rotl(src * stride_mul, stride_rot); k = min(win, D - 26); idx = ((y & (mask >> k)) | ((off & (2^k - 1)) << (D - k))) & mask; a wide load aligns idx down to W words", + "windows": "per instruction, after the width roll: win = below(3), off = low32(next()) & (2^win - 1); used on a load slot (the instruction's win and off fields)", + "dataset_word": "dataset[w] = item(t(w))[j(w)]: j(w) gathers the bits of w at the interleave positions, t(w) is w with those bits removed", + "program_id_suffix": "'era/' || allowed[3] || width_words || stride_mul_le32 || stride_rot_le32 || interleave[4]" + }, + "op_mix": {"load": 16, "add": 15, "shfl": 6, "mad": 4, "or": 4, "rotl": 4, "rotr": 4, "xor": 4, "mulhi": 3, "mul": 2, "sub": 2}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "day_bytes": "69676e65756d2d6461792ffa50000000000000", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0xceed56d7", + "d1": "0x9ba270d2", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0xceed56d7", "0x9ba270d2", "0x82caab2d", "0x81ebce0e", "0x12b6ecf1", "0xd0f3fd7c", "0xd872eefe", "0xc158c7bd"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [17, 12, 20, 23, 7, 3, 27, 16], "mul": ["0xf351d601", "0xa3bb398f", "0xb5a09e35", "0x7509c9c1", "0x6bbf31e9", "0xfc849a79", "0xded91851", "0x8d9113d1", "0x0ff15225", "0x3a5bdd41", "0xab533435", "0xe1c55ad5", "0xe6d3bd0d", "0x9d9ffbbd", "0xbb2a3cf3", "0x50a7c08d"], "rc": ["0xc6892460", "0x25b7228a", "0xcd515004", "0x2846527a", "0xa6324241", "0x36e3ec53", "0x82961bac", "0x0f97ba7d", "0xb6f921a9", "0x3ada24e5", "0xde20ab91", "0x5378eeb2", "0x7d161662", "0x89353cc1", "0xb1aa03a2", "0x788acae6"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 1, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xbaab6229", "imm2": "0xed861989", "rot": 26, "bit": 22, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 2, "op": "shfl", "dst": 3, "src": 6, "src2": 2, "imm": "0x5b623116", "imm2": "0xff12e5b2", "rot": 12, "bit": 24, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 3, "op": "sub", "dst": 4, "src": 1, "src2": 1, "imm": "0xe99741c7", "imm2": "0xf5fa5009", "rot": 1, "bit": 21, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 4, "op": "mad", "dst": 2, "src": 0, "src2": 4, "imm": "0x673c2157", "imm2": "0xee02465f", "rot": 20, "bit": 22, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 5, "op": "rotl", "dst": 4, "src": 7, "src2": 7, "imm": "0x946f7818", "imm2": "0x45d3399e", "rot": 9, "bit": 2, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 6, "op": "rotr", "dst": 0, "src": 2, "src2": 4, "imm": "0x5f6a0ed2", "imm2": "0x7043a636", "rot": 19, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 7, "op": "load", "dst": 6, "src": 7, "src2": 1, "imm": "0x5c61dcf7", "imm2": "0x7466aa40", "rot": 19, "bit": 9, "mask": 2, "width": 1, "win": 2, "off": 1}, + {"i": 8, "op": "load", "dst": 1, "src": 4, "src2": 5, "imm": "0x85668475", "imm2": "0xdb8cc483", "rot": 29, "bit": 7, "mask": 4, "width": 1, "win": 1, "off": 1}, + {"i": 9, "op": "load", "dst": 1, "src": 2, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 10, "op": "load", "dst": 7, "src": 0, "src2": 2, "imm": "0xe075297c", "imm2": "0x5779c44c", "rot": 10, "bit": 22, "mask": 2, "width": 1, "win": 1, "off": 1}, + {"i": 11, "op": "load", "dst": 7, "src": 1, "src2": 6, "imm": "0x65aa4311", "imm2": "0x4fe48ea9", "rot": 15, "bit": 9, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 12, "op": "mul", "dst": 5, "src": 4, "src2": 2, "imm": "0x1383d3ad", "imm2": "0xf3094b29", "rot": 8, "bit": 9, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 13, "op": "load", "dst": 7, "src": 6, "src2": 2, "imm": "0xed8a496f", "imm2": "0x3072c3c6", "rot": 28, "bit": 19, "mask": 8, "width": 1, "win": 1, "off": 1}, + {"i": 14, "op": "shfl", "dst": 6, "src": 5, "src2": 0, "imm": "0x8b965b57", "imm2": "0xcfeca6c1", "rot": 12, "bit": 27, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 15, "op": "shfl", "dst": 3, "src": 5, "src2": 3, "imm": "0x877c7586", "imm2": "0xa9cb2a03", "rot": 2, "bit": 29, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 16, "op": "or", "dst": 2, "src": 7, "src2": 3, "imm": "0xb740221a", "imm2": "0x89d38d6d", "rot": 6, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 17, "op": "rotr", "dst": 0, "src": 6, "src2": 1, "imm": "0x26f3ad8a", "imm2": "0x27256f15", "rot": 18, "bit": 5, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 18, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 19, "op": "rotl", "dst": 7, "src": 0, "src2": 5, "imm": "0x849ae6ee", "imm2": "0x02b358f9", "rot": 24, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 20, "op": "add", "dst": 6, "src": 7, "src2": 6, "imm": "0x52334d12", "imm2": "0x8c9f0ef8", "rot": 11, "bit": 23, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 21, "op": "or", "dst": 2, "src": 6, "src2": 5, "imm": "0xb1871e63", "imm2": "0xb2e40191", "rot": 5, "bit": 13, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 22, "op": "mad", "dst": 6, "src": 5, "src2": 4, "imm": "0x97df29e4", "imm2": "0xe60fea84", "rot": 11, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 23, "op": "or", "dst": 1, "src": 0, "src2": 3, "imm": "0x8f30d21d", "imm2": "0x2df685a0", "rot": 31, "bit": 0, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 24, "op": "xor", "dst": 6, "src": 1, "src2": 2, "imm": "0xd2c4025f", "imm2": "0x5269eb4d", "rot": 31, "bit": 21, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 25, "op": "add", "dst": 2, "src": 6, "src2": 5, "imm": "0x659fc3d3", "imm2": "0x9cec0e12", "rot": 6, "bit": 17, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 26, "op": "rotr", "dst": 7, "src": 0, "src2": 1, "imm": "0xd89ef484", "imm2": "0x20be3846", "rot": 12, "bit": 9, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 27, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 28, "op": "mad", "dst": 3, "src": 2, "src2": 4, "imm": "0xc5c46d76", "imm2": "0x700044b5", "rot": 22, "bit": 10, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 29, "op": "load", "dst": 1, "src": 4, "src2": 3, "imm": "0x0fbaf177", "imm2": "0xfff4f2ed", "rot": 20, "bit": 31, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 30, "op": "load", "dst": 2, "src": 3, "src2": 0, "imm": "0xe90eb2e5", "imm2": "0xb0f9eb79", "rot": 27, "bit": 14, "mask": 4, "width": 1, "win": 2, "off": 2}, + {"i": 31, "op": "load", "dst": 1, "src": 5, "src2": 2, "imm": "0x97ba3fc3", "imm2": "0x7894e657", "rot": 3, "bit": 30, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 32, "op": "add", "dst": 7, "src": 2, "src2": 7, "imm": "0x070888a8", "imm2": "0xe403240e", "rot": 2, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 33, "op": "add", "dst": 2, "src": 0, "src2": 5, "imm": "0xf2e46d55", "imm2": "0x29701828", "rot": 31, "bit": 28, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 34, "op": "add", "dst": 2, "src": 3, "src2": 0, "imm": "0x58f75b87", "imm2": "0x343b7aee", "rot": 12, "bit": 14, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 35, "op": "mulhi", "dst": 2, "src": 5, "src2": 6, "imm": "0x5892a9e6", "imm2": "0xc9824c94", "rot": 19, "bit": 26, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 36, "op": "xor", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 37, "op": "mul", "dst": 6, "src": 5, "src2": 7, "imm": "0xccf564a5", "imm2": "0x873ad101", "rot": 7, "bit": 11, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 38, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xcac8f06d", "imm2": "0x6b97c683", "rot": 18, "bit": 28, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 39, "op": "add", "dst": 7, "src": 2, "src2": 4, "imm": "0xb8180e9d", "imm2": "0x32bbd117", "rot": 23, "bit": 19, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 40, "op": "rotr", "dst": 2, "src": 3, "src2": 0, "imm": "0x2d6070bc", "imm2": "0x68ff101e", "rot": 13, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 41, "op": "sub", "dst": 7, "src": 0, "src2": 2, "imm": "0x0daf96ea", "imm2": "0x36f37be1", "rot": 5, "bit": 0, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 42, "op": "add", "dst": 4, "src": 3, "src2": 6, "imm": "0x6ced15b7", "imm2": "0x6df7aed4", "rot": 19, "bit": 4, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 43, "op": "shfl", "dst": 7, "src": 3, "src2": 5, "imm": "0x8ace05f3", "imm2": "0xd378ec12", "rot": 23, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 44, "op": "load", "dst": 0, "src": 7, "src2": 4, "imm": "0xb0607786", "imm2": "0xc4acabbc", "rot": 13, "bit": 7, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 45, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 46, "op": "load", "dst": 3, "src": 1, "src2": 0, "imm": "0x63cc1e4e", "imm2": "0xa1be8118", "rot": 12, "bit": 6, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 47, "op": "load", "dst": 6, "src": 3, "src2": 7, "imm": "0x353f1d79", "imm2": "0x3b2e7456", "rot": 18, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 48, "op": "mulhi", "dst": 4, "src": 2, "src2": 7, "imm": "0x00d8a3cd", "imm2": "0x231866d2", "rot": 21, "bit": 20, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 49, "op": "add", "dst": 5, "src": 0, "src2": 2, "imm": "0xa8bae6df", "imm2": "0xf572bdb9", "rot": 14, "bit": 7, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 50, "op": "shfl", "dst": 0, "src": 7, "src2": 7, "imm": "0x81ef22e1", "imm2": "0x74438fc5", "rot": 28, "bit": 18, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 51, "op": "add", "dst": 6, "src": 0, "src2": 6, "imm": "0x383b9260", "imm2": "0x11e17c61", "rot": 12, "bit": 19, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 52, "op": "load", "dst": 5, "src": 2, "src2": 2, "imm": "0xfb84f451", "imm2": "0x11cd863e", "rot": 21, "bit": 20, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 53, "op": "rotl", "dst": 6, "src": 5, "src2": 4, "imm": "0xb1a7db6b", "imm2": "0x76686b9b", "rot": 12, "bit": 4, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 54, "op": "rotl", "dst": 3, "src": 6, "src2": 3, "imm": "0x6f981f52", "imm2": "0xd99aeba2", "rot": 12, "bit": 27, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 55, "op": "add", "dst": 2, "src": 1, "src2": 2, "imm": "0xac6be8e3", "imm2": "0x18d67dbb", "rot": 26, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 56, "op": "load", "dst": 1, "src": 4, "src2": 0, "imm": "0x7e7f6a00", "imm2": "0x6f0747da", "rot": 25, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 57, "op": "add", "dst": 5, "src": 0, "src2": 4, "imm": "0xf03673fe", "imm2": "0xa75cd60d", "rot": 16, "bit": 12, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 58, "op": "load", "dst": 5, "src": 0, "src2": 2, "imm": "0x227f94a6", "imm2": "0x0e8344f9", "rot": 20, "bit": 10, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 59, "op": "add", "dst": 1, "src": 4, "src2": 3, "imm": "0xdecd4794", "imm2": "0x8dfb96bb", "rot": 21, "bit": 7, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 60, "op": "mulhi", "dst": 3, "src": 2, "src2": 2, "imm": "0x0dd268e0", "imm2": "0x53034ca9", "rot": 1, "bit": 8, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 61, "op": "or", "dst": 6, "src": 4, "src2": 7, "imm": "0x3a45a321", "imm2": "0x9bc59a5f", "rot": 25, "bit": 11, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 62, "op": "shfl", "dst": 5, "src": 4, "src2": 2, "imm": "0x8f229cc1", "imm2": "0xcaac64a2", "rot": 17, "bit": 13, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0xb2574178", "imm2": "0xcbcc798d", "rot": 28, "bit": 0, "mask": 16, "width": 1, "win": 1, "off": 1} + ] +} diff --git a/proto-cuda/packs-ca2-era/era-5/program.metal b/proto-cuda/packs-ca2-era/era-5/program.metal new file mode 100644 index 00000000..a14632dd --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/program.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-5/program_bound.metal b/proto-cuda/packs-ca2-era/era-5/program_bound.metal new file mode 100644 index 00000000..c1fe03c6 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/program_bound.metal @@ -0,0 +1,111 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x03ac37adu, 22u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x03ac37adu, 22u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x03ac37adu, 22u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-era/era-5/seeds.txt b/proto-cuda/packs-ca2-era/era-5/seeds.txt new file mode 100644 index 00000000..ff7b31ee --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/seeds.txt @@ -0,0 +1,5 @@ +epoch_seed_hex edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07 +day_seed_hex 69676e65756d2d6461792ffa50000000000000 +epoch_index 0 +day_index 20730 +daa_score 0 diff --git a/proto-cuda/packs-ca2-era/era-5/vectors.h b/proto-cuda/packs-ca2-era/era-5/vectors.h new file mode 100644 index 00000000..02e97584 --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/vectors.h @@ -0,0 +1,57 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0x2db41cf6c7af96f3ull, 0x7fb10b72307a9a54ull, 0x29745e1b3faac3bcull, 0xfac26f39a0c10352ull, 0x27a840a938abfab5ull, 0xbff83c8c7002dd7dull, 0x75eeaa3644794177ull, 0x27af0df4449a9af8ull, + 0x20f63f8623b4d27bull, 0x5f185cdf257139a2ull, 0xfe856bbdc14a9d00ull, 0x75afbd5bf6aff171ull, 0xbeb4747caff3825bull, 0x732e6a1d507f5553ull, 0xcebad71cc5667e98ull, 0x9a348da211240839ull, + 0xcd7aef8110941186ull, 0x07de91b90080abdaull, 0xf57c41187ed07aa4ull, 0x333465d0d32dbbf3ull, 0x1af724ae2d46212full, 0x32401faf581fbf3aull, 0x5e17e5f8a8a041f2ull, 0x341a99051ce73344ull, + 0xca1e16b1e0667d5dull, 0x29a00e23b166e107ull, 0x80340e9bf913c10aull, 0x504a277409796603ull, 0x30a37c2ee897d721ull, 0xad8651e4bf99a237ull, 0x58daf3a88e4b45f7ull, 0x9b8a4b4bff0bf113ull + }, + { // base nonce 4096 + 0x0bf4da3e489363e3ull, 0x0dbea592d74bd704ull, 0xb71f74d5fc74b3acull, 0x4484fc773d34c715ull, 0xcdccd9508c1d4b18ull, 0x63bd8af4608eddb0ull, 0x821fb75fe7f477d8ull, 0x3b05158071d781bfull, + 0x314d0fdf84dc1253ull, 0x05d72102d3c691b1ull, 0x4e952bc2080517ffull, 0xa7fd18dbbc098402ull, 0x61c8e269f3d5a985ull, 0x10d6cd5a58b5a7c6ull, 0xfbbe92eec88b825full, 0x7883d15dc89e4ec8ull, + 0xe9c2a972de6ec682ull, 0x1e8cc7731a411d89ull, 0xe08a27e57fff9792ull, 0x7e5f53655eb81585ull, 0x871553b59413784cull, 0x8148130a58f0e5feull, 0xd8531aada9d6c55cull, 0xe42fb76fdf380eecull, + 0x4d7b7adc743db968ull, 0xba4705cf0928c39full, 0x9c0b6ad26b4f6ff5ull, 0x7ab1c01b8c88fb16ull, 0x586bd03f8f60d8d7ull, 0xdee397b42dfe2e6dull, 0x0db04bc1e66f099bull, 0x583f1670cd1dc91cull + }, + { // base nonce 1000000 + 0xbb874c9300805389ull, 0xaf7416b01b0059e6ull, 0x6d90b71470bbf4b5ull, 0x397fe67caedc8c53ull, 0x29cf7d38ad70857full, 0xe9d0c6f8f957524eull, 0x0ac6384db3ac4e90ull, 0x57f02bb957e51a77ull, + 0xc2cfded2133a57daull, 0xbcf718fb072d2052ull, 0x31cc1574fa6472c0ull, 0xbdbfb3be16c0c797ull, 0x173f764f53e1e0dfull, 0xeac42ae3a6b3dd9bull, 0x03f478695ad6abe4ull, 0xfd3913d32e21ac01ull, + 0x4b95c2bd3f95ff21ull, 0x8c677d2917e25e7aull, 0x309fa642634958f4ull, 0xcbac756299a8cf64ull, 0x49ec74ef6fba7b27ull, 0xe4d291c0294abd05ull, 0x1a4ae0aae503257cull, 0xeb8b228788d3dac3ull, + 0x0439d39ca4b6e471ull, 0xd7da9d74d91ee421ull, 0x6377779157d20db9ull, 0xec8c6fc70df81aa9ull, 0xea251e44b4c4bf67ull, 0x209ba0e64cdd2443ull, 0x3aa5cb567a6ae4f3ull, 0x9bff7b0427c95ed9ull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0x3dd50b1fu, 0x48edec90u, 0x93123701u, 0x3a7d2407u, 0x5119540eu, 0x657ac748u, 0x3358ef2du, 0x09e2105cu, + 0xcdd88b45u, 0xb03a0ac0u, 0x2c37c6b0u, 0x10e2c6a5u, 0x25e2929cu, 0x4317cabdu, 0x5fa0cdccu, 0x6178ee52u +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xf7b7180eu; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0x1a8d5842u, 0x02457408u, 0x9ee5ee59u, 0x1fadd2a3u, 0x046827bcu, 0xcb31518fu, 0xc502a5cbu, 0xc030deeeu, 0xa690604cu, 0x89831ed7u, 0xf640c0f4u, 0xf145a054u, 0xe4d9ed8du, 0x6abc511bu, 0xfd62884cu, 0x641fb4bdu, 0x252f7254u, 0xa224439cu, 0x395e9a21u, 0xb5e60d24u, 0x87ff915au, 0x8ddc5d17u, 0xe7da99a3u, 0x16acb57au, 0x103f86c1u, 0x7144efafu, 0x98e820c1u, 0x893347ebu, 0x0c063619u, 0x01c44610u, 0xab88c304u, 0xcd17d46au, 0x4dc37dbbu, 0xfbb80109u, 0x3d5154cbu, 0x406cb5bcu, 0xac1e69e5u, 0x9b52077au, 0x7b09b6fcu, 0xa1a5f8a9u, 0x14a583a6u, 0xc866af23u, 0xfaddd4d2u, 0xd79db283u, 0x7ac395cau, 0xc0d1c920u, 0xe3f98318u, 0x33ad1217u, 0x976191a9u, 0xf1a97b54u, 0x50ce7f4cu, 0x003f843cu, 0xfcdd3e77u, 0xe153a1c9u, 0x73bde4b8u, 0xcc5d5e5cu, 0x9ac3815eu, 0xa54ddfd1u, 0x8a9ab3adu, 0x788a9ca6u, 0xbb567051u, 0x8805df35u, 0x896453cdu, 0x4c77ba49u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0xebd9055cu, 0x7eaf6b21u, 0x9b610855u, 0x134a8562u, 0xb10059bau, 0x7f459e58u, 0x8f3c7873u, 0x3523e609u, + 0x75b8f4cau, 0x750d31b4u, 0x0dae0782u, 0x26f10015u, 0x7ba87a41u, 0x0efd8543u, 0x691d6368u, 0x8929d967u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x51474edfu, 0xc3cfba93u, 0xf21454cfu, 0x9b79baacu, 0x4d4fce23u, 0xd701dfd5u, 0x37357ab3u, 0x1be693fau, + 0xa701cb3bu, 0x7467c620u, 0x428184e8u, 0xf4010df0u, 0xe33aa88fu, 0xc78d6d62u, 0xae9ba9c0u, 0xf4bba97eu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x448274a57f508cbcull; diff --git a/proto-cuda/packs-ca2-era/era-5/vectors.json b/proto-cuda/packs-ca2-era/era-5/vectors.json new file mode 100644 index 00000000..ce6c30cc --- /dev/null +++ b/proto-cuda/packs-ca2-era/era-5/vectors.json @@ -0,0 +1,36 @@ +{ + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0x2db41cf6c7af96f3", "0x7fb10b72307a9a54", "0x29745e1b3faac3bc", "0xfac26f39a0c10352", "0x27a840a938abfab5", "0xbff83c8c7002dd7d", "0x75eeaa3644794177", "0x27af0df4449a9af8", + "0x20f63f8623b4d27b", "0x5f185cdf257139a2", "0xfe856bbdc14a9d00", "0x75afbd5bf6aff171", "0xbeb4747caff3825b", "0x732e6a1d507f5553", "0xcebad71cc5667e98", "0x9a348da211240839", + "0xcd7aef8110941186", "0x07de91b90080abda", "0xf57c41187ed07aa4", "0x333465d0d32dbbf3", "0x1af724ae2d46212f", "0x32401faf581fbf3a", "0x5e17e5f8a8a041f2", "0x341a99051ce73344", + "0xca1e16b1e0667d5d", "0x29a00e23b166e107", "0x80340e9bf913c10a", "0x504a277409796603", "0x30a37c2ee897d721", "0xad8651e4bf99a237", "0x58daf3a88e4b45f7", "0x9b8a4b4bff0bf113" + ]}, + {"base_nonce": 4096, "expected": [ + "0x0bf4da3e489363e3", "0x0dbea592d74bd704", "0xb71f74d5fc74b3ac", "0x4484fc773d34c715", "0xcdccd9508c1d4b18", "0x63bd8af4608eddb0", "0x821fb75fe7f477d8", "0x3b05158071d781bf", + "0x314d0fdf84dc1253", "0x05d72102d3c691b1", "0x4e952bc2080517ff", "0xa7fd18dbbc098402", "0x61c8e269f3d5a985", "0x10d6cd5a58b5a7c6", "0xfbbe92eec88b825f", "0x7883d15dc89e4ec8", + "0xe9c2a972de6ec682", "0x1e8cc7731a411d89", "0xe08a27e57fff9792", "0x7e5f53655eb81585", "0x871553b59413784c", "0x8148130a58f0e5fe", "0xd8531aada9d6c55c", "0xe42fb76fdf380eec", + "0x4d7b7adc743db968", "0xba4705cf0928c39f", "0x9c0b6ad26b4f6ff5", "0x7ab1c01b8c88fb16", "0x586bd03f8f60d8d7", "0xdee397b42dfe2e6d", "0x0db04bc1e66f099b", "0x583f1670cd1dc91c" + ]}, + {"base_nonce": 1000000, "expected": [ + "0xbb874c9300805389", "0xaf7416b01b0059e6", "0x6d90b71470bbf4b5", "0x397fe67caedc8c53", "0x29cf7d38ad70857f", "0xe9d0c6f8f957524e", "0x0ac6384db3ac4e90", "0x57f02bb957e51a77", + "0xc2cfded2133a57da", "0xbcf718fb072d2052", "0x31cc1574fa6472c0", "0xbdbfb3be16c0c797", "0x173f764f53e1e0df", "0xeac42ae3a6b3dd9b", "0x03f478695ad6abe4", "0xfd3913d32e21ac01", + "0x4b95c2bd3f95ff21", "0x8c677d2917e25e7a", "0x309fa642634958f4", "0xcbac756299a8cf64", "0x49ec74ef6fba7b27", "0xe4d291c0294abd05", "0x1a4ae0aae503257c", "0xeb8b228788d3dac3", + "0x0439d39ca4b6e471", "0xd7da9d74d91ee421", "0x6377779157d20db9", "0xec8c6fc70df81aa9", "0xea251e44b4c4bf67", "0x209ba0e64cdd2443", "0x3aa5cb567a6ae4f3", "0x9bff7b0427c95ed9" + ]} + ], + "dataset_head": ["0x3dd50b1f", "0x48edec90", "0x93123701", "0x3a7d2407", "0x5119540e", "0x657ac748", "0x3358ef2d", "0x09e2105c", "0xcdd88b45", "0xb03a0ac0", "0x2c37c6b0", "0x10e2c6a5", "0x25e2929c", "0x4317cabd", "0x5fa0cdcc", "0x6178ee52"], + "dataset_last_index": 268435455, + "dataset_last": "0xf7b7180e", + "dataset_samples": [{"index": 59471966, "value": "0x1a8d5842"}, {"index": 217795994, "value": "0x02457408"}, {"index": 208353206, "value": "0x9ee5ee59"}, {"index": 42483309, "value": "0x1fadd2a3"}, {"index": 172547758, "value": "0x046827bc"}, {"index": 148076330, "value": "0xcb31518f"}, {"index": 183853158, "value": "0xc502a5cb"}, {"index": 214389424, "value": "0xc030deee"}, {"index": 267488061, "value": "0xa690604c"}, {"index": 169781097, "value": "0x89831ed7"}, {"index": 184093494, "value": "0xf640c0f4"}, {"index": 153880993, "value": "0xf145a054"}, {"index": 84977930, "value": "0xe4d9ed8d"}, {"index": 46426879, "value": "0x6abc511b"}, {"index": 3093825, "value": "0xfd62884c"}, {"index": 225364072, "value": "0x641fb4bd"}, {"index": 44593546, "value": "0x252f7254"}, {"index": 260713159, "value": "0xa224439c"}, {"index": 168250303, "value": "0x395e9a21"}, {"index": 52384140, "value": "0xb5e60d24"}, {"index": 223401610, "value": "0x87ff915a"}, {"index": 45554030, "value": "0x8ddc5d17"}, {"index": 95410555, "value": "0xe7da99a3"}, {"index": 175039924, "value": "0x16acb57a"}, {"index": 79171087, "value": "0x103f86c1"}, {"index": 267580473, "value": "0x7144efaf"}, {"index": 24168642, "value": "0x98e820c1"}, {"index": 37981670, "value": "0x893347eb"}, {"index": 171551130, "value": "0x0c063619"}, {"index": 195559979, "value": "0x01c44610"}, {"index": 204611762, "value": "0xab88c304"}, {"index": 140997658, "value": "0xcd17d46a"}, {"index": 138925853, "value": "0x4dc37dbb"}, {"index": 86637313, "value": "0xfbb80109"}, {"index": 20736778, "value": "0x3d5154cb"}, {"index": 219665210, "value": "0x406cb5bc"}, {"index": 160430336, "value": "0xac1e69e5"}, {"index": 264654675, "value": "0x9b52077a"}, {"index": 8013395, "value": "0x7b09b6fc"}, {"index": 228945585, "value": "0xa1a5f8a9"}, {"index": 213884386, "value": "0x14a583a6"}, {"index": 104419827, "value": "0xc866af23"}, {"index": 44185464, "value": "0xfaddd4d2"}, {"index": 142737231, "value": "0xd79db283"}, {"index": 99284897, "value": "0x7ac395ca"}, {"index": 132475900, "value": "0xc0d1c920"}, {"index": 61861762, "value": "0xe3f98318"}, {"index": 132056166, "value": "0x33ad1217"}, {"index": 262388043, "value": "0x976191a9"}, {"index": 91878046, "value": "0xf1a97b54"}, {"index": 117353561, "value": "0x50ce7f4c"}, {"index": 124768597, "value": "0x003f843c"}, {"index": 71352993, "value": "0xfcdd3e77"}, {"index": 190698941, "value": "0xe153a1c9"}, {"index": 46055428, "value": "0x73bde4b8"}, {"index": 55281366, "value": "0xcc5d5e5c"}, {"index": 165145231, "value": "0x9ac3815e"}, {"index": 106810753, "value": "0xa54ddfd1"}, {"index": 171985651, "value": "0x8a9ab3ad"}, {"index": 232085256, "value": "0x788a9ca6"}, {"index": 159510492, "value": "0xbb567051"}, {"index": 40072060, "value": "0x8805df35"}, {"index": 209107596, "value": "0x896453cd"}, {"index": 39023794, "value": "0x4c77ba49"}], + "cache_head": ["0xebd9055c", "0x7eaf6b21", "0x9b610855", "0x134a8562", "0xb10059ba", "0x7f459e58", "0x8f3c7873", "0x3523e609", "0x75b8f4ca", "0x750d31b4", "0x0dae0782", "0x26f10015", "0x7ba87a41", "0x0efd8543", "0x691d6368", "0x8929d967"], + "cache_last_line": ["0x51474edf", "0xc3cfba93", "0xf21454cf", "0x9b79baac", "0x4d4fce23", "0xd701dfd5", "0x37357ab3", "0x1be693fa", "0xa701cb3b", "0x7467c620", "0x428184e8", "0xf4010df0", "0xe33aa88f", "0xc78d6d62", "0xae9ba9c0", "0xf4bba97e"], + "cache_fnv1a64": "0x448274a57f508cbc" +} diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cl b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cl index a32be499..68d88081 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cl +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cl @@ -155,8 +155,12 @@ static inline void mh_item(__global const uint* cache, uint t, uint* s) { } for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); } -// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. -static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 12 13 of w. +static inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 12u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x00000fffu) | ((w >> 13u) << 12u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 12u) << 13u) | (w & 0x00000fffu) | (((j >> 2u) & 1u) << 12u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } // Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. // The same constants as memhard.h in this pack (one emitter, three dialects). @@ -169,8 +173,7 @@ __kernel void igneum_build(__global uint* ds, __global const uint* cache, uint n if (t < nItems) { uint s[16]; mh_item(cache, t, s); - __global uint* d = ds + ((ulong)t * 16u); - for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; } } @@ -200,69 +203,69 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl - r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add - r0 = rotl_imm(r0, 19u); // 3 rotl - r7 = rotr_var(r7, r6); // 4 rotr - r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add - r1 = mul_hi(r1, r7); // 6 mulhi - r4 = r4 ^ ds[r2 & mask]; // 7 load - r7 = r7 ^ ds[r4 & mask]; // 8 load - r0 = r0 ^ ds[r3 & mask]; // 9 load - r5 = r5 ^ ds[r1 & mask]; // 10 load - r1 = r1 ^ ds[r5 & mask]; // 11 load - r3 = mul_hi(r3, r5); // 12 mulhi - r1 = r1 ^ ds[r3 & mask]; // 13 load - r0 = r0 - r3; // 14 sub - r5 = r1 * r3 + r5; // 15 mad - r6 = mul_hi(r6, r1); // 16 mulhi - r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add - r0 = mul_hi(r0, r6); // 18 mulhi - r5 = rotr_var(r5, r3); // 19 rotr - r5 = mul_hi(r5, r2); // 20 mulhi - r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add - r1 = mul_hi(r1, r5); // 23 mulhi - r2 = r2 - r5; // 24 sub - r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add - { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl - r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add - r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add - r2 = r2 ^ ds[r1 & mask]; // 29 load - r5 = r5 ^ ds[r7 & mask]; // 30 load - r2 = r2 ^ ds[r5 & mask]; // 31 load - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl - r4 = r5 * r7 + r4; // 33 mad - r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl - r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add - r5 = r5 ^ r7; // 37 xor - r2 = r2 | r1; // 38 or - r1 = mul_hi(r1, r0); // 39 mulhi - r6 = rotl_imm(r6, 19u); // 40 rotl - r4 = mul_hi(r4, r6); // 41 mulhi - r6 = r6 - r0; // 42 sub - { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl - r4 = r4 ^ ds[r2 & mask]; // 44 load - r1 = r1 ^ r3; // 45 xor - r7 = r7 ^ ds[r0 & mask]; // 46 load - r3 = r3 ^ ds[r1 & mask]; // 47 load - r5 = r5 * r3; // 48 mul - r1 = r1 - r5; // 49 sub - r2 = rotl_imm(r2, 8u); // 50 rotl - r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add - r4 = r4 ^ ds[r7 & mask]; // 52 load - r2 = r2 - r7; // 53 sub - r4 = r4 ^ r0; // 54 xor - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add - r2 = r2 ^ ds[r4 & mask]; // 56 load - r0 = r1 * r4 + r0; // 57 mad - r3 = r3 ^ ds[r5 & mask]; // 58 load - r5 = r5 | r6; // 59 or - r6 = r5 * r7 + r6; // 60 mad - r4 = rotl_imm(r4, 28u); // 61 rotl - r5 = mul_hi(r5, r0); // 62 mulhi - r3 = r3 ^ ds[r6 & mask]; // 63 load + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cu b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cu index c129edee..2a042276 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cu +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cu @@ -36,8 +36,7 @@ __global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItem if (t < nItems) { uint32_t s[16]; mh_item(cache, t, s); - uint32_t* d = ds + (size_t)t * 16u; - for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + for (uint32_t i = 0u; i < 16u; ++i) ds[(size_t)mh_addr(t, i)] = s[i]; } } @@ -60,69 +59,69 @@ __global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonc for (uint32_t it = 0u; it < 8u; ++it) { uint32_t sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 4); // 1 shfl - r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add - r0 = rotl_imm(r0, 19u); // 3 rotl - r7 = rotr_var(r7, r6); // 4 rotr - r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add - r1 = __umulhi(r1, r7); // 6 mulhi - r4 = r4 ^ ds[r2 & mask]; // 7 load - r7 = r7 ^ ds[r4 & mask]; // 8 load - r0 = r0 ^ ds[r3 & mask]; // 9 load - r5 = r5 ^ ds[r1 & mask]; // 10 load - r1 = r1 ^ ds[r5 & mask]; // 11 load - r3 = __umulhi(r3, r5); // 12 mulhi - r1 = r1 ^ ds[r3 & mask]; // 13 load - r0 = r0 - r3; // 14 sub - r5 = r1 * r3 + r5; // 15 mad - r6 = __umulhi(r6, r1); // 16 mulhi - r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add - r0 = __umulhi(r0, r6); // 18 mulhi - r5 = rotr_var(r5, r3); // 19 rotr - r5 = __umulhi(r5, r2); // 20 mulhi - r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add - r1 = __umulhi(r1, r5); // 23 mulhi - r2 = r2 - r5; // 24 sub - r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add - r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 26 shfl - r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add - r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add - r2 = r2 ^ ds[r1 & mask]; // 29 load - r5 = r5 ^ ds[r7 & mask]; // 30 load - r2 = r2 ^ ds[r5 & mask]; // 31 load - r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 32 shfl - r4 = r5 * r7 + r4; // 33 mad - r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add - r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r7, 8); // 35 shfl - r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add - r5 = r5 ^ r7; // 37 xor - r2 = r2 | r1; // 38 or - r1 = __umulhi(r1, r0); // 39 mulhi - r6 = rotl_imm(r6, 19u); // 40 rotl - r4 = __umulhi(r4, r6); // 41 mulhi - r6 = r6 - r0; // 42 sub - r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl - r4 = r4 ^ ds[r2 & mask]; // 44 load - r1 = r1 ^ r3; // 45 xor - r7 = r7 ^ ds[r0 & mask]; // 46 load - r3 = r3 ^ ds[r1 & mask]; // 47 load - r5 = r5 * r3; // 48 mul - r1 = r1 - r5; // 49 sub - r2 = rotl_imm(r2, 8u); // 50 rotl - r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add - r4 = r4 ^ ds[r7 & mask]; // 52 load - r2 = r2 - r7; // 53 sub - r4 = r4 ^ r0; // 54 xor - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add - r2 = r2 ^ ds[r4 & mask]; // 56 load - r0 = r1 * r4 + r0; // 57 mad - r3 = r3 ^ ds[r5 & mask]; // 58 load - r5 = r5 | r6; // 59 or - r6 = r5 * r7 + r6; // 60 mad - r4 = rotl_imm(r4, 28u); // 61 rotl - r5 = __umulhi(r5, r0); // 62 mulhi - r3 = r3 ^ ds[r6 & mask]; // 63 load + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load } uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cl b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cl index 3ae5ef11..bc66d9eb 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cl +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cl @@ -155,8 +155,12 @@ static inline void mh_item(__global const uint* cache, uint t, uint* s) { } for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); } -// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. -static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 12 13 of w. +static inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 12u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x00000fffu) | ((w >> 13u) << 12u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 12u) << 13u) | (w & 0x00000fffu) | (((j >> 2u) & 1u) << 12u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } // Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. // The same constants as memhard.h in this pack (one emitter, three dialects). @@ -169,8 +173,7 @@ __kernel void igneum_build(__global uint* ds, __global const uint* cache, uint n if (t < nItems) { uint s[16]; mh_item(cache, t, s); - __global uint* d = ds + ((ulong)t * 16u); - for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; } } @@ -200,69 +203,69 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl - r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add - r0 = rotl_imm(r0, 19u); // 3 rotl - r7 = rotr_var(r7, r6); // 4 rotr - r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add - r1 = mul_hi(r1, r7); // 6 mulhi - r4 = r4 ^ ds[r2 & mask]; // 7 load - r7 = r7 ^ ds[r4 & mask]; // 8 load - r0 = r0 ^ ds[r3 & mask]; // 9 load - r5 = r5 ^ ds[r1 & mask]; // 10 load - r1 = r1 ^ ds[r5 & mask]; // 11 load - r3 = mul_hi(r3, r5); // 12 mulhi - r1 = r1 ^ ds[r3 & mask]; // 13 load - r0 = r0 - r3; // 14 sub - r5 = r1 * r3 + r5; // 15 mad - r6 = mul_hi(r6, r1); // 16 mulhi - r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add - r0 = mul_hi(r0, r6); // 18 mulhi - r5 = rotr_var(r5, r3); // 19 rotr - r5 = mul_hi(r5, r2); // 20 mulhi - r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add - r1 = mul_hi(r1, r5); // 23 mulhi - r2 = r2 - r5; // 24 sub - r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add - { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl - r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add - r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add - r2 = r2 ^ ds[r1 & mask]; // 29 load - r5 = r5 ^ ds[r7 & mask]; // 30 load - r2 = r2 ^ ds[r5 & mask]; // 31 load - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl - r4 = r5 * r7 + r4; // 33 mad - r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl - r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add - r5 = r5 ^ r7; // 37 xor - r2 = r2 | r1; // 38 or - r1 = mul_hi(r1, r0); // 39 mulhi - r6 = rotl_imm(r6, 19u); // 40 rotl - r4 = mul_hi(r4, r6); // 41 mulhi - r6 = r6 - r0; // 42 sub - { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl - r4 = r4 ^ ds[r2 & mask]; // 44 load - r1 = r1 ^ r3; // 45 xor - r7 = r7 ^ ds[r0 & mask]; // 46 load - r3 = r3 ^ ds[r1 & mask]; // 47 load - r5 = r5 * r3; // 48 mul - r1 = r1 - r5; // 49 sub - r2 = rotl_imm(r2, 8u); // 50 rotl - r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add - r4 = r4 ^ ds[r7 & mask]; // 52 load - r2 = r2 - r7; // 53 sub - r4 = r4 ^ r0; // 54 xor - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add - r2 = r2 ^ ds[r4 & mask]; // 56 load - r0 = r1 * r4 + r0; // 57 mad - r3 = r3 ^ ds[r5 & mask]; // 58 load - r5 = r5 | r6; // 59 or - r6 = r5 * r7 + r6; // 60 mad - r4 = rotl_imm(r4, 28u); // 61 rotl - r5 = mul_hi(r5, r0); // 62 mulhi - r3 = r3 ^ ds[r6 & mask]; // 63 load + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); @@ -303,69 +306,69 @@ IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulon for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl - r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add - r0 = rotl_imm(r0, 19u); // 3 rotl - r7 = rotr_var(r7, r6); // 4 rotr - r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add - r1 = mul_hi(r1, r7); // 6 mulhi - r4 = r4 ^ ds[r2 & mask]; // 7 load - r7 = r7 ^ ds[r4 & mask]; // 8 load - r0 = r0 ^ ds[r3 & mask]; // 9 load - r5 = r5 ^ ds[r1 & mask]; // 10 load - r1 = r1 ^ ds[r5 & mask]; // 11 load - r3 = mul_hi(r3, r5); // 12 mulhi - r1 = r1 ^ ds[r3 & mask]; // 13 load - r0 = r0 - r3; // 14 sub - r5 = r1 * r3 + r5; // 15 mad - r6 = mul_hi(r6, r1); // 16 mulhi - r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add - r0 = mul_hi(r0, r6); // 18 mulhi - r5 = rotr_var(r5, r3); // 19 rotr - r5 = mul_hi(r5, r2); // 20 mulhi - r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add - r1 = mul_hi(r1, r5); // 23 mulhi - r2 = r2 - r5; // 24 sub - r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add - { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl - r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add - r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add - r2 = r2 ^ ds[r1 & mask]; // 29 load - r5 = r5 ^ ds[r7 & mask]; // 30 load - r2 = r2 ^ ds[r5 & mask]; // 31 load - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl - r4 = r5 * r7 + r4; // 33 mad - r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl - r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add - r5 = r5 ^ r7; // 37 xor - r2 = r2 | r1; // 38 or - r1 = mul_hi(r1, r0); // 39 mulhi - r6 = rotl_imm(r6, 19u); // 40 rotl - r4 = mul_hi(r4, r6); // 41 mulhi - r6 = r6 - r0; // 42 sub - { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl - r4 = r4 ^ ds[r2 & mask]; // 44 load - r1 = r1 ^ r3; // 45 xor - r7 = r7 ^ ds[r0 & mask]; // 46 load - r3 = r3 ^ ds[r1 & mask]; // 47 load - r5 = r5 * r3; // 48 mul - r1 = r1 - r5; // 49 sub - r2 = rotl_imm(r2, 8u); // 50 rotl - r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add - r4 = r4 ^ ds[r7 & mask]; // 52 load - r2 = r2 - r7; // 53 sub - r4 = r4 ^ r0; // 54 xor - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add - r2 = r2 ^ ds[r4 & mask]; // 56 load - r0 = r1 * r4 + r0; // 57 mad - r3 = r3 ^ ds[r5 & mask]; // 58 load - r5 = r5 | r6; // 59 or - r6 = r5 * r7 + r6; // 60 mad - r4 = rotl_imm(r4, 28u); // 61 rotl - r5 = mul_hi(r5, r0); // 62 mulhi - r3 = r3 ^ ds[r6 & mask]; // 63 load + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cu b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cu index 9a46b748..af900376 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cu +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cu @@ -36,69 +36,69 @@ __global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t ba for (uint32_t it = 0u; it < 8u; ++it) { uint32_t sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 4); // 1 shfl - r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add - r0 = rotl_imm(r0, 19u); // 3 rotl - r7 = rotr_var(r7, r6); // 4 rotr - r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add - r1 = __umulhi(r1, r7); // 6 mulhi - r4 = r4 ^ ds[r2 & mask]; // 7 load - r7 = r7 ^ ds[r4 & mask]; // 8 load - r0 = r0 ^ ds[r3 & mask]; // 9 load - r5 = r5 ^ ds[r1 & mask]; // 10 load - r1 = r1 ^ ds[r5 & mask]; // 11 load - r3 = __umulhi(r3, r5); // 12 mulhi - r1 = r1 ^ ds[r3 & mask]; // 13 load - r0 = r0 - r3; // 14 sub - r5 = r1 * r3 + r5; // 15 mad - r6 = __umulhi(r6, r1); // 16 mulhi - r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add - r0 = __umulhi(r0, r6); // 18 mulhi - r5 = rotr_var(r5, r3); // 19 rotr - r5 = __umulhi(r5, r2); // 20 mulhi - r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add - r1 = __umulhi(r1, r5); // 23 mulhi - r2 = r2 - r5; // 24 sub - r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add - r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 26 shfl - r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add - r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add - r2 = r2 ^ ds[r1 & mask]; // 29 load - r5 = r5 ^ ds[r7 & mask]; // 30 load - r2 = r2 ^ ds[r5 & mask]; // 31 load - r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 32 shfl - r4 = r5 * r7 + r4; // 33 mad - r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add - r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r7, 8); // 35 shfl - r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add - r5 = r5 ^ r7; // 37 xor - r2 = r2 | r1; // 38 or - r1 = __umulhi(r1, r0); // 39 mulhi - r6 = rotl_imm(r6, 19u); // 40 rotl - r4 = __umulhi(r4, r6); // 41 mulhi - r6 = r6 - r0; // 42 sub - r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl - r4 = r4 ^ ds[r2 & mask]; // 44 load - r1 = r1 ^ r3; // 45 xor - r7 = r7 ^ ds[r0 & mask]; // 46 load - r3 = r3 ^ ds[r1 & mask]; // 47 load - r5 = r5 * r3; // 48 mul - r1 = r1 - r5; // 49 sub - r2 = rotl_imm(r2, 8u); // 50 rotl - r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add - r4 = r4 ^ ds[r7 & mask]; // 52 load - r2 = r2 - r7; // 53 sub - r4 = r4 ^ r0; // 54 xor - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add - r2 = r2 ^ ds[r4 & mask]; // 56 load - r0 = r1 * r4 + r0; // 57 mad - r3 = r3 ^ ds[r5 & mask]; // 58 load - r5 = r5 | r6; // 59 or - r6 = r5 * r7 + r6; // 60 mad - r4 = rotl_imm(r4, 28u); // 61 rotl - r5 = __umulhi(r5, r0); // 62 mulhi - r3 = r3 ^ ds[r6 & mask]; // 63 load + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load } uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.h b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.h index bbe76f94..79d760f4 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.h +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.h @@ -105,5 +105,9 @@ IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { } for (uint32_t j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); } -// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. -IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 12 13 of w. +IGNEUM_HD uint32_t mh_j(uint32_t w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 12u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +IGNEUM_HD uint32_t mh_t(uint32_t w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x00000fffu) | ((w >> 13u) << 12u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +IGNEUM_HD uint32_t mh_addr(uint32_t t, uint32_t j) { uint32_t w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 12u) << 13u) | (w & 0x00000fffu) | (((j >> 2u) & 1u) << 12u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.metal b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.metal index 57022480..89171a04 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.metal +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.metal @@ -90,8 +90,12 @@ inline void mh_item(device const uint* cache, uint t, thread uint* s) { } for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); } -// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. -inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 12 13 of w. +inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 12u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x00000fffu) | ((w >> 13u) << 12u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 12u) << 13u) | (w & 0x00000fffu) | (((j >> 2u) & 1u) << 12u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } // One thread per segment (2^16 threads). kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { @@ -102,6 +106,5 @@ kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* da uint gid [[thread_position_in_grid]]) { uint s[16]; mh_item(cache, gid, s); - device uint* d = dataset + gid * 16u; - for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + for (uint i = 0u; i < 16u; ++i) dataset[mh_addr(gid, i)] = s[i]; } diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.h b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.h index a9558ba3..e353c0a0 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.h +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.h @@ -27,14 +27,14 @@ #define IGNEUM_INSTR_COUNT 64 #define IGNEUM_LOADS_PER_HASH 128 #define IGNEUM_WIDE_LOADS_PER_HASH 0 -#define IGNEUM_OP_MIX "load=16 add=13 mulhi=9 shfl=5 sub=5 mad=4 rotl=4 xor=3 or=2 rotr=2 mul=1" +#define IGNEUM_OP_MIX "load=16 add=15 shfl=6 mad=4 or=4 rotl=4 rotr=4 xor=4 mulhi=3 mul=2 sub=2" // Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that // runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). #define IGNEUM_PROGRAM_CLASS "v3" #define IGNEUM_ERA_SEED_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" // Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item // derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. -#define IGNEUM_LOAD_CLASS "mx4" +#define IGNEUM_LOAD_CLASS "mx4-erad810f22d" #define IGNEUM_CLASS_MIXER_MULT 4 #define IGNEUM_CACHE_GROWTH 1 // 1: cache words = 2^(26 + doublings(day)), doublings = floor(log2(1 + day / 1460)) #define IGNEUM_LOAD_SLOTS 16 @@ -43,6 +43,18 @@ #define IGNEUM_BYTES_PER_HASH 512 #define IGNEUM_FOLD_ROT 11 #define IGNEUM_FOLD_MUL 0x9e3779b1u +// Era layout (5 October 2026, docs/plans/era-layout.md): NOT the lottery hash. Every dataset load reads +// idx = ((rotl(src * STRIDE_MUL, STRIDE_ROT) & window mask) | window offset) & MASK; the window of a load site is the +// dataset, a half or a quarter of it (IGNEUM_ERA_WINDOWS: site:shrink:offset); dataset word w holds word j(w) of item +// t(w) with j's bits at the INTERLEAVE positions (memhard.h: mh_t, mh_j, mh_addr). +#define IGNEUM_ERA_LABEL "d810f22d" +#define IGNEUM_ERA_SEED_WORDS { 0xd810f22du, 0xcf9dc883u, 0x5f3e571fu, 0x2b7d97fcu, 0x810012c4u, 0x002d9798u, 0xa4180c41u, 0xa4562c21u } +#define IGNEUM_ERA_ALLOWED_WIDTHS { 1, 0, 0 } // words, ascending, 0 = unused; one entry pins the width +#define IGNEUM_ERA_WIDTH_WORDS 1 +#define IGNEUM_ERA_STRIDE_MUL 0x9ad30d99u +#define IGNEUM_ERA_STRIDE_ROT 29 +#define IGNEUM_ERA_INTERLEAVE { 0, 2, 12, 13 } +#define IGNEUM_ERA_WINDOWS "7:2:1 8:1:1 9:1:1 10:1:1 11:0:0 13:1:1 29:0:0 30:2:2 31:1:1 44:1:1 46:2:0 47:0:0 52:0:0 56:0:0 58:2:0 63:1:1" // 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) #define IGNEUM_DATASET_MODE 1 diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.json b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.json index 87a22e6d..a78da713 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.json +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.json @@ -17,7 +17,7 @@ "loads_per_hash": 128, "program_class": "v3", "era_seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07", - "load_class": "mx4", + "load_class": "mx4-erad810f22d", "mixer_mult": 4, "cache_growth": true, "mixer": "class v3 (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): every mixer application of the item derivation is 4 applications with round keys (r * 4 + j + 1) * 0x9E3779B9, the 8 dependent cache reads per item unchanged; cache growth rule option C: cache words = 2^(26 + doublings(day)), dataset words = 2^(genesis_log2 + doublings(day)), doublings(day) = floor(log2(1 + day / 1460)) for day = days since genesis", @@ -26,7 +26,21 @@ "load_width_counts_4_16_64": [16, 0, 0], "bytes_per_hash": 512, "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", - "op_mix": {"load": 16, "add": 13, "mulhi": 9, "shfl": 5, "sub": 5, "mad": 4, "rotl": 4, "xor": 3, "or": 2, "rotr": 2, "mul": 1}, + "era": { + "label": "d810f22d", + "seed_words": ["0xd810f22d", "0xcf9dc883", "0x5f3e571f", "0x2b7d97fc", "0x810012c4", "0x002d9798", "0xa4180c41", "0xa4562c21"], + "draw": "docs/plans/era-layout.md 1.1: SplitMix64 seeded with seed_words[0] | seed_words[1] << 32 of seed_words_from_bytes('igneum-era/' || n_le64 || E_n); width = allowed[below(|allowed|)], stride_mul = low32(next()) | 1, stride_rot = 1 + below(31), then four next() draws for a partial Fisher-Yates over positions log2(W)..15 of which 4 - log2(W) are used", + "allowed_widths": [1], + "width_words": 1, + "stride_mul": "0x9ad30d99", + "stride_rot": 29, + "interleave": [0, 2, 12, 13], + "address": "y = rotl(src * stride_mul, stride_rot); k = min(win, D - 26); idx = ((y & (mask >> k)) | ((off & (2^k - 1)) << (D - k))) & mask; a wide load aligns idx down to W words", + "windows": "per instruction, after the width roll: win = below(3), off = low32(next()) & (2^win - 1); used on a load slot (the instruction's win and off fields)", + "dataset_word": "dataset[w] = item(t(w))[j(w)]: j(w) gathers the bits of w at the interleave positions, t(w) is w with those bits removed", + "program_id_suffix": "'era/' || allowed[3] || width_words || stride_mul_le32 || stride_rot_le32 || interleave[4]" + }, + "op_mix": {"load": 16, "add": 15, "shfl": 6, "mad": 4, "or": 4, "rotl": 4, "rotr": 4, "xor": 4, "mulhi": 3, "mul": 2, "sub": 2}, "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", @@ -65,69 +79,69 @@ "word": "dataset[w] = item(w >> 4)[w & 15]" }, "instructions": [ - {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1}, - {"i": 1, "op": "shfl", "dst": 2, "src": 0, "src2": 7, "imm": "0xe3c2f9cb", "imm2": "0xde0bea4e", "rot": 6, "bit": 9, "mask": 4, "width": 1}, - {"i": 2, "op": "add", "dst": 3, "src": 2, "src2": 0, "imm": "0x2cccb6ca", "imm2": "0x642e66db", "rot": 27, "bit": 10, "mask": 2, "width": 1}, - {"i": 3, "op": "rotl", "dst": 0, "src": 2, "src2": 1, "imm": "0xb59e83b2", "imm2": "0x19c26fb9", "rot": 19, "bit": 20, "mask": 4, "width": 1}, - {"i": 4, "op": "rotr", "dst": 7, "src": 6, "src2": 5, "imm": "0x6f055f55", "imm2": "0x550e4ea1", "rot": 31, "bit": 20, "mask": 4, "width": 1}, - {"i": 5, "op": "add", "dst": 7, "src": 4, "src2": 7, "imm": "0xee02465f", "imm2": "0xc1535555", "rot": 31, "bit": 21, "mask": 4, "width": 1}, - {"i": 6, "op": "mulhi", "dst": 1, "src": 7, "src2": 5, "imm": "0x7826a6a7", "imm2": "0x946f7818", "rot": 18, "bit": 21, "mask": 8, "width": 1}, - {"i": 7, "op": "load", "dst": 4, "src": 2, "src2": 0, "imm": "0x5d080878", "imm2": "0xdf885578", "rot": 5, "bit": 18, "mask": 4, "width": 1}, - {"i": 8, "op": "load", "dst": 7, "src": 4, "src2": 1, "imm": "0x875bbb36", "imm2": "0x594a838f", "rot": 24, "bit": 4, "mask": 2, "width": 1}, - {"i": 9, "op": "load", "dst": 0, "src": 3, "src2": 1, "imm": "0xe5e607c2", "imm2": "0xa5cd9f75", "rot": 26, "bit": 28, "mask": 4, "width": 1}, - {"i": 10, "op": "load", "dst": 5, "src": 1, "src2": 3, "imm": "0xea3f7b43", "imm2": "0x10c8d4e7", "rot": 5, "bit": 29, "mask": 8, "width": 1}, - {"i": 11, "op": "load", "dst": 1, "src": 5, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1}, - {"i": 12, "op": "mulhi", "dst": 3, "src": 5, "src2": 7, "imm": "0xbfd5615c", "imm2": "0xd8224ae2", "rot": 21, "bit": 12, "mask": 2, "width": 1}, - {"i": 13, "op": "load", "dst": 1, "src": 3, "src2": 5, "imm": "0x35c07cc5", "imm2": "0xe82db54f", "rot": 7, "bit": 6, "mask": 8, "width": 1}, - {"i": 14, "op": "sub", "dst": 0, "src": 3, "src2": 0, "imm": "0x587ee0f1", "imm2": "0xfd23eefd", "rot": 16, "bit": 21, "mask": 16, "width": 1}, - {"i": 15, "op": "mad", "dst": 5, "src": 1, "src2": 3, "imm": "0x6974dd29", "imm2": "0xc8148960", "rot": 3, "bit": 11, "mask": 2, "width": 1}, - {"i": 16, "op": "mulhi", "dst": 6, "src": 1, "src2": 7, "imm": "0x3072c3c6", "imm2": "0x55ee21f8", "rot": 26, "bit": 1, "mask": 1, "width": 1}, - {"i": 17, "op": "add", "dst": 5, "src": 2, "src2": 2, "imm": "0x697b3d00", "imm2": "0x8b965b57", "rot": 9, "bit": 28, "mask": 1, "width": 1}, - {"i": 18, "op": "mulhi", "dst": 0, "src": 6, "src2": 3, "imm": "0x2910cacb", "imm2": "0x6ac79431", "rot": 7, "bit": 6, "mask": 4, "width": 1}, - {"i": 19, "op": "rotr", "dst": 5, "src": 3, "src2": 0, "imm": "0xed96a94a", "imm2": "0x4c988c10", "rot": 24, "bit": 21, "mask": 8, "width": 1}, - {"i": 20, "op": "mulhi", "dst": 5, "src": 2, "src2": 7, "imm": "0x40d2fc76", "imm2": "0x2f7c7eca", "rot": 13, "bit": 8, "mask": 4, "width": 1}, - {"i": 21, "op": "add", "dst": 1, "src": 0, "src2": 5, "imm": "0xebcf247a", "imm2": "0x6d7e8d05", "rot": 31, "bit": 1, "mask": 16, "width": 1}, - {"i": 22, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1}, - {"i": 23, "op": "mulhi", "dst": 1, "src": 5, "src2": 7, "imm": "0xfdfe72fd", "imm2": "0x735eeb8d", "rot": 30, "bit": 25, "mask": 8, "width": 1}, - {"i": 24, "op": "sub", "dst": 2, "src": 5, "src2": 0, "imm": "0x32cf1258", "imm2": "0xd813deb6", "rot": 30, "bit": 6, "mask": 16, "width": 1}, - {"i": 25, "op": "add", "dst": 7, "src": 4, "src2": 2, "imm": "0x08ffa6c7", "imm2": "0x699ef1bb", "rot": 7, "bit": 2, "mask": 16, "width": 1}, - {"i": 26, "op": "shfl", "dst": 3, "src": 4, "src2": 5, "imm": "0x6f53c70d", "imm2": "0x3357513f", "rot": 3, "bit": 26, "mask": 2, "width": 1}, - {"i": 27, "op": "add", "dst": 7, "src": 1, "src2": 4, "imm": "0xe60fea84", "imm2": "0xb4ead2fb", "rot": 14, "bit": 14, "mask": 1, "width": 1}, - {"i": 28, "op": "add", "dst": 3, "src": 1, "src2": 0, "imm": "0x65c76dab", "imm2": "0x8f30d21d", "rot": 24, "bit": 6, "mask": 1, "width": 1}, - {"i": 29, "op": "load", "dst": 2, "src": 1, "src2": 2, "imm": "0x82fad9a6", "imm2": "0x8c6358db", "rot": 7, "bit": 31, "mask": 2, "width": 1}, - {"i": 30, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x6e947ee0", "imm2": "0xaf9a2dda", "rot": 2, "bit": 30, "mask": 4, "width": 1}, - {"i": 31, "op": "load", "dst": 2, "src": 5, "src2": 1, "imm": "0x608bb7ce", "imm2": "0x4be663db", "rot": 19, "bit": 1, "mask": 16, "width": 1}, - {"i": 32, "op": "shfl", "dst": 1, "src": 7, "src2": 6, "imm": "0x88e52e20", "imm2": "0x77647269", "rot": 20, "bit": 25, "mask": 4, "width": 1}, - {"i": 33, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1}, - {"i": 34, "op": "add", "dst": 4, "src": 2, "src2": 3, "imm": "0x0480debe", "imm2": "0xc7ce690c", "rot": 12, "bit": 21, "mask": 16, "width": 1}, - {"i": 35, "op": "shfl", "dst": 3, "src": 7, "src2": 5, "imm": "0xa73f59de", "imm2": "0x84b9e329", "rot": 21, "bit": 27, "mask": 8, "width": 1}, - {"i": 36, "op": "add", "dst": 7, "src": 1, "src2": 7, "imm": "0xc53b542e", "imm2": "0xe10c2c95", "rot": 21, "bit": 2, "mask": 4, "width": 1}, - {"i": 37, "op": "xor", "dst": 5, "src": 7, "src2": 4, "imm": "0x81cd7b0e", "imm2": "0x21a51823", "rot": 12, "bit": 10, "mask": 2, "width": 1}, - {"i": 38, "op": "or", "dst": 2, "src": 1, "src2": 3, "imm": "0x7894e657", "imm2": "0xf8e4b972", "rot": 18, "bit": 9, "mask": 4, "width": 1}, - {"i": 39, "op": "mulhi", "dst": 1, "src": 0, "src2": 4, "imm": "0xbee8421f", "imm2": "0x070888a8", "rot": 20, "bit": 28, "mask": 4, "width": 1}, - {"i": 40, "op": "rotl", "dst": 6, "src": 1, "src2": 4, "imm": "0x4609857a", "imm2": "0xaeecb156", "rot": 19, "bit": 21, "mask": 1, "width": 1}, - {"i": 41, "op": "mulhi", "dst": 4, "src": 6, "src2": 0, "imm": "0x1a84e1e9", "imm2": "0x9b26bb72", "rot": 28, "bit": 19, "mask": 1, "width": 1}, - {"i": 42, "op": "sub", "dst": 6, "src": 0, "src2": 6, "imm": "0xbf62908e", "imm2": "0xdf03ea88", "rot": 11, "bit": 27, "mask": 16, "width": 1}, - {"i": 43, "op": "shfl", "dst": 6, "src": 3, "src2": 4, "imm": "0xa966241c", "imm2": "0x9c639efa", "rot": 12, "bit": 4, "mask": 4, "width": 1}, - {"i": 44, "op": "load", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1}, - {"i": 45, "op": "xor", "dst": 1, "src": 3, "src2": 6, "imm": "0x006193d0", "imm2": "0xfc2acc3f", "rot": 25, "bit": 1, "mask": 1, "width": 1}, - {"i": 46, "op": "load", "dst": 7, "src": 0, "src2": 6, "imm": "0x4777c4f8", "imm2": "0x3cf0a02f", "rot": 11, "bit": 14, "mask": 4, "width": 1}, - {"i": 47, "op": "load", "dst": 3, "src": 1, "src2": 3, "imm": "0x10692532", "imm2": "0x1a292ea5", "rot": 20, "bit": 23, "mask": 1, "width": 1}, - {"i": 48, "op": "mul", "dst": 5, "src": 3, "src2": 7, "imm": "0xadf5bd13", "imm2": "0xb999de2e", "rot": 23, "bit": 10, "mask": 4, "width": 1}, - {"i": 49, "op": "sub", "dst": 1, "src": 5, "src2": 4, "imm": "0x68ff101e", "imm2": "0xbdaaf46a", "rot": 25, "bit": 23, "mask": 16, "width": 1}, - {"i": 50, "op": "rotl", "dst": 2, "src": 6, "src2": 4, "imm": "0x92d9a412", "imm2": "0x0daf96ea", "rot": 8, "bit": 4, "mask": 4, "width": 1}, - {"i": 51, "op": "add", "dst": 1, "src": 5, "src2": 0, "imm": "0xa900fec4", "imm2": "0x77b9bd43", "rot": 6, "bit": 23, "mask": 2, "width": 1}, - {"i": 52, "op": "load", "dst": 4, "src": 7, "src2": 1, "imm": "0x51392a72", "imm2": "0x99e8bb36", "rot": 11, "bit": 9, "mask": 1, "width": 1}, - {"i": 53, "op": "sub", "dst": 2, "src": 7, "src2": 2, "imm": "0x0ffe2ac7", "imm2": "0x030743df", "rot": 9, "bit": 30, "mask": 16, "width": 1}, - {"i": 54, "op": "xor", "dst": 4, "src": 0, "src2": 4, "imm": "0x9123ff15", "imm2": "0x10c329a7", "rot": 28, "bit": 15, "mask": 2, "width": 1}, - {"i": 55, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1}, - {"i": 56, "op": "load", "dst": 2, "src": 4, "src2": 3, "imm": "0x239de52c", "imm2": "0xf80bae18", "rot": 15, "bit": 24, "mask": 1, "width": 1}, - {"i": 57, "op": "mad", "dst": 0, "src": 1, "src2": 4, "imm": "0x31dede8e", "imm2": "0xd3f619e6", "rot": 29, "bit": 7, "mask": 2, "width": 1}, - {"i": 58, "op": "load", "dst": 3, "src": 5, "src2": 3, "imm": "0x206437d6", "imm2": "0x28d1c290", "rot": 17, "bit": 28, "mask": 4, "width": 1}, - {"i": 59, "op": "or", "dst": 5, "src": 6, "src2": 3, "imm": "0x8fffd674", "imm2": "0x0507903a", "rot": 26, "bit": 27, "mask": 2, "width": 1}, - {"i": 60, "op": "mad", "dst": 6, "src": 5, "src2": 7, "imm": "0xf572bdb9", "imm2": "0xeda2af31", "rot": 21, "bit": 8, "mask": 2, "width": 1}, - {"i": 61, "op": "rotl", "dst": 4, "src": 2, "src2": 7, "imm": "0x84f12ddf", "imm2": "0x81ef22e1", "rot": 28, "bit": 30, "mask": 1, "width": 1}, - {"i": 62, "op": "mulhi", "dst": 5, "src": 0, "src2": 6, "imm": "0xf6bb45ee", "imm2": "0x6bfb632d", "rot": 22, "bit": 0, "mask": 4, "width": 1}, - {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0x50ec702a", "imm2": "0xae6ee96e", "rot": 20, "bit": 25, "mask": 2, "width": 1} + {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 1, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xbaab6229", "imm2": "0xed861989", "rot": 26, "bit": 22, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 2, "op": "shfl", "dst": 3, "src": 6, "src2": 2, "imm": "0x5b623116", "imm2": "0xff12e5b2", "rot": 12, "bit": 24, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 3, "op": "sub", "dst": 4, "src": 1, "src2": 1, "imm": "0xe99741c7", "imm2": "0xf5fa5009", "rot": 1, "bit": 21, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 4, "op": "mad", "dst": 2, "src": 0, "src2": 4, "imm": "0x673c2157", "imm2": "0xee02465f", "rot": 20, "bit": 22, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 5, "op": "rotl", "dst": 4, "src": 7, "src2": 7, "imm": "0x946f7818", "imm2": "0x45d3399e", "rot": 9, "bit": 2, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 6, "op": "rotr", "dst": 0, "src": 2, "src2": 4, "imm": "0x5f6a0ed2", "imm2": "0x7043a636", "rot": 19, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 7, "op": "load", "dst": 6, "src": 7, "src2": 1, "imm": "0x5c61dcf7", "imm2": "0x7466aa40", "rot": 19, "bit": 9, "mask": 2, "width": 1, "win": 2, "off": 1}, + {"i": 8, "op": "load", "dst": 1, "src": 4, "src2": 5, "imm": "0x85668475", "imm2": "0xdb8cc483", "rot": 29, "bit": 7, "mask": 4, "width": 1, "win": 1, "off": 1}, + {"i": 9, "op": "load", "dst": 1, "src": 2, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 10, "op": "load", "dst": 7, "src": 0, "src2": 2, "imm": "0xe075297c", "imm2": "0x5779c44c", "rot": 10, "bit": 22, "mask": 2, "width": 1, "win": 1, "off": 1}, + {"i": 11, "op": "load", "dst": 7, "src": 1, "src2": 6, "imm": "0x65aa4311", "imm2": "0x4fe48ea9", "rot": 15, "bit": 9, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 12, "op": "mul", "dst": 5, "src": 4, "src2": 2, "imm": "0x1383d3ad", "imm2": "0xf3094b29", "rot": 8, "bit": 9, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 13, "op": "load", "dst": 7, "src": 6, "src2": 2, "imm": "0xed8a496f", "imm2": "0x3072c3c6", "rot": 28, "bit": 19, "mask": 8, "width": 1, "win": 1, "off": 1}, + {"i": 14, "op": "shfl", "dst": 6, "src": 5, "src2": 0, "imm": "0x8b965b57", "imm2": "0xcfeca6c1", "rot": 12, "bit": 27, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 15, "op": "shfl", "dst": 3, "src": 5, "src2": 3, "imm": "0x877c7586", "imm2": "0xa9cb2a03", "rot": 2, "bit": 29, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 16, "op": "or", "dst": 2, "src": 7, "src2": 3, "imm": "0xb740221a", "imm2": "0x89d38d6d", "rot": 6, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 17, "op": "rotr", "dst": 0, "src": 6, "src2": 1, "imm": "0x26f3ad8a", "imm2": "0x27256f15", "rot": 18, "bit": 5, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 18, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 19, "op": "rotl", "dst": 7, "src": 0, "src2": 5, "imm": "0x849ae6ee", "imm2": "0x02b358f9", "rot": 24, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 20, "op": "add", "dst": 6, "src": 7, "src2": 6, "imm": "0x52334d12", "imm2": "0x8c9f0ef8", "rot": 11, "bit": 23, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 21, "op": "or", "dst": 2, "src": 6, "src2": 5, "imm": "0xb1871e63", "imm2": "0xb2e40191", "rot": 5, "bit": 13, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 22, "op": "mad", "dst": 6, "src": 5, "src2": 4, "imm": "0x97df29e4", "imm2": "0xe60fea84", "rot": 11, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 23, "op": "or", "dst": 1, "src": 0, "src2": 3, "imm": "0x8f30d21d", "imm2": "0x2df685a0", "rot": 31, "bit": 0, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 24, "op": "xor", "dst": 6, "src": 1, "src2": 2, "imm": "0xd2c4025f", "imm2": "0x5269eb4d", "rot": 31, "bit": 21, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 25, "op": "add", "dst": 2, "src": 6, "src2": 5, "imm": "0x659fc3d3", "imm2": "0x9cec0e12", "rot": 6, "bit": 17, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 26, "op": "rotr", "dst": 7, "src": 0, "src2": 1, "imm": "0xd89ef484", "imm2": "0x20be3846", "rot": 12, "bit": 9, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 27, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 28, "op": "mad", "dst": 3, "src": 2, "src2": 4, "imm": "0xc5c46d76", "imm2": "0x700044b5", "rot": 22, "bit": 10, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 29, "op": "load", "dst": 1, "src": 4, "src2": 3, "imm": "0x0fbaf177", "imm2": "0xfff4f2ed", "rot": 20, "bit": 31, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 30, "op": "load", "dst": 2, "src": 3, "src2": 0, "imm": "0xe90eb2e5", "imm2": "0xb0f9eb79", "rot": 27, "bit": 14, "mask": 4, "width": 1, "win": 2, "off": 2}, + {"i": 31, "op": "load", "dst": 1, "src": 5, "src2": 2, "imm": "0x97ba3fc3", "imm2": "0x7894e657", "rot": 3, "bit": 30, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 32, "op": "add", "dst": 7, "src": 2, "src2": 7, "imm": "0x070888a8", "imm2": "0xe403240e", "rot": 2, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 33, "op": "add", "dst": 2, "src": 0, "src2": 5, "imm": "0xf2e46d55", "imm2": "0x29701828", "rot": 31, "bit": 28, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 34, "op": "add", "dst": 2, "src": 3, "src2": 0, "imm": "0x58f75b87", "imm2": "0x343b7aee", "rot": 12, "bit": 14, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 35, "op": "mulhi", "dst": 2, "src": 5, "src2": 6, "imm": "0x5892a9e6", "imm2": "0xc9824c94", "rot": 19, "bit": 26, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 36, "op": "xor", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 37, "op": "mul", "dst": 6, "src": 5, "src2": 7, "imm": "0xccf564a5", "imm2": "0x873ad101", "rot": 7, "bit": 11, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 38, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xcac8f06d", "imm2": "0x6b97c683", "rot": 18, "bit": 28, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 39, "op": "add", "dst": 7, "src": 2, "src2": 4, "imm": "0xb8180e9d", "imm2": "0x32bbd117", "rot": 23, "bit": 19, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 40, "op": "rotr", "dst": 2, "src": 3, "src2": 0, "imm": "0x2d6070bc", "imm2": "0x68ff101e", "rot": 13, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 41, "op": "sub", "dst": 7, "src": 0, "src2": 2, "imm": "0x0daf96ea", "imm2": "0x36f37be1", "rot": 5, "bit": 0, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 42, "op": "add", "dst": 4, "src": 3, "src2": 6, "imm": "0x6ced15b7", "imm2": "0x6df7aed4", "rot": 19, "bit": 4, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 43, "op": "shfl", "dst": 7, "src": 3, "src2": 5, "imm": "0x8ace05f3", "imm2": "0xd378ec12", "rot": 23, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 44, "op": "load", "dst": 0, "src": 7, "src2": 4, "imm": "0xb0607786", "imm2": "0xc4acabbc", "rot": 13, "bit": 7, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 45, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 46, "op": "load", "dst": 3, "src": 1, "src2": 0, "imm": "0x63cc1e4e", "imm2": "0xa1be8118", "rot": 12, "bit": 6, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 47, "op": "load", "dst": 6, "src": 3, "src2": 7, "imm": "0x353f1d79", "imm2": "0x3b2e7456", "rot": 18, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 48, "op": "mulhi", "dst": 4, "src": 2, "src2": 7, "imm": "0x00d8a3cd", "imm2": "0x231866d2", "rot": 21, "bit": 20, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 49, "op": "add", "dst": 5, "src": 0, "src2": 2, "imm": "0xa8bae6df", "imm2": "0xf572bdb9", "rot": 14, "bit": 7, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 50, "op": "shfl", "dst": 0, "src": 7, "src2": 7, "imm": "0x81ef22e1", "imm2": "0x74438fc5", "rot": 28, "bit": 18, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 51, "op": "add", "dst": 6, "src": 0, "src2": 6, "imm": "0x383b9260", "imm2": "0x11e17c61", "rot": 12, "bit": 19, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 52, "op": "load", "dst": 5, "src": 2, "src2": 2, "imm": "0xfb84f451", "imm2": "0x11cd863e", "rot": 21, "bit": 20, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 53, "op": "rotl", "dst": 6, "src": 5, "src2": 4, "imm": "0xb1a7db6b", "imm2": "0x76686b9b", "rot": 12, "bit": 4, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 54, "op": "rotl", "dst": 3, "src": 6, "src2": 3, "imm": "0x6f981f52", "imm2": "0xd99aeba2", "rot": 12, "bit": 27, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 55, "op": "add", "dst": 2, "src": 1, "src2": 2, "imm": "0xac6be8e3", "imm2": "0x18d67dbb", "rot": 26, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 56, "op": "load", "dst": 1, "src": 4, "src2": 0, "imm": "0x7e7f6a00", "imm2": "0x6f0747da", "rot": 25, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 57, "op": "add", "dst": 5, "src": 0, "src2": 4, "imm": "0xf03673fe", "imm2": "0xa75cd60d", "rot": 16, "bit": 12, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 58, "op": "load", "dst": 5, "src": 0, "src2": 2, "imm": "0x227f94a6", "imm2": "0x0e8344f9", "rot": 20, "bit": 10, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 59, "op": "add", "dst": 1, "src": 4, "src2": 3, "imm": "0xdecd4794", "imm2": "0x8dfb96bb", "rot": 21, "bit": 7, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 60, "op": "mulhi", "dst": 3, "src": 2, "src2": 2, "imm": "0x0dd268e0", "imm2": "0x53034ca9", "rot": 1, "bit": 8, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 61, "op": "or", "dst": 6, "src": 4, "src2": 7, "imm": "0x3a45a321", "imm2": "0x9bc59a5f", "rot": 25, "bit": 11, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 62, "op": "shfl", "dst": 5, "src": 4, "src2": 2, "imm": "0x8f229cc1", "imm2": "0xcaac64a2", "rot": 17, "bit": 13, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0xb2574178", "imm2": "0xcbcc798d", "rot": 28, "bit": 0, "mask": 16, "width": 1, "win": 1, "off": 1} ] } diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.metal b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.metal index b899adec..8c0a2d1c 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.metal +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.metal @@ -39,69 +39,69 @@ kernel void igneum_hash(device const uint* dataset [[buffer(0)]], for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 - r2 = r2 ^ simd_shuffle_xor(r0, (ushort)4); // 1 - r3 = r3 + r2 + select(0x2cccb6cau, 0x642e66dbu, ((sel >> 10u) & 1u) != 0u); // 2 - r0 = rotl_imm(r0, 19u); // 3 - r7 = rotr_var(r7, r6); // 4 - r7 = r7 + r4 + select(0xee02465fu, 0xc1535555u, ((sel >> 21u) & 1u) != 0u); // 5 - r1 = mulhi(r1, r7); // 6 - r4 = r4 ^ dataset[r2 & MASK]; // 7 - r7 = r7 ^ dataset[r4 & MASK]; // 8 - r0 = r0 ^ dataset[r3 & MASK]; // 9 - r5 = r5 ^ dataset[r1 & MASK]; // 10 - r1 = r1 ^ dataset[r5 & MASK]; // 11 - r3 = mulhi(r3, r5); // 12 - r1 = r1 ^ dataset[r3 & MASK]; // 13 - r0 = r0 - r3; // 14 - r5 = r1 * r3 + r5; // 15 - r6 = mulhi(r6, r1); // 16 - r5 = r5 + r2 + select(0x697b3d00u, 0x8b965b57u, ((sel >> 28u) & 1u) != 0u); // 17 - r0 = mulhi(r0, r6); // 18 - r5 = rotr_var(r5, r3); // 19 - r5 = mulhi(r5, r2); // 20 - r1 = r1 + r0 + select(0xebcf247au, 0x6d7e8d05u, ((sel >> 1u) & 1u) != 0u); // 21 - r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 22 - r1 = mulhi(r1, r5); // 23 - r2 = r2 - r5; // 24 - r7 = r7 + r4 + select(0x08ffa6c7u, 0x699ef1bbu, ((sel >> 2u) & 1u) != 0u); // 25 - r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 26 - r7 = r7 + r1 + select(0xe60fea84u, 0xb4ead2fbu, ((sel >> 14u) & 1u) != 0u); // 27 - r3 = r3 + r1 + select(0x65c76dabu, 0x8f30d21du, ((sel >> 6u) & 1u) != 0u); // 28 - r2 = r2 ^ dataset[r1 & MASK]; // 29 - r5 = r5 ^ dataset[r7 & MASK]; // 30 - r2 = r2 ^ dataset[r5 & MASK]; // 31 - r1 = r1 ^ simd_shuffle_xor(r7, (ushort)4); // 32 - r4 = r5 * r7 + r4; // 33 - r4 = r4 + r2 + select(0x0480debeu, 0xc7ce690cu, ((sel >> 21u) & 1u) != 0u); // 34 - r3 = r3 ^ simd_shuffle_xor(r7, (ushort)8); // 35 - r7 = r7 + r1 + select(0xc53b542eu, 0xe10c2c95u, ((sel >> 2u) & 1u) != 0u); // 36 - r5 = r5 ^ r7; // 37 - r2 = r2 | r1; // 38 - r1 = mulhi(r1, r0); // 39 - r6 = rotl_imm(r6, 19u); // 40 - r4 = mulhi(r4, r6); // 41 - r6 = r6 - r0; // 42 - r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 43 - r4 = r4 ^ dataset[r2 & MASK]; // 44 - r1 = r1 ^ r3; // 45 - r7 = r7 ^ dataset[r0 & MASK]; // 46 - r3 = r3 ^ dataset[r1 & MASK]; // 47 - r5 = r5 * r3; // 48 - r1 = r1 - r5; // 49 - r2 = rotl_imm(r2, 8u); // 50 - r1 = r1 + r5 + select(0xa900fec4u, 0x77b9bd43u, ((sel >> 23u) & 1u) != 0u); // 51 - r4 = r4 ^ dataset[r7 & MASK]; // 52 - r2 = r2 - r7; // 53 - r4 = r4 ^ r0; // 54 - r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 55 - r2 = r2 ^ dataset[r4 & MASK]; // 56 - r0 = r1 * r4 + r0; // 57 - r3 = r3 ^ dataset[r5 & MASK]; // 58 - r5 = r5 | r6; // 59 - r6 = r5 * r7 + r6; // 60 - r4 = rotl_imm(r4, 28u); // 61 - r5 = mulhi(r5, r0); // 62 - r3 = r3 ^ dataset[r6 & MASK]; // 63 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program_bound.metal b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program_bound.metal index 2f021814..bc806a1e 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program_bound.metal +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program_bound.metal @@ -41,69 +41,69 @@ kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 - r2 = r2 ^ simd_shuffle_xor(r0, (ushort)4); // 1 - r3 = r3 + r2 + select(0x2cccb6cau, 0x642e66dbu, ((sel >> 10u) & 1u) != 0u); // 2 - r0 = rotl_imm(r0, 19u); // 3 - r7 = rotr_var(r7, r6); // 4 - r7 = r7 + r4 + select(0xee02465fu, 0xc1535555u, ((sel >> 21u) & 1u) != 0u); // 5 - r1 = mulhi(r1, r7); // 6 - r4 = r4 ^ dataset[r2 & MASK]; // 7 - r7 = r7 ^ dataset[r4 & MASK]; // 8 - r0 = r0 ^ dataset[r3 & MASK]; // 9 - r5 = r5 ^ dataset[r1 & MASK]; // 10 - r1 = r1 ^ dataset[r5 & MASK]; // 11 - r3 = mulhi(r3, r5); // 12 - r1 = r1 ^ dataset[r3 & MASK]; // 13 - r0 = r0 - r3; // 14 - r5 = r1 * r3 + r5; // 15 - r6 = mulhi(r6, r1); // 16 - r5 = r5 + r2 + select(0x697b3d00u, 0x8b965b57u, ((sel >> 28u) & 1u) != 0u); // 17 - r0 = mulhi(r0, r6); // 18 - r5 = rotr_var(r5, r3); // 19 - r5 = mulhi(r5, r2); // 20 - r1 = r1 + r0 + select(0xebcf247au, 0x6d7e8d05u, ((sel >> 1u) & 1u) != 0u); // 21 - r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 22 - r1 = mulhi(r1, r5); // 23 - r2 = r2 - r5; // 24 - r7 = r7 + r4 + select(0x08ffa6c7u, 0x699ef1bbu, ((sel >> 2u) & 1u) != 0u); // 25 - r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 26 - r7 = r7 + r1 + select(0xe60fea84u, 0xb4ead2fbu, ((sel >> 14u) & 1u) != 0u); // 27 - r3 = r3 + r1 + select(0x65c76dabu, 0x8f30d21du, ((sel >> 6u) & 1u) != 0u); // 28 - r2 = r2 ^ dataset[r1 & MASK]; // 29 - r5 = r5 ^ dataset[r7 & MASK]; // 30 - r2 = r2 ^ dataset[r5 & MASK]; // 31 - r1 = r1 ^ simd_shuffle_xor(r7, (ushort)4); // 32 - r4 = r5 * r7 + r4; // 33 - r4 = r4 + r2 + select(0x0480debeu, 0xc7ce690cu, ((sel >> 21u) & 1u) != 0u); // 34 - r3 = r3 ^ simd_shuffle_xor(r7, (ushort)8); // 35 - r7 = r7 + r1 + select(0xc53b542eu, 0xe10c2c95u, ((sel >> 2u) & 1u) != 0u); // 36 - r5 = r5 ^ r7; // 37 - r2 = r2 | r1; // 38 - r1 = mulhi(r1, r0); // 39 - r6 = rotl_imm(r6, 19u); // 40 - r4 = mulhi(r4, r6); // 41 - r6 = r6 - r0; // 42 - r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 43 - r4 = r4 ^ dataset[r2 & MASK]; // 44 - r1 = r1 ^ r3; // 45 - r7 = r7 ^ dataset[r0 & MASK]; // 46 - r3 = r3 ^ dataset[r1 & MASK]; // 47 - r5 = r5 * r3; // 48 - r1 = r1 - r5; // 49 - r2 = rotl_imm(r2, 8u); // 50 - r1 = r1 + r5 + select(0xa900fec4u, 0x77b9bd43u, ((sel >> 23u) & 1u) != 0u); // 51 - r4 = r4 ^ dataset[r7 & MASK]; // 52 - r2 = r2 - r7; // 53 - r4 = r4 ^ r0; // 54 - r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 55 - r2 = r2 ^ dataset[r4 & MASK]; // 56 - r0 = r1 * r4 + r0; // 57 - r3 = r3 ^ dataset[r5 & MASK]; // 58 - r5 = r5 | r6; // 59 - r6 = r5 * r7 + r6; // 60 - r4 = rotl_imm(r4, 28u); // 61 - r5 = mulhi(r5, r0); // 62 - r3 = r3 ^ dataset[r6 & MASK]; // 63 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.h b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.h index 106bdc34..9f27cdee 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.h +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.h @@ -11,29 +11,29 @@ static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { { // base nonce 0 - 0x212c6442b51e87aeull, 0xc374795c00839331ull, 0xb6036a220a98f4b3ull, 0xeb8b8013e637367bull, 0xdb5866e9b73930fdull, 0xf3f3d01f46e90333ull, 0x9d913991ab8ed428ull, 0x7ccb1d8fa100a800ull, - 0x3cf45ba44f09a0feull, 0x91acf48ef1a63082ull, 0x6ea46c69fb082f99ull, 0x581f0218977a9d72ull, 0x9a4623a5c62ddf2dull, 0xab6eb5e768f0feb4ull, 0x07b70bdccf8aca12ull, 0xd666311ae5e4311eull, - 0x53114757d669f0a4ull, 0xbd5d6ace87ce2ce4ull, 0xfb712015e8189192ull, 0xa32cec81103e134bull, 0x83f3d18c3289c124ull, 0xfe29f1984b132b3dull, 0xc9ffcf4e3774497aull, 0xac99c9243dc63809ull, - 0xd78a7e8217a32f3cull, 0xab81ad63d242fc31ull, 0x0e4c30b7e00024afull, 0xce014289fff6778dull, 0x64a1292e2a8b4a91ull, 0xd5b8c90e681d7e3aull, 0x06078117673030fdull, 0x51bf77b280173930ull + 0xc478aa7b014b314full, 0x5f3b248ea1746690ull, 0x8b8e8d9ea2b748aaull, 0x5feb8eb98d85f482ull, 0x45c41509215309bdull, 0x5e2867e2fe2eed4aull, 0x8aab5a828a90a606ull, 0x570e96ddd777c289ull, + 0x6286e93f7c85d849ull, 0x566c00af118a621aull, 0x5a90210268f469cfull, 0x485984b2c57fc4c3ull, 0x1b42cd7bf2052143ull, 0x61754006818ac84bull, 0x5ab1a0391203c228ull, 0x75085f9f4fec3a81ull, + 0x2d02e728ee7f7b3cull, 0x2bc86c904068d09eull, 0x5e41fedd34afbd06ull, 0xa7792d807213f5b5ull, 0xe22d665d69e17c16ull, 0xb52880c283c7da56ull, 0xdb005f50a716a881ull, 0xea72f35c9f5309bbull, + 0x240401c19d2cab92ull, 0xc83d663994524ce8ull, 0x771c1e871be79867ull, 0xda09af3bd47125f5ull, 0x64d4b67009887d73ull, 0xd6373f5d4afd9c07ull, 0x61ef84e034a884ddull, 0x14c268b894bcd1a4ull }, { // base nonce 4096 - 0x3c797978566b5950ull, 0x7c759e60185b6411ull, 0x199136b84028d653ull, 0x0ad7b60cb722da50ull, 0x93e5fd443b85acc5ull, 0x4a0fadca2f0b7dc6ull, 0x91b5f2478c1fbd4full, 0xe3debc1f1cdc889aull, - 0x9c39b1635a6aec09ull, 0xbeaea0ab408df45aull, 0x080283b6f52a494aull, 0x9acae9a878c16f2eull, 0xc37dc4e098fee807ull, 0xf3daf9ff09a2c0e5ull, 0xfff82c3572c8f362ull, 0xd96414dfa0b4bd51ull, - 0xecf77498c26cc5abull, 0x72945c7a2e52a90cull, 0xe5b6749102690fa4ull, 0x90eddb84d13a2b9full, 0x6e9186a56dc002c1ull, 0xfa2047ec0ae61fe3ull, 0xa8cd4efd68b7eff5ull, 0xd23b650a6345bf73ull, - 0xe1141a33ea09bae1ull, 0x0e9aae812f5d762aull, 0x9ae3811dcc68797full, 0x0a4dab24c1efff6full, 0xa22d476bc42b9064ull, 0xc7a16d6f59414a2eull, 0x5aff92ecae652329ull, 0x96a903eb9a0ca390ull + 0x613fdff51675cbc5ull, 0x33542a1c13240331ull, 0xc12327f1c985c157ull, 0x5983d76c84850677ull, 0x07b876d962db9aeeull, 0x061556b249a2f4edull, 0x7a6a864253cbb666ull, 0x202a4c3f7caf4842ull, + 0xaf47fb1b6f8fd897ull, 0xed86312481032b66ull, 0xe10287d2dc43f393ull, 0x4366d593f75c8a9eull, 0xc265f45fc11562f9ull, 0x23a5ae8634848667ull, 0xb75271a5e58e2e07ull, 0xe2dca00457ff11eeull, + 0x12610d35f18ee235ull, 0x13c1d91de3a5590dull, 0x7af3bbc2e82ce300ull, 0x2725a442d7e70923ull, 0x3938551cfcce0b5eull, 0xb525335b7e13a279ull, 0xf2951f26f6130dcdull, 0x807acf761deff319ull, + 0xafcb894ac5e8cba9ull, 0x1408e0631222a04dull, 0x43a6f79feff89569ull, 0xba577b357fd3b1fcull, 0x424ce7a84318939full, 0xef8f70968010b59eull, 0x8c8a5bcfb16c5a97ull, 0xf2aae270614f2559ull }, { // base nonce 1000000 - 0xd5a8da0568df8ee7ull, 0x68cb69c04208285aull, 0xe3e0c57190a809dbull, 0x87ba27d76aa356d5ull, 0x5a30616d4b8bfcf2ull, 0x66f5d2c31997fd24ull, 0x0447779e26a2854aull, 0x1225eb67933b2faeull, - 0xde78d94be53c669bull, 0x02a3fe00eb77c8cdull, 0x4fd029ba4cfc526aull, 0x2ef8ea603a0463d6ull, 0x345cff9b465b6f12ull, 0x03bb80d5a700b7feull, 0xf7da33ca2094ae26ull, 0xb3adaaa8383ad2e9ull, - 0x68d5eafde2924d62ull, 0xeab53a1b039835e0ull, 0xfd04e678835cb646ull, 0xf25b86a707f333c0ull, 0xa3bb24e6981917deull, 0x791d38b29dd6055aull, 0xfd18f01f064bdbcaull, 0xee051af1de1b70f9ull, - 0x246bfe79acd966eaull, 0xbcb8105b8a73efedull, 0x9b7e8c6fcc25fa92ull, 0xa0ffe2d9e4717892ull, 0x22f52e2324035114ull, 0xcdac38103479920dull, 0x49477ffa08ac4a6cull, 0xf8ca84a1a5d78cf5ull + 0xc3b0f31bc9c9a941ull, 0x3dc9db6098337122ull, 0x88ed65e7030bf6e7ull, 0xc3afffd7ef432358ull, 0x4d63429d0816cad6ull, 0xb606c57fc50dfd1bull, 0xbdb00eeb9bd26950ull, 0x379d208ba4c59b68ull, + 0x1b16aa85bffae547ull, 0xf7985ad46430cf69ull, 0x8b5c01051cc55d79ull, 0x0aae8efd1fad2942ull, 0x1256bc0bdfa48f58ull, 0xe657eab8150aa145ull, 0xef6dad537975a2c8ull, 0x805fbcfbccd6710cull, + 0x40be9f4eb86765c6ull, 0x84a5731b7e4ecdacull, 0xd728fb6b057d6ef5ull, 0xa40663757a846c1bull, 0xa38a545e91fd339cull, 0x31629cfa5d2d5d6bull, 0x5d0f4f101daa520full, 0xe57bb2de8033df0full, + 0x892443887a68e483ull, 0xe5066fc7e6b374bfull, 0x5f443c99c6d0f851ull, 0xeaa61aa7109c0fe5ull, 0x9b4a43a24c80dfe6ull, 0x69ad31dea437a76eull, 0x7cb849f7209c482cull, 0x0c42712849a48773ull } }; // Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). static const uint32_t IGNEUM_DS_HEAD[16] = { - 0xafe80d67u, 0xb9fbd029u, 0x6c79f193u, 0x95139ad9u, 0x96310affu, 0x4609f8b1u, 0x75279e63u, 0x28235be1u, - 0x47b17dcbu, 0x718e0ef2u, 0xa52588c8u, 0xa8bf49d5u, 0x19cf243eu, 0x5ec8905eu, 0xa4851f66u, 0xaf9cd9f3u + 0xafe80d67u, 0xb9fbd029u, 0x7155550bu, 0x32965d99u, 0x6c79f193u, 0x95139ad9u, 0x71bdb5ddu, 0x7de50b28u, + 0xf4980f73u, 0x01fd5739u, 0x5b410497u, 0xe4c84638u, 0x08d1d374u, 0x29058a56u, 0xee171b93u, 0xa4fed894u }; static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; static const uint32_t IGNEUM_DS_LAST = 0xe6a99c7au; @@ -43,7 +43,7 @@ static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u }; static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { - 0x57642b58u, 0xc279baddu, 0xcbccbaadu, 0x32cce392u, 0x71fdb4c6u, 0xf5a268ceu, 0x3ca1d676u, 0x7977b03du, 0xb73a6cb1u, 0xe773c5c9u, 0x2533a3f1u, 0xd7c48638u, 0xfc80830au, 0xab3874e9u, 0xc093812eu, 0xb71e49d6u, 0x0e151e7bu, 0x395af161u, 0x7156ef4eu, 0x9202463fu, 0x562e88c8u, 0xb9b0c5b3u, 0xe3f6102fu, 0xe3a94cbbu, 0x9e26c494u, 0x47ace326u, 0xe20ea113u, 0xce4eefa1u, 0x97a41cc9u, 0xa6d51cb5u, 0x71f3df27u, 0x115df551u, 0x20e273b8u, 0x50bf99ffu, 0xc9b1ae13u, 0x342160c6u, 0xc1b8eb36u, 0x9b11737bu, 0x4b73d48fu, 0x455e9b9eu, 0xe5213a1cu, 0xf7049bc3u, 0x0a026f93u, 0x156a8a99u, 0x1029fe6cu, 0x4f29d205u, 0x273315edu, 0x483e85a2u, 0xec802349u, 0x76023cccu, 0xe37e8135u, 0x0881d277u, 0x56ba9c69u, 0x31ad3b78u, 0xc8ad9363u, 0x8647e3c0u, 0x12e78b02u, 0x2d3a1b5fu, 0xa75e9390u, 0xbe0cebd8u, 0x759af86du, 0x9b4bd567u, 0xd272d0aeu, 0x7a18d82eu + 0xaade6dc9u, 0x1da7efe9u, 0xc41ae56eu, 0xf25480a3u, 0x0d628857u, 0xc506bba7u, 0xfd9b5dc6u, 0xac36cef7u, 0xabb0c725u, 0x2169d0a5u, 0x79f374c8u, 0xd4517909u, 0x105cc0a8u, 0xa5c081f8u, 0x5d91aef0u, 0x13c5930au, 0x9d49f3fcu, 0x9da43ab7u, 0x28bc6d8fu, 0x135b0b0bu, 0x428a8bd8u, 0xc9f90868u, 0x83328bcfu, 0xb02ef21eu, 0xee8098cdu, 0xf0c365feu, 0x32138523u, 0x26c4c59au, 0xd11deb96u, 0x9e1cd011u, 0x0a83dcd4u, 0x6fbf8ce3u, 0x7bf6dd56u, 0xf079569eu, 0x98d970acu, 0xd6feefdfu, 0xbe12559bu, 0x90e64549u, 0x81238e75u, 0x66b80a1du, 0x6637db36u, 0x6682fda0u, 0x0b0b6b0fu, 0xdebcaa14u, 0x78b382b9u, 0xa92cda07u, 0x693ae44bu, 0x13d96cceu, 0x0ad96728u, 0x4615abe5u, 0xc8ef2953u, 0x226c9b04u, 0xea5fe9b4u, 0xa57e1812u, 0x0cfc72c0u, 0x35dfac56u, 0x8d0ae5a7u, 0xfd635e70u, 0xf14b2d3fu, 0x56fb5ff4u, 0x2340e755u, 0x4a4a0aadu, 0x1abd16edu, 0xa7790ffeu }; // Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. static const uint32_t IGNEUM_CACHE_HEAD[16] = { diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.json b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.json index 5060023f..fa018775 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.json +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.json @@ -8,28 +8,28 @@ "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", "warps": [ {"base_nonce": 0, "expected": [ - "0x212c6442b51e87ae", "0xc374795c00839331", "0xb6036a220a98f4b3", "0xeb8b8013e637367b", "0xdb5866e9b73930fd", "0xf3f3d01f46e90333", "0x9d913991ab8ed428", "0x7ccb1d8fa100a800", - "0x3cf45ba44f09a0fe", "0x91acf48ef1a63082", "0x6ea46c69fb082f99", "0x581f0218977a9d72", "0x9a4623a5c62ddf2d", "0xab6eb5e768f0feb4", "0x07b70bdccf8aca12", "0xd666311ae5e4311e", - "0x53114757d669f0a4", "0xbd5d6ace87ce2ce4", "0xfb712015e8189192", "0xa32cec81103e134b", "0x83f3d18c3289c124", "0xfe29f1984b132b3d", "0xc9ffcf4e3774497a", "0xac99c9243dc63809", - "0xd78a7e8217a32f3c", "0xab81ad63d242fc31", "0x0e4c30b7e00024af", "0xce014289fff6778d", "0x64a1292e2a8b4a91", "0xd5b8c90e681d7e3a", "0x06078117673030fd", "0x51bf77b280173930" + "0xc478aa7b014b314f", "0x5f3b248ea1746690", "0x8b8e8d9ea2b748aa", "0x5feb8eb98d85f482", "0x45c41509215309bd", "0x5e2867e2fe2eed4a", "0x8aab5a828a90a606", "0x570e96ddd777c289", + "0x6286e93f7c85d849", "0x566c00af118a621a", "0x5a90210268f469cf", "0x485984b2c57fc4c3", "0x1b42cd7bf2052143", "0x61754006818ac84b", "0x5ab1a0391203c228", "0x75085f9f4fec3a81", + "0x2d02e728ee7f7b3c", "0x2bc86c904068d09e", "0x5e41fedd34afbd06", "0xa7792d807213f5b5", "0xe22d665d69e17c16", "0xb52880c283c7da56", "0xdb005f50a716a881", "0xea72f35c9f5309bb", + "0x240401c19d2cab92", "0xc83d663994524ce8", "0x771c1e871be79867", "0xda09af3bd47125f5", "0x64d4b67009887d73", "0xd6373f5d4afd9c07", "0x61ef84e034a884dd", "0x14c268b894bcd1a4" ]}, {"base_nonce": 4096, "expected": [ - "0x3c797978566b5950", "0x7c759e60185b6411", "0x199136b84028d653", "0x0ad7b60cb722da50", "0x93e5fd443b85acc5", "0x4a0fadca2f0b7dc6", "0x91b5f2478c1fbd4f", "0xe3debc1f1cdc889a", - "0x9c39b1635a6aec09", "0xbeaea0ab408df45a", "0x080283b6f52a494a", "0x9acae9a878c16f2e", "0xc37dc4e098fee807", "0xf3daf9ff09a2c0e5", "0xfff82c3572c8f362", "0xd96414dfa0b4bd51", - "0xecf77498c26cc5ab", "0x72945c7a2e52a90c", "0xe5b6749102690fa4", "0x90eddb84d13a2b9f", "0x6e9186a56dc002c1", "0xfa2047ec0ae61fe3", "0xa8cd4efd68b7eff5", "0xd23b650a6345bf73", - "0xe1141a33ea09bae1", "0x0e9aae812f5d762a", "0x9ae3811dcc68797f", "0x0a4dab24c1efff6f", "0xa22d476bc42b9064", "0xc7a16d6f59414a2e", "0x5aff92ecae652329", "0x96a903eb9a0ca390" + "0x613fdff51675cbc5", "0x33542a1c13240331", "0xc12327f1c985c157", "0x5983d76c84850677", "0x07b876d962db9aee", "0x061556b249a2f4ed", "0x7a6a864253cbb666", "0x202a4c3f7caf4842", + "0xaf47fb1b6f8fd897", "0xed86312481032b66", "0xe10287d2dc43f393", "0x4366d593f75c8a9e", "0xc265f45fc11562f9", "0x23a5ae8634848667", "0xb75271a5e58e2e07", "0xe2dca00457ff11ee", + "0x12610d35f18ee235", "0x13c1d91de3a5590d", "0x7af3bbc2e82ce300", "0x2725a442d7e70923", "0x3938551cfcce0b5e", "0xb525335b7e13a279", "0xf2951f26f6130dcd", "0x807acf761deff319", + "0xafcb894ac5e8cba9", "0x1408e0631222a04d", "0x43a6f79feff89569", "0xba577b357fd3b1fc", "0x424ce7a84318939f", "0xef8f70968010b59e", "0x8c8a5bcfb16c5a97", "0xf2aae270614f2559" ]}, {"base_nonce": 1000000, "expected": [ - "0xd5a8da0568df8ee7", "0x68cb69c04208285a", "0xe3e0c57190a809db", "0x87ba27d76aa356d5", "0x5a30616d4b8bfcf2", "0x66f5d2c31997fd24", "0x0447779e26a2854a", "0x1225eb67933b2fae", - "0xde78d94be53c669b", "0x02a3fe00eb77c8cd", "0x4fd029ba4cfc526a", "0x2ef8ea603a0463d6", "0x345cff9b465b6f12", "0x03bb80d5a700b7fe", "0xf7da33ca2094ae26", "0xb3adaaa8383ad2e9", - "0x68d5eafde2924d62", "0xeab53a1b039835e0", "0xfd04e678835cb646", "0xf25b86a707f333c0", "0xa3bb24e6981917de", "0x791d38b29dd6055a", "0xfd18f01f064bdbca", "0xee051af1de1b70f9", - "0x246bfe79acd966ea", "0xbcb8105b8a73efed", "0x9b7e8c6fcc25fa92", "0xa0ffe2d9e4717892", "0x22f52e2324035114", "0xcdac38103479920d", "0x49477ffa08ac4a6c", "0xf8ca84a1a5d78cf5" + "0xc3b0f31bc9c9a941", "0x3dc9db6098337122", "0x88ed65e7030bf6e7", "0xc3afffd7ef432358", "0x4d63429d0816cad6", "0xb606c57fc50dfd1b", "0xbdb00eeb9bd26950", "0x379d208ba4c59b68", + "0x1b16aa85bffae547", "0xf7985ad46430cf69", "0x8b5c01051cc55d79", "0x0aae8efd1fad2942", "0x1256bc0bdfa48f58", "0xe657eab8150aa145", "0xef6dad537975a2c8", "0x805fbcfbccd6710c", + "0x40be9f4eb86765c6", "0x84a5731b7e4ecdac", "0xd728fb6b057d6ef5", "0xa40663757a846c1b", "0xa38a545e91fd339c", "0x31629cfa5d2d5d6b", "0x5d0f4f101daa520f", "0xe57bb2de8033df0f", + "0x892443887a68e483", "0xe5066fc7e6b374bf", "0x5f443c99c6d0f851", "0xeaa61aa7109c0fe5", "0x9b4a43a24c80dfe6", "0x69ad31dea437a76e", "0x7cb849f7209c482c", "0x0c42712849a48773" ]} ], - "dataset_head": ["0xafe80d67", "0xb9fbd029", "0x6c79f193", "0x95139ad9", "0x96310aff", "0x4609f8b1", "0x75279e63", "0x28235be1", "0x47b17dcb", "0x718e0ef2", "0xa52588c8", "0xa8bf49d5", "0x19cf243e", "0x5ec8905e", "0xa4851f66", "0xaf9cd9f3"], + "dataset_head": ["0xafe80d67", "0xb9fbd029", "0x7155550b", "0x32965d99", "0x6c79f193", "0x95139ad9", "0x71bdb5dd", "0x7de50b28", "0xf4980f73", "0x01fd5739", "0x5b410497", "0xe4c84638", "0x08d1d374", "0x29058a56", "0xee171b93", "0xa4fed894"], "dataset_last_index": 268435455, "dataset_last": "0xe6a99c7a", - "dataset_samples": [{"index": 59471966, "value": "0x57642b58"}, {"index": 217795994, "value": "0xc279badd"}, {"index": 208353206, "value": "0xcbccbaad"}, {"index": 42483309, "value": "0x32cce392"}, {"index": 172547758, "value": "0x71fdb4c6"}, {"index": 148076330, "value": "0xf5a268ce"}, {"index": 183853158, "value": "0x3ca1d676"}, {"index": 214389424, "value": "0x7977b03d"}, {"index": 267488061, "value": "0xb73a6cb1"}, {"index": 169781097, "value": "0xe773c5c9"}, {"index": 184093494, "value": "0x2533a3f1"}, {"index": 153880993, "value": "0xd7c48638"}, {"index": 84977930, "value": "0xfc80830a"}, {"index": 46426879, "value": "0xab3874e9"}, {"index": 3093825, "value": "0xc093812e"}, {"index": 225364072, "value": "0xb71e49d6"}, {"index": 44593546, "value": "0x0e151e7b"}, {"index": 260713159, "value": "0x395af161"}, {"index": 168250303, "value": "0x7156ef4e"}, {"index": 52384140, "value": "0x9202463f"}, {"index": 223401610, "value": "0x562e88c8"}, {"index": 45554030, "value": "0xb9b0c5b3"}, {"index": 95410555, "value": "0xe3f6102f"}, {"index": 175039924, "value": "0xe3a94cbb"}, {"index": 79171087, "value": "0x9e26c494"}, {"index": 267580473, "value": "0x47ace326"}, {"index": 24168642, "value": "0xe20ea113"}, {"index": 37981670, "value": "0xce4eefa1"}, {"index": 171551130, "value": "0x97a41cc9"}, {"index": 195559979, "value": "0xa6d51cb5"}, {"index": 204611762, "value": "0x71f3df27"}, {"index": 140997658, "value": "0x115df551"}, {"index": 138925853, "value": "0x20e273b8"}, {"index": 86637313, "value": "0x50bf99ff"}, {"index": 20736778, "value": "0xc9b1ae13"}, {"index": 219665210, "value": "0x342160c6"}, {"index": 160430336, "value": "0xc1b8eb36"}, {"index": 264654675, "value": "0x9b11737b"}, {"index": 8013395, "value": "0x4b73d48f"}, {"index": 228945585, "value": "0x455e9b9e"}, {"index": 213884386, "value": "0xe5213a1c"}, {"index": 104419827, "value": "0xf7049bc3"}, {"index": 44185464, "value": "0x0a026f93"}, {"index": 142737231, "value": "0x156a8a99"}, {"index": 99284897, "value": "0x1029fe6c"}, {"index": 132475900, "value": "0x4f29d205"}, {"index": 61861762, "value": "0x273315ed"}, {"index": 132056166, "value": "0x483e85a2"}, {"index": 262388043, "value": "0xec802349"}, {"index": 91878046, "value": "0x76023ccc"}, {"index": 117353561, "value": "0xe37e8135"}, {"index": 124768597, "value": "0x0881d277"}, {"index": 71352993, "value": "0x56ba9c69"}, {"index": 190698941, "value": "0x31ad3b78"}, {"index": 46055428, "value": "0xc8ad9363"}, {"index": 55281366, "value": "0x8647e3c0"}, {"index": 165145231, "value": "0x12e78b02"}, {"index": 106810753, "value": "0x2d3a1b5f"}, {"index": 171985651, "value": "0xa75e9390"}, {"index": 232085256, "value": "0xbe0cebd8"}, {"index": 159510492, "value": "0x759af86d"}, {"index": 40072060, "value": "0x9b4bd567"}, {"index": 209107596, "value": "0xd272d0ae"}, {"index": 39023794, "value": "0x7a18d82e"}], + "dataset_samples": [{"index": 59471966, "value": "0xaade6dc9"}, {"index": 217795994, "value": "0x1da7efe9"}, {"index": 208353206, "value": "0xc41ae56e"}, {"index": 42483309, "value": "0xf25480a3"}, {"index": 172547758, "value": "0x0d628857"}, {"index": 148076330, "value": "0xc506bba7"}, {"index": 183853158, "value": "0xfd9b5dc6"}, {"index": 214389424, "value": "0xac36cef7"}, {"index": 267488061, "value": "0xabb0c725"}, {"index": 169781097, "value": "0x2169d0a5"}, {"index": 184093494, "value": "0x79f374c8"}, {"index": 153880993, "value": "0xd4517909"}, {"index": 84977930, "value": "0x105cc0a8"}, {"index": 46426879, "value": "0xa5c081f8"}, {"index": 3093825, "value": "0x5d91aef0"}, {"index": 225364072, "value": "0x13c5930a"}, {"index": 44593546, "value": "0x9d49f3fc"}, {"index": 260713159, "value": "0x9da43ab7"}, {"index": 168250303, "value": "0x28bc6d8f"}, {"index": 52384140, "value": "0x135b0b0b"}, {"index": 223401610, "value": "0x428a8bd8"}, {"index": 45554030, "value": "0xc9f90868"}, {"index": 95410555, "value": "0x83328bcf"}, {"index": 175039924, "value": "0xb02ef21e"}, {"index": 79171087, "value": "0xee8098cd"}, {"index": 267580473, "value": "0xf0c365fe"}, {"index": 24168642, "value": "0x32138523"}, {"index": 37981670, "value": "0x26c4c59a"}, {"index": 171551130, "value": "0xd11deb96"}, {"index": 195559979, "value": "0x9e1cd011"}, {"index": 204611762, "value": "0x0a83dcd4"}, {"index": 140997658, "value": "0x6fbf8ce3"}, {"index": 138925853, "value": "0x7bf6dd56"}, {"index": 86637313, "value": "0xf079569e"}, {"index": 20736778, "value": "0x98d970ac"}, {"index": 219665210, "value": "0xd6feefdf"}, {"index": 160430336, "value": "0xbe12559b"}, {"index": 264654675, "value": "0x90e64549"}, {"index": 8013395, "value": "0x81238e75"}, {"index": 228945585, "value": "0x66b80a1d"}, {"index": 213884386, "value": "0x6637db36"}, {"index": 104419827, "value": "0x6682fda0"}, {"index": 44185464, "value": "0x0b0b6b0f"}, {"index": 142737231, "value": "0xdebcaa14"}, {"index": 99284897, "value": "0x78b382b9"}, {"index": 132475900, "value": "0xa92cda07"}, {"index": 61861762, "value": "0x693ae44b"}, {"index": 132056166, "value": "0x13d96cce"}, {"index": 262388043, "value": "0x0ad96728"}, {"index": 91878046, "value": "0x4615abe5"}, {"index": 117353561, "value": "0xc8ef2953"}, {"index": 124768597, "value": "0x226c9b04"}, {"index": 71352993, "value": "0xea5fe9b4"}, {"index": 190698941, "value": "0xa57e1812"}, {"index": 46055428, "value": "0x0cfc72c0"}, {"index": 55281366, "value": "0x35dfac56"}, {"index": 165145231, "value": "0x8d0ae5a7"}, {"index": 106810753, "value": "0xfd635e70"}, {"index": 171985651, "value": "0xf14b2d3f"}, {"index": 232085256, "value": "0x56fb5ff4"}, {"index": 159510492, "value": "0x2340e755"}, {"index": 40072060, "value": "0x4a4a0aad"}, {"index": 209107596, "value": "0x1abd16ed"}, {"index": 39023794, "value": "0xa7790ffe"}], "cache_head": ["0xebd9055c", "0x7eaf6b21", "0x9b610855", "0x134a8562", "0xb10059ba", "0x7f459e58", "0x8f3c7873", "0x3523e609", "0x75b8f4ca", "0x750d31b4", "0x0dae0782", "0x26f10015", "0x7ba87a41", "0x0efd8543", "0x691d6368", "0x8929d967"], "cache_last_line": ["0x51474edf", "0xc3cfba93", "0xf21454cf", "0x9b79baac", "0x4d4fce23", "0xd701dfd5", "0x37357ab3", "0x1be693fa", "0xa701cb3b", "0x7467c620", "0x428184e8", "0xf4010df0", "0xe33aa88f", "0xc78d6d62", "0xae9ba9c0", "0xf4bba97e"], "cache_fnv1a64": "0x448274a57f508cbc" diff --git a/proto-cuda/packs-readwidth/scr0k32/kernel.cl b/proto-cuda/packs-readwidth/scr0k32/kernel.cl index 040d671a..517a52f3 100644 --- a/proto-cuda/packs-readwidth/scr0k32/kernel.cl +++ b/proto-cuda/packs-readwidth/scr0k32/kernel.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 256u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] diff --git a/proto-cuda/packs-readwidth/scr0k32/kernel_bound.cl b/proto-cuda/packs-readwidth/scr0k32/kernel_bound.cl index 057f228d..1b033b3c 100644 --- a/proto-cuda/packs-readwidth/scr0k32/kernel_bound.cl +++ b/proto-cuda/packs-readwidth/scr0k32/kernel_bound.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 256u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] @@ -296,20 +296,20 @@ IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulon uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 256u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; - uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } diff --git a/proto-cuda/packs-readwidth/scr2k128/kernel.cl b/proto-cuda/packs-readwidth/scr2k128/kernel.cl index 8fc591da..706ed7e3 100644 --- a/proto-cuda/packs-readwidth/scr2k128/kernel.cl +++ b/proto-cuda/packs-readwidth/scr2k128/kernel.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 1024u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] diff --git a/proto-cuda/packs-readwidth/scr2k128/kernel_bound.cl b/proto-cuda/packs-readwidth/scr2k128/kernel_bound.cl index b08bc77a..314afb38 100644 --- a/proto-cuda/packs-readwidth/scr2k128/kernel_bound.cl +++ b/proto-cuda/packs-readwidth/scr2k128/kernel_bound.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 1024u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] @@ -296,20 +296,20 @@ IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulon uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 1024u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; - uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } diff --git a/proto-cuda/packs-readwidth/scr2k32/kernel.cl b/proto-cuda/packs-readwidth/scr2k32/kernel.cl index 6befdde0..59e009d7 100644 --- a/proto-cuda/packs-readwidth/scr2k32/kernel.cl +++ b/proto-cuda/packs-readwidth/scr2k32/kernel.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 256u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] diff --git a/proto-cuda/packs-readwidth/scr2k32/kernel_bound.cl b/proto-cuda/packs-readwidth/scr2k32/kernel_bound.cl index 9fb87c71..fa26b1c7 100644 --- a/proto-cuda/packs-readwidth/scr2k32/kernel_bound.cl +++ b/proto-cuda/packs-readwidth/scr2k32/kernel_bound.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 256u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] @@ -296,20 +296,20 @@ IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulon uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 256u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; - uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } diff --git a/proto-cuda/packs-readwidth/scr4k128/kernel.cl b/proto-cuda/packs-readwidth/scr4k128/kernel.cl index 3917a927..babcdd9e 100644 --- a/proto-cuda/packs-readwidth/scr4k128/kernel.cl +++ b/proto-cuda/packs-readwidth/scr4k128/kernel.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 1024u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] diff --git a/proto-cuda/packs-readwidth/scr4k128/kernel_bound.cl b/proto-cuda/packs-readwidth/scr4k128/kernel_bound.cl index 669eb1d5..93b2e735 100644 --- a/proto-cuda/packs-readwidth/scr4k128/kernel_bound.cl +++ b/proto-cuda/packs-readwidth/scr4k128/kernel_bound.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 1024u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] @@ -296,20 +296,20 @@ IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulon uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 1024u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; - uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } diff --git a/proto-cuda/packs-readwidth/scr4k32/kernel.cl b/proto-cuda/packs-readwidth/scr4k32/kernel.cl index 00f78596..b1c967ee 100644 --- a/proto-cuda/packs-readwidth/scr4k32/kernel.cl +++ b/proto-cuda/packs-readwidth/scr4k32/kernel.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 256u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] diff --git a/proto-cuda/packs-readwidth/scr4k32/kernel_bound.cl b/proto-cuda/packs-readwidth/scr4k32/kernel_bound.cl index 46034e31..1090d77b 100644 --- a/proto-cuda/packs-readwidth/scr4k32/kernel_bound.cl +++ b/proto-cuda/packs-readwidth/scr4k32/kernel_bound.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 256u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] @@ -296,20 +296,20 @@ IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulon uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 256u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; - uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } diff --git a/proto-cuda/packs-readwidth/scr8k128/kernel.cl b/proto-cuda/packs-readwidth/scr8k128/kernel.cl index 03840512..09bfaa3c 100644 --- a/proto-cuda/packs-readwidth/scr8k128/kernel.cl +++ b/proto-cuda/packs-readwidth/scr8k128/kernel.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 1024u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] diff --git a/proto-cuda/packs-readwidth/scr8k128/kernel_bound.cl b/proto-cuda/packs-readwidth/scr8k128/kernel_bound.cl index 9cb72a38..ae27d866 100644 --- a/proto-cuda/packs-readwidth/scr8k128/kernel_bound.cl +++ b/proto-cuda/packs-readwidth/scr8k128/kernel_bound.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 1024u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] @@ -296,20 +296,20 @@ IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulon uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 1024u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; - uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } diff --git a/proto-cuda/packs-readwidth/scr8k32/kernel.cl b/proto-cuda/packs-readwidth/scr8k32/kernel.cl index 54168b39..39cddf51 100644 --- a/proto-cuda/packs-readwidth/scr8k32/kernel.cl +++ b/proto-cuda/packs-readwidth/scr8k32/kernel.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 256u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] diff --git a/proto-cuda/packs-readwidth/scr8k32/kernel_bound.cl b/proto-cuda/packs-readwidth/scr8k32/kernel_bound.cl index 922739e4..a7356de5 100644 --- a/proto-cuda/packs-readwidth/scr8k32/kernel_bound.cl +++ b/proto-cuda/packs-readwidth/scr8k32/kernel_bound.cl @@ -186,19 +186,19 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 256u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] @@ -296,20 +296,20 @@ IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulon uint warp_ = (uint)get_global_id(0) >> 5; uint nwarps_ = (uint)get_global_size(0) >> 5; __global uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 256u; - for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { - uint gid = g_ * 32u + lane; - uint gbase = baseNonce + g_ * 32u; - uint tag = salt + g_; uint lid = (uint)get_local_id(0); - uint nonce = baseNonce + gid; - uint r0, r1, r2, r3, r4, r5, r6, r7; - uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; #if IGNEUM_EXCHANGE == 0 IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); uint xk = 0u; #else (void)lid; #endif + for (uint g_ = warp_; g_ < groups; g_ += nwarps_) { + uint gid = g_ * 32u + lane; + uint gbase = baseNonce + g_ * 32u; + uint tag = salt + g_; + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } diff --git a/proto-opencl/host.c b/proto-opencl/host.c index 42b55485..8423180a 100644 --- a/proto-opencl/host.c +++ b/proto-opencl/host.c @@ -174,10 +174,10 @@ static uint64_t fnv1a64(const void* p, size_t n) { } static const uint32_t CACHE_WORDS_HOST = 1u << IGNEUM_CACHE_LOG2_WORDS; static uint32_t* hCache = NULL; +/* dataset[w] through the pack's own mh_word (memhard.h), which carries the pack's item-to-word layout (era layout, + * 5 October 2026; the former w >> 4 / w & 15 here failed the random points of every interleaved pack). */ static uint32_t host_ds_word(uint32_t w) { - uint32_t s[16]; - mh_item(hCache, w >> 4u, s); - return s[w & 15u]; + return mh_word(hCache, w); } #endif diff --git a/relay/playbooks/ca2-era-pc1.ps1 b/relay/playbooks/ca2-era-pc1.ps1 new file mode 100644 index 00000000..76b20ebc --- /dev/null +++ b/relay/playbooks/ca2-era-pc1.ps1 @@ -0,0 +1,93 @@ +# Igneum run job: the era layout experiment (Counter ASIC 2.0 layers 4 and 8, docs/plans/era-layout.md) on PC 1 +# (machine ae432dc7): the RTX 5090 through igneum-worker-cuda.exe --bench, then the AMD card through +# igneum-worker-opencl.exe --bench-pack, each card switched off in the app ONLY while it is under test and restored +# after with the settings it had. 5 October 2026. The AMD card: the RX 9070 XT (gfx1201) when its eGPU box is on the +# bus (it dropped from the USB4 link at about 20:40 UTC on 5 October), else the integrated gfx1036 (gate G1's AMD +# vendor by the coordinator's ruling): bit-exactness (vectors, the 2^24 fingerprint at base 0 equal to the 5090's +# and the Mac's) with fewer dispatches, its rate a reference only. +# Published as a plain `run` job (NOT --stop-miners): the installed app keeps every other card mining. Packs: the v2 +# baseline (igneum-devnet-v4-epoch0, the same epoch seed and day) and era-0 to era-5 (the same program under six era +# draws, width pinned at 4 B). Every result line starts with RESULT so `node tools/jobs.mjs ` shows them. +$ErrorActionPreference = 'Continue' +function Say([string] $m) { Write-Host ("[" + (Get-Date -Format 'HH:mm:ss') + "] " + $m) } +$jobs = Split-Path $env:IGNEUM_JOB_DIR +$fetched = Join-Path $jobs 'fetch-ca2-era-20261005' +$cuda = Join-Path $fetched 'igneum-worker-cuda.exe' +$ocl = Join-Path $fetched 'igneum-worker-opencl.exe' +$packs = Join-Path $fetched 'packs-ca2-era' +$packList = @('v2', 'era-0', 'era-1', 'era-2', 'era-3', 'era-4', 'era-5') +if (-not (Test-Path $cuda)) { Write-Output "RESULT error cuda worker missing at $cuda (the fetch job runs first)"; exit 2 } +if (-not (Test-Path $ocl)) { Write-Output "RESULT error opencl worker missing at $ocl"; exit 2 } +if (-not (Test-Path $packs)) { Write-Output "RESULT error packs missing at $packs"; exit 2 } +foreach ($pk in $packList) { if (-not (Test-Path (Join-Path $packs $pk))) { Write-Output "RESULT error pack $pk missing"; exit 2 } } +$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-worker-cuda.exe') } | Select-Object -First 1 +if (-not $inst) { Write-Output 'RESULT error no installed igneum-worker-cuda.exe (the NVRTC DLLs come from there)'; exit 2 } +Get-ChildItem $inst -Filter 'nvrtc*.dll' | Copy-Item -Destination $fetched -Force +Write-Output "RESULT worker-cuda $cuda sha256 $((Get-FileHash -Algorithm SHA256 $cuda).Hash.ToLower()) with $((Get-ChildItem $fetched -Filter 'nvrtc*.dll').Count) NVRTC DLL(s) from $inst" +Write-Output "RESULT worker-opencl $ocl sha256 $((Get-FileHash -Algorithm SHA256 $ocl).Hash.ToLower())" + +# the app +$appDir = $env:IGNEUM_APP_DIR +if (-not $appDir) { $appDir = Join-Path $env:LOCALAPPDATA 'igneum\app' } +$urlFile = Join-Path $appDir 'app.url' +$url = $null +if (Test-Path $urlFile) { $url = (Get-Content -LiteralPath $urlFile -Raw).Trim() } +function Find-Card([string] $vendor, [string] $keyMatch) { + if (-not $url) { return $null } + try { + $st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10 + $c = $st.mining.cards | Where-Object { $_.vendor -eq $vendor -and $_.key -match $keyMatch } | Select-Object -First 1 + if (-not $c) { $c = $st.cards | Where-Object { $_.vendor -eq $vendor -and $_.key -match $keyMatch } | Select-Object -First 1 } + return $c + } catch { Say ("api/state: " + $_.Exception.Message); return $null } +} +function Card-Off($card) { + Write-Output ("RESULT card " + $card.key + " enabled=" + $card.enabled + " identities=" + $card.identities + " power_pct=" + $card.power_pct + " state=" + $card.state) + $body = @{ cards = @(@{ key = $card.key; enabled = $false; identities = [int]$card.identities; power_pct = [int]$card.power_pct }) } | ConvertTo-Json -Depth 5 + try { Invoke-RestMethod -Uri ($url + 'api/cards') -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; Say "card off requested" } catch { Say ("api/cards off: " + $_.Exception.Message) } + $t = 0 + while ($t -lt 90) { + Start-Sleep -Seconds 5; $t += 5 + try { $st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10; $c2 = $st.mining.cards | Where-Object { $_.key -eq $card.key }; if (-not $c2) { $c2 = $st.cards | Where-Object { $_.key -eq $card.key } }; if ($c2 -and $c2.state -eq 'off' -and $c2.pid -eq 0) { break } } catch { } + } + Write-Output ("RESULT card-off " + $card.key + " after " + $t + " s") + Start-Sleep -Seconds 5 +} +function Card-Restore($card) { + $body = @{ cards = @(@{ key = $card.key; enabled = [bool]$card.enabled; identities = [int]$card.identities; power_pct = [int]$card.power_pct }) } | ConvertTo-Json -Depth 5 + try { Invoke-RestMethod -Uri ($url + 'api/cards') -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; Write-Output ("RESULT card restored " + $card.key + " enabled=" + $card.enabled) } catch { Write-Output ("RESULT error card restore " + $card.key + ": " + $_.Exception.Message) } +} + +# ---- the RTX 5090 (CUDA, NVRTC) ---- +$nv = Find-Card 'nvidia' '.' +if ($nv) { Card-Off $nv } else { Write-Output 'RESULT card none-found nvidia (the app is not running or has no NVIDIA card); measuring with whatever else runs on the GPU' } +& nvidia-smi --query-gpu=name,driver_version,power.limit,clocks.sm,clocks.mem,memory.used,temperature.gpu --format=csv,noheader 2>&1 | ForEach-Object { "RESULT gpu-before $_" } +foreach ($pk in $packList) { + $d = Join-Path $packs $pk + Write-Output "RESULT bench-5090 $pk start $(Get-Date -Format HH:mm:ss)" + & $cuda --bench --pack $d --batches 5 --batch-log2 24 --block-warps 1 2>&1 | ForEach-Object { "RESULT $_" } + & $cuda --bench --pack $d --batches 5 --batch-log2 24 --block-warps 8 2>&1 | Where-Object { $_ -match '^RESULT|error|FAIL' } | ForEach-Object { "RESULT $_" } +} +& nvidia-smi --query-gpu=power.draw,clocks.sm,clocks.mem,memory.used,temperature.gpu --format=csv,noheader 2>&1 | ForEach-Object { "RESULT gpu-after $_" } +if ($nv) { Card-Restore $nv } + +# ---- the AMD card (OpenCL): the gfx1201 on the eGPU when present, else the integrated gfx1036 ---- +$list = & $ocl --list 2>&1 +$list | ForEach-Object { "RESULT list $_" } +$dev = $null; $gfx = $null +foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1201' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; $gfx = 'gfx1201'; break } } +if ($null -eq $dev) { foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1036' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; $gfx = 'gfx1036'; break } } } +if ($null -eq $dev) { Write-Output 'RESULT error no gfx1201 and no gfx1036 device in --list'; exit 2 } +Write-Output "RESULT device $dev $gfx" +$amd = Find-Card 'amd' $gfx +if ($amd) { Card-Off $amd } else { Write-Output "RESULT card none-found $gfx (the app is not running or has no such card); measuring with whatever else runs on the GPU" } +# the 9070 XT: the full shape; the gfx1036 (about 3 MH/s): the same 2^24 fingerprint range, 2 timed dispatches, one shape +$batches = 5; if ($gfx -eq 'gfx1036') { $batches = 2 } +foreach ($pk in $packList) { + $d = Join-Path $packs $pk + Write-Output "RESULT bench-$gfx $pk start $(Get-Date -Format HH:mm:ss)" + & $ocl --bench-pack --pack $d --batches $batches --batch-log2 24 --device $dev 2>&1 | ForEach-Object { "RESULT $_" } + if ($gfx -eq 'gfx1201') { & $ocl --bench-pack --pack $d --batches 5 --batch-log2 24 --device $dev --group-warps 8 2>&1 | Where-Object { $_ -match '^RESULT|error|FAIL' } | ForEach-Object { "RESULT $_" } } +} +if ($amd) { Card-Restore $amd } +exit 0 From 30f7c41c29b76373815c8811452d81366bdf5239 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:43:45 +0000 Subject: [PATCH 080/158] Consequences ledger: C19 and C14 closed on the mixer verifier measurement (x8 = 2.1x, 7.1 ms margin); D5 carries it Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 4 ++-- docs/plans/consequences-decisions.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index da1cc37e..42ada62e 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -21,7 +21,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C11 | RX 9070 XT 17.73 MH/s at 198.9 W (0.089 MH/W) against the RTX 5090 122.30 MH/s at 307.6 W (0.398 MH/W); every read width costs the 9070 XT the same 2.4 G line fetches a second | bench-log AMD telemetry entry; readwidth probe ceilings (status 20:35) | AMD home miners; the "three vendors" copy | Under the "widest latency-bound read" rule w16 moves bytes per hash, not loads per hash, so the AMD card keeps about a seventh of the 5090's hash rate and pays 4.5x the electricity per hash; at a UK tariff of 25 p/kWh (approximate) the 9070 XT spends 4.5x the 5090's pence per IGN. The readwidth decision table carries MH/s only; no MH/W or MH per pound per card per class, and the public copy implies vendor parity | The readwidth table adds MH/W (and MH per pound at list prices, approximate) per card per class; the v3 decision names the AMD consequence; whether the class should favour fewer loads per hash for AMD's 64-byte lines is a consensus choice for the project lead (D6) | read-width a451c9935bfb1bc19; coordinator; decision | closed (read-width e752fc7 section 4.1: MH/W and MH per pound per class per card; the AMD gap is the card's random-access rate, no width closes it; the coordinator's 22:30 entry says "near parity per pound" where the table says the 5090 is 2.2x per pound: C18) | | C12 | The v3 working set on Apple: 1,568 to 1,760 MiB today, 2,592 to 2,784 MiB at the 2 GiB genesis dataset, in unified memory shared with macOS; 2,048 launched warps x 128 KiB scratch | `docs/plans/hot-table.md` section 3 M5 Max row; era-layout section 3 | Apple silicon with 8 GB and 16 GB (the base Mac mini, MacBook Air) | An 8 GB Mac holds the miner's 2.6 GB beside macOS's 3 to 4 GB: it mines today and swaps at the first dataset growth step. The app has no gate on Metal's `recommendedMaxWorkingSetSize`; the site's "Any 4 GB card" has no Apple line. The CPU prover's RAM on a 16 GB Mac is unmeasured (the Settings switch lets it on) | The app reads `recommendedMaxWorkingSetSize` and refuses to mine (with the reason on the Mine tile) when the working set exceeds it; the site says "Mac: 8 GB or more"; the Apple scratch row at 128 KiB is measured in the readwidth table, not launched at 2,048 by assumption | miner UI adbf58b058186a18b; read-width (the Apple row) | in work (read-width 30ff674: the Apple scratch footprint by arithmetic is 1.4 GiB at 2,048 x 32 KiB and 1.6 GiB at 2,048 x 128 KiB, 2.4 GiB at 4,096 x 128 KiB; the Metal harness now prints currentAllocatedSize and recommendedMaxWorkingSetSize in its RESULT line, the measured row waits for the Mac measure lock, held by a prover measurement since 20:31Z; the miner UI gates on the arithmetic plus its output buffer until then, mining.gate_reason next cut) | | C13 | `/api/supply` `max_supply_ign` 4,000,000,000 against `minted_at_end_ign` about 3,963,000,000 (the ramp withholds about 37 M, the floors the rest); the live tables lack `number`, `tx_count`, `detail` until the observer restarts | `site/api/supply.mjs` 31 to 48; `docs/plans/explorer.md` "Open" | everyone who reads the explorer beside the homepage tile "4B IGN hard cap, ever" (`site/index.html` 386) | The tile says 4 billion and the explorer page says "of 4,000,000,000 by the rule" while the API's own end figure is 3.963 billion: a reader who adds the two columns finds 37 million missing. The explorer page shows "Circulating 0 IGN" on the devnet deployment until the observer restarts on the new code | The tile, the litepaper's supply line and the explorer carry "cap 4,000,000,000; about 3.96 billion ever minted" (the litepaper already says "approached and never reached"); the explorer does not go live before the observer restart lands the columns | explorer a76f60b415859b7b5; wording: the project lead (D7, with D3) | closed (explorer 3e01212: the tile reads "cap 4,000,000,000, 3.96 billion ever minted, x% so far" with the withheld figure on hover; the homepage tile and litepaper wording stay with the project lead, D3 and D7; the zero-circulating premise was sharper than stated, a 42703 failure not a null, now a 503 with the reason, and moot: the live observer restarted on the explorer code at 19:42:50Z, so the live tables carry the columns) | -| C14 | The M16 mixer x4 decided into class v3 at 22:00 (coordinator): verifier 1.6 to 4.8 ms per warp against 0.441 ms today (0.87 cold), measurement running on branch ca2-mixer | coordinator's reply 21:5x; `docs/analysis/m16-recompute-attacker-2026-10-05.md` table; spec 09 section 9.8 item 5 | pool operators; every node (the Hetzner seeds, the observer, a 2019-class laptop); the 10 ms verify gate | At x4 one pool core verifies 200 to 600 shares a second instead of 2,270, so one core covers 2,000 to 6,000 members at one share per 10 s instead of 22,000; a block's CPU re-check and the miner's own `cpu re-check` of every found hash cost 4 to 11x; the seeds' small VMs verify every header at that cost; the gate's margin falls from 23x to 2 to 6x, which bounds Counter ASIC 3.0's room | The coordinator is adding the numbers to the ca2-mixer document and the status (said in reply); the reviewer checks at the next sweep that the pool-members-per-core and the seed-VM header-verify rows are there, and that the spec 09 figure 2,270 shares a second per core is re-cut with v3 | coordinator ada8afb62d752b1e2 | in work | +| C14 | The M16 mixer x4 decided into class v3 at 22:00 (coordinator): verifier 1.6 to 4.8 ms per warp against 0.441 ms today (0.87 cold), measurement running on branch ca2-mixer | coordinator's reply 21:5x; `docs/analysis/m16-recompute-attacker-2026-10-05.md` table; spec 09 section 9.8 item 5 | pool operators; every node (the Hetzner seeds, the observer, a 2019-class laptop); the 10 ms verify gate | At x4 one pool core verifies 200 to 600 shares a second instead of 2,270, so one core covers 2,000 to 6,000 members at one share per 10 s instead of 22,000; a block's CPU re-check and the miner's own `cpu re-check` of every found hash cost 4 to 11x; the seeds' small VMs verify every header at that cost; the gate's margin falls from 23x to 2 to 6x, which bounds Counter ASIC 3.0's room | The coordinator is adding the numbers to the ca2-mixer document and the status (said in reply); the reviewer checks at the next sweep that the pool-members-per-core and the seed-VM header-verify rows are there, and that the spec 09 figure 2,270 shares a second per core is re-cut with v3 | coordinator ada8afb62d752b1e2 | closed with C19 (the same measurement: x4 is 1.45x and x8 2.1x the verifier, not 4 to 11x; a pool core verifies 1,140 shares a second at x4 and 790 at x8 quiet) | | C15 | A full prototype shard peaks at 28.3 GB on sp1-gpu-server 6.8.1 (the sweep, proving agent 22:0x); an empty one 13.9 GB; no knob moves either floor | proving agent's reply; sweep job `memsweep-pc2-pv1` | 24 GB cards (4090, 7900-class if it had a path); the 5090 that mines and proves; the fleet table | A 24 GB card cannot prove a prototype full shard at all, mining or not; a 5090 mining (3.4 GB resident) plus a full shard is 31.7 GB against 31.8 GB, the edge. The 20 GB / 16 GB default rests on the empty-shard number. From H tomorrow the fleet proves v1 shards of 30,000 pgas (about 7 M cycles, a ninth of the prototype shard) whose peak is unmeasured; `proving/fixtures/fees-v1-shards2` and `-shards3` are that shape. The fleet table's "proving-only" rows are 32 GB-card rows until then; the litepaper's "12 GB" (D2) and the evidence row 16 fall with it | The sweep's last row is a v1-budget shard with and without the miner, and the provedefault gates are set from it before 0.3.11 ships default-on; the fleet table labels its rows by the card that fits | proving v1 | closed as measured (proving agent, the S_p curve, app default 440fd59): 32 GB mines and proves today (28.3 GB alone, 30.0 beside the miner); 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner) from DAA 210,000 and nothing before it; 16 GB proves only empty shards alone (13.9 GB), nothing beside the miner (15.7 GB); 12 GB proves nothing on SP1 6.8.1; the default is on at 24 GB or more. Relayed to the rig installer and the HiveOS words sub-agent for their tables; until H tomorrow every prover on the devnet is a 32 GB card | | C16 | PC 2's app quit at 20:01:09Z ("aborted (the app is quitting)"), logged by the proving agent as "the app quit for the 0.3.10 update"; the shipper says nothing of 0.3.10 was published and no update-now of its exists (the live manifest is 0.3.9 from 17:59:14Z) | bench-log "proving v1" chain run 1; the shipper's reply 22:0x | every measurement on PC 2 that straddles 20:01Z (the prover-cost phase B ended 19:51Z, the chain re-run began 20:05Z, the readwidth 5090 job queued) | An app that quits for an unknown reason voids any number taken across it (CLAUDE.md: a number taken while another build or simulation ran is not a number; the same for a restart). The bench-log line names a cause that did not happen | The proving agent reads PC 2's app log for the quit reason at 20:01Z and corrects the bench-log line; if the cause is another agent's job or the auto-update, that agent's measurements across it are marked | proving v1 (the log read); the agent the cause names | closed in part (proving agent: PC 2 logged "quit: stopping the miners, then the node" at 20:01:09Z, a plain quit command 20 s after the efficiency sweep's administrator prompt was cancelled at the keyboard and 13 s after the live prover failed on a root-owned /tmp/sp1-cuda-0.sock left by the chain job; the bench-log line corrected; the quit's origin is not in the log, see C17) | | C17 | The chain job ran igneum-prove-host as root inside WSL2 and left a root-owned `/tmp/sp1-cuda-0.sock`; the live prover (the app's user) then failed with `CudaClientError: Connect(PermissionDenied)` at 20:00:56Z; the app quit at 20:01:09Z on a command whose source the log does not name | proving agent's reply 22:1x; PC 2's app log | every PC 2 measurement that shares the card with the live prover; every operator whose machine takes remote jobs | Two classes, not one bug. (1) Any job script that runs the SP1 server or the host as root in WSL2 breaks the live prover for every later shard until a reboot or a manual unlink; the proving agent fixed its own scripts (kill the server, remove the socket at the end), the class check (CLAUDE.md, 5 October: grep every script with the same shape, add a check that fails when the shape comes back) is not yet written. (2) A quit the log cannot attribute (job, UI, signal, update) voids the measurements around it and nobody can say who stopped a miner; the app logs "quit:" without a source | (1) `tools/ci/` gets a check that fails on any `.ps1` or `.sh` playbook that invokes `igneum-prove-host`, `sp1-gpu-server` or `wsl -u root` without the socket cleanup line, and the proving agent greps tonight's four PC 2 scripts; (2) the engine's quit log line carries its source (job id and playbook name, the UI, a signal, the updater) in the next app cut | proving v1 (1); coordinator for the next-cut list (2) | sent | @@ -31,7 +31,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | # | Number | Source | Tier affected | Consequence | Action | Owner | State | |---|---|---|---|---|---|---|---| | C18 | 5090 0.072 against 9070 XT 0.032 MH/s per pound at list prices (2.2x), 4.9x per watt | read-width.md 4.1 | AMD home miners; the public level 3 copy | The coordinator's 22:30 status entry says "near parity per pound"; the table it cites says 2.2x. The level 3 page is written from the status | The status and level 3 carry the table's figure (2.2x per pound, 4.9x per watt, 7.5x in rate); "near parity" is struck | coordinator | closed (coordinator 6b07776: the status, the rollout plan and the public copy carry 2.2x per pound, 4.9x per watt, 7.5x in rate) | -| C19 | Mixer x4 into class v3: CPU verify 1.6 to 4.8 ms per warp against 0.604 today; pruning depth 108,000 DAA s (spec 02) | status 22:00; M16 table; spec 02 line 17 | every node (the three testnet seeds on small Hetzner VMs, the observer, a laptop node); IBD; pools | A new node verifies every header in the pruning window on one core: 108,000 x 4.8 ms = 8.6 min at x4 against 1.1 min today (a 30-s block-time budget at 1 block/s is unaffected: 4.8 ms per block is 0.5% of a core). Every miner's own `cpu re-check` of a found hash and every pool share verification cost the same 8x; the 10 ms gate (ledger M9) keeps 2x of margin at the slow end, which is what Counter ASIC 3.0 has left to spend. On the 2019-class laptop core the evidence table names (rule 3) the figure is unmeasured and may pass 10 ms | The ca2-mixer document carries: ms per warp on the M5 Max core AND a scaled 2019-class figure (marked approximate), the pruning-window IBD minutes per tier, pool shares per core per second, and the gate margin left for 3.0; the seeds' header-verify load is checked in the testnet go checklist | ca2-mixer af345b1e2c541ffbb; coordinator | taken in full (ca2-mixer, docs/plans/mixer-x4.md section 6.5: measured ms per warp v2 and v3 on the M5 Max core, a 2019-class figure scaled and marked approximate, IBD minutes per tier over the 108,000-header window, pool shares per core and members per core v2 and v3 so spec 09's 2,270 is re-cut, the gate margin in ms; the timing run queued behind the Mac measure lock). Correction taken: the x4 multiplies only the ALU part of the verifier, the 8 dependent cache misses per item are unchanged, so the measured ratio is under 4x, not the 8x this row assumed from the M16 range; the row's minutes and share counts are upper bounds until the run | +| C19 | Mixer x4 into class v3: CPU verify 1.6 to 4.8 ms per warp against 0.604 today; pruning depth 108,000 DAA s (spec 02) | status 22:00; M16 table; spec 02 line 17 | every node (the three testnet seeds on small Hetzner VMs, the observer, a laptop node); IBD; pools | A new node verifies every header in the pruning window on one core: 108,000 x 4.8 ms = 8.6 min at x4 against 1.1 min today (a 30-s block-time budget at 1 block/s is unaffected: 4.8 ms per block is 0.5% of a core). Every miner's own `cpu re-check` of a found hash and every pool share verification cost the same 8x; the 10 ms gate (ledger M9) keeps 2x of margin at the slow end, which is what Counter ASIC 3.0 has left to spend. On the 2019-class laptop core the evidence table names (rule 3) the figure is unmeasured and may pass 10 ms | The ca2-mixer document carries: ms per warp on the M5 Max core AND a scaled 2019-class figure (marked approximate), the pruning-window IBD minutes per tier, pool shares per core per second, and the gate margin left for 3.0; the seeds' header-verify load is checked in the testnet go checklist | ca2-mixer af345b1e2c541ffbb; coordinator | closed as measured (ca2-mixer 54bbfcc, mixer-x4.md 6.5): one M5 Max core under a load of 5.6, ms per warp v2 1.33, x4 1.94 (1.45x), x8 2.79 (2.1x), worst cold 1.58 / 2.04 / 2.94; quiet-core scaled 0.60 / 0.88 / 1.26 (approximate); 2019-class laptop 1.5 / 2.2 / 3.2 (approximate, unmeasured); shares per core per second quiet 1,660 / 1,140 / 790 (spec 09's 2,270 re-cut to 1,660), a 22,000-member pool needs 1.3 / 1.9 / 2.8 quiet cores; IBD over 108,000 headers quiet 1.1 / 1.6 / 2.3 min (laptop 2.7 / 4.0 / 5.8); gate margin for 3.0 on the loaded core 8.4 / 8.0 / 7.1 ms. The mixer multiplies the ALU part only, so x8 is 2.1x the verifier. Owed before the level 3 page quotes an absolute: one quiet-core run (the ratios are the measurement tonight) | | C20 | Layer 9: the epoch length as an era parameter, 600 to 7,200 DAA s (10 min to 2 h), base 3,600 | status 22:30 and 23:00 | rigs (HiveOS and the rig installer), Macs, pools, the seed path | Both rig miners run `--exit-on-seed-change` and re-export the pack on exit 42 (h-run.sh 56 to 61, igneum-miner.sh 67 to 78): at a 10-minute epoch every card's miner restarts six times an hour with a pack export each time, and the restart gap is lost hashing; the app's prepare-ahead path does not restart. The Mac fleet's prepare pause (35 s an hour at one epoch an hour, bench-log M11) becomes 3.5 min an hour, 6%. The 10-minute seed VDF (spec 04) equals the shortest epoch, so the seed for epoch n+1 is known only as epoch n starts, which is the compile-ahead window the agent must measure per card (the 5090 compiled in 1,285 ms, the 9070 XT unmeasured). A pool's `job` cadence and the dev-fee counter are unaffected | The epoch-length document carries a per-tier row: rig restart cost per epoch length (and the fix: prepare-ahead in the rig scripts, no exit 42 path), the Mac pause share, the compile-ahead margin per card at 600 DAA s against the VDF; the rig installer removes `--exit-on-seed-change` in favour of the prepare path before layer 9 can draw a short epoch | ca2-epoch a32a3ece66c02417a; rig installer | closed with a correction (ca2-epoch 4300608, epoch-length.md sections 6.3, 7, 9): the rig scripts pass --prepare-packs as well, so a worker with prepare support swaps in place and exit 42 is the fallback on a prepare MISS (the loaded iGPU missed 2 of 10, M11), not a restart every epoch; the risk at 600 s is one restart plus export plus inline compile per missed boundary; the Mac race pause is 6.3% of a 600-s epoch (race default off); the program is known a full epoch ahead at every length (lead and T_epoch fixed, option A); the 9070 XT compile is OWED (no prepared line from gfx1201 in any upload); the rig installer (88f31d9) confirms the rig already takes the prepare path: exit 42 fires only when a worker's ready line lacks "prepare 1", and both shipped workers answer it; documented in its README, no code change | | C21 | OTA K2: apps embed `OTA_PUBLIC_KEYS = [K1, K2]` and honour a signed `revoked_keys` list; the rig installer verifies the manifest with ONE key (`OTA_PUBLIC_KEY_HEX`, install-rig.sh 168, igneum-update.sh 2) and knows no revocation; the HiveOS package verifies nothing (no manifest, the override reaches it only by republish) | ota-k2 c722579; packaging/linux; packaging/hive | rigs (both packages), the seeds (if they take the manifest) | The day K1 is lost or revoked and the manifest is signed with K2, every rig on the installer refuses the manifest, stops taking overrides, and is isolated at the next height switch; a leaked K1 keeps signing for rigs, because they carry no revocation list. HiveOS rigs get neither keys nor revocation: a republished package is their only path, and nothing checks who published it | The rig installer carries both public keys and the `revoked_keys` rule in the same form as the app (keys.md section 4, step 3), installed and read from the manifest; the HiveOS README states that the package is unsigned and names the sha256 the Flight Sheet URL should be checked against; keys.md lists the rig and HiveOS paths in its table of what trusts K1 | OTA key af2bb75a5436324d0 (keys.md, the shared verifier form); rig installer a3e7b2b03222f5cff | closed for the rig and the docs (rig installer 88f31d9: OTA_PUBLIC_KEYS [K1, K2 slot] embedded, manifest_check mirrors the app's manifest::check with the revoked_keys record at /var/lib/igneum/updates/revoked.json, tested on three throwaway keys; OTA agent 00fcbb5: keys.md table of every path that trusts K1, the HiveOS README unsigned-archive note); open: the wallet (wallet-v1) still trusts K1 alone, listed in keys.md for its owner; merge note: packaging/hive/README.md is edited on both ota-k2 and hive-words | diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index d7ecdb2e..cfa46bad 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -8,7 +8,7 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | D2 | C3 | The litepaper says "12 GB or more proves full shards"; the only measurement puts the prover alone at 13.8 GB on a 32 GB card | The sweep and the S_p curve are in (proving agent, 5 October late): no knob moves the floor; 12 GB proves nothing on SP1 6.8.1, 16 GB proves only empty shards alone, 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner), 32 GB proves everything. Change the sentence to "24 GB or more proves; 32 GB proves and mines on one card" and evidence row 16 to tested-by-the-team on those rows; the 12 GB figure returns only if a smaller GPU server or a smaller shard measures under 12 GB. The proving agent has drafted the two replacement sentences on its branch (app 440fd59, litepaper and evidence rows 15 and 16); nothing is pushed, so the project lead approves or rewrites the draft rather than starting from the measurement. Two drafts exist: the proving agent's litepaper sentences (proving-v1 440fd59) and the coordinator's site, litepaper and miner-page line (ca2-coord 1c8439f); the integrator keeps one. One marker is owed on either: the 24 GB figure is the 5090's allocation pattern on a 32 GB card (22.2 GB beside the miner), not a measurement on a 24 GB card, and evidence rule 3 wants that said until a 4090 or a 5080-class 24 GB card runs it | A public number that the first 3060 owner disproves is the FUD the ledger exists to prevent | | D3 | C8, C13 | The homepage says "Any 4 GB card" and the litepaper says a 4 GB card mines for about four years and an 8 GB card for more than a decade; the tile says "4B hard cap" while the rule mints about 3.96 billion | Replace with the lifetime table's numbers once the sub-agent lands it: "4 GB cards mine at launch; 8 GB for about six years; 12 GB for about fourteen; the dataset grows half a gigabyte a year" and "cap 4 billion, about 3.96 billion ever minted" | The schedule is public and the arithmetic is one line; a reader will do it | | D4 | C9, C8 | Index mapping at gate 1, now with the lifetime table (`docs/analysis/card-lifetime-2026-10-05.md`): (a) multiply-shift, continuous growth: 4 GB cards out at 1.0 to 1.5 years, 8 GB at 6.3 to 7.5, 12 GB at 12 to 13.5, Apple 8 GB at 2.6 to 3.4; (b) power-of-two steps at years 4, 12, 28, 60: 4 GB to year 4, 8 GB to year 12, 12 GB to year 28 (the cache freed after the build, decided by the coordinator at 22:50), each tier ending on a step day | (b), with the step calendar published on the miners page from day one (the years 4, 12, 28 and 60 named beside the tiers), and the 1 GiB vectors kept. Revised from (a) at 23:0x: the table shows (b) is the only mapping under which the litepaper's "4 GB about four years, 8 GB more than a decade" holds, and a step day known twelve years ahead is a schedule, not an event; the coordinator recommends the same | Under (a) both public sentences are wrong today by 2.5 to 5 years; under (b) they hold and the cliffs are dated | -| D5 | C10 | The site's "under 2x" chip claim: the scratch layer leaves the on-die-cache recompute chip at 2.4x at every share; the mixer multiplier x2 brings it to 1.2x at twice the CPU verify cost (about 0.9 ms per warp, half the pool members per core) | Overtaken by the coordinator's delegated decisions (21:50 to 21:28 Mac clock): the public claim was qualified, the mixer x4 is in class v3 (chip row 0.61x bare, 1.84x with a 3x fixed-function factor, 1.53x at equal silicon with the mirror deducted: "under 2x" holds on the equal-silicon convention and is thin), and x8 is built and measured beside it (0.92x with the factor, from the M16 table); x8 goes in if the verify stays under 10 ms and the daily build under 1 s on every card we own (C23 asks that the iGPU tier be named in that rule). For the project lead: the public sentence after tonight reads "under 2x at equal silicon, measured against the strongest chip we can model" with the convention named, or it does not read "under 2x" | The claim is the project's first public sentence on chips; it is either true by a measured lever or it is marked | +| D5 | C10 | The site's "under 2x" chip claim: the scratch layer leaves the on-die-cache recompute chip at 2.4x at every share; the mixer multiplier x2 brings it to 1.2x at twice the CPU verify cost (about 0.9 ms per warp, half the pool members per core) | Overtaken by the coordinator's delegated decisions (21:50 to 21:28 Mac clock): the public claim was qualified, the mixer x4 is in class v3 (chip row 0.61x bare, 1.84x with a 3x fixed-function factor, 1.53x at equal silicon with the mirror deducted: "under 2x" holds on the equal-silicon convention and is thin), and x8 is built and measured beside it (0.92x with the factor, from the M16 table); x8 goes in if the verify stays under 10 ms and the daily build under 1 s on every card we own (C23 asks that the iGPU tier be named in that rule). Measured at 21:40 UTC (ca2-mixer 54bbfcc): x8 is 2.1x the v2 verifier (2.79 ms per warp on a loaded core, 1.26 quiet by scaling), 7.1 ms of the 10 ms gate left, the Mac 1 GiB build flat at 21 ms; the verify half of the x8 rule passes, the PC build rows are owed. For the project lead: the public sentence after tonight reads "under 2x at equal silicon, measured against the strongest chip we can model" with the convention named, or it does not read "under 2x" | The claim is the project's first public sentence on chips; it is either true by a measured lever or it is marked | | D6 | C11 | Whether class v3 should favour AMD (fewer, wider loads per hash) at a cost to the 5090's latency-bound share, or accept that AMD cards mine at about a seventh of a 5090 and 4.5x the electricity per hash | Accept it for v3 and say so on the miners page ("NVIDIA first; AMD mines at a lower rate per watt on this class"); open the AMD question as a Counter ASIC 3.0 item with its own measurement | Tonight's rule was the project lead's and the 5090 margin is the anti-chip argument; AMD's position is a public-copy question, not a gate | | D7 | C13 | Same as D3's supply wording | with D3 | | | D8 | C7 | The miner page says "One click: install, start, the card mines and proves" (site/miner.html 7, 13, 21; site/index.html 484). On HiveOS and the rig installer a rig mines only until a Linux prover build is published, and on the app a 12 GB card mines only, a 16 GB card proves with the miner paused, 20 GB and up does both (the sweep of 5 October, before the v1-shard row) | Qualify the sentence on the miner page and the homepage card: "the card mines; 24 GB cards prove too, 32 GB does both at once; rigs mine until the Linux prover ships". The same page's "a visible 1% software fee you can switch off" becomes "solo mining carries a 1% software fee you can switch off; in a pool the pool's own fee is the only one" once pool-v0 ships (the pool agent fixed the rule: no software dev fee in pool mode) | The sentence is the product's first promise and tonight's measurement bounds it by card | From 6d4767b2a9c25ee73a2879f50c8b5bfb6d3ad1f4 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:49:39 +0000 Subject: [PATCH 081/158] Consequences ledger: sweep 7 notes (hot table out, era draw in, x8 verifier half, the PC 2 resume defect) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 42ada62e..c7092be5 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -47,3 +47,5 @@ Sweep 5 (21:08 Mac clock) notes: the coordinator applied the public proving line | C23 | Mixer x4 or x8: the 5090's daily dataset build 13.4 ms at x1 (54 ms at x4, about 107 ms at x8); the integrated gfx1036 builds the dataset per PREPARE, 7 to 12 s at x1 and 55 to 124 s under CPU load (epoch-length.md section 7); the x8 rule: "the daily build under 1 s on every card we own" | status 21:25 and 21:27; epoch-length.md section 7 | integrated GPUs (the iGPU tier), 8 GB cards (about a tenth of a 5090's rate), rigs with one weak card | The x8 rule names "every card we own": the gfx1036 is one, and at x8 its per-prepare build is 56 to 96 s per epoch (7 to 16 min under load), so it misses every epoch boundary and falls to the exit-42 path; at x4 it is 28 to 48 s per hour (0.8 to 1.3%) or 4 to 8 min under load. An 8 GB discrete card scales at about a tenth of the 5090: about 1 s at x8, on the rule's edge. The per-day dataset reuse in the worker (owed in epoch-length.md section 9) is what makes the mixer cheap for the iGPU tier; without it the mixer multiplies a per-epoch cost | The mixer decision names the gfx1036 and an 8 GB-class scaled row beside the 5090, M5 Max and 9070 XT in the "under 1 s" check, and the per-day dataset reuse in the worker lands before (or with) class v3, or the iGPU tier is stated as "mines v3 with a restart per epoch" on the level 3 page | ca2-mixer af345b1e2c541ffbb; coordinator | taken (coordinator and ca2-mixer, mixer-x4.md build-time table: the x8 table carries the gfx1036 row and a scaled 8 GB-class row; the under-1-s rule applies to the discrete cards' daily build; for the integrated tier either the per-day dataset reuse in the three workers lands with class v3, asked of the mixer and node agents as a bounded change tonight, or the level 3 page says the iGPU tier mines v3 with a restart per epoch; recorded with the x4/x8 choice) | | C24 | Two PowerShell job scripts lost a quote inside an inline bash body tonight: amd-prove's awk program (cpu-prove-pc1-small, exit 0 with nothing proved) and the 0.3.10 installer's `bash -c` string (fb-installer-pc1-3, exit 2 in 4 s); amd-prove added `tools/amd-prove/check-job-bash.sh` (bash -n on its own scripts' bash bodies) | amd-proving.md section 2; status 21:28 | every PC job; the morning's rollouts | The class (CLAUDE.md, 5 October: fix the class the same day, add a check that fails when the shape comes back) is "a bash body inside a PowerShell job string"; the check exists for one agent's scripts and did not cover the shipper's, which failed the same way an hour later | A repo-wide CI check: every `.ps1` under relay/playbooks and tools that carries a bash body (`wsl ... bash -c`, `bash -lc`, here-strings fed to bash) has that body extracted and passed through `bash -n`; the shipper's rule (the WSL part as a file run with `bash `) written in packaging/README-ship.md as the convention | sub-agent (bounded, no owner); coordinator told | in work (sub-agent bash-body-check; the coordinator: no collision with PC 1, the shipper moved its WSL part to a file for attempt 4, the repro agent added a PowerShell 5.1 parse gate for bench/; the check fails on an unextractable body rather than skipping it) | | C25 | Ember Tune: the signed manifest carries per-card-model tuning priors (power limit and core clock) that a new card applies and confirms in two steps; K1 signs it | ember-tune.md sections 4 and 5; bench-log Ember Tune entry | every NVIDIA and AMD card on the app; the keys | The manifest now sets clocks and power limits on every user's card, so the signing key's blast radius grew: a signed prior can underclock the fleet or push a card model to its power ceiling. The plan's clamps (inside power.min_limit / max_limit and clocks.max.gr, the confirm step, a faulted step reverted) are the bound; keys.md's "what K1 signs" table (ota-k2 00fcbb5) predates the priors and does not list them | ember-tune.md section 5 states the bound in one line (a prior can never set a value outside the card's own reported limits, and never a memory clock), with the test that proves it; keys.md's table gains the tuning priors under K1 with that bound | Ember Tune a855dcc4bd05e0615; OTA key agent (the table row) | closed (Ember Tune 5d7ced9: the rule as a row in section 5 with the two tests named, a prior of 9,000 MHz at 30% clamps to 3,090 MHz at 50%, a bad prior costs one confirm step per card; the OTA agent has the keys.md note: tuning priors and the kill switch under K1 with that bound) | + +Sweep 7 (21:49 UTC) notes, no new row: the hot table is measured and NOT adopted (g 0.93 to 0.96 on the Mac against the 0.97 rule, bdab8df); the era draw passes the 5% rule on the Mac (spread 0.8%, c570da3) and its chip line says the union of a program's 16 windows covered the whole dataset in 300 of 300 programs, so a chip mirrors the whole dataset or nothing; the mixer x8 passes the verifier half of its rule (C19) and the PC build rows decide the other half about 22:10; PC 2's miners have been off since a job's /api/resume at 21:25 answered ok without restarting them (the devnet short PC 2's rate, the aggregation-cost mining phases void, a next-cut defect: a resume re-checks the miner processes), all stated by the coordinator at 21:45; PC 1 restarted on 0.3.10 at 21:40:41Z with no measurement straddling it. From 495c55280ce6639a7cbfc00a00bc70f38d3996b1 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:53:37 +0000 Subject: [PATCH 082/158] counter-asic-2-node.md: gate G4 run 2 PASS on the composed class (era in the class on the mixer, 1558971): 181 / 124 blocks, 0 rejected, 0 forks; the PC 2 suite job id; the cache merge owed Co-Authored-By: Claude Fable 5.1 --- .../class-v3-20261005-2146Z-era-mx4.json | 378 ++++++++++++++++++ docs/plans/counter-asic-2-node.md | 22 +- 2 files changed, 398 insertions(+), 2 deletions(-) create mode 100644 docs/plans/counter-asic-2-gate/class-v3-20261005-2146Z-era-mx4.json diff --git a/docs/plans/counter-asic-2-gate/class-v3-20261005-2146Z-era-mx4.json b/docs/plans/counter-asic-2-gate/class-v3-20261005-2146Z-era-mx4.json new file mode 100644 index 00000000..70712ab1 --- /dev/null +++ b/docs/plans/counter-asic-2-gate/class-v3-20261005-2146Z-era-mx4.json @@ -0,0 +1,378 @@ +{ + "pass": true, + "checks": { + "switch_line_on_every_node": true, + "switch_line_names_the_rounded_epoch": true, + "template_switched_at_the_first_v3_epoch": true, + "blocks_before_the_boundary": true, + "blocks_after_the_boundary": true, + "v2_and_v3_programs_seen": true, + "program_ids_differ_across_the_switch": true, + "miners_agree_on_every_program": true, + "zero_rejected_by_miners": true, + "zero_rejected_by_nodes": true, + "sinks_agree": true, + "block_counts_agree": true + }, + "activation": 150, + "epoch_blocks": 60, + "first_v3_epoch": 3, + "boundary_daa": 180, + "secs": 480, + "threads": 1, + "node": "/Users/joshm/Projects/igneum-wt-ca2-v3/vendor/igneum-node-ca2/target-ca2/release/igneumd", + "miner": "/Users/joshm/Projects/igneum-wt-ca2-v3/vendor/igneum-node-ca2/target-ca2/release/igneum-miner", + "genesis_bits": "0x1f010000", + "template_switch": { + "epoch": 3, + "daa": 180, + "at": 171 + }, + "run_ended_at_s": 292.3, + "final_daa": 300, + "blocks": { + "total": 305, + "before_boundary": 181, + "after_boundary": 124, + "chain_before": 180, + "chain_after": 122 + }, + "programs": [ + { + "epoch": 0, + "class": "v2", + "program_id": "8f8806638d59850f", + "seed": "234e082d653dc69d", + "miners": 3, + "disagree": false, + "ready_ms": [ + 177, + 177, + 178 + ] + }, + { + "epoch": 1, + "class": "v2", + "program_id": "bb8dd9ddbf9eb63f", + "seed": "2f66af56da44ca92", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + }, + { + "epoch": 2, + "class": "v2", + "program_id": "8ee7a9f33d418e48", + "seed": "5194dfa8a53259bc", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + }, + { + "epoch": 3, + "class": "v3", + "program_id": "2d278041ba482dba", + "seed": "54c353de1d8609d7", + "miners": 3, + "disagree": false, + "ready_ms": [ + 181, + 179, + 179 + ] + }, + { + "epoch": 4, + "class": "v3", + "program_id": "2ae786d294a8a59d", + "seed": "8529c69223a2194c", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + }, + { + "epoch": 5, + "class": "v3", + "program_id": "bc36813df2f41b5f", + "seed": "fdc233b69c42ffea", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + } + ], + "accepted_per_miner": [ + 102, + 102, + 100 + ], + "rejected_by_miners": [ + 0, + 0, + 0 + ], + "rejected_by_nodes": [ + 0, + 0, + 0 + ], + "rejected_lines": [], + "sinks": [ + "712c1b212091dcdc", + "712c1b212091dcdc", + "712c1b212091dcdc" + ], + "block_counts": [ + 303, + 303, + 303 + ], + "tips_per_node": [ + 1, + 1, + 1 + ], + "switch_lines": [ + "Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)" + ], + "samples": [ + { + "t": 4.6, + "daa": 0, + "epoch": 0, + "class": 2, + "nodes": [ + "0/234e082d", + "0/234e082d", + "0/234e082d" + ] + }, + { + "t": 19.7, + "daa": 18, + "epoch": 0, + "class": 2, + "nodes": [ + "18/5b5f2d36", + "18/5b5f2d36", + "18/5b5f2d36" + ] + }, + { + "t": 34.7, + "daa": 51, + "epoch": 0, + "class": 2, + "nodes": [ + "51/333c61cc", + "51/333c61cc", + "51/333c61cc" + ] + }, + { + "t": 49.8, + "daa": 67, + "epoch": 1, + "class": 2, + "nodes": [ + "67/f1fcbc43", + "67/f1fcbc43", + "67/f1fcbc43" + ] + }, + { + "t": 64.8, + "daa": 89, + "epoch": 1, + "class": 2, + "nodes": [ + "89/91180594", + "89/91180594", + "89/91180594" + ] + }, + { + "t": 79.8, + "daa": 101, + "epoch": 1, + "class": 2, + "nodes": [ + "101/49dd0422", + "101/49dd0422", + "101/49dd0422" + ] + }, + { + "t": 94.9, + "daa": 111, + "epoch": 1, + "class": 2, + "nodes": [ + "111/a65a10b3", + "111/a65a10b3", + "111/a65a10b3" + ] + }, + { + "t": 109.9, + "daa": 122, + "epoch": 2, + "class": 2, + "nodes": [ + "122/f06b1eb3", + "122/f06b1eb3", + "122/f06b1eb3" + ] + }, + { + "t": 124.9, + "daa": 140, + "epoch": 2, + "class": 2, + "nodes": [ + "140/bbfe6710", + "140/bbfe6710", + "140/bbfe6710" + ] + }, + { + "t": 140, + "daa": 155, + "epoch": 2, + "class": 2, + "nodes": [ + "155/a4e736dd", + "155/a4e736dd", + "155/a4e736dd" + ] + }, + { + "t": 155, + "daa": 171, + "epoch": 2, + "class": 2, + "nodes": [ + "171/c754adeb", + "171/c754adeb", + "171/c754adeb" + ] + }, + { + "t": 170, + "daa": 179, + "epoch": 2, + "class": 2, + "nodes": [ + "179/da89ab4d", + "179/da89ab4d", + "179/da89ab4d" + ] + }, + { + "t": 185, + "daa": 187, + "epoch": 3, + "class": 3, + "nodes": [ + "187/e0f61041", + "187/e0f61041", + "187/e0f61041" + ] + }, + { + "t": 200.1, + "daa": 204, + "epoch": 3, + "class": 3, + "nodes": [ + "204/5d37e885", + "204/5d37e885", + "204/5d37e885" + ] + }, + { + "t": 215.1, + "daa": 222, + "epoch": 3, + "class": 3, + "nodes": [ + "222/46643165", + "222/46643165", + "222/46643165" + ] + }, + { + "t": 230.1, + "daa": 236, + "epoch": 3, + "class": 3, + "nodes": [ + "236/17eb5a95", + "236/17eb5a95", + "236/17eb5a95" + ] + }, + { + "t": 245.2, + "daa": 247, + "epoch": 4, + "class": 3, + "nodes": [ + "247/7aada19d", + "247/7aada19d", + "247/7aada19d" + ] + }, + { + "t": 260.2, + "daa": 261, + "epoch": 4, + "class": 3, + "nodes": [ + "261/15d8f83e", + "261/15d8f83e", + "261/15d8f83e" + ] + }, + { + "t": 275.3, + "daa": 274, + "epoch": 4, + "class": 3, + "nodes": [ + "274/9f3d19c5", + "274/9f3d19c5", + "274/9f3d19c5" + ] + }, + { + "t": 290.3, + "daa": 297, + "epoch": 4, + "class": 3, + "nodes": [ + "297/fa7975bd", + "297/fa7975bd", + "297/fa7975bd" + ] + } + ] +} \ No newline at end of file diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index 7f93f4af..1ab5164e 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -92,7 +92,25 @@ Binaries: fork ca2-v3-node 79bd8e10 and igneum-pow at ca2-v3 66eeba3 (the mixer- | Forks | sinks `082fd39ba65df2ff` on all three nodes, block counts 304 / 304 / 304, one tip each | | Program and cache ready, one CPU core | a new `(day, class)` cache: v2 epoch 0 219 to 235 ms, the first v3 epoch 177 to 185 ms (two per miner: the 24-minute day rolled at DAA 190); a program swap inside a day 2 ms | -The mixer x4 build-time number the rollout asks for is not visible here: the CPU miner derives dataset words on demand from the cache (no dataset build), so the x4 cost lands on the GPU workers' dataset build, measured by the ca2-mixer playbooks on the PCs. Run 2, on the composed class (era and hot fields), follows the era and cache merges. +The mixer x4 build-time number the rollout asks for is not visible here: the CPU miner derives dataset words on demand from the cache (no dataset build), so the x4 cost lands on the GPU workers' dataset build, measured by the ca2-mixer playbooks on the PCs. + +### Result, run 2 (5 October 2026, 21:46:36Z to 21:51:31Z): the composed class + +Binaries rebuilt on ca2-v3 b105a55 (era layout merged on the mixer: `V3_CLASS` = MX4 with the era drawn inside `generate_from_seed_bytes_program_class`, hot `None`), fork 79bd8e10 unchanged. Summary: `docs/plans/counter-asic-2-gate/class-v3-20261005-2146Z-era-mx4.json`. PASS: every check true. + +| Check | Measured | +|---|---| +| Switch lines, digest | 3 of 3, the same line as run 1; digest `0186df7d0834d054...` | +| Template class per epoch | epochs 0 to 2 class 2, 3 to 5 class 3; the switch at DAA 180, 171.0 s wall | +| Blocks before / after the boundary | 181 / 124 (selected chain 180 / 122), 305 in all; 102, 102, 100 accepted per miner | +| Program id per epoch, all three miners agreeing | e0 v2 `8f8806638d59850f`, e1 v2 `bb8dd9ddbf9eb63f`, e2 v2 `8ee7a9f33d418e48`, e3 v3 `2d278041ba482dba`, e4 v3 `2ae786d294a8a59d`, e5 v3 `bc36813df2f41b5f` | +| Rejected blocks | miners 0 / 0 / 0, nodes 0 / 0 / 0 | +| Forks | sinks `712c1b212091dcdc` on all three nodes, block counts 303 / 303 / 303, one tip each | +| Program and cache ready, one CPU core | v2 epoch 0 178 ms, the first v3 epoch 181 ms, an in-day swap 2 ms | + +Epoch 0's v2 id is the same in both runs (`8f8806638d59850f`: a v2 program is untouched by the era code, on the chain as in the packs); the v3 ids differ from run 1 because the era draw is now inside the class. + +The PC 2 suite job for the same tree: `build-20261005-215219` (fork 79bd8e10, main b105a55, published 21:52:19Z; its SUMMARY is the G6 evidence, recorded by the coordinator in the status file). ## 6. Tests @@ -111,6 +129,6 @@ The mixer x4 build-time number the rollout asks for is not visible here: the CPU - The Metal worker has no class v3 generator (it regenerates from the seed in Swift): the integration branch adds generator 3 there, or the Mac mines v3 with the OpenCL worker from a pack. - The GPU workers' class refusal was checked by the C test of `pf_pack_class_ok` and the syntax of both hosts, not by a live worker on a v3 pack: the integration's bit-exact gate (G1) is where a real worker first builds a v3 pack. - `next_pair` keeps the current era seed for the next epoch; an epoch boundary that is also an era boundary (once per 180 days) would prepare the wrong era, and the job line then names the right one, so the worker refuses the prepared pair and the miner prepares again (one wasted compile, no wrong block). The VDF era seed replaces the stand-in before this matters. -- Owed: gate run 2 on the composed class (ca2-era and ca2-cache merged onto the mixer tip; both branches still base on 464d6e1 at 22:40Z, before the mixer, so the era agent's rebased commit comes first), then a rebuild of `target-ca2/release` and `class-v3.mjs` again. +- Owed: the ca2-cache code (2de19e5, measured-not-adopted, hot `None`) once the cache agent rebases it onto b105a55: its merge against the era and mixer tip conflicts in 8 files (35 hunks in generator.rs, emit.rs, tests/packs.rs, verify.rs, packfile.h, accept.rs, packbench.swift, bench-log.md), aborted here on 5 October 2026 at 21:45Z; the gate did not need it. - Owed (0.3.12, coordinator's ask of 5 October 2026 22:20Z): per-day dataset reuse in the CUDA and OpenCL workers (a `Day` object shared by consecutive pairs, the cache freed after the build); the Metal worker already keys datasets by day. Until then the iGPU tier mines v3 with a dataset rebuild per epoch on those two workers. - Wire compatibility: `RpcPowEpochInfo` gained five fields in its Borsh form (wRPC) and five proto fields (gRPC); the gRPC side reads an old node's zeros as v2 / never / none; the Borsh form is versioned by `GetBlockTemplateResponse` (version 2 carries the whole struct), so a 0.3.11 wRPC client against a 0.3.10 node reads short: the miner uses gRPC, the console reads JSON (serde defaults). From c4da08d30214be924cc0276ba46bc526f25bcf14 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:56:20 +0000 Subject: [PATCH 083/158] App: the resume path re-arms every stopped card, re-exports its pack and checks 90 s later that every enabled card mines (PC 2 at 21:25:11Z and the Mac that afternoon stayed at 0 MH/s after resume); the PC 2 socket-fix job The known-failed case, from PC 2's 0.3.9 log (run win-1ccfe586-20261005-200114): 1791234223 pause -> 'stopping the miners (paused)' (every slot's restart_at cleared, the 5090 'off'); 1791235511 '[ok] mining resumed'; then '0.00 MH/s, waiting' at every 30-s status line until the 0.3.10 restart at 21:49:41Z. Cause: Cmd::Resume re-armed only slots whose watchdog said faulted; the 5090's slot was healthy and stopped, so nothing restarted it. The test the_pc2_resume_of_21_25_11z_restarts_under_the_new_rule_and_not_the_old encodes that slot (faulted false, live false): the old rule returns [] (the defect), the new rule [0]. cargo test -p igneum-app resume: 3 passed; provedefault: 6 passed. Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/src/engine.rs | 99 ++++++++++++++++++++++++++++- docs/plans/proving-v1.md | 2 + tools/proving-v1/pc2-socket-fix.ps1 | 31 +++++++++ 3 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 tools/proving-v1/pc2-socket-fix.ps1 diff --git a/app/igneum-app/src/engine.rs b/app/igneum-app/src/engine.rs index 8831769b..84d356d5 100644 --- a/app/igneum-app/src/engine.rs +++ b/app/igneum-app/src/engine.rs @@ -449,6 +449,36 @@ fn jitter_secs(seed: u64) -> u64 { 5 + (seed.wrapping_mul(2654435761) >> 7) % 56 } +/// Seconds after a resume before every enabled card must be mining (a worker takes 10 to 60 s to its first STATUS +/// line with a hash rate; the pack export before it a few seconds more). +pub(crate) const RESUME_CHECK_SECS: u64 = 90; + +/// What the resume rule reads from a miner slot. +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) struct ResumeSlot { + /// the watchdog marked the card faulted + pub faulted: bool, + /// a worker process is alive on the slot + pub live: bool, +} + +/// The resume rule: every slot without a live worker is re-armed, faulted or not. (The rule before 5 October 2026 +/// re-armed only faulted slots; `stop_miners("paused")` had cleared every slot's `restart_at`, so a healthy paused +/// card never restarted: PC 2 at 21:25:11Z, the Mac that afternoon.) +pub(crate) fn slots_to_rearm_on_resume(slots: &[ResumeSlot]) -> Vec { + slots.iter().enumerate().filter(|(_, s)| !s.live).map(|(i, _)| i).collect() +} + +/// The check RESUME_CHECK_SECS after a resume: every enabled, present, non-faulted card must report a hash rate +/// above 0 or its name is returned with its state (the caller logs one line per card). +pub(crate) fn resume_check(cards: &[CardState]) -> Vec { + cards + .iter() + .filter(|c| c.enabled && c.present() && c.state != "faulted" && c.hash_now <= 0.0) + .map(|c| format!("{} is not mining {RESUME_CHECK_SECS} s after resume (state {}, pid {}{})", c.name, c.state, c.pid, if c.message.is_empty() { String::new() } else { format!(", {}", c.message) })) + .collect() +} + struct MinerSlot { card: usize, label: String, @@ -477,6 +507,8 @@ pub struct Engine { node: Option, node_external: bool, node_restart_at: Option, + /// resume rule (5 October 2026): when due, every enabled card must be mining or its name goes to the log + resume_check_at: Option, node_started_at: Instant, node_starts: u32, node_restarts: u32, @@ -579,6 +611,7 @@ impl Engine { node: None, node_external: false, node_restart_at: None, + resume_check_at: None, node_started_at: now, node_starts: 0, node_restarts: 0, @@ -765,13 +798,23 @@ impl Engine { self.shared.save_settings(); self.st().mining.paused = false; self.shared.event("ok", "mining resumed"); - // a user action: faulted cards try again - for m in self.miners.iter_mut() { + // the resume rule (5 October 2026, PC 2 at 21:25:11Z and the Mac that afternoon): stop_miners("paused") + // cleared every slot's restart_at and this arm re-armed only FAULTED slots, so a card whose worker had + // simply been stopped stayed "off" at 0 MH/s until the app was relaunched. Now every slot without a + // live worker is re-armed (a faulted one reset first), its pack is exported again before the start + // (prepared = false: the hour may have turned while paused), and a check 90 s later names any + // enabled card that is not mining (`resume_check`). + let views: Vec = self.miners.iter().map(|m| ResumeSlot { faulted: m.watch.faulted().is_some(), live: m.proc.is_some() }).collect(); + let now = Instant::now(); + for i in slots_to_rearm_on_resume(&views) { + let m = &mut self.miners[i]; if m.watch.faulted().is_some() { m.watch.event(0.0, crate::watchdog::Event::Reset); - m.restart_at = Some(Instant::now()); } + m.restart_at = Some(now); + m.prepared = false; } + self.resume_check_at = Some(now + Duration::from_secs(RESUME_CHECK_SECS)); if !self.running { self.start(); } @@ -2193,6 +2236,18 @@ impl Engine { self.tick_node(now); self.tick_watch(now); self.tick_miners(now); + if let Some(at) = self.resume_check_at { + if now >= at { + self.resume_check_at = None; + let (cards, paused) = { let st = self.st(); (st.mining.cards.clone(), st.mining.paused) }; + if !paused { + for line in resume_check(&cards) { + self.shared.log(&format!("resume: {line}")); + self.shared.event("error", &format!("resume: {line}")); + } + } + } + } self.tick_telemetry(now); self.tick_sweep(now); } @@ -3392,6 +3447,44 @@ pub fn parse_race(body: &str) -> Option { Some(r) } +#[cfg(test)] +mod resume_tests { + use super::*; + + fn card(name: &str, enabled: bool, state: &str, hash: f64) -> CardState { + CardState { name: name.into(), vendor: "nvidia".into(), kind: "discrete".into(), enabled, state: state.into(), hash_now: hash, ..Default::default() } + } + + /// The state machine: paused (every slot stopped, restart_at cleared) -> resumed -> every slot without a live + /// worker is re-armed, faulted or not. + #[test] + fn resume_rearms_every_stopped_slot() { + let slots = [ResumeSlot { faulted: false, live: false }, ResumeSlot { faulted: true, live: false }, ResumeSlot { faulted: false, live: true }]; + assert_eq!(slots_to_rearm_on_resume(&slots), vec![0, 1], "the healthy stopped slot and the faulted one restart; the live one is left alone"); + } + + /// The known-failed case (PC 2, 5 October 2026, 21:25:11Z, app 0.3.9: `[ok] mining resumed`, then `0.00 MH/s, + /// waiting` for 20 minutes): one healthy slot, stopped by the pause, not faulted. The old rule re-armed only + /// faulted slots and returned nothing for it; the new rule returns it. + #[test] + fn the_pc2_resume_of_21_25_11z_restarts_under_the_new_rule_and_not_the_old() { + let old_rule = |slots: &[ResumeSlot]| -> Vec { slots.iter().enumerate().filter(|(_, s)| s.faulted).map(|(i, _)| i).collect() }; + let pc2 = [ResumeSlot { faulted: false, live: false }]; + assert!(old_rule(&pc2).is_empty(), "the 0.3.9 rule left the 5090's slot unarmed: this is the defect"); + assert_eq!(slots_to_rearm_on_resume(&pc2), vec![0]); + } + + /// The check 90 s after a resume names every enabled card without a hash rate, and nothing else. + #[test] + fn resume_check_names_the_cards_not_mining() { + let cards = [card("NVIDIA GeForce RTX 5090", true, "off", 0.0), card("AMD Radeon(TM) Graphics", true, "mining", 3.4), card("Intel UHD", false, "off", 0.0), card("RTX 3060", true, "faulted", 0.0)]; + let lines = resume_check(&cards); + assert_eq!(lines.len(), 1, "{lines:?}"); + assert!(lines[0].starts_with("NVIDIA GeForce RTX 5090 is not mining 90 s after resume (state off"), "{}", lines[0]); + assert!(resume_check(&[card("RTX 5090", true, "mining", 118.9)]).is_empty()); + } +} + #[cfg(test)] mod tests { use super::{digest_from_line, parse_race, switches_of, sync_decision, Reading}; diff --git a/docs/plans/proving-v1.md b/docs/plans/proving-v1.md index bad75e90..ecaa10ba 100644 --- a/docs/plans/proving-v1.md +++ b/docs/plans/proving-v1.md @@ -112,6 +112,8 @@ Reading. Nobody pays an aggregator as a separate role: Aztec's 30% goes to whoev | Apple silicon default | **off** | the gate was "a shard under 60 s with the miner running": the M5 Max CPU took 41.3 and 55.4 s for EMPTY shards under tonight's load and 272 s for a 200-pgas shard on 4 October; a full shard at `S_p` was never under 60 s. Settings switches it on | bench-log "proving v1" CPU chain row; 4 October CPU rows | | The prover profile per card and the 12 GB and 16 GB gates (the project lead: "make sure we can prove on 12gb cards"; "is there any way we can make 12gb cards mine and prove?") | **measured on PC 2, the rows below** | the SP1 6.8.1 GPU server reads `ELEMENT_THRESHOLD`, `HEIGHT_THRESHOLD`, `SHARD_SIZE` and the `SP1_WORKER_NUM_*`/`BUFFER_SIZE` knobs from the environment it inherits (`sp1-core-executor-6.8.1/src/opts.rs`, `sp1-prover-6.8.1/src/worker/config.rs`); the app passes a profile per card (`provedefault.rs`) and the host forwards it | the sweep job `memsweep-pc2-pv1` and the miner-on run | +The resume path (5 October 2026, the 0.3.11 app): `POST /api/resume` on 0.3.9 re-armed only FAULTED cards (`stop_miners("paused")` clears every slot's `restart_at`), so a healthy paused card stayed "off" at 0 MH/s until the app was relaunched: PC 2 at 21:25:11Z (the aggregation-cost job's pause and resume; `[ok] mining resumed` then `0.00 MH/s, waiting` for 20 minutes), the Mac that afternoon. Now every slot without a live worker is re-armed and its pack exported again before the start, and 90 s later `resume_check` logs `resume: is not mining 90 s after resume (state ..., pid ...)` for every enabled card without a hash rate (`engine.rs`, three unit tests: the state machine, the 21:25:11Z case against the old rule, the check). + A prover job on a shared card runs as the app's user or cleans its socket (`pkill -f sp1-gpu-server; rm -f /tmp/sp1-cuda-*.sock` at the start and the end; `tools/ci/prover-socket-check.sh`): the root-socket fault of 20:00Z, bench-log. ### The prover profiles: the tiers from the S_p curve (bench-log, "proving v1", the sweep, the miner-on pair and the curve) diff --git a/tools/proving-v1/pc2-socket-fix.ps1 b/tools/proving-v1/pc2-socket-fix.ps1 new file mode 100644 index 00000000..80eb0d28 --- /dev/null +++ b/tools/proving-v1/pc2-socket-fix.ps1 @@ -0,0 +1,31 @@ +# The root-socket fix for PC 2 (5 October 2026): a measurement job that ran igneum-prove-host as root inside WSL2 left +# /tmp/sp1-cuda-0.sock owned by root (and possibly a root sp1-gpu-server), so the app's prover (its own WSL user) fails +# every shard with "CudaClientError: Connect(PermissionDenied)". This job, as root: kills every sp1-gpu-server, removes +# the sockets, prints their owners before and after, then switches the app's prover off and on so its next shard +# starts a fresh server under the app's user. Never stops the miners. About 60 s. +$ErrorActionPreference = 'Continue' +$urlFile = if ($env:IGNEUM_APP_DIR) { Join-Path $env:IGNEUM_APP_DIR 'app.url' } else { Join-Path $env:LOCALAPPDATA 'igneum\app\app.url' } +if (-not (Test-Path $urlFile)) { $urlFile = Join-Path $env:LOCALAPPDATA 'igneum\app\app.url' } +$base = (Get-Content $urlFile -Raw).Trim().TrimEnd('/') +function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') } +function Prove($on) { try { (Invoke-RestMethod -Method Post -Uri "$base/api/prove" -ContentType 'application/json' -Body (@{on=$on} | ConvertTo-Json -Compress) -TimeoutSec 10) | ConvertTo-Json -Compress } catch { "error: $_" } } +"RESULT start $(Stamp)" +$bash = @' +echo "RESULT sockets_before $(ls -la /tmp/sp1-cuda-*.sock 2>&1 | tr '\n' ' ')" +echo "RESULT servers_before $(ps -eo user,pid,cmd | grep -E '[s]p1-gpu-server' | tr '\n' ' ' || echo none)" +pkill -f sp1-gpu-server 2>/dev/null; sleep 2; rm -f /tmp/sp1-cuda-*.sock +echo "RESULT sockets_after $(ls -la /tmp/sp1-cuda-*.sock 2>&1 | tr '\n' ' ')" +echo "RESULT servers_after $(ps -eo user,pid,cmd | grep -E '[s]p1-gpu-server' | tr '\n' ' ' || echo none)" +'@ +$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = $env:TEMP } +$bashFile = Join-Path $job 'socket-fix.sh' +[IO.File]::WriteAllText($bashFile, ($bash -replace "`r`n", "`n"), (New-Object System.Text.UTF8Encoding $false)) +$wslPath = (& wsl.exe -d Ubuntu-24.04 -u root -- wslpath -a ($bashFile -replace '\\', '/') 2>$null) +if (-not $wslPath) { $wslPath = '/mnt/c' + ($bashFile.Substring(2) -replace '\\', '/') } +& wsl.exe -d Ubuntu-24.04 -u root -- bash (($wslPath -replace "`0", '').Trim()) 2>&1 | ForEach-Object { ($_ -replace "`0", '') } +"RESULT prove_off $(Stamp) $(Prove $false)" +Start-Sleep -Seconds 15 +"RESULT prove_on $(Stamp) $(Prove $true)" +Start-Sleep -Seconds 40 +$st = $null; try { $st = Invoke-RestMethod -Uri "$base/api/state" -TimeoutSec 20 } catch {} +"RESULT end $(Stamp) proving status: $($st.proving.status) message: $($st.proving.message)" From 26ebf9cf2938c361c72b0da63cc04b9b6a1bf671 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:57:21 +0000 Subject: [PATCH 084/158] release-0.3.10 plan: the ship (run 37374158235 after six dispatches through GitHub's outage, the fallback rehearsed on PC 1 and stood down), the rollout with per-machine times, the hand nodes and the seed, the digest sweep, the mixed fleet, the next cut and the open items Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.10.md | 223 +++++++++++++++++++++++++++++++++-- 1 file changed, 216 insertions(+), 7 deletions(-) diff --git a/docs/plans/release-0.3.10.md b/docs/plans/release-0.3.10.md index 3caa8d85..dda7f443 100644 --- a/docs/plans/release-0.3.10.md +++ b/docs/plans/release-0.3.10.md @@ -1,4 +1,8 @@ -# Igneum Miner 0.3.10: the certificate-driven reorg, transaction gossip and the PC-built Windows node, 5 October 2026 +# Igneum Miner 0.3.10: the certificate-driven reorg, transaction gossip, the pack loader fix, the six-section miner and the PC-built Windows node, 5 October 2026 + +Shipped: manifest 0.3.10 published 21:32:40Z, every reachable machine on 0.3.10 by 21:49:41Z, the hand nodes and the seed on 21d4c73c by +21:50:15Z, one digest (1f4b4425...) on every node that restarted; PC 37ba0461 (the owner's laptop) mid-install and Sam's Mac quit at the +time of writing. The cut ran from 16:39Z to 21:5xZ, two hours of it GitHub's hosted-runner outage. Release engineer, from 16:39 UTC. Worktree `/Users/joshm/Projects/igneum-wt-ship0310`, branch `release-0.3.10` from master after the 0.3.9 merge. Fork worktree `vendor/igneum-node-0310`, branch `release-0.3.10` from a24ab01a (the 0.3.9 fork tip). @@ -106,7 +110,9 @@ manifest republished 17:59:14Z, `docs/plans/finality-v3-devnet-publish.md`): the | PC 37ba0461 | silent (0.3.7 at the 0.3.8 cut) | | | Master moved under the branch while it was cut (fef98a2/a850aef CLAUDE.md, 900bba7 the site's 0.3.9 download cards, 46a6a4e and -bf8d1ba the N3 record, a93199a); merged as d1f4923 (docs and site only, 5 files, no conflict). +bf8d1ba the N3 record, a93199a); merged as d1f4923 (docs and site only, 5 files, no conflict). It moved again at 19:4xZ (the coordinator's +`explorer` merge, origin/master 9746391: the observer's explorer detail, `site/api/stats.mjs` and `supply.mjs`, the explorer pages, three node +tests and `tools/ci/public-api-check.mjs` in ci.yml; no app, node or packaging file): the final merge to master lands on it (section 7b). ## 5. The node binaries and the DMG (fork 21d4c73c, app 5520fb1 and later) @@ -232,22 +238,207 @@ the app (G, H, I: the UI, the engine additions, the export lock), the two Window | `cargo build --release` in `app/igneum-app`, then `cargo test --release -p igneum-app`, under the lock on this Mac (the coordinator's ask) | 19:24Z: ok, 103 (lib) + 27 (ota-sign) + 8 (prove-verify), 0 failed; `igneum-app 0.3.10` | | `--mode id` on the worktree's host (unchanged prover) | shard `0x2b1a81cb...`, aggregator `0x474678f3...` | | The DMG, `packaging/mac/build-dmg.sh` under the lock, 19:24:57 to 19:25:18Z | `Igneum-Miner-0.3.10.dmg` 151687c5672553c571af7224a8e4228348135b8a313fc89e6641db7ae21c85b3, 41,383,375 bytes (engine 0.3.10 with G, H, I; node 21d4c73c; the 0.3.9 prover), fingerprints 477bb0ef and ed9c4d2e; it replaces the e8f1f9f6... DMG of the first tree | -| PC 1 app-only job (`node tools/build-job.mjs run --target ae432dc7 --targets windows --no-node --no-tests`, the branch's new `--no-node`) | (pending) | +| PC 1 app-only job (`node tools/build-job.mjs run --target ae432dc7 --targets windows --no-node --no-tests`, the branch's new `--no-node`) | published 19:24:50Z, done 19:26:47Z in 14 s (the engine alone, 6 s on PC 1's warm cache): igneum-app.exe 4564f8502bf40fdfb61a979ff19543838ca66be95b6b9f0beeaee53330c4d284 (2,962,944), warnings only (the dead-code set of 5a plus `count` is never used); `host.cpp` (WM_GETMINMAXINFO, WM_DEVICECHANGE) compiles on the runner only: CI run 37363381420 (section 7b) | Not in this tree (they arrived after it; next cut): `job-console` 3562f26 (its second offer, 19:2xZ: power control as a setting, default off), and the FORK-side `pack-loop` 05ef0fa3 (`vendor/igneum-node`: the miner checks every pack and rebuilds a refused one, exit 44; a node change: the node stays 21d4c73c). The app side of af983a7 (`watchdog.rs` `PackRebuilds`, the engine re-exporting the pack on a refusal, capped per -epoch) works with the 21d4c73c miner: it keys on the worker's refusal line, not on exit 44. +epoch) is keyed on the miner's exit 44, which only the fork-side miner emits (the pack-loop agent's correction, 19:3xZ): with the 21d4c73c miner a worker refusal line shows "program pack out of date, rebuilding" on the strip, the card and the log but does NOT re-export; with the attempt-aware workers of this release a refusal means a genuinely broken pack, so the gap is small, and the self-healing half lands with the next node cut (section 11). -(pending: the push, CI, the publish) +### 7b. The second push and CI + +`git push origin release-0.3.10` at 5b0d54f (19:26:13Z; the pre-push hook flipped the two site files again, restored with `git checkout -- site/`), +`gh workflow run windows.yml --ref release-0.3.10` -> run 37363381420 (queued 19:26:19Z on 5b0d54f). The ship state file now names 5b0d54f +and that run. At 19:46Z the run was still QUEUED with no runner assigned, as were the repo's two `ci` runs (19:26Z, 19:39Z): GitHub's status +API reported Actions "degraded_performance" with an unresolved "Incident with Actions" (investigating since 19:15:17Z). Nothing in this +repository or on this Mac can shorten that: the installer comes only from the hosted `windows-latest` runner. The rollout waits for the +verdict; every other step is staged (the DMG, the signed inputs, the runbook `r0310/rollout.sh` with the exact commands). + +Run 37363381420 ended at 19:41:24Z as `failure` with its first job CANCELLED by GitHub after 15 minutes queued ("The job was not acquired by +Runner of type hosted even after multiple attempts", the job's annotation) and the build job skipped: nothing of the tree ran. The workflow +was dispatched again under a watcher that dispatches again on that same annotation and stops on a green or on a failure of the tree itself: +try 2 run 37365130137 (19:42:43Z), try 3 run 37366744501 (19:57:52Z), try 4 run 37368355454 (20:13:21Z), try 5 run 37369931084 +(20:28:32Z), every one cancelled by GitHub the same way after about 15 minutes queued; a follow-on watcher took over at 20:54:38Z and +dispatched try 6, run 37374158235 (20:54:55Z), which a runner acquired at 21:24:04Z and which went green at 21:30:29Z (section 7d). +Six dispatches, 2 h 04 min from the first to the green; GitHub's incident ("major outage" at 20:4xZ) was the whole of it. + +### 7c. The fallback, prepared at 20:3xZ (the coordinator: a switch at 21:00Z, not a scramble; nothing built yet) + +What the runner does for the installer, and what PC 1 has for each step (probe job `probe-installer-pc1-0310`, read-only, ran 20:33:5xZ +in 3 s; its `R` helper collided with PowerShell's `r` alias so every line came back inside an error message, the facts intact): + +| Runner step | Needs | PC 1 (ae432dc7) | Fallback | +|---|---|---|---| +| engine (`cargo build --release --locked`, MSVC target) | Rust on Windows | not probed (the PC's build jobs build the engine in WSL on the GNU target: igneum-app.exe 4564f850..., 5a) | the GNU-target engine from job `build-20261005-192450` unless `cargo` exists on the Windows side (checked at the start of job B); recorded either way | +| packaged configuration | the intake key and the folder token as files | the installed 0.3.9 app's `igneum-app.json` at `C:\Users\Admin\AppData\Local\Programs\Igneum Miner\` carries the same manifest URL and key (`override=False`: 0.3.9 shipped no packaged override) | copy that file and add `node_override_params` = the four-field object (not a secret); no secret leaves the Mac | +| payload inputs (`payload-inputs.zip`, signature, hashes, node commit) | the dl folder URL | the URL's folder is in the packaged json | download, verify the sha256s against `payload-inputs.json` (the signature is verified by the runner's step with the key compiled into the app; on the PC the app's own `igneum-ota-sign` is not installed: recorded as a gap, the hashes stand) | +| window host (`app\windows\BUILD-APP.bat`, MSVC v143 cl.exe and rc.exe) | Visual Studio with MSVC | `vcvarsall.bat` under `C:\Program Files\Microsoft Visual Studio\...`, cl.exe 19.51.36260 | runs as on the runner | +| payload (`make-payload.sh`, bash) | Git Bash | `C:\Program Files\Git\bin\bash.exe` (and WSL) | runs as on the runner | +| installer (`build-installer.ps1`: Inno Setup 6 `ISCC.exe`, rcedit) | Inno Setup 6, rcedit-x64 | ISCC NOT FOUND; rcedit not on PATH; winget present | `build-installer.ps1` installs Inno Setup 6 through winget (a tool install on PC 1: the coordinator's call) and downloads rcedit-x64 from GitHub (`-NoRcedit` skips it: the exes then ship without the coin icon and version block, cosmetic, recorded) | +| smoke run, launcher dry run | the exes | | the same PowerShell lines in job B | +| the files back to the Mac | | `relay/clients/send.ps1` (a file up to 50 MB straight to Blob): the installer is 25 MB, the payload zip 35 MB | two `send.ps1 ` calls with the sha256s in the report; on the Mac `node tools/relay.mjs read `, sha256 compared, `packaging/windows/check-runtime-dlls.sh` on the payload folder | +| code signing | none on the runner either | | none | + +The jobs, in order (neither published until the word; both staged in the scratchpad `r0310/fb/`: the tree zip `fb-tree-0310.zip`, 709,814 bytes, +98 files from 5b0d54f by `git archive`, sha256 012c8a52c27631e8539704d703bf13eb68de0afd9cef094e68d1e6fc398f2cfc; the script `fb-installer-pc1.ps1`, +which stops on any sha mismatch, pins the inputs' node commit against `node-source.pin` as the runner's G13 step does, and takes `-NoRcedit` as its one argument): + +1. `fetch` job `fb-tree-0310` to ae432dc7: a zip of the tree at 5b0d54f (`git archive`: `app/windows`, `brand/icons`, `packaging/windows`, + `proto-cuda/windows-app`, `proto-cuda/{host.cu,build.bat,README.md}`, `proto-opencl/{host.c,build.bat,README.md}`, `wsl2`, the two + worker `.rc` files), `--dir jobs --extract --extract-dir fb-0310 --fresh`. +2. `run` job `fb-installer-0310` to ae432dc7 (PowerShell, 20 min): `cargo --version` if any; the packaged json copied and extended; the + inputs zip downloaded and hash-checked; `BUILD-APP.bat`; `make-payload.sh` under Git Bash with `IGNEUM_APP_EXE` = the WSL engine + (`\\wsl$\Ubuntu-24.04\root\igneum-build\...\igneum-app.exe`, its sha256 checked against 4564f850...); `build-installer.ps1 -Payload + -Version 0.3.10` (with or without `-NoRcedit` per the word); `igneum-app.exe --version`, `Igneum Miner.exe --version`; `send.ps1` the + installer and the zip; every version and sha256 in the report. +3. On the Mac: the two files into the downloads folder, hashes equal to the report, the DLL gate, then + `node tools/ship-app.mjs 0.3.10 ... --from dmg` (preflight, then dmg already, copy, manifest, deploy, verify, console; the fetch step is + skipped by `--from`, the console item carries no run id), then the rollout as planned. + +What the gate records in this plan if the fallback ships: the installer marked "PC-built on ae432dc7, GitHub run owed"; non-reproducible +(the runner's engine is the MSVC target, this one the GNU target from WSL; Inno Setup's version from winget; cl.exe 19.51.36260; Git Bash's +version; Windows 11 build 26200); the sha256 and size of the engine, the host, the payload zip and the installer; the GitHub run's id and its +own installer sha256 when it lands (they differ by construction); and the next cut goes back through the runner. + +The coordinator asked at 21:3xZ whether the Mac mingw path was now faster and safer; the answer stands as below (there is no such path to an +installer), so PC 1's queue position was kept for attempt 4. + +What does not exist: a Mac mingw build of `host.cpp` (the coordinator's "how 0.3.7 shipped"): 0.3.7's node exes were cross-built on the Mac +and its installer still came from the runner (release-0.3.6 plan, section 9); the host has only ever been built by `BUILD-APP.bat` with MSVC, +on the runner or on a PC. So if PC 1 lacked MSVC the next fallback would be new work, not a known path; PC 1 has MSVC, so it is not needed. + +### 7d. The fallback, run (the coordinator's word at 21:00Z: GitHub's status page "Actions: major outage", the sixth queued run) + +The PC 1 window: asked of the Counter ASIC coordinator (it schedules PC 1 tonight) at 21:0xZ; granted at 21:09:24Z when its reproducible +benchmark released the machine (every card restored; the RX 9070 XT back on the bus). `fb-tree-0310` (fetch) published 21:10:59Z, ran +21:11:32 to 21:11:33Z: the zip (709,814 bytes, sha256 ok) extracted into `%LOCALAPPDATA%\igneum\app\jobs\fb-tree-0310\fb-0310` (the +script's expected path was wrong and it now finds the tree by search). `fb-installer-pc1` (run, 25 min cap) published 21:19:28Z, FAILED at 21:20:05Z with exit 2 after 2 s: the script, not the build. +Its own lines: the tree found (82 files), `cargo on Windows: none` (so the engine is the WSL GNU-target build), Git Bash 5.3.15, Windows +10.0.26200, and then `engine not found at \\wsl$\Ubuntu-24.04\root\igneum-build\...\igneum-app.exe` with PowerShell's +`ItemExistsUnauthorizedAccessError`: the WSL tree belongs to root and the `\\wsl$` share refuses it to the app's non-elevated session. The +build jobs read that tree with `wsl -u root`, so the script now copies the engine out with +`wsl.exe -d Ubuntu-24.04 -u root -- bash -c "cp ... /mnt/c/.../fb-0310-work/igneum-app.exe"`. Republished as `fb-installer-pc1-2` at 21:2xZ +(the Counter ASIC coordinator kept PC 1 for it: "a 2-second exit 2 is the script, not the build", 5-minute reply window met). + +`fb-installer-pc1-2` (21:23:08Z) failed at 21:23:58Z, exit 2 in 4 s: `/root/igneum-build/app/igneum-app/target/...` does not exist (the build +job keeps ONE target dir for the whole job; the acceptance script had read the node exes from `/root/igneum-build/target/...`): the script +now finds `igneum-app.exe` under `/root/igneum-build` with `find` and prints its sha256 from inside WSL. `fb-installer-pc1-3` (21:26:52Z) +failed at 21:27:20Z, exit 2 in 4 s: the inline `bash -c "..."` string lost a quote on its way through PowerShell (`unexpected EOF while +looking for matching quote`), the class the 0.3.6 cut closed for the app's own WSL calls (3811d8e, "every WSL script runs from a file") and +which this scratch script had reopened: the bash part is now written to `engine.sh` on the PC (LF, no BOM, the acceptance script's own +pattern) and run as `bash `. Three 4-second failures of the script, none of the build; by the Counter ASIC coordinator's rule +its 10-minute measurement takes PC 1 first, then a read-only path probe (every path the script needs, found and printed), then attempt 4. + +Before attempt 4 one more change, after reading the fixed block: `find ... | tail -1` would take the NEWEST `igneum-app.exe` under +`/root/igneum-build`, and other agents' build jobs have run on PC 1 since mine (job-console's, pack-loop's), so the sha check could stop +attempt 4 on another tree's engine. The engine this plan already holds and verified (4564f850..., 2,962,944 bytes, from PC 1's own job +`build-20261005-192450`) now travels INSIDE the tree zip (`fb-tree-0310b.zip`, with it under `app/igneum-app/target/x86_64-pc-windows-gnu/release/`), +and the script reads nothing from WSL at all; the path probe checks that file too. + +Attempt 4 was never published: at 21:30:29Z GitHub's runner acquired try 6 (run 37374158235, dispatched 21:10:04Z on 5b0d54f) and it went +GREEN (the parse job 21:24:04 to 21:24:57Z; engine, window host, payload, installer, smoke run 21:25:18 to 21:30:29Z; the G13 inputs step +against the 21d4c73c inputs of 7a). The recipe's own installer therefore ships; the fallback stops here with nothing built on PC 1, PC 1 +released to the Counter ASIC coordinator at 21:31Z, and the prepared pieces (the tree zip with the engine, the installer script, the two +probes, the runbook steps) kept in the scratchpad `r0310/fb/` as the rehearsed path for the next outage. Cost of the detour: three +4-second script failures on PC 1 and about 70 minutes of the shipper's attention; the gate record "PC-built, non-reproducible, GitHub run +owed" is not needed. + +### 7e. The ship (21:31 to 21:40Z) + +`OTA_SKIP=1 CONSOLE_SKIP=1 packaging/windows/fetch-ci-artifacts.sh 37374158235` (21:31:21Z): the installer and the payload zip into the +downloads folder; the DMG copied beside them. The payload zip holds the rebuilt workers (igneum-worker-cuda.exe 85cc357b..., 1,510,912; +igneum-worker-opencl.exe afa73a32..., 445,952: both differ from 0.3.9's f50e19f2.../1abc673e... and from the PCs' hot-fix pair), the PC-built +node 3adb01a7..., and the runner's MSVC engine 496c4883... (3,361,792). The first ship run (21:32:02Z) stopped in preflight: the tree was 4 +commits behind origin/master (the coordinator's explorer merge, 9746391: docs, site, observer, ci.yml; no app, node or packaging file), so +`origin/master` was merged as ff873f6 (37 files, no conflict), pushed 21:32:27Z (the hook's site flip restored), and the state file kept +`sha` = 5b0d54f, the CI commit, as the 0.3.6 and 0.3.9 cuts did. + +``` +node tools/ship-app.mjs 0.3.10 --node vendor/igneum-node-0310 --branch release-0.3.10 --public \ + --activation-height 135200 --deadline-note "finality v3" --notes "" --from ci +``` + +| Step | Result | +|---|---| +| preflight | ok: tree ff873f6 clean, 0.3.10 in all 6, fork 21d4c73c, gh igneum-labs, live inputs 21d4c73c built 19:24:41Z | +| ci | already: run 37374158235 green | +| fetch, dmg, copy | already (above) | +| manifest | 0.3.10 mac+windows, signed (key 8f186e37...), verified locally; `consensus` carried over from the folder's 0.3.9 manifest: `activation_height` 135200, `deadline_note` "finality v3", `override` the four-field object; and in `dl/public/` (with the wallet 0.1.4 manifest) | +| deploy | one deploy, 21:33Z | +| verify | the token folder: `igneum-app-latest.json` 0.3.10, signature ok, mac 151687c5..., windows 24e58849...; the public folder: every file HEAD 200 with the local size (the DMG, the installer, the 0.3.9 HiveOS package, the wallet DMG, the four `/public/` aliases, the two manifests and their signatures, `igneum-downloads.json`), but the byte comparison of a json file against the edge still failed after 12 tries at 21:37Z and again on a resume from `verify`: the edge cache, the class of the 0.3.6 and 0.3.9 verifies (section 11 if it does not clear) | +| console | (pending: `--from console` after the verify clears) | + +| File | sha256 | Size | +|---|---|---| +| Igneum-Miner-0.3.10.dmg | 151687c5672553c571af7224a8e4228348135b8a313fc89e6641db7ae21c85b3 | 41,383,375 | +| Igneum-Miner-Setup-0.3.10.exe | 24e58849f2b517e8c5579455e8c9d8376ff7dd27052f458ef91913cbc48abc88 | 49,858,273 | +| igneum-windows-app.zip | 16b68b7bf625a946ac62a22413982b38c6649a124d799b08424a070f1b34810e | 71,809,486 | + +The installer is 30 MB larger than 0.3.9's (19,836,912): the two NVRTC DLLs (93 MB unpacked) ride with the rebuilt CUDA worker now. + +`update-now-0310` to all, published 21:39:59Z (apps woken, stamp a23f594a). Timing with the Counter ASIC coordinator (it schedules PC 1 tonight): PC 1 +was released by its hot-table job at 21:35:39Z, so one update-now went to every machine; its era measurement starts on PC 1's 0.3.10 STATUS line. + +## 8. The machines after the publish (manifest live 21:33Z, update-now 21:39:59Z) + +Baseline 21:40:31Z: Mac d937c69d app 0.3.9 (its card reads node 1, a24ab01a), PC 1 ae432dc7 0.3.9 node a24ab01a DAA 133,903 141.5 MH/s, +PC 2 1ccfe586 0.3.9 node a24ab01a DAA 133,945 0.0 MH/s (its 5090 worker off since a job's `/api/resume` at 21:25:11Z that the 0.3.9 app +answered and never acted on, section 11), Sam's Mac 3a9bf309 quit 53 min earlier, PC 37ba0461 0.3.9 node `2.1.0` 2.0 MH/s. A machine +counts as updated when its engine logs the 0.3.10 header, its node reports a DAA score and its miner a hash rate on 0.3.10. The two checks +asked by the coordinator: (1) the workers start without the seed-words error on the first try, on the rebuilt pair; (2) the Mac's card +shows node 1's digest, by design. C5: the provers' "stopped after" lines (shards aborted by the restart). + +| Machine | On 0.3.10 | Its log | +|---|---|---| +| PC 1 ae432dc7 (Windows) | engine restart 21:40:41Z (run `win-ae432dc7-20261005-214041`), 42 s after the job; `[ok] updated to Igneum Miner 0.3.10 from 0.3.9` 21:40:42Z; `igneumd started` 21:40:46Z (the PC-built node from the new install path); `node proof verifier: command` and `reported: command` +6 s; `cards: NVIDIA GeForce RTX 5090 [discrete, off] \| AMD Radeon(TM) Graphics [integrated, off] \| AMD Radeon RX 9070 XT [discrete, off]` (hotplug's line: the iGPU integrated and off, the two discrete cards then started); miners started 21:40:47Z (nvidia-ae432dc7-1, the bundled `igneum-worker-cuda.exe`) and 21:40:48Z (amd-ae432dc7-3, the bundled OpenCL worker); `update to 0.3.10 complete` 21:42:12Z. Check 1 PASS: the NVIDIA miner's `epoch seed 66b26013... (daa 133967): CPU program and cache ready in 176 ms` 21:40:47Z, then `worker: info first pack packs\devnet: nvrtc 170 cache 4 dataset 23 check 249 race 37802 ms variant base; self-test PASS` and `worker: ready cuda NVIDIA_GeForce_RTX_5090 ... prepare 1 path nvrtc 12.8` at 21:41:25Z, no `epoch seed bytes do not give` line; STATUS 45.1 MH/s wall at 60 s (124.1 inside jobs), 124.3 MH/s inside jobs from 90 s on; the console 141.4 MH/s at 21:45:17Z (both cards). The node log: `igneumd/2.1.0` (no commit: the PC build, section 11), `Calibrated v1 fees ... 210000`, digest 1f4b44255fcd2ea8f75664ed47200f409186ddd2292960c9e2cf95bbbdc11505, peers node 1 (192.168.68.64) outbound and inbound and the seed 188.245.5.161, flows registered at THEIR protocol version 13 (a v14 node beside v13 peers, the mixed-fleet case of section 10, measured) | | +| Mac d937c69d | engine restart 21:40:51Z (run `mac-d937c69d-20261005-214051`), 52 s after the job; `[ok] updated to Igneum Miner 0.3.10 from 0.3.9` 21:40:52Z; `a node already answers on 127.0.0.1:26610; using it (it is not stopped by this app)`: the app attaches to node 1 as it has since 17:45Z, so its card shows node 1's version and digest (check 2, by design); `cards: Apple M5 Max [apple, off]` (its miner was off before the update too); `update to 0.3.10 complete` 21:42:22Z | | +| PC 2 1ccfe586 (Windows) | its 0.3.9 app fetched the woken jobs file at 21:40:33Z (`1 new for this machine, 1 queued`) and queued the update behind the aggregation-cost agent's running job `agg-cost-pc2-2` (one job at a time), so the update ran at 21:49:24Z when that job ended: installer downloaded and verified 21:49:27Z, `quit: stopping the miners, then the node` 21:49:29Z (no prover "stopped after" line: no shard in flight), engine restart 21:49:41Z (run `win-1ccfe586-20261005-214940`), 9 min 41 s after the job; `igneumd started` 21:49:42Z; `cards: NVIDIA GeForce RTX 5090 [discrete, off] \| AMD Radeon(TM) Graphics [integrated, off]`; miners started 21:49:44Z. Check 1 PASS: `epoch seed 66b26013... (daa 134395): CPU program and cache ready in 169 ms`, `worker: ready cuda NVIDIA_GeForce_RTX_5090 ... first pack ... self-test PASS` at 21:50:21Z, no seed-words line; STATUS 120.7 MH/s inside jobs at 60 s, 120.5 at 90 s; the console 123.2 MH/s at 21:53:04Z. This also ended the `/api/resume` no-op (its 5090 had been off since 21:25:11Z). The node log: `igneumd/2.1.0`, digest 1f4b4425..., flows at version 13 with node 1 and the seed (still a24ab01a for 10 s more) and at 14 with PC 1. The prover: `prover: host /opt/igneum/igneum-prove-host (WSL2), CUDA`, the pinned ids, then three assigned shards (22126, 51922, 84099) each failed with `Failed to create the CUDA prover impl: CudaClientError: Connect(Os { code: 13, kind: PermissionDenied })` at 21:49:56, 21:50:12 and 21:50:32Z: PC 2's prover is dark after the update (section 11) | | +| PC 37ba0461 (Windows, the US laptop) | its 0.3.9 app (run `win-37ba0461-20261005-202247`) downloaded and verified the installer at 21:40:52Z, started it at 21:40:53Z (`per-user install, no administrator prompt`), stopped its miners and node at 21:41:16Z, and no 0.3.10 engine run had reported by 21:56Z (the console: `STOPPED (update)` for 14 min): the install is in progress or stuck on the owner's machine; nothing to drive from here (section 11) | | +| Sam's Mac 3a9bf309 | quit since 20:47Z (0.3.9); takes 0.3.10 when it is started | | + +C5, the shards aborted by the restart: PC 2's engine logged no prover "stopped after" line at its quit (21:49:29Z) and PC 1 runs no prover; +the Mac's prover was off. Observed count: 0. The coverage numbers measured across the restart carry no abort from it. + +The ship's last step, `--from console` (21:55:08Z): item #364 "Igneum Miner 0.3.10 shipped (mac+windows)"; the tool's closing line +"manifest 0.3.10 published 2026-10-05T21:32:40Z". ## 8. The machines after the publish (pending) -## 9. The hand nodes and the seed +## 9. The hand nodes and the seed (21:49 to 21:50Z) -(pending) +Moved while PC 2 and PC 37ba0461 were still on 0.3.9 (their updates gated by another agent's job and a slow download): the digest does +not change with 0.3.10 and a v14 node beside v13 peers is the measured mixed-fleet case (section 10), so moving them shortened the mixed +window. + +| Node | Command | Result | +|---|---|---| +| The observer, then node 1 | `IGNEUMD=/target-integration/release/igneumd IGNEUMD_COMMIT=21d4c73c infra/devnet/restart-hand-nodes.sh ''` (the branch's script: `grep -c` for the commit string, the object written to `/tmp/igneum-devnet/override-v3.json`, the previous file kept with a stamp) | observer restarted 21:49:38Z (pid 67770), node 1 21:49:50Z (pid 67963, caffeinate 67965); both print `Calibrated v1 fees ... from DAA score 210000` and digest 1f4b44255fcd2ea8f75664ed47200f409186ddd2292960c9e2cf95bbbdc11505; the Mac app's card follows node 1 from here (its node line reads 21d4c73c) | +| The seed 188.245.5.161 | `IGNEUMD_LINUX=infra/cross/out-0310/igneumd IGNEUMD_LINUX_SHA256=40be0e14... infra/devnet/restart-seed.sh ''` (the zig cross-build of 21d4c73c, glibc 2.36 target; the script checks the sha on both sides, keeps the previous binary as `igneumd.prev-035` and the previous override file with a stamp) | restart 21:50:15Z, unit active, MainPID 125195, `igneumd/2.1.0-21d4c73c`, `Calibrated v1 fees ... 210000`, digest 1f4b4425... | + +Peers after the restarts: the observer and node 1 connected to the seed within 20 s and register flows at protocol version 14 with each +other and the seed (node 1's inbound from the observer 21:50:08Z, node 1 to the seed 21:50:20Z, the observer to the seed 21:50:38Z; the +seed's three inbound peers from this Mac's address at 21:50:20, 21:50:27 and 21:50:38Z, all at 14). PC 1's node (started 21:40:46Z, before +the hand nodes moved) registered flows at version 13 with node 1 and the seed (then a24ab01a) and keeps them; PC 2's node (21:49:42Z) at 13 +with node 1 and the seed (10 s before their restarts) and at 14 with PC 1: the mixed fleet of section 10, measured on the live network, with +no refusal and no drop. + +### 9a. The digest sweep (21:50Z) + +| Node | Binary | Digest | Read from | +|---|---|---|---| +| PC 1 app node ae432dc7 | the PC-built 3adb01a7... (`igneumd/2.1.0`, no commit string) | 1f4b44255fcd2ea8f75664ed47200f409186ddd2292960c9e2cf95bbbdc11505 | its node log through the log intake (run 214041) | +| PC 2 app node 1ccfe586 | the same | 1f4b4425... | its node log (run 214940) | +| The Mac app d937c69d | node 1's (attached; its card's commit string is the app's own reading from its start at 21:40:51Z, before node 1 restarted, and refreshes on the app's schedule; its status line reads node 1's height and peers) | node 1's | | +| Node 1 | 21d4c73c Mac arm64 4bb356f4... | 1f4b4425... | `/tmp/igneum-devnet/node1.out` | +| The observer | the same | 1f4b4425... | `/tmp/igneum-devnet/observer-v4.out` | +| The seed | 21d4c73c Linux 40be0e14... | 1f4b4425... | the journal through `restart-seed.sh` | +| PC 37ba0461 | (its update in progress) | (pending) | | +| Sam's Mac 3a9bf309 | quit since 20:47Z | (pending) | | + +One digest, equal to the N3 publish's (`finality-v3-devnet-publish.md`) and to this cut's three readings on the fork (section 3): 0.3.10 +moved no parameter, as measured. ## 10. The mixed fleet during the window @@ -267,12 +458,30 @@ publish. ## 11. Open after the cut +The next cut (0.3.11), decided by the coordinator on 5 October 2026 night: + +| Branch | What | Why not 0.3.10 | +|---|---|---| +| fork `pack-loop` 05ef0fa3 (`vendor/igneum-node`) | `write_pack_checked`, `export-pack` exit 3, the force-prepare at the epoch boundary, the miner's exit 44 that unlocks the app's capped re-export | a node change after the node was frozen at 21d4c73c with its suites, harness runs and Windows acceptance done; the app side (af983a7) with the attempt-aware workers is the fix that matters tonight and it is in | +| `job-console` 13755b9, then 3562f26 and whatever follows | one hidden-console builder for every elevated launch, the spawn check in CI, PC 1 console watchers; then power control as a setting, default off (the project lead: "if we don't have to ask then don't ask") | arrived after the tree closed (both times); repoints elevated-exit's `include_str!` test at `platform.rs` at its own merge | +| `opencl-rdna4-telemetry` 7adcd4c (`igneum-wt-rdna4-telemetry`, not pushed: master + a08c371 as 38c9eec + 7adcd4c) | `igneum-gpu-telemetry.exe` (ADLX, SetupAPI bus, PDH fallback; amdgpu sysfs on Linux) built by `build-windows.sh`, shipped by `make-payload.sh` and `push-inputs.sh`; the engine runs it at `-l 5` and fills power, temperature, fan, memory clock and utilisation on the AMD card; 82 app tests pass. The 9070 XT measured on PC 1: 198.9 W, 64 C, 17.73 MH/s, 0.089 MH/W against the 5090's 0.398 MH/W (job `tele-measure-1`) | arrived after the tree closed; a new shipped exe and an engine source | +| `ember-tune` 54ff1bc (+38ef711, `igneum-wt-ember-tune`; its agent's message at 21:2xZ) | Ember Tune: `ember.rs` replaces the NVIDIA power-only run in `sweep.rs` and the AMD single-lever sweep of 720b369; sits on cherry-picks of 7adcd4c and 13755b9+3562f26, so those merge first; 93 app tests, `relay/test/ember.test.mjs` and `ui/tune-line.test.mjs` in ci.yml; design in `docs/plans/ember-tune.md` | arrived after the tree closed | +| the rest of `opencl-rdna4` a08c371 | the OpenCL worker's duplicate-platform fold, `--readback select`, `--memprobe`, `detect.rs parse_opencl_list`, the 9070 XT bench-log entry | conflicts with gpu-hotplug in `detect.rs` and `host.c`; only its EXPORT_LOCK hunk shipped (854f9a8) | + | Item | State | |---|---| +| The per-job build-inputs zip (68f14b9, c4's tooling commit; the coordinator's check after two agents' PC jobs ran on another agent's sources through the shared `build-inputs.zip` tonight, jobs build-20261005-191656 and -192537). Checked on this tree's own jobs in the live jobs file rather than a dry run (a dry publish would leave a stray entry in the file the ship deploys): `build-20261005-182405` (PC 1) pins `params.zip_url` = `.../build-inputs-20261005182334-74461.zip`, `params.sha256` 628e6dae..., size 8,266,818; `build-20261005-183131` (PC 2) pins `build-inputs-20261005183051-83796.zip`, c83ebb2a..., 7,271,492; both shas equal the local zips of those names, and 15 per-job zips sit beside the folder default. So a job published through `tools/build-job.mjs` pins the zip it just pushed. The residual: `packaging/ota/publish-jobs.sh add --kind build` WITHOUT `--zip` still defaults to the shared `$DEST/build-inputs.zip` (line 307); nothing refuses that name. A hand-added build job can therefore still pin whatever the folder default holds | build-job.mjs path closed; the hand path is the first item of the next cut: refuse `--kind build` without `--zip`, or default to the newest per-job zip | | `infra/cross/build-linux.sh` resolved a relative `TARGET_DIR` inside the node source after its `cd`, so the copy step shipped the previous build's bytes (twice tonight, caught by the commit string in `strings`). Fixed here (e0a5fd1). The class: any script that takes a directory argument and changes directory before using it; `proto-cuda/windows-node/cross-build.sh` takes the node worktree as `$1` and `CARGO_TARGET_DIR` from the environment (the 0.3.9 cut passed a relative one and it worked only because that script does not cd); a CI check for the shape is owed | fixed on the branch; the check owed | | hotplug's `proto-opencl/host.c` change (the worker's `--list` prints the PCI address, the key to one row per physical card): no prebuilt OpenCL worker is in the payload inputs (the live zip holds igneumd.exe, igneum-miner.exe and three DLLs), the payload carries `host.c` and `build.bat` and the app builds the worker on the PC from them (`engine.rs build_worker_from_source`), so the change ships inside the 0.3.10 installer; whether an app that already has a worker exe rebuilds it from the newer source was not read here. Check on the console after the update: the PCs' card rows carry the PCI address (and one row per AMD card) only if the worker was rebuilt; else a rebuild is the hotplug agent's follow-up | open: told the hotplug agent | | `kaspa-consensus` test `ban_is_decided_by_the_carrying_block_so_nodes_agree_on_every_voter_list` (c4-fix) failed once under the six-package parallel run on PC 2 with `UnexpectedDifficulty(487112096 vs 487129281)` in `mine_on_all` (section 3) and passes alone, twice. The helper builds a block on one `TestConsensus` and inserts it into others; the expected difficulty of the receiving node differs when the run is slow, which points at a wall-clock dependence (difficulty v2's sanitised clock per header). For the c4 agent: pin the clock or the timestamps in that helper, as the finality tests do for the cache queue. Until then a parallel six-package run can fail this test under load; the suites are run as consensus alone plus the other five | open | | `site/build.mjs` is not idempotent: one bench entry's label alternates between "RTX 5090 first run" and "RTX 5090, memory-hard dataset" on every run of the same tree (three runs at 18:38Z: first-run, memory-hard, first-run), because the build reads its own `site/journey.json` back (line 248) and the label table at lines 203 and 204 matches against what the previous run wrote. Every push flips the two files through the pre-push hook, which is why the 0.3.9 and 0.3.10 cuts both met a modified tree after the push | open: build the journey from the sources only (never from the previous output), then have the hook refuse a push whose build differs from the committed site | | The Windows payload carries the PC's three mingw DLLs (34 MB of inputs, `libstdc++-6.dll` alone 26.3 MB unstripped) although the exes import none of them since housekeeping A; the installer grew 5.2 MB | open: drop the DLLs from `push-inputs.sh` and `jobbuild.rs`'s copy once no shipped fork needs them, or strip them | | The two worker exes' version blocks say 0.3.0 (`proto-cuda/nvrtc/igneum-worker-cuda.rc`, `igneum-worker-opencl.rc` are not among the six files `ship-app.mjs` bumps) | open: add the two `.rc` files to `VERSION_FILES` | +| The app's re-export of a refused pack (af983a7's `watchdog.rs` `PackRebuilds`, 3 per epoch) waits for the miner's exit 44, which the 21d4c73c miner never emits: a refusal in 0.3.10 shows the notice and restarts the worker, no re-export. The fork-side `pack-loop` 05ef0fa3 (the miner checks every pack, rebuilds a refused one, exit 44) is the other half | next node cut | +| C1 (the consequences reviewer, 20:0xZ): the 0.3.10 node's `igneum_exportSegments` (fork `igneum/exec/src/rpc.rs`) writes no `daaScore` and no `feesV1ActivationDaa` per segment, which the 0.3.9 exporter needs to replay both sides of the fee switch (`export/src/main.rs`: "a dump without `daaScore` is accepted only when the switch is never or 0"). Measured here: the handler (90 lines from `rpc.rs` 849 in `vendor/igneum-node-0310`) carries neither key (`daaScore` appears in the fork only in other RPCs, lines 239, 423, 819); the reviewer names `vendor/igneum-node-pv1` at eb32c645 (on 21d4c73c) as the carrier: its `rpc.rs` writes `daaScore` per segment (line 961) and `fees` and `feesV1ActivationDaa` at the top level (line 982); confirmed here at 20:4xZ (`git -C vendor/igneum-node-pv1 grep -n feesV1ActivationDaa -- igneum/exec/src/rpc.rs`: line 982); my first read of that path at 20:1xZ was wrong. So at H = 210,000 (about 19:50Z on 6 October) every 0.3.10 prover's export of a post-H block fails or meters with the wrong table and the fleet's provers go dark, unless the next node cut carries that RPC onto every prover before H, or H is republished later. The mechanism (the proving agent, 20:1xZ): the app's prover calls the exporter with no fee flags, so from H every app prover on 0.3.10 cuts with the prototype table and every statement is vetoed. The two closes: (a) the proving-v1 fork (eb32c645, on 21d4c73c) on every prover before H through 0.3.11; (b) republish H = tip + 86,400 by the fee-switch plan's rule. Decision due 16:00Z on 6 October; the coordinator, the proving agent and the 0.3.11 shipper hold the same line | open, dated: (a) or (b) by 16:00Z on 6 October | +| C5 (the same reviewer): the app kills its prover child on quit (`prover.rs` 266 to 272), so the `update-now` of this rollout aborts whichever shard each prover has in flight (up to 37 s each, no payout). Accepted as the cost of the restart; the count is read after the rollout from the provers' "stopped after" lines (section 8) so the coverage numbers measured across the restart are read with it | recorded at the rollout | +| `/api/resume` answered ok on the 0.3.9 app and never restarted the miners (PC 2's 5090 worker "off" with the card holding 1.7 GB from a job's resume at 21:25:11Z until its 0.3.10 restart, the iGPU miner too; the Counter ASIC coordinator, 21:4xZ). The class: a resume that reports success without a miner restart. The app should re-check the miner processes after a resume and report a failure | open: next cut | +| PC 2's prover after the 0.3.10 restart: every assigned shard fails at once with `CudaClientError: Connect(PermissionDenied)` (section 8), where 0.3.9's PC 2 proved shards from 17:36Z. The host, the pinned ids and the CUDA backend are detected as before; the connect that fails is the SP1 CUDA prover's client socket. The aggregation-cost agent's job `agg-cost-pc2-2` ran as root in WSL minutes before and its lines show no CUDA or socket change; the 0.3.10 app's prover path changed only to read the program ids (`--mode id`). Unexplained; handed to the coordinator and the Counter ASIC coordinator (PC 2's measurement agents) | open: PC 2 proves nothing until it is found; a restart of the WSL distro or of the CUDA prover service is the first thing to try | +| PC 37ba0461 (the US laptop) started the 0.3.10 install at 21:40:53Z, stopped its miners and node at 21:41:16Z and had not come back by 21:56Z: the per-user installer on the owner's machine, nothing to drive remotely | open: the console shows when it returns; its 0.3.10 line and worker start are read then | +| `dl/public/igneum-downloads.json` (the unsigned index the site's download page reads) alternates at the edge between the new bytes and the previous ones for over 20 minutes after the deploy (one fetch byte-identical at 21:52Z, the next three not): different edge nodes behind one hostname. The two signed manifests were byte-identical and verified from the first check. The ship's verify step counts it as a failure and refuses to post the console item, so the item was posted with `--from console` | open: the verify should accept the index after the signed manifests pass, or retry it for longer; the site serves the previous version's buttons from a stale edge until it settles | | The PC-built node binaries embed no commit (the build inputs zip has no git dir, `build-info` falls back to the bare version): the console shows PC 1 and PC 2 as node `2.1.0` with no commit after 0.3.10, as the 0.3.6 PC build did. The build inputs manifest records the fork commit (2f88a82f and later) | open: a commit stamp through the build job (`jobbuild.rs`) is an app change, not tonight | From 3f8190115586079d46af282f58b5b288b9203dc8 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:57:26 +0000 Subject: [PATCH 085/158] plan: the aggregation-cost agent's card-alone rows (an empty shard 2 s, an aggregation 2 s, 4 proving-only cards for an empty chain) Co-Authored-By: Claude Fable 5.1 --- docs/plans/proving-v1.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/plans/proving-v1.md b/docs/plans/proving-v1.md index ecaa10ba..06319a34 100644 --- a/docs/plans/proving-v1.md +++ b/docs/plans/proving-v1.md @@ -131,5 +131,7 @@ The GPU server of SP1 6.8.1 sets the memory, not the shard: a floor of 13.9 GB f The three profile numbers the coordinator asked for, as measured: under 9.0 GB does not exist on this build (floor 13.9); under 15.0 GB mine-and-prove does not exist for any full shard (the v1 shard alone is 20.4); the full profile is the 32 GB card. The fleet table's "proving-only" rows therefore read 24 GB cards at the v1 budget and 32 GB cards at the prototype budget. Shards per block at the v1 budget: 1 on tonight's empty chain, 2 to 4 on blocks with transactions (`B_p` 120,000 = 4 x `S_p`); the aggregation count is one per block whatever the shard count (the chained recursion), so the aggregation-cost agent's target is per block. +The aggregation-cost agent's first rows (branch agg-cost, 5 October 2026 night, the same four live blocks on PC 2): with the miners paused an empty shard proves in 1.9 to 2.2 s and an aggregation in 1.7 to 2.2 s with the card 15.8% busy; mining, 7.4 to 7.8 s and 7.9 to 9.8 s at 93.9% busy, so the miner's kernels take the card and the prover runs 3.6x (shards) to 4.5x (aggregations) slower beside them; its batch-size curve is still open. That puts a proving-only card at about 4 s per empty block (one shard and one aggregation), 4 cards for an empty-block chain at 1 block/s, against 18 mining cards. + The re-plans of block 344 at 2.25 M and 4.5 M pgas peak at 28.3 to 28.4 GB alone (the server's buffers step up between 4.7 M and 20 M cycles and are flat to 60 M), so no shard size between the v1 budget and the prototype one changes a tier; with the miner the adopted shard proves 3.1x slower (13.2 s against 4.2 s) and the chained aggregation 9.7 s against 2.5 s: a mining 24 GB card delivers one adopted-size shard plus one aggregation in about 23 s, inside T by 25x. From 39ecd7c52bdef5e88d0462064c310250526ee199 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:43:02 +0000 Subject: [PATCH 086/158] hot table (Counter ASIC 2.0 layer 5), measured and not adopted, on the ca2-v3 composed class (squash of tag ca2-cache-history-2026-10-05) LoadClass::hot (Option) beside mix, slots, scratch, mixer_mult, growth and era; V3_CLASS = { era: None, hot: None, ..LoadClass::MX4 } (hot stays None: the hot code is behind the flag, measured and not adopted). Op::Hot, the hot slots drawn after the scratch slots, no width roll (v2_loads allows the added form's extra slots), the id suffix hot/[added], the hot parsing inside parse_loads, the era branch first in name(). HotTable under seed_words("igneum-hot/" || epoch seed) with the cache chain and tag umHT, read at H[mulhi(src, HOT_WORDS)]; DatasetSource::hot attached by new_class_day and from_seed_bytes_class; the acceptance stand-in dataset_elem(idx, S[2], S[3]); the three emitters (hot argument after the init words, ht_segment and igneum_hot_fill beside the layout-aware cores); packfile.h hot fields beside class, era, attempt and mixerMult; OpenCL host, Metal packbench and NVRTC worker fill H on the device and self-test it. Eight packs under proto-cuda/packs-ca2-hot (replaced hot32k4 hot64k4 hot96k4 hot64k2 hot64k8, added hot32k4a hot64k4a hot96k4a) re-exported on the merged crate: vectors unchanged, program.h and program.json carry the mixer fields. docs/plans/hot-table.md (design, spec text, per-tier budget, chip model, Mac and PC measurements, the decision: layer 5 out of v3, the 3.0 note); bench-log entry and addenda with job ids and worker sha256s; the two PC playbooks. Checks on this commit: cargo test --release 53 + 19 pass (the pinned v2, mx4, era, readwidth and hot packs); the pinned packs under proto-cuda/packs, packs-ca2-mixer, packs-ca2-era and packs-readwidth untouched; Metal packbench and Apple OpenCL --bench-pack on all eight hot packs (run lock, 2^20 at base 0): 96/96 lanes, hot table head, last line and FNV PASS, one fingerprint per pack on both harnesses, equal to the fingerprints before the rebase (hot32k4 679e5e83378d3790, hot64k4 d4c9e456b039fdef, hot96k4 7c98eceffee9fd73, hot64k2 f43b10a95879b8e5, hot64k8 c11309d743be9392, hot32k4a afb700b2d997c847, hot64k4a ba214baa9c1a9e85, hot96k4a 29e1916aed6deff5). No era or mixer behaviour changed: every resolution kept the ca2-v3 side and appended the hot branch. Co-Authored-By: Claude Fable 5.1 --- docs/bench-log.md | 74 ++++ docs/plans/hot-table.md | 266 ++++++++++++ igneum-pow/src/accept.rs | 67 ++- igneum-pow/src/emit.rs | 248 +++++++++-- igneum-pow/src/generator.rs | 264 +++++++++++- igneum-pow/src/main.rs | 7 + igneum-pow/src/memhard.rs | 131 +++++- igneum-pow/src/verify.rs | 71 +++- igneum-pow/tests/packs.rs | 203 ++++++++- proto-cuda/nvrtc/packfile.h | 35 +- proto-cuda/nvrtc/worker.cpp | 54 ++- proto-cuda/packs-ca2-hot/hot32k4/kernel.cl | 305 +++++++++++++ proto-cuda/packs-ca2-hot/hot32k4/kernel.cu | 179 ++++++++ .../packs-ca2-hot/hot32k4/kernel_bound.cl | 399 ++++++++++++++++++ .../packs-ca2-hot/hot32k4/kernel_bound.cu | 125 ++++++ proto-cuda/packs-ca2-hot/hot32k4/memhard.h | 129 ++++++ .../packs-ca2-hot/hot32k4/memhard.metal | 131 ++++++ proto-cuda/packs-ca2-hot/hot32k4/program.h | 70 +++ proto-cuda/packs-ca2-hot/hot32k4/program.json | 129 ++++++ .../packs-ca2-hot/hot32k4/program.metal | 112 +++++ .../packs-ca2-hot/hot32k4/program_bound.metal | 114 +++++ proto-cuda/packs-ca2-hot/hot32k4/vectors.h | 67 +++ proto-cuda/packs-ca2-hot/hot32k4/vectors.json | 39 ++ proto-cuda/packs-ca2-hot/hot32k4a/kernel.cl | 305 +++++++++++++ proto-cuda/packs-ca2-hot/hot32k4a/kernel.cu | 179 ++++++++ .../packs-ca2-hot/hot32k4a/kernel_bound.cl | 399 ++++++++++++++++++ .../packs-ca2-hot/hot32k4a/kernel_bound.cu | 125 ++++++ proto-cuda/packs-ca2-hot/hot32k4a/memhard.h | 129 ++++++ .../packs-ca2-hot/hot32k4a/memhard.metal | 131 ++++++ proto-cuda/packs-ca2-hot/hot32k4a/program.h | 70 +++ .../packs-ca2-hot/hot32k4a/program.json | 129 ++++++ .../packs-ca2-hot/hot32k4a/program.metal | 112 +++++ .../hot32k4a/program_bound.metal | 114 +++++ proto-cuda/packs-ca2-hot/hot32k4a/vectors.h | 67 +++ .../packs-ca2-hot/hot32k4a/vectors.json | 39 ++ proto-cuda/packs-ca2-hot/hot64k2/kernel.cl | 305 +++++++++++++ proto-cuda/packs-ca2-hot/hot64k2/kernel.cu | 179 ++++++++ .../packs-ca2-hot/hot64k2/kernel_bound.cl | 399 ++++++++++++++++++ .../packs-ca2-hot/hot64k2/kernel_bound.cu | 125 ++++++ proto-cuda/packs-ca2-hot/hot64k2/memhard.h | 129 ++++++ .../packs-ca2-hot/hot64k2/memhard.metal | 131 ++++++ proto-cuda/packs-ca2-hot/hot64k2/program.h | 70 +++ proto-cuda/packs-ca2-hot/hot64k2/program.json | 129 ++++++ .../packs-ca2-hot/hot64k2/program.metal | 112 +++++ .../packs-ca2-hot/hot64k2/program_bound.metal | 114 +++++ proto-cuda/packs-ca2-hot/hot64k2/vectors.h | 67 +++ proto-cuda/packs-ca2-hot/hot64k2/vectors.json | 39 ++ proto-cuda/packs-ca2-hot/hot64k4/kernel.cl | 305 +++++++++++++ proto-cuda/packs-ca2-hot/hot64k4/kernel.cu | 179 ++++++++ .../packs-ca2-hot/hot64k4/kernel_bound.cl | 399 ++++++++++++++++++ .../packs-ca2-hot/hot64k4/kernel_bound.cu | 125 ++++++ proto-cuda/packs-ca2-hot/hot64k4/memhard.h | 129 ++++++ .../packs-ca2-hot/hot64k4/memhard.metal | 131 ++++++ proto-cuda/packs-ca2-hot/hot64k4/program.h | 70 +++ proto-cuda/packs-ca2-hot/hot64k4/program.json | 129 ++++++ .../packs-ca2-hot/hot64k4/program.metal | 112 +++++ .../packs-ca2-hot/hot64k4/program_bound.metal | 114 +++++ proto-cuda/packs-ca2-hot/hot64k4/vectors.h | 67 +++ proto-cuda/packs-ca2-hot/hot64k4/vectors.json | 39 ++ proto-cuda/packs-ca2-hot/hot64k4a/kernel.cl | 305 +++++++++++++ proto-cuda/packs-ca2-hot/hot64k4a/kernel.cu | 179 ++++++++ .../packs-ca2-hot/hot64k4a/kernel_bound.cl | 399 ++++++++++++++++++ .../packs-ca2-hot/hot64k4a/kernel_bound.cu | 125 ++++++ proto-cuda/packs-ca2-hot/hot64k4a/memhard.h | 129 ++++++ .../packs-ca2-hot/hot64k4a/memhard.metal | 131 ++++++ proto-cuda/packs-ca2-hot/hot64k4a/program.h | 70 +++ .../packs-ca2-hot/hot64k4a/program.json | 129 ++++++ .../packs-ca2-hot/hot64k4a/program.metal | 112 +++++ .../hot64k4a/program_bound.metal | 114 +++++ proto-cuda/packs-ca2-hot/hot64k4a/vectors.h | 67 +++ .../packs-ca2-hot/hot64k4a/vectors.json | 39 ++ proto-cuda/packs-ca2-hot/hot64k8/kernel.cl | 305 +++++++++++++ proto-cuda/packs-ca2-hot/hot64k8/kernel.cu | 179 ++++++++ .../packs-ca2-hot/hot64k8/kernel_bound.cl | 399 ++++++++++++++++++ .../packs-ca2-hot/hot64k8/kernel_bound.cu | 125 ++++++ proto-cuda/packs-ca2-hot/hot64k8/memhard.h | 129 ++++++ .../packs-ca2-hot/hot64k8/memhard.metal | 131 ++++++ proto-cuda/packs-ca2-hot/hot64k8/program.h | 70 +++ proto-cuda/packs-ca2-hot/hot64k8/program.json | 129 ++++++ .../packs-ca2-hot/hot64k8/program.metal | 112 +++++ .../packs-ca2-hot/hot64k8/program_bound.metal | 114 +++++ proto-cuda/packs-ca2-hot/hot64k8/vectors.h | 67 +++ proto-cuda/packs-ca2-hot/hot64k8/vectors.json | 39 ++ proto-cuda/packs-ca2-hot/hot96k4/kernel.cl | 305 +++++++++++++ proto-cuda/packs-ca2-hot/hot96k4/kernel.cu | 179 ++++++++ .../packs-ca2-hot/hot96k4/kernel_bound.cl | 399 ++++++++++++++++++ .../packs-ca2-hot/hot96k4/kernel_bound.cu | 125 ++++++ proto-cuda/packs-ca2-hot/hot96k4/memhard.h | 129 ++++++ .../packs-ca2-hot/hot96k4/memhard.metal | 131 ++++++ proto-cuda/packs-ca2-hot/hot96k4/program.h | 70 +++ proto-cuda/packs-ca2-hot/hot96k4/program.json | 129 ++++++ .../packs-ca2-hot/hot96k4/program.metal | 112 +++++ .../packs-ca2-hot/hot96k4/program_bound.metal | 114 +++++ proto-cuda/packs-ca2-hot/hot96k4/vectors.h | 67 +++ proto-cuda/packs-ca2-hot/hot96k4/vectors.json | 39 ++ proto-cuda/packs-ca2-hot/hot96k4a/kernel.cl | 305 +++++++++++++ proto-cuda/packs-ca2-hot/hot96k4a/kernel.cu | 179 ++++++++ .../packs-ca2-hot/hot96k4a/kernel_bound.cl | 399 ++++++++++++++++++ .../packs-ca2-hot/hot96k4a/kernel_bound.cu | 125 ++++++ proto-cuda/packs-ca2-hot/hot96k4a/memhard.h | 129 ++++++ .../packs-ca2-hot/hot96k4a/memhard.metal | 131 ++++++ proto-cuda/packs-ca2-hot/hot96k4a/program.h | 70 +++ .../packs-ca2-hot/hot96k4a/program.json | 129 ++++++ .../packs-ca2-hot/hot96k4a/program.metal | 112 +++++ .../hot96k4a/program_bound.metal | 114 +++++ proto-cuda/packs-ca2-hot/hot96k4a/vectors.h | 67 +++ .../packs-ca2-hot/hot96k4a/vectors.json | 39 ++ proto-metal/packbench.swift | 42 +- proto-opencl/host.c | 71 +++- relay/playbooks/ca2-hot-5090-bench.ps1 | 66 +++ relay/playbooks/ca2-hot-9070-bench.ps1 | 85 ++++ 111 files changed, 16006 insertions(+), 70 deletions(-) create mode 100644 docs/plans/hot-table.md create mode 100644 proto-cuda/packs-ca2-hot/hot32k4/kernel.cl create mode 100644 proto-cuda/packs-ca2-hot/hot32k4/kernel.cu create mode 100644 proto-cuda/packs-ca2-hot/hot32k4/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-hot/hot32k4/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-hot/hot32k4/memhard.h create mode 100644 proto-cuda/packs-ca2-hot/hot32k4/memhard.metal create mode 100644 proto-cuda/packs-ca2-hot/hot32k4/program.h create mode 100644 proto-cuda/packs-ca2-hot/hot32k4/program.json create mode 100644 proto-cuda/packs-ca2-hot/hot32k4/program.metal create mode 100644 proto-cuda/packs-ca2-hot/hot32k4/program_bound.metal create mode 100644 proto-cuda/packs-ca2-hot/hot32k4/vectors.h create mode 100644 proto-cuda/packs-ca2-hot/hot32k4/vectors.json create mode 100644 proto-cuda/packs-ca2-hot/hot32k4a/kernel.cl create mode 100644 proto-cuda/packs-ca2-hot/hot32k4a/kernel.cu create mode 100644 proto-cuda/packs-ca2-hot/hot32k4a/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-hot/hot32k4a/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-hot/hot32k4a/memhard.h create mode 100644 proto-cuda/packs-ca2-hot/hot32k4a/memhard.metal create mode 100644 proto-cuda/packs-ca2-hot/hot32k4a/program.h create mode 100644 proto-cuda/packs-ca2-hot/hot32k4a/program.json create mode 100644 proto-cuda/packs-ca2-hot/hot32k4a/program.metal create mode 100644 proto-cuda/packs-ca2-hot/hot32k4a/program_bound.metal create mode 100644 proto-cuda/packs-ca2-hot/hot32k4a/vectors.h create mode 100644 proto-cuda/packs-ca2-hot/hot32k4a/vectors.json create mode 100644 proto-cuda/packs-ca2-hot/hot64k2/kernel.cl create mode 100644 proto-cuda/packs-ca2-hot/hot64k2/kernel.cu create mode 100644 proto-cuda/packs-ca2-hot/hot64k2/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-hot/hot64k2/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-hot/hot64k2/memhard.h create mode 100644 proto-cuda/packs-ca2-hot/hot64k2/memhard.metal create mode 100644 proto-cuda/packs-ca2-hot/hot64k2/program.h create mode 100644 proto-cuda/packs-ca2-hot/hot64k2/program.json create mode 100644 proto-cuda/packs-ca2-hot/hot64k2/program.metal create mode 100644 proto-cuda/packs-ca2-hot/hot64k2/program_bound.metal create mode 100644 proto-cuda/packs-ca2-hot/hot64k2/vectors.h create mode 100644 proto-cuda/packs-ca2-hot/hot64k2/vectors.json create mode 100644 proto-cuda/packs-ca2-hot/hot64k4/kernel.cl create mode 100644 proto-cuda/packs-ca2-hot/hot64k4/kernel.cu create mode 100644 proto-cuda/packs-ca2-hot/hot64k4/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-hot/hot64k4/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-hot/hot64k4/memhard.h create mode 100644 proto-cuda/packs-ca2-hot/hot64k4/memhard.metal create mode 100644 proto-cuda/packs-ca2-hot/hot64k4/program.h create mode 100644 proto-cuda/packs-ca2-hot/hot64k4/program.json create mode 100644 proto-cuda/packs-ca2-hot/hot64k4/program.metal create mode 100644 proto-cuda/packs-ca2-hot/hot64k4/program_bound.metal create mode 100644 proto-cuda/packs-ca2-hot/hot64k4/vectors.h create mode 100644 proto-cuda/packs-ca2-hot/hot64k4/vectors.json create mode 100644 proto-cuda/packs-ca2-hot/hot64k4a/kernel.cl create mode 100644 proto-cuda/packs-ca2-hot/hot64k4a/kernel.cu create mode 100644 proto-cuda/packs-ca2-hot/hot64k4a/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-hot/hot64k4a/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-hot/hot64k4a/memhard.h create mode 100644 proto-cuda/packs-ca2-hot/hot64k4a/memhard.metal create mode 100644 proto-cuda/packs-ca2-hot/hot64k4a/program.h create mode 100644 proto-cuda/packs-ca2-hot/hot64k4a/program.json create mode 100644 proto-cuda/packs-ca2-hot/hot64k4a/program.metal create mode 100644 proto-cuda/packs-ca2-hot/hot64k4a/program_bound.metal create mode 100644 proto-cuda/packs-ca2-hot/hot64k4a/vectors.h create mode 100644 proto-cuda/packs-ca2-hot/hot64k4a/vectors.json create mode 100644 proto-cuda/packs-ca2-hot/hot64k8/kernel.cl create mode 100644 proto-cuda/packs-ca2-hot/hot64k8/kernel.cu create mode 100644 proto-cuda/packs-ca2-hot/hot64k8/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-hot/hot64k8/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-hot/hot64k8/memhard.h create mode 100644 proto-cuda/packs-ca2-hot/hot64k8/memhard.metal create mode 100644 proto-cuda/packs-ca2-hot/hot64k8/program.h create mode 100644 proto-cuda/packs-ca2-hot/hot64k8/program.json create mode 100644 proto-cuda/packs-ca2-hot/hot64k8/program.metal create mode 100644 proto-cuda/packs-ca2-hot/hot64k8/program_bound.metal create mode 100644 proto-cuda/packs-ca2-hot/hot64k8/vectors.h create mode 100644 proto-cuda/packs-ca2-hot/hot64k8/vectors.json create mode 100644 proto-cuda/packs-ca2-hot/hot96k4/kernel.cl create mode 100644 proto-cuda/packs-ca2-hot/hot96k4/kernel.cu create mode 100644 proto-cuda/packs-ca2-hot/hot96k4/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-hot/hot96k4/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-hot/hot96k4/memhard.h create mode 100644 proto-cuda/packs-ca2-hot/hot96k4/memhard.metal create mode 100644 proto-cuda/packs-ca2-hot/hot96k4/program.h create mode 100644 proto-cuda/packs-ca2-hot/hot96k4/program.json create mode 100644 proto-cuda/packs-ca2-hot/hot96k4/program.metal create mode 100644 proto-cuda/packs-ca2-hot/hot96k4/program_bound.metal create mode 100644 proto-cuda/packs-ca2-hot/hot96k4/vectors.h create mode 100644 proto-cuda/packs-ca2-hot/hot96k4/vectors.json create mode 100644 proto-cuda/packs-ca2-hot/hot96k4a/kernel.cl create mode 100644 proto-cuda/packs-ca2-hot/hot96k4a/kernel.cu create mode 100644 proto-cuda/packs-ca2-hot/hot96k4a/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-hot/hot96k4a/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-hot/hot96k4a/memhard.h create mode 100644 proto-cuda/packs-ca2-hot/hot96k4a/memhard.metal create mode 100644 proto-cuda/packs-ca2-hot/hot96k4a/program.h create mode 100644 proto-cuda/packs-ca2-hot/hot96k4a/program.json create mode 100644 proto-cuda/packs-ca2-hot/hot96k4a/program.metal create mode 100644 proto-cuda/packs-ca2-hot/hot96k4a/program_bound.metal create mode 100644 proto-cuda/packs-ca2-hot/hot96k4a/vectors.h create mode 100644 proto-cuda/packs-ca2-hot/hot96k4a/vectors.json create mode 100644 relay/playbooks/ca2-hot-5090-bench.ps1 create mode 100644 relay/playbooks/ca2-hot-9070-bench.ps1 diff --git a/docs/bench-log.md b/docs/bench-log.md index d9bb12a0..8a82a73b 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -1571,3 +1571,77 @@ The 9070 XT rows are 2,048 persistent warps (4,096 within 1 percent), arena 64 M **Readings.** (1) Same count, wider: the vendor gap does not move at 16 B (7.8x) because on the 9070 XT a 4-byte read already costs a 64-byte line and on the 5090 a 16-byte read costs one 32-byte sector, the same as 4 bytes: the memory systems do identical work, only the fold's input grows. At 64 B the gap closes to 4.1x, entirely by the 5090 losing half its rate (its share falls to 0.58 and its DRAM traffic reaches 589 GB/s, 37 percent of the stream: bandwidth, not latency, bounds it), while the 9070 XT and the M5 Max do not move. (2) Fewer, wider (w64x4): 3.7x, but every card runs 4x faster because the dependent chain is 32 loads long instead of 128; the 5090 sits at a 0.56 share (bandwidth), so a chip with more bandwidth per dollar than a GPU gains, which is the Ethash shape the design avoids. (3) The mix: the hour-to-hour spread is 7 to 22 percent of the median per card (the 5090 the widest, because its 64-byte loads are the expensive ones and their count per program runs 2 to 8 of 16); the programs with many 64-byte loads (mixA-3, mixA-5, mixB-2) are the slow hours on the 5090 and the fast ones nowhere. (4) The scratch: on the 5090 every RMW share costs 12 to 48 percent against the persistent control, the 32 KiB arena less than the 128 KiB one (the smaller arena, 64 MiB over 2,048 warps, sits inside the 96 MB L2); on the M5 Max the 32 KiB rows are FASTER than the control (+12 and +74 percent at 25 and 50 percent), because the arena (128 MiB over 4,096 warps) lives in the chip's caches and a scratch op is cheaper than a dataset read, so replacing dataset loads raises the rate: the scratch at these sizes is not memory work on Apple and is partly cached on NVIDIA. The chip row for these variants comes from the ca2-soundness branch; what this entry gives is the GPU cost and the share. (5) Latency-bound shares: v2 0.87 to 1.01 on the three cards, w16 0.84 to 1.03, w64 0.58 (5090) and 0.78 (9070 XT); the Mac's shares above 1 are an Apple OpenCL probe under load against a Metal rate. Jobs: `run-readwidth-5090-20261005` and `run-readwidth-9070-20261005` (probes; the packs refused for their string seeds, fixed in a9e002c), `run-readwidth-5090-20261005c`, `run-readwidth-9070-20261005c` (benches), `run-readwidth-9070-scratch-20261005d` (the scratch packs after the `__local` fix d0018cf, AMD's compiler requires the exchange buffer at the kernel's outermost scope); read back with `node tools/jobs.mjs --all`. Mac commands and logs: `docs/plans/read-width.md` section 3. The worker exes for the jobs: `proto-cuda/nvrtc/build-windows.sh` on this branch (mingw), sha256 of the CUDA one `6f46336f...defe1`. +## 5 October 2026 (night), the hot table on the M5 Max: a second table sized to GPU cache beside the 1 GiB dataset (Counter ASIC 2.0 layer 5) + +Branch `ca2-cache` (on readwidth 1ea7a52), `docs/plans/hot-table.md`. Apple M5 Max, measure lock held, the Mac's load average 14 to 27 throughout (other agents' CPU work; the lock serialises builds and measurements, not every process), so the ratios inside one session are the result and the absolute rates are not quiet numbers. Packs `proto-cuda/packs-ca2-hot/hot{32,64,96}k4`, `hot64k2`, `hot64k8` from `igneum-pow export --seed igneum-genesis --day 2026-10-03 --class hotk` (the version 2 genesis program with k of its 16 loads redirected to an S MiB table H keyed by `seed_words("igneum-hot/" || seed bytes)`, read at `H[mulhi(src, words)]`). + +**Probe** (`proto-opencl/igneum-bench-cl-igneum-genesis-mh --memprobe --probe-mib S`, Apple OpenCL, wall time, best of 3, 256 dependent steps per lane, work-group 256; the ceiling row is 4,194,304 lanes): + +| MiB | chase at 4,096 lanes | ns per dependent load | chase ceiling, G loads/s | indep x8 ceiling | stream | +|---|---|---|---|---|---| +| 32 | 3.51 G/s | 1,168 | 21.7 | 21.8 | 138.7 GB/s | +| 64 | 3.63 | 1,129 | 12.8 | 13.0 | 199.1 | +| 96 | 3.30 | 1,242 | 12.3 | 12.7 | 242.6 | +| 1024 | 2.22 | 1,844 | 3.50 | 3.50 | 521.5 | + +**Hash rate and bit-exactness** (Metal `proto-metal/packbench --pack --batches 5 --batch-log2 24`, GPU time; Apple OpenCL `--bench-pack --pack --batches 5 --batch-log2 24`, wall; both fill H on the device from the pack's `igneum_hot_fill` and check it; fingerprint = FNV-1a 64 over 2^24 outputs at base 0): + +| Pack | Metal Mhash/s | Apple OpenCL Mhash/s | fingerprint (equal on both) | vectors | hot table head, last line, FNV | g against v2 (Metal) | probe-predicted g | ideal g | +|---|---|---|---|---|---|---|---|---| +| igneum-genesis-mh (v2) | 27.68 | 27.61 | 25f96e7dce90bd4e | 96/96 both | none | 1 | 1 | 1 | +| hot32k4 | 33.90 | 33.92 | d2e6cf3b61d0b9fe | 96/96 both | PASS both | 1.22 | 1.27 | 1.33 | +| hot64k4 | 30.93 | 30.89 | e4c5263ac650cc0d | 96/96 both | PASS both | 1.12 | 1.22 | 1.33 | +| hot96k4 | 29.06 | 28.97 | 5d63439b6e394521 | 96/96 both | PASS both | 1.05 | 1.22 | 1.33 | +| hot64k2 | 27.67 | 27.27 | 352633bdbbb0d2b6 | 96/96 both | PASS both | 1.00 | 1.10 | 1.14 | +| hot64k8 | 47.42 | 46.73 | da54630d7dfaaf85 | 96/96 both | PASS both | 1.71 | 1.57 | 2.0 | + +Hot table fill, Metal GPU time: 0.07 ms (32 MiB), 0.15 (64), 0.22 (96). Hot table FNV-1a 64 of the genesis epoch: c1767ba3ef02719f (32 MiB), 77ca4b9527104530 (64), 79bcf436c4e5bc47 (96); cache 48c4f5bf24166b2e unchanged. + +**CPU verifier** (`igneum-pow bench --seed igneum-genesis --day 2026-10-03 --class --warps 50`, one core, release): + +| Class | hot fill, one core | items per warp | ms per warp | +|---|---|---|---| +| v2 | none | 4,096 | 0.626 | +| hot32k4 | 24.0 ms | 3,072 | 0.489 | +| hot64k4 | 46.4 ms | 3,072 | 0.504 | +| hot96k4 | 73.0 ms | 3,072 | 0.488 | +| hot64k2 | 45.5 ms | 3,584 | 0.560 | +| hot64k8 | 47.7 ms | 2,048 | 0.344 | + +Reading: bit-exact across Metal, Apple OpenCL and the Rust reference on every hot pack, hot table included. On this card the 32 MiB table delivers 92% of the probe's predicted gain with the dataset streaming beside it, 64 MiB about half, 96 MiB a quarter; k = 8 at 64 MiB gives 1.71x against an ideal 2.0x. The verifier gets cheaper with k (a hot load is one table read, a dataset load is an item derivation) and pays 24 to 73 ms per epoch for the fill. Chip model with these g in the plan, section 6.4. The RTX 5090 and RX 9070 XT rows are a prepared PC job (`relay/playbooks/ca2-hot-{5090,9070}-bench.ps1`, zip `~/Desktop/igneum-ca2-hot.zip`), not run. + +Crate: `cargo test --release` 52 pass (39 unit, 13 pack tests: the four pinned v2 packs byte-identical, the five hot packs pinned with their load-form count: exactly 16 - k masked dataset loads and k hot loads per hash kernel). + +**Addendum, the added form** (coordinator's form of 5 October 2026: 16 + k load slots, the k hot ones drawn among them, the 16 dataset loads and the 4,096-item verifier bound unchanged; packs `hot32k4a`, `hot64k4a`, `hot96k4a`; second Mac session 21:03 to 21:19 UTC, load average 7 to 14; same harnesses and commands, branch `ca2-cache` on ca2-v3 464d6e1, the hosts rebuilt on the merged packfile.h): + +| Pack | Metal Mhash/s | Apple OpenCL Mhash/s | fingerprint (equal on both) | vectors | hot table | g against v2 (Metal, v2 27.63 in this session) | probe-predicted g | CPU verify ms/warp (v2 0.602) | hot fill, one core | +|---|---|---|---|---|---|---|---|---|---| +| hot32k4a | 25.76 | 25.72 | 8a3414735db4523c | 96/96 both | PASS both | 0.93 | 0.96 | 0.631 | 21.7 ms | +| hot64k4a | 23.92 | 23.87 | 45668f34105f6307 | 96/96 both | PASS both | 0.87 | 0.94 | 0.609 | 43.3 ms | +| hot96k4a | 22.92 | 22.88 | af763997dfee4c82 | 96/96 both | PASS both | 0.83 | 0.93 | 0.614 | 64.9 ms | + +Reading: the added form costs this card 7, 13 and 17% of its rate at 32, 64 and 96 MiB for four extra loads per iteration, more than the probe predicts as the table grows; the verifier is unchanged (4,096 items, plus 32 table reads) and pays the fill per epoch. Chip arithmetic in the plan, section 6.4. All eight packs load and self-test through the rebuilt OpenCL host (the Windows exe's host.c) on the Mac. + +**Addendum, the PCs** (5 October 2026, 21:29 to 21:35 UTC, PC 1 ae432dc7, app 0.3.9 before and after; fetch `fetch-ca2-hot-20261005` (zip sha256 bd49faa1c9d48024f49c615481faff5c68a4c09f0889dbaf009c208674d67b3f), jobs `run-ca2-hot-5090-20261005` (126 s) and `run-ca2-hot-9070-20261005` (247 s), both exit 0, the card under test switched off in the app through `api/cards` and restored; workers `igneum-worker-cuda.exe` sha256 956c4ab34f42cbcd1d2c1c6fb1a58fd9b3a8c70166df771cafcd0296ca6a27d4 and `igneum-worker-opencl.exe` sha256 32d3d34390aad70485c3524424c354223387137d383b5c5daf01f40073c12703, built from ca2-cache 196db96 on ca2-v3's merged packfile.h d2cd6e1; read back with `node tools/jobs.mjs --all`): + +Probe (`--memprobe --probe-mib S`, dependent 4 B chase ceiling at 4,194,304 lanes, G loads/s; ns per dependent load at 4,096 lanes in brackets): + +| Card | 32 MiB | 64 | 96 | 1024 | stream at 1024 MiB | +|---|---|---|---|---|---| +| RTX 5090 (CUDA, wall) | 112.6 (320) | 112.6 (340) | 112.6 (336) | 17.6 (610) | 1,563 GB/s | +| RX 9070 XT (OpenCL, event) | 9.88 (396) | 9.47 (457) | 8.18 (454) | 2.43 (1,579) | 633 GB/s | + +Rates (5 dispatches of 2^24 after a warm-up; 5090 `--bench --block-warps 1`, 9070 XT `--bench-pack --device 1` work-group 256; every row check=PASS with the Mac's fingerprint; v2 references from the readwidth entry, same night, same workers: 136.1 and 18.15 MH/s): + +| Pack | 5090 MH/s | g | 9070 XT MH/s | g | ideal g | +|---|---|---|---|---|---| +| hot32k4 | 146.6 | 1.08 | 19.79 | 1.09 | 1.33 | +| hot64k4 | 140.8 | 1.03 | 18.73 | 1.03 | 1.33 | +| hot96k4 | 138.5 | 1.02 | 18.33 | 1.01 | 1.33 | +| hot64k2 | 137.5 | 1.01 | 18.17 | 1.00 | 1.14 | +| hot64k8 | 163.6 | 1.20 | 22.32 | 1.23 | 2.0 | +| hot32k4a | 118.7 | 0.87 | 15.27 | 0.84 | 1 | +| hot64k4a | 115.4 | 0.85 | 14.62 | 0.81 | 1 | +| hot96k4a | 114.4 | 0.84 | 14.56 | 0.80 | 1 | + +Reading: the probe promises a full hit rate on the 5090 (every S inside the 96 MiB L2 at one ceiling, 6.4x DRAM) and the hash gets 2 to 8% at k = 4 and 20% at k = 8; the 9070 XT the same shape. The dataset's random lines evict the table from the shared cache on every card. The added form costs 13 to 20% of the rate. Recommendation in `docs/plans/hot-table.md` section 6.4: do not adopt layer 5 in either form on these measurements. diff --git a/docs/plans/hot-table.md b/docs/plans/hot-table.md new file mode 100644 index 00000000..426ca7aa --- /dev/null +++ b/docs/plans/hot-table.md @@ -0,0 +1,266 @@ +# Hot table: a second table sized to GPU cache, read beside the 1 GiB dataset + +Counter ASIC 2.0, layer 5 (`docs/plans/counter-asic-2.md`). Experiment branch `ca2-cache`, 5 October 2026 (night), on top of the read-width branch (`readwidth` b970dda: `LoadClass`, `verify::fold_words`, the scratch variant, `proto-metal/packbench.swift`, `proto-opencl/host.c --bench-pack`). Nothing here is the lottery hash: every hot class sits behind the generator flag and the default v2 path is byte-identical (the pinned packs `igneum-genesis-mh` and `igneum-devnet-v4-epoch0` are diffed by `igneum-pow/tests/packs.rs`). + +## 1. The idea + +The honest hash is 128 dependent random 4-byte reads over 1 GiB (spec 01 section 1.5). A chip that wants a gain on that must beat a GPU at DRAM random reads, which is the same physics for both (the plan's last section). What a chip can do that a GPU cannot is choose its memory: a chip can mirror read-only data into SRAM and serve it at SRAM latency, if the data fits. + +The hot table turns that around. A second table `H` of `S` MiB (32, 64 or 96 in this experiment) is derived from the epoch seed and read by `k` of the 16 load slots (2, 4 or 8). `S` is chosen to fit the caches of the cards that mine: 96 MiB L2 on the RTX 5090, 64 MB Infinity Cache plus 8 MB L2 on the RX 9070 XT, the system level cache on the M5 Max (figures approximate, from memory; the probe of section 6 measures what each card does at each size). A GPU gets the hot loads as cache hits for free. A chip must carry `S` MiB of SRAM for the same hits, beside the DRAM path it still needs for the other `16 - k` slots, or serve `H` from DRAM and fall behind the GPU by the hot share. Either way the chip pays for something the GPU already has. + +What this does not change: the cold loads (the 1 GiB dataset) stay a dependent chain at DRAM latency, so the latency-bound property holds for them; the hot loads are interleaved in the same chain (every load's address is a fresh register of the same iteration, G2), so a hot hit shortens the chain by one DRAM latency and nothing else. + +## 2. The specification text (proposed; prototype values, to be fixed at gate 1) + +### 2.1 Hot key and fill + +For an epoch whose program seed bytes are `e` (spec 01 section 1.12: the UTF-8 of a seed string in the packs, the 32-byte VDF output on the chain; the bytes before the attempt suffix of 1.4.6, so every attempt of one epoch shares one table): + +``` +KH = seed_words_from_bytes("igneum-hot/" || e) 8 words +``` + +`H` has `N_H = S x 2^18` words (`S` MiB) in `N_seg = S x 256` segments of 64 chained lines of 16 words, filled exactly as the cache of section 1.8.3 with `KH` in place of `K` and the tag `("Igne", "umHT") = (0x49676e65, 0x756d4854)` in place of `("Igne", "umMH")`: + +``` +prev = 0^16 +for j in 0..63: + in = prev XOR (sigma[0..3] || KH[0..7] || s || j || tag[0..1]) + line = B(in) ChaCha12 with feed-forward, section 1.8.2 + H[segment s, line j] = line + prev = line +``` + +One GPU thread per segment, as the cache fill. The fill is a once-per-epoch cost: `S / 256` of the 256 MiB cache fill (section 1.8.3 table: 0.6 to 2.1 ms on the M5 Max GPU, 0.67 ms on the RTX 5090, 175 to 190 ms on one CPU core for 256 MiB), so under 1 ms on a GPU and 22 to 71 ms on one core, measured below. + +### 2.2 The hot load + +A hot load slot reads `H` instead of the dataset with the same fold (width 1: a plain XOR): + +``` +hot: dst = dst XOR H[mulhi(src, N_H)] +``` + +`mulhi(a, b)` is the high 32 bits of the 64-bit product (the `mulhi` family of section 1.4.1, bit-exact on Metal, CUDA and OpenCL). The index lies in `[0, N_H)` for any `N_H`, which is what lets `S = 96` exist: 96 MiB is not a power of two, so `src AND MASK` cannot address it. This is the multiply-shift range reduction section 1.13.3 proposes for the growing dataset, so the hot table is also its first measured use. For `S = 32` and `64` the mapping takes the top 23 or 24 bits of `src` where the dataset load takes the low 28; a fresh source is uniform, so neither choice costs uniformity (section 6, hot-load uniformity test). + +The emitted text has one form per dialect, checkable by text search as the mask check of section 1.14 item 2: `hot[mulhi(rN, HOT_WORDS)]` (Metal), `hot[__umulhi(rN, HOT_WORDS)]` (CUDA), `hot[mul_hi(rN, HOT_WORDS)]` (OpenCL), with `HOT_WORDS` a literal of the pack. A hot pack's hash kernels carry exactly `16 - k` masked dataset loads and exactly `k` hot loads (`igneum-pow/tests/packs.rs`). + +### 2.3 Which slots are hot + +The 16 load slots are drawn first by the partial Fisher-Yates of section 1.4.3, which emits them in a uniformly random order. The first `k` slots in that draw order are the hot slots. Drawn, not fixed, because a fixed pattern (every fourth load, say) would let a pipeline schedule its SRAM reads statically for every hour; drawn costs no extra draw, so a `hot(S, k)` program takes the version 2 stream exactly and is the version 2 program with `k` of its loads redirected (the width roll of the read-width classes is not taken: `LoadClass::takes_width_roll`). A hot slot keeps every rule of a load: the fresh-source draw (G2), the injecting-write count (acceptance (b)), the lane-constant test and the distinct-address count (acceptance (c)); its address is tagged apart from dataset addresses in the count so a hot word and a dataset word at the same index are two addresses. + +The class composes with the read-width fields of `LoadClass` (width mix, scratch `k` and `kb`): the hot slots are taken from the drawn slots after the scratch slots, so a class may carry a width mix, a scratch and a hot table at once. The experiment packs below use the version 2 widths and no scratch. + +Two forms (coordinator's decision, 5 October 2026, after the first Mac measurement): + +| Form | Load slots drawn | Dataset loads per hash | Hot loads per hash | Items per warp (verifier bound) | Class name | What a chip pays | +|---|---|---|---|---|---|---| +| replaced (`hot(S, k)`) | 16, the first `k` in draw order hot | `(16 - k) x 8` | `8k` | `(16 - k) x 256` | `hot64k4` | the on-die-cache recompute chip of `docs/analysis/scratch-soundness.md` 3.4 (the 256 MiB cache in SRAM, items derived on the fly, 333 MH/s at 50 T op/s, 2.4x the 5090) GAINS: a hot load replaces an item derivation (1,170 ops) with an SRAM read, so at `k = 4` its rate rises 1.33x against the GPU's measured 1.05 to 1.22x | +| added (`hot(S, k, added)`) | `16 + k`, the first `k` in draw order hot | `16 x 8 = 128` | `8k` | 4,096, unchanged | `hot64k4a` | `S` MiB of SRAM and `k` reads per iteration for nothing: the 16 item derivations stay; the GPU pays `k` cache hits | + +The added form is the one that taxes the named chip; the replaced form stays as the measured record (section 6). In the added form the slot draw is a partial Fisher-Yates of `16 + k` slots over 1..63, so the program stream differs from version 2 (another slot count), and the width roll is still not taken (`takes_width_roll`: the slot count is 16 plus the class's own added hot slots). `loads_per_hash` is `128 + 8k`; the acceptance rule's distinct-address bound scales with it as for the read-width classes. + +Program id: `FNV-1a 64 over "igneum-program-rw/" || generator || seed words || attempt || mix || load_slots || "hot/" || S || k [|| "added"]` (the read-width id with the hot fields appended), so no hot pack can be mistaken for a version 2 one, for another hot class or for the other form. + +### 2.4 Acceptance (section 1.4.6) + +The dynamic test stays a pure function of the program. A hot load reads `dataset_elem(mulhi(src, N_H), SW[2], SW[3])` (the six-operation closed form keyed by seed words 2 and 3, where the dataset stand-in is keyed by words 0 and 1), so the two stand-ins are two tables without any cache or day. Every other test is unchanged; the distinct-address bound is the read-width rule's (dataset and hot loads counted, scratch read-modify-writes not). + +### 2.5 The verifier (section 1.11) + +A verifier holds, per day, the mixer parameters and the 256 MiB cache, and, per epoch, `H` (`S` MiB, filled on one core in the times of section 6). A hot load is one table read per lane; a dataset load is the item derivation of section 1.11 as before. The verifier never holds the dataset. Per epoch the verifier's memory is `256 MiB + S MiB`. + +## 3. Memory budget (the project lead's cap, 5 October 2026: the working set on a card stays under 6 GB on an 8 GB card) + +Decided by the coordinator on 5 October 2026 after the card-lifetime review (`docs/analysis/card-lifetime-2026-10-05.md`, branch `card-lifetime` 1fecfe2, merged into `ca2-coord`): the GPU frees the 256 MiB cache after the daily dataset build (the hash never reads it; the rebuild costs 0.67 ms of fill and 13.4 ms of build on the 5090, bench-log 3 October 2026), so the cache is not resident and the working set is dataset + hot table + scratch (0 in v3) + buffers. The table below is that review's per-tier table with the hot table at its largest (96 MiB), buffers 128 MiB (the harnesses' 2^24-nonce output), resident warps = SMs x 48 on NVIDIA Ampere and later (SM counts approximate, from memory; the GTX 1650 is Turing at 32 warps per SM), 2,048 launched warps on Apple (the Metal harness). The scratch columns are the read-width variant's 32 and 128 KiB per resident warp, kept for the record; v3 carries no scratch. + +| Tier | Card assumed (SMs, approximate) | Resident warps | Scratch at 32 KiB | Scratch at 128 KiB | Hot table | Dataset at genesis | Buffers | Total, no scratch | Total at 32 KiB | Total at 128 KiB | Years the dataset leaves under mapping (b), cache freed | +|---|---|---|---|---|---|---|---|---|---|---|---| +| 4 GB | GTX 1650 (14 SMs x 32) | 448 | 14 MiB | 56 MiB | 96 MiB | 2,048 MiB | 128 MiB | 2,272 MiB | 2,286 MiB | 2,328 MiB | to year 4 (the 4 GiB step) | +| 8 GB | RTX 3050 (20) | 960 | 30 | 120 | 96 | 2,048 | 128 | 2,272 | 2,302 | 2,392 | to year 12 (the 8 GiB step) | +| 12 GB | RTX 3060 (28) | 1,344 | 42 | 168 | 96 | 2,048 | 128 | 2,272 | 2,314 | 2,440 | to year 28 (the 16 GiB step; year 12 if the cache were resident) | +| 16 GB | RTX 5060 Ti (36) | 1,728 | 54 | 216 | 96 | 2,048 | 128 | 2,272 | 2,326 | 2,488 | to year 28 | +| 24 GB | RTX 4090 (128) | 6,144 | 192 | 768 | 96 | 2,048 | 128 | 2,272 | 2,464 | 3,040 | to year 60 (the 32 GiB step) | +| 32 GB | RTX 5090 (170) | 8,160 | 255 | 1,020 | 96 | 2,048 | 128 | 2,272 | 2,527 | 3,292 | to year 60 | +| Apple 8 to 64 GB | M-series, 2,048 launched | 2,048 | 64 | 256 | 96 | 2,048 | 128 | 2,272 | 2,336 | 2,528 | 8 GB to year 4, 16 GB to year 12, 32 GB to year 28, 64 GB to year 60 (50% of unified memory usable, the review's assumption) | + +The prototype packs here use the 1 GiB dataset of spec 1.5 (1,024 MiB less in every total). Every total is under 6 GB with the hot table at its largest, so `S` is not what the cap binds: the dataset's growth is, and the hot table takes 96 MiB of the room at every tier (about 2 months of the 0.5 GiB-a-year schedule). The verifier's memory is `256 MiB + S MiB` (section 2.5); the GPU's is the table above. + +## 4. The chip model with the SRAM it would need + +Figures: SRAM area per bit from `docs/analysis/m16-recompute-attacker-2026-10-05.md` section 3 (256 MiB in about 100 to 300 mm^2 at a current node: the low end from a 0.02 um^2 bit cell with array overhead, the high end from wafer-scale parts at about 1 MB per mm^2; approximate, from memory) and from `docs/plans/counter-asic-2.md` (256 MB in about 45 mm^2 at a leading node, approximate). That is 0.18, 0.39 and 1.17 mm^2 per MiB. Die areas: a 750 mm^2 GPU-class die (the M16 model's equal-silicon comparison) and a 100 mm^2 memory-chip die (an assumption for a latency-bound chip whose die holds memory controllers and little else; labelled as such). + +| S (MiB) | SRAM at 0.18 mm^2/MiB | at 0.39 | at 1.17 | Share of a 100 mm^2 die (0.39) | Share of a 750 mm^2 die (0.39) | +|---|---|---|---|---|---| +| 32 | 6 mm^2 | 12 mm^2 | 37 mm^2 | 11% | 1.6% | +| 64 | 12 mm^2 | 25 mm^2 | 75 mm^2 | 20% | 3.2% | +| 96 | 17 mm^2 | 37 mm^2 | 112 mm^2 | 27% | 4.8% | + +The gain arithmetic. Let `G0` be a chip's gain over a GPU on the version 2 hash (the plan's public claim: under 2x; the plan's last section says the bound is DRAM latency, the same physics on both). Let `g` be the GPU's own measured speed-up from the hot class over version 2 on the same card (section 6: `hash rate hot(S, k) / hash rate v2`). The ideal `g` with every hot load a hit at zero cost is `16 / (16 - k)`: 1.14x at `k = 2`, 1.33x at `k = 4`, 2.0x at `k = 8`; the measured `g` says what share of that a real cache delivers while the 1 GiB dataset streams through the same cache. + +| Chip | Hot loads served from | Gain after the hot table | Arithmetic | +|---|---|---|---| +| A, no SRAM for H | DRAM | `G0 / g` | the chip's rate is what it was; the honest GPU gained `g` | +| B, S MiB of SRAM for H | SRAM | `G0 x A_die / (A_die + A_S)` per unit of silicon | the chip regains `g` and pays `A_S` on top of its die | +| B on a 100 mm^2 die, S = 64, 0.39 mm^2/MiB | SRAM | `0.80 x G0` | 100 / 125 | +| B on a 750 mm^2 die, S = 96, 0.39 mm^2/MiB | SRAM | `0.95 x G0` | 750 / 787 | + +What the big-table loads still cost the chip: `(16 - k) x 8` dependent DRAM reads per hash at the card's loaded latency (the 9070 XT entry's probe: 451 ns on the 5090 at 4,096 lanes, 276 ns unloaded on the 9070 XT, 1,949 ns on the M5 Max at 4,096 lanes; section 6 repeats the probe here). At `k = 4` that is 96 reads per hash; to match one RTX 5090 at its honest 229 Mhash/s (the M16 model's reference) a chip must keep `229 M x 96 x 300 ns = about 6,600` DRAM reads in flight at a 300 ns latency, and 11,000 at 500 ns, whatever its arithmetic. That queue depth is a memory-controller property, which is the latency-bound argument of the plan restated for the cold share. + +What the hot table does to the recompute attacker of M16: nothing good. `H` is a plain ChaCha12 chain, so a word of it can be recomputed from `KH` at `j + 1` block evaluations (32.5 on average, about 40,000 integer operations per hot load, approximate, against 1,170 per dataset item), which is 35x the cost of recomputing a dataset word. A chip stores `H` or reads it from DRAM; it does not recompute it. + +Reading, before the measurements: the hot table costs a chip `A_S` of die area or `g` of rate. Which one binds depends on the measured `g`, which is why the measurement comes first. If a GPU's cache delivers most of the ideal `g` with the dataset streaming beside it, `k = 8` at `S = 64` doubles the honest rate and halves chip A. If the cache delivers little, the hot table is a cost to the verifier (`S` MiB per epoch) with no gain, and the layer is dropped. + +## 5. Implementation (branch `ca2-cache`) + +| Where | What | +|---|---| +| `igneum-pow/src/generator.rs` | `LoadClass { hot: Option }` beside `mix`, `load_slots`, `scratch`, `scratch_kb`; `HotClass { mb, k }`; names `hot32k4`, `hot64k2`; `Op::Hot`; the first `k` drawn load slots after the scratch slots are hot; no width roll for a hot class with version 2 widths (`takes_width_roll`); the program id carries `hot/S/k` | +| `igneum-pow/src/memhard.rs` | `HotTable` (key, S, words), `hot_key(seed_bytes)`, `hot_words(mb)`, `hot_segments(mb)`, `hot_index(src, words)`, the tagged segment fill shared with the cache, `HOT_TAG` | +| `igneum-pow/src/verify.rs` | `DatasetSource::hot: Option`; `Op::Hot` in `step`; `Epoch::new_class` and `from_seed_bytes_class` fill `H` from the program's seed bytes when the class has a hot table | +| `igneum-pow/src/accept.rs` | `Op::Hot` with the closed-form stand-in of 2.4 | +| `igneum-pow/src/emit.rs` | the hot load statement in the three dialects; `hot` as the buffer after the init words (Metal buffer 3, or 4 when bound; CUDA and OpenCL argument after `mask`, or after the init words when bound) and before the scratch triple; `HOT_WORDS` literal; `ht_cache_segment` and `igneum_hot_fill` kernels in memhard.h, kernel.cu, kernel.cl and memhard.metal; `program.h` `IGNEUM_HOT_MB`, `IGNEUM_HOT_WORDS`, `IGNEUM_HOT_SEGMENTS`, `IGNEUM_HOT_SLOTS`, `IGNEUM_HOT_KEY_INIT`; `vectors.h` and `vectors.json` the hot head, last line and FNV-1a 64 | +| `igneum-pow/src/main.rs` | `--class hotk` on every command (`bench` reports the fill time and the verifier ms per warp) | +| `igneum-pow/tests/packs.rs` | the five hot packs pinned (program, vectors, every emitted file, the load-form count: `16 - k` masked loads and `k` hot loads) | +| `proto-cuda/packs-ca2-hot/` | replaced: `hot32k4`, `hot64k4`, `hot96k4`, `hot64k2`, `hot64k8`; added: `hot32k4a`, `hot64k4a`, `hot96k4a`; all from seed `igneum-genesis`, day `2026-10-03` | +| `proto-cuda/nvrtc/packfile.h` | `hotMb`, `hotWords`, `hotSegments`, `hotSlots`; the hot self-test values; `pf_selftest` checks them when the pack carries them | +| `proto-opencl/host.c` | `--bench-pack` and the self-test allocate and fill `H` from the pack's `igneum_hot_fill`, check its head, last line and FNV-1a 64, pass it as the argument after the init words | +| `proto-metal/packbench.swift` | the same on Metal from `memhard.metal` | +| `proto-cuda/nvrtc/worker.cpp` | `--check` and `--bench` (new: the base kernel timed over `--batches` dispatches, one RESULT line) allocate, fill and self-test `H`; the launch passes it | + +## 6. Measurements + +Every row names the machine, the harness and the command; the bench-log entry of 5 October 2026 ("the hot table on the M5 Max") carries the raw lines. The Mac rows were taken on 5 October 2026, 20:19 to 20:21 UTC, under the measure lock, with the Mac's load average at 14 to 27 from other agents' CPU work (the lock serialises builds and measurements, not every process), so they are ordered, repeatable to within a few percent against each other, and not the Mac's quiet numbers. The PC rows wait for the coordinator's go. + +### 6.1 Random-read probe at the hot sizes + +`igneum-bench-cl-igneum-genesis-mh --memprobe --probe-mib S` (`proto-opencl/host.c`; dependent random 4-byte loads, best of 3, 256 steps per lane, work-group 256; the ceiling is the 4,194,304-lane row; Apple OpenCL reports wall time). + +| Card | 32 MiB ceiling | 64 MiB | 96 MiB | 1024 MiB | Ratio 32 / 1024 | 64 / 1024 | 96 / 1024 | ns per dependent load at 4,096 lanes (32, 64, 96, 1024 MiB) | +|---|---|---|---|---|---|---|---|---| +| M5 Max (Apple OpenCL) | 21.7 G loads/s | 12.8 | 12.3 | 3.50 | 6.2 | 3.7 | 3.5 | 1,168; 1,129; 1,242; 1,844 | +| RTX 5090 (CUDA worker `--memprobe`, PC 1, job run-ca2-hot-5090-20261005, card off in the app) | 112.6 | 112.6 | 112.6 | 17.6 | 6.4 | 6.4 | 6.4 | 320; 340; 336; 610 | +| RX 9070 XT (OpenCL worker `--memprobe --device 1`, PC 1, job run-ca2-hot-9070-20261005, card off in the app) | 9.88 (10.8 at 262,144 lanes) | 9.47 | 8.18 | 2.43 | 4.1 | 3.9 | 3.4 | 396; 457; 454; 1,579 | + +Reading, RTX 5090: all three sizes sit inside the 96 MiB L2 at one ceiling (112.6 G loads/s, 6.4x the DRAM figure), so the probe alone promises a full hit rate for every S. RX 9070 XT: 32 and 64 MiB inside the Infinity Cache at 9.5 to 10.8 G loads/s (3.9 to 4.1x), 96 MiB at 8.2 (3.4x), as the 9070 XT entry's 64 MiB row said. Streams: 5090 1,563 GB/s at 1024 MiB, 9070 XT 633 GB/s (both at their rated figures, the cards were not parked). + +Reading, M5 Max: the step from 32 to 64 MiB halves the ceiling (21.7 to 12.8 G loads/s) and 96 MiB sits with 64, so 32 MiB is inside a cache level that 64 MiB is not (the M5 Max's system level cache size is not published; approximate reading: the 32 MiB table fits, the two larger ones mostly do not and run at a 3.5 to 3.7x advantage over DRAM from whatever hits they get). The coalesced stream grows with the buffer (139, 199, 243, 522 GB/s) because the small buffers are read once from cold. + +Predicted `g` from the probe alone, with a hot load costing `1 / ceiling_S` and a cold load `1 / ceiling_1024`: `g = 1 / ((16 - k) / 16 + (k / 16) x ceiling_1024 / ceiling_S)`. + +### 6.2 Bit-exactness and hash rate per hot pack + +Metal: `proto-metal/packbench --pack --batches 5 --batch-log2 24` (GPU time). Apple OpenCL: `igneum-bench-cl-igneum-genesis-mh --bench-pack --pack --batches 5 --batch-log2 24` (wall time). Both harnesses fill the hot table on the device from the pack's `igneum_hot_fill` and check its head, last line and FNV-1a 64 against vectors.json or vectors.h; vectors are the 96 lanes of the Rust reference; the fingerprint is FNV-1a 64 over the 2^24 outputs at base nonce 0. + +| Pack | Vectors (Metal, OpenCL) | Hot table FNV (Metal, OpenCL) | Fingerprint 2^24 (both harnesses equal) | Metal Mhash/s | Apple OpenCL Mhash/s | g against v2 (Metal) | Predicted g from the probe | Ideal g | +|---|---|---|---|---|---|---|---|---| +| v2 (igneum-genesis-mh) | 3/3, 96/96 | none | 25f96e7dce90bd4e | 27.68 | 27.61 | 1 | 1 | 1 | +| hot32k4 | 3/3, 96/96 | PASS, PASS | d2e6cf3b61d0b9fe | 33.90 | 33.92 | 1.22 | 1.27 | 1.33 | +| hot64k4 | 3/3, 96/96 | PASS, PASS | e4c5263ac650cc0d | 30.93 | 30.89 | 1.12 | 1.22 | 1.33 | +| hot96k4 | 3/3, 96/96 | PASS, PASS | 5d63439b6e394521 | 29.06 | 28.97 | 1.05 | 1.22 | 1.33 | +| hot64k2 | 3/3, 96/96 | PASS, PASS | 352633bdbbb0d2b6 | 27.67 | 27.27 | 1.00 | 1.10 | 1.14 | +| hot64k8 | 3/3, 96/96 | PASS, PASS | da54630d7dfaaf85 | 47.42 | 46.73 | 1.71 | 1.57 | 2.0 | +| hot32k4a (added) | 3/3, 96/96 | PASS, PASS | 8a3414735db4523c | 25.76 | 25.72 | 0.93 | 0.96 | 1 | +| hot64k4a (added) | 3/3, 96/96 | PASS, PASS | 45668f34105f6307 | 23.92 | 23.87 | 0.87 | 0.94 | 1 | +| hot96k4a (added) | 3/3, 96/96 | PASS, PASS | af763997dfee4c82 | 22.92 | 22.88 | 0.83 | 0.93 | 1 | + +For the added form the ideal `g` is 1 (the 16 dataset loads stay) and the probe predicts `g = 1 / (1 + (k / 16) x ceiling_1024 / ceiling_S)`: 0.96 at 32 MiB, 0.94 at 64 and 96 MiB for `k = 4` on the M5 Max; what matters is how far below 1 the GPU lands (its cost of the layer) against the chip's `S` MiB of SRAM and `k` reads. + +The PCs (5 October 2026, 21:29 to 21:35 UTC, PC 1 ae432dc7 on app 0.3.9 before and after, the card under test switched off in the app through `api/cards` and restored; CUDA worker `--bench --batches 5 --batch-log2 24 --block-warps 1` on the RTX 5090, wall time around the stream sync; OpenCL worker `--bench-pack --batches 5 --batch-log2 24 --device 1` on the RX 9070 XT, device event time, work-group 256; the v2 references are the readwidth entry's same-night, same-worker numbers: 136.1 and 18.15 MH/s). Every pack bit-exact with the Mac's fingerprint, hot table head, last line and FNV PASS, 96/96 lanes, on both cards. + +| Pack | RTX 5090 MH/s | g (v2 136.1) | probe-predicted g (5090) | RX 9070 XT MH/s | g (v2 18.15) | probe-predicted g (9070 XT) | ideal g | +|---|---|---|---|---|---|---|---| +| hot32k4 | 146.6 | 1.08 | 1.27 | 19.79 | 1.09 | 1.23 | 1.33 | +| hot64k4 | 140.8 | 1.03 | 1.27 | 18.73 | 1.03 | 1.23 | 1.33 | +| hot96k4 | 138.5 | 1.02 | 1.27 | 18.33 | 1.01 | 1.21 | 1.33 | +| hot64k2 | 137.5 | 1.01 | 1.13 | 18.17 | 1.00 | 1.11 | 1.14 | +| hot64k8 | 163.6 | 1.20 | 1.73 | 22.32 | 1.23 | 1.59 | 2.0 | +| hot32k4a (added) | 118.7 | 0.87 | 0.96 | 15.27 | 0.84 | 0.94 | 1 | +| hot64k4a (added) | 115.4 | 0.85 | 0.96 | 14.62 | 0.81 | 0.94 | 1 | +| hot96k4a (added) | 114.4 | 0.84 | 0.96 | 14.56 | 0.80 | 0.93 | 1 | + +The 5090 at `--block-warps 8` (6 blocks per SM, 8,160 resident warps) is within 0.7% of every row above; the 9070 XT at work-group 32 within 0.5%. Hot fill on the 5090: 1.2 to 2.5 ms (wall, driver API); on the 9070 XT 1.6 to 5.3 ms. + +Reading, the PCs. The probe promised a full hit rate for every S on the 5090 (all three tables inside the 96 MiB L2 at one ceiling) and 3.4 to 4.1x on the 9070 XT, and the hash delivered a fraction of it: 1.02 to 1.08x at `k = 4` against the probe's 1.27x and the ideal 1.33x on the 5090, 1.01 to 1.09x on the 9070 XT, 1.20 to 1.23x at `k = 8` against 1.73 and 2.0x. The table that stands alone in the probe does not stand up with the 1 GiB dataset streaming through the same cache: the dataset's random lines evict it (the 5090's L2 and the 9070 XT's Infinity Cache are shared by every load; neither card partitions them). The added form costs the 5090 13 to 16% and the 9070 XT 16 to 20% of its rate for four extra loads per iteration, three to five times the probe's 4 to 7%. The three cards agree on the shape; the 5090's larger cache buys it nothing over the Mac at 32 MiB (1.08 against 1.22). + +Hot table fill on the M5 Max GPU (Metal, GPU time): 0.07 ms at 32 MiB, 0.15 ms at 64 MiB, 0.22 ms at 96 MiB. + +Reading, M5 Max. Bit-exactness holds: Metal and Apple OpenCL give one fingerprint per pack and every vector lane against the Rust reference, hot table included. On the rate, the hot loads are worth 92% of the probe's prediction at 32 MiB (1.22 against 1.27), 50% at 64 MiB (1.12 against 1.22 is 0.12 of 0.22) and 23% at 96 MiB; at `k = 8` the measured 1.71 is above the prediction (1.57), which says the cold loads also go faster when half of them are gone (fewer dependent DRAM reads in the chain per hash). `hot64k2` gained nothing on this Mac at load (27.67 against 27.68). So on Apple silicon, with the 1 GiB dataset streaming through the same cache, the table that fits (32 MiB) delivers most of its ideal and the larger ones lose most of theirs. The 5090 and 9070 XT, whose caches are the plan's targets, are the PC job. + +### 6.3 CPU verifier, one core, avg of 50 warps + +`igneum-pow bench --seed igneum-genesis --day 2026-10-03 --class --warps 50` (release build, one M5 Max core, the Mac at load as above; the v2 row from the same session, the 0.604 ms of the brief was an earlier quiet run). + +| Class | Hot fill, one core | Items derived per warp | ms per warp | Against v2 | +|---|---|---|---|---| +| v2 | none | 4,096 | 0.626 | 1 | +| hot32k4 | 24.0 ms | 3,072 | 0.489 | 0.78 | +| hot64k4 | 46.4 ms | 3,072 | 0.504 | 0.81 | +| hot96k4 | 73.0 ms | 3,072 | 0.488 | 0.78 | +| hot64k2 | 45.5 ms | 3,584 | 0.560 | 0.89 | +| hot64k8 | 47.7 ms | 2,048 | 0.344 | 0.55 | +| hot32k4a (added) | 21.7 ms | 4,096 | 0.631 | 1.05 (v2 in the same session 0.602) | +| hot64k4a (added) | 43.3 ms | 4,096 | 0.609 | 1.01 | +| hot96k4a (added) | 64.9 ms | 4,096 | 0.614 | 1.02 | + +Reading: the verifier gets cheaper with `k`, because a hot load is one table read where a dataset load is an item derivation (9 mixers and 8 cache reads); the per-epoch cost is the fill, 24 to 73 ms on one core, against a 3,600 s epoch and the 20-minute seed lead. The 10 ms gate (spec 1.16) is unaffected. + +### 6.4 The chip model with the measured g (M5 Max; the PC rows will replace it) + +Chip A (no SRAM for `H`): gain after = `G0 / g`. Chip B (`H` in SRAM): `G0 x A_die / (A_die + A_S)` at 0.39 mm^2 per MiB, 100 mm^2 die. + +| Class | g (M5 Max) | Chip A, gain after as a share of G0 | Chip B, share of G0 at 100 mm^2 | Chip B at 750 mm^2 | +|---|---|---|---|---| +| hot32k4 | 1.22 | 0.82 | 0.89 | 0.98 | +| hot64k4 | 1.12 | 0.89 | 0.80 | 0.97 | +| hot96k4 | 1.05 | 0.95 | 0.73 | 0.95 | +| hot64k8 | 1.71 | 0.58 | 0.80 | 0.97 | + +The added form (second Mac session, 21:03 to 21:19 UTC, load average 7 to 14; v2 in that session 27.63 Metal, 27.59 OpenCL): the GPU keeps 0.93 / 0.87 / 0.83 of its rate at 32 / 64 / 96 MiB (`k = 4`), against the probe's 0.96 / 0.94 / 0.93, so the hot hits cost this card more than the probe says as the table grows, and 96 MiB is not resident. The named chip's position under the added form, same assumptions: + +| Chip | Hot loads served from | Rate against its own version 2 rate | Gain after, as a share of G0 (S = 32 / 64 / 96) | Arithmetic | +|---|---|---|---|---| +| A, no SRAM for H | DRAM | at most 16 / 20 = 0.80 (20 dependent DRAM loads per iteration in place of 16) | 0.86 / 0.92 / 0.96 | `0.80 / g` | +| B, S MiB of SRAM for H, 100 mm^2 die, 0.39 mm^2/MiB | SRAM (the k reads near free) | 1.00 | 0.96 / 0.92 / 0.88 | `(1 / g) x A_die / (A_die + A_S)` | +| B on a 750 mm^2 die | SRAM | 1.00 | 1.06 / 1.12 / 1.15 | the SRAM is 1.6 to 4.8% of the die, the GPU's loss is larger | + +With the PCs' `g` (added form, `k = 4`): RTX 5090 0.87 / 0.85 / 0.84, RX 9070 XT 0.84 / 0.81 / 0.80 at 32 / 64 / 96 MiB. + +| Chip, added form, S = 32 / 64 / 96 MiB | Gain after as a share of G0, 5090 g | 9070 XT g | +|---|---|---| +| A, H from DRAM (rate 0.80 of its own) | 0.92 / 0.94 / 0.95 | 0.95 / 0.99 / 1.00 | +| B, S MiB of SRAM, 100 mm^2 die, 0.39 mm^2/MiB | 1.03 / 0.94 / 0.87 | 1.06 / 0.99 / 0.91 | +| B, 750 mm^2 die | 1.13 / 1.14 / 1.17 | 1.17 / 1.20 / 1.22 | + +Reading: on the cards the plan named, the added form taxes no chip. A chip that serves H from its DRAM loses at most 8% of its gain on the 5090's figures and nothing on the 9070 XT's; a chip with the SRAM comes out ahead on every row except the smallest die at 64 and 96 MiB. The replaced form helps the recompute chip outright (section 2.3). The hot hits are not free on a GPU while the 1 GiB dataset streams through the same cache, and the layer's premise (section 1) needed them to be. + +**Recommendation (owner: the project lead, gate 1):** do not adopt layer 5 in either form on these measurements. What could change it: a GPU-side way to keep H resident (cache partitioning or persisting-access controls exist on NVIDIA, approximate, from memory, and are a driver setting, not a consensus rule), or a dataset access pattern that bypasses the cache; both are outside the hash and were not measured. The experiment stays behind the flag with its packs and vectors as the record. + +Reading, replaced form: on the M5 Max figures the chip's cheaper way out of `hot64k8` is the SRAM (0.80 of `G0` at a 100 mm^2 die) rather than serving `H` from DRAM (0.58). The layer's value is then the SRAM area, which is small on a large die (0.97 at 750 mm^2). The strongest configuration on this card is the one whose table fits the cache and whose `k` is large; whether 64 MiB fits the 5090's L2 and the 9070 XT's Infinity Cache with the dataset streaming beside it is the PC measurement. + +## 7. Decision and what would make it pay (the Counter ASIC 3.0 note) + +Decided by the coordinator on 5 October 2026 after the PC rows: layer 5 is out of v3. The rule was a GPU cost under 3% (`g` above 0.97) for a chip cost worth having; measured `g` 0.84 to 0.87 on the RTX 5090 and 0.80 to 0.84 on the RX 9070 XT for the added form, and the replaced form helps the recompute chip. No re-run. + +What would make a hot table pay, for a later round: + +| Condition | What the measurements say | What would have to be shown | +|---|---|---| +| A table that stays resident beside a streaming 1 GiB | The probe's ceiling at every S inside the cache (5090: 112.6 G loads/s at 32, 64 and 96 MiB) and the hash's 1.02 to 1.08x say the dataset's random lines evict it; 32 MiB on the M5 Max kept 92% of its probe gain, the 5090 kept 29% | A size small enough to survive: the probe cannot say which (it has no competing stream); a sweep of `S` down from 32 MiB (16, 8, 4, 2) with the hash itself, on the 5090 first, would. A 2 to 4 MiB table costs a chip under 2 mm^2 of SRAM, so the layer would then tax nothing; the point of the layer was a table a chip cannot afford, and a table a GPU keeps is one a chip affords | +| The `k` and `S` the probe says | `g` moved with `k` (1.20x at `k = 8`, 1.02 to 1.08 at `k = 4`, 1.00 at `k = 2`) and barely with `S`; the probe predicted 1.27 to 1.73 | Only a resident table makes `k` worth raising; at `k = 8` with a resident table the replaced form reaches 2x (ideal) and the added form costs 0 by the probe. Without residency, `k` buys rate on the replaced form (which helps the chip) and costs rate on the added form | +| A different access shape | Both forms read H at one random word per hot load, the dataset's own pattern, and share the cache with 128 dataset reads per hash | A shape that touches the table in cache-line units and few lines per hash (one 64-byte line per iteration, say) would need a smaller resident set per hash and could be measured with the read-width emitters (`wide_load_stmt`) pointed at H. Or a hot table whose lines are read in a fixed order per epoch (a stream, not a random read), which a GPU prefetches and a chip must still hold or fetch; not designed here | +| Cache partitioning on the GPU | NVIDIA exposes persisting L2 access controls to CUDA programs (approximate, from memory); AMD and Apple do not expose an equivalent to OpenCL or Metal | A consensus rule cannot depend on a driver feature of one vendor; it could be a miner-side optimisation if the layer were in, which it is not | + +What the round leaves in place: the code behind the flag (`LoadClass::hot`, both forms, the hosts filling H on the device from the epoch seed, the eight packs and their vectors), the probe at the hot sizes on three cards, and the measured rule that a read-only table a GPU cache could hold is not one it does hold while the dataset streams. + +## 8. What is unverified + +Listed here until measured, and carried into the bench-log entry. + +1. Measured on all three cards (6.2): no card keeps `H` resident enough to deliver the probe's hit rate while the dataset streams. Not measured: whether a driver-side cache partition (NVIDIA's persisting L2 access controls, approximate, from memory) would; it is not something a consensus rule can rely on. +2. The PC rows are one run each (jobs run-ca2-hot-5090-20261005 and run-ca2-hot-9070-20261005, 5 October 2026); the two launch shapes per card agree within 1%, and no re-run was taken (PC 1 was handed on). +3. The resident warp counts of section 3 are approximate; the occupancy the hosts reach is printed by each harness (`kernel:` lines) and should replace them. +4. The SRAM area figures are approximate (section 4 cites their sources); no chip was priced. +5. `S = 96` uses the multiply-shift mapping; the spec's dataset still uses `AND MASK`. If the layer goes in, gate 1 decides whether the dataset mapping follows (section 1.13.3) or `S` stays a power of two. +6. The hot table on the chain needs the epoch seed about 1 ms (GPU) or up to 71 ms (CPU) before the epoch starts; the 20-minute lead of section 4.3 covers it. Not exercised on a node. +7. The Mac numbers were taken at load average 14 to 27 (other agents' CPU work); the ratios between packs of one session are the result, the absolute Mhash/s are not quiet numbers. diff --git a/igneum-pow/src/accept.rs b/igneum-pow/src/accept.rs index 7a5352ee..198871a2 100644 --- a/igneum-pow/src/accept.rs +++ b/igneum-pow/src/accept.rs @@ -11,11 +11,13 @@ //! | (c) dynamic | the program is interpreted for [`ACCEPT_UNITS`] (64) units of 32 lanes at base nonces drawn from SplitMix64 seeded with `FNV-1a-64("igneum-accept/" \|\| seed words as little-endian bytes)`, each `low32(next()) AND NOT 31`, with init words equal to the seed words and the closed-form dataset `dataset_elem(idx, S[0], S[1])` at [`ACCEPT_DATASET_LOG2`] (2^28 words) in place of the memory-hard dataset. Over the 2,048 evaluations: no register has a bit equal in every final value; no load site (iteration, instruction) reads one address in all 32 lanes of any unit; fewer than [`MAX_SATURATED`] (164, 1 percent of 16,384) final register values are 0 or 2^32 - 1; every output bit's ones count is within [`BIAS_TOLERANCE`] (136, 6 sigma) of 1,024; the distinct masked addresses read by one lane in one evaluation, summed over the 2,048 evaluations, exceed [`MIN_DISTINCT_SUM`] (245,760, a mean above 120 of the 128 loads) | //! //! The dynamic test uses the closed form so that it is a pure function of the program (no cache, no day) and -//! costs about a millisecond on one core. The census (section 7.3) checked on 100,000 programs that the +//! costs about a millisecond on one core. A hot-table load (`docs/plans/hot-table.md`) reads the closed form keyed by +//! seed words 2 and 3 at its multiply-shift index, a second pure table beside the dataset stand-in (words 0 and 1). The census (section 7.3) checked on 100,000 programs that the //! closed-form verdict agrees with the memory-hard one on all but 39 threshold-edge cases. use crate::generator::{Instr, Op, Program, INSTR_COUNT, ITERATIONS, LANES}; use crate::seed::{fnv1a64, SplitMix64}; +use crate::memhard::hot_index; use crate::verify::{dataset_elem, fold_words, load_index, splitmix32, ScratchModel}; /// Units (32-lane warps) the dynamic test interprets. @@ -178,6 +180,8 @@ fn run_unit(p: &Program, unit: usize, base: u32, acc: &mut Acc, lane_addrs: &mut let seed = &p.seed; let mask: u32 = (1u32 << ACCEPT_DATASET_LOG2) - 1; let (d0, d1) = (seed[0], seed[1]); + let (h0, h1) = (seed[2], seed[3]); + let hot_words = p.hot_words(); let loads = p.loads_per_hash(); let mut r = [[0u32; LANES]; 8]; for lane in 0..LANES { @@ -305,6 +309,21 @@ fn run_unit(p: &Program, unit: usize, base: u32, acc: &mut Acc, lane_addrs: &mut } nload += 1; } + Op::Hot => { + // Hot table: the stand-in is dataset_elem keyed by seed words 2 and 3; the address is tagged with + // bit 30 so a hot word and a dataset word at one index count as two addresses. + for lane in 0..LANES { + idx[lane] = hot_index(r[a][lane], hot_words); + } + if idx.iter().all(|&x| x == idx[0]) { + return Err(Reject::LaneConstantSite { iteration: it as u8, instr: k as u8, unit: unit as u8 }); + } + for lane in 0..LANES { + r[d][lane] ^= dataset_elem(idx[lane], h0, h1); + lane_addrs[lane * loads + nload] = 0x4000_0000 | idx[lane]; + } + nload += 1; + } Op::WLoad => { let b = (r[a][0] & mask) & !31; for lane in 0..LANES { @@ -461,6 +480,52 @@ mod tests { } } + /// Hot-table experiment: the hot classes pass the rule at about the version 2 rate, and the hot addresses are + /// uniform over the table (16 buckets of the index over 64 units x 32 lanes x 32 hot loads). + #[test] + fn hot_classes_pass_and_hot_loads_are_uniform() { + for name in ["hot32k4", "hot64k4", "hot96k4", "hot64k2", "hot64k8", "scr4k32+hot64k4", "hot32k4a", "hot64k4a", "hot96k4a"] { + let c = LoadClass::parse(name).unwrap(); + let p = generate_class("igneum-genesis", c); + assert!(check(&p).is_ok(), "{name}"); + let mut rejected = 0; + for i in 0..60u32 { + let s = format!("igneum-hot-accept/{i}"); + let q = candidate_class(&s, s.as_bytes(), 0, c); + if check(&q).is_err() { + rejected += 1; + } + } + assert!(rejected < 15, "{name}: {rejected} of 60 rejected"); + } + let p = generate_class("igneum-genesis", LoadClass::hot(96, 4)); + let words = p.hot_words(); + let loads = p.loads_per_hash(); + let mut acc = Acc { and_acc: [u32::MAX; 8], or_acc: [0; 8], saturated: 0, bit_ones: [0; 64], distinct_sum: 0 }; + let mut la = vec![0u32; LANES * loads]; + let mut buckets = [0u64; 16]; + let mut hot_count = 0u64; + for (u, &b) in accept_base_nonces(&p.seed).iter().enumerate() { + run_unit(&p, u, b, &mut acc, &mut la).unwrap(); + for &a in &la { + if a & 0xC000_0000 == 0x4000_0000 { + let idx = a & 0x3FFF_FFFF; + assert!(idx < words); + buckets[(idx as u64 * 16 / words as u64) as usize] += 1; + hot_count += 1; + } + } + } + assert_eq!(hot_count, 64 * 32 * 32, "32 hot loads per hash over 2,048 hashes"); + let mean = hot_count as f64 / 16.0; + for (i, &b) in buckets.iter().enumerate() { + assert!((b as f64 - mean).abs() < 0.15 * mean, "bucket {i}: {b} against a mean of {mean}"); + } + // the dataset distinct count still holds for the dataset loads alone + let r = check(&p).unwrap(); + assert!(r.distinct_mean() > 120.0); + } + #[test] fn base_nonces_are_aligned_and_seed_dependent() { let a = accept_base_nonces(&[1, 2, 3, 4, 5, 6, 7, 8]); diff --git a/igneum-pow/src/emit.rs b/igneum-pow/src/emit.rs index ef9410ef..7cb1ceed 100644 --- a/igneum-pow/src/emit.rs +++ b/igneum-pow/src/emit.rs @@ -11,8 +11,8 @@ use crate::generator::{EraParams, Instr, Op, Program, ProgramClass, GENERATOR_VERSION, INSTR_COUNT, ITERATIONS, LOAD_SLOTS}; use crate::memhard::{ - Layout, MixParams, Shape, CACHE_LINES_PER_SEGMENT, CACHE_SEGMENT_LOG2_LINES, CACHE_TAG, CHACHA_ROUNDS, CHACHA_SIGMA, - ITEM_ROUNDS, + hot_key, hot_segments, hot_words, Layout, MixParams, Shape, CACHE_LINES_PER_SEGMENT, CACHE_SEGMENT_LOG2_LINES, CACHE_TAG, + CHACHA_ROUNDS, CHACHA_SIGMA, HOT_TAG, ITEM_ROUNDS, }; use crate::seed::SplitMix64; use crate::verify::{window, DatasetMode, DatasetSource, Epoch, DEFAULT_DATASET_LOG2, FOLD_MUL, FOLD_ROT}; @@ -306,6 +306,87 @@ fn scratch_header_lines(p: &Program) -> String { s } +/// Hot-table experiment (`docs/plans/hot-table.md`): the `HOT_WORDS` literal of a hot pack's hash kernels (empty +/// for every other class, so the pinned packs do not change). +fn hot_define(p: &Program) -> String { + match p.class.hot { + Some(h) => format!("// Hot table ({} MiB, {} of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS {} +", h.mb, h.k, hex(hot_words(h.mb as u32))), + None => String::new(), + } +} + +/// The hot load as one statement per dialect: the high 32 bits of `src x HOT_WORDS` index the table. +fn hot_stmt(dialect: CoreDialect, d: &str, a: &str) -> String { + match dialect { + CoreDialect::Metal => format!("{d} = {d} ^ hot[mulhi({a}, HOT_WORDS)];"), + CoreDialect::Cuda => format!("{d} = {d} ^ hot[__umulhi({a}, HOT_WORDS)];"), + CoreDialect::OpenCl => format!("{d} = {d} ^ hot[mul_hi({a}, HOT_WORDS)];"), + } +} + +/// The hot table's fill core: `ht_segment(hot, seg)`, the cache chain of `mh_cache_segment` under the hot key and +/// the hot tag (`mh_chacha_block` and `MH_SEGMENT_LINES` must be in scope: the memory-hard core comes first). +fn emit_hot_core(p: &Program, dialect: CoreDialect) -> String { + let Some(h) = p.class.hot else { return String::new() }; + let (u, fn_, wptr) = match dialect { + CoreDialect::Metal => ("uint", "inline", "device uint*"), + CoreDialect::Cuda => ("uint32_t", "IGNEUM_HD", "uint32_t*"), + CoreDialect::OpenCl => ("uint", "static inline", "__global uint*"), + }; + let k = hot_key(&p.seed_bytes); + let mut s = String::with_capacity(1500); + s.push_str(&format!( + "// Hot table (docs/plans/hot-table.md): {} MiB = {} segments of {} chained ChaCha{} lines under the hot key KH = seed_words(\"igneum-hot/\" || epoch seed bytes), tag \"Igne\" \"umHT\". The cache chain with another key and tag.\n", + h.mb, + hot_segments(h.mb as u32), + CACHE_LINES_PER_SEGMENT, + CHACHA_ROUNDS + )); + s.push_str(&format!("{fn_} void ht_segment({wptr} hot, {u} seg) {{\n")); + s.push_str(&format!(" {u} prev[16]; {u} x[16]; {u} y[16];\n")); + s.push_str(&format!(" for ({u} i = 0u; i < 16u; ++i) prev[i] = 0u;\n")); + s.push_str(&format!(" for ({u} j = 0u; j < MH_SEGMENT_LINES; ++j) {{\n")); + s.push_str(&format!( + " x[0] = {} ^ prev[0]; x[1] = {} ^ prev[1]; x[2] = {} ^ prev[2]; x[3] = {} ^ prev[3];\n", + hex(CHACHA_SIGMA[0]), + hex(CHACHA_SIGMA[1]), + hex(CHACHA_SIGMA[2]), + hex(CHACHA_SIGMA[3]) + )); + for i in 0..8 { + s.push_str(&format!(" x[{}] = {} ^ prev[{}];\n", 4 + i, hex(k[i]), 4 + i)); + } + s.push_str(&format!( + " x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = {} ^ prev[14]; x[15] = {} ^ prev[15];\n", + hex(HOT_TAG[0]), + hex(HOT_TAG[1]) + )); + s.push_str(" mh_chacha_block(x, y);\n"); + s.push_str(&format!(" {wptr} line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u);\n")); + s.push_str(&format!(" for ({u} i = 0u; i < 16u; ++i) {{ line[i] = y[i]; prev[i] = y[i]; }}\n")); + s.push_str(" }\n"); + s.push_str("}\n"); + s +} + +/// The hot lines of program.h. +fn hot_header_lines(p: &Program) -> String { + let Some(h) = p.class.hot else { return String::new() }; + let mut s = String::new(); + s.push_str("// Hot table (hot-table experiment, 5 October 2026, docs/plans/hot-table.md): NOT the lottery hash. IGNEUM_HOT_SLOTS of the\n"); + s.push_str("// 16 load slots read H[mulhi(src, IGNEUM_HOT_WORDS)], H = IGNEUM_HOT_MB MiB of chained ChaCha12 lines (the cache chain) under\n"); + s.push_str("// KH = seed_words(\"igneum-hot/\" || epoch seed bytes) and the tag \"Igne\" \"umHT\"; filled by igneum_hot_fill once per epoch.\n"); + s.push_str(&format!("#define IGNEUM_HOT_MB {}\n", h.mb)); + s.push_str(&format!("#define IGNEUM_HOT_WORDS {}\n", hex(hot_words(h.mb as u32)))); + s.push_str(&format!("#define IGNEUM_HOT_SEGMENTS {}u\n", hot_segments(h.mb as u32))); + s.push_str(&format!("#define IGNEUM_HOT_SLOTS {} // hot loads per program ({} per hash), {} the dataset loads ({} of them)\n", h.k, p.hot_loads_per_hash(), if h.added { "added beside" } else { "replacing" }, p.class.dataset_slots())); + s.push_str(&format!("#define IGNEUM_HOT_ADDED {}\n", h.added as u8)); + s.push_str(&format!("#define IGNEUM_HOT_KEY_INIT {{ {} }}\n", join_hex(&hot_key(&p.seed_bytes)))); + s +} + pub fn hex(v: u32) -> String { format!("0x{v:08x}u") } @@ -538,6 +619,21 @@ fn build_store(layout: Layout, dialect: CoreDialect, ds: &str, t: &str) -> Strin } } +/// [`metal_memhard`] plus, for a hot pack, the hot table's `ht_segment` and `igneum_hot_fill` kernel (one thread per +/// segment, `IGNEUM_HOT_SEGMENTS` threads). Byte-identical to [`metal_memhard`] for every other class. +pub fn metal_memhard_for(p: &Program, mp: &MixParams) -> String { + let mut s = metal_memhard_layout(mp, p.class.layout()); + if p.has_hot() { + s.push('\n'); + s.push_str(&emit_hot_core(p, CoreDialect::Metal)); + s.push_str("// Hot table: one thread per segment (IGNEUM_HOT_SEGMENTS threads).\n"); + s.push_str("kernel void igneum_hot_fill(device uint* hot [[buffer(0)]], uint gid [[thread_position_in_grid]]) {\n"); + s.push_str(" ht_segment(hot, gid);\n"); + s.push_str("}\n"); + } + s +} + /// Metal library with the cache fill and dataset build kernels for one day key (`memhardMSL`, memhard.metal). pub fn metal_memhard(mp: &MixParams) -> String { metal_memhard_layout(mp, Layout::LINEAR) @@ -589,6 +685,7 @@ fn metal_program_impl(p: &Program, dataset_log2: u32, source: LoadSource, bound: s.push_str("using namespace metal;\n"); s.push('\n'); s.push_str(&format!("#define MASK {}\n", hex(mask))); + s.push_str(&hot_define(p)); s.push_str(&format!("constant uint SEEDW[8] = {{ {} }};\n", join_hex(&p.seed))); s.push('\n'); s.push_str("inline uint splitmix32(uint x) {\n"); @@ -624,8 +721,11 @@ fn metal_program_impl(p: &Program, dataset_log2: u32, source: LoadSource, bound: if bound { s.push_str(" constant uint* initw [[buffer(3)]],\n"); } + if p.has_hot() { + s.push_str(&format!(" device const uint* hot [[buffer({})]],\n", if bound { 4 } else { 3 })); + } if p.has_scratch() { - let b = if bound { 4 } else { 3 }; + let b = (if bound { 4 } else { 3 }) + p.has_hot() as usize; s.push_str(&format!(" device uint* scratch [[buffer({b})]],\n")); s.push_str(&format!(" constant uint& groups [[buffer({})]],\n", b + 1)); s.push_str(&format!(" constant uint& salt [[buffer({})]],\n", b + 2)); @@ -695,6 +795,7 @@ fn metal_program_impl(p: &Program, dataset_log2: u32, source: LoadSource, bound: Op::Load => format!("{d} = {d} ^ {};", fetch(word_index(&a, false, ins))), Op::WLoad => format!("{d} = {d} ^ {};", fetch(word_index(&a, true, ins))), Op::Scratch => scratch_stmt(CoreDialect::Metal, &d, &a, p.class.scratch_slot_mask()), + Op::Hot => hot_stmt(CoreDialect::Metal, &d, &a), }; s.push_str(&format!(" {line} // {k}\n")); } @@ -763,6 +864,7 @@ fn cuda_instr_lines(p: &Program, dataset_log2: u32) -> String { Op::Load => format!("{d} = {d} ^ ds[{}];", load_index_expr(CoreDialect::Cuda, era.as_ref(), ins, &a, dataset_log2)), Op::WLoad => format!("{d} = {d} ^ ds[(__shfl_sync(0xffffffffu, {a}, 0) & wmask) + lane];"), Op::Scratch => scratch_stmt(CoreDialect::Cuda, &d, &a, p.class.scratch_slot_mask()), + Op::Hot => hot_stmt(CoreDialect::Cuda, &d, &a), }; s.push_str(&format!(" {line} // {k} {}\n", ins.op.name())); } @@ -791,6 +893,7 @@ pub fn cuda_kernel_at(p: &Program, memhard: Option<&MixParams>, dataset_log2: u3 s.push_str("#include \"memhard.h\"\n"); } s.push('\n'); + s.push_str(&hot_define(p)); s.push_str("__device__ __forceinline__ uint32_t splitmix32(uint32_t x) {\n"); s.push_str(" x ^= x >> 16; x *= 0x7feb352du;\n"); s.push_str(" x ^= x >> 15; x *= 0x846ca68bu;\n"); @@ -831,14 +934,25 @@ pub fn cuda_kernel_at(p: &Program, memhard: Option<&MixParams>, dataset_log2: u3 s.push_str(&build_store(layout, CoreDialect::Cuda, "ds", "t")); s.push_str(" }\n"); s.push_str("}\n"); + if p.has_hot() { + s.push_str("// Hot table (ht_segment is in memhard.h): one thread per segment.\n"); + s.push_str("__global__ void igneum_hot_fill(uint32_t* hot, uint32_t nSegments) {\n"); + s.push_str(" uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x;\n"); + s.push_str(" if (seg < nSegments) ht_segment(hot, seg);\n"); + s.push_str("}\n"); + } s.push('\n'); } + if memhard.is_none() && p.has_hot() { + panic!("a hot-table pack needs the memory-hard dataset (the hot fill shares its ChaCha core)"); + } s.push_str("// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every\n"); s.push_str("// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a\n"); s.push_str("// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid.\n"); s.push_str(&scratch_prelude(p, CoreDialect::Cuda)); let scratch_args = if p.has_scratch() { ", uint32_t* scratch, uint32_t groups, uint32_t salt" } else { "" }; - s.push_str(&format!("__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask{scratch_args}) {{\n")); + let hot_args = if p.has_hot() { ", const uint32_t* hot" } else { "" }; + s.push_str(&format!("__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask{hot_args}{scratch_args}) {{\n")); if p.has_scratch() { s.push_str(&persistent_prologue(CoreDialect::Cuda, p.class.scratch_words_per_lane())); } else { @@ -890,26 +1004,37 @@ pub fn cuda_kernel_at(p: &Program, memhard: Option<&MixParams>, dataset_log2: u3 s.push_str(" return cudaGetLastError();\n"); s.push_str("}\n"); s.push('\n'); + if p.has_hot() { + s.push_str("cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments) {\n"); + s.push_str(" if (nSegments == 0u) return cudaErrorInvalidValue;\n"); + s.push_str(" uint32_t block = 256u;\n"); + s.push_str(" uint32_t grid = (nSegments + block - 1u) / block;\n"); + s.push_str(" igneum_hot_fill<<>>(hot, nSegments);\n"); + s.push_str(" return cudaGetLastError();\n"); + s.push_str("}\n"); + s.push('\n'); + } } + let (hot_decl, hot_pass) = if p.has_hot() { (" const uint32_t* hot,", " hot,") } else { ("", "") }; if p.has_scratch() { s.push_str("// Variant 5: the wrapper launches `warps` persistent warps over `nonces / 32` units (host.cu does not use it).\n"); - s.push_str("cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n"); + s.push_str(&format!("cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,{hot_decl}\n")); s.push_str(" uint32_t nonces, uint32_t blockWarps, uint32_t* scratch, uint32_t warps, uint32_t salt) {\n"); s.push_str(" if (blockWarps == 0u || blockWarps > 32u || warps == 0u || (warps % blockWarps) != 0u) return cudaErrorInvalidValue;\n"); s.push_str(" uint32_t block = 32u * blockWarps;\n"); s.push_str(" if (nonces == 0u || (nonces % (32u * warps)) != 0u) return cudaErrorInvalidValue;\n"); - s.push_str(" igneum_hash<<>>(ds, out, baseNonce, mask, scratch, nonces / 32u, salt);\n"); + s.push_str(&format!(" igneum_hash<<>>(ds, out, baseNonce, mask,{hot_pass} scratch, nonces / 32u, salt);\n")); s.push_str(" return cudaGetLastError();\n"); s.push_str("}\n"); } else { - s.push_str( - "cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n", - ); + s.push_str(&format!( + "cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,{hot_decl}\n", + )); s.push_str(" uint32_t nonces, uint32_t blockWarps) {\n"); s.push_str(" if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue;\n"); s.push_str(" uint32_t block = 32u * blockWarps;\n"); s.push_str(" if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue;\n"); - s.push_str(" igneum_hash<<>>(ds, out, baseNonce, mask);\n"); + s.push_str(&format!(" igneum_hash<<>>(ds, out, baseNonce, mask{});\n", if p.has_hot() { ", hot" } else { "" })); s.push_str(" return cudaGetLastError();\n"); s.push_str("}\n"); } @@ -952,6 +1077,7 @@ pub fn cuda_kernel_bound_at(p: &Program, memhard: Option<&MixParams>, dataset_lo s.push('\n'); s.push_str("struct IgneumInitWords { uint32_t w[8]; };\n"); s.push('\n'); + s.push_str(&hot_define(p)); s.push_str("__device__ __forceinline__ uint32_t splitmix32(uint32_t x) {\n"); s.push_str(" x ^= x >> 16; x *= 0x7feb352du;\n"); s.push_str(" x ^= x >> 15; x *= 0x846ca68bu;\n"); @@ -964,7 +1090,8 @@ pub fn cuda_kernel_bound_at(p: &Program, memhard: Option<&MixParams>, dataset_lo let _ = memhard; // the bound kernel reads the stored dataset in both constructions s.push_str(&scratch_prelude(p, CoreDialect::Cuda)); let scratch_args = if p.has_scratch() { ", uint32_t* scratch, uint32_t groups, uint32_t salt" } else { "" }; - s.push_str(&format!("__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw{scratch_args}) {{\n")); + let hot_args = if p.has_hot() { ", const uint32_t* hot" } else { "" }; + s.push_str(&format!("__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw{hot_args}{scratch_args}) {{\n")); if p.has_scratch() { s.push_str(&persistent_prologue(CoreDialect::Cuda, p.class.scratch_words_per_lane())); } else { @@ -993,25 +1120,26 @@ pub fn cuda_kernel_bound_at(p: &Program, memhard: Option<&MixParams>, dataset_lo } s.push_str("}\n"); s.push('\n'); + let (hot_decl, hot_pass) = if p.has_hot() { (" const uint32_t* hot,", ", hot") } else { ("", "") }; if p.has_scratch() { s.push_str("// Variant 5: the wrapper launches `warps` persistent warps over `nonces / 32` units (host.cu does not use it).\n"); s.push_str("cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n"); - s.push_str(" IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps, uint32_t* scratch, uint32_t warps, uint32_t salt) {\n"); + s.push_str(&format!(" IgneumInitWords iw,{hot_decl} uint32_t nonces, uint32_t blockWarps, uint32_t* scratch, uint32_t warps, uint32_t salt) {{\n")); s.push_str(" if (blockWarps == 0u || blockWarps > 32u || warps == 0u || (warps % blockWarps) != 0u) return cudaErrorInvalidValue;\n"); s.push_str(" uint32_t block = 32u * blockWarps;\n"); s.push_str(" if (nonces == 0u || (nonces % (32u * warps)) != 0u) return cudaErrorInvalidValue;\n"); - s.push_str(" igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw, scratch, nonces / 32u, salt);\n"); + s.push_str(&format!(" igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw{hot_pass}, scratch, nonces / 32u, salt);\n")); s.push_str(" return cudaGetLastError();\n"); s.push_str("}\n"); } else { s.push_str( "cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n", ); - s.push_str(" IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) {\n"); + s.push_str(&format!(" IgneumInitWords iw,{hot_decl} uint32_t nonces, uint32_t blockWarps) {{\n")); s.push_str(" if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue;\n"); s.push_str(" uint32_t block = 32u * blockWarps;\n"); s.push_str(" if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue;\n"); - s.push_str(" igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw);\n"); + s.push_str(&format!(" igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw{hot_pass});\n")); s.push_str(" return cudaGetLastError();\n"); s.push_str("}\n"); } @@ -1056,6 +1184,7 @@ fn opencl_instr_lines(p: &Program, dataset_log2: u32) -> String { Op::Load => format!("{d} = {d} ^ ds[{}];", load_index_expr(CoreDialect::OpenCl, era.as_ref(), ins, &a, dataset_log2)), Op::WLoad => format!("{{ uint t_; IGNEUM_BCAST0(t_, {a}); {d} = {d} ^ ds[(t_ & wmask) + lane]; }}"), Op::Scratch => scratch_stmt(CoreDialect::OpenCl, &d, &a, p.class.scratch_slot_mask()), + Op::Hot => hot_stmt(CoreDialect::OpenCl, &d, &a), }; s.push_str(&format!(" {line} // {k} {}\n", ins.op.name())); } @@ -1077,7 +1206,8 @@ pub fn opencl_kernel_bound_at(p: &Program, memhard: Option<&MixParams>, dataset_ "// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash.\n", ); let scratch_args = if p.has_scratch() { ", __global uint* scratch, uint groups, uint salt" } else { "" }; - s.push_str(&format!("IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw{scratch_args}) {{\n")); + let hot_args = if p.has_hot() { ", __global const uint* hot" } else { "" }; + s.push_str(&format!("IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw{hot_args}{scratch_args}) {{\n")); let (setup, unit_loop) = persistent_prologue_parts(CoreDialect::OpenCl, p.class.scratch_words_per_lane()); if p.has_scratch() { s.push_str(&setup); @@ -1184,6 +1314,7 @@ pub fn opencl_kernel_at(p: &Program, memhard: Option<&MixParams>, dataset_log2: s.push_str("#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }\n"); s.push_str("#endif\n"); s.push('\n'); + s.push_str(&hot_define(p)); s.push_str("static inline uint splitmix32(uint x) {\n"); s.push_str(" x ^= x >> 16; x *= 0x7feb352du;\n"); s.push_str(" x ^= x >> 15; x *= 0x846ca68bu;\n"); @@ -1215,7 +1346,17 @@ pub fn opencl_kernel_at(p: &Program, memhard: Option<&MixParams>, dataset_log2: s.push_str(&build_store(layout, CoreDialect::OpenCl, "ds", "t")); s.push_str(" }\n"); s.push_str("}\n"); + if p.has_hot() { + s.push_str(&emit_hot_core(p, CoreDialect::OpenCl)); + s.push_str("// Hot table: one work-item per segment.\n"); + s.push_str("__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) {\n"); + s.push_str(" uint seg = (uint)get_global_id(0);\n"); + s.push_str(" if (seg < nSegments) ht_segment(hot, seg);\n"); + s.push_str("}\n"); + } s.push('\n'); + } else if p.has_hot() { + panic!("a hot-table pack needs the memory-hard dataset (the hot fill shares its ChaCha core)"); } else { s.push_str("// dataset[i] = ds_elem(i, d0, d1) for i < n. Same closed form as the Metal igneum_fill kernel.\n"); s.push_str("__kernel void igneum_fill(__global uint* ds, uint n, uint d0, uint d1) {\n"); @@ -1229,7 +1370,8 @@ pub fn opencl_kernel_at(p: &Program, memhard: Option<&MixParams>, dataset_log2: s.push_str("// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).\n"); s.push_str(&scratch_prelude(p, CoreDialect::OpenCl)); let scratch_args = if p.has_scratch() { ", __global uint* scratch, uint groups, uint salt" } else { "" }; - s.push_str(&format!("IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask{scratch_args}) {{\n")); + let hot_args = if p.has_hot() { ", __global const uint* hot" } else { "" }; + s.push_str(&format!("IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask{hot_args}{scratch_args}) {{\n")); let (setup, unit_loop) = persistent_prologue_parts(CoreDialect::OpenCl, p.class.scratch_words_per_lane()); if p.has_scratch() { s.push_str(&setup); @@ -1330,6 +1472,7 @@ pub fn program_header(p: &Program, day: &str, ds: &DatasetSource) -> String { s.push_str(&class_header_lines(p)); s.push_str(&scratch_header_lines(p)); s.push_str(&era_header_lines(p)); + s.push_str(&hot_header_lines(p)); s.push_str("// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h)\n"); s.push_str(&format!("#define IGNEUM_DATASET_MODE {}\n", if memhard.is_some() { 1 } else { 0 })); s.push('\n'); @@ -1354,18 +1497,22 @@ pub fn program_header(p: &Program, day: &str, ds: &DatasetSource) -> String { s.push_str("// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError().\n"); s.push_str("cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments);\n"); s.push_str("cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems);\n"); + if p.has_hot() { + s.push_str("cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments);\n"); + } } else { s.push_str("#ifndef IGNEUM_NO_CUDA\n"); s.push_str("// Defined in kernel.cu. Both launch on the default stream and return cudaGetLastError().\n"); s.push_str("cudaError_t igneum_launch_fill(uint32_t* ds, uint32_t nWords, uint32_t d0, uint32_t d1);\n"); } + let hot_decl = if p.has_hot() { " const uint32_t* hot," } else { "" }; if p.has_scratch() { - s.push_str("cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n"); + s.push_str(&format!("cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,{hot_decl}\n")); s.push_str(" uint32_t nonces, uint32_t blockWarps, uint32_t* scratch, uint32_t warps, uint32_t salt);\n"); } else { - s.push_str( - "cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n", - ); + s.push_str(&format!( + "cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,{hot_decl}\n", + )); s.push_str(" uint32_t nonces, uint32_t blockWarps);\n"); } s.push_str("cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);\n"); @@ -1392,6 +1539,10 @@ pub fn cuda_memhard_header(p: &Program, mp: &MixParams) -> String { s.push_str("#define IGNEUM_HD static inline\n"); s.push_str("#endif\n"); s.push_str(&emit_memhard_core_layout(mp, CoreDialect::Cuda, p.class.layout())); + if p.has_hot() { + s.push('\n'); + s.push_str(&emit_hot_core(p, CoreDialect::Cuda)); + } s } @@ -1413,6 +1564,11 @@ pub struct PackVectors { pub cache_fnv: u64, /// The cache is 2^cache_log2_words words (memory-hard only; 26 under version 2) pub cache_log2_words: u32, + /// Hot table (hot packs only): head line, last line, FNV-1a 64 over the whole table + pub has_hot: bool, + pub hot_head: Vec, + pub hot_last: Vec, + pub hot_fnv: u64, } /// The base nonces of the three vector warps every pack carries. @@ -1487,6 +1643,18 @@ pub fn vectors_header( s.push_str("};\n"); s.push_str(&format!("static const uint64_t IGNEUM_CACHE_FNV64 = {};\n", hex64(v.cache_fnv))); } + if v.has_hot { + s.push_str("// Hot table self-test (docs/plans/hot-table.md): hot[0..15], the last 16 words, and FNV-1a 64 over all IGNEUM_HOT_WORDS words.\n"); + s.push_str("static const uint32_t IGNEUM_HOT_HEAD[16] = {\n"); + s.push_str(&format!(" {},\n", join_hex(&v.hot_head[..8]))); + s.push_str(&format!(" {}\n", join_hex(&v.hot_head[8..16]))); + s.push_str("};\n"); + s.push_str("static const uint32_t IGNEUM_HOT_LAST[16] = {\n"); + s.push_str(&format!(" {},\n", join_hex(&v.hot_last[..8]))); + s.push_str(&format!(" {}\n", join_hex(&v.hot_last[8..16]))); + s.push_str("};\n"); + s.push_str(&format!("static const uint64_t IGNEUM_HOT_FNV64 = {};\n", hex64(v.hot_fnv))); + } s } @@ -1557,6 +1725,21 @@ pub fn program_json(p: &Program, day: &str, ds: &DatasetSource) -> String { s.push_str(" \"program_id_suffix\": \"'era/' || allowed[3] || width_words || stride_mul_le32 || stride_rot_le32 || interleave[4]\"\n"); s.push_str(" },\n"); } + if let Some(h) = p.class.hot { + let hk = hot_key(&p.seed_bytes); + s.push_str(&format!( + " \"hot_table\": {{\"mb\": {}, \"words\": {}, \"segments\": {}, \"slots\": {}, \"form\": {}, \"dataset_slots\": {}, \"hot_loads_per_hash\": {}, \"key\": [{}], \"key_derivation\": \"seed_words_from_bytes('igneum-hot/' || seed_bytes), the same for every attempt of the epoch\", \"tag\": [{}], \"chain\": \"the cache chain of dataset.cache with the hot key and tag: in_j = prev ^ (sigma || key || seg || j || tag); line_j = block(in_j); prev_0 = 0\", \"load\": \"dst = dst ^ hot[mulhi(src, words)] (the high 32 bits of the 64-bit product; the index lies in [0, words) for any size)\", \"slots_rule\": \"the first k load slots in the Fisher-Yates draw order after the scratch slots (a uniform k-subset); no extra draw, so with version 2 widths and no scratch the program is the version 2 program with k loads redirected\", \"acceptance_stand_in\": \"dataset_elem(mulhi(src, words), seed_words[2], seed_words[3])\", \"program_id\": \"the read-width id with 'hot/' || mb || k appended\", \"spec\": \"docs/plans/hot-table.md\"}},\n", + h.mb, + hot_words(h.mb as u32), + hot_segments(h.mb as u32), + h.k, + jstr(if h.added { "added: k load slots added beside the class's, the dataset loads unchanged" } else { "replaced: k of the class's load slots read the table" }), + p.class.dataset_slots(), + p.hot_loads_per_hash(), + join_jhex(&hk), + join_jhex(&HOT_TAG) + )); + } } s.push_str(&format!( " \"op_mix\": {{{}}},\n", @@ -1582,7 +1765,11 @@ pub fn program_json(p: &Program, day: &str, ds: &DatasetSource) -> String { " \"shfl\": \"dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp\",\n", ); s.push_str(" \"load\": \"dst = dst ^ dataset[src & dataset.mask]\",\n"); - s.push_str(" \"wload\": \"base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)\"\n"); + s.push_str(" \"wload\": \"base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)\""); + if p.has_hot() { + s.push_str(",\n \"hot\": \"dst = dst ^ hot[mulhi(src, hot_table.words)] (hot-table experiment)\""); + } + s.push('\n'); s.push_str(" },\n"); s.push_str(" \"dataset\": {\n"); s.push_str(&format!(" \"log2_words\": {dataset_log2},\n")); @@ -1723,7 +1910,13 @@ pub fn vectors_json( if memhard { s.push_str(&format!(",\n \"cache_head\": [{}],\n", join_jhex(&v.cache_head))); s.push_str(&format!(" \"cache_last_line\": [{}],\n", join_jhex(&v.cache_last))); - s.push_str(&format!(" \"cache_fnv1a64\": {}\n", jhex64(v.cache_fnv))); + s.push_str(&format!(" \"cache_fnv1a64\": {}", jhex64(v.cache_fnv))); + if v.has_hot { + s.push_str(&format!(",\n \"hot_head\": [{}],\n", join_jhex(&v.hot_head))); + s.push_str(&format!(" \"hot_last_line\": [{}],\n", join_jhex(&v.hot_last))); + s.push_str(&format!(" \"hot_fnv1a64\": {}", jhex64(v.hot_fnv))); + } + s.push('\n'); } else { s.push('\n'); } @@ -1773,6 +1966,13 @@ pub fn export_pack(epoch: &Epoch, day: &str, source: &str) -> Pack { v.cache_fnv = m.cache.fnv1a64(); v.cache_log2_words = m.shape().cache_log2_words; } + if let Some(h) = &ds.hot { + let w = h.words(); + v.has_hot = true; + v.hot_head = w[..16].to_vec(); + v.hot_last = w[w.len() - 16..].to_vec(); + v.hot_fnv = h.fnv1a64(); + } let is_mh = memhard.is_some(); let mut files = vec![ ("program.json".to_string(), program_json(p, day, ds)), @@ -1789,7 +1989,7 @@ pub fn export_pack(epoch: &Epoch, day: &str, source: &str) -> Pack { ]; if let Some(mp) = memhard { files.push(("memhard.h".to_string(), cuda_memhard_header(p, mp))); - files.push(("memhard.metal".to_string(), metal_memhard_layout(mp, p.class.layout()))); + files.push(("memhard.metal".to_string(), metal_memhard_for(p, mp))); } Pack { files, bases, outs, vectors: v } } diff --git a/igneum-pow/src/generator.rs b/igneum-pow/src/generator.rs index 7d5bbac4..0c22b784 100644 --- a/igneum-pow/src/generator.rs +++ b/igneum-pow/src/generator.rs @@ -31,6 +31,12 @@ //! an era-fixed mix. The default class [`LoadClass::V2`] is the generator above, draw for draw and byte for byte; //! every other class takes one extra draw per instruction (the width roll), so its program stream differs from //! version 2 and its program id carries the class. +//! +//! Hot-table experiment (5 October 2026, Counter ASIC 2.0 layer 5, `docs/plans/hot-table.md`; NOT the lottery hash, +//! behind [`LoadClass::hot`]): `k` of the load slots read a second table `H` of `S` MiB derived from the epoch seed +//! ([`crate::memhard::HotTable`]) at `H[mulhi(src, words)]` with the plain one-word fold. The hot slots are the +//! first `k` drawn load slots after the scratch slots (a uniform `k`-subset, no extra draw), so a class with the +//! version 2 widths and no scratch takes the version 2 stream exactly ([`LoadClass::takes_width_roll`]). use crate::accept::{check, Reject}; use crate::seed::{fnv1a64, program_rng, seed_words_from_bytes, SplitMix64}; @@ -69,6 +75,9 @@ pub enum Op { /// Scratch read-modify-write (read-width experiment, variant 5, 5 October 2026): a 16-byte slot of the lane's /// own 32 KiB of the warp's 1 MiB scratch, read, folded into dst, rewritten. Never emitted by version 2. Scratch, + /// Hot-table load (hot-table experiment, 5 October 2026): `dst = dst XOR H[mulhi(src, HOT_WORDS)]`, one word + /// of the epoch's `S` MiB table. Never emitted by version 2. + Hot, } impl Op { @@ -88,6 +97,7 @@ impl Op { Op::Load => "load", Op::WLoad => "wload", Op::Scratch => "scratch", + Op::Hot => "hot", } } @@ -106,6 +116,7 @@ impl Op { "load" => Op::Load, "wload" => Op::WLoad, "scratch" => Op::Scratch, + "hot" => Op::Hot, _ => return None, }) } @@ -113,13 +124,13 @@ impl Op { /// An injecting op: bijective in `dst` and bringing another register (or the dataset) in. The acceptance /// rule's part (b) requires one such write per register. pub fn injects(self) -> bool { - matches!(self, Op::Add | Op::Sub | Op::Xor | Op::Mad | Op::Shfl | Op::Load | Op::WLoad | Op::Scratch) + matches!(self, Op::Add | Op::Sub | Op::Xor | Op::Mad | Op::Shfl | Op::Load | Op::WLoad | Op::Scratch | Op::Hot) } /// A memory operation: the fresh-source rule, the acceptance tests and the load count treat the scratch - /// read-modify-write as a load (it is one of the program's 128 memory operations). + /// read-modify-write and the hot-table load as loads (each is one of the program's 128 memory operations). pub fn is_load(self) -> bool { - matches!(self, Op::Load | Op::WLoad | Op::Scratch) + matches!(self, Op::Load | Op::WLoad | Op::Scratch | Op::Hot) } } @@ -202,6 +213,9 @@ pub struct LoadClass { /// Era layout (`docs/plans/era-layout.md`): `Some` turns on the strided, windowed load address and the /// interleaved dataset mapping with the parameters drawn from the era seed. `None` for every other class. pub era: Option, + /// Hot table (`docs/plans/hot-table.md`, measured 5 October 2026 and not adopted): `Some(HotClass { mb, k, added })` + /// turns `k` load slots into reads of an `mb` MiB epoch table. `None` for every other class, class v3 included. + pub hot: Option, } /// The parameters one era draws from its seed `E_n` (`docs/plans/era-layout.md` section 1.1, the proposed text of @@ -316,6 +330,18 @@ pub fn era_draw(era_bytes: &[u8], allowed: &[u8]) -> EraParams { EraParams { words, allowed: al, width_words, stride_mul, stride_rot, pos } } +/// The hot table of a class: `mb` MiB (32, 64 or 96 in the experiment) and `k` hot slots. Two forms: `replaced` +/// (`added = false`): `k` of the 16 load slots read the table, 16 - k dataset loads; `added` (`added = true`, +/// coordinator's form of 5 October 2026 against the on-die-cache recompute chip): the program has 16 + k load slots, +/// the `k` hot ones drawn among them, so the 16 dataset loads and the 4,096-item verifier bound are unchanged and the +/// hot loads are extra work (a cache hit on a GPU, SRAM and a read on a chip). +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct HotClass { + pub mb: u8, + pub k: u8, + pub added: bool, +} + /// Scratch geometry (variant 5): 16-byte slots, lane-major, 32 lanes per warp; `scratch_kb` KiB per warp gives /// `scratch_kb x 2` slots per lane (32 KiB: 64 slots, 128 KiB: 256 slots). pub const SCRATCH_SLOT_BYTES: usize = 16; @@ -339,13 +365,13 @@ impl LoadClass { impl LoadClass { /// Generator version 2 as adopted on 4 October 2026: 16 loads of one word. The lottery hash. pub const V2: LoadClass = - LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 1, growth: false, era: None }; + LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 1, growth: false, era: None, hot: None }; /// The construction decided for program class v3 on 5 October 2026 (Counter ASIC 2.0, `docs/plans/mixer-x4.md`): /// version 2 loads (16 slots of one word, no scratch, no width roll, so the program stream is version 2's), the /// mixer applied 4 times per round, and the cache growth rule. Name "mx4". pub const MX4: LoadClass = - LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 4, growth: true, era: None }; + LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 4, growth: true, era: None, hot: None }; /// The era class over `base` (`docs/plans/era-layout.md`): the parameters drawn by [`era_draw`]; when `allowed` /// has more than one width the drawn width becomes the class mix (every load that width), otherwise the base @@ -396,6 +422,50 @@ impl LoadClass { LoadClass { scratch: Some(k), scratch_kb: kb, ..LoadClass::V2 } } + /// Hot table, replaced form: version 2 widths, 16 load slots of which `k` read an `mb` MiB epoch table (`hot64k4`). + pub fn hot(mb: u8, k: u8) -> LoadClass { + LoadClass::V2.with_hot(mb, k) + } + + /// Hot table, added form: version 2 widths, 16 + `k` load slots of which `k` read the table (`hot64k4a`), the 16 + /// dataset loads unchanged. + pub fn hot_added(mb: u8, k: u8) -> LoadClass { + LoadClass::V2.with_hot_added(mb, k) + } + + /// This class with a hot table in the replaced form (composes with a width mix or a scratch: the hot slots are + /// drawn after the scratch slots, and `scratch + hot` must fit the slot count). + pub fn with_hot(mut self, mb: u8, k: u8) -> LoadClass { + assert!(mb >= 1, "a hot table needs at least 1 MiB"); + assert!(self.scratch_slots() + k as usize <= self.load_slots as usize, "scratch and hot slots exceed the load slots"); + self.hot = Some(HotClass { mb, k, added: false }); + self + } + + /// This class with a hot table in the added form: `k` load slots are added to the class's and read the table. + pub fn with_hot_added(mut self, mb: u8, k: u8) -> LoadClass { + assert!(mb >= 1, "a hot table needs at least 1 MiB"); + assert!((self.load_slots as usize) + (k as usize) < INSTR_COUNT, "added hot slots exceed the instruction count"); + self.load_slots += k; + self.hot = Some(HotClass { mb, k, added: true }); + self + } + + /// Hot slots per program (0 without a hot table). + pub fn hot_slots(&self) -> usize { + self.hot.map(|h| h.k as usize).unwrap_or(0) + } + + /// Hot slots that were added to the slot count (0 for the replaced form and without a hot table). + pub fn hot_added_slots(&self) -> usize { + self.hot.map(|h| if h.added { h.k as usize } else { 0 }).unwrap_or(0) + } + + /// Load slots that read the dataset: the slot count less the scratch and hot slots. + pub fn dataset_slots(&self) -> usize { + self.load_slots as usize - self.scratch_slots() - self.hot_slots() + } + /// This class with the mixer multiplier `m` (1, 2, 4, 8 or 16) and the cache growth rule on or off. pub fn with_mixer(self, mixer_mult: u8, growth: bool) -> LoadClass { assert!(mixer_mult >= 1 && mixer_mult <= 16 && mixer_mult.is_power_of_two(), "mixer multiplier must be 1, 2, 4, 8 or 16"); @@ -411,7 +481,7 @@ impl LoadClass { /// roll, so its program stream is the version 2 stream draw for draw (the mixer and the cache are properties of /// the dataset, not of the program). pub fn v2_loads(&self) -> bool { - self.mix == [100, 0, 0] && self.load_slots as usize == LOAD_SLOTS && self.scratch.is_none() + self.mix == [100, 0, 0] && self.load_slots as usize == LOAD_SLOTS + self.hot_added_slots() && self.scratch.is_none() } /// Whether every instruction takes the tenth draw (the width roll): every class whose loads are not version 2's. @@ -448,6 +518,36 @@ impl LoadClass { /// The load part of a class name (no mixer suffix). fn parse_loads(s: &str) -> Option { + // "+hotk[a]" composes a hot table with any class; "hotk[a]" alone is the version 2 base; + // the "a" suffix is the added form (k slots added to the class's), without it the replaced form + if let Some((base, hot)) = s.split_once("+hot") { + let (added, hot) = match hot.strip_suffix('a') { Some(h) => (true, h), None => (false, hot) }; + let (mb, k) = hot.split_once('k')?; + let (mb, k): (u8, u8) = (mb.parse().ok()?, k.parse().ok()?); + let c = LoadClass::parse_loads(base)?; + if mb == 0 || k == 0 { + return None; + } + if added { + if c.load_slots as usize + k as usize >= INSTR_COUNT { + return None; + } + return Some(c.with_hot_added(mb, k)); + } + if c.scratch_slots() + k as usize > c.load_slots as usize { + return None; + } + return Some(c.with_hot(mb, k)); + } + if let Some(rest) = s.strip_prefix("hot") { + let (added, rest) = match rest.strip_suffix('a') { Some(h) => (true, h), None => (false, rest) }; + let (mb, k) = rest.split_once('k')?; + let (mb, k): (u8, u8) = (mb.parse().ok()?, k.parse().ok()?); + if mb == 0 || k == 0 || k as usize > LOAD_SLOTS { + return None; + } + return Some(if added { LoadClass::hot_added(mb, k) } else { LoadClass::hot(mb, k) }); + } if let Some(rest) = s.strip_prefix("scr") { let (k, kb) = rest.split_once('k')?; let k: u8 = k.parse().ok()?; @@ -492,12 +592,23 @@ impl LoadClass { /// "v2", "w4", "w16", "w64", "w64x4", "mix50-35-15", "mix25-50-25x8", "scr4k32"; "mx4" for the v3 construction; /// any other mixer setting appends "m" and, with the growth rule, "g" ("v2m2", "w16m4g"). /// An era class is the base name with "-era" appended ("w4-era401998a5", "mx4-era..."). + /// A hot class appends "hotk[a]" ("hot64k4", "scr4k32+hot64k4a"; measured and not adopted). pub fn name(&self) -> String { if let Some(e) = self.era { let base = LoadClass { era: None, ..*self }; let base_name = if base.is_v2() { "w4".to_string() } else { base.name() }; return format!("{base_name}-era{}", e.label()); } + let base = LoadClass { hot: None, load_slots: self.load_slots - self.hot_added_slots() as u8, ..*self }.base_name(); + match self.hot { + None => base, + Some(h) if base == "v2" => format!("hot{}k{}{}", h.mb, h.k, if h.added { "a" } else { "" }), + Some(h) => format!("{base}+hot{}k{}{}", h.mb, h.k, if h.added { "a" } else { "" }), + } + } + + /// The name without the hot table. + fn base_name(&self) -> String { if self.is_v2() { return "v2".to_string(); } @@ -578,7 +689,7 @@ pub enum ProgramClass { /// placeholder of the seam (w16) is replaced here; nothing else in the seam names the class. /// Composed on 5 October 2026 (branch ca2-era): the era layout of `docs/plans/era-layout.md` is drawn inside this class by /// [`generate_from_seed_bytes_program_class`] (`LoadClass::era(V3_CLASS, era, &V3_ALLOWED)`); here `era` is `None`. -pub const V3_CLASS: LoadClass = LoadClass { era: None, ..LoadClass::MX4 }; +pub const V3_CLASS: LoadClass = LoadClass { era: None, hot: None, ..LoadClass::MX4 }; /// The width set class v3's era draw chooses from: 4 bytes only (the read-width decision of 5 October 2026; the /// draw is consumed, so widening the set at genesis keeps the derivation). @@ -667,6 +778,17 @@ impl Program { pub fn has_scratch(&self) -> bool { self.class.scratch.is_some() } + /// Hot-table loads per hash (one 4-byte word each). + pub fn hot_loads_per_hash(&self) -> usize { + self.instrs.iter().filter(|i| i.op == Op::Hot).count() * ITERATIONS + } + pub fn has_hot(&self) -> bool { + self.class.hot.is_some() + } + /// The hot table's words (0 without one). + pub fn hot_words(&self) -> u32 { + self.class.hot.map(|h| crate::memhard::hot_words(h.mb as u32)).unwrap_or(0) + } /// Width histogram of the loads, in words: (1, 4, 16) counts. pub fn width_counts(&self) -> [usize; 3] { let mut c = [0usize; 3]; @@ -677,9 +799,11 @@ impl Program { } c } - /// Distinct dataset items a 32-lane warp touches per hash: 32 per plain load, 2 per wide load. + /// Distinct dataset items a 32-lane warp touches per hash: 32 per plain load, 2 per wide load; scratch and + /// hot loads touch none (so the added form keeps 4,096). pub fn items_per_warp(&self) -> usize { - (self.loads_per_hash() - self.wide_loads_per_hash()) * 32 + self.wide_loads_per_hash() * 2 + (self.loads_per_hash() - self.wide_loads_per_hash() - self.scratch_ops_per_hash() - self.hot_loads_per_hash()) * 32 + + self.wide_loads_per_hash() * 2 } /// Op histogram, count descending then name ascending. pub fn histogram(&self) -> Vec<(&'static str, usize)> { @@ -760,6 +884,14 @@ pub fn program_id_class(generator: u32, seed: &[u32; 8], attempt: u32, class: &L b.extend_from_slice(b"era/"); b.extend_from_slice(&e.id_bytes()); } + if let Some(h) = class.hot { + b.extend_from_slice(b"hot/"); + b.push(h.mb); + b.push(h.k); + if h.added { + b.extend_from_slice(b"added"); + } + } fnv1a64(&b) } @@ -841,6 +973,11 @@ pub fn candidate_from_words_class( for &slot in &p[..class.scratch_slots()] { is_scratch[slot as usize] = true; } + // Hot table: the next k drawn load slots after the scratch slots are hot loads (again a uniform subset). + let mut is_hot = [false; INSTR_COUNT]; + for &slot in &p[class.scratch_slots()..class.scratch_slots() + class.hot_slots()] { + is_hot[slot as usize] = true; + } // (2) The instructions. `fresh[r]`: r was written by an earlier instruction and no load has read it since. let mut fresh = [false; 8]; let mut instrs = Vec::with_capacity(INSTR_COUNT); @@ -855,7 +992,13 @@ pub fn candidate_from_words_class( roll -= w; } if is_load[k] { - op = if is_scratch[k] { Op::Scratch } else { Op::Load }; + op = if is_scratch[k] { + Op::Scratch + } else if is_hot[k] { + Op::Hot + } else { + Op::Load + }; } let dst = rng.below(8); let src = if op.is_load() { @@ -1434,6 +1577,107 @@ mod tests { } } + /// Hot-table experiment: names and ids; a hot class with version 2 widths and no scratch takes the version 2 + /// stream, so its program is the version 2 program with k of the load slots turned into hot loads; the hot + /// table composes with a scratch class. + #[test] + fn hot_classes() { + assert_eq!(LoadClass::parse("hot64k4"), Some(LoadClass::hot(64, 4))); + assert_eq!(LoadClass::hot(64, 4).name(), "hot64k4"); + assert_eq!(LoadClass::parse("hot96k4").unwrap().name(), "hot96k4"); + assert_eq!(LoadClass::parse("hot0k4"), None); + assert_eq!(LoadClass::parse("hot64k17"), None); + assert_eq!(LoadClass::parse("hot64"), None); + // the added form: 16 + k slots, 16 dataset loads, no width roll, its own name and id + for (mb, k) in [(32u8, 4u8), (64, 4), (96, 4)] { + let c = LoadClass::parse(&format!("hot{mb}k{k}a")).unwrap(); + assert_eq!(c, LoadClass::hot_added(mb, k)); + assert_eq!(c.name(), format!("hot{mb}k{k}a")); + assert_eq!(c.load_slots as usize, 16 + k as usize); + assert_eq!(c.dataset_slots(), 16); + assert!(!c.takes_width_roll()); + assert_ne!(c, LoadClass::hot(mb, k)); + let p = candidate_class("igneum-genesis", b"igneum-genesis", 0, c); + assert_eq!(p.loads_per_hash(), 128 + 8 * k as usize); + assert_eq!(p.hot_loads_per_hash(), 8 * k as usize); + assert_eq!(p.bytes_per_hash(), 512); + assert_eq!(p.items_per_warp(), 4096); + assert_eq!(p.instrs.iter().filter(|i| i.op == Op::Load).count(), 16); + assert_eq!(p.instrs.iter().filter(|i| i.op == Op::Hot).count(), k as usize); + assert!(p.instrs.iter().all(|i| i.width == 1)); + assert_ne!(p.program_id(), candidate_class("igneum-genesis", b"igneum-genesis", 0, LoadClass::hot(mb, k)).program_id()); + } + assert_eq!(LoadClass::parse("scr4k32+hot64k4a").unwrap().name(), "scr4k32+hot64k4a"); + assert_eq!(LoadClass::parse("scr4k32+hot64k4a").unwrap().dataset_slots(), 12); + assert_eq!(LoadClass::parse("hot64k48a"), None); + assert!(!LoadClass::hot(64, 4).is_v2()); + assert!(!LoadClass::hot(64, 4).takes_width_roll()); + assert!(LoadClass::V2.takes_width_roll() == false); + assert!(LoadClass::scratch(4, 32).takes_width_roll()); + assert!(LoadClass::fixed(4, 16).takes_width_roll()); + let v2 = candidate("igneum-genesis", b"igneum-genesis", 0); + let mut ids = std::collections::HashSet::new(); + ids.insert(v2.program_id()); + for (mb, k) in [(32u8, 4u8), (64, 4), (96, 4), (64, 2), (64, 8)] { + let c = LoadClass::hot(mb, k); + let p = candidate_class("igneum-genesis", b"igneum-genesis", 0, c); + assert_eq!(p.class, c); + assert_eq!(p.loads_per_hash(), 128); + assert_eq!(p.hot_loads_per_hash(), 8 * k as usize); + assert_eq!(p.bytes_per_hash(), (16 - k as usize) * 8 * 4); + assert_eq!(p.hot_words(), crate::memhard::hot_words(mb as u32)); + assert!(ids.insert(p.program_id()), "hot{mb}k{k}: program id collides"); + // the version 2 program with k loads redirected: every other field identical, instruction by instruction + let mut hot = 0; + for (a, b) in p.instrs.iter().zip(v2.instrs.iter()) { + if a.op == Op::Hot { + hot += 1; + assert_eq!(b.op, Op::Load, "a hot slot is one of the version 2 load slots"); + assert_eq!((a.dst, a.src, a.src2, a.imm, a.imm2, a.rot, a.bit, a.mask, a.width), (b.dst, b.src, b.src2, b.imm, b.imm2, b.rot, b.bit, b.mask, b.width)); + } else { + assert_eq!(a, b); + } + } + assert_eq!(hot, k as usize); + } + // the same k at two sizes: the same instructions, different ids and tables + let a = candidate_class("igneum-genesis", b"igneum-genesis", 0, LoadClass::hot(32, 4)); + let b = candidate_class("igneum-genesis", b"igneum-genesis", 0, LoadClass::hot(64, 4)); + assert_eq!(a.instrs, b.instrs); + assert_ne!(a.program_id(), b.program_id()); + // composition with a scratch class: scratch slots first, then hot, the rest dataset loads + let c = LoadClass::parse("scr4k32+hot64k4").unwrap(); + assert_eq!(c, LoadClass::scratch(4, 32).with_hot(64, 4)); + assert_eq!(c.name(), "scr4k32+hot64k4"); + assert!(c.takes_width_roll()); + let p = candidate_class("igneum-genesis", b"igneum-genesis", 0, c); + assert_eq!(p.scratch_ops_per_hash(), 32); + assert_eq!(p.hot_loads_per_hash(), 32); + assert_eq!(p.loads_per_hash(), 128); + assert_eq!(p.bytes_per_hash(), 8 * 8 * 4); + assert!(ids.insert(p.program_id())); + assert_eq!(LoadClass::parse("scr12k32+hot8k8"), None, "scratch and hot slots exceed the 16"); + assert_eq!(LoadClass::parse("w16+hot64k4").unwrap().name(), "w16+hot64k4"); + // the hot slots are a uniform subset of the load slots over a population + let mut position_sum = 0usize; + let mut n = 0usize; + for i in 0..200u32 { + let s = format!("igneum-hot-slots/{i}"); + let p = candidate_class(&s, s.as_bytes(), 0, LoadClass::hot(64, 4)); + let loads: Vec = p.instrs.iter().enumerate().filter(|(_, x)| x.op.is_load()).map(|(i, _)| i).collect(); + assert_eq!(loads.len(), 16); + for (rank, &i) in loads.iter().enumerate() { + if p.instrs[i].op == Op::Hot { + position_sum += rank; + n += 1; + } + } + } + assert_eq!(n, 800); + let mean_rank = position_sum as f64 / n as f64; + assert!((mean_rank - 7.5).abs() < 0.6, "hot slots sit anywhere among the 16 loads: mean rank {mean_rank}"); + } + #[test] fn generate_returns_an_accepted_program() { let p = generate("igneum-genesis"); diff --git a/igneum-pow/src/main.rs b/igneum-pow/src/main.rs index 714ed336..cf82ab76 100644 --- a/igneum-pow/src/main.rs +++ b/igneum-pow/src/main.rs @@ -252,6 +252,13 @@ fn bench(a: &Args, mode: DatasetMode) { ); drop(c); } + if let Some(h) = a.class.hot { + // the hot table of the epoch on its own first (one core), then the epoch (which fills it again) + let t0 = Instant::now(); + let t = igneum_pow::memhard::HotTable::for_seed_bytes(a.seed.as_bytes(), h.mb as u32); + let fill_ms = t0.elapsed().as_secs_f64() * 1e3; + println!("hot table: {} MiB filled in {fill_ms:.1} ms on one core ({} chains of 64 ChaCha12 blocks), FNV-1a 64 {:016x}", h.mb, igneum_pow::memhard::hot_segments(h.mb as u32), t.fnv1a64()); + } let t0 = Instant::now(); let (e, _) = epoch_of(a, mode); let build_ms = t0.elapsed().as_secs_f64() * 1e3; diff --git a/igneum-pow/src/memhard.rs b/igneum-pow/src/memhard.rs index dd3cdf92..5a0ecd8a 100644 --- a/igneum-pow/src/memhard.rs +++ b/igneum-pow/src/memhard.rs @@ -29,6 +29,14 @@ pub const CHACHA_ROUNDS: usize = 12; pub const CHACHA_SIGMA: [u32; 4] = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]; /// "Igne", "umMH". pub const CACHE_TAG: [u32; 2] = [0x49676e65, 0x756d4d48]; +/// "Igne", "umHT": the chain tag of the hot table (hot-table experiment, `docs/plans/hot-table.md`). +pub const HOT_TAG: [u32; 2] = [0x49676e65, 0x756d4854]; +/// Domain tag of the hot key: `KH = seed_words_from_bytes("igneum-hot/" || epoch seed bytes)`. +pub const HOT_KEY_TAG: &[u8] = b"igneum-hot/"; +/// Words per MiB of hot table. +pub const HOT_WORDS_PER_MIB: u32 = 1 << 18; +/// Segments (64 chained lines of 16 words, 4 KiB) per MiB of hot table. +pub const HOT_SEGMENTS_PER_MIB: u32 = 256; /// The shape of the item derivation and of the cache: the mixer multiplier and the cache size. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -306,6 +314,12 @@ impl Cache { /// One segment: 64 chained lines written at `cache[seg * 1024 ..]`. /// `in_j = prev XOR (sigma || K || seg || j || tag)`, `line_j = B(in_j)`, `prev_0 = 0`. pub fn fill_segment(words: &mut [u32], seg: usize, key: &[u32; 8]) { + Self::fill_segment_tagged(words, seg, key, &CACHE_TAG) + } + + /// [`Cache::fill_segment`] with an explicit chain tag: [`CACHE_TAG`] for the cache, [`HOT_TAG`] for the hot + /// table of `docs/plans/hot-table.md` (the same chain, another key and tag). + pub fn fill_segment_tagged(words: &mut [u32], seg: usize, key: &[u32; 8], tag: &[u32; 2]) { let base = (seg << CACHE_SEGMENT_LOG2_LINES) * 16; let seg_words = &mut words[base..base + CACHE_LINES_PER_SEGMENT * 16]; let mut prev = [0u32; 16]; @@ -315,8 +329,8 @@ impl Cache { x[4..12].copy_from_slice(key); x[12] = seg as u32; x[13] = j as u32; - x[14] = CACHE_TAG[0]; - x[15] = CACHE_TAG[1]; + x[14] = tag[0]; + x[15] = tag[1]; for i in 0..16 { x[i] ^= prev[i]; } @@ -376,6 +390,85 @@ impl Cache { } } +/// The hot key of an epoch: `seed_words_from_bytes("igneum-hot/" || seed_bytes)`, `seed_bytes` the program seed +/// bytes before any attempt suffix, so every attempt of one epoch shares one table. +pub fn hot_key(seed_bytes: &[u8]) -> [u32; 8] { + let mut b = Vec::with_capacity(HOT_KEY_TAG.len() + seed_bytes.len()); + b.extend_from_slice(HOT_KEY_TAG); + b.extend_from_slice(seed_bytes); + crate::seed::seed_words_from_bytes(&b) +} + +/// Words of a hot table of `mb` MiB. +pub fn hot_words(mb: u32) -> u32 { + mb * HOT_WORDS_PER_MIB +} + +/// Segments of a hot table of `mb` MiB. +pub fn hot_segments(mb: u32) -> u32 { + mb * HOT_SEGMENTS_PER_MIB +} + +/// The hot index of a source word: `mulhi(src, words)`, the high 32 bits of the 64-bit product, in `[0, words)` +/// for any table size (the multiply-shift range reduction of spec 01 section 1.13.3). +#[inline(always)] +pub fn hot_index(src: u32, words: u32) -> u32 { + ((src as u64 * words as u64) >> 32) as u32 +} + +/// The hot table `H` of one epoch (hot-table experiment): `mb` MiB of chained ChaCha12 lines under the hot key, +/// read by the hot load slots as `dst ^= H[hot_index(src, words)]`. The verifier holds it beside the cache. +pub struct HotTable { + pub key: [u32; 8], + pub mb: u32, + words: Vec, +} + +impl HotTable { + /// Fill `mb` MiB under `key` on the calling thread. + pub fn fill(key: [u32; 8], mb: u32) -> HotTable { + assert!(mb >= 1 && mb <= 4096, "hot table size in MiB out of range"); + let n = hot_words(mb) as usize; + let mut words = vec![0u32; n]; + for seg in 0..hot_segments(mb) as usize { + Cache::fill_segment_tagged(&mut words, seg, &key, &HOT_TAG); + } + HotTable { key, mb, words } + } + + /// The table of the epoch whose program seed bytes are `seed_bytes`. + pub fn for_seed_bytes(seed_bytes: &[u8], mb: u32) -> HotTable { + Self::fill(hot_key(seed_bytes), mb) + } + + #[inline(always)] + pub fn n_words(&self) -> u32 { + self.words.len() as u32 + } + + /// `H[i]`. + #[inline(always)] + pub fn at(&self, i: u32) -> u32 { + self.words[i as usize] + } + + /// `H[hot_index(src, words)]`: what a hot load reads for source word `src`. + #[inline(always)] + pub fn word(&self, src: u32) -> u32 { + self.words[hot_index(src, self.n_words()) as usize] + } + + #[inline(always)] + pub fn words(&self) -> &[u32] { + &self.words + } + + /// FNV-1a 64 over the table as little-endian bytes (what `vectors.h` carries as `IGNEUM_HOT_FNV64`). + pub fn fnv1a64(&self) -> u64 { + fnv1a64_words(&self.words) + } +} + /// Derive `ts.len()` items into `out`, all chains interleaved round by round so the cache-line misses of /// independent items overlap in the memory system (`deriveItems` in the Swift). Under multiplier `m` /// (`mp.shape.mixer_mult`) round `r` applies `M` with keys `round_key(r m + j)` for `j = 0 .. m - 1` before its @@ -577,6 +670,40 @@ mod tests { assert_eq!(y, z); } + /// Hot-table experiment: the genesis epoch's table (seed bytes "igneum-genesis") as the hot packs carry it + /// (`proto-cuda/packs-ca2-hot/hot32k4/vectors.json`: hot_head, hot_fnv1a64; the head is the same at every size, + /// a larger table is more segments). The index mapping stays inside the table for any size. + #[test] + fn hot_table_fill_vector_and_index() { + assert_ne!(HOT_TAG, CACHE_TAG); + let h = HotTable::for_seed_bytes(b"igneum-genesis", 32); + assert_eq!(h.n_words(), 1 << 23); + assert_eq!(hot_segments(32), 8192); + assert_eq!( + &h.words()[..16], + &[ + 0x8068cc73, 0x6036ebf9, 0xb604cd25, 0x8ffb840e, 0xc54074a2, 0x285c0695, 0x77512425, 0xc26a58a7, + 0x72c88757, 0xc10fca78, 0x513825dd, 0x30d6ccc8, 0x9a05e7cf, 0xb9533f50, 0x4bac3ba0, 0xa5c19528 + ] + ); + assert_eq!(h.fnv1a64(), 0xc1767ba3ef02719f, "hot32k4 pack, hot_fnv1a64"); + assert_eq!(h.key, hot_key(b"igneum-genesis")); + assert_ne!(h.key, day_key("2026-10-03")); + // a different seed, a different table; the same seed under the cache tag is not the hot table + assert_ne!(HotTable::for_seed_bytes(b"igneum-genesis\x01\x00\x00\x00", 1).words()[..16], h.words()[..16]); + let mut under_cache_tag = vec![0u32; 1024]; + Cache::fill_segment(&mut under_cache_tag, 0, &h.key); + assert_ne!(&under_cache_tag[..16], &h.words()[..16]); + for words in [hot_words(32), hot_words(64), hot_words(96)] { + assert_eq!(hot_index(0, words), 0); + assert!(hot_index(u32::MAX, words) < words); + assert_eq!(hot_index(u32::MAX, words), words - 1); + assert!(hot_index(0x8000_0000, words) == words / 2); + } + assert_eq!(hot_index(0x1234_5678, 1 << 24), 0x1234_5678 >> 8); + assert_eq!(h.word(0x8000_0000), h.at(1 << 22)); + } + #[test] fn first_cache_line_matches_pack() { // vectors.json cache_head for day 2026-10-03: segment 0, line 0, with prev = 0. diff --git a/igneum-pow/src/verify.rs b/igneum-pow/src/verify.rs index 940590c3..ba7338b7 100644 --- a/igneum-pow/src/verify.rs +++ b/igneum-pow/src/verify.rs @@ -2,7 +2,7 @@ //! calls. Dataset words come from the memory-hard cache (default) or from the closed form (old packs). use crate::generator::{generate, generate_class, EraParams, Instr, LoadClass, Op, Program, ProgramClass, ITERATIONS, LANES}; -use crate::memhard::{Layout, MemhardCpu, Shape}; +use crate::memhard::{hot_index, HotTable, Layout, MemhardCpu, Shape}; use crate::seed::day_key; /// The load address of an era program (`docs/plans/era-layout.md` section 1.3): `y = rotl(x * M, R)`, then the @@ -171,6 +171,10 @@ pub struct DatasetSource { /// in packs so any implementation can rebuild the key. Empty when the key was given directly. pub key_bytes: Vec, pub dataset: Dataset, + /// The hot table of the epoch (hot-table experiment, `docs/plans/hot-table.md`): `Some` when the program's + /// class has one; filled by [`Epoch::new_class`] and [`Epoch::from_seed_bytes_class`] from the program's seed + /// bytes. A hot load reads `hot[hot_index(src, words)]`. + pub hot: Option, } impl DatasetSource { @@ -199,7 +203,18 @@ impl DatasetSource { DatasetMode::ClosedForm => Dataset::ClosedForm { d0: key[0], d1: key[1] }, DatasetMode::MemoryHard => Dataset::MemoryHard(MemhardCpu::with_shape(key, shape)), }; - Self { log2_words, mask, key, key_bytes: Vec::new(), dataset } + Self { log2_words, mask, key, key_bytes: Vec::new(), dataset, hot: None } + } + + /// This source with the hot table of the epoch whose program seed bytes are `seed_bytes` (`mb` MiB). + pub fn with_hot(mut self, seed_bytes: &[u8], mb: u32) -> Self { + self.hot = Some(HotTable::for_seed_bytes(seed_bytes, mb)); + self + } + + /// The hot table of a program's class, filled from its seed bytes (none for a class without one). + pub fn attach_hot_for(&mut self, program: &Program) { + self.hot = program.class.hot.map(|h| HotTable::for_seed_bytes(&program.seed_bytes, h.mb as u32)); } /// The shape of the memory-hard construction ([`Shape::V2`] for the closed form, which has none). @@ -309,6 +324,10 @@ pub fn interpret_warp_init(program: &Program, seed: &[u32; 8], base_nonce: u32, let mut val = [0u32; LANES]; let mut scratch = if program.has_scratch() { Some(ScratchModel::new(program.class.scratch_slots_per_lane())) } else { None }; let slot_mask = program.class.scratch_slot_mask(); + if program.has_hot() { + let h = ds.hot.as_ref().expect("a hot-table program needs the epoch's hot table on the dataset source"); + assert_eq!(h.n_words(), program.hot_words(), "the hot table's size is the class's"); + } for _ in 0..ITERATIONS { let sel = r[0]; for ins in &program.instrs { @@ -440,6 +459,14 @@ fn step( Op::Scratch => { // handled by the caller (interpret_warp_init), which owns the unit's scratch model } + Op::Hot => { + // Hot-table experiment: one word of the epoch table at the multiply-shift index, plain xor fold. + let h = ds.hot.as_ref().expect("a hot load needs the hot table"); + let n = h.n_words(); + for lane in 0..LANES { + r[d][lane] ^= h.at(hot_index(r[a][lane], n)); + } + } Op::WLoad => { // Lane 0's register, masked, aligned down to 32 words; lane l reads word base + l. let base = (r[a][0] & mask) & !31; @@ -491,7 +518,11 @@ impl Epoch { /// `memhard::cache_log2_words` for a class with the growth rule; the dataset size is the caller's). pub fn new_class_day(seed: &str, day: &str, mode: DatasetMode, dataset_log2: u32, class: LoadClass, days_since_genesis: u64) -> Self { let shape = Shape::for_class_day(&class, days_since_genesis); - Self { program: generate_class(seed, class), dataset: DatasetSource::new_shape(day, mode, dataset_log2, shape) } + let program = generate_class(seed, class); + let mut dataset = DatasetSource::new_shape(day, mode, dataset_log2, shape); + // hot-table experiment: a hot class fills its table from the seed bytes + dataset.attach_hot_for(&program); + Self { program, dataset } } /// `dataset[w]` as this epoch's program reads it: under the program's layout (era layout; linear for v2). @@ -531,6 +562,7 @@ impl Epoch { let dataset_log2 = if class.growth { crate::memhard::dataset_log2_words(genesis_dataset_log2, days_since_genesis) } else { genesis_dataset_log2 }; let mut dataset = DatasetSource::from_key_shape(key, DatasetMode::MemoryHard, dataset_log2, shape); dataset.key_bytes = day_bytes.to_vec(); + dataset.attach_hot_for(&program); Self { program, dataset } } @@ -751,6 +783,39 @@ mod tests { } } + /// Hot-table experiment: an epoch of a hot class carries the table, hashes deterministically and differs from + /// version 2; the reference interpreter agrees with a hand-stepped hot load; a hot program without its table is + /// refused. + #[test] + fn hot_epochs_hash() { + let e = Epoch::new_class("igneum-genesis", "2026-10-03", DatasetMode::ClosedForm, 20, LoadClass::hot(32, 4)); + let h = e.dataset.hot.as_ref().expect("the epoch fills the hot table"); + assert_eq!(h.n_words(), 1 << 23); + assert_eq!(h.key, crate::memhard::hot_key(b"igneum-genesis")); + let v2 = Epoch::new_class("igneum-genesis", "2026-10-03", DatasetMode::ClosedForm, 20, LoadClass::V2); + let a = e.hash_warp(0); + assert_eq!(a, e.hash_warp(0)); + assert_ne!(a, v2.hash_warp(0)); + assert_ne!(a[0], a[1]); + // the same program under a 64 MiB table reads other words + let e64 = Epoch::new_class("igneum-genesis", "2026-10-03", DatasetMode::ClosedForm, 20, LoadClass::hot(64, 4)); + assert_eq!(e64.program.instrs, e.program.instrs); + assert_ne!(e64.hash_warp(0), a); + // from seed bytes, the chain's shape, with a hot class + let genesis = crate::bind::unhex("edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07").unwrap(); + let ec = Epoch::from_seed_bytes_class(&genesis, &crate::bind::day_bytes(20_730), "devnet", LoadClass::hot(32, 2)); + assert_eq!(ec.dataset.hot.as_ref().unwrap().key, crate::memhard::hot_key(&genesis)); + assert_eq!(ec.hash_warp(0), ec.hash_warp(0)); + } + + #[test] + #[should_panic(expected = "needs the epoch's hot table")] + fn hot_program_without_a_table_is_refused() { + let p = generate_class("igneum-genesis", LoadClass::hot(32, 4)); + let ds = DatasetSource::new("2026-10-03", DatasetMode::ClosedForm, 20); + let _ = hash_warp(&p, 0, &ds); + } + #[test] fn wide_class_epochs_hash() { for name in ["w16", "w64x4", "50,35,15"] { diff --git a/igneum-pow/tests/packs.rs b/igneum-pow/tests/packs.rs index 54cf2692..f03cf643 100644 --- a/igneum-pow/tests/packs.rs +++ b/igneum-pow/tests/packs.rs @@ -12,10 +12,10 @@ use igneum_pow::accept; use igneum_pow::emit::{ - cuda_kernel, cuda_kernel_bound, cuda_memhard_header, export_pack, metal_memhard, metal_program, + cuda_kernel, cuda_kernel_bound, cuda_memhard_header, export_pack, metal_memhard, metal_memhard_for, metal_program, metal_program_bound, opencl_kernel, opencl_kernel_bound, program_header, program_json, LoadSource, }; -use igneum_pow::generator::{generate_from_seed_bytes, generate_from_seed_bytes_program_class, LoadClass, Op, ProgramClass, GENERATOR_VERSION, GENERATOR_VERSION_V3, LOAD_SLOTS, V3_CLASS}; +use igneum_pow::generator::{generate_from_seed_bytes, generate_from_seed_bytes_class, generate_from_seed_bytes_program_class, LoadClass, Op, ProgramClass, GENERATOR_VERSION, GENERATOR_VERSION_V3, LOAD_SLOTS, V3_CLASS}; use igneum_pow::memhard::{Shape, CACHE_WORDS}; use igneum_pow::verify::{DatasetMode, DatasetSource, Epoch}; use serde_json::Value; @@ -675,3 +675,202 @@ fn era_dataset_is_a_prefix_at_smaller_sizes() { } } } + +// --------------------------------------------------------------------------------------------------------- +// Hot-table experiment (5 October 2026, docs/plans/hot-table.md): the five packs under proto-cuda/packs-ca2-hot/ are +// pinned the same way (program, vectors, every emitted file byte for byte), plus the hot table's fingerprint and the +// one-form load check: exactly 16 - k masked dataset loads and exactly k hot loads in every hash kernel. +// --------------------------------------------------------------------------------------------------------- + +const HOT_PACKS: [&str; 8] = ["hot32k4", "hot64k4", "hot96k4", "hot64k2", "hot64k8", "hot32k4a", "hot64k4a", "hot96k4a"]; + +fn hot_packs_dir() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda/packs-ca2-hot") +} + +fn hread(pack: &str, file: &str) -> String { + let p = hot_packs_dir().join(pack).join(file); + std::fs::read_to_string(&p).unwrap_or_else(|e| panic!("read {}: {e}", p.display())) +} + +fn hjson(pack: &str, file: &str) -> Value { + serde_json::from_str(&hread(pack, file)).unwrap_or_else(|e| panic!("{pack}/{file}: {e}")) +} + +/// The epoch of a hot pack from program.json alone: the class from `load_class`, the program from the seed bytes, +/// the dataset from the day bytes, the hot table from the seed bytes (what `Epoch::from_seed_bytes_class` does). +fn hepoch(pack: &str) -> &'static Epoch { + static E: OnceLock> = OnceLock::new(); + let all = E.get_or_init(|| { + HOT_PACKS + .iter() + .map(|p| { + let j = hjson(p, "program.json"); + let seed = j["seed"].as_str().unwrap(); + let seed_bytes = unhex(&j["seed_bytes"]); + let day_bytes = unhex(&j["dataset"]["day_bytes"]); + assert_eq!(j["dataset_mode"].as_str().unwrap(), "memory-hard"); + let class = LoadClass::parse(j["load_class"].as_str().unwrap()).unwrap(); + assert_eq!(class.name(), *p, "the pack directory is the class name"); + let log2 = j["dataset"]["log2_words"].as_u64().unwrap() as u32; + let program = generate_from_seed_bytes_class(seed, &seed_bytes, class); + let mut dataset = + DatasetSource::from_key(igneum_pow::seed::seed_words_from_bytes(&day_bytes), DatasetMode::MemoryHard, log2); + dataset.key_bytes = day_bytes; + dataset.attach_hot_for(&program); + (p.to_string(), Epoch { program, dataset }) + }) + .collect() + }); + &all.iter().find(|(n, _)| n == pack).unwrap().1 +} + +fn hassert_same_text(pack: &str, file: &str, got: &str) { + let want = hread(pack, file); + if got != want { + let (gl, wl): (Vec<&str>, Vec<&str>) = (got.lines().collect(), want.lines().collect()); + for i in 0..gl.len().max(wl.len()) { + let g = gl.get(i).copied().unwrap_or(""); + let w = wl.get(i).copied().unwrap_or(""); + if g != w { + panic!("{pack}/{file} differs at line {}:\n pack: {w}\n rust: {g}", i + 1); + } + } + panic!("{pack}/{file} differs only in trailing bytes (len {} vs {})", got.len(), want.len()); + } +} + +#[test] +fn hot_packs_program_and_vectors() { + for pack in HOT_PACKS { + let e = hepoch(pack); + let p = &e.program; + let j = hjson(pack, "program.json"); + let h = p.class.hot.unwrap(); + assert_eq!(j["generator"].as_u64().unwrap() as u32, GENERATOR_VERSION); + assert_eq!(j["attempt"].as_u64().unwrap() as u32, p.attempt); + assert_eq!(hex64(&j["program_id"]), p.program_id(), "{pack}: program id"); + let dataset_slots = if h.added { 16 } else { 16 - h.k as usize }; + assert_eq!(j["loads_per_hash"].as_u64().unwrap() as usize, (dataset_slots + h.k as usize) * 8); + assert_eq!(j["hot_table"]["mb"].as_u64().unwrap(), h.mb as u64); + assert_eq!(j["hot_table"]["slots"].as_u64().unwrap(), h.k as u64); + assert_eq!(j["hot_table"]["dataset_slots"].as_u64().unwrap() as usize, dataset_slots); + assert_eq!(j["hot_table"]["words"].as_u64().unwrap() as u32, p.hot_words()); + assert_eq!(j["op_mix"]["hot"].as_u64().unwrap(), h.k as u64, "{pack}: k hot instructions"); + assert_eq!(j["op_mix"]["load"].as_u64().unwrap() as usize, dataset_slots); + assert_eq!(p.items_per_warp(), dataset_slots * 8 * 32); + assert!(accept::check(p).is_ok(), "{pack}: passes the acceptance rule"); + let v2 = &epoch("igneum-genesis-mh").program; + if !h.added { + // replaced form: the version 2 genesis program with k loads redirected (attempt 0 on both) + assert_eq!(p.attempt, v2.attempt); + for (a, b) in p.instrs.iter().zip(v2.instrs.iter()) { + if a.op == Op::Hot { + assert_eq!(b.op, Op::Load); + } else { + assert_eq!(a, b); + } + } + } else { + // added form: 16 + k load slots, so another slot draw and another program; 16 dataset loads stay + assert_ne!(p.instrs, v2.instrs); + assert_eq!(p.instrs.iter().filter(|i| i.op == Op::Load).count(), 16); + assert!(p.instrs.iter().all(|i| i.width == 1)); + } + // the hot table: the pack's head, last line and fingerprint + let v = hjson(pack, "vectors.json"); + let t = e.dataset.hot.as_ref().unwrap(); + assert_eq!(t.n_words(), p.hot_words()); + let head: Vec = v["hot_head"].as_array().unwrap().iter().map(hex32).collect(); + assert_eq!(&t.words()[..16], &head[..]); + let last: Vec = v["hot_last_line"].as_array().unwrap().iter().map(hex32).collect(); + assert_eq!(&t.words()[t.words().len() - 16..], &last[..]); + assert_eq!(t.fnv1a64(), hex64(&v["hot_fnv1a64"]), "{pack}: hot_fnv1a64"); + assert_eq!(t.key, igneum_pow::memhard::hot_key(&p.seed_bytes)); + // the cache is the day's, unchanged by the class + assert_eq!(e.dataset.memhard().unwrap().cache.fnv1a64(), 0x48c4f5bf24166b2e); + // 96 vectors + let warps = v["warps"].as_array().unwrap(); + assert_eq!(warps.len(), 3); + for w in warps { + let base = w["base_nonce"].as_u64().unwrap() as u32; + let expected: Vec = w["expected"].as_array().unwrap().iter().map(hex64).collect(); + let got = e.hash_warp(base); + for lane in 0..32 { + assert_eq!(got[lane], expected[lane], "{pack}: base {base} lane {lane}"); + } + assert_eq!(e.hash(base + 5), expected[5]); + } + // the dataset words are the day's + let head: Vec = v["dataset_head"].as_array().unwrap().iter().map(hex32).collect(); + for (i, hd) in head.iter().enumerate() { + assert_eq!(e.dataset.word(i as u32), *hd); + } + } + // the same k at three sizes: identical programs, three fingerprints, three vector sets + let a = hepoch("hot32k4"); + let b = hepoch("hot64k4"); + let c = hepoch("hot96k4"); + assert_eq!(a.program.instrs, b.program.instrs); + assert_eq!(b.program.instrs, c.program.instrs); + assert_ne!(a.hash_warp(0), b.hash_warp(0)); + assert_ne!(b.hash_warp(0), c.hash_warp(0)); +} + +#[test] +fn hot_packs_emitted_sources_and_load_forms() { + for pack in HOT_PACKS { + let e = hepoch(pack); + let p = &e.program; + let k = p.class.hot.unwrap().k as usize; + let dataset_loads = p.class.dataset_slots(); + let day = hjson(pack, "program.json")["dataset"]["day"].as_str().unwrap().to_string(); + let mp = &e.dataset.memhard().unwrap().params; + hassert_same_text(pack, "kernel.cu", &cuda_kernel(p, Some(mp))); + hassert_same_text(pack, "kernel_bound.cu", &cuda_kernel_bound(p, Some(mp))); + hassert_same_text(pack, "program.metal", &metal_program(p, e.dataset.log2_words, LoadSource::Stored)); + hassert_same_text(pack, "program_bound.metal", &metal_program_bound(p, e.dataset.log2_words)); + hassert_same_text(pack, "kernel.cl", &opencl_kernel(p, Some(mp))); + hassert_same_text(pack, "kernel_bound.cl", &opencl_kernel_bound(p, Some(mp))); + hassert_same_text(pack, "program.h", &program_header(p, &day, &e.dataset)); + hassert_same_text(pack, "memhard.h", &cuda_memhard_header(p, mp)); + hassert_same_text(pack, "memhard.metal", &metal_memhard_for(p, mp)); + assert_ne!(metal_memhard_for(p, mp), metal_memhard(mp), "{pack}: the hot fill kernel is in memhard.metal"); + let got = program_json(p, &day, &e.dataset); + hassert_same_text(pack, "program.json", &got); + let _: Value = serde_json::from_str(&got).expect("program.json is valid JSON"); + let v = hjson(pack, "vectors.json"); + let out = export_pack(e, &day, v["source"].as_str().unwrap()); + let file = |name: &str| -> &str { &out.files.iter().find(|(n, _)| n == name).unwrap().1 }; + hassert_same_text(pack, "vectors.json", file("vectors.json")); + hassert_same_text(pack, "vectors.h", file("vectors.h")); + assert_eq!(out.files.len(), 12); + // One form per dialect, exactly 16 - k masked dataset loads and k hot loads in every hash kernel; the fill + // kernel is present once per source that builds the table. + for (file, load, masked, hot) in [ + ("kernel.cu", "ds[r", " & mask]", "hot[__umulhi(r"), + ("kernel_bound.cu", "ds[r", " & mask]", "hot[__umulhi(r"), + ("program.metal", "dataset[r", " & MASK]", "hot[mulhi(r"), + ("program_bound.metal", "dataset[r", " & MASK]", "hot[mulhi(r"), + ("kernel.cl", "ds[r", " & mask]", "hot[mul_hi(r"), + ] { + let text = hread(pack, file); + assert_eq!(text.matches(load).count(), dataset_loads, "{pack}/{file}: {dataset_loads} dataset loads"); + assert_eq!(text.matches(masked).count(), dataset_loads, "{pack}/{file}: masked loads"); + assert_eq!(text.matches(hot).count(), k, "{pack}/{file}: {k} hot loads"); + assert!(text.contains(&format!("#define HOT_WORDS 0x{:08x}u", p.hot_words())), "{pack}/{file}: HOT_WORDS literal"); + } + // kernel_bound.cl carries both kernels + let text = hread(pack, "kernel_bound.cl"); + assert_eq!(text.matches("hot[mul_hi(r").count(), 2 * k); + assert_eq!(text.matches("ds[r").count(), 2 * dataset_loads); + for file in ["kernel.cu", "kernel.cl", "kernel_bound.cl", "memhard.metal"] { + assert_eq!(hread(pack, file).matches("igneum_hot_fill(").count(), 1, "{pack}/{file}: one hot fill kernel"); + } + assert_eq!(hread(pack, "memhard.h").matches("void ht_segment(").count(), 1); + let ph = hread(pack, "program.h"); + assert!(ph.contains(&format!("#define IGNEUM_HOT_MB {}", p.class.hot.unwrap().mb))); + assert!(ph.contains(&format!("#define IGNEUM_HOT_SLOTS {k}"))); + assert!(ph.contains("igneum_launch_hot_fill(")); + } +} diff --git a/proto-cuda/nvrtc/packfile.h b/proto-cuda/nvrtc/packfile.h index 2a740998..d4f2bc19 100644 --- a/proto-cuda/nvrtc/packfile.h +++ b/proto-cuda/nvrtc/packfile.h @@ -34,6 +34,13 @@ typedef struct { // Counter ASIC 2.0 (5 October 2026): the mixer multiplier of the item derivation (IGNEUM_MIXER_MULT, 1 when absent: // version 2; 4 under class v3). The emitted memhard.h / kernel.cl carry it in their text; this is for the log lines. uint32_t mixerMult; + // hot-table experiment (5 October 2026, docs/plans/hot-table.md): hotMb 0 when the pack has no hot table; the + // table is filled on the device from the pack's igneum_hot_fill (never shipped), its self-test values from vectors.h + uint32_t hotMb, hotWords, hotSegments, hotSlots; + uint32_t hotKey[8]; + int haveHot; + uint32_t hotHead[16], hotLast[16]; + uint64_t hotFnv; // seeds.txt (or program.h): the seeds as the worker protocol carries them char epochHex[65]; char dayHex[PF_HEX_CAP]; @@ -300,6 +307,12 @@ static int pf_load(const char* dir, PfPack* pk, char* err, size_t cap) { pk->scratchWordsPerLane = 8192; pf_define_u32(prog, "IGNEUM_SCRATCH_WORDS_PER_LANE", &pk->scratchWordsPerLane); strcpy(pk->loadClass, "v2"); pf_define_str(prog, "IGNEUM_LOAD_CLASS", pk->loadClass, sizeof(pk->loadClass)); pk->mixerMult = 1; pf_define_u32(prog, "IGNEUM_MIXER_MULT", &pk->mixerMult); + pk->hotMb = 0; pf_define_u32(prog, "IGNEUM_HOT_MB", &pk->hotMb); + if (pk->hotMb) { + if (!pf_define_u32(prog, "IGNEUM_HOT_WORDS", &pk->hotWords) || !pf_define_u32(prog, "IGNEUM_HOT_SEGMENTS", &pk->hotSegments) || + !pf_define_u32(prog, "IGNEUM_HOT_SLOTS", &pk->hotSlots) || pf_define_words(prog, "IGNEUM_HOT_KEY_INIT", pk->hotKey, 8) != 8) { free(prog); return pf_fail(err, cap, "program.h has IGNEUM_HOT_MB but not IGNEUM_HOT_WORDS, IGNEUM_HOT_SEGMENTS, IGNEUM_HOT_SLOTS and IGNEUM_HOT_KEY_INIT"); } + if (pk->hotWords != pk->hotMb * 262144u || pk->hotSegments != pk->hotMb * 256u || pk->hotMb > 4096u) { free(prog); return pf_fail(err, cap, "program.h hot table sizes disagree (words must be MiB x 2^18, segments MiB x 256)"); } + } if (pf_define_words(prog, "IGNEUM_KEY_INIT", pk->keyw, 8) != 8) { free(prog); return pf_fail(err, cap, "program.h has no IGNEUM_KEY_INIT with 8 words"); } if (!pf_define_str(prog, "IGNEUM_SEED_STRING", pk->seedString, sizeof(pk->seedString))) strncpy(pk->seedString, "(no IGNEUM_SEED_STRING)", sizeof(pk->seedString) - 1); pf_define_str(prog, "IGNEUM_SEED_BYTES_HEX", ehex, sizeof(ehex)); @@ -364,6 +377,9 @@ static int pf_load(const char* dir, PfPack* pk, char* err, size_t cap) { pf_symbol_numbers(vec, "IGNEUM_CACHE_FNV64", &pk->cacheFnv, 1) == 1 && pk->vecWarps > 0) { uint32_t ns = 0; pk->haveVectors = 1; + if (pk->hotMb && pf_symbol_u32s(vec, "IGNEUM_HOT_HEAD", pk->hotHead, 16) == 16 && + pf_symbol_u32s(vec, "IGNEUM_HOT_LAST", pk->hotLast, 16) == 16 && + pf_symbol_numbers(vec, "IGNEUM_HOT_FNV64", &pk->hotFnv, 1) == 1) pk->haveHot = 1; if (pf_define_u32(vec, "IGNEUM_DS_SAMPLES", &ns) && ns > 0 && ns <= PF_MAX_SAMPLES) { int a = pf_symbol_u32s(vec, "IGNEUM_DS_SAMPLE_INDEX", pk->sampleIdx, (int)ns); int b = pf_symbol_u32s(vec, "IGNEUM_DS_SAMPLE_VALUE", pk->sampleVal, (int)ns); @@ -377,23 +393,32 @@ static int pf_load(const char* dir, PfPack* pk, char* err, size_t cap) { // The self-test verdict from values the host read back from the device. `vec` holds vecWarps x 32 outputs of the // bound kernel run with the pack's own seed words as init words (that is igneum_hash of kernel.cu). Writes one line. +// A hot-table pack (pk->hotMb) also hands the hot table's head, last line and FNV-1a 64 (NULL and 0 otherwise); a +// hot pack whose vectors.h carries no hot values is not checked on the table (the vectors cover it) and says so. static int pf_selftest(const PfPack* pk, const uint32_t* cacheHead, const uint32_t* cacheLast, uint64_t cacheFnv, const uint32_t* dsHead, uint32_t dsLast, const uint32_t* sampleVals, const uint64_t* vec, + const uint32_t* hotHead, const uint32_t* hotLast, uint64_t hotFnv, char* out, size_t cap) { int okCH = memcmp(cacheHead, pk->cacheHead, 64) == 0, okCL = memcmp(cacheLast, pk->cacheLast, 64) == 0; int okFnv = (cacheFnv == pk->cacheFnv); int okDH = memcmp(dsHead, pk->dsHead, 64) == 0, okDL = (dsLast == pk->dsLast); + int hotChecked = (pk->hotMb && pk->haveHot && hotHead && hotLast); + int okHot = !hotChecked || (memcmp(hotHead, pk->hotHead, 64) == 0 && memcmp(hotLast, pk->hotLast, 64) == 0 && hotFnv == pk->hotFnv); int badS = 0, badV = 0, i, w, l, firstBadWarp = -1, firstBadLane = -1; for (i = 0; i < pk->nSamples; ++i) if (sampleVals[i] != pk->sampleVal[i]) ++badS; for (w = 0; w < pk->vecWarps; ++w) for (l = 0; l < 32; ++l) if (vec[w * 32 + l] != pk->vecOut[w][l]) { if (firstBadWarp < 0) { firstBadWarp = w; firstBadLane = l; } ++badV; } - if (okCH && okCL && okFnv && okDH && okDL && badS == 0 && badV == 0) { - snprintf(out, cap, "self-test PASS (cache head, last line and FNV-1a 64 %016llx; dataset head, word [%u] and %d samples; %d of %d vector lanes)", - (unsigned long long)cacheFnv, pk->dsLastIndex, pk->nSamples, pk->vecWarps * 32, pk->vecWarps * 32); + if (okCH && okCL && okFnv && okDH && okDL && okHot && badS == 0 && badV == 0) { + if (pk->hotMb) + snprintf(out, cap, "self-test PASS (cache head, last line and FNV-1a 64 %016llx; dataset head, word [%u] and %d samples; hot table %u MiB %s; %d of %d vector lanes)", + (unsigned long long)cacheFnv, pk->dsLastIndex, pk->nSamples, pk->hotMb, hotChecked ? "head, last line and FNV-1a 64 ok" : "not in vectors.h (the vectors cover it)", pk->vecWarps * 32, pk->vecWarps * 32); + else + snprintf(out, cap, "self-test PASS (cache head, last line and FNV-1a 64 %016llx; dataset head, word [%u] and %d samples; %d of %d vector lanes)", + (unsigned long long)cacheFnv, pk->dsLastIndex, pk->nSamples, pk->vecWarps * 32, pk->vecWarps * 32); return 1; } - snprintf(out, cap, "self-test FAIL (cache head %s, cache last %s, cache FNV %016llx vs pack %016llx %s, dataset head %s, dataset last %s, samples %d bad of %d, vector lanes %d bad of %d%s)", + snprintf(out, cap, "self-test FAIL (cache head %s, cache last %s, cache FNV %016llx vs pack %016llx %s, dataset head %s, dataset last %s, samples %d bad of %d, hot table %s, vector lanes %d bad of %d%s)", okCH ? "ok" : "BAD", okCL ? "ok" : "BAD", (unsigned long long)cacheFnv, (unsigned long long)pk->cacheFnv, okFnv ? "ok" : "BAD", - okDH ? "ok" : "BAD", okDL ? "ok" : "BAD", badS, pk->nSamples, badV, pk->vecWarps * 32, + okDH ? "ok" : "BAD", okDL ? "ok" : "BAD", badS, pk->nSamples, hotChecked ? (okHot ? "ok" : "BAD") : "none", badV, pk->vecWarps * 32, firstBadWarp >= 0 ? " (first bad lane in the warp at base nonce" : ""); if (firstBadWarp >= 0) { size_t n = strlen(out); diff --git a/proto-cuda/nvrtc/worker.cpp b/proto-cuda/nvrtc/worker.cpp index 175b2bc0..0439e427 100644 --- a/proto-cuda/nvrtc/worker.cpp +++ b/proto-cuda/nvrtc/worker.cpp @@ -434,6 +434,12 @@ struct Pair { int warps = 0; // persistent warps launched (the arena holds this many) int residentWarps = 0; // the occupancy query's capacity: blocks/SM x warps/block x SMs size_t scratchBytes = 0; + // hot-table experiment (5 October 2026, docs/plans/hot-table.md): the epoch's hot table, filled on the device by the + // pack's igneum_hot_fill, the argument after the init words + uint32_t hotMb = 0, hotWords = 0, hotSegments = 0, hotSlots = 0; + CUfunction fHotFill = nullptr; + CUdeviceptr hot = 0; + double hotMs = 0; }; static bool pairIs(const Pair* p, const std::string& epochHex, const std::string& dayHex) { @@ -467,6 +473,7 @@ static void releasePair(Ctx& c, Pair* p) { if (p->ds) c.drv.memFree(p->ds); if (p->cache) c.drv.memFree(p->cache); if (p->scratch) c.drv.memFree(p->scratch); + if (p->hot) c.drv.memFree(p->hot); if (p->modBound) c.drv.moduleUnload(p->modBound); if (p->modKernel) c.drv.moduleUnload(p->modKernel); delete p; @@ -485,11 +492,13 @@ static bool launchHash(Ctx& c, Pair* p, CUdeviceptr out, uint32_t baseNonce, con while (warps > 1u && units % warps != 0u) warps >>= 1; if (block != 32u) { err = "a variant-5 pack runs one warp per block (--block-warps 1)"; return false; } uint32_t salt = c.salt; c.salt += units; - void* args[8] = { &p->ds, &out, &baseNonce, &mask, &a, &p->scratch, &units, &salt }; + // the hot table (when the pack has one) sits between the init words and the scratch triple + void* args[9] = { &p->ds, &out, &baseNonce, &mask, &a, &p->scratch, &units, &salt, nullptr }; + if (p->hot) { args[5] = &p->hot; args[6] = &p->scratch; args[7] = &units; args[8] = &salt; } DRV_CHECK(c, c.drv.launchKernel(p->fHashBound, warps, 1, 1, 32, 1, 1, 0, s, args, nullptr), "cuLaunchKernel igneum_hash_bound (persistent)"); return true; } - void* args[5] = { &p->ds, &out, &baseNonce, &mask, &a }; + void* args[6] = { &p->ds, &out, &baseNonce, &mask, &a, &p->hot }; DRV_CHECK(c, c.drv.launchKernel(p->fHashBound, nonces / block, 1, 1, block, 1, 1, 0, s, args, nullptr), "cuLaunchKernel igneum_hash_bound"); return true; } @@ -812,7 +821,9 @@ static Pair* buildPair(Ctx& c, const std::string& dir, CUstream s, std::string& p->datasetLog2 = pk.datasetLog2; p->words = 1u << pk.datasetLog2; p->cacheWords = 1u << pk.cacheLog2Words; p->cacheSegments = pk.cacheSegments; // Compile Compiled ck, cb; - if (!rtcCompile(c, kernelDev, "kernel.cu", programH, memhardH, { "igneum_cache_fill", "igneum_build" }, ck, err)) { releasePair(c, p); return nullptr; } + std::vector kernelNames = { "igneum_cache_fill", "igneum_build" }; + if (pk.hotMb) kernelNames.push_back("igneum_hot_fill"); + if (!rtcCompile(c, kernelDev, "kernel.cu", programH, memhardH, kernelNames, ck, err)) { releasePair(c, p); return nullptr; } if (!rtcCompile(c, boundDev, "kernel_bound.cu", programH, memhardH, { "igneum_hash_bound" }, cb, err)) { releasePair(c, p); return nullptr; } p->compileMs = ck.ms + cb.ms; // Load @@ -824,14 +835,16 @@ static Pair* buildPair(Ctx& c, const std::string& dir, CUstream s, std::string& if (c.drv.moduleGetFunction(&p->fCacheFill, p->modKernel, ck.lowered[0].c_str()) != CUDA_SUCCESS) { err = "igneum_cache_fill (" + ck.lowered[0] + ") not in the module"; releasePair(c, p); return nullptr; } if (c.drv.moduleGetFunction(&p->fBuild, p->modKernel, ck.lowered[1].c_str()) != CUDA_SUCCESS) { err = "igneum_build (" + ck.lowered[1] + ") not in the module"; releasePair(c, p); return nullptr; } if (c.drv.moduleGetFunction(&p->fHashBound, p->modBound, cb.lowered[0].c_str()) != CUDA_SUCCESS) { err = "igneum_hash_bound (" + cb.lowered[0] + ") not in the module"; releasePair(c, p); return nullptr; } + if (pk.hotMb && c.drv.moduleGetFunction(&p->fHotFill, p->modKernel, ck.lowered[2].c_str()) != CUDA_SUCCESS) { err = "igneum_hot_fill (" + ck.lowered[2] + ") not in the module"; releasePair(c, p); return nullptr; } c.drv.funcGetAttribute(&p->regs, CU_FUNC_ATTRIBUTE_NUM_REGS, p->fHashBound); c.drv.occupancy(&p->blocksPerSM, p->fHashBound, 32 * c.blockWarps, 0); } p->loadClass = pk.loadClass; p->loadsPerHash = pk.loadsPerHash; p->bytesPerHash = pk.bytesPerHash; p->scratchOps = pk.scratchOps; p->programClass = pk.programClass; p->eraHex = pk.eraHex; p->persistent = pk.persistent != 0; + p->hotMb = pk.hotMb; p->hotWords = pk.hotWords; p->hotSegments = pk.hotSegments; p->hotSlots = pk.hotSlots; p->residentWarps = p->blocksPerSM * c.blockWarps * c.sms; - size_t scratchBytes = 0; + size_t scratchBytes = 0, hotBytes = (size_t)pk.hotWords * 4u; if (p->persistent) { // Variant 5: one arena per launched warp. The launch is the resident capacity (the occupancy query), rounded // down to a power of two so it divides every batch, or --warps; the allocation cannot change the occupancy @@ -847,8 +860,8 @@ static Pair* buildPair(Ctx& c, const std::string& dir, CUstream s, std::string& size_t cacheBytes = (size_t)p->cacheWords * 4u, dsBytes = (size_t)p->words * 4u; { size_t freeB = 0, totalB = 0; - if (c.drv.memGetInfo(&freeB, &totalB) == CUDA_SUCCESS && freeB < cacheBytes + dsBytes + scratchBytes + (64u << 20)) { - err = fmt("%llu MiB free on the device, this pack needs %llu MiB (cache %llu + dataset %llu + scratch %llu)", (unsigned long long)(freeB >> 20), (unsigned long long)((cacheBytes + dsBytes + scratchBytes) >> 20), (unsigned long long)(cacheBytes >> 20), (unsigned long long)(dsBytes >> 20), (unsigned long long)(scratchBytes >> 20)); + if (c.drv.memGetInfo(&freeB, &totalB) == CUDA_SUCCESS && freeB < cacheBytes + dsBytes + scratchBytes + hotBytes + (64u << 20)) { + err = fmt("%llu MiB free on the device, this pack needs %llu MiB (cache %llu + dataset %llu + scratch %llu + hot %llu)", (unsigned long long)(freeB >> 20), (unsigned long long)((cacheBytes + dsBytes + scratchBytes + hotBytes) >> 20), (unsigned long long)(cacheBytes >> 20), (unsigned long long)(dsBytes >> 20), (unsigned long long)(scratchBytes >> 20), (unsigned long long)(hotBytes >> 20)); releasePair(c, p); return nullptr; } } @@ -883,6 +896,18 @@ static Pair* buildPair(Ctx& c, const std::string& dir, CUstream s, std::string& if (r != CUDA_SUCCESS) { err = "dataset build: " + c.err(r); releasePair(c, p); return nullptr; } } p->dsMs = wallMs() - t0; + // Hot table (hot-table experiment): filled from the epoch seed by the pack's own kernel, never shipped + if (pk.hotMb) { + t0 = wallMs(); + CUresult r = c.drv.memAlloc(&p->hot, hotBytes); + if (r != CUDA_SUCCESS) { err = "cuMemAlloc hot table: " + c.err(r); p->hot = 0; releasePair(c, p); return nullptr; } + uint32_t nSeg = pk.hotSegments, block = 256u, grid = (nSeg + block - 1u) / block; + void* args[2] = { &p->hot, &nSeg }; + r = c.drv.launchKernel(p->fHotFill, grid, 1, 1, block, 1, 1, 0, s, args, nullptr); + if (r == CUDA_SUCCESS) r = c.drv.streamSynchronize(s); + if (r != CUDA_SUCCESS) { err = "hot table fill: " + c.err(r); releasePair(c, p); return nullptr; } + p->hotMs = wallMs() - t0; + } // Self-test against vectors.h t0 = wallMs(); if (!pk.haveVectors) { @@ -911,8 +936,17 @@ static Pair* buildPair(Ctx& c, const std::string& dir, CUstream s, std::string& if ((r = c.drv.streamSynchronize(s)) != CUDA_SUCCESS || (r = c.drv.memcpyDtoH(&vec[(size_t)w * 32u], out, 32u * 8u)) != CUDA_SUCCESS) { err = "vector warp: " + c.err(r); c.drv.memFree(out); releasePair(c, p); return nullptr; } } c.drv.memFree(out); + uint32_t hotHead[16] = {0}, hotLast[16] = {0}; + uint64_t hotFnv = 0; + if (p->hot) { + std::vector hw(p->hotWords); + if ((r = c.drv.memcpyDtoH(hw.data(), p->hot, hotBytes)) != CUDA_SUCCESS) { err = "cuMemcpyDtoH hot table: " + c.err(r); releasePair(c, p); return nullptr; } + std::memcpy(hotHead, hw.data(), 64); + std::memcpy(hotLast, hw.data() + p->hotWords - 16u, 64); + hotFnv = pf_fnv1a64(hw.data(), hotBytes); + } char line[1024]; - p->checkPass = pf_selftest(&pk, cacheHead, cacheLast, fnv, dsHead, dsLast, samples.data(), vec.data(), line, sizeof(line)) != 0; + p->checkPass = pf_selftest(&pk, cacheHead, cacheLast, fnv, dsHead, dsLast, samples.data(), vec.data(), p->hot ? hotHead : nullptr, p->hot ? hotLast : nullptr, hotFnv, line, sizeof(line)) != 0; p->checked = true; p->check = line; } @@ -924,7 +958,7 @@ static Pair* buildPair(Ctx& c, const std::string& dir, CUstream s, std::string& } static std::string pairSummary(const Pair* p) { - return fmt("nvrtc %.0f cache %.0f dataset %.0f check %.0f race %.0f ms variant %s; %s", p->compileMs, p->cacheMs, p->dsMs, p->checkMs, p->raceMs, p->variant.c_str(), p->check.c_str()); + return fmt("nvrtc %.0f cache %.0f dataset %.0f hot %.0f check %.0f race %.0f ms variant %s; %s", p->compileMs, p->cacheMs, p->dsMs, p->hotMs, p->checkMs, p->raceMs, p->variant.c_str(), p->check.c_str()); } // --------------------------------------------------------------------------------------------- @@ -1276,8 +1310,8 @@ static int runBench(Ctx& c, const Options& o, Pair* p) { c.drv.memFree(dOut); std::string dev = c.name; for (char& ch : dev) if (ch == ' ') ch = '_'; std::printf("warm-up dispatch (base 0): %.2f ms; %d timed dispatches of %u nonces: mean %.2f ms\n", warm, o.batches, nonces, sum / o.batches); - std::printf("RESULT pack=%s class=%s device=%s arch=%s regs=%d blocks_per_sm=%d warps=%d resident=%d arena_mib=%llu nonces=%u batches=%d check=%s fingerprint=%016llx mhs=%.3f loads=%u bytes=%u scratch_ops=%u time=wall\n", - p->dir.c_str(), p->loadClass.c_str(), dev.c_str(), c.archOpt.c_str(), p->regs, p->blocksPerSM, p->warps, p->residentWarps, (unsigned long long)(p->scratchBytes >> 20), nonces, o.batches, + std::printf("RESULT pack=%s class=%s device=%s arch=%s regs=%d blocks_per_sm=%d warps=%d resident=%d arena_mib=%llu hot_mib=%u hot_slots=%u hot_fill_ms=%.2f nonces=%u batches=%d check=%s fingerprint=%016llx mhs=%.3f loads=%u bytes=%u scratch_ops=%u time=wall\n", + p->dir.c_str(), p->loadClass.c_str(), dev.c_str(), c.archOpt.c_str(), p->regs, p->blocksPerSM, p->warps, p->residentWarps, (unsigned long long)(p->scratchBytes >> 20), p->hotMb, p->hotSlots, p->hotMs, nonces, o.batches, p->checked ? (p->checkPass ? "PASS" : "FAIL") : "skipped", (unsigned long long)fp, (double)nonces * (double)o.batches / (sum / 1000.0) / 1e6, p->loadsPerHash, p->bytesPerHash, p->scratchOps * 8u); return 0; diff --git a/proto-cuda/packs-ca2-hot/hot32k4/kernel.cl b/proto-cuda/packs-ca2-hot/hot32k4/kernel.cl new file mode 100644 index 00000000..47a4fbfd --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4/kernel.cl @@ -0,0 +1,305 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (32 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x00800000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 32 MiB = 8192 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-hot/hot32k4/kernel.cu b/proto-cuda/packs-ca2-hot/hot32k4/kernel.cu new file mode 100644 index 00000000..9d889886 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4/kernel.cu @@ -0,0 +1,179 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +// Hot table (32 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x00800000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (ht_segment is in memhard.h): one thread per segment. +__global__ void igneum_hot_fill(uint32_t* hot, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[__umulhi(r1, HOT_WORDS)]; // 34 hot + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_hot_fill<<>>(hot, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4/kernel_bound.cl b/proto-cuda/packs-ca2-hot/hot32k4/kernel_bound.cl new file mode 100644 index 00000000..9bf292b5 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4/kernel_bound.cl @@ -0,0 +1,399 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (32 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x00800000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 32 MiB = 8192 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4/kernel_bound.cu b/proto-cuda/packs-ca2-hot/hot32k4/kernel_bound.cu new file mode 100644 index 00000000..a86e26c2 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4/kernel_bound.cu @@ -0,0 +1,125 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +// Hot table (32 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x00800000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[__umulhi(r1, HOT_WORDS)]; // 34 hot + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, const uint32_t* hot, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4/memhard.h b/proto-cuda/packs-ca2-hot/hot32k4/memhard.h new file mode 100644 index 00000000..4e0eee82 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4/memhard.h @@ -0,0 +1,129 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Hot table (docs/plans/hot-table.md): 32 MiB = 8192 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +IGNEUM_HD void ht_segment(uint32_t* hot, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4/memhard.metal b/proto-cuda/packs-ca2-hot/hot32k4/memhard.metal new file mode 100644 index 00000000..4325ec79 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4/memhard.metal @@ -0,0 +1,131 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; +} + +// Hot table (docs/plans/hot-table.md): 32 MiB = 8192 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +inline void ht_segment(device uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one thread per segment (IGNEUM_HOT_SEGMENTS threads). +kernel void igneum_hot_fill(device uint* hot [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + ht_segment(hot, gid); +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4/program.h b/proto-cuda/packs-ca2-hot/hot32k4/program.h new file mode 100644 index 00000000..f28f4872 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4/program.h @@ -0,0 +1,70 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-genesis" +#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973" +#define IGNEUM_GENERATOR 2 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x3370a4466e5f322cull +#define IGNEUM_DAY_STRING "2026-10-03" +#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033" +#define IGNEUM_DAY0 0x3067619fu +#define IGNEUM_DAY1 0x3c269176u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=12 add=8 shfl=8 xor=6 mad=5 mul=5 mulhi=5 hot=4 sub=4 rotl=3 rotr=3 or=1" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "hot32k4" +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 12, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 384 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Hot table (hot-table experiment, 5 October 2026, docs/plans/hot-table.md): NOT the lottery hash. IGNEUM_HOT_SLOTS of the +// 16 load slots read H[mulhi(src, IGNEUM_HOT_WORDS)], H = IGNEUM_HOT_MB MiB of chained ChaCha12 lines (the cache chain) under +// KH = seed_words("igneum-hot/" || epoch seed bytes) and the tag "Igne" "umHT"; filled by igneum_hot_fill once per epoch. +#define IGNEUM_HOT_MB 32 +#define IGNEUM_HOT_WORDS 0x00800000u +#define IGNEUM_HOT_SEGMENTS 8192u +#define IGNEUM_HOT_SLOTS 4 // hot loads per program (32 per hash), replacing the dataset loads (12 of them) +#define IGNEUM_HOT_ADDED 0 +#define IGNEUM_HOT_KEY_INIT { 0x3a48bef5u, 0x6b54b1a1u, 0x9ff897c3u, 0x7d5d85e4u, 0xcd35379fu, 0x9d76bb86u, 0xbe2affb1u, 0x0f8f80b4u } +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u } +#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u } +#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u } +#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-hot/hot32k4/program.json b/proto-cuda/packs-ca2-hot/hot32k4/program.json new file mode 100644 index 00000000..6521857a --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4/program.json @@ -0,0 +1,129 @@ +{ + "format": "igneum-program-pack-3", + "generator": 2, + "attempt": 0, + "program_id": "0x3370a4466e5f322c", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-genesis", + "seed_bytes": "69676e65756d2d67656e65736973", + "seed_words": ["0x67a9a7be", "0x1a155b25", "0xfddfb732", "0x4b5af2e8", "0xc55caf33", "0xa27c13b7", "0x06628a48", "0x03852469"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "load_class": "hot32k4", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [12, 0, 0], + "bytes_per_hash": 384, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "hot_table": {"mb": 32, "words": 8388608, "segments": 8192, "slots": 4, "form": "replaced: k of the class's load slots read the table", "dataset_slots": 12, "hot_loads_per_hash": 32, "key": ["0x3a48bef5", "0x6b54b1a1", "0x9ff897c3", "0x7d5d85e4", "0xcd35379f", "0x9d76bb86", "0xbe2affb1", "0x0f8f80b4"], "key_derivation": "seed_words_from_bytes('igneum-hot/' || seed_bytes), the same for every attempt of the epoch", "tag": ["0x49676e65", "0x756d4854"], "chain": "the cache chain of dataset.cache with the hot key and tag: in_j = prev ^ (sigma || key || seg || j || tag); line_j = block(in_j); prev_0 = 0", "load": "dst = dst ^ hot[mulhi(src, words)] (the high 32 bits of the 64-bit product; the index lies in [0, words) for any size)", "slots_rule": "the first k load slots in the Fisher-Yates draw order after the scratch slots (a uniform k-subset); no extra draw, so with version 2 widths and no scratch the program is the version 2 program with k loads redirected", "acceptance_stand_in": "dataset_elem(mulhi(src, words), seed_words[2], seed_words[3])", "program_id": "the read-width id with 'hot/' || mb || k appended", "spec": "docs/plans/hot-table.md"}, + "op_mix": {"load": 12, "add": 8, "shfl": 8, "xor": 6, "mad": 5, "mul": 5, "mulhi": 5, "hot": 4, "sub": 4, "rotl": 3, "rotr": 3, "or": 1}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)", + "hot": "dst = dst ^ hot[mulhi(src, hot_table.words)] (hot-table experiment)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "2026-10-03", + "day_bytes": "6461792f323032362d31302d3033", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0x3067619f", + "d1": "0x3c269176", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "mad", "dst": 2, "src": 3, "src2": 4, "imm": "0xbf7b174d", "imm2": "0x337b762e", "rot": 17, "bit": 2, "mask": 2, "width": 1}, + {"i": 1, "op": "mad", "dst": 2, "src": 1, "src2": 1, "imm": "0xdd04a5da", "imm2": "0x42da7657", "rot": 15, "bit": 30, "mask": 16, "width": 1}, + {"i": 2, "op": "mad", "dst": 2, "src": 3, "src2": 2, "imm": "0x734003fa", "imm2": "0x5bb67700", "rot": 3, "bit": 20, "mask": 1, "width": 1}, + {"i": 3, "op": "xor", "dst": 3, "src": 5, "src2": 5, "imm": "0xc55a1b1c", "imm2": "0xa19720f3", "rot": 7, "bit": 8, "mask": 1, "width": 1}, + {"i": 4, "op": "load", "dst": 7, "src": 2, "src2": 5, "imm": "0xad572dd7", "imm2": "0x9ceb3ea7", "rot": 18, "bit": 30, "mask": 2, "width": 1}, + {"i": 5, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x769a53be", "imm2": "0x80f9067e", "rot": 12, "bit": 22, "mask": 1, "width": 1}, + {"i": 6, "op": "shfl", "dst": 1, "src": 4, "src2": 0, "imm": "0xd3613d88", "imm2": "0x262fb219", "rot": 10, "bit": 30, "mask": 8, "width": 1}, + {"i": 7, "op": "shfl", "dst": 7, "src": 3, "src2": 4, "imm": "0xce38e42f", "imm2": "0xb868b818", "rot": 11, "bit": 8, "mask": 8, "width": 1}, + {"i": 8, "op": "mulhi", "dst": 1, "src": 5, "src2": 2, "imm": "0xa5eebca5", "imm2": "0x5703a72b", "rot": 13, "bit": 13, "mask": 16, "width": 1}, + {"i": 9, "op": "rotr", "dst": 6, "src": 3, "src2": 4, "imm": "0x17a5a9c7", "imm2": "0xdcfb93a1", "rot": 20, "bit": 27, "mask": 2, "width": 1}, + {"i": 10, "op": "or", "dst": 3, "src": 4, "src2": 1, "imm": "0xccb7d785", "imm2": "0xc335364c", "rot": 14, "bit": 12, "mask": 4, "width": 1}, + {"i": 11, "op": "load", "dst": 4, "src": 3, "src2": 2, "imm": "0x88cb9af3", "imm2": "0x4e7dc10d", "rot": 24, "bit": 17, "mask": 4, "width": 1}, + {"i": 12, "op": "mulhi", "dst": 0, "src": 4, "src2": 4, "imm": "0x45374321", "imm2": "0x3cd91989", "rot": 11, "bit": 4, "mask": 2, "width": 1}, + {"i": 13, "op": "add", "dst": 5, "src": 1, "src2": 2, "imm": "0xc7934706", "imm2": "0xd3177981", "rot": 16, "bit": 30, "mask": 2, "width": 1}, + {"i": 14, "op": "load", "dst": 0, "src": 4, "src2": 3, "imm": "0xfd7f56bb", "imm2": "0x65e14f52", "rot": 13, "bit": 22, "mask": 2, "width": 1}, + {"i": 15, "op": "sub", "dst": 2, "src": 4, "src2": 4, "imm": "0x35a80b49", "imm2": "0x060f2d13", "rot": 16, "bit": 20, "mask": 16, "width": 1}, + {"i": 16, "op": "hot", "dst": 2, "src": 0, "src2": 2, "imm": "0xae0a32c2", "imm2": "0x4c2a4cfe", "rot": 8, "bit": 31, "mask": 16, "width": 1}, + {"i": 17, "op": "load", "dst": 7, "src": 2, "src2": 6, "imm": "0x82a84cc3", "imm2": "0x21a38d68", "rot": 15, "bit": 21, "mask": 2, "width": 1}, + {"i": 18, "op": "shfl", "dst": 7, "src": 3, "src2": 3, "imm": "0xa3818806", "imm2": "0x8f66b5c8", "rot": 14, "bit": 6, "mask": 4, "width": 1}, + {"i": 19, "op": "mul", "dst": 5, "src": 0, "src2": 1, "imm": "0xa00de107", "imm2": "0x77bfcaa5", "rot": 3, "bit": 10, "mask": 2, "width": 1}, + {"i": 20, "op": "shfl", "dst": 3, "src": 4, "src2": 7, "imm": "0x1d2b8cab", "imm2": "0x80b4f9a2", "rot": 14, "bit": 25, "mask": 2, "width": 1}, + {"i": 21, "op": "shfl", "dst": 2, "src": 4, "src2": 5, "imm": "0x3ac915d2", "imm2": "0x5fba7bc2", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 22, "op": "mulhi", "dst": 6, "src": 2, "src2": 0, "imm": "0xdc3ec8fd", "imm2": "0x599e2fa3", "rot": 22, "bit": 3, "mask": 2, "width": 1}, + {"i": 23, "op": "load", "dst": 6, "src": 1, "src2": 5, "imm": "0x2a6b16d5", "imm2": "0xd73e396f", "rot": 28, "bit": 29, "mask": 2, "width": 1}, + {"i": 24, "op": "mul", "dst": 5, "src": 0, "src2": 7, "imm": "0x376d0223", "imm2": "0xe1c2169a", "rot": 4, "bit": 16, "mask": 16, "width": 1}, + {"i": 25, "op": "rotl", "dst": 5, "src": 7, "src2": 3, "imm": "0x78ad8c60", "imm2": "0x6f5b77d5", "rot": 19, "bit": 11, "mask": 16, "width": 1}, + {"i": 26, "op": "shfl", "dst": 7, "src": 6, "src2": 5, "imm": "0x93915b9f", "imm2": "0x1e61fb6b", "rot": 28, "bit": 23, "mask": 2, "width": 1}, + {"i": 27, "op": "xor", "dst": 0, "src": 5, "src2": 7, "imm": "0x6378fe15", "imm2": "0x66c78f42", "rot": 12, "bit": 31, "mask": 8, "width": 1}, + {"i": 28, "op": "xor", "dst": 0, "src": 4, "src2": 7, "imm": "0x20a57fda", "imm2": "0x088c848e", "rot": 16, "bit": 13, "mask": 4, "width": 1}, + {"i": 29, "op": "sub", "dst": 3, "src": 0, "src2": 2, "imm": "0x49d95fd5", "imm2": "0x1a5f946a", "rot": 6, "bit": 12, "mask": 1, "width": 1}, + {"i": 30, "op": "mul", "dst": 5, "src": 1, "src2": 7, "imm": "0x0a816217", "imm2": "0x405c4f73", "rot": 13, "bit": 27, "mask": 4, "width": 1}, + {"i": 31, "op": "load", "dst": 7, "src": 2, "src2": 2, "imm": "0x09ed045e", "imm2": "0xd69c4715", "rot": 5, "bit": 9, "mask": 2, "width": 1}, + {"i": 32, "op": "hot", "dst": 1, "src": 0, "src2": 6, "imm": "0xeb79ea49", "imm2": "0xcc587f5a", "rot": 6, "bit": 8, "mask": 16, "width": 1}, + {"i": 33, "op": "xor", "dst": 5, "src": 6, "src2": 1, "imm": "0x3027401e", "imm2": "0x5f20c27e", "rot": 18, "bit": 9, "mask": 2, "width": 1}, + {"i": 34, "op": "hot", "dst": 5, "src": 1, "src2": 3, "imm": "0x0e1cab07", "imm2": "0x09356c5b", "rot": 19, "bit": 31, "mask": 1, "width": 1}, + {"i": 35, "op": "mulhi", "dst": 0, "src": 5, "src2": 2, "imm": "0x90e31357", "imm2": "0xabd32484", "rot": 26, "bit": 5, "mask": 8, "width": 1}, + {"i": 36, "op": "shfl", "dst": 5, "src": 2, "src2": 5, "imm": "0xee9a955f", "imm2": "0x31b3faed", "rot": 8, "bit": 24, "mask": 4, "width": 1}, + {"i": 37, "op": "load", "dst": 7, "src": 0, "src2": 7, "imm": "0x3ba2f832", "imm2": "0x1160dcd3", "rot": 4, "bit": 29, "mask": 1, "width": 1}, + {"i": 38, "op": "add", "dst": 3, "src": 1, "src2": 7, "imm": "0x75ba2fad", "imm2": "0x230c005c", "rot": 4, "bit": 27, "mask": 1, "width": 1}, + {"i": 39, "op": "shfl", "dst": 1, "src": 5, "src2": 3, "imm": "0xdbf37e75", "imm2": "0xb5ac1969", "rot": 30, "bit": 13, "mask": 4, "width": 1}, + {"i": 40, "op": "xor", "dst": 2, "src": 5, "src2": 5, "imm": "0x47f136c5", "imm2": "0x06ce9153", "rot": 19, "bit": 10, "mask": 2, "width": 1}, + {"i": 41, "op": "mad", "dst": 3, "src": 6, "src2": 3, "imm": "0xce13eff8", "imm2": "0x04cc1d55", "rot": 3, "bit": 1, "mask": 4, "width": 1}, + {"i": 42, "op": "sub", "dst": 6, "src": 7, "src2": 1, "imm": "0x6a65ab71", "imm2": "0x8fbc1bcd", "rot": 4, "bit": 1, "mask": 8, "width": 1}, + {"i": 43, "op": "xor", "dst": 7, "src": 0, "src2": 7, "imm": "0xdaeb4928", "imm2": "0xc0423027", "rot": 24, "bit": 11, "mask": 8, "width": 1}, + {"i": 44, "op": "load", "dst": 1, "src": 7, "src2": 7, "imm": "0x778f01c9", "imm2": "0x28cedcea", "rot": 12, "bit": 4, "mask": 16, "width": 1}, + {"i": 45, "op": "mul", "dst": 2, "src": 3, "src2": 2, "imm": "0xf4264f1b", "imm2": "0x0f627d56", "rot": 5, "bit": 28, "mask": 8, "width": 1}, + {"i": 46, "op": "mulhi", "dst": 1, "src": 5, "src2": 1, "imm": "0xffb2147a", "imm2": "0xccde9b05", "rot": 13, "bit": 9, "mask": 2, "width": 1}, + {"i": 47, "op": "sub", "dst": 4, "src": 3, "src2": 5, "imm": "0x73b36234", "imm2": "0x3f5d5997", "rot": 7, "bit": 18, "mask": 2, "width": 1}, + {"i": 48, "op": "rotr", "dst": 2, "src": 6, "src2": 3, "imm": "0x3a4d9aa9", "imm2": "0x212bec7b", "rot": 4, "bit": 29, "mask": 16, "width": 1}, + {"i": 49, "op": "load", "dst": 3, "src": 5, "src2": 2, "imm": "0x626f11df", "imm2": "0x56cd5bfd", "rot": 7, "bit": 1, "mask": 1, "width": 1}, + {"i": 50, "op": "add", "dst": 1, "src": 5, "src2": 6, "imm": "0x81b8bc2c", "imm2": "0x1907970c", "rot": 28, "bit": 7, "mask": 4, "width": 1}, + {"i": 51, "op": "mul", "dst": 0, "src": 2, "src2": 2, "imm": "0xa8848b30", "imm2": "0xef6ac348", "rot": 9, "bit": 15, "mask": 8, "width": 1}, + {"i": 52, "op": "add", "dst": 0, "src": 2, "src2": 0, "imm": "0x4f92b968", "imm2": "0x699fd448", "rot": 22, "bit": 6, "mask": 4, "width": 1}, + {"i": 53, "op": "add", "dst": 1, "src": 0, "src2": 2, "imm": "0x2bb965af", "imm2": "0x77b1520d", "rot": 2, "bit": 12, "mask": 8, "width": 1}, + {"i": 54, "op": "rotl", "dst": 7, "src": 1, "src2": 0, "imm": "0x553e678b", "imm2": "0x3cc8eae0", "rot": 14, "bit": 20, "mask": 2, "width": 1}, + {"i": 55, "op": "add", "dst": 3, "src": 7, "src2": 2, "imm": "0x7b0fe07a", "imm2": "0xa54c55a0", "rot": 10, "bit": 1, "mask": 1, "width": 1}, + {"i": 56, "op": "load", "dst": 6, "src": 7, "src2": 4, "imm": "0x01eba9aa", "imm2": "0x2758c0f7", "rot": 14, "bit": 15, "mask": 4, "width": 1}, + {"i": 57, "op": "rotr", "dst": 1, "src": 5, "src2": 2, "imm": "0x1f5267b3", "imm2": "0x236f5a27", "rot": 2, "bit": 31, "mask": 16, "width": 1}, + {"i": 58, "op": "load", "dst": 5, "src": 4, "src2": 3, "imm": "0xa9954a9b", "imm2": "0x6a54d4e8", "rot": 11, "bit": 10, "mask": 16, "width": 1}, + {"i": 59, "op": "hot", "dst": 6, "src": 2, "src2": 4, "imm": "0x9923ff88", "imm2": "0x9357254e", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 60, "op": "mad", "dst": 3, "src": 5, "src2": 0, "imm": "0xc0cc51a6", "imm2": "0x3fd7701b", "rot": 20, "bit": 1, "mask": 4, "width": 1}, + {"i": 61, "op": "add", "dst": 5, "src": 7, "src2": 7, "imm": "0xaf9dd72d", "imm2": "0xad7493e7", "rot": 7, "bit": 31, "mask": 16, "width": 1}, + {"i": 62, "op": "add", "dst": 4, "src": 6, "src2": 2, "imm": "0x89841d87", "imm2": "0x1e07c3d9", "rot": 6, "bit": 27, "mask": 1, "width": 1}, + {"i": 63, "op": "rotl", "dst": 5, "src": 4, "src2": 2, "imm": "0xae210f8d", "imm2": "0x8e499ba4", "rot": 19, "bit": 9, "mask": 1, "width": 1} + ] +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4/program.metal b/proto-cuda/packs-ca2-hot/hot32k4/program.metal new file mode 100644 index 00000000..25438279 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4/program.metal @@ -0,0 +1,112 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (32 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x00800000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + device const uint* hot [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ dataset[r2 & MASK]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ dataset[r1 & MASK]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ hot[mulhi(r1, HOT_WORDS)]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ dataset[r0 & MASK]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ dataset[r7 & MASK]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4/program_bound.metal b/proto-cuda/packs-ca2-hot/hot32k4/program_bound.metal new file mode 100644 index 00000000..3e5f6999 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4/program_bound.metal @@ -0,0 +1,114 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (32 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x00800000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + device const uint* hot [[buffer(4)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ dataset[r2 & MASK]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ dataset[r1 & MASK]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ hot[mulhi(r1, HOT_WORDS)]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ dataset[r0 & MASK]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ dataset[r7 & MASK]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4/vectors.h b/proto-cuda/packs-ca2-hot/hot32k4/vectors.h new file mode 100644 index 00000000..48402daa --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4/vectors.h @@ -0,0 +1,67 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0xd6dd7fa4e8ce0412ull, 0x7227f09f836fdfddull, 0xf278ed201cceafbfull, 0xec020afe5a5250acull, 0xf665fb744a23508dull, 0x279db29eb25d9ed7ull, 0x94acf8ad2077b200ull, 0x4570de146f552f46ull, + 0x41af921801576a43ull, 0xbcedf5682351398eull, 0x2f85798eaf3888d2ull, 0xb1973b681b608696ull, 0x43e637d721e2e02cull, 0x348f3e941642b441ull, 0x7a6d0c65ef9b3d99ull, 0xd0b3d21c07182e2aull, + 0x381c06bc6910e404ull, 0x2d6c2d4c8c64a835ull, 0x2a86874d214946a3ull, 0x2b59e5cd9a5b7b34ull, 0x59410384d5464398ull, 0x8a849a3e01c9c3a2ull, 0xa125cf7866802a38ull, 0xe1a8bbe849d936c8ull, + 0x0ca4566095a358ceull, 0xffe671a4d5e8ccc8ull, 0x57d840333cb092a6ull, 0xf97100913713d0deull, 0x442b713409774c64ull, 0xa10cf4e4c635ed10ull, 0xcf114a69a2f009b2ull, 0x36cd1a0b5f16adacull + }, + { // base nonce 4096 + 0x207cc1a5be16289full, 0x9ed2b155c8fc75e3ull, 0x2166ea7f0e13ac0dull, 0x44e3ee81290df4c9ull, 0xc2ecf745975f1401ull, 0x92be86c70096f95dull, 0xfd379260776410f7ull, 0x6cf6e19d5970bbb3ull, + 0x3a8b0506bcbf743cull, 0x10874f7196f213a0ull, 0x694b4277c2aab714ull, 0x343a854f40a54ea9ull, 0xedd91ea3095028cdull, 0x159ff1bc3d1dbf23ull, 0x337fccf9a5f8b745ull, 0x64e49f51dceeb174ull, + 0xd9e29b85844c0b6cull, 0x1223fdc9653f8a81ull, 0xfe4012a89d8cba42ull, 0x366e927907cee509ull, 0xe3b28f6d222d2a24ull, 0x45670ab50f08b3a3ull, 0xb18c0b1390d13480ull, 0xe8e65e2c34e96e4aull, + 0x105bdc8261ccafefull, 0xdf0ab212de234c1cull, 0x13ce611089323eafull, 0x8b964cb8e8ccdba2ull, 0xee088976c82e24e1ull, 0x4a12e75839901f2aull, 0x393d6ceb206e9f2dull, 0xd609e6da965d5304ull + }, + { // base nonce 1000000 + 0xca3bb360bd104e97ull, 0x534f429fa0c22461ull, 0xb7c03deeb1e3f559ull, 0xf1f803cd6f5f31e5ull, 0x4f68db8c38c02ea1ull, 0x3049cbc39de3c35cull, 0x903fdd301faa7a1full, 0xffb430e35c2c4a6full, + 0x0382a5e9c1446e5cull, 0xbec1b6d8a8f169f5ull, 0x447215c419241bc4ull, 0xa70e933c1419bd7eull, 0xac89eacda4f55c87ull, 0x3d0ca0cd39354441ull, 0xf0052aa94cc5abcfull, 0xfd6faddd678d1b18ull, + 0x8c1d8aac97e5ffd4ull, 0x7f831c277f83614full, 0xce48cf6eafce98c6ull, 0xaba6c8080267b4a5ull, 0x1afbd998e0cd01d6ull, 0xc1eac18a208d66cbull, 0x27f5f333f9f753ffull, 0x7e42e3cc90be0cd7ull, + 0x69694bcd49ff23c9ull, 0xc8abe7af7b18359dull, 0x54597507488baadfull, 0x2b2550309446c26bull, 0x63dbc8a53eb904e3ull, 0x5c6193647ccec1a9ull, 0xe7fa7ad65a4ca5bbull, 0x04d2f4278f7272aeull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu, + 0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u, + 0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du, + 0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull; +// Hot table self-test (docs/plans/hot-table.md): hot[0..15], the last 16 words, and FNV-1a 64 over all IGNEUM_HOT_WORDS words. +static const uint32_t IGNEUM_HOT_HEAD[16] = { + 0x8068cc73u, 0x6036ebf9u, 0xb604cd25u, 0x8ffb840eu, 0xc54074a2u, 0x285c0695u, 0x77512425u, 0xc26a58a7u, + 0x72c88757u, 0xc10fca78u, 0x513825ddu, 0x30d6ccc8u, 0x9a05e7cfu, 0xb9533f50u, 0x4bac3ba0u, 0xa5c19528u +}; +static const uint32_t IGNEUM_HOT_LAST[16] = { + 0x2e5537fcu, 0x5e1daf5au, 0x4068fac1u, 0xe48688a5u, 0x38ff563au, 0xbd50595eu, 0x4fd1cffdu, 0xcbdad89au, + 0xecd4eaebu, 0xfd452ab4u, 0xcb2ba071u, 0xc147b12cu, 0x2863ad2au, 0x0f58974fu, 0xe0804105u, 0x7a2298aau +}; +static const uint64_t IGNEUM_HOT_FNV64 = 0xc1767ba3ef02719full; diff --git a/proto-cuda/packs-ca2-hot/hot32k4/vectors.json b/proto-cuda/packs-ca2-hot/hot32k4/vectors.json new file mode 100644 index 00000000..97fa3028 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4/vectors.json @@ -0,0 +1,39 @@ +{ + "seed": "igneum-genesis", + "day": "2026-10-03", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0xd6dd7fa4e8ce0412", "0x7227f09f836fdfdd", "0xf278ed201cceafbf", "0xec020afe5a5250ac", "0xf665fb744a23508d", "0x279db29eb25d9ed7", "0x94acf8ad2077b200", "0x4570de146f552f46", + "0x41af921801576a43", "0xbcedf5682351398e", "0x2f85798eaf3888d2", "0xb1973b681b608696", "0x43e637d721e2e02c", "0x348f3e941642b441", "0x7a6d0c65ef9b3d99", "0xd0b3d21c07182e2a", + "0x381c06bc6910e404", "0x2d6c2d4c8c64a835", "0x2a86874d214946a3", "0x2b59e5cd9a5b7b34", "0x59410384d5464398", "0x8a849a3e01c9c3a2", "0xa125cf7866802a38", "0xe1a8bbe849d936c8", + "0x0ca4566095a358ce", "0xffe671a4d5e8ccc8", "0x57d840333cb092a6", "0xf97100913713d0de", "0x442b713409774c64", "0xa10cf4e4c635ed10", "0xcf114a69a2f009b2", "0x36cd1a0b5f16adac" + ]}, + {"base_nonce": 4096, "expected": [ + "0x207cc1a5be16289f", "0x9ed2b155c8fc75e3", "0x2166ea7f0e13ac0d", "0x44e3ee81290df4c9", "0xc2ecf745975f1401", "0x92be86c70096f95d", "0xfd379260776410f7", "0x6cf6e19d5970bbb3", + "0x3a8b0506bcbf743c", "0x10874f7196f213a0", "0x694b4277c2aab714", "0x343a854f40a54ea9", "0xedd91ea3095028cd", "0x159ff1bc3d1dbf23", "0x337fccf9a5f8b745", "0x64e49f51dceeb174", + "0xd9e29b85844c0b6c", "0x1223fdc9653f8a81", "0xfe4012a89d8cba42", "0x366e927907cee509", "0xe3b28f6d222d2a24", "0x45670ab50f08b3a3", "0xb18c0b1390d13480", "0xe8e65e2c34e96e4a", + "0x105bdc8261ccafef", "0xdf0ab212de234c1c", "0x13ce611089323eaf", "0x8b964cb8e8ccdba2", "0xee088976c82e24e1", "0x4a12e75839901f2a", "0x393d6ceb206e9f2d", "0xd609e6da965d5304" + ]}, + {"base_nonce": 1000000, "expected": [ + "0xca3bb360bd104e97", "0x534f429fa0c22461", "0xb7c03deeb1e3f559", "0xf1f803cd6f5f31e5", "0x4f68db8c38c02ea1", "0x3049cbc39de3c35c", "0x903fdd301faa7a1f", "0xffb430e35c2c4a6f", + "0x0382a5e9c1446e5c", "0xbec1b6d8a8f169f5", "0x447215c419241bc4", "0xa70e933c1419bd7e", "0xac89eacda4f55c87", "0x3d0ca0cd39354441", "0xf0052aa94cc5abcf", "0xfd6faddd678d1b18", + "0x8c1d8aac97e5ffd4", "0x7f831c277f83614f", "0xce48cf6eafce98c6", "0xaba6c8080267b4a5", "0x1afbd998e0cd01d6", "0xc1eac18a208d66cb", "0x27f5f333f9f753ff", "0x7e42e3cc90be0cd7", + "0x69694bcd49ff23c9", "0xc8abe7af7b18359d", "0x54597507488baadf", "0x2b2550309446c26b", "0x63dbc8a53eb904e3", "0x5c6193647ccec1a9", "0xe7fa7ad65a4ca5bb", "0x04d2f4278f7272ae" + ]} + ], + "dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"], + "dataset_last_index": 268435455, + "dataset_last": "0xa33ada72", + "dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}], + "cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"], + "cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"], + "cache_fnv1a64": "0x48c4f5bf24166b2e", + "hot_head": ["0x8068cc73", "0x6036ebf9", "0xb604cd25", "0x8ffb840e", "0xc54074a2", "0x285c0695", "0x77512425", "0xc26a58a7", "0x72c88757", "0xc10fca78", "0x513825dd", "0x30d6ccc8", "0x9a05e7cf", "0xb9533f50", "0x4bac3ba0", "0xa5c19528"], + "hot_last_line": ["0x2e5537fc", "0x5e1daf5a", "0x4068fac1", "0xe48688a5", "0x38ff563a", "0xbd50595e", "0x4fd1cffd", "0xcbdad89a", "0xecd4eaeb", "0xfd452ab4", "0xcb2ba071", "0xc147b12c", "0x2863ad2a", "0x0f58974f", "0xe0804105", "0x7a2298aa"], + "hot_fnv1a64": "0xc1767ba3ef02719f" +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4a/kernel.cl b/proto-cuda/packs-ca2-hot/hot32k4a/kernel.cl new file mode 100644 index 00000000..b2c90a66 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4a/kernel.cl @@ -0,0 +1,305 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (32 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x00800000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 32 MiB = 8192 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 1u); r7 = r7 ^ t_; } // 1 shfl + r0 = r0 | r2; // 2 or + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 16u); r3 = r3 ^ t_; } // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[mul_hi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = mul_hi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 8u); r3 = r3 ^ t_; } // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[mul_hi(r6, HOT_WORDS)]; // 34 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 2u); r4 = r4 ^ t_; } // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r6 = r6 ^ t_; } // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r0 = r0 ^ t_; } // 54 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 16u); r0 = r0 ^ t_; } // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r3 = r3 ^ t_; } // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-hot/hot32k4a/kernel.cu b/proto-cuda/packs-ca2-hot/hot32k4a/kernel.cu new file mode 100644 index 00000000..1ad7393a --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4a/kernel.cu @@ -0,0 +1,179 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +// Hot table (32 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x00800000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (ht_segment is in memhard.h): one thread per segment. +__global__ void igneum_hot_fill(uint32_t* hot, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r6 = r6 | r4; // 0 or + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r4, 1); // 1 shfl + r0 = r0 | r2; // 2 or + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r0, 16); // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[__umulhi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = __umulhi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 8); // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[__umulhi(r6, HOT_WORDS)]; // 34 hot + r4 = r4 ^ __shfl_xor_sync(0xffffffffu, r0, 2); // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 54 shfl + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 16); // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_hot_fill<<>>(hot, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4a/kernel_bound.cl b/proto-cuda/packs-ca2-hot/hot32k4a/kernel_bound.cl new file mode 100644 index 00000000..c8e463b0 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4a/kernel_bound.cl @@ -0,0 +1,399 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (32 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x00800000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 32 MiB = 8192 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 1u); r7 = r7 ^ t_; } // 1 shfl + r0 = r0 | r2; // 2 or + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 16u); r3 = r3 ^ t_; } // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[mul_hi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = mul_hi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 8u); r3 = r3 ^ t_; } // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[mul_hi(r6, HOT_WORDS)]; // 34 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 2u); r4 = r4 ^ t_; } // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r6 = r6 ^ t_; } // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r0 = r0 ^ t_; } // 54 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 16u); r0 = r0 ^ t_; } // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r3 = r3 ^ t_; } // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 1u); r7 = r7 ^ t_; } // 1 shfl + r0 = r0 | r2; // 2 or + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 16u); r3 = r3 ^ t_; } // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[mul_hi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = mul_hi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 8u); r3 = r3 ^ t_; } // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[mul_hi(r6, HOT_WORDS)]; // 34 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 2u); r4 = r4 ^ t_; } // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r6 = r6 ^ t_; } // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r0 = r0 ^ t_; } // 54 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 16u); r0 = r0 ^ t_; } // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r3 = r3 ^ t_; } // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4a/kernel_bound.cu b/proto-cuda/packs-ca2-hot/hot32k4a/kernel_bound.cu new file mode 100644 index 00000000..1e164c42 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4a/kernel_bound.cu @@ -0,0 +1,125 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +// Hot table (32 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x00800000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r6 = r6 | r4; // 0 or + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r4, 1); // 1 shfl + r0 = r0 | r2; // 2 or + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r0, 16); // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[__umulhi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = __umulhi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 8); // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[__umulhi(r6, HOT_WORDS)]; // 34 hot + r4 = r4 ^ __shfl_xor_sync(0xffffffffu, r0, 2); // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 54 shfl + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 16); // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, const uint32_t* hot, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4a/memhard.h b/proto-cuda/packs-ca2-hot/hot32k4a/memhard.h new file mode 100644 index 00000000..4e0eee82 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4a/memhard.h @@ -0,0 +1,129 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Hot table (docs/plans/hot-table.md): 32 MiB = 8192 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +IGNEUM_HD void ht_segment(uint32_t* hot, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4a/memhard.metal b/proto-cuda/packs-ca2-hot/hot32k4a/memhard.metal new file mode 100644 index 00000000..4325ec79 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4a/memhard.metal @@ -0,0 +1,131 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; +} + +// Hot table (docs/plans/hot-table.md): 32 MiB = 8192 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +inline void ht_segment(device uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one thread per segment (IGNEUM_HOT_SEGMENTS threads). +kernel void igneum_hot_fill(device uint* hot [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + ht_segment(hot, gid); +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4a/program.h b/proto-cuda/packs-ca2-hot/hot32k4a/program.h new file mode 100644 index 00000000..d79dcf8b --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4a/program.h @@ -0,0 +1,70 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-genesis" +#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973" +#define IGNEUM_GENERATOR 2 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0xd40c36ab07d82002ull +#define IGNEUM_DAY_STRING "2026-10-03" +#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033" +#define IGNEUM_DAY0 0x3067619fu +#define IGNEUM_DAY1 0x3c269176u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 160 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 shfl=8 add=6 xor=6 mul=5 rotr=5 hot=4 mad=4 or=4 sub=4 mulhi=1 rotl=1" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "hot32k4a" +#define IGNEUM_LOAD_SLOTS 20 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Hot table (hot-table experiment, 5 October 2026, docs/plans/hot-table.md): NOT the lottery hash. IGNEUM_HOT_SLOTS of the +// 16 load slots read H[mulhi(src, IGNEUM_HOT_WORDS)], H = IGNEUM_HOT_MB MiB of chained ChaCha12 lines (the cache chain) under +// KH = seed_words("igneum-hot/" || epoch seed bytes) and the tag "Igne" "umHT"; filled by igneum_hot_fill once per epoch. +#define IGNEUM_HOT_MB 32 +#define IGNEUM_HOT_WORDS 0x00800000u +#define IGNEUM_HOT_SEGMENTS 8192u +#define IGNEUM_HOT_SLOTS 4 // hot loads per program (32 per hash), added beside the dataset loads (16 of them) +#define IGNEUM_HOT_ADDED 1 +#define IGNEUM_HOT_KEY_INIT { 0x3a48bef5u, 0x6b54b1a1u, 0x9ff897c3u, 0x7d5d85e4u, 0xcd35379fu, 0x9d76bb86u, 0xbe2affb1u, 0x0f8f80b4u } +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u } +#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u } +#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u } +#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-hot/hot32k4a/program.json b/proto-cuda/packs-ca2-hot/hot32k4a/program.json new file mode 100644 index 00000000..0d46fc05 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4a/program.json @@ -0,0 +1,129 @@ +{ + "format": "igneum-program-pack-3", + "generator": 2, + "attempt": 0, + "program_id": "0xd40c36ab07d82002", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-genesis", + "seed_bytes": "69676e65756d2d67656e65736973", + "seed_words": ["0x67a9a7be", "0x1a155b25", "0xfddfb732", "0x4b5af2e8", "0xc55caf33", "0xa27c13b7", "0x06628a48", "0x03852469"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 160, + "load_class": "hot32k4a", + "load_slots": 20, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "hot_table": {"mb": 32, "words": 8388608, "segments": 8192, "slots": 4, "form": "added: k load slots added beside the class's, the dataset loads unchanged", "dataset_slots": 16, "hot_loads_per_hash": 32, "key": ["0x3a48bef5", "0x6b54b1a1", "0x9ff897c3", "0x7d5d85e4", "0xcd35379f", "0x9d76bb86", "0xbe2affb1", "0x0f8f80b4"], "key_derivation": "seed_words_from_bytes('igneum-hot/' || seed_bytes), the same for every attempt of the epoch", "tag": ["0x49676e65", "0x756d4854"], "chain": "the cache chain of dataset.cache with the hot key and tag: in_j = prev ^ (sigma || key || seg || j || tag); line_j = block(in_j); prev_0 = 0", "load": "dst = dst ^ hot[mulhi(src, words)] (the high 32 bits of the 64-bit product; the index lies in [0, words) for any size)", "slots_rule": "the first k load slots in the Fisher-Yates draw order after the scratch slots (a uniform k-subset); no extra draw, so with version 2 widths and no scratch the program is the version 2 program with k loads redirected", "acceptance_stand_in": "dataset_elem(mulhi(src, words), seed_words[2], seed_words[3])", "program_id": "the read-width id with 'hot/' || mb || k appended", "spec": "docs/plans/hot-table.md"}, + "op_mix": {"load": 16, "shfl": 8, "add": 6, "xor": 6, "mul": 5, "rotr": 5, "hot": 4, "mad": 4, "or": 4, "sub": 4, "mulhi": 1, "rotl": 1}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)", + "hot": "dst = dst ^ hot[mulhi(src, hot_table.words)] (hot-table experiment)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "2026-10-03", + "day_bytes": "6461792f323032362d31302d3033", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0x3067619f", + "d1": "0x3c269176", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "or", "dst": 6, "src": 4, "src2": 2, "imm": "0x535c5dd3", "imm2": "0xf8694615", "rot": 3, "bit": 1, "mask": 16, "width": 1}, + {"i": 1, "op": "shfl", "dst": 7, "src": 4, "src2": 6, "imm": "0x87bc9ee4", "imm2": "0xfdb0c856", "rot": 9, "bit": 25, "mask": 1, "width": 1}, + {"i": 2, "op": "or", "dst": 0, "src": 2, "src2": 4, "imm": "0xaf91f0c8", "imm2": "0xe2d5d1fa", "rot": 1, "bit": 24, "mask": 2, "width": 1}, + {"i": 3, "op": "shfl", "dst": 3, "src": 0, "src2": 0, "imm": "0x3044ba32", "imm2": "0x7b1a7ffe", "rot": 14, "bit": 10, "mask": 16, "width": 1}, + {"i": 4, "op": "load", "dst": 7, "src": 3, "src2": 6, "imm": "0x5d1ca2a2", "imm2": "0xe2481807", "rot": 24, "bit": 3, "mask": 1, "width": 1}, + {"i": 5, "op": "load", "dst": 6, "src": 0, "src2": 6, "imm": "0xaba3dbaa", "imm2": "0x987c017a", "rot": 16, "bit": 30, "mask": 1, "width": 1}, + {"i": 6, "op": "mul", "dst": 1, "src": 4, "src2": 6, "imm": "0x12a93d05", "imm2": "0x10761047", "rot": 17, "bit": 17, "mask": 4, "width": 1}, + {"i": 7, "op": "sub", "dst": 0, "src": 1, "src2": 0, "imm": "0x8884e389", "imm2": "0x31009b67", "rot": 5, "bit": 30, "mask": 1, "width": 1}, + {"i": 8, "op": "load", "dst": 3, "src": 7, "src2": 5, "imm": "0xfa6358b3", "imm2": "0xfea9038f", "rot": 17, "bit": 18, "mask": 2, "width": 1}, + {"i": 9, "op": "mad", "dst": 1, "src": 0, "src2": 3, "imm": "0xb154ae4f", "imm2": "0xe85f13f6", "rot": 6, "bit": 18, "mask": 16, "width": 1}, + {"i": 10, "op": "mul", "dst": 4, "src": 0, "src2": 4, "imm": "0x9edffbc3", "imm2": "0x643812d4", "rot": 24, "bit": 25, "mask": 4, "width": 1}, + {"i": 11, "op": "load", "dst": 5, "src": 4, "src2": 1, "imm": "0x163dbb1b", "imm2": "0x20c1f743", "rot": 27, "bit": 25, "mask": 16, "width": 1}, + {"i": 12, "op": "xor", "dst": 1, "src": 7, "src2": 4, "imm": "0x9cadb4f8", "imm2": "0xd9da785b", "rot": 8, "bit": 26, "mask": 4, "width": 1}, + {"i": 13, "op": "load", "dst": 1, "src": 6, "src2": 6, "imm": "0x1bb1b429", "imm2": "0x33d1e391", "rot": 20, "bit": 19, "mask": 16, "width": 1}, + {"i": 14, "op": "load", "dst": 2, "src": 1, "src2": 6, "imm": "0xa672cdd3", "imm2": "0x59a4829c", "rot": 22, "bit": 13, "mask": 16, "width": 1}, + {"i": 15, "op": "mul", "dst": 3, "src": 1, "src2": 4, "imm": "0xe3c4bf4d", "imm2": "0x028b4d37", "rot": 11, "bit": 28, "mask": 8, "width": 1}, + {"i": 16, "op": "hot", "dst": 6, "src": 5, "src2": 7, "imm": "0x71fbe6f2", "imm2": "0x61b6261e", "rot": 15, "bit": 9, "mask": 1, "width": 1}, + {"i": 17, "op": "load", "dst": 0, "src": 2, "src2": 5, "imm": "0x8e516fd3", "imm2": "0x2aa480c2", "rot": 19, "bit": 1, "mask": 8, "width": 1}, + {"i": 18, "op": "add", "dst": 0, "src": 7, "src2": 6, "imm": "0xc035a4e6", "imm2": "0x535b545f", "rot": 18, "bit": 26, "mask": 8, "width": 1}, + {"i": 19, "op": "sub", "dst": 5, "src": 0, "src2": 2, "imm": "0xde38f954", "imm2": "0xde1507bc", "rot": 31, "bit": 21, "mask": 4, "width": 1}, + {"i": 20, "op": "mul", "dst": 2, "src": 4, "src2": 1, "imm": "0x3f64b7c9", "imm2": "0x1f07754a", "rot": 23, "bit": 28, "mask": 4, "width": 1}, + {"i": 21, "op": "add", "dst": 2, "src": 1, "src2": 1, "imm": "0x1ecdd2cf", "imm2": "0x925d5e63", "rot": 6, "bit": 6, "mask": 8, "width": 1}, + {"i": 22, "op": "xor", "dst": 3, "src": 7, "src2": 3, "imm": "0x63c7f533", "imm2": "0xd312164d", "rot": 4, "bit": 14, "mask": 2, "width": 1}, + {"i": 23, "op": "load", "dst": 7, "src": 2, "src2": 5, "imm": "0x6d3ddc5a", "imm2": "0x433d8c2a", "rot": 28, "bit": 7, "mask": 2, "width": 1}, + {"i": 24, "op": "mulhi", "dst": 2, "src": 5, "src2": 0, "imm": "0xc7e9887a", "imm2": "0x19ec898f", "rot": 14, "bit": 9, "mask": 1, "width": 1}, + {"i": 25, "op": "rotr", "dst": 5, "src": 2, "src2": 3, "imm": "0x97acf9f2", "imm2": "0xc7fcfc8f", "rot": 8, "bit": 25, "mask": 1, "width": 1}, + {"i": 26, "op": "mad", "dst": 3, "src": 2, "src2": 7, "imm": "0x400383b6", "imm2": "0xe9bab735", "rot": 16, "bit": 18, "mask": 1, "width": 1}, + {"i": 27, "op": "sub", "dst": 2, "src": 0, "src2": 7, "imm": "0x8ea6cd8d", "imm2": "0x55b67f9f", "rot": 19, "bit": 8, "mask": 8, "width": 1}, + {"i": 28, "op": "mad", "dst": 6, "src": 1, "src2": 5, "imm": "0x17723e5a", "imm2": "0x00779664", "rot": 19, "bit": 2, "mask": 4, "width": 1}, + {"i": 29, "op": "add", "dst": 2, "src": 3, "src2": 4, "imm": "0xbdc6da76", "imm2": "0x46b1b505", "rot": 28, "bit": 16, "mask": 16, "width": 1}, + {"i": 30, "op": "shfl", "dst": 3, "src": 2, "src2": 3, "imm": "0xe586702c", "imm2": "0xd83a1462", "rot": 24, "bit": 6, "mask": 8, "width": 1}, + {"i": 31, "op": "load", "dst": 5, "src": 7, "src2": 1, "imm": "0x1cddad42", "imm2": "0xb0c6b0d0", "rot": 25, "bit": 25, "mask": 8, "width": 1}, + {"i": 32, "op": "hot", "dst": 2, "src": 0, "src2": 0, "imm": "0x2abcfdc6", "imm2": "0xfa4cc809", "rot": 22, "bit": 24, "mask": 2, "width": 1}, + {"i": 33, "op": "or", "dst": 6, "src": 3, "src2": 1, "imm": "0x61c9a38d", "imm2": "0x86597500", "rot": 15, "bit": 8, "mask": 2, "width": 1}, + {"i": 34, "op": "hot", "dst": 3, "src": 6, "src2": 7, "imm": "0xc37723fa", "imm2": "0xf3b024da", "rot": 16, "bit": 27, "mask": 16, "width": 1}, + {"i": 35, "op": "shfl", "dst": 4, "src": 0, "src2": 5, "imm": "0x84cad367", "imm2": "0xcc7972c4", "rot": 21, "bit": 17, "mask": 2, "width": 1}, + {"i": 36, "op": "xor", "dst": 5, "src": 2, "src2": 0, "imm": "0xc24a7d70", "imm2": "0x6591d24c", "rot": 21, "bit": 24, "mask": 1, "width": 1}, + {"i": 37, "op": "load", "dst": 3, "src": 4, "src2": 5, "imm": "0xd404cfe8", "imm2": "0x144ca538", "rot": 10, "bit": 11, "mask": 2, "width": 1}, + {"i": 38, "op": "load", "dst": 4, "src": 3, "src2": 3, "imm": "0x04b0080c", "imm2": "0xb938c290", "rot": 10, "bit": 1, "mask": 8, "width": 1}, + {"i": 39, "op": "rotr", "dst": 1, "src": 4, "src2": 5, "imm": "0x9fe93344", "imm2": "0xff7296e4", "rot": 29, "bit": 14, "mask": 1, "width": 1}, + {"i": 40, "op": "add", "dst": 3, "src": 6, "src2": 2, "imm": "0xbc3ff65f", "imm2": "0x6328cb2c", "rot": 4, "bit": 28, "mask": 16, "width": 1}, + {"i": 41, "op": "xor", "dst": 5, "src": 1, "src2": 1, "imm": "0xdcf4a02e", "imm2": "0x5ee3a976", "rot": 17, "bit": 0, "mask": 8, "width": 1}, + {"i": 42, "op": "or", "dst": 5, "src": 0, "src2": 1, "imm": "0x44846c7a", "imm2": "0x67338877", "rot": 27, "bit": 18, "mask": 1, "width": 1}, + {"i": 43, "op": "xor", "dst": 7, "src": 3, "src2": 3, "imm": "0x1b053acf", "imm2": "0x32e2d23d", "rot": 31, "bit": 23, "mask": 8, "width": 1}, + {"i": 44, "op": "load", "dst": 2, "src": 5, "src2": 4, "imm": "0xf8662282", "imm2": "0x10bb9e30", "rot": 8, "bit": 6, "mask": 2, "width": 1}, + {"i": 45, "op": "shfl", "dst": 6, "src": 4, "src2": 4, "imm": "0x87d9ef84", "imm2": "0x49087d74", "rot": 1, "bit": 2, "mask": 8, "width": 1}, + {"i": 46, "op": "xor", "dst": 5, "src": 3, "src2": 1, "imm": "0x74a59b7d", "imm2": "0xc4766ff1", "rot": 30, "bit": 3, "mask": 16, "width": 1}, + {"i": 47, "op": "add", "dst": 7, "src": 6, "src2": 2, "imm": "0x5af3bd5b", "imm2": "0x9342df0d", "rot": 26, "bit": 22, "mask": 2, "width": 1}, + {"i": 48, "op": "add", "dst": 3, "src": 4, "src2": 5, "imm": "0x9ff95776", "imm2": "0x485614db", "rot": 27, "bit": 23, "mask": 1, "width": 1}, + {"i": 49, "op": "load", "dst": 5, "src": 3, "src2": 1, "imm": "0xd022a812", "imm2": "0xfbe147d6", "rot": 15, "bit": 29, "mask": 4, "width": 1}, + {"i": 50, "op": "rotr", "dst": 4, "src": 5, "src2": 7, "imm": "0x7e099325", "imm2": "0x40d55d10", "rot": 29, "bit": 15, "mask": 16, "width": 1}, + {"i": 51, "op": "mad", "dst": 0, "src": 1, "src2": 7, "imm": "0xd8ab8843", "imm2": "0x7f842b90", "rot": 16, "bit": 9, "mask": 1, "width": 1}, + {"i": 52, "op": "mul", "dst": 0, "src": 3, "src2": 6, "imm": "0x4c30250b", "imm2": "0x9ee1681f", "rot": 20, "bit": 3, "mask": 8, "width": 1}, + {"i": 53, "op": "rotr", "dst": 5, "src": 4, "src2": 4, "imm": "0xc1c15026", "imm2": "0x588915e7", "rot": 1, "bit": 29, "mask": 16, "width": 1}, + {"i": 54, "op": "shfl", "dst": 0, "src": 6, "src2": 4, "imm": "0x636a9dc4", "imm2": "0xac023d9b", "rot": 22, "bit": 29, "mask": 1, "width": 1}, + {"i": 55, "op": "shfl", "dst": 0, "src": 7, "src2": 1, "imm": "0x5c933bd0", "imm2": "0x2baec8c9", "rot": 25, "bit": 27, "mask": 16, "width": 1}, + {"i": 56, "op": "load", "dst": 7, "src": 4, "src2": 7, "imm": "0xf62515d5", "imm2": "0x4a164f9f", "rot": 28, "bit": 30, "mask": 2, "width": 1}, + {"i": 57, "op": "rotr", "dst": 7, "src": 3, "src2": 7, "imm": "0x15ef64da", "imm2": "0x7149e3c9", "rot": 28, "bit": 30, "mask": 2, "width": 1}, + {"i": 58, "op": "load", "dst": 0, "src": 6, "src2": 2, "imm": "0xcde7100c", "imm2": "0x040fc0cf", "rot": 8, "bit": 28, "mask": 2, "width": 1}, + {"i": 59, "op": "hot", "dst": 6, "src": 2, "src2": 1, "imm": "0xcf8b48be", "imm2": "0x14cb1d3f", "rot": 16, "bit": 19, "mask": 1, "width": 1}, + {"i": 60, "op": "shfl", "dst": 3, "src": 2, "src2": 1, "imm": "0xae1529ee", "imm2": "0x732bc114", "rot": 4, "bit": 23, "mask": 1, "width": 1}, + {"i": 61, "op": "sub", "dst": 7, "src": 5, "src2": 7, "imm": "0x0a8f8168", "imm2": "0xf1c9b0ed", "rot": 29, "bit": 2, "mask": 8, "width": 1}, + {"i": 62, "op": "rotl", "dst": 1, "src": 2, "src2": 3, "imm": "0x9b7914dd", "imm2": "0xd14b33a3", "rot": 4, "bit": 16, "mask": 16, "width": 1}, + {"i": 63, "op": "load", "dst": 4, "src": 5, "src2": 1, "imm": "0xbbaa8e24", "imm2": "0xd15ec6a6", "rot": 24, "bit": 2, "mask": 2, "width": 1} + ] +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4a/program.metal b/proto-cuda/packs-ca2-hot/hot32k4a/program.metal new file mode 100644 index 00000000..9e29a844 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4a/program.metal @@ -0,0 +1,112 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (32 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x00800000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + device const uint* hot [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 + r7 = r7 ^ simd_shuffle_xor(r4, (ushort)1); // 1 + r0 = r0 | r2; // 2 + r3 = r3 ^ simd_shuffle_xor(r0, (ushort)16); // 3 + r7 = r7 ^ dataset[r3 & MASK]; // 4 + r6 = r6 ^ dataset[r0 & MASK]; // 5 + r1 = r1 * r4; // 6 + r0 = r0 - r1; // 7 + r3 = r3 ^ dataset[r7 & MASK]; // 8 + r1 = r0 * r3 + r1; // 9 + r4 = r4 * r0; // 10 + r5 = r5 ^ dataset[r4 & MASK]; // 11 + r1 = r1 ^ r7; // 12 + r1 = r1 ^ dataset[r6 & MASK]; // 13 + r2 = r2 ^ dataset[r1 & MASK]; // 14 + r3 = r3 * r1; // 15 + r6 = r6 ^ hot[mulhi(r5, HOT_WORDS)]; // 16 + r0 = r0 ^ dataset[r2 & MASK]; // 17 + r0 = r0 + r7 + select(0xc035a4e6u, 0x535b545fu, ((sel >> 26u) & 1u) != 0u); // 18 + r5 = r5 - r0; // 19 + r2 = r2 * r4; // 20 + r2 = r2 + r1 + select(0x1ecdd2cfu, 0x925d5e63u, ((sel >> 6u) & 1u) != 0u); // 21 + r3 = r3 ^ r7; // 22 + r7 = r7 ^ dataset[r2 & MASK]; // 23 + r2 = mulhi(r2, r5); // 24 + r5 = rotr_var(r5, r2); // 25 + r3 = r2 * r7 + r3; // 26 + r2 = r2 - r0; // 27 + r6 = r1 * r5 + r6; // 28 + r2 = r2 + r3 + select(0xbdc6da76u, 0x46b1b505u, ((sel >> 16u) & 1u) != 0u); // 29 + r3 = r3 ^ simd_shuffle_xor(r2, (ushort)8); // 30 + r5 = r5 ^ dataset[r7 & MASK]; // 31 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r6 = r6 | r3; // 33 + r3 = r3 ^ hot[mulhi(r6, HOT_WORDS)]; // 34 + r4 = r4 ^ simd_shuffle_xor(r0, (ushort)2); // 35 + r5 = r5 ^ r2; // 36 + r3 = r3 ^ dataset[r4 & MASK]; // 37 + r4 = r4 ^ dataset[r3 & MASK]; // 38 + r1 = rotr_var(r1, r4); // 39 + r3 = r3 + r6 + select(0xbc3ff65fu, 0x6328cb2cu, ((sel >> 28u) & 1u) != 0u); // 40 + r5 = r5 ^ r1; // 41 + r5 = r5 | r0; // 42 + r7 = r7 ^ r3; // 43 + r2 = r2 ^ dataset[r5 & MASK]; // 44 + r6 = r6 ^ simd_shuffle_xor(r4, (ushort)8); // 45 + r5 = r5 ^ r3; // 46 + r7 = r7 + r6 + select(0x5af3bd5bu, 0x9342df0du, ((sel >> 22u) & 1u) != 0u); // 47 + r3 = r3 + r4 + select(0x9ff95776u, 0x485614dbu, ((sel >> 23u) & 1u) != 0u); // 48 + r5 = r5 ^ dataset[r3 & MASK]; // 49 + r4 = rotr_var(r4, r5); // 50 + r0 = r1 * r7 + r0; // 51 + r0 = r0 * r3; // 52 + r5 = rotr_var(r5, r4); // 53 + r0 = r0 ^ simd_shuffle_xor(r6, (ushort)1); // 54 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)16); // 55 + r7 = r7 ^ dataset[r4 & MASK]; // 56 + r7 = rotr_var(r7, r3); // 57 + r0 = r0 ^ dataset[r6 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r3 ^ simd_shuffle_xor(r2, (ushort)1); // 60 + r7 = r7 - r5; // 61 + r1 = rotl_imm(r1, 4u); // 62 + r4 = r4 ^ dataset[r5 & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4a/program_bound.metal b/proto-cuda/packs-ca2-hot/hot32k4a/program_bound.metal new file mode 100644 index 00000000..79e8d649 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4a/program_bound.metal @@ -0,0 +1,114 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (32 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x00800000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + device const uint* hot [[buffer(4)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 + r7 = r7 ^ simd_shuffle_xor(r4, (ushort)1); // 1 + r0 = r0 | r2; // 2 + r3 = r3 ^ simd_shuffle_xor(r0, (ushort)16); // 3 + r7 = r7 ^ dataset[r3 & MASK]; // 4 + r6 = r6 ^ dataset[r0 & MASK]; // 5 + r1 = r1 * r4; // 6 + r0 = r0 - r1; // 7 + r3 = r3 ^ dataset[r7 & MASK]; // 8 + r1 = r0 * r3 + r1; // 9 + r4 = r4 * r0; // 10 + r5 = r5 ^ dataset[r4 & MASK]; // 11 + r1 = r1 ^ r7; // 12 + r1 = r1 ^ dataset[r6 & MASK]; // 13 + r2 = r2 ^ dataset[r1 & MASK]; // 14 + r3 = r3 * r1; // 15 + r6 = r6 ^ hot[mulhi(r5, HOT_WORDS)]; // 16 + r0 = r0 ^ dataset[r2 & MASK]; // 17 + r0 = r0 + r7 + select(0xc035a4e6u, 0x535b545fu, ((sel >> 26u) & 1u) != 0u); // 18 + r5 = r5 - r0; // 19 + r2 = r2 * r4; // 20 + r2 = r2 + r1 + select(0x1ecdd2cfu, 0x925d5e63u, ((sel >> 6u) & 1u) != 0u); // 21 + r3 = r3 ^ r7; // 22 + r7 = r7 ^ dataset[r2 & MASK]; // 23 + r2 = mulhi(r2, r5); // 24 + r5 = rotr_var(r5, r2); // 25 + r3 = r2 * r7 + r3; // 26 + r2 = r2 - r0; // 27 + r6 = r1 * r5 + r6; // 28 + r2 = r2 + r3 + select(0xbdc6da76u, 0x46b1b505u, ((sel >> 16u) & 1u) != 0u); // 29 + r3 = r3 ^ simd_shuffle_xor(r2, (ushort)8); // 30 + r5 = r5 ^ dataset[r7 & MASK]; // 31 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r6 = r6 | r3; // 33 + r3 = r3 ^ hot[mulhi(r6, HOT_WORDS)]; // 34 + r4 = r4 ^ simd_shuffle_xor(r0, (ushort)2); // 35 + r5 = r5 ^ r2; // 36 + r3 = r3 ^ dataset[r4 & MASK]; // 37 + r4 = r4 ^ dataset[r3 & MASK]; // 38 + r1 = rotr_var(r1, r4); // 39 + r3 = r3 + r6 + select(0xbc3ff65fu, 0x6328cb2cu, ((sel >> 28u) & 1u) != 0u); // 40 + r5 = r5 ^ r1; // 41 + r5 = r5 | r0; // 42 + r7 = r7 ^ r3; // 43 + r2 = r2 ^ dataset[r5 & MASK]; // 44 + r6 = r6 ^ simd_shuffle_xor(r4, (ushort)8); // 45 + r5 = r5 ^ r3; // 46 + r7 = r7 + r6 + select(0x5af3bd5bu, 0x9342df0du, ((sel >> 22u) & 1u) != 0u); // 47 + r3 = r3 + r4 + select(0x9ff95776u, 0x485614dbu, ((sel >> 23u) & 1u) != 0u); // 48 + r5 = r5 ^ dataset[r3 & MASK]; // 49 + r4 = rotr_var(r4, r5); // 50 + r0 = r1 * r7 + r0; // 51 + r0 = r0 * r3; // 52 + r5 = rotr_var(r5, r4); // 53 + r0 = r0 ^ simd_shuffle_xor(r6, (ushort)1); // 54 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)16); // 55 + r7 = r7 ^ dataset[r4 & MASK]; // 56 + r7 = rotr_var(r7, r3); // 57 + r0 = r0 ^ dataset[r6 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r3 ^ simd_shuffle_xor(r2, (ushort)1); // 60 + r7 = r7 - r5; // 61 + r1 = rotl_imm(r1, 4u); // 62 + r4 = r4 ^ dataset[r5 & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot32k4a/vectors.h b/proto-cuda/packs-ca2-hot/hot32k4a/vectors.h new file mode 100644 index 00000000..5fe569dc --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4a/vectors.h @@ -0,0 +1,67 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0x38c6c58d536902f6ull, 0xeca297b8b15ee71dull, 0x5de01e156c268debull, 0x1e5cd7af09839b5eull, 0x426a782b31120694ull, 0xc8ab7a4e0afb32cbull, 0xcb837c845978ac7dull, 0x23b49de45ebc7c11ull, + 0x7c0c5cf7351c2d8dull, 0xf0b045dc0ee668f2ull, 0x2c5fa9b30927a776ull, 0x2aa40e68443d17b4ull, 0xb26d47f8416fbae3ull, 0x9a89690fe2ec670eull, 0x3c0e6e8eeefadc75ull, 0x051c17f50deeac6bull, + 0x6dea6ee96547b120ull, 0xc77505c9a33bfd3eull, 0x01053c0daf25675eull, 0xfc848d11c52e74b0ull, 0x62c401ad3901bb70ull, 0x814874675dd17b2full, 0x4f9f5bd5ad5654a1ull, 0x4eff35d3051104c4ull, + 0xb43c9692e05e06c9ull, 0x83d5eae233ab4ef8ull, 0x4eec3566c6704968ull, 0x6898ee32326466b7ull, 0xbae78f0e96eaee59ull, 0x6fb1ca15f2821951ull, 0xb921ea490246b277ull, 0x65191db7bb92704dull + }, + { // base nonce 4096 + 0x48173935e10a494aull, 0x066dd67c062c745dull, 0xbbd46811f0e3b431ull, 0xc2bea4945c842815ull, 0x4b551ffd6ce1ecfaull, 0x8b66b40f1f943c26ull, 0x963e4d85b6f859f7ull, 0xf9074c2c4c1ea3dfull, + 0x3f636d3d52790daaull, 0x7262e3045c51b1a4ull, 0x553fa2addcdea901ull, 0xa1b3cd0352d644f0ull, 0x9a552108f5e8d60dull, 0xfa36f2ee4133a5abull, 0xdf948681ab41478aull, 0xa751577e6ae0dcadull, + 0x52021cc6ca69492full, 0x2a00c1d68707da0eull, 0xf4bed9e03a59067full, 0xa771bd7f7474b8b1ull, 0x7e1b2cd186e17fa5ull, 0x14da55f44e6d124aull, 0x75764180011d0091ull, 0xf7ab7903fc13e1a2ull, + 0xa8f872e445ffcf33ull, 0x60ff937b67bb6402ull, 0x6b49537c29d3718eull, 0xf69d979ac3a5d090ull, 0x6a55aa7340b6165dull, 0xdbbcb64c2a24c99cull, 0xa268f115f13fe127ull, 0xcd5fdcbfcaec70c7ull + }, + { // base nonce 1000000 + 0x76bf4ad8a26dbba4ull, 0x2e9fbdd49584b54cull, 0x987ed8813b3f0081ull, 0xf731f37a24cb3aecull, 0x423835c437849ecfull, 0x254e0f1383ecd39cull, 0xbaf5f2d123ded780ull, 0xb80051f68ec6d039ull, + 0xcd1b2835e6a6dc7aull, 0xc4fadc46cf78c3f1ull, 0x9ff6e37471b41d78ull, 0xf80e51b0a2f2a594ull, 0xa2a5e9f0e5f5a987ull, 0xb088c5e41a0249d4ull, 0xe3b2437cd2722245ull, 0x5bfdcfea07b9a065ull, + 0x4e07cfad12ab7371ull, 0xca412ad441fb07baull, 0x602d2b491952aab8ull, 0xb4c1862ac545813bull, 0x1cda1df3eb9bfc85ull, 0x0d215ce8fd579321ull, 0x40882ecd21fe8998ull, 0xb6367f56fdf2043dull, + 0x43722735a16493f8ull, 0x319091f0e4aa9ef3ull, 0x2143e27fbfd051efull, 0xbcaf43b30ae26f99ull, 0xdf31272fcb9eb458ull, 0x845ee6177cbc27c2ull, 0xba4d0c371ca002f0ull, 0x13e29438461f5f80ull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu, + 0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u, + 0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du, + 0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull; +// Hot table self-test (docs/plans/hot-table.md): hot[0..15], the last 16 words, and FNV-1a 64 over all IGNEUM_HOT_WORDS words. +static const uint32_t IGNEUM_HOT_HEAD[16] = { + 0x8068cc73u, 0x6036ebf9u, 0xb604cd25u, 0x8ffb840eu, 0xc54074a2u, 0x285c0695u, 0x77512425u, 0xc26a58a7u, + 0x72c88757u, 0xc10fca78u, 0x513825ddu, 0x30d6ccc8u, 0x9a05e7cfu, 0xb9533f50u, 0x4bac3ba0u, 0xa5c19528u +}; +static const uint32_t IGNEUM_HOT_LAST[16] = { + 0x2e5537fcu, 0x5e1daf5au, 0x4068fac1u, 0xe48688a5u, 0x38ff563au, 0xbd50595eu, 0x4fd1cffdu, 0xcbdad89au, + 0xecd4eaebu, 0xfd452ab4u, 0xcb2ba071u, 0xc147b12cu, 0x2863ad2au, 0x0f58974fu, 0xe0804105u, 0x7a2298aau +}; +static const uint64_t IGNEUM_HOT_FNV64 = 0xc1767ba3ef02719full; diff --git a/proto-cuda/packs-ca2-hot/hot32k4a/vectors.json b/proto-cuda/packs-ca2-hot/hot32k4a/vectors.json new file mode 100644 index 00000000..d4fbee87 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot32k4a/vectors.json @@ -0,0 +1,39 @@ +{ + "seed": "igneum-genesis", + "day": "2026-10-03", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0x38c6c58d536902f6", "0xeca297b8b15ee71d", "0x5de01e156c268deb", "0x1e5cd7af09839b5e", "0x426a782b31120694", "0xc8ab7a4e0afb32cb", "0xcb837c845978ac7d", "0x23b49de45ebc7c11", + "0x7c0c5cf7351c2d8d", "0xf0b045dc0ee668f2", "0x2c5fa9b30927a776", "0x2aa40e68443d17b4", "0xb26d47f8416fbae3", "0x9a89690fe2ec670e", "0x3c0e6e8eeefadc75", "0x051c17f50deeac6b", + "0x6dea6ee96547b120", "0xc77505c9a33bfd3e", "0x01053c0daf25675e", "0xfc848d11c52e74b0", "0x62c401ad3901bb70", "0x814874675dd17b2f", "0x4f9f5bd5ad5654a1", "0x4eff35d3051104c4", + "0xb43c9692e05e06c9", "0x83d5eae233ab4ef8", "0x4eec3566c6704968", "0x6898ee32326466b7", "0xbae78f0e96eaee59", "0x6fb1ca15f2821951", "0xb921ea490246b277", "0x65191db7bb92704d" + ]}, + {"base_nonce": 4096, "expected": [ + "0x48173935e10a494a", "0x066dd67c062c745d", "0xbbd46811f0e3b431", "0xc2bea4945c842815", "0x4b551ffd6ce1ecfa", "0x8b66b40f1f943c26", "0x963e4d85b6f859f7", "0xf9074c2c4c1ea3df", + "0x3f636d3d52790daa", "0x7262e3045c51b1a4", "0x553fa2addcdea901", "0xa1b3cd0352d644f0", "0x9a552108f5e8d60d", "0xfa36f2ee4133a5ab", "0xdf948681ab41478a", "0xa751577e6ae0dcad", + "0x52021cc6ca69492f", "0x2a00c1d68707da0e", "0xf4bed9e03a59067f", "0xa771bd7f7474b8b1", "0x7e1b2cd186e17fa5", "0x14da55f44e6d124a", "0x75764180011d0091", "0xf7ab7903fc13e1a2", + "0xa8f872e445ffcf33", "0x60ff937b67bb6402", "0x6b49537c29d3718e", "0xf69d979ac3a5d090", "0x6a55aa7340b6165d", "0xdbbcb64c2a24c99c", "0xa268f115f13fe127", "0xcd5fdcbfcaec70c7" + ]}, + {"base_nonce": 1000000, "expected": [ + "0x76bf4ad8a26dbba4", "0x2e9fbdd49584b54c", "0x987ed8813b3f0081", "0xf731f37a24cb3aec", "0x423835c437849ecf", "0x254e0f1383ecd39c", "0xbaf5f2d123ded780", "0xb80051f68ec6d039", + "0xcd1b2835e6a6dc7a", "0xc4fadc46cf78c3f1", "0x9ff6e37471b41d78", "0xf80e51b0a2f2a594", "0xa2a5e9f0e5f5a987", "0xb088c5e41a0249d4", "0xe3b2437cd2722245", "0x5bfdcfea07b9a065", + "0x4e07cfad12ab7371", "0xca412ad441fb07ba", "0x602d2b491952aab8", "0xb4c1862ac545813b", "0x1cda1df3eb9bfc85", "0x0d215ce8fd579321", "0x40882ecd21fe8998", "0xb6367f56fdf2043d", + "0x43722735a16493f8", "0x319091f0e4aa9ef3", "0x2143e27fbfd051ef", "0xbcaf43b30ae26f99", "0xdf31272fcb9eb458", "0x845ee6177cbc27c2", "0xba4d0c371ca002f0", "0x13e29438461f5f80" + ]} + ], + "dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"], + "dataset_last_index": 268435455, + "dataset_last": "0xa33ada72", + "dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}], + "cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"], + "cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"], + "cache_fnv1a64": "0x48c4f5bf24166b2e", + "hot_head": ["0x8068cc73", "0x6036ebf9", "0xb604cd25", "0x8ffb840e", "0xc54074a2", "0x285c0695", "0x77512425", "0xc26a58a7", "0x72c88757", "0xc10fca78", "0x513825dd", "0x30d6ccc8", "0x9a05e7cf", "0xb9533f50", "0x4bac3ba0", "0xa5c19528"], + "hot_last_line": ["0x2e5537fc", "0x5e1daf5a", "0x4068fac1", "0xe48688a5", "0x38ff563a", "0xbd50595e", "0x4fd1cffd", "0xcbdad89a", "0xecd4eaeb", "0xfd452ab4", "0xcb2ba071", "0xc147b12c", "0x2863ad2a", "0x0f58974f", "0xe0804105", "0x7a2298aa"], + "hot_fnv1a64": "0xc1767ba3ef02719f" +} diff --git a/proto-cuda/packs-ca2-hot/hot64k2/kernel.cl b/proto-cuda/packs-ca2-hot/hot64k2/kernel.cl new file mode 100644 index 00000000..93db63ec --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k2/kernel.cl @@ -0,0 +1,305 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (64 MiB, 2 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-hot/hot64k2/kernel.cu b/proto-cuda/packs-ca2-hot/hot64k2/kernel.cu new file mode 100644 index 00000000..877ecbf7 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k2/kernel.cu @@ -0,0 +1,179 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +// Hot table (64 MiB, 2 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (ht_segment is in memhard.h): one thread per segment. +__global__ void igneum_hot_fill(uint32_t* hot, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[__umulhi(r1, HOT_WORDS)]; // 34 hot + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_hot_fill<<>>(hot, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot64k2/kernel_bound.cl b/proto-cuda/packs-ca2-hot/hot64k2/kernel_bound.cl new file mode 100644 index 00000000..80ad5f3c --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k2/kernel_bound.cl @@ -0,0 +1,399 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (64 MiB, 2 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot64k2/kernel_bound.cu b/proto-cuda/packs-ca2-hot/hot64k2/kernel_bound.cu new file mode 100644 index 00000000..c2f89653 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k2/kernel_bound.cu @@ -0,0 +1,125 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +// Hot table (64 MiB, 2 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[__umulhi(r1, HOT_WORDS)]; // 34 hot + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, const uint32_t* hot, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot64k2/memhard.h b/proto-cuda/packs-ca2-hot/hot64k2/memhard.h new file mode 100644 index 00000000..180f678b --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k2/memhard.h @@ -0,0 +1,129 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +IGNEUM_HD void ht_segment(uint32_t* hot, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} diff --git a/proto-cuda/packs-ca2-hot/hot64k2/memhard.metal b/proto-cuda/packs-ca2-hot/hot64k2/memhard.metal new file mode 100644 index 00000000..c2ec5702 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k2/memhard.metal @@ -0,0 +1,131 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; +} + +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +inline void ht_segment(device uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one thread per segment (IGNEUM_HOT_SEGMENTS threads). +kernel void igneum_hot_fill(device uint* hot [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + ht_segment(hot, gid); +} diff --git a/proto-cuda/packs-ca2-hot/hot64k2/program.h b/proto-cuda/packs-ca2-hot/hot64k2/program.h new file mode 100644 index 00000000..81ba8d05 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k2/program.h @@ -0,0 +1,70 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-genesis" +#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973" +#define IGNEUM_GENERATOR 2 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x322a62466d49ff66ull +#define IGNEUM_DAY_STRING "2026-10-03" +#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033" +#define IGNEUM_DAY0 0x3067619fu +#define IGNEUM_DAY1 0x3c269176u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=14 add=8 shfl=8 xor=6 mad=5 mul=5 mulhi=5 sub=4 rotl=3 rotr=3 hot=2 or=1" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "hot64k2" +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 14, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 448 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Hot table (hot-table experiment, 5 October 2026, docs/plans/hot-table.md): NOT the lottery hash. IGNEUM_HOT_SLOTS of the +// 16 load slots read H[mulhi(src, IGNEUM_HOT_WORDS)], H = IGNEUM_HOT_MB MiB of chained ChaCha12 lines (the cache chain) under +// KH = seed_words("igneum-hot/" || epoch seed bytes) and the tag "Igne" "umHT"; filled by igneum_hot_fill once per epoch. +#define IGNEUM_HOT_MB 64 +#define IGNEUM_HOT_WORDS 0x01000000u +#define IGNEUM_HOT_SEGMENTS 16384u +#define IGNEUM_HOT_SLOTS 2 // hot loads per program (16 per hash), replacing the dataset loads (14 of them) +#define IGNEUM_HOT_ADDED 0 +#define IGNEUM_HOT_KEY_INIT { 0x3a48bef5u, 0x6b54b1a1u, 0x9ff897c3u, 0x7d5d85e4u, 0xcd35379fu, 0x9d76bb86u, 0xbe2affb1u, 0x0f8f80b4u } +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u } +#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u } +#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u } +#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-hot/hot64k2/program.json b/proto-cuda/packs-ca2-hot/hot64k2/program.json new file mode 100644 index 00000000..5d402be4 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k2/program.json @@ -0,0 +1,129 @@ +{ + "format": "igneum-program-pack-3", + "generator": 2, + "attempt": 0, + "program_id": "0x322a62466d49ff66", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-genesis", + "seed_bytes": "69676e65756d2d67656e65736973", + "seed_words": ["0x67a9a7be", "0x1a155b25", "0xfddfb732", "0x4b5af2e8", "0xc55caf33", "0xa27c13b7", "0x06628a48", "0x03852469"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "load_class": "hot64k2", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [14, 0, 0], + "bytes_per_hash": 448, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "hot_table": {"mb": 64, "words": 16777216, "segments": 16384, "slots": 2, "form": "replaced: k of the class's load slots read the table", "dataset_slots": 14, "hot_loads_per_hash": 16, "key": ["0x3a48bef5", "0x6b54b1a1", "0x9ff897c3", "0x7d5d85e4", "0xcd35379f", "0x9d76bb86", "0xbe2affb1", "0x0f8f80b4"], "key_derivation": "seed_words_from_bytes('igneum-hot/' || seed_bytes), the same for every attempt of the epoch", "tag": ["0x49676e65", "0x756d4854"], "chain": "the cache chain of dataset.cache with the hot key and tag: in_j = prev ^ (sigma || key || seg || j || tag); line_j = block(in_j); prev_0 = 0", "load": "dst = dst ^ hot[mulhi(src, words)] (the high 32 bits of the 64-bit product; the index lies in [0, words) for any size)", "slots_rule": "the first k load slots in the Fisher-Yates draw order after the scratch slots (a uniform k-subset); no extra draw, so with version 2 widths and no scratch the program is the version 2 program with k loads redirected", "acceptance_stand_in": "dataset_elem(mulhi(src, words), seed_words[2], seed_words[3])", "program_id": "the read-width id with 'hot/' || mb || k appended", "spec": "docs/plans/hot-table.md"}, + "op_mix": {"load": 14, "add": 8, "shfl": 8, "xor": 6, "mad": 5, "mul": 5, "mulhi": 5, "sub": 4, "rotl": 3, "rotr": 3, "hot": 2, "or": 1}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)", + "hot": "dst = dst ^ hot[mulhi(src, hot_table.words)] (hot-table experiment)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "2026-10-03", + "day_bytes": "6461792f323032362d31302d3033", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0x3067619f", + "d1": "0x3c269176", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "mad", "dst": 2, "src": 3, "src2": 4, "imm": "0xbf7b174d", "imm2": "0x337b762e", "rot": 17, "bit": 2, "mask": 2, "width": 1}, + {"i": 1, "op": "mad", "dst": 2, "src": 1, "src2": 1, "imm": "0xdd04a5da", "imm2": "0x42da7657", "rot": 15, "bit": 30, "mask": 16, "width": 1}, + {"i": 2, "op": "mad", "dst": 2, "src": 3, "src2": 2, "imm": "0x734003fa", "imm2": "0x5bb67700", "rot": 3, "bit": 20, "mask": 1, "width": 1}, + {"i": 3, "op": "xor", "dst": 3, "src": 5, "src2": 5, "imm": "0xc55a1b1c", "imm2": "0xa19720f3", "rot": 7, "bit": 8, "mask": 1, "width": 1}, + {"i": 4, "op": "load", "dst": 7, "src": 2, "src2": 5, "imm": "0xad572dd7", "imm2": "0x9ceb3ea7", "rot": 18, "bit": 30, "mask": 2, "width": 1}, + {"i": 5, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x769a53be", "imm2": "0x80f9067e", "rot": 12, "bit": 22, "mask": 1, "width": 1}, + {"i": 6, "op": "shfl", "dst": 1, "src": 4, "src2": 0, "imm": "0xd3613d88", "imm2": "0x262fb219", "rot": 10, "bit": 30, "mask": 8, "width": 1}, + {"i": 7, "op": "shfl", "dst": 7, "src": 3, "src2": 4, "imm": "0xce38e42f", "imm2": "0xb868b818", "rot": 11, "bit": 8, "mask": 8, "width": 1}, + {"i": 8, "op": "mulhi", "dst": 1, "src": 5, "src2": 2, "imm": "0xa5eebca5", "imm2": "0x5703a72b", "rot": 13, "bit": 13, "mask": 16, "width": 1}, + {"i": 9, "op": "rotr", "dst": 6, "src": 3, "src2": 4, "imm": "0x17a5a9c7", "imm2": "0xdcfb93a1", "rot": 20, "bit": 27, "mask": 2, "width": 1}, + {"i": 10, "op": "or", "dst": 3, "src": 4, "src2": 1, "imm": "0xccb7d785", "imm2": "0xc335364c", "rot": 14, "bit": 12, "mask": 4, "width": 1}, + {"i": 11, "op": "load", "dst": 4, "src": 3, "src2": 2, "imm": "0x88cb9af3", "imm2": "0x4e7dc10d", "rot": 24, "bit": 17, "mask": 4, "width": 1}, + {"i": 12, "op": "mulhi", "dst": 0, "src": 4, "src2": 4, "imm": "0x45374321", "imm2": "0x3cd91989", "rot": 11, "bit": 4, "mask": 2, "width": 1}, + {"i": 13, "op": "add", "dst": 5, "src": 1, "src2": 2, "imm": "0xc7934706", "imm2": "0xd3177981", "rot": 16, "bit": 30, "mask": 2, "width": 1}, + {"i": 14, "op": "load", "dst": 0, "src": 4, "src2": 3, "imm": "0xfd7f56bb", "imm2": "0x65e14f52", "rot": 13, "bit": 22, "mask": 2, "width": 1}, + {"i": 15, "op": "sub", "dst": 2, "src": 4, "src2": 4, "imm": "0x35a80b49", "imm2": "0x060f2d13", "rot": 16, "bit": 20, "mask": 16, "width": 1}, + {"i": 16, "op": "hot", "dst": 2, "src": 0, "src2": 2, "imm": "0xae0a32c2", "imm2": "0x4c2a4cfe", "rot": 8, "bit": 31, "mask": 16, "width": 1}, + {"i": 17, "op": "load", "dst": 7, "src": 2, "src2": 6, "imm": "0x82a84cc3", "imm2": "0x21a38d68", "rot": 15, "bit": 21, "mask": 2, "width": 1}, + {"i": 18, "op": "shfl", "dst": 7, "src": 3, "src2": 3, "imm": "0xa3818806", "imm2": "0x8f66b5c8", "rot": 14, "bit": 6, "mask": 4, "width": 1}, + {"i": 19, "op": "mul", "dst": 5, "src": 0, "src2": 1, "imm": "0xa00de107", "imm2": "0x77bfcaa5", "rot": 3, "bit": 10, "mask": 2, "width": 1}, + {"i": 20, "op": "shfl", "dst": 3, "src": 4, "src2": 7, "imm": "0x1d2b8cab", "imm2": "0x80b4f9a2", "rot": 14, "bit": 25, "mask": 2, "width": 1}, + {"i": 21, "op": "shfl", "dst": 2, "src": 4, "src2": 5, "imm": "0x3ac915d2", "imm2": "0x5fba7bc2", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 22, "op": "mulhi", "dst": 6, "src": 2, "src2": 0, "imm": "0xdc3ec8fd", "imm2": "0x599e2fa3", "rot": 22, "bit": 3, "mask": 2, "width": 1}, + {"i": 23, "op": "load", "dst": 6, "src": 1, "src2": 5, "imm": "0x2a6b16d5", "imm2": "0xd73e396f", "rot": 28, "bit": 29, "mask": 2, "width": 1}, + {"i": 24, "op": "mul", "dst": 5, "src": 0, "src2": 7, "imm": "0x376d0223", "imm2": "0xe1c2169a", "rot": 4, "bit": 16, "mask": 16, "width": 1}, + {"i": 25, "op": "rotl", "dst": 5, "src": 7, "src2": 3, "imm": "0x78ad8c60", "imm2": "0x6f5b77d5", "rot": 19, "bit": 11, "mask": 16, "width": 1}, + {"i": 26, "op": "shfl", "dst": 7, "src": 6, "src2": 5, "imm": "0x93915b9f", "imm2": "0x1e61fb6b", "rot": 28, "bit": 23, "mask": 2, "width": 1}, + {"i": 27, "op": "xor", "dst": 0, "src": 5, "src2": 7, "imm": "0x6378fe15", "imm2": "0x66c78f42", "rot": 12, "bit": 31, "mask": 8, "width": 1}, + {"i": 28, "op": "xor", "dst": 0, "src": 4, "src2": 7, "imm": "0x20a57fda", "imm2": "0x088c848e", "rot": 16, "bit": 13, "mask": 4, "width": 1}, + {"i": 29, "op": "sub", "dst": 3, "src": 0, "src2": 2, "imm": "0x49d95fd5", "imm2": "0x1a5f946a", "rot": 6, "bit": 12, "mask": 1, "width": 1}, + {"i": 30, "op": "mul", "dst": 5, "src": 1, "src2": 7, "imm": "0x0a816217", "imm2": "0x405c4f73", "rot": 13, "bit": 27, "mask": 4, "width": 1}, + {"i": 31, "op": "load", "dst": 7, "src": 2, "src2": 2, "imm": "0x09ed045e", "imm2": "0xd69c4715", "rot": 5, "bit": 9, "mask": 2, "width": 1}, + {"i": 32, "op": "load", "dst": 1, "src": 0, "src2": 6, "imm": "0xeb79ea49", "imm2": "0xcc587f5a", "rot": 6, "bit": 8, "mask": 16, "width": 1}, + {"i": 33, "op": "xor", "dst": 5, "src": 6, "src2": 1, "imm": "0x3027401e", "imm2": "0x5f20c27e", "rot": 18, "bit": 9, "mask": 2, "width": 1}, + {"i": 34, "op": "hot", "dst": 5, "src": 1, "src2": 3, "imm": "0x0e1cab07", "imm2": "0x09356c5b", "rot": 19, "bit": 31, "mask": 1, "width": 1}, + {"i": 35, "op": "mulhi", "dst": 0, "src": 5, "src2": 2, "imm": "0x90e31357", "imm2": "0xabd32484", "rot": 26, "bit": 5, "mask": 8, "width": 1}, + {"i": 36, "op": "shfl", "dst": 5, "src": 2, "src2": 5, "imm": "0xee9a955f", "imm2": "0x31b3faed", "rot": 8, "bit": 24, "mask": 4, "width": 1}, + {"i": 37, "op": "load", "dst": 7, "src": 0, "src2": 7, "imm": "0x3ba2f832", "imm2": "0x1160dcd3", "rot": 4, "bit": 29, "mask": 1, "width": 1}, + {"i": 38, "op": "add", "dst": 3, "src": 1, "src2": 7, "imm": "0x75ba2fad", "imm2": "0x230c005c", "rot": 4, "bit": 27, "mask": 1, "width": 1}, + {"i": 39, "op": "shfl", "dst": 1, "src": 5, "src2": 3, "imm": "0xdbf37e75", "imm2": "0xb5ac1969", "rot": 30, "bit": 13, "mask": 4, "width": 1}, + {"i": 40, "op": "xor", "dst": 2, "src": 5, "src2": 5, "imm": "0x47f136c5", "imm2": "0x06ce9153", "rot": 19, "bit": 10, "mask": 2, "width": 1}, + {"i": 41, "op": "mad", "dst": 3, "src": 6, "src2": 3, "imm": "0xce13eff8", "imm2": "0x04cc1d55", "rot": 3, "bit": 1, "mask": 4, "width": 1}, + {"i": 42, "op": "sub", "dst": 6, "src": 7, "src2": 1, "imm": "0x6a65ab71", "imm2": "0x8fbc1bcd", "rot": 4, "bit": 1, "mask": 8, "width": 1}, + {"i": 43, "op": "xor", "dst": 7, "src": 0, "src2": 7, "imm": "0xdaeb4928", "imm2": "0xc0423027", "rot": 24, "bit": 11, "mask": 8, "width": 1}, + {"i": 44, "op": "load", "dst": 1, "src": 7, "src2": 7, "imm": "0x778f01c9", "imm2": "0x28cedcea", "rot": 12, "bit": 4, "mask": 16, "width": 1}, + {"i": 45, "op": "mul", "dst": 2, "src": 3, "src2": 2, "imm": "0xf4264f1b", "imm2": "0x0f627d56", "rot": 5, "bit": 28, "mask": 8, "width": 1}, + {"i": 46, "op": "mulhi", "dst": 1, "src": 5, "src2": 1, "imm": "0xffb2147a", "imm2": "0xccde9b05", "rot": 13, "bit": 9, "mask": 2, "width": 1}, + {"i": 47, "op": "sub", "dst": 4, "src": 3, "src2": 5, "imm": "0x73b36234", "imm2": "0x3f5d5997", "rot": 7, "bit": 18, "mask": 2, "width": 1}, + {"i": 48, "op": "rotr", "dst": 2, "src": 6, "src2": 3, "imm": "0x3a4d9aa9", "imm2": "0x212bec7b", "rot": 4, "bit": 29, "mask": 16, "width": 1}, + {"i": 49, "op": "load", "dst": 3, "src": 5, "src2": 2, "imm": "0x626f11df", "imm2": "0x56cd5bfd", "rot": 7, "bit": 1, "mask": 1, "width": 1}, + {"i": 50, "op": "add", "dst": 1, "src": 5, "src2": 6, "imm": "0x81b8bc2c", "imm2": "0x1907970c", "rot": 28, "bit": 7, "mask": 4, "width": 1}, + {"i": 51, "op": "mul", "dst": 0, "src": 2, "src2": 2, "imm": "0xa8848b30", "imm2": "0xef6ac348", "rot": 9, "bit": 15, "mask": 8, "width": 1}, + {"i": 52, "op": "add", "dst": 0, "src": 2, "src2": 0, "imm": "0x4f92b968", "imm2": "0x699fd448", "rot": 22, "bit": 6, "mask": 4, "width": 1}, + {"i": 53, "op": "add", "dst": 1, "src": 0, "src2": 2, "imm": "0x2bb965af", "imm2": "0x77b1520d", "rot": 2, "bit": 12, "mask": 8, "width": 1}, + {"i": 54, "op": "rotl", "dst": 7, "src": 1, "src2": 0, "imm": "0x553e678b", "imm2": "0x3cc8eae0", "rot": 14, "bit": 20, "mask": 2, "width": 1}, + {"i": 55, "op": "add", "dst": 3, "src": 7, "src2": 2, "imm": "0x7b0fe07a", "imm2": "0xa54c55a0", "rot": 10, "bit": 1, "mask": 1, "width": 1}, + {"i": 56, "op": "load", "dst": 6, "src": 7, "src2": 4, "imm": "0x01eba9aa", "imm2": "0x2758c0f7", "rot": 14, "bit": 15, "mask": 4, "width": 1}, + {"i": 57, "op": "rotr", "dst": 1, "src": 5, "src2": 2, "imm": "0x1f5267b3", "imm2": "0x236f5a27", "rot": 2, "bit": 31, "mask": 16, "width": 1}, + {"i": 58, "op": "load", "dst": 5, "src": 4, "src2": 3, "imm": "0xa9954a9b", "imm2": "0x6a54d4e8", "rot": 11, "bit": 10, "mask": 16, "width": 1}, + {"i": 59, "op": "load", "dst": 6, "src": 2, "src2": 4, "imm": "0x9923ff88", "imm2": "0x9357254e", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 60, "op": "mad", "dst": 3, "src": 5, "src2": 0, "imm": "0xc0cc51a6", "imm2": "0x3fd7701b", "rot": 20, "bit": 1, "mask": 4, "width": 1}, + {"i": 61, "op": "add", "dst": 5, "src": 7, "src2": 7, "imm": "0xaf9dd72d", "imm2": "0xad7493e7", "rot": 7, "bit": 31, "mask": 16, "width": 1}, + {"i": 62, "op": "add", "dst": 4, "src": 6, "src2": 2, "imm": "0x89841d87", "imm2": "0x1e07c3d9", "rot": 6, "bit": 27, "mask": 1, "width": 1}, + {"i": 63, "op": "rotl", "dst": 5, "src": 4, "src2": 2, "imm": "0xae210f8d", "imm2": "0x8e499ba4", "rot": 19, "bit": 9, "mask": 1, "width": 1} + ] +} diff --git a/proto-cuda/packs-ca2-hot/hot64k2/program.metal b/proto-cuda/packs-ca2-hot/hot64k2/program.metal new file mode 100644 index 00000000..ae2552c0 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k2/program.metal @@ -0,0 +1,112 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (64 MiB, 2 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + device const uint* hot [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ dataset[r2 & MASK]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ dataset[r1 & MASK]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ dataset[r0 & MASK]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ hot[mulhi(r1, HOT_WORDS)]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ dataset[r0 & MASK]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ dataset[r7 & MASK]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ dataset[r2 & MASK]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot64k2/program_bound.metal b/proto-cuda/packs-ca2-hot/hot64k2/program_bound.metal new file mode 100644 index 00000000..7fbf2e65 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k2/program_bound.metal @@ -0,0 +1,114 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (64 MiB, 2 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + device const uint* hot [[buffer(4)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ dataset[r2 & MASK]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ dataset[r1 & MASK]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ dataset[r0 & MASK]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ hot[mulhi(r1, HOT_WORDS)]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ dataset[r0 & MASK]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ dataset[r7 & MASK]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ dataset[r2 & MASK]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot64k2/vectors.h b/proto-cuda/packs-ca2-hot/hot64k2/vectors.h new file mode 100644 index 00000000..fd8e01ca --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k2/vectors.h @@ -0,0 +1,67 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0x65b0158a345b67e7ull, 0x8c0df6208996119dull, 0xa82920dfd2564384ull, 0x3f98635d88e71980ull, 0x9330bc0a2c56e1a8ull, 0x2f909a91ce4c3f70ull, 0xaefbb1536b8d51bcull, 0x339807367ad7a031ull, + 0xfbfffb50795b4050ull, 0x36038d518a9f7b87ull, 0x56c10e7bbcc1b46cull, 0x7ee8f70d9c40a0aeull, 0x430641c5c64e7f33ull, 0x1cf9748074f98ec1ull, 0x013ecaf70fe24220ull, 0x1e1d2b948428b87aull, + 0x4d681ef40907816cull, 0xf594910d3fd5fc06ull, 0x29b8b25ba39f2ccfull, 0x5bc340343406553full, 0x05f87eb5c67aa7d6ull, 0x8d3b61236cb38cb5ull, 0xc056056ed6154abaull, 0xdbaf8e4ace0da7aaull, + 0x551d2ef6ff72a674ull, 0x99613e85f5c9187eull, 0x9597e6e00fac1a7dull, 0x4806742b7b0869a9ull, 0x685f008c389db453ull, 0x4ec754ef00d2026dull, 0xbf0c4e9528978691ull, 0x3d2cd0c742789be9ull + }, + { // base nonce 4096 + 0x18e0a4279770b00bull, 0xb5b83f4d0edb6b8eull, 0x024399529627448aull, 0xc83b95ce143a618full, 0x4dbabfa14be76be6ull, 0x207079f745606905ull, 0x8e920df676583078ull, 0xd8499ebc77afe275ull, + 0x9d8c674573683174ull, 0x346de7a99fdb40b6ull, 0x924a7287c62be081ull, 0x6baa55408c5b2901ull, 0x73003fd8f521c962ull, 0x8ed90cefc544e916ull, 0x4056df74a34656eaull, 0x6dfe2ec0874d6872ull, + 0x38d8f3ab3b5a5414ull, 0x8f082c3e5af03d0cull, 0x659be876ab2fb5e1ull, 0x6796490dba2bde16ull, 0xcd389d4acf88a6b3ull, 0xa72e8293a53777a3ull, 0x27688d5f693b358bull, 0x7925eab9cdc62dc1ull, + 0x7e18db5099e624c5ull, 0xd18bba6cd4629ab5ull, 0xecde3dc17c4796feull, 0xe5386f3afa28f523ull, 0x16b4ca1e85042299ull, 0xb4411635998d0bdfull, 0xbc4793e8fd01d963ull, 0xab67165dcfe87a9dull + }, + { // base nonce 1000000 + 0xd2eb4ca6afb04f3aull, 0xef1f5c1416c10822ull, 0x0ac3034339afe313ull, 0x3e29eb72d7492296ull, 0x00e70e214591abdcull, 0x71ddb7760dd8e86aull, 0xb9f6dc2158b0c389ull, 0xc4d5a7905b475bd9ull, + 0x6510ed7b7976c952ull, 0x18f41cf0fad53752ull, 0x3b01c44ed18ee920ull, 0xea78aa293930586full, 0xc7eb0149acd0991bull, 0x640d3e9176c9dd09ull, 0x3f1cc7bb223f56e0ull, 0xdcc6b668b2872187ull, + 0xa4a124ab824d5ea9ull, 0xbb95eb77c20d50e8ull, 0xcdae9c9edd32978bull, 0x5675a40696b5666bull, 0xdc619039a79b0d7dull, 0x52b23d4117deb142ull, 0xc1dddbbd50a34c20ull, 0xd0f54b71e0e4ce96ull, + 0x4334aed9d2294383ull, 0x519ac5278300d625ull, 0xadc42115aa1f645bull, 0xb67280570485b157ull, 0x2d72f2b3a89fdaffull, 0x39456ffbfcb86733ull, 0xaaebb5e9afba3337ull, 0xf092fdfb9df66980ull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu, + 0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u, + 0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du, + 0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull; +// Hot table self-test (docs/plans/hot-table.md): hot[0..15], the last 16 words, and FNV-1a 64 over all IGNEUM_HOT_WORDS words. +static const uint32_t IGNEUM_HOT_HEAD[16] = { + 0x8068cc73u, 0x6036ebf9u, 0xb604cd25u, 0x8ffb840eu, 0xc54074a2u, 0x285c0695u, 0x77512425u, 0xc26a58a7u, + 0x72c88757u, 0xc10fca78u, 0x513825ddu, 0x30d6ccc8u, 0x9a05e7cfu, 0xb9533f50u, 0x4bac3ba0u, 0xa5c19528u +}; +static const uint32_t IGNEUM_HOT_LAST[16] = { + 0x7c6d7cebu, 0xe24fcd46u, 0xe5cce976u, 0x3ecffe59u, 0x94e98b66u, 0x3f6ef45cu, 0xa3715aa1u, 0xdbe35281u, + 0xba05d27bu, 0x00541963u, 0xe636f453u, 0xd3972366u, 0x529599b8u, 0x79ae3c8cu, 0x48436863u, 0x897a8bf3u +}; +static const uint64_t IGNEUM_HOT_FNV64 = 0x77ca4b9527104530ull; diff --git a/proto-cuda/packs-ca2-hot/hot64k2/vectors.json b/proto-cuda/packs-ca2-hot/hot64k2/vectors.json new file mode 100644 index 00000000..d886fb83 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k2/vectors.json @@ -0,0 +1,39 @@ +{ + "seed": "igneum-genesis", + "day": "2026-10-03", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0x65b0158a345b67e7", "0x8c0df6208996119d", "0xa82920dfd2564384", "0x3f98635d88e71980", "0x9330bc0a2c56e1a8", "0x2f909a91ce4c3f70", "0xaefbb1536b8d51bc", "0x339807367ad7a031", + "0xfbfffb50795b4050", "0x36038d518a9f7b87", "0x56c10e7bbcc1b46c", "0x7ee8f70d9c40a0ae", "0x430641c5c64e7f33", "0x1cf9748074f98ec1", "0x013ecaf70fe24220", "0x1e1d2b948428b87a", + "0x4d681ef40907816c", "0xf594910d3fd5fc06", "0x29b8b25ba39f2ccf", "0x5bc340343406553f", "0x05f87eb5c67aa7d6", "0x8d3b61236cb38cb5", "0xc056056ed6154aba", "0xdbaf8e4ace0da7aa", + "0x551d2ef6ff72a674", "0x99613e85f5c9187e", "0x9597e6e00fac1a7d", "0x4806742b7b0869a9", "0x685f008c389db453", "0x4ec754ef00d2026d", "0xbf0c4e9528978691", "0x3d2cd0c742789be9" + ]}, + {"base_nonce": 4096, "expected": [ + "0x18e0a4279770b00b", "0xb5b83f4d0edb6b8e", "0x024399529627448a", "0xc83b95ce143a618f", "0x4dbabfa14be76be6", "0x207079f745606905", "0x8e920df676583078", "0xd8499ebc77afe275", + "0x9d8c674573683174", "0x346de7a99fdb40b6", "0x924a7287c62be081", "0x6baa55408c5b2901", "0x73003fd8f521c962", "0x8ed90cefc544e916", "0x4056df74a34656ea", "0x6dfe2ec0874d6872", + "0x38d8f3ab3b5a5414", "0x8f082c3e5af03d0c", "0x659be876ab2fb5e1", "0x6796490dba2bde16", "0xcd389d4acf88a6b3", "0xa72e8293a53777a3", "0x27688d5f693b358b", "0x7925eab9cdc62dc1", + "0x7e18db5099e624c5", "0xd18bba6cd4629ab5", "0xecde3dc17c4796fe", "0xe5386f3afa28f523", "0x16b4ca1e85042299", "0xb4411635998d0bdf", "0xbc4793e8fd01d963", "0xab67165dcfe87a9d" + ]}, + {"base_nonce": 1000000, "expected": [ + "0xd2eb4ca6afb04f3a", "0xef1f5c1416c10822", "0x0ac3034339afe313", "0x3e29eb72d7492296", "0x00e70e214591abdc", "0x71ddb7760dd8e86a", "0xb9f6dc2158b0c389", "0xc4d5a7905b475bd9", + "0x6510ed7b7976c952", "0x18f41cf0fad53752", "0x3b01c44ed18ee920", "0xea78aa293930586f", "0xc7eb0149acd0991b", "0x640d3e9176c9dd09", "0x3f1cc7bb223f56e0", "0xdcc6b668b2872187", + "0xa4a124ab824d5ea9", "0xbb95eb77c20d50e8", "0xcdae9c9edd32978b", "0x5675a40696b5666b", "0xdc619039a79b0d7d", "0x52b23d4117deb142", "0xc1dddbbd50a34c20", "0xd0f54b71e0e4ce96", + "0x4334aed9d2294383", "0x519ac5278300d625", "0xadc42115aa1f645b", "0xb67280570485b157", "0x2d72f2b3a89fdaff", "0x39456ffbfcb86733", "0xaaebb5e9afba3337", "0xf092fdfb9df66980" + ]} + ], + "dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"], + "dataset_last_index": 268435455, + "dataset_last": "0xa33ada72", + "dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}], + "cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"], + "cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"], + "cache_fnv1a64": "0x48c4f5bf24166b2e", + "hot_head": ["0x8068cc73", "0x6036ebf9", "0xb604cd25", "0x8ffb840e", "0xc54074a2", "0x285c0695", "0x77512425", "0xc26a58a7", "0x72c88757", "0xc10fca78", "0x513825dd", "0x30d6ccc8", "0x9a05e7cf", "0xb9533f50", "0x4bac3ba0", "0xa5c19528"], + "hot_last_line": ["0x7c6d7ceb", "0xe24fcd46", "0xe5cce976", "0x3ecffe59", "0x94e98b66", "0x3f6ef45c", "0xa3715aa1", "0xdbe35281", "0xba05d27b", "0x00541963", "0xe636f453", "0xd3972366", "0x529599b8", "0x79ae3c8c", "0x48436863", "0x897a8bf3"], + "hot_fnv1a64": "0x77ca4b9527104530" +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4/kernel.cl b/proto-cuda/packs-ca2-hot/hot64k4/kernel.cl new file mode 100644 index 00000000..5efed6de --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4/kernel.cl @@ -0,0 +1,305 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (64 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-hot/hot64k4/kernel.cu b/proto-cuda/packs-ca2-hot/hot64k4/kernel.cu new file mode 100644 index 00000000..092127f1 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4/kernel.cu @@ -0,0 +1,179 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +// Hot table (64 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (ht_segment is in memhard.h): one thread per segment. +__global__ void igneum_hot_fill(uint32_t* hot, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[__umulhi(r1, HOT_WORDS)]; // 34 hot + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_hot_fill<<>>(hot, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4/kernel_bound.cl b/proto-cuda/packs-ca2-hot/hot64k4/kernel_bound.cl new file mode 100644 index 00000000..b2261717 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4/kernel_bound.cl @@ -0,0 +1,399 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (64 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4/kernel_bound.cu b/proto-cuda/packs-ca2-hot/hot64k4/kernel_bound.cu new file mode 100644 index 00000000..73341306 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4/kernel_bound.cu @@ -0,0 +1,125 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +// Hot table (64 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[__umulhi(r1, HOT_WORDS)]; // 34 hot + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, const uint32_t* hot, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4/memhard.h b/proto-cuda/packs-ca2-hot/hot64k4/memhard.h new file mode 100644 index 00000000..180f678b --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4/memhard.h @@ -0,0 +1,129 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +IGNEUM_HD void ht_segment(uint32_t* hot, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4/memhard.metal b/proto-cuda/packs-ca2-hot/hot64k4/memhard.metal new file mode 100644 index 00000000..c2ec5702 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4/memhard.metal @@ -0,0 +1,131 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; +} + +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +inline void ht_segment(device uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one thread per segment (IGNEUM_HOT_SEGMENTS threads). +kernel void igneum_hot_fill(device uint* hot [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + ht_segment(hot, gid); +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4/program.h b/proto-cuda/packs-ca2-hot/hot64k4/program.h new file mode 100644 index 00000000..a52279c4 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4/program.h @@ -0,0 +1,70 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-genesis" +#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973" +#define IGNEUM_GENERATOR 2 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x322a64466d4a02ccull +#define IGNEUM_DAY_STRING "2026-10-03" +#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033" +#define IGNEUM_DAY0 0x3067619fu +#define IGNEUM_DAY1 0x3c269176u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=12 add=8 shfl=8 xor=6 mad=5 mul=5 mulhi=5 hot=4 sub=4 rotl=3 rotr=3 or=1" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "hot64k4" +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 12, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 384 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Hot table (hot-table experiment, 5 October 2026, docs/plans/hot-table.md): NOT the lottery hash. IGNEUM_HOT_SLOTS of the +// 16 load slots read H[mulhi(src, IGNEUM_HOT_WORDS)], H = IGNEUM_HOT_MB MiB of chained ChaCha12 lines (the cache chain) under +// KH = seed_words("igneum-hot/" || epoch seed bytes) and the tag "Igne" "umHT"; filled by igneum_hot_fill once per epoch. +#define IGNEUM_HOT_MB 64 +#define IGNEUM_HOT_WORDS 0x01000000u +#define IGNEUM_HOT_SEGMENTS 16384u +#define IGNEUM_HOT_SLOTS 4 // hot loads per program (32 per hash), replacing the dataset loads (12 of them) +#define IGNEUM_HOT_ADDED 0 +#define IGNEUM_HOT_KEY_INIT { 0x3a48bef5u, 0x6b54b1a1u, 0x9ff897c3u, 0x7d5d85e4u, 0xcd35379fu, 0x9d76bb86u, 0xbe2affb1u, 0x0f8f80b4u } +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u } +#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u } +#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u } +#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-hot/hot64k4/program.json b/proto-cuda/packs-ca2-hot/hot64k4/program.json new file mode 100644 index 00000000..436d58d6 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4/program.json @@ -0,0 +1,129 @@ +{ + "format": "igneum-program-pack-3", + "generator": 2, + "attempt": 0, + "program_id": "0x322a64466d4a02cc", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-genesis", + "seed_bytes": "69676e65756d2d67656e65736973", + "seed_words": ["0x67a9a7be", "0x1a155b25", "0xfddfb732", "0x4b5af2e8", "0xc55caf33", "0xa27c13b7", "0x06628a48", "0x03852469"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "load_class": "hot64k4", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [12, 0, 0], + "bytes_per_hash": 384, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "hot_table": {"mb": 64, "words": 16777216, "segments": 16384, "slots": 4, "form": "replaced: k of the class's load slots read the table", "dataset_slots": 12, "hot_loads_per_hash": 32, "key": ["0x3a48bef5", "0x6b54b1a1", "0x9ff897c3", "0x7d5d85e4", "0xcd35379f", "0x9d76bb86", "0xbe2affb1", "0x0f8f80b4"], "key_derivation": "seed_words_from_bytes('igneum-hot/' || seed_bytes), the same for every attempt of the epoch", "tag": ["0x49676e65", "0x756d4854"], "chain": "the cache chain of dataset.cache with the hot key and tag: in_j = prev ^ (sigma || key || seg || j || tag); line_j = block(in_j); prev_0 = 0", "load": "dst = dst ^ hot[mulhi(src, words)] (the high 32 bits of the 64-bit product; the index lies in [0, words) for any size)", "slots_rule": "the first k load slots in the Fisher-Yates draw order after the scratch slots (a uniform k-subset); no extra draw, so with version 2 widths and no scratch the program is the version 2 program with k loads redirected", "acceptance_stand_in": "dataset_elem(mulhi(src, words), seed_words[2], seed_words[3])", "program_id": "the read-width id with 'hot/' || mb || k appended", "spec": "docs/plans/hot-table.md"}, + "op_mix": {"load": 12, "add": 8, "shfl": 8, "xor": 6, "mad": 5, "mul": 5, "mulhi": 5, "hot": 4, "sub": 4, "rotl": 3, "rotr": 3, "or": 1}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)", + "hot": "dst = dst ^ hot[mulhi(src, hot_table.words)] (hot-table experiment)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "2026-10-03", + "day_bytes": "6461792f323032362d31302d3033", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0x3067619f", + "d1": "0x3c269176", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "mad", "dst": 2, "src": 3, "src2": 4, "imm": "0xbf7b174d", "imm2": "0x337b762e", "rot": 17, "bit": 2, "mask": 2, "width": 1}, + {"i": 1, "op": "mad", "dst": 2, "src": 1, "src2": 1, "imm": "0xdd04a5da", "imm2": "0x42da7657", "rot": 15, "bit": 30, "mask": 16, "width": 1}, + {"i": 2, "op": "mad", "dst": 2, "src": 3, "src2": 2, "imm": "0x734003fa", "imm2": "0x5bb67700", "rot": 3, "bit": 20, "mask": 1, "width": 1}, + {"i": 3, "op": "xor", "dst": 3, "src": 5, "src2": 5, "imm": "0xc55a1b1c", "imm2": "0xa19720f3", "rot": 7, "bit": 8, "mask": 1, "width": 1}, + {"i": 4, "op": "load", "dst": 7, "src": 2, "src2": 5, "imm": "0xad572dd7", "imm2": "0x9ceb3ea7", "rot": 18, "bit": 30, "mask": 2, "width": 1}, + {"i": 5, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x769a53be", "imm2": "0x80f9067e", "rot": 12, "bit": 22, "mask": 1, "width": 1}, + {"i": 6, "op": "shfl", "dst": 1, "src": 4, "src2": 0, "imm": "0xd3613d88", "imm2": "0x262fb219", "rot": 10, "bit": 30, "mask": 8, "width": 1}, + {"i": 7, "op": "shfl", "dst": 7, "src": 3, "src2": 4, "imm": "0xce38e42f", "imm2": "0xb868b818", "rot": 11, "bit": 8, "mask": 8, "width": 1}, + {"i": 8, "op": "mulhi", "dst": 1, "src": 5, "src2": 2, "imm": "0xa5eebca5", "imm2": "0x5703a72b", "rot": 13, "bit": 13, "mask": 16, "width": 1}, + {"i": 9, "op": "rotr", "dst": 6, "src": 3, "src2": 4, "imm": "0x17a5a9c7", "imm2": "0xdcfb93a1", "rot": 20, "bit": 27, "mask": 2, "width": 1}, + {"i": 10, "op": "or", "dst": 3, "src": 4, "src2": 1, "imm": "0xccb7d785", "imm2": "0xc335364c", "rot": 14, "bit": 12, "mask": 4, "width": 1}, + {"i": 11, "op": "load", "dst": 4, "src": 3, "src2": 2, "imm": "0x88cb9af3", "imm2": "0x4e7dc10d", "rot": 24, "bit": 17, "mask": 4, "width": 1}, + {"i": 12, "op": "mulhi", "dst": 0, "src": 4, "src2": 4, "imm": "0x45374321", "imm2": "0x3cd91989", "rot": 11, "bit": 4, "mask": 2, "width": 1}, + {"i": 13, "op": "add", "dst": 5, "src": 1, "src2": 2, "imm": "0xc7934706", "imm2": "0xd3177981", "rot": 16, "bit": 30, "mask": 2, "width": 1}, + {"i": 14, "op": "load", "dst": 0, "src": 4, "src2": 3, "imm": "0xfd7f56bb", "imm2": "0x65e14f52", "rot": 13, "bit": 22, "mask": 2, "width": 1}, + {"i": 15, "op": "sub", "dst": 2, "src": 4, "src2": 4, "imm": "0x35a80b49", "imm2": "0x060f2d13", "rot": 16, "bit": 20, "mask": 16, "width": 1}, + {"i": 16, "op": "hot", "dst": 2, "src": 0, "src2": 2, "imm": "0xae0a32c2", "imm2": "0x4c2a4cfe", "rot": 8, "bit": 31, "mask": 16, "width": 1}, + {"i": 17, "op": "load", "dst": 7, "src": 2, "src2": 6, "imm": "0x82a84cc3", "imm2": "0x21a38d68", "rot": 15, "bit": 21, "mask": 2, "width": 1}, + {"i": 18, "op": "shfl", "dst": 7, "src": 3, "src2": 3, "imm": "0xa3818806", "imm2": "0x8f66b5c8", "rot": 14, "bit": 6, "mask": 4, "width": 1}, + {"i": 19, "op": "mul", "dst": 5, "src": 0, "src2": 1, "imm": "0xa00de107", "imm2": "0x77bfcaa5", "rot": 3, "bit": 10, "mask": 2, "width": 1}, + {"i": 20, "op": "shfl", "dst": 3, "src": 4, "src2": 7, "imm": "0x1d2b8cab", "imm2": "0x80b4f9a2", "rot": 14, "bit": 25, "mask": 2, "width": 1}, + {"i": 21, "op": "shfl", "dst": 2, "src": 4, "src2": 5, "imm": "0x3ac915d2", "imm2": "0x5fba7bc2", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 22, "op": "mulhi", "dst": 6, "src": 2, "src2": 0, "imm": "0xdc3ec8fd", "imm2": "0x599e2fa3", "rot": 22, "bit": 3, "mask": 2, "width": 1}, + {"i": 23, "op": "load", "dst": 6, "src": 1, "src2": 5, "imm": "0x2a6b16d5", "imm2": "0xd73e396f", "rot": 28, "bit": 29, "mask": 2, "width": 1}, + {"i": 24, "op": "mul", "dst": 5, "src": 0, "src2": 7, "imm": "0x376d0223", "imm2": "0xe1c2169a", "rot": 4, "bit": 16, "mask": 16, "width": 1}, + {"i": 25, "op": "rotl", "dst": 5, "src": 7, "src2": 3, "imm": "0x78ad8c60", "imm2": "0x6f5b77d5", "rot": 19, "bit": 11, "mask": 16, "width": 1}, + {"i": 26, "op": "shfl", "dst": 7, "src": 6, "src2": 5, "imm": "0x93915b9f", "imm2": "0x1e61fb6b", "rot": 28, "bit": 23, "mask": 2, "width": 1}, + {"i": 27, "op": "xor", "dst": 0, "src": 5, "src2": 7, "imm": "0x6378fe15", "imm2": "0x66c78f42", "rot": 12, "bit": 31, "mask": 8, "width": 1}, + {"i": 28, "op": "xor", "dst": 0, "src": 4, "src2": 7, "imm": "0x20a57fda", "imm2": "0x088c848e", "rot": 16, "bit": 13, "mask": 4, "width": 1}, + {"i": 29, "op": "sub", "dst": 3, "src": 0, "src2": 2, "imm": "0x49d95fd5", "imm2": "0x1a5f946a", "rot": 6, "bit": 12, "mask": 1, "width": 1}, + {"i": 30, "op": "mul", "dst": 5, "src": 1, "src2": 7, "imm": "0x0a816217", "imm2": "0x405c4f73", "rot": 13, "bit": 27, "mask": 4, "width": 1}, + {"i": 31, "op": "load", "dst": 7, "src": 2, "src2": 2, "imm": "0x09ed045e", "imm2": "0xd69c4715", "rot": 5, "bit": 9, "mask": 2, "width": 1}, + {"i": 32, "op": "hot", "dst": 1, "src": 0, "src2": 6, "imm": "0xeb79ea49", "imm2": "0xcc587f5a", "rot": 6, "bit": 8, "mask": 16, "width": 1}, + {"i": 33, "op": "xor", "dst": 5, "src": 6, "src2": 1, "imm": "0x3027401e", "imm2": "0x5f20c27e", "rot": 18, "bit": 9, "mask": 2, "width": 1}, + {"i": 34, "op": "hot", "dst": 5, "src": 1, "src2": 3, "imm": "0x0e1cab07", "imm2": "0x09356c5b", "rot": 19, "bit": 31, "mask": 1, "width": 1}, + {"i": 35, "op": "mulhi", "dst": 0, "src": 5, "src2": 2, "imm": "0x90e31357", "imm2": "0xabd32484", "rot": 26, "bit": 5, "mask": 8, "width": 1}, + {"i": 36, "op": "shfl", "dst": 5, "src": 2, "src2": 5, "imm": "0xee9a955f", "imm2": "0x31b3faed", "rot": 8, "bit": 24, "mask": 4, "width": 1}, + {"i": 37, "op": "load", "dst": 7, "src": 0, "src2": 7, "imm": "0x3ba2f832", "imm2": "0x1160dcd3", "rot": 4, "bit": 29, "mask": 1, "width": 1}, + {"i": 38, "op": "add", "dst": 3, "src": 1, "src2": 7, "imm": "0x75ba2fad", "imm2": "0x230c005c", "rot": 4, "bit": 27, "mask": 1, "width": 1}, + {"i": 39, "op": "shfl", "dst": 1, "src": 5, "src2": 3, "imm": "0xdbf37e75", "imm2": "0xb5ac1969", "rot": 30, "bit": 13, "mask": 4, "width": 1}, + {"i": 40, "op": "xor", "dst": 2, "src": 5, "src2": 5, "imm": "0x47f136c5", "imm2": "0x06ce9153", "rot": 19, "bit": 10, "mask": 2, "width": 1}, + {"i": 41, "op": "mad", "dst": 3, "src": 6, "src2": 3, "imm": "0xce13eff8", "imm2": "0x04cc1d55", "rot": 3, "bit": 1, "mask": 4, "width": 1}, + {"i": 42, "op": "sub", "dst": 6, "src": 7, "src2": 1, "imm": "0x6a65ab71", "imm2": "0x8fbc1bcd", "rot": 4, "bit": 1, "mask": 8, "width": 1}, + {"i": 43, "op": "xor", "dst": 7, "src": 0, "src2": 7, "imm": "0xdaeb4928", "imm2": "0xc0423027", "rot": 24, "bit": 11, "mask": 8, "width": 1}, + {"i": 44, "op": "load", "dst": 1, "src": 7, "src2": 7, "imm": "0x778f01c9", "imm2": "0x28cedcea", "rot": 12, "bit": 4, "mask": 16, "width": 1}, + {"i": 45, "op": "mul", "dst": 2, "src": 3, "src2": 2, "imm": "0xf4264f1b", "imm2": "0x0f627d56", "rot": 5, "bit": 28, "mask": 8, "width": 1}, + {"i": 46, "op": "mulhi", "dst": 1, "src": 5, "src2": 1, "imm": "0xffb2147a", "imm2": "0xccde9b05", "rot": 13, "bit": 9, "mask": 2, "width": 1}, + {"i": 47, "op": "sub", "dst": 4, "src": 3, "src2": 5, "imm": "0x73b36234", "imm2": "0x3f5d5997", "rot": 7, "bit": 18, "mask": 2, "width": 1}, + {"i": 48, "op": "rotr", "dst": 2, "src": 6, "src2": 3, "imm": "0x3a4d9aa9", "imm2": "0x212bec7b", "rot": 4, "bit": 29, "mask": 16, "width": 1}, + {"i": 49, "op": "load", "dst": 3, "src": 5, "src2": 2, "imm": "0x626f11df", "imm2": "0x56cd5bfd", "rot": 7, "bit": 1, "mask": 1, "width": 1}, + {"i": 50, "op": "add", "dst": 1, "src": 5, "src2": 6, "imm": "0x81b8bc2c", "imm2": "0x1907970c", "rot": 28, "bit": 7, "mask": 4, "width": 1}, + {"i": 51, "op": "mul", "dst": 0, "src": 2, "src2": 2, "imm": "0xa8848b30", "imm2": "0xef6ac348", "rot": 9, "bit": 15, "mask": 8, "width": 1}, + {"i": 52, "op": "add", "dst": 0, "src": 2, "src2": 0, "imm": "0x4f92b968", "imm2": "0x699fd448", "rot": 22, "bit": 6, "mask": 4, "width": 1}, + {"i": 53, "op": "add", "dst": 1, "src": 0, "src2": 2, "imm": "0x2bb965af", "imm2": "0x77b1520d", "rot": 2, "bit": 12, "mask": 8, "width": 1}, + {"i": 54, "op": "rotl", "dst": 7, "src": 1, "src2": 0, "imm": "0x553e678b", "imm2": "0x3cc8eae0", "rot": 14, "bit": 20, "mask": 2, "width": 1}, + {"i": 55, "op": "add", "dst": 3, "src": 7, "src2": 2, "imm": "0x7b0fe07a", "imm2": "0xa54c55a0", "rot": 10, "bit": 1, "mask": 1, "width": 1}, + {"i": 56, "op": "load", "dst": 6, "src": 7, "src2": 4, "imm": "0x01eba9aa", "imm2": "0x2758c0f7", "rot": 14, "bit": 15, "mask": 4, "width": 1}, + {"i": 57, "op": "rotr", "dst": 1, "src": 5, "src2": 2, "imm": "0x1f5267b3", "imm2": "0x236f5a27", "rot": 2, "bit": 31, "mask": 16, "width": 1}, + {"i": 58, "op": "load", "dst": 5, "src": 4, "src2": 3, "imm": "0xa9954a9b", "imm2": "0x6a54d4e8", "rot": 11, "bit": 10, "mask": 16, "width": 1}, + {"i": 59, "op": "hot", "dst": 6, "src": 2, "src2": 4, "imm": "0x9923ff88", "imm2": "0x9357254e", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 60, "op": "mad", "dst": 3, "src": 5, "src2": 0, "imm": "0xc0cc51a6", "imm2": "0x3fd7701b", "rot": 20, "bit": 1, "mask": 4, "width": 1}, + {"i": 61, "op": "add", "dst": 5, "src": 7, "src2": 7, "imm": "0xaf9dd72d", "imm2": "0xad7493e7", "rot": 7, "bit": 31, "mask": 16, "width": 1}, + {"i": 62, "op": "add", "dst": 4, "src": 6, "src2": 2, "imm": "0x89841d87", "imm2": "0x1e07c3d9", "rot": 6, "bit": 27, "mask": 1, "width": 1}, + {"i": 63, "op": "rotl", "dst": 5, "src": 4, "src2": 2, "imm": "0xae210f8d", "imm2": "0x8e499ba4", "rot": 19, "bit": 9, "mask": 1, "width": 1} + ] +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4/program.metal b/proto-cuda/packs-ca2-hot/hot64k4/program.metal new file mode 100644 index 00000000..0f37469b --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4/program.metal @@ -0,0 +1,112 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (64 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + device const uint* hot [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ dataset[r2 & MASK]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ dataset[r1 & MASK]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ hot[mulhi(r1, HOT_WORDS)]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ dataset[r0 & MASK]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ dataset[r7 & MASK]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4/program_bound.metal b/proto-cuda/packs-ca2-hot/hot64k4/program_bound.metal new file mode 100644 index 00000000..48eec87a --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4/program_bound.metal @@ -0,0 +1,114 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (64 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + device const uint* hot [[buffer(4)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ dataset[r2 & MASK]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ dataset[r1 & MASK]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ hot[mulhi(r1, HOT_WORDS)]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ dataset[r0 & MASK]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ dataset[r7 & MASK]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4/vectors.h b/proto-cuda/packs-ca2-hot/hot64k4/vectors.h new file mode 100644 index 00000000..e8e3ddb4 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4/vectors.h @@ -0,0 +1,67 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0xaa6fadb5bf6a8657ull, 0x72a8df1dfcdc031dull, 0xdfc9bfb4dfc1fe4cull, 0x4f1d147208fe55a8ull, 0x857268e9be0d985aull, 0x2cd63cf21c16a144ull, 0xf0ce56007bef6616ull, 0x390d7530f3749f9dull, + 0xa625999bb3fc4033ull, 0x75277fef995afda6ull, 0xc51f53a3f0fafa5dull, 0x9225edc2f6f599d0ull, 0x8ea8d001890faaebull, 0x5814328cac0dbabfull, 0x2792499724c133b8ull, 0x93676b263a0a932dull, + 0xd04556c931e6cc5cull, 0xac770e5eacfccbfdull, 0x8f639b44b57ffcddull, 0x9faaa47b6a8f7906ull, 0xcf6945dec1692c76ull, 0xbae76864ab2853efull, 0xfdf1a7b9f292e721ull, 0xd1be28c4e06896caull, + 0x524ca1a30966dfc3ull, 0x6d9f82b4a0c49d55ull, 0xe9ccc952024c37a3ull, 0xcb1e2c142fe640fbull, 0xeb6a3ddceff93637ull, 0x9db685563837b969ull, 0x735d0a97397e9a73ull, 0x769e87d4851edc62ull + }, + { // base nonce 4096 + 0x86e437a3824dd195ull, 0xc4c6be5e71c3ce95ull, 0xd5bfb6d3c5607e8full, 0x2d66fc5035f07923ull, 0x2208b36836ddad05ull, 0x475ee8b8358f5d9bull, 0xc7073fa62542094eull, 0x8772a170da76fe86ull, + 0xde59b7b944b9cca2ull, 0xaca1169164b071f2ull, 0xc6b0c77aaf6156d7ull, 0xb269aa74d6ac8bbfull, 0x7813c5f52b4be07bull, 0x47347f1e833a9f8dull, 0xc2279941ae011758ull, 0x2f7e5a51aa488898ull, + 0x31d5b0d6d59bbc55ull, 0xc8428b0ffb32249aull, 0x81ccf818cb08e92aull, 0x3c36ddb655e2ae21ull, 0xaa420ec37a8513c8ull, 0x9d0916264bbc2838ull, 0x524c8e78f960f455ull, 0x2925791f277fbbcfull, + 0x1954e7e4d0621360ull, 0xf9d956934cbe24ecull, 0x59a0d78af50ec50eull, 0xd948051009055877ull, 0x8e250d4154d7799bull, 0x6c239c628f0aae74ull, 0x33b91411356782bcull, 0xfc984387a607428dull + }, + { // base nonce 1000000 + 0xd03a4006cc65d85dull, 0x7543d04a066ffa80ull, 0xbafb3d9d03e61806ull, 0x0511df048363823bull, 0x29e8e974f2559768ull, 0x2c62638edf0e0604ull, 0xb13fb1cc73af933dull, 0x17c0d802411cddddull, + 0x0783fd5e1ede02e0ull, 0x051f25268a6c036bull, 0x4616e4f6ba9f8365ull, 0x250e2bf1b1f533ecull, 0x1d88f9330f831014ull, 0x96ebf15542bf1cd8ull, 0x9c8c7424c761fb7aull, 0xa1cafc96cb1c25dbull, + 0x3fced2ed32d9d18aull, 0xb36a4d069a211707ull, 0x0af3b656c4af55dfull, 0x855756ea7662bf41ull, 0xd1a5ecf9b38f188dull, 0x4829cb01e2dc9865ull, 0x2f19493a1a2598acull, 0x03143778f0d53d3cull, + 0x981e77585c6a5655ull, 0x3bef255f9ed8d0acull, 0x0180828c60148870ull, 0xbf3c07d9686c4c29ull, 0x21475e8dff5f01d9ull, 0xac6a1d4618256c7cull, 0xa1c7ecbbac17bae7ull, 0x879d3d5a5b0407d4ull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu, + 0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u, + 0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du, + 0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull; +// Hot table self-test (docs/plans/hot-table.md): hot[0..15], the last 16 words, and FNV-1a 64 over all IGNEUM_HOT_WORDS words. +static const uint32_t IGNEUM_HOT_HEAD[16] = { + 0x8068cc73u, 0x6036ebf9u, 0xb604cd25u, 0x8ffb840eu, 0xc54074a2u, 0x285c0695u, 0x77512425u, 0xc26a58a7u, + 0x72c88757u, 0xc10fca78u, 0x513825ddu, 0x30d6ccc8u, 0x9a05e7cfu, 0xb9533f50u, 0x4bac3ba0u, 0xa5c19528u +}; +static const uint32_t IGNEUM_HOT_LAST[16] = { + 0x7c6d7cebu, 0xe24fcd46u, 0xe5cce976u, 0x3ecffe59u, 0x94e98b66u, 0x3f6ef45cu, 0xa3715aa1u, 0xdbe35281u, + 0xba05d27bu, 0x00541963u, 0xe636f453u, 0xd3972366u, 0x529599b8u, 0x79ae3c8cu, 0x48436863u, 0x897a8bf3u +}; +static const uint64_t IGNEUM_HOT_FNV64 = 0x77ca4b9527104530ull; diff --git a/proto-cuda/packs-ca2-hot/hot64k4/vectors.json b/proto-cuda/packs-ca2-hot/hot64k4/vectors.json new file mode 100644 index 00000000..969e686b --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4/vectors.json @@ -0,0 +1,39 @@ +{ + "seed": "igneum-genesis", + "day": "2026-10-03", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0xaa6fadb5bf6a8657", "0x72a8df1dfcdc031d", "0xdfc9bfb4dfc1fe4c", "0x4f1d147208fe55a8", "0x857268e9be0d985a", "0x2cd63cf21c16a144", "0xf0ce56007bef6616", "0x390d7530f3749f9d", + "0xa625999bb3fc4033", "0x75277fef995afda6", "0xc51f53a3f0fafa5d", "0x9225edc2f6f599d0", "0x8ea8d001890faaeb", "0x5814328cac0dbabf", "0x2792499724c133b8", "0x93676b263a0a932d", + "0xd04556c931e6cc5c", "0xac770e5eacfccbfd", "0x8f639b44b57ffcdd", "0x9faaa47b6a8f7906", "0xcf6945dec1692c76", "0xbae76864ab2853ef", "0xfdf1a7b9f292e721", "0xd1be28c4e06896ca", + "0x524ca1a30966dfc3", "0x6d9f82b4a0c49d55", "0xe9ccc952024c37a3", "0xcb1e2c142fe640fb", "0xeb6a3ddceff93637", "0x9db685563837b969", "0x735d0a97397e9a73", "0x769e87d4851edc62" + ]}, + {"base_nonce": 4096, "expected": [ + "0x86e437a3824dd195", "0xc4c6be5e71c3ce95", "0xd5bfb6d3c5607e8f", "0x2d66fc5035f07923", "0x2208b36836ddad05", "0x475ee8b8358f5d9b", "0xc7073fa62542094e", "0x8772a170da76fe86", + "0xde59b7b944b9cca2", "0xaca1169164b071f2", "0xc6b0c77aaf6156d7", "0xb269aa74d6ac8bbf", "0x7813c5f52b4be07b", "0x47347f1e833a9f8d", "0xc2279941ae011758", "0x2f7e5a51aa488898", + "0x31d5b0d6d59bbc55", "0xc8428b0ffb32249a", "0x81ccf818cb08e92a", "0x3c36ddb655e2ae21", "0xaa420ec37a8513c8", "0x9d0916264bbc2838", "0x524c8e78f960f455", "0x2925791f277fbbcf", + "0x1954e7e4d0621360", "0xf9d956934cbe24ec", "0x59a0d78af50ec50e", "0xd948051009055877", "0x8e250d4154d7799b", "0x6c239c628f0aae74", "0x33b91411356782bc", "0xfc984387a607428d" + ]}, + {"base_nonce": 1000000, "expected": [ + "0xd03a4006cc65d85d", "0x7543d04a066ffa80", "0xbafb3d9d03e61806", "0x0511df048363823b", "0x29e8e974f2559768", "0x2c62638edf0e0604", "0xb13fb1cc73af933d", "0x17c0d802411cdddd", + "0x0783fd5e1ede02e0", "0x051f25268a6c036b", "0x4616e4f6ba9f8365", "0x250e2bf1b1f533ec", "0x1d88f9330f831014", "0x96ebf15542bf1cd8", "0x9c8c7424c761fb7a", "0xa1cafc96cb1c25db", + "0x3fced2ed32d9d18a", "0xb36a4d069a211707", "0x0af3b656c4af55df", "0x855756ea7662bf41", "0xd1a5ecf9b38f188d", "0x4829cb01e2dc9865", "0x2f19493a1a2598ac", "0x03143778f0d53d3c", + "0x981e77585c6a5655", "0x3bef255f9ed8d0ac", "0x0180828c60148870", "0xbf3c07d9686c4c29", "0x21475e8dff5f01d9", "0xac6a1d4618256c7c", "0xa1c7ecbbac17bae7", "0x879d3d5a5b0407d4" + ]} + ], + "dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"], + "dataset_last_index": 268435455, + "dataset_last": "0xa33ada72", + "dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}], + "cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"], + "cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"], + "cache_fnv1a64": "0x48c4f5bf24166b2e", + "hot_head": ["0x8068cc73", "0x6036ebf9", "0xb604cd25", "0x8ffb840e", "0xc54074a2", "0x285c0695", "0x77512425", "0xc26a58a7", "0x72c88757", "0xc10fca78", "0x513825dd", "0x30d6ccc8", "0x9a05e7cf", "0xb9533f50", "0x4bac3ba0", "0xa5c19528"], + "hot_last_line": ["0x7c6d7ceb", "0xe24fcd46", "0xe5cce976", "0x3ecffe59", "0x94e98b66", "0x3f6ef45c", "0xa3715aa1", "0xdbe35281", "0xba05d27b", "0x00541963", "0xe636f453", "0xd3972366", "0x529599b8", "0x79ae3c8c", "0x48436863", "0x897a8bf3"], + "hot_fnv1a64": "0x77ca4b9527104530" +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4a/kernel.cl b/proto-cuda/packs-ca2-hot/hot64k4a/kernel.cl new file mode 100644 index 00000000..40ff760e --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4a/kernel.cl @@ -0,0 +1,305 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (64 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 1u); r7 = r7 ^ t_; } // 1 shfl + r0 = r0 | r2; // 2 or + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 16u); r3 = r3 ^ t_; } // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[mul_hi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = mul_hi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 8u); r3 = r3 ^ t_; } // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[mul_hi(r6, HOT_WORDS)]; // 34 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 2u); r4 = r4 ^ t_; } // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r6 = r6 ^ t_; } // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r0 = r0 ^ t_; } // 54 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 16u); r0 = r0 ^ t_; } // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r3 = r3 ^ t_; } // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-hot/hot64k4a/kernel.cu b/proto-cuda/packs-ca2-hot/hot64k4a/kernel.cu new file mode 100644 index 00000000..84bdb1a5 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4a/kernel.cu @@ -0,0 +1,179 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +// Hot table (64 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (ht_segment is in memhard.h): one thread per segment. +__global__ void igneum_hot_fill(uint32_t* hot, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r6 = r6 | r4; // 0 or + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r4, 1); // 1 shfl + r0 = r0 | r2; // 2 or + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r0, 16); // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[__umulhi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = __umulhi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 8); // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[__umulhi(r6, HOT_WORDS)]; // 34 hot + r4 = r4 ^ __shfl_xor_sync(0xffffffffu, r0, 2); // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 54 shfl + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 16); // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_hot_fill<<>>(hot, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4a/kernel_bound.cl b/proto-cuda/packs-ca2-hot/hot64k4a/kernel_bound.cl new file mode 100644 index 00000000..e7682978 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4a/kernel_bound.cl @@ -0,0 +1,399 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (64 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 1u); r7 = r7 ^ t_; } // 1 shfl + r0 = r0 | r2; // 2 or + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 16u); r3 = r3 ^ t_; } // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[mul_hi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = mul_hi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 8u); r3 = r3 ^ t_; } // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[mul_hi(r6, HOT_WORDS)]; // 34 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 2u); r4 = r4 ^ t_; } // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r6 = r6 ^ t_; } // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r0 = r0 ^ t_; } // 54 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 16u); r0 = r0 ^ t_; } // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r3 = r3 ^ t_; } // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 1u); r7 = r7 ^ t_; } // 1 shfl + r0 = r0 | r2; // 2 or + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 16u); r3 = r3 ^ t_; } // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[mul_hi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = mul_hi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 8u); r3 = r3 ^ t_; } // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[mul_hi(r6, HOT_WORDS)]; // 34 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 2u); r4 = r4 ^ t_; } // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r6 = r6 ^ t_; } // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r0 = r0 ^ t_; } // 54 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 16u); r0 = r0 ^ t_; } // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r3 = r3 ^ t_; } // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4a/kernel_bound.cu b/proto-cuda/packs-ca2-hot/hot64k4a/kernel_bound.cu new file mode 100644 index 00000000..57081032 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4a/kernel_bound.cu @@ -0,0 +1,125 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +// Hot table (64 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r6 = r6 | r4; // 0 or + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r4, 1); // 1 shfl + r0 = r0 | r2; // 2 or + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r0, 16); // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[__umulhi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = __umulhi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 8); // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[__umulhi(r6, HOT_WORDS)]; // 34 hot + r4 = r4 ^ __shfl_xor_sync(0xffffffffu, r0, 2); // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 54 shfl + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 16); // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, const uint32_t* hot, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4a/memhard.h b/proto-cuda/packs-ca2-hot/hot64k4a/memhard.h new file mode 100644 index 00000000..180f678b --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4a/memhard.h @@ -0,0 +1,129 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +IGNEUM_HD void ht_segment(uint32_t* hot, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4a/memhard.metal b/proto-cuda/packs-ca2-hot/hot64k4a/memhard.metal new file mode 100644 index 00000000..c2ec5702 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4a/memhard.metal @@ -0,0 +1,131 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; +} + +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +inline void ht_segment(device uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one thread per segment (IGNEUM_HOT_SEGMENTS threads). +kernel void igneum_hot_fill(device uint* hot [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + ht_segment(hot, gid); +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4a/program.h b/proto-cuda/packs-ca2-hot/hot64k4a/program.h new file mode 100644 index 00000000..771be93f --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4a/program.h @@ -0,0 +1,70 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-genesis" +#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973" +#define IGNEUM_GENERATOR 2 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x7f581216e95eb062ull +#define IGNEUM_DAY_STRING "2026-10-03" +#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033" +#define IGNEUM_DAY0 0x3067619fu +#define IGNEUM_DAY1 0x3c269176u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 160 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 shfl=8 add=6 xor=6 mul=5 rotr=5 hot=4 mad=4 or=4 sub=4 mulhi=1 rotl=1" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "hot64k4a" +#define IGNEUM_LOAD_SLOTS 20 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Hot table (hot-table experiment, 5 October 2026, docs/plans/hot-table.md): NOT the lottery hash. IGNEUM_HOT_SLOTS of the +// 16 load slots read H[mulhi(src, IGNEUM_HOT_WORDS)], H = IGNEUM_HOT_MB MiB of chained ChaCha12 lines (the cache chain) under +// KH = seed_words("igneum-hot/" || epoch seed bytes) and the tag "Igne" "umHT"; filled by igneum_hot_fill once per epoch. +#define IGNEUM_HOT_MB 64 +#define IGNEUM_HOT_WORDS 0x01000000u +#define IGNEUM_HOT_SEGMENTS 16384u +#define IGNEUM_HOT_SLOTS 4 // hot loads per program (32 per hash), added beside the dataset loads (16 of them) +#define IGNEUM_HOT_ADDED 1 +#define IGNEUM_HOT_KEY_INIT { 0x3a48bef5u, 0x6b54b1a1u, 0x9ff897c3u, 0x7d5d85e4u, 0xcd35379fu, 0x9d76bb86u, 0xbe2affb1u, 0x0f8f80b4u } +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u } +#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u } +#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u } +#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-hot/hot64k4a/program.json b/proto-cuda/packs-ca2-hot/hot64k4a/program.json new file mode 100644 index 00000000..1fc72c2d --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4a/program.json @@ -0,0 +1,129 @@ +{ + "format": "igneum-program-pack-3", + "generator": 2, + "attempt": 0, + "program_id": "0x7f581216e95eb062", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-genesis", + "seed_bytes": "69676e65756d2d67656e65736973", + "seed_words": ["0x67a9a7be", "0x1a155b25", "0xfddfb732", "0x4b5af2e8", "0xc55caf33", "0xa27c13b7", "0x06628a48", "0x03852469"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 160, + "load_class": "hot64k4a", + "load_slots": 20, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "hot_table": {"mb": 64, "words": 16777216, "segments": 16384, "slots": 4, "form": "added: k load slots added beside the class's, the dataset loads unchanged", "dataset_slots": 16, "hot_loads_per_hash": 32, "key": ["0x3a48bef5", "0x6b54b1a1", "0x9ff897c3", "0x7d5d85e4", "0xcd35379f", "0x9d76bb86", "0xbe2affb1", "0x0f8f80b4"], "key_derivation": "seed_words_from_bytes('igneum-hot/' || seed_bytes), the same for every attempt of the epoch", "tag": ["0x49676e65", "0x756d4854"], "chain": "the cache chain of dataset.cache with the hot key and tag: in_j = prev ^ (sigma || key || seg || j || tag); line_j = block(in_j); prev_0 = 0", "load": "dst = dst ^ hot[mulhi(src, words)] (the high 32 bits of the 64-bit product; the index lies in [0, words) for any size)", "slots_rule": "the first k load slots in the Fisher-Yates draw order after the scratch slots (a uniform k-subset); no extra draw, so with version 2 widths and no scratch the program is the version 2 program with k loads redirected", "acceptance_stand_in": "dataset_elem(mulhi(src, words), seed_words[2], seed_words[3])", "program_id": "the read-width id with 'hot/' || mb || k appended", "spec": "docs/plans/hot-table.md"}, + "op_mix": {"load": 16, "shfl": 8, "add": 6, "xor": 6, "mul": 5, "rotr": 5, "hot": 4, "mad": 4, "or": 4, "sub": 4, "mulhi": 1, "rotl": 1}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)", + "hot": "dst = dst ^ hot[mulhi(src, hot_table.words)] (hot-table experiment)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "2026-10-03", + "day_bytes": "6461792f323032362d31302d3033", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0x3067619f", + "d1": "0x3c269176", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "or", "dst": 6, "src": 4, "src2": 2, "imm": "0x535c5dd3", "imm2": "0xf8694615", "rot": 3, "bit": 1, "mask": 16, "width": 1}, + {"i": 1, "op": "shfl", "dst": 7, "src": 4, "src2": 6, "imm": "0x87bc9ee4", "imm2": "0xfdb0c856", "rot": 9, "bit": 25, "mask": 1, "width": 1}, + {"i": 2, "op": "or", "dst": 0, "src": 2, "src2": 4, "imm": "0xaf91f0c8", "imm2": "0xe2d5d1fa", "rot": 1, "bit": 24, "mask": 2, "width": 1}, + {"i": 3, "op": "shfl", "dst": 3, "src": 0, "src2": 0, "imm": "0x3044ba32", "imm2": "0x7b1a7ffe", "rot": 14, "bit": 10, "mask": 16, "width": 1}, + {"i": 4, "op": "load", "dst": 7, "src": 3, "src2": 6, "imm": "0x5d1ca2a2", "imm2": "0xe2481807", "rot": 24, "bit": 3, "mask": 1, "width": 1}, + {"i": 5, "op": "load", "dst": 6, "src": 0, "src2": 6, "imm": "0xaba3dbaa", "imm2": "0x987c017a", "rot": 16, "bit": 30, "mask": 1, "width": 1}, + {"i": 6, "op": "mul", "dst": 1, "src": 4, "src2": 6, "imm": "0x12a93d05", "imm2": "0x10761047", "rot": 17, "bit": 17, "mask": 4, "width": 1}, + {"i": 7, "op": "sub", "dst": 0, "src": 1, "src2": 0, "imm": "0x8884e389", "imm2": "0x31009b67", "rot": 5, "bit": 30, "mask": 1, "width": 1}, + {"i": 8, "op": "load", "dst": 3, "src": 7, "src2": 5, "imm": "0xfa6358b3", "imm2": "0xfea9038f", "rot": 17, "bit": 18, "mask": 2, "width": 1}, + {"i": 9, "op": "mad", "dst": 1, "src": 0, "src2": 3, "imm": "0xb154ae4f", "imm2": "0xe85f13f6", "rot": 6, "bit": 18, "mask": 16, "width": 1}, + {"i": 10, "op": "mul", "dst": 4, "src": 0, "src2": 4, "imm": "0x9edffbc3", "imm2": "0x643812d4", "rot": 24, "bit": 25, "mask": 4, "width": 1}, + {"i": 11, "op": "load", "dst": 5, "src": 4, "src2": 1, "imm": "0x163dbb1b", "imm2": "0x20c1f743", "rot": 27, "bit": 25, "mask": 16, "width": 1}, + {"i": 12, "op": "xor", "dst": 1, "src": 7, "src2": 4, "imm": "0x9cadb4f8", "imm2": "0xd9da785b", "rot": 8, "bit": 26, "mask": 4, "width": 1}, + {"i": 13, "op": "load", "dst": 1, "src": 6, "src2": 6, "imm": "0x1bb1b429", "imm2": "0x33d1e391", "rot": 20, "bit": 19, "mask": 16, "width": 1}, + {"i": 14, "op": "load", "dst": 2, "src": 1, "src2": 6, "imm": "0xa672cdd3", "imm2": "0x59a4829c", "rot": 22, "bit": 13, "mask": 16, "width": 1}, + {"i": 15, "op": "mul", "dst": 3, "src": 1, "src2": 4, "imm": "0xe3c4bf4d", "imm2": "0x028b4d37", "rot": 11, "bit": 28, "mask": 8, "width": 1}, + {"i": 16, "op": "hot", "dst": 6, "src": 5, "src2": 7, "imm": "0x71fbe6f2", "imm2": "0x61b6261e", "rot": 15, "bit": 9, "mask": 1, "width": 1}, + {"i": 17, "op": "load", "dst": 0, "src": 2, "src2": 5, "imm": "0x8e516fd3", "imm2": "0x2aa480c2", "rot": 19, "bit": 1, "mask": 8, "width": 1}, + {"i": 18, "op": "add", "dst": 0, "src": 7, "src2": 6, "imm": "0xc035a4e6", "imm2": "0x535b545f", "rot": 18, "bit": 26, "mask": 8, "width": 1}, + {"i": 19, "op": "sub", "dst": 5, "src": 0, "src2": 2, "imm": "0xde38f954", "imm2": "0xde1507bc", "rot": 31, "bit": 21, "mask": 4, "width": 1}, + {"i": 20, "op": "mul", "dst": 2, "src": 4, "src2": 1, "imm": "0x3f64b7c9", "imm2": "0x1f07754a", "rot": 23, "bit": 28, "mask": 4, "width": 1}, + {"i": 21, "op": "add", "dst": 2, "src": 1, "src2": 1, "imm": "0x1ecdd2cf", "imm2": "0x925d5e63", "rot": 6, "bit": 6, "mask": 8, "width": 1}, + {"i": 22, "op": "xor", "dst": 3, "src": 7, "src2": 3, "imm": "0x63c7f533", "imm2": "0xd312164d", "rot": 4, "bit": 14, "mask": 2, "width": 1}, + {"i": 23, "op": "load", "dst": 7, "src": 2, "src2": 5, "imm": "0x6d3ddc5a", "imm2": "0x433d8c2a", "rot": 28, "bit": 7, "mask": 2, "width": 1}, + {"i": 24, "op": "mulhi", "dst": 2, "src": 5, "src2": 0, "imm": "0xc7e9887a", "imm2": "0x19ec898f", "rot": 14, "bit": 9, "mask": 1, "width": 1}, + {"i": 25, "op": "rotr", "dst": 5, "src": 2, "src2": 3, "imm": "0x97acf9f2", "imm2": "0xc7fcfc8f", "rot": 8, "bit": 25, "mask": 1, "width": 1}, + {"i": 26, "op": "mad", "dst": 3, "src": 2, "src2": 7, "imm": "0x400383b6", "imm2": "0xe9bab735", "rot": 16, "bit": 18, "mask": 1, "width": 1}, + {"i": 27, "op": "sub", "dst": 2, "src": 0, "src2": 7, "imm": "0x8ea6cd8d", "imm2": "0x55b67f9f", "rot": 19, "bit": 8, "mask": 8, "width": 1}, + {"i": 28, "op": "mad", "dst": 6, "src": 1, "src2": 5, "imm": "0x17723e5a", "imm2": "0x00779664", "rot": 19, "bit": 2, "mask": 4, "width": 1}, + {"i": 29, "op": "add", "dst": 2, "src": 3, "src2": 4, "imm": "0xbdc6da76", "imm2": "0x46b1b505", "rot": 28, "bit": 16, "mask": 16, "width": 1}, + {"i": 30, "op": "shfl", "dst": 3, "src": 2, "src2": 3, "imm": "0xe586702c", "imm2": "0xd83a1462", "rot": 24, "bit": 6, "mask": 8, "width": 1}, + {"i": 31, "op": "load", "dst": 5, "src": 7, "src2": 1, "imm": "0x1cddad42", "imm2": "0xb0c6b0d0", "rot": 25, "bit": 25, "mask": 8, "width": 1}, + {"i": 32, "op": "hot", "dst": 2, "src": 0, "src2": 0, "imm": "0x2abcfdc6", "imm2": "0xfa4cc809", "rot": 22, "bit": 24, "mask": 2, "width": 1}, + {"i": 33, "op": "or", "dst": 6, "src": 3, "src2": 1, "imm": "0x61c9a38d", "imm2": "0x86597500", "rot": 15, "bit": 8, "mask": 2, "width": 1}, + {"i": 34, "op": "hot", "dst": 3, "src": 6, "src2": 7, "imm": "0xc37723fa", "imm2": "0xf3b024da", "rot": 16, "bit": 27, "mask": 16, "width": 1}, + {"i": 35, "op": "shfl", "dst": 4, "src": 0, "src2": 5, "imm": "0x84cad367", "imm2": "0xcc7972c4", "rot": 21, "bit": 17, "mask": 2, "width": 1}, + {"i": 36, "op": "xor", "dst": 5, "src": 2, "src2": 0, "imm": "0xc24a7d70", "imm2": "0x6591d24c", "rot": 21, "bit": 24, "mask": 1, "width": 1}, + {"i": 37, "op": "load", "dst": 3, "src": 4, "src2": 5, "imm": "0xd404cfe8", "imm2": "0x144ca538", "rot": 10, "bit": 11, "mask": 2, "width": 1}, + {"i": 38, "op": "load", "dst": 4, "src": 3, "src2": 3, "imm": "0x04b0080c", "imm2": "0xb938c290", "rot": 10, "bit": 1, "mask": 8, "width": 1}, + {"i": 39, "op": "rotr", "dst": 1, "src": 4, "src2": 5, "imm": "0x9fe93344", "imm2": "0xff7296e4", "rot": 29, "bit": 14, "mask": 1, "width": 1}, + {"i": 40, "op": "add", "dst": 3, "src": 6, "src2": 2, "imm": "0xbc3ff65f", "imm2": "0x6328cb2c", "rot": 4, "bit": 28, "mask": 16, "width": 1}, + {"i": 41, "op": "xor", "dst": 5, "src": 1, "src2": 1, "imm": "0xdcf4a02e", "imm2": "0x5ee3a976", "rot": 17, "bit": 0, "mask": 8, "width": 1}, + {"i": 42, "op": "or", "dst": 5, "src": 0, "src2": 1, "imm": "0x44846c7a", "imm2": "0x67338877", "rot": 27, "bit": 18, "mask": 1, "width": 1}, + {"i": 43, "op": "xor", "dst": 7, "src": 3, "src2": 3, "imm": "0x1b053acf", "imm2": "0x32e2d23d", "rot": 31, "bit": 23, "mask": 8, "width": 1}, + {"i": 44, "op": "load", "dst": 2, "src": 5, "src2": 4, "imm": "0xf8662282", "imm2": "0x10bb9e30", "rot": 8, "bit": 6, "mask": 2, "width": 1}, + {"i": 45, "op": "shfl", "dst": 6, "src": 4, "src2": 4, "imm": "0x87d9ef84", "imm2": "0x49087d74", "rot": 1, "bit": 2, "mask": 8, "width": 1}, + {"i": 46, "op": "xor", "dst": 5, "src": 3, "src2": 1, "imm": "0x74a59b7d", "imm2": "0xc4766ff1", "rot": 30, "bit": 3, "mask": 16, "width": 1}, + {"i": 47, "op": "add", "dst": 7, "src": 6, "src2": 2, "imm": "0x5af3bd5b", "imm2": "0x9342df0d", "rot": 26, "bit": 22, "mask": 2, "width": 1}, + {"i": 48, "op": "add", "dst": 3, "src": 4, "src2": 5, "imm": "0x9ff95776", "imm2": "0x485614db", "rot": 27, "bit": 23, "mask": 1, "width": 1}, + {"i": 49, "op": "load", "dst": 5, "src": 3, "src2": 1, "imm": "0xd022a812", "imm2": "0xfbe147d6", "rot": 15, "bit": 29, "mask": 4, "width": 1}, + {"i": 50, "op": "rotr", "dst": 4, "src": 5, "src2": 7, "imm": "0x7e099325", "imm2": "0x40d55d10", "rot": 29, "bit": 15, "mask": 16, "width": 1}, + {"i": 51, "op": "mad", "dst": 0, "src": 1, "src2": 7, "imm": "0xd8ab8843", "imm2": "0x7f842b90", "rot": 16, "bit": 9, "mask": 1, "width": 1}, + {"i": 52, "op": "mul", "dst": 0, "src": 3, "src2": 6, "imm": "0x4c30250b", "imm2": "0x9ee1681f", "rot": 20, "bit": 3, "mask": 8, "width": 1}, + {"i": 53, "op": "rotr", "dst": 5, "src": 4, "src2": 4, "imm": "0xc1c15026", "imm2": "0x588915e7", "rot": 1, "bit": 29, "mask": 16, "width": 1}, + {"i": 54, "op": "shfl", "dst": 0, "src": 6, "src2": 4, "imm": "0x636a9dc4", "imm2": "0xac023d9b", "rot": 22, "bit": 29, "mask": 1, "width": 1}, + {"i": 55, "op": "shfl", "dst": 0, "src": 7, "src2": 1, "imm": "0x5c933bd0", "imm2": "0x2baec8c9", "rot": 25, "bit": 27, "mask": 16, "width": 1}, + {"i": 56, "op": "load", "dst": 7, "src": 4, "src2": 7, "imm": "0xf62515d5", "imm2": "0x4a164f9f", "rot": 28, "bit": 30, "mask": 2, "width": 1}, + {"i": 57, "op": "rotr", "dst": 7, "src": 3, "src2": 7, "imm": "0x15ef64da", "imm2": "0x7149e3c9", "rot": 28, "bit": 30, "mask": 2, "width": 1}, + {"i": 58, "op": "load", "dst": 0, "src": 6, "src2": 2, "imm": "0xcde7100c", "imm2": "0x040fc0cf", "rot": 8, "bit": 28, "mask": 2, "width": 1}, + {"i": 59, "op": "hot", "dst": 6, "src": 2, "src2": 1, "imm": "0xcf8b48be", "imm2": "0x14cb1d3f", "rot": 16, "bit": 19, "mask": 1, "width": 1}, + {"i": 60, "op": "shfl", "dst": 3, "src": 2, "src2": 1, "imm": "0xae1529ee", "imm2": "0x732bc114", "rot": 4, "bit": 23, "mask": 1, "width": 1}, + {"i": 61, "op": "sub", "dst": 7, "src": 5, "src2": 7, "imm": "0x0a8f8168", "imm2": "0xf1c9b0ed", "rot": 29, "bit": 2, "mask": 8, "width": 1}, + {"i": 62, "op": "rotl", "dst": 1, "src": 2, "src2": 3, "imm": "0x9b7914dd", "imm2": "0xd14b33a3", "rot": 4, "bit": 16, "mask": 16, "width": 1}, + {"i": 63, "op": "load", "dst": 4, "src": 5, "src2": 1, "imm": "0xbbaa8e24", "imm2": "0xd15ec6a6", "rot": 24, "bit": 2, "mask": 2, "width": 1} + ] +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4a/program.metal b/proto-cuda/packs-ca2-hot/hot64k4a/program.metal new file mode 100644 index 00000000..5b1be82f --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4a/program.metal @@ -0,0 +1,112 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (64 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + device const uint* hot [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 + r7 = r7 ^ simd_shuffle_xor(r4, (ushort)1); // 1 + r0 = r0 | r2; // 2 + r3 = r3 ^ simd_shuffle_xor(r0, (ushort)16); // 3 + r7 = r7 ^ dataset[r3 & MASK]; // 4 + r6 = r6 ^ dataset[r0 & MASK]; // 5 + r1 = r1 * r4; // 6 + r0 = r0 - r1; // 7 + r3 = r3 ^ dataset[r7 & MASK]; // 8 + r1 = r0 * r3 + r1; // 9 + r4 = r4 * r0; // 10 + r5 = r5 ^ dataset[r4 & MASK]; // 11 + r1 = r1 ^ r7; // 12 + r1 = r1 ^ dataset[r6 & MASK]; // 13 + r2 = r2 ^ dataset[r1 & MASK]; // 14 + r3 = r3 * r1; // 15 + r6 = r6 ^ hot[mulhi(r5, HOT_WORDS)]; // 16 + r0 = r0 ^ dataset[r2 & MASK]; // 17 + r0 = r0 + r7 + select(0xc035a4e6u, 0x535b545fu, ((sel >> 26u) & 1u) != 0u); // 18 + r5 = r5 - r0; // 19 + r2 = r2 * r4; // 20 + r2 = r2 + r1 + select(0x1ecdd2cfu, 0x925d5e63u, ((sel >> 6u) & 1u) != 0u); // 21 + r3 = r3 ^ r7; // 22 + r7 = r7 ^ dataset[r2 & MASK]; // 23 + r2 = mulhi(r2, r5); // 24 + r5 = rotr_var(r5, r2); // 25 + r3 = r2 * r7 + r3; // 26 + r2 = r2 - r0; // 27 + r6 = r1 * r5 + r6; // 28 + r2 = r2 + r3 + select(0xbdc6da76u, 0x46b1b505u, ((sel >> 16u) & 1u) != 0u); // 29 + r3 = r3 ^ simd_shuffle_xor(r2, (ushort)8); // 30 + r5 = r5 ^ dataset[r7 & MASK]; // 31 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r6 = r6 | r3; // 33 + r3 = r3 ^ hot[mulhi(r6, HOT_WORDS)]; // 34 + r4 = r4 ^ simd_shuffle_xor(r0, (ushort)2); // 35 + r5 = r5 ^ r2; // 36 + r3 = r3 ^ dataset[r4 & MASK]; // 37 + r4 = r4 ^ dataset[r3 & MASK]; // 38 + r1 = rotr_var(r1, r4); // 39 + r3 = r3 + r6 + select(0xbc3ff65fu, 0x6328cb2cu, ((sel >> 28u) & 1u) != 0u); // 40 + r5 = r5 ^ r1; // 41 + r5 = r5 | r0; // 42 + r7 = r7 ^ r3; // 43 + r2 = r2 ^ dataset[r5 & MASK]; // 44 + r6 = r6 ^ simd_shuffle_xor(r4, (ushort)8); // 45 + r5 = r5 ^ r3; // 46 + r7 = r7 + r6 + select(0x5af3bd5bu, 0x9342df0du, ((sel >> 22u) & 1u) != 0u); // 47 + r3 = r3 + r4 + select(0x9ff95776u, 0x485614dbu, ((sel >> 23u) & 1u) != 0u); // 48 + r5 = r5 ^ dataset[r3 & MASK]; // 49 + r4 = rotr_var(r4, r5); // 50 + r0 = r1 * r7 + r0; // 51 + r0 = r0 * r3; // 52 + r5 = rotr_var(r5, r4); // 53 + r0 = r0 ^ simd_shuffle_xor(r6, (ushort)1); // 54 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)16); // 55 + r7 = r7 ^ dataset[r4 & MASK]; // 56 + r7 = rotr_var(r7, r3); // 57 + r0 = r0 ^ dataset[r6 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r3 ^ simd_shuffle_xor(r2, (ushort)1); // 60 + r7 = r7 - r5; // 61 + r1 = rotl_imm(r1, 4u); // 62 + r4 = r4 ^ dataset[r5 & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4a/program_bound.metal b/proto-cuda/packs-ca2-hot/hot64k4a/program_bound.metal new file mode 100644 index 00000000..df4eb94f --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4a/program_bound.metal @@ -0,0 +1,114 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (64 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + device const uint* hot [[buffer(4)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 + r7 = r7 ^ simd_shuffle_xor(r4, (ushort)1); // 1 + r0 = r0 | r2; // 2 + r3 = r3 ^ simd_shuffle_xor(r0, (ushort)16); // 3 + r7 = r7 ^ dataset[r3 & MASK]; // 4 + r6 = r6 ^ dataset[r0 & MASK]; // 5 + r1 = r1 * r4; // 6 + r0 = r0 - r1; // 7 + r3 = r3 ^ dataset[r7 & MASK]; // 8 + r1 = r0 * r3 + r1; // 9 + r4 = r4 * r0; // 10 + r5 = r5 ^ dataset[r4 & MASK]; // 11 + r1 = r1 ^ r7; // 12 + r1 = r1 ^ dataset[r6 & MASK]; // 13 + r2 = r2 ^ dataset[r1 & MASK]; // 14 + r3 = r3 * r1; // 15 + r6 = r6 ^ hot[mulhi(r5, HOT_WORDS)]; // 16 + r0 = r0 ^ dataset[r2 & MASK]; // 17 + r0 = r0 + r7 + select(0xc035a4e6u, 0x535b545fu, ((sel >> 26u) & 1u) != 0u); // 18 + r5 = r5 - r0; // 19 + r2 = r2 * r4; // 20 + r2 = r2 + r1 + select(0x1ecdd2cfu, 0x925d5e63u, ((sel >> 6u) & 1u) != 0u); // 21 + r3 = r3 ^ r7; // 22 + r7 = r7 ^ dataset[r2 & MASK]; // 23 + r2 = mulhi(r2, r5); // 24 + r5 = rotr_var(r5, r2); // 25 + r3 = r2 * r7 + r3; // 26 + r2 = r2 - r0; // 27 + r6 = r1 * r5 + r6; // 28 + r2 = r2 + r3 + select(0xbdc6da76u, 0x46b1b505u, ((sel >> 16u) & 1u) != 0u); // 29 + r3 = r3 ^ simd_shuffle_xor(r2, (ushort)8); // 30 + r5 = r5 ^ dataset[r7 & MASK]; // 31 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r6 = r6 | r3; // 33 + r3 = r3 ^ hot[mulhi(r6, HOT_WORDS)]; // 34 + r4 = r4 ^ simd_shuffle_xor(r0, (ushort)2); // 35 + r5 = r5 ^ r2; // 36 + r3 = r3 ^ dataset[r4 & MASK]; // 37 + r4 = r4 ^ dataset[r3 & MASK]; // 38 + r1 = rotr_var(r1, r4); // 39 + r3 = r3 + r6 + select(0xbc3ff65fu, 0x6328cb2cu, ((sel >> 28u) & 1u) != 0u); // 40 + r5 = r5 ^ r1; // 41 + r5 = r5 | r0; // 42 + r7 = r7 ^ r3; // 43 + r2 = r2 ^ dataset[r5 & MASK]; // 44 + r6 = r6 ^ simd_shuffle_xor(r4, (ushort)8); // 45 + r5 = r5 ^ r3; // 46 + r7 = r7 + r6 + select(0x5af3bd5bu, 0x9342df0du, ((sel >> 22u) & 1u) != 0u); // 47 + r3 = r3 + r4 + select(0x9ff95776u, 0x485614dbu, ((sel >> 23u) & 1u) != 0u); // 48 + r5 = r5 ^ dataset[r3 & MASK]; // 49 + r4 = rotr_var(r4, r5); // 50 + r0 = r1 * r7 + r0; // 51 + r0 = r0 * r3; // 52 + r5 = rotr_var(r5, r4); // 53 + r0 = r0 ^ simd_shuffle_xor(r6, (ushort)1); // 54 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)16); // 55 + r7 = r7 ^ dataset[r4 & MASK]; // 56 + r7 = rotr_var(r7, r3); // 57 + r0 = r0 ^ dataset[r6 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r3 ^ simd_shuffle_xor(r2, (ushort)1); // 60 + r7 = r7 - r5; // 61 + r1 = rotl_imm(r1, 4u); // 62 + r4 = r4 ^ dataset[r5 & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot64k4a/vectors.h b/proto-cuda/packs-ca2-hot/hot64k4a/vectors.h new file mode 100644 index 00000000..b5754ac5 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4a/vectors.h @@ -0,0 +1,67 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0x4a6c42fc5764d8daull, 0x37c212a56e0569d3ull, 0x7b7a757a397db3f9ull, 0x5f9239251eba91acull, 0x135c81a6021e6ebaull, 0xdfa3a5e06a090a2dull, 0xa9fb970071c3f44eull, 0x52ea9c6007fab551ull, + 0xd8cf556ecaecf076ull, 0xa62cb0a038cd1b6bull, 0xb9f2deaafe56f529ull, 0x12d5820babcd7ed7ull, 0x2fd74c1f4c263db1ull, 0x0a6d69f426775ccfull, 0x54770096d3830839ull, 0x716fb1b1386bb7c9ull, + 0xabee533d572e49d0ull, 0x1016d59d921ac63aull, 0xcba4535a1aa1cbecull, 0x8bb81e8fc4155979ull, 0x49b38d7f02559f63ull, 0x5d3305c9df5caec5ull, 0x7d27218c53fceaffull, 0xd5227fd7a84cd56bull, + 0x9a8bdfa2072dabfaull, 0x04d0c1de6291afcbull, 0x081e212f940bdcb0ull, 0xd89a68f84756809aull, 0x01c8d632262d9d4bull, 0x8aacf4738a85e560ull, 0x0cd6e71517b63f2eull, 0x16228149075efbbdull + }, + { // base nonce 4096 + 0xacc15aebf682c54eull, 0xb0161dc67381582bull, 0xb6e4fe3e5c7ef39bull, 0x84e8d563e4177b06ull, 0x7a82671908cf3339ull, 0x615d82bfc8764e2bull, 0x5e10090034c4f702ull, 0x6ac4930e87ec07d4ull, + 0xb84b0e2f24efc7cfull, 0xc32dbd5e9ba5d8ceull, 0xc08a54d4cdf36eb1ull, 0xa97d9ed652f10bb6ull, 0x5245ef8bd1f8c7ecull, 0x255f3c03e0aab8d2ull, 0xe13ac7aa4fea498bull, 0x359427f974cefc36ull, + 0x7e1edeceb422b084ull, 0xd52ab3281a61b0bfull, 0xcadfb6c064863f68ull, 0x151dea2e8ba325feull, 0x72765f61e58f7e14ull, 0xc1408a61c1a7a8e9ull, 0xe19b0547c381fcefull, 0x1ffe3c35eaf66da6ull, + 0xa65d3957dded456eull, 0x9d8be52052fdf575ull, 0xf9215ef5fe2cff7dull, 0xdb6a6d7479219649ull, 0x6d7c535bf72c9244ull, 0x3cb662e110255e7bull, 0x10ea30028c05989full, 0x4cd430d0231a71d4ull + }, + { // base nonce 1000000 + 0x9ac3bb238bb22131ull, 0x0bda004ad3f1ff29ull, 0x899ca1164378afb2ull, 0x46b6a4102c6d7a6cull, 0x2af9223204de4911ull, 0x5970c0c0a985a8e7ull, 0x204dd16a10561ecdull, 0x18fb72c36de899eeull, + 0xf1cc27b160f797feull, 0x080b9a4ae81f63a1ull, 0x7ba0935f19aa11cfull, 0xa4e3728850084707ull, 0xcd7be68dcef247a7ull, 0x16ac696dc46ed44eull, 0x05f2629a93b1d468ull, 0x52e6f0ec20da269full, + 0xb2b6041980aea11cull, 0x257d149fbd830dccull, 0x8b176d000043b42eull, 0xb318688b422040d5ull, 0xb1795b8c9a7d7ba9ull, 0x8639442ed4c424caull, 0x4ccf966dd01cf886ull, 0xbc7e653603bf904bull, + 0x25593174d1637b15ull, 0xac52c8645ce16ee0ull, 0x1ab7366733ec0850ull, 0x76d375c623fa7438ull, 0xb8af625ded198b28ull, 0xd34b4bd1e70ea01full, 0xd50747c69aac5f02ull, 0xc3a4c63e9b334ed1ull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu, + 0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u, + 0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du, + 0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull; +// Hot table self-test (docs/plans/hot-table.md): hot[0..15], the last 16 words, and FNV-1a 64 over all IGNEUM_HOT_WORDS words. +static const uint32_t IGNEUM_HOT_HEAD[16] = { + 0x8068cc73u, 0x6036ebf9u, 0xb604cd25u, 0x8ffb840eu, 0xc54074a2u, 0x285c0695u, 0x77512425u, 0xc26a58a7u, + 0x72c88757u, 0xc10fca78u, 0x513825ddu, 0x30d6ccc8u, 0x9a05e7cfu, 0xb9533f50u, 0x4bac3ba0u, 0xa5c19528u +}; +static const uint32_t IGNEUM_HOT_LAST[16] = { + 0x7c6d7cebu, 0xe24fcd46u, 0xe5cce976u, 0x3ecffe59u, 0x94e98b66u, 0x3f6ef45cu, 0xa3715aa1u, 0xdbe35281u, + 0xba05d27bu, 0x00541963u, 0xe636f453u, 0xd3972366u, 0x529599b8u, 0x79ae3c8cu, 0x48436863u, 0x897a8bf3u +}; +static const uint64_t IGNEUM_HOT_FNV64 = 0x77ca4b9527104530ull; diff --git a/proto-cuda/packs-ca2-hot/hot64k4a/vectors.json b/proto-cuda/packs-ca2-hot/hot64k4a/vectors.json new file mode 100644 index 00000000..1233f634 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k4a/vectors.json @@ -0,0 +1,39 @@ +{ + "seed": "igneum-genesis", + "day": "2026-10-03", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0x4a6c42fc5764d8da", "0x37c212a56e0569d3", "0x7b7a757a397db3f9", "0x5f9239251eba91ac", "0x135c81a6021e6eba", "0xdfa3a5e06a090a2d", "0xa9fb970071c3f44e", "0x52ea9c6007fab551", + "0xd8cf556ecaecf076", "0xa62cb0a038cd1b6b", "0xb9f2deaafe56f529", "0x12d5820babcd7ed7", "0x2fd74c1f4c263db1", "0x0a6d69f426775ccf", "0x54770096d3830839", "0x716fb1b1386bb7c9", + "0xabee533d572e49d0", "0x1016d59d921ac63a", "0xcba4535a1aa1cbec", "0x8bb81e8fc4155979", "0x49b38d7f02559f63", "0x5d3305c9df5caec5", "0x7d27218c53fceaff", "0xd5227fd7a84cd56b", + "0x9a8bdfa2072dabfa", "0x04d0c1de6291afcb", "0x081e212f940bdcb0", "0xd89a68f84756809a", "0x01c8d632262d9d4b", "0x8aacf4738a85e560", "0x0cd6e71517b63f2e", "0x16228149075efbbd" + ]}, + {"base_nonce": 4096, "expected": [ + "0xacc15aebf682c54e", "0xb0161dc67381582b", "0xb6e4fe3e5c7ef39b", "0x84e8d563e4177b06", "0x7a82671908cf3339", "0x615d82bfc8764e2b", "0x5e10090034c4f702", "0x6ac4930e87ec07d4", + "0xb84b0e2f24efc7cf", "0xc32dbd5e9ba5d8ce", "0xc08a54d4cdf36eb1", "0xa97d9ed652f10bb6", "0x5245ef8bd1f8c7ec", "0x255f3c03e0aab8d2", "0xe13ac7aa4fea498b", "0x359427f974cefc36", + "0x7e1edeceb422b084", "0xd52ab3281a61b0bf", "0xcadfb6c064863f68", "0x151dea2e8ba325fe", "0x72765f61e58f7e14", "0xc1408a61c1a7a8e9", "0xe19b0547c381fcef", "0x1ffe3c35eaf66da6", + "0xa65d3957dded456e", "0x9d8be52052fdf575", "0xf9215ef5fe2cff7d", "0xdb6a6d7479219649", "0x6d7c535bf72c9244", "0x3cb662e110255e7b", "0x10ea30028c05989f", "0x4cd430d0231a71d4" + ]}, + {"base_nonce": 1000000, "expected": [ + "0x9ac3bb238bb22131", "0x0bda004ad3f1ff29", "0x899ca1164378afb2", "0x46b6a4102c6d7a6c", "0x2af9223204de4911", "0x5970c0c0a985a8e7", "0x204dd16a10561ecd", "0x18fb72c36de899ee", + "0xf1cc27b160f797fe", "0x080b9a4ae81f63a1", "0x7ba0935f19aa11cf", "0xa4e3728850084707", "0xcd7be68dcef247a7", "0x16ac696dc46ed44e", "0x05f2629a93b1d468", "0x52e6f0ec20da269f", + "0xb2b6041980aea11c", "0x257d149fbd830dcc", "0x8b176d000043b42e", "0xb318688b422040d5", "0xb1795b8c9a7d7ba9", "0x8639442ed4c424ca", "0x4ccf966dd01cf886", "0xbc7e653603bf904b", + "0x25593174d1637b15", "0xac52c8645ce16ee0", "0x1ab7366733ec0850", "0x76d375c623fa7438", "0xb8af625ded198b28", "0xd34b4bd1e70ea01f", "0xd50747c69aac5f02", "0xc3a4c63e9b334ed1" + ]} + ], + "dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"], + "dataset_last_index": 268435455, + "dataset_last": "0xa33ada72", + "dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}], + "cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"], + "cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"], + "cache_fnv1a64": "0x48c4f5bf24166b2e", + "hot_head": ["0x8068cc73", "0x6036ebf9", "0xb604cd25", "0x8ffb840e", "0xc54074a2", "0x285c0695", "0x77512425", "0xc26a58a7", "0x72c88757", "0xc10fca78", "0x513825dd", "0x30d6ccc8", "0x9a05e7cf", "0xb9533f50", "0x4bac3ba0", "0xa5c19528"], + "hot_last_line": ["0x7c6d7ceb", "0xe24fcd46", "0xe5cce976", "0x3ecffe59", "0x94e98b66", "0x3f6ef45c", "0xa3715aa1", "0xdbe35281", "0xba05d27b", "0x00541963", "0xe636f453", "0xd3972366", "0x529599b8", "0x79ae3c8c", "0x48436863", "0x897a8bf3"], + "hot_fnv1a64": "0x77ca4b9527104530" +} diff --git a/proto-cuda/packs-ca2-hot/hot64k8/kernel.cl b/proto-cuda/packs-ca2-hot/hot64k8/kernel.cl new file mode 100644 index 00000000..d02e82dd --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k8/kernel.cl @@ -0,0 +1,305 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (64 MiB, 8 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ hot[mul_hi(r2, HOT_WORDS)]; // 4 hot + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ hot[mul_hi(r1, HOT_WORDS)]; // 23 hot + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ hot[mul_hi(r0, HOT_WORDS)]; // 37 hot + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ hot[mul_hi(r7, HOT_WORDS)]; // 44 hot + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-hot/hot64k8/kernel.cu b/proto-cuda/packs-ca2-hot/hot64k8/kernel.cu new file mode 100644 index 00000000..b51ebb34 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k8/kernel.cu @@ -0,0 +1,179 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +// Hot table (64 MiB, 8 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (ht_segment is in memhard.h): one thread per segment. +__global__ void igneum_hot_fill(uint32_t* hot, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ hot[__umulhi(r2, HOT_WORDS)]; // 4 hot + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ hot[__umulhi(r1, HOT_WORDS)]; // 23 hot + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[__umulhi(r1, HOT_WORDS)]; // 34 hot + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ hot[__umulhi(r0, HOT_WORDS)]; // 37 hot + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ hot[__umulhi(r7, HOT_WORDS)]; // 44 hot + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_hot_fill<<>>(hot, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot64k8/kernel_bound.cl b/proto-cuda/packs-ca2-hot/hot64k8/kernel_bound.cl new file mode 100644 index 00000000..5c40a8ea --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k8/kernel_bound.cl @@ -0,0 +1,399 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (64 MiB, 8 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ hot[mul_hi(r2, HOT_WORDS)]; // 4 hot + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ hot[mul_hi(r1, HOT_WORDS)]; // 23 hot + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ hot[mul_hi(r0, HOT_WORDS)]; // 37 hot + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ hot[mul_hi(r7, HOT_WORDS)]; // 44 hot + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ hot[mul_hi(r2, HOT_WORDS)]; // 4 hot + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ hot[mul_hi(r1, HOT_WORDS)]; // 23 hot + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ hot[mul_hi(r0, HOT_WORDS)]; // 37 hot + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ hot[mul_hi(r7, HOT_WORDS)]; // 44 hot + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot64k8/kernel_bound.cu b/proto-cuda/packs-ca2-hot/hot64k8/kernel_bound.cu new file mode 100644 index 00000000..c28cece2 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k8/kernel_bound.cu @@ -0,0 +1,125 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +// Hot table (64 MiB, 8 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ hot[__umulhi(r2, HOT_WORDS)]; // 4 hot + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ hot[__umulhi(r1, HOT_WORDS)]; // 23 hot + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[__umulhi(r1, HOT_WORDS)]; // 34 hot + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ hot[__umulhi(r0, HOT_WORDS)]; // 37 hot + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ hot[__umulhi(r7, HOT_WORDS)]; // 44 hot + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, const uint32_t* hot, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot64k8/memhard.h b/proto-cuda/packs-ca2-hot/hot64k8/memhard.h new file mode 100644 index 00000000..180f678b --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k8/memhard.h @@ -0,0 +1,129 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +IGNEUM_HD void ht_segment(uint32_t* hot, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} diff --git a/proto-cuda/packs-ca2-hot/hot64k8/memhard.metal b/proto-cuda/packs-ca2-hot/hot64k8/memhard.metal new file mode 100644 index 00000000..c2ec5702 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k8/memhard.metal @@ -0,0 +1,131 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; +} + +// Hot table (docs/plans/hot-table.md): 64 MiB = 16384 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +inline void ht_segment(device uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one thread per segment (IGNEUM_HOT_SEGMENTS threads). +kernel void igneum_hot_fill(device uint* hot [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + ht_segment(hot, gid); +} diff --git a/proto-cuda/packs-ca2-hot/hot64k8/program.h b/proto-cuda/packs-ca2-hot/hot64k8/program.h new file mode 100644 index 00000000..88d5df38 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k8/program.h @@ -0,0 +1,70 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-genesis" +#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973" +#define IGNEUM_GENERATOR 2 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x322a68466d4a0998ull +#define IGNEUM_DAY_STRING "2026-10-03" +#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033" +#define IGNEUM_DAY0 0x3067619fu +#define IGNEUM_DAY1 0x3c269176u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "add=8 hot=8 load=8 shfl=8 xor=6 mad=5 mul=5 mulhi=5 sub=4 rotl=3 rotr=3 or=1" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "hot64k8" +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 8, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 256 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Hot table (hot-table experiment, 5 October 2026, docs/plans/hot-table.md): NOT the lottery hash. IGNEUM_HOT_SLOTS of the +// 16 load slots read H[mulhi(src, IGNEUM_HOT_WORDS)], H = IGNEUM_HOT_MB MiB of chained ChaCha12 lines (the cache chain) under +// KH = seed_words("igneum-hot/" || epoch seed bytes) and the tag "Igne" "umHT"; filled by igneum_hot_fill once per epoch. +#define IGNEUM_HOT_MB 64 +#define IGNEUM_HOT_WORDS 0x01000000u +#define IGNEUM_HOT_SEGMENTS 16384u +#define IGNEUM_HOT_SLOTS 8 // hot loads per program (64 per hash), replacing the dataset loads (8 of them) +#define IGNEUM_HOT_ADDED 0 +#define IGNEUM_HOT_KEY_INIT { 0x3a48bef5u, 0x6b54b1a1u, 0x9ff897c3u, 0x7d5d85e4u, 0xcd35379fu, 0x9d76bb86u, 0xbe2affb1u, 0x0f8f80b4u } +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u } +#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u } +#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u } +#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-hot/hot64k8/program.json b/proto-cuda/packs-ca2-hot/hot64k8/program.json new file mode 100644 index 00000000..63e52f1c --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k8/program.json @@ -0,0 +1,129 @@ +{ + "format": "igneum-program-pack-3", + "generator": 2, + "attempt": 0, + "program_id": "0x322a68466d4a0998", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-genesis", + "seed_bytes": "69676e65756d2d67656e65736973", + "seed_words": ["0x67a9a7be", "0x1a155b25", "0xfddfb732", "0x4b5af2e8", "0xc55caf33", "0xa27c13b7", "0x06628a48", "0x03852469"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "load_class": "hot64k8", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [8, 0, 0], + "bytes_per_hash": 256, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "hot_table": {"mb": 64, "words": 16777216, "segments": 16384, "slots": 8, "form": "replaced: k of the class's load slots read the table", "dataset_slots": 8, "hot_loads_per_hash": 64, "key": ["0x3a48bef5", "0x6b54b1a1", "0x9ff897c3", "0x7d5d85e4", "0xcd35379f", "0x9d76bb86", "0xbe2affb1", "0x0f8f80b4"], "key_derivation": "seed_words_from_bytes('igneum-hot/' || seed_bytes), the same for every attempt of the epoch", "tag": ["0x49676e65", "0x756d4854"], "chain": "the cache chain of dataset.cache with the hot key and tag: in_j = prev ^ (sigma || key || seg || j || tag); line_j = block(in_j); prev_0 = 0", "load": "dst = dst ^ hot[mulhi(src, words)] (the high 32 bits of the 64-bit product; the index lies in [0, words) for any size)", "slots_rule": "the first k load slots in the Fisher-Yates draw order after the scratch slots (a uniform k-subset); no extra draw, so with version 2 widths and no scratch the program is the version 2 program with k loads redirected", "acceptance_stand_in": "dataset_elem(mulhi(src, words), seed_words[2], seed_words[3])", "program_id": "the read-width id with 'hot/' || mb || k appended", "spec": "docs/plans/hot-table.md"}, + "op_mix": {"add": 8, "hot": 8, "load": 8, "shfl": 8, "xor": 6, "mad": 5, "mul": 5, "mulhi": 5, "sub": 4, "rotl": 3, "rotr": 3, "or": 1}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)", + "hot": "dst = dst ^ hot[mulhi(src, hot_table.words)] (hot-table experiment)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "2026-10-03", + "day_bytes": "6461792f323032362d31302d3033", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0x3067619f", + "d1": "0x3c269176", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "mad", "dst": 2, "src": 3, "src2": 4, "imm": "0xbf7b174d", "imm2": "0x337b762e", "rot": 17, "bit": 2, "mask": 2, "width": 1}, + {"i": 1, "op": "mad", "dst": 2, "src": 1, "src2": 1, "imm": "0xdd04a5da", "imm2": "0x42da7657", "rot": 15, "bit": 30, "mask": 16, "width": 1}, + {"i": 2, "op": "mad", "dst": 2, "src": 3, "src2": 2, "imm": "0x734003fa", "imm2": "0x5bb67700", "rot": 3, "bit": 20, "mask": 1, "width": 1}, + {"i": 3, "op": "xor", "dst": 3, "src": 5, "src2": 5, "imm": "0xc55a1b1c", "imm2": "0xa19720f3", "rot": 7, "bit": 8, "mask": 1, "width": 1}, + {"i": 4, "op": "hot", "dst": 7, "src": 2, "src2": 5, "imm": "0xad572dd7", "imm2": "0x9ceb3ea7", "rot": 18, "bit": 30, "mask": 2, "width": 1}, + {"i": 5, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x769a53be", "imm2": "0x80f9067e", "rot": 12, "bit": 22, "mask": 1, "width": 1}, + {"i": 6, "op": "shfl", "dst": 1, "src": 4, "src2": 0, "imm": "0xd3613d88", "imm2": "0x262fb219", "rot": 10, "bit": 30, "mask": 8, "width": 1}, + {"i": 7, "op": "shfl", "dst": 7, "src": 3, "src2": 4, "imm": "0xce38e42f", "imm2": "0xb868b818", "rot": 11, "bit": 8, "mask": 8, "width": 1}, + {"i": 8, "op": "mulhi", "dst": 1, "src": 5, "src2": 2, "imm": "0xa5eebca5", "imm2": "0x5703a72b", "rot": 13, "bit": 13, "mask": 16, "width": 1}, + {"i": 9, "op": "rotr", "dst": 6, "src": 3, "src2": 4, "imm": "0x17a5a9c7", "imm2": "0xdcfb93a1", "rot": 20, "bit": 27, "mask": 2, "width": 1}, + {"i": 10, "op": "or", "dst": 3, "src": 4, "src2": 1, "imm": "0xccb7d785", "imm2": "0xc335364c", "rot": 14, "bit": 12, "mask": 4, "width": 1}, + {"i": 11, "op": "load", "dst": 4, "src": 3, "src2": 2, "imm": "0x88cb9af3", "imm2": "0x4e7dc10d", "rot": 24, "bit": 17, "mask": 4, "width": 1}, + {"i": 12, "op": "mulhi", "dst": 0, "src": 4, "src2": 4, "imm": "0x45374321", "imm2": "0x3cd91989", "rot": 11, "bit": 4, "mask": 2, "width": 1}, + {"i": 13, "op": "add", "dst": 5, "src": 1, "src2": 2, "imm": "0xc7934706", "imm2": "0xd3177981", "rot": 16, "bit": 30, "mask": 2, "width": 1}, + {"i": 14, "op": "load", "dst": 0, "src": 4, "src2": 3, "imm": "0xfd7f56bb", "imm2": "0x65e14f52", "rot": 13, "bit": 22, "mask": 2, "width": 1}, + {"i": 15, "op": "sub", "dst": 2, "src": 4, "src2": 4, "imm": "0x35a80b49", "imm2": "0x060f2d13", "rot": 16, "bit": 20, "mask": 16, "width": 1}, + {"i": 16, "op": "hot", "dst": 2, "src": 0, "src2": 2, "imm": "0xae0a32c2", "imm2": "0x4c2a4cfe", "rot": 8, "bit": 31, "mask": 16, "width": 1}, + {"i": 17, "op": "load", "dst": 7, "src": 2, "src2": 6, "imm": "0x82a84cc3", "imm2": "0x21a38d68", "rot": 15, "bit": 21, "mask": 2, "width": 1}, + {"i": 18, "op": "shfl", "dst": 7, "src": 3, "src2": 3, "imm": "0xa3818806", "imm2": "0x8f66b5c8", "rot": 14, "bit": 6, "mask": 4, "width": 1}, + {"i": 19, "op": "mul", "dst": 5, "src": 0, "src2": 1, "imm": "0xa00de107", "imm2": "0x77bfcaa5", "rot": 3, "bit": 10, "mask": 2, "width": 1}, + {"i": 20, "op": "shfl", "dst": 3, "src": 4, "src2": 7, "imm": "0x1d2b8cab", "imm2": "0x80b4f9a2", "rot": 14, "bit": 25, "mask": 2, "width": 1}, + {"i": 21, "op": "shfl", "dst": 2, "src": 4, "src2": 5, "imm": "0x3ac915d2", "imm2": "0x5fba7bc2", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 22, "op": "mulhi", "dst": 6, "src": 2, "src2": 0, "imm": "0xdc3ec8fd", "imm2": "0x599e2fa3", "rot": 22, "bit": 3, "mask": 2, "width": 1}, + {"i": 23, "op": "hot", "dst": 6, "src": 1, "src2": 5, "imm": "0x2a6b16d5", "imm2": "0xd73e396f", "rot": 28, "bit": 29, "mask": 2, "width": 1}, + {"i": 24, "op": "mul", "dst": 5, "src": 0, "src2": 7, "imm": "0x376d0223", "imm2": "0xe1c2169a", "rot": 4, "bit": 16, "mask": 16, "width": 1}, + {"i": 25, "op": "rotl", "dst": 5, "src": 7, "src2": 3, "imm": "0x78ad8c60", "imm2": "0x6f5b77d5", "rot": 19, "bit": 11, "mask": 16, "width": 1}, + {"i": 26, "op": "shfl", "dst": 7, "src": 6, "src2": 5, "imm": "0x93915b9f", "imm2": "0x1e61fb6b", "rot": 28, "bit": 23, "mask": 2, "width": 1}, + {"i": 27, "op": "xor", "dst": 0, "src": 5, "src2": 7, "imm": "0x6378fe15", "imm2": "0x66c78f42", "rot": 12, "bit": 31, "mask": 8, "width": 1}, + {"i": 28, "op": "xor", "dst": 0, "src": 4, "src2": 7, "imm": "0x20a57fda", "imm2": "0x088c848e", "rot": 16, "bit": 13, "mask": 4, "width": 1}, + {"i": 29, "op": "sub", "dst": 3, "src": 0, "src2": 2, "imm": "0x49d95fd5", "imm2": "0x1a5f946a", "rot": 6, "bit": 12, "mask": 1, "width": 1}, + {"i": 30, "op": "mul", "dst": 5, "src": 1, "src2": 7, "imm": "0x0a816217", "imm2": "0x405c4f73", "rot": 13, "bit": 27, "mask": 4, "width": 1}, + {"i": 31, "op": "load", "dst": 7, "src": 2, "src2": 2, "imm": "0x09ed045e", "imm2": "0xd69c4715", "rot": 5, "bit": 9, "mask": 2, "width": 1}, + {"i": 32, "op": "hot", "dst": 1, "src": 0, "src2": 6, "imm": "0xeb79ea49", "imm2": "0xcc587f5a", "rot": 6, "bit": 8, "mask": 16, "width": 1}, + {"i": 33, "op": "xor", "dst": 5, "src": 6, "src2": 1, "imm": "0x3027401e", "imm2": "0x5f20c27e", "rot": 18, "bit": 9, "mask": 2, "width": 1}, + {"i": 34, "op": "hot", "dst": 5, "src": 1, "src2": 3, "imm": "0x0e1cab07", "imm2": "0x09356c5b", "rot": 19, "bit": 31, "mask": 1, "width": 1}, + {"i": 35, "op": "mulhi", "dst": 0, "src": 5, "src2": 2, "imm": "0x90e31357", "imm2": "0xabd32484", "rot": 26, "bit": 5, "mask": 8, "width": 1}, + {"i": 36, "op": "shfl", "dst": 5, "src": 2, "src2": 5, "imm": "0xee9a955f", "imm2": "0x31b3faed", "rot": 8, "bit": 24, "mask": 4, "width": 1}, + {"i": 37, "op": "hot", "dst": 7, "src": 0, "src2": 7, "imm": "0x3ba2f832", "imm2": "0x1160dcd3", "rot": 4, "bit": 29, "mask": 1, "width": 1}, + {"i": 38, "op": "add", "dst": 3, "src": 1, "src2": 7, "imm": "0x75ba2fad", "imm2": "0x230c005c", "rot": 4, "bit": 27, "mask": 1, "width": 1}, + {"i": 39, "op": "shfl", "dst": 1, "src": 5, "src2": 3, "imm": "0xdbf37e75", "imm2": "0xb5ac1969", "rot": 30, "bit": 13, "mask": 4, "width": 1}, + {"i": 40, "op": "xor", "dst": 2, "src": 5, "src2": 5, "imm": "0x47f136c5", "imm2": "0x06ce9153", "rot": 19, "bit": 10, "mask": 2, "width": 1}, + {"i": 41, "op": "mad", "dst": 3, "src": 6, "src2": 3, "imm": "0xce13eff8", "imm2": "0x04cc1d55", "rot": 3, "bit": 1, "mask": 4, "width": 1}, + {"i": 42, "op": "sub", "dst": 6, "src": 7, "src2": 1, "imm": "0x6a65ab71", "imm2": "0x8fbc1bcd", "rot": 4, "bit": 1, "mask": 8, "width": 1}, + {"i": 43, "op": "xor", "dst": 7, "src": 0, "src2": 7, "imm": "0xdaeb4928", "imm2": "0xc0423027", "rot": 24, "bit": 11, "mask": 8, "width": 1}, + {"i": 44, "op": "hot", "dst": 1, "src": 7, "src2": 7, "imm": "0x778f01c9", "imm2": "0x28cedcea", "rot": 12, "bit": 4, "mask": 16, "width": 1}, + {"i": 45, "op": "mul", "dst": 2, "src": 3, "src2": 2, "imm": "0xf4264f1b", "imm2": "0x0f627d56", "rot": 5, "bit": 28, "mask": 8, "width": 1}, + {"i": 46, "op": "mulhi", "dst": 1, "src": 5, "src2": 1, "imm": "0xffb2147a", "imm2": "0xccde9b05", "rot": 13, "bit": 9, "mask": 2, "width": 1}, + {"i": 47, "op": "sub", "dst": 4, "src": 3, "src2": 5, "imm": "0x73b36234", "imm2": "0x3f5d5997", "rot": 7, "bit": 18, "mask": 2, "width": 1}, + {"i": 48, "op": "rotr", "dst": 2, "src": 6, "src2": 3, "imm": "0x3a4d9aa9", "imm2": "0x212bec7b", "rot": 4, "bit": 29, "mask": 16, "width": 1}, + {"i": 49, "op": "load", "dst": 3, "src": 5, "src2": 2, "imm": "0x626f11df", "imm2": "0x56cd5bfd", "rot": 7, "bit": 1, "mask": 1, "width": 1}, + {"i": 50, "op": "add", "dst": 1, "src": 5, "src2": 6, "imm": "0x81b8bc2c", "imm2": "0x1907970c", "rot": 28, "bit": 7, "mask": 4, "width": 1}, + {"i": 51, "op": "mul", "dst": 0, "src": 2, "src2": 2, "imm": "0xa8848b30", "imm2": "0xef6ac348", "rot": 9, "bit": 15, "mask": 8, "width": 1}, + {"i": 52, "op": "add", "dst": 0, "src": 2, "src2": 0, "imm": "0x4f92b968", "imm2": "0x699fd448", "rot": 22, "bit": 6, "mask": 4, "width": 1}, + {"i": 53, "op": "add", "dst": 1, "src": 0, "src2": 2, "imm": "0x2bb965af", "imm2": "0x77b1520d", "rot": 2, "bit": 12, "mask": 8, "width": 1}, + {"i": 54, "op": "rotl", "dst": 7, "src": 1, "src2": 0, "imm": "0x553e678b", "imm2": "0x3cc8eae0", "rot": 14, "bit": 20, "mask": 2, "width": 1}, + {"i": 55, "op": "add", "dst": 3, "src": 7, "src2": 2, "imm": "0x7b0fe07a", "imm2": "0xa54c55a0", "rot": 10, "bit": 1, "mask": 1, "width": 1}, + {"i": 56, "op": "load", "dst": 6, "src": 7, "src2": 4, "imm": "0x01eba9aa", "imm2": "0x2758c0f7", "rot": 14, "bit": 15, "mask": 4, "width": 1}, + {"i": 57, "op": "rotr", "dst": 1, "src": 5, "src2": 2, "imm": "0x1f5267b3", "imm2": "0x236f5a27", "rot": 2, "bit": 31, "mask": 16, "width": 1}, + {"i": 58, "op": "load", "dst": 5, "src": 4, "src2": 3, "imm": "0xa9954a9b", "imm2": "0x6a54d4e8", "rot": 11, "bit": 10, "mask": 16, "width": 1}, + {"i": 59, "op": "hot", "dst": 6, "src": 2, "src2": 4, "imm": "0x9923ff88", "imm2": "0x9357254e", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 60, "op": "mad", "dst": 3, "src": 5, "src2": 0, "imm": "0xc0cc51a6", "imm2": "0x3fd7701b", "rot": 20, "bit": 1, "mask": 4, "width": 1}, + {"i": 61, "op": "add", "dst": 5, "src": 7, "src2": 7, "imm": "0xaf9dd72d", "imm2": "0xad7493e7", "rot": 7, "bit": 31, "mask": 16, "width": 1}, + {"i": 62, "op": "add", "dst": 4, "src": 6, "src2": 2, "imm": "0x89841d87", "imm2": "0x1e07c3d9", "rot": 6, "bit": 27, "mask": 1, "width": 1}, + {"i": 63, "op": "rotl", "dst": 5, "src": 4, "src2": 2, "imm": "0xae210f8d", "imm2": "0x8e499ba4", "rot": 19, "bit": 9, "mask": 1, "width": 1} + ] +} diff --git a/proto-cuda/packs-ca2-hot/hot64k8/program.metal b/proto-cuda/packs-ca2-hot/hot64k8/program.metal new file mode 100644 index 00000000..38e0298f --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k8/program.metal @@ -0,0 +1,112 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (64 MiB, 8 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + device const uint* hot [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ hot[mulhi(r2, HOT_WORDS)]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ hot[mulhi(r1, HOT_WORDS)]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ hot[mulhi(r1, HOT_WORDS)]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ hot[mulhi(r0, HOT_WORDS)]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ hot[mulhi(r7, HOT_WORDS)]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot64k8/program_bound.metal b/proto-cuda/packs-ca2-hot/hot64k8/program_bound.metal new file mode 100644 index 00000000..cfb7e40f --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k8/program_bound.metal @@ -0,0 +1,114 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (64 MiB, 8 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01000000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + device const uint* hot [[buffer(4)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ hot[mulhi(r2, HOT_WORDS)]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ hot[mulhi(r1, HOT_WORDS)]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ hot[mulhi(r1, HOT_WORDS)]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ hot[mulhi(r0, HOT_WORDS)]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ hot[mulhi(r7, HOT_WORDS)]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot64k8/vectors.h b/proto-cuda/packs-ca2-hot/hot64k8/vectors.h new file mode 100644 index 00000000..83b42f86 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k8/vectors.h @@ -0,0 +1,67 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0xe2e3466ed893b4f8ull, 0x8a8a8c07512484a5ull, 0x2eab76ad896647ebull, 0x308c5fb089674ebdull, 0x6851d37a31b0da79ull, 0xdf80d0cd9d2bbd54ull, 0x0bb440d7e0a0c83bull, 0xde6fb92cd5441665ull, + 0xd2a4449d68eb862cull, 0xf863dc5645b0081bull, 0xb08cdd14d2362b87ull, 0x27be058ae53b67c9ull, 0x898f910ebf79bf14ull, 0x25de3eb6b1834550ull, 0xd5e9f52d4a43dc4bull, 0x17a4229a36fbfe81ull, + 0xb16266ab869066e8ull, 0x036640126ec2de22ull, 0xc32cba038e36202full, 0x0b35fe0444f234d7ull, 0x907e8a28e7216ec1ull, 0x013b9d3e595a08d8ull, 0xf113d862e5d87a49ull, 0xee1f6d8873e2fd6cull, + 0xbc8eeb89f8c2468cull, 0xc893fa696ddb1348ull, 0xf775fe419ea65ea0ull, 0x59daf16ad92be258ull, 0xafc9d2775158e383ull, 0x7bca50ec84d8f4e3ull, 0xb60e23cc4c57ec8bull, 0x14ea22b4a5bf58cfull + }, + { // base nonce 4096 + 0x7493ca83442f0349ull, 0x14d80b5c689132dfull, 0xb3aae72dd710e516ull, 0xf0206e95d8082ec9ull, 0xf7edf2b8c33ed76eull, 0xaa99a579cd72c064ull, 0x13982b590565347full, 0x91ca2fab3e30f7d9ull, + 0x1a23a5a146439384ull, 0x6d7e9a1095396238ull, 0x35f22e5244fd6eeaull, 0x35b7e646dc856457ull, 0x12cb9a082f761df4ull, 0x2dab46f7cee1d833ull, 0xb6b3a052f5f46a2full, 0x1bb92a53c6282e16ull, + 0xc464cdf1a18e398cull, 0xe79e62c2cd81d3ebull, 0x7d269c15d28e6e34ull, 0x635d32a0c93e5e73ull, 0x043280c159109d85ull, 0x34e079fb8358db7eull, 0xa8eaa16f25e34d81ull, 0x862a845e3d6eaea4ull, + 0x173c275d8a9b36eeull, 0x3d4b4470f1a1e240ull, 0xabd1ee2cdc472377ull, 0x1a6bed28465dcc1eull, 0xa30a7ce538dc5919ull, 0x359785e23a8baef5ull, 0x639a2cab894c1953ull, 0xdce43d7b8b0ddfaeull + }, + { // base nonce 1000000 + 0x893e52b4424a3fc3ull, 0x631562f1f7a3a700ull, 0x50b555a482edb887ull, 0x3fbe532fcf33c9b7ull, 0x897f1231b0b11e8dull, 0xba406a33052b8d0eull, 0x1c31e6025bebaf00ull, 0x1b5acf7c6b3a2a41ull, + 0xdc69bc95894f455eull, 0x0f5b4794879e53dfull, 0x05238a17040b9f19ull, 0x0b9ed9ee63d82972ull, 0xa379e13eee8b0eb7ull, 0x0a75f0260ea17b27ull, 0x964f7c4d2cfc88b9ull, 0x8c65f784a47824ccull, + 0x55e521a2a346bd68ull, 0x566d04a22d56fa8full, 0x7595b733b70030aaull, 0xc7e6d1d5af426108ull, 0x9f75b8bc9d27aa23ull, 0x9363b556eedd83bcull, 0x305bb3c5e763bd12ull, 0xf998dc80a32b23f3ull, + 0xc9eab03b3fbb0ca4ull, 0x36c7156c238fcce9ull, 0x576b9d41066425bdull, 0x11d6c4e5cb55f9c9ull, 0x35ca8337b91469acull, 0x243d6ee5c95910b5ull, 0x0fce828d660c4426ull, 0xbf1d2b2a057e58efull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu, + 0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u, + 0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du, + 0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull; +// Hot table self-test (docs/plans/hot-table.md): hot[0..15], the last 16 words, and FNV-1a 64 over all IGNEUM_HOT_WORDS words. +static const uint32_t IGNEUM_HOT_HEAD[16] = { + 0x8068cc73u, 0x6036ebf9u, 0xb604cd25u, 0x8ffb840eu, 0xc54074a2u, 0x285c0695u, 0x77512425u, 0xc26a58a7u, + 0x72c88757u, 0xc10fca78u, 0x513825ddu, 0x30d6ccc8u, 0x9a05e7cfu, 0xb9533f50u, 0x4bac3ba0u, 0xa5c19528u +}; +static const uint32_t IGNEUM_HOT_LAST[16] = { + 0x7c6d7cebu, 0xe24fcd46u, 0xe5cce976u, 0x3ecffe59u, 0x94e98b66u, 0x3f6ef45cu, 0xa3715aa1u, 0xdbe35281u, + 0xba05d27bu, 0x00541963u, 0xe636f453u, 0xd3972366u, 0x529599b8u, 0x79ae3c8cu, 0x48436863u, 0x897a8bf3u +}; +static const uint64_t IGNEUM_HOT_FNV64 = 0x77ca4b9527104530ull; diff --git a/proto-cuda/packs-ca2-hot/hot64k8/vectors.json b/proto-cuda/packs-ca2-hot/hot64k8/vectors.json new file mode 100644 index 00000000..2ac1d60d --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot64k8/vectors.json @@ -0,0 +1,39 @@ +{ + "seed": "igneum-genesis", + "day": "2026-10-03", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0xe2e3466ed893b4f8", "0x8a8a8c07512484a5", "0x2eab76ad896647eb", "0x308c5fb089674ebd", "0x6851d37a31b0da79", "0xdf80d0cd9d2bbd54", "0x0bb440d7e0a0c83b", "0xde6fb92cd5441665", + "0xd2a4449d68eb862c", "0xf863dc5645b0081b", "0xb08cdd14d2362b87", "0x27be058ae53b67c9", "0x898f910ebf79bf14", "0x25de3eb6b1834550", "0xd5e9f52d4a43dc4b", "0x17a4229a36fbfe81", + "0xb16266ab869066e8", "0x036640126ec2de22", "0xc32cba038e36202f", "0x0b35fe0444f234d7", "0x907e8a28e7216ec1", "0x013b9d3e595a08d8", "0xf113d862e5d87a49", "0xee1f6d8873e2fd6c", + "0xbc8eeb89f8c2468c", "0xc893fa696ddb1348", "0xf775fe419ea65ea0", "0x59daf16ad92be258", "0xafc9d2775158e383", "0x7bca50ec84d8f4e3", "0xb60e23cc4c57ec8b", "0x14ea22b4a5bf58cf" + ]}, + {"base_nonce": 4096, "expected": [ + "0x7493ca83442f0349", "0x14d80b5c689132df", "0xb3aae72dd710e516", "0xf0206e95d8082ec9", "0xf7edf2b8c33ed76e", "0xaa99a579cd72c064", "0x13982b590565347f", "0x91ca2fab3e30f7d9", + "0x1a23a5a146439384", "0x6d7e9a1095396238", "0x35f22e5244fd6eea", "0x35b7e646dc856457", "0x12cb9a082f761df4", "0x2dab46f7cee1d833", "0xb6b3a052f5f46a2f", "0x1bb92a53c6282e16", + "0xc464cdf1a18e398c", "0xe79e62c2cd81d3eb", "0x7d269c15d28e6e34", "0x635d32a0c93e5e73", "0x043280c159109d85", "0x34e079fb8358db7e", "0xa8eaa16f25e34d81", "0x862a845e3d6eaea4", + "0x173c275d8a9b36ee", "0x3d4b4470f1a1e240", "0xabd1ee2cdc472377", "0x1a6bed28465dcc1e", "0xa30a7ce538dc5919", "0x359785e23a8baef5", "0x639a2cab894c1953", "0xdce43d7b8b0ddfae" + ]}, + {"base_nonce": 1000000, "expected": [ + "0x893e52b4424a3fc3", "0x631562f1f7a3a700", "0x50b555a482edb887", "0x3fbe532fcf33c9b7", "0x897f1231b0b11e8d", "0xba406a33052b8d0e", "0x1c31e6025bebaf00", "0x1b5acf7c6b3a2a41", + "0xdc69bc95894f455e", "0x0f5b4794879e53df", "0x05238a17040b9f19", "0x0b9ed9ee63d82972", "0xa379e13eee8b0eb7", "0x0a75f0260ea17b27", "0x964f7c4d2cfc88b9", "0x8c65f784a47824cc", + "0x55e521a2a346bd68", "0x566d04a22d56fa8f", "0x7595b733b70030aa", "0xc7e6d1d5af426108", "0x9f75b8bc9d27aa23", "0x9363b556eedd83bc", "0x305bb3c5e763bd12", "0xf998dc80a32b23f3", + "0xc9eab03b3fbb0ca4", "0x36c7156c238fcce9", "0x576b9d41066425bd", "0x11d6c4e5cb55f9c9", "0x35ca8337b91469ac", "0x243d6ee5c95910b5", "0x0fce828d660c4426", "0xbf1d2b2a057e58ef" + ]} + ], + "dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"], + "dataset_last_index": 268435455, + "dataset_last": "0xa33ada72", + "dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}], + "cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"], + "cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"], + "cache_fnv1a64": "0x48c4f5bf24166b2e", + "hot_head": ["0x8068cc73", "0x6036ebf9", "0xb604cd25", "0x8ffb840e", "0xc54074a2", "0x285c0695", "0x77512425", "0xc26a58a7", "0x72c88757", "0xc10fca78", "0x513825dd", "0x30d6ccc8", "0x9a05e7cf", "0xb9533f50", "0x4bac3ba0", "0xa5c19528"], + "hot_last_line": ["0x7c6d7ceb", "0xe24fcd46", "0xe5cce976", "0x3ecffe59", "0x94e98b66", "0x3f6ef45c", "0xa3715aa1", "0xdbe35281", "0xba05d27b", "0x00541963", "0xe636f453", "0xd3972366", "0x529599b8", "0x79ae3c8c", "0x48436863", "0x897a8bf3"], + "hot_fnv1a64": "0x77ca4b9527104530" +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4/kernel.cl b/proto-cuda/packs-ca2-hot/hot96k4/kernel.cl new file mode 100644 index 00000000..d9c5169c --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4/kernel.cl @@ -0,0 +1,305 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (96 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01800000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 96 MiB = 24576 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-hot/hot96k4/kernel.cu b/proto-cuda/packs-ca2-hot/hot96k4/kernel.cu new file mode 100644 index 00000000..7fdd2dd4 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4/kernel.cu @@ -0,0 +1,179 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +// Hot table (96 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01800000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (ht_segment is in memhard.h): one thread per segment. +__global__ void igneum_hot_fill(uint32_t* hot, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[__umulhi(r1, HOT_WORDS)]; // 34 hot + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_hot_fill<<>>(hot, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4/kernel_bound.cl b/proto-cuda/packs-ca2-hot/hot96k4/kernel_bound.cl new file mode 100644 index 00000000..c87d81a0 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4/kernel_bound.cl @@ -0,0 +1,399 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (96 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01800000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 96 MiB = 24576 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[mul_hi(r1, HOT_WORDS)]; // 34 hot + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4/kernel_bound.cu b/proto-cuda/packs-ca2-hot/hot96k4/kernel_bound.cu new file mode 100644 index 00000000..e96dc2ce --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4/kernel_bound.cu @@ -0,0 +1,125 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +// Hot table (96 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01800000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 16 hot + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ hot[__umulhi(r1, HOT_WORDS)]; // 34 hot + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, const uint32_t* hot, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4/memhard.h b/proto-cuda/packs-ca2-hot/hot96k4/memhard.h new file mode 100644 index 00000000..8691d33e --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4/memhard.h @@ -0,0 +1,129 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Hot table (docs/plans/hot-table.md): 96 MiB = 24576 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +IGNEUM_HD void ht_segment(uint32_t* hot, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4/memhard.metal b/proto-cuda/packs-ca2-hot/hot96k4/memhard.metal new file mode 100644 index 00000000..0acb2b7e --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4/memhard.metal @@ -0,0 +1,131 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; +} + +// Hot table (docs/plans/hot-table.md): 96 MiB = 24576 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +inline void ht_segment(device uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one thread per segment (IGNEUM_HOT_SEGMENTS threads). +kernel void igneum_hot_fill(device uint* hot [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + ht_segment(hot, gid); +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4/program.h b/proto-cuda/packs-ca2-hot/hot96k4/program.h new file mode 100644 index 00000000..f34d9155 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4/program.h @@ -0,0 +1,70 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-genesis" +#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973" +#define IGNEUM_GENERATOR 2 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x329724466da667ecull +#define IGNEUM_DAY_STRING "2026-10-03" +#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033" +#define IGNEUM_DAY0 0x3067619fu +#define IGNEUM_DAY1 0x3c269176u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=12 add=8 shfl=8 xor=6 mad=5 mul=5 mulhi=5 hot=4 sub=4 rotl=3 rotr=3 or=1" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "hot96k4" +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 12, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 384 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Hot table (hot-table experiment, 5 October 2026, docs/plans/hot-table.md): NOT the lottery hash. IGNEUM_HOT_SLOTS of the +// 16 load slots read H[mulhi(src, IGNEUM_HOT_WORDS)], H = IGNEUM_HOT_MB MiB of chained ChaCha12 lines (the cache chain) under +// KH = seed_words("igneum-hot/" || epoch seed bytes) and the tag "Igne" "umHT"; filled by igneum_hot_fill once per epoch. +#define IGNEUM_HOT_MB 96 +#define IGNEUM_HOT_WORDS 0x01800000u +#define IGNEUM_HOT_SEGMENTS 24576u +#define IGNEUM_HOT_SLOTS 4 // hot loads per program (32 per hash), replacing the dataset loads (12 of them) +#define IGNEUM_HOT_ADDED 0 +#define IGNEUM_HOT_KEY_INIT { 0x3a48bef5u, 0x6b54b1a1u, 0x9ff897c3u, 0x7d5d85e4u, 0xcd35379fu, 0x9d76bb86u, 0xbe2affb1u, 0x0f8f80b4u } +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u } +#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u } +#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u } +#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-hot/hot96k4/program.json b/proto-cuda/packs-ca2-hot/hot96k4/program.json new file mode 100644 index 00000000..c0d414cf --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4/program.json @@ -0,0 +1,129 @@ +{ + "format": "igneum-program-pack-3", + "generator": 2, + "attempt": 0, + "program_id": "0x329724466da667ec", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-genesis", + "seed_bytes": "69676e65756d2d67656e65736973", + "seed_words": ["0x67a9a7be", "0x1a155b25", "0xfddfb732", "0x4b5af2e8", "0xc55caf33", "0xa27c13b7", "0x06628a48", "0x03852469"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "load_class": "hot96k4", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [12, 0, 0], + "bytes_per_hash": 384, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "hot_table": {"mb": 96, "words": 25165824, "segments": 24576, "slots": 4, "form": "replaced: k of the class's load slots read the table", "dataset_slots": 12, "hot_loads_per_hash": 32, "key": ["0x3a48bef5", "0x6b54b1a1", "0x9ff897c3", "0x7d5d85e4", "0xcd35379f", "0x9d76bb86", "0xbe2affb1", "0x0f8f80b4"], "key_derivation": "seed_words_from_bytes('igneum-hot/' || seed_bytes), the same for every attempt of the epoch", "tag": ["0x49676e65", "0x756d4854"], "chain": "the cache chain of dataset.cache with the hot key and tag: in_j = prev ^ (sigma || key || seg || j || tag); line_j = block(in_j); prev_0 = 0", "load": "dst = dst ^ hot[mulhi(src, words)] (the high 32 bits of the 64-bit product; the index lies in [0, words) for any size)", "slots_rule": "the first k load slots in the Fisher-Yates draw order after the scratch slots (a uniform k-subset); no extra draw, so with version 2 widths and no scratch the program is the version 2 program with k loads redirected", "acceptance_stand_in": "dataset_elem(mulhi(src, words), seed_words[2], seed_words[3])", "program_id": "the read-width id with 'hot/' || mb || k appended", "spec": "docs/plans/hot-table.md"}, + "op_mix": {"load": 12, "add": 8, "shfl": 8, "xor": 6, "mad": 5, "mul": 5, "mulhi": 5, "hot": 4, "sub": 4, "rotl": 3, "rotr": 3, "or": 1}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)", + "hot": "dst = dst ^ hot[mulhi(src, hot_table.words)] (hot-table experiment)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "2026-10-03", + "day_bytes": "6461792f323032362d31302d3033", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0x3067619f", + "d1": "0x3c269176", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "mad", "dst": 2, "src": 3, "src2": 4, "imm": "0xbf7b174d", "imm2": "0x337b762e", "rot": 17, "bit": 2, "mask": 2, "width": 1}, + {"i": 1, "op": "mad", "dst": 2, "src": 1, "src2": 1, "imm": "0xdd04a5da", "imm2": "0x42da7657", "rot": 15, "bit": 30, "mask": 16, "width": 1}, + {"i": 2, "op": "mad", "dst": 2, "src": 3, "src2": 2, "imm": "0x734003fa", "imm2": "0x5bb67700", "rot": 3, "bit": 20, "mask": 1, "width": 1}, + {"i": 3, "op": "xor", "dst": 3, "src": 5, "src2": 5, "imm": "0xc55a1b1c", "imm2": "0xa19720f3", "rot": 7, "bit": 8, "mask": 1, "width": 1}, + {"i": 4, "op": "load", "dst": 7, "src": 2, "src2": 5, "imm": "0xad572dd7", "imm2": "0x9ceb3ea7", "rot": 18, "bit": 30, "mask": 2, "width": 1}, + {"i": 5, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x769a53be", "imm2": "0x80f9067e", "rot": 12, "bit": 22, "mask": 1, "width": 1}, + {"i": 6, "op": "shfl", "dst": 1, "src": 4, "src2": 0, "imm": "0xd3613d88", "imm2": "0x262fb219", "rot": 10, "bit": 30, "mask": 8, "width": 1}, + {"i": 7, "op": "shfl", "dst": 7, "src": 3, "src2": 4, "imm": "0xce38e42f", "imm2": "0xb868b818", "rot": 11, "bit": 8, "mask": 8, "width": 1}, + {"i": 8, "op": "mulhi", "dst": 1, "src": 5, "src2": 2, "imm": "0xa5eebca5", "imm2": "0x5703a72b", "rot": 13, "bit": 13, "mask": 16, "width": 1}, + {"i": 9, "op": "rotr", "dst": 6, "src": 3, "src2": 4, "imm": "0x17a5a9c7", "imm2": "0xdcfb93a1", "rot": 20, "bit": 27, "mask": 2, "width": 1}, + {"i": 10, "op": "or", "dst": 3, "src": 4, "src2": 1, "imm": "0xccb7d785", "imm2": "0xc335364c", "rot": 14, "bit": 12, "mask": 4, "width": 1}, + {"i": 11, "op": "load", "dst": 4, "src": 3, "src2": 2, "imm": "0x88cb9af3", "imm2": "0x4e7dc10d", "rot": 24, "bit": 17, "mask": 4, "width": 1}, + {"i": 12, "op": "mulhi", "dst": 0, "src": 4, "src2": 4, "imm": "0x45374321", "imm2": "0x3cd91989", "rot": 11, "bit": 4, "mask": 2, "width": 1}, + {"i": 13, "op": "add", "dst": 5, "src": 1, "src2": 2, "imm": "0xc7934706", "imm2": "0xd3177981", "rot": 16, "bit": 30, "mask": 2, "width": 1}, + {"i": 14, "op": "load", "dst": 0, "src": 4, "src2": 3, "imm": "0xfd7f56bb", "imm2": "0x65e14f52", "rot": 13, "bit": 22, "mask": 2, "width": 1}, + {"i": 15, "op": "sub", "dst": 2, "src": 4, "src2": 4, "imm": "0x35a80b49", "imm2": "0x060f2d13", "rot": 16, "bit": 20, "mask": 16, "width": 1}, + {"i": 16, "op": "hot", "dst": 2, "src": 0, "src2": 2, "imm": "0xae0a32c2", "imm2": "0x4c2a4cfe", "rot": 8, "bit": 31, "mask": 16, "width": 1}, + {"i": 17, "op": "load", "dst": 7, "src": 2, "src2": 6, "imm": "0x82a84cc3", "imm2": "0x21a38d68", "rot": 15, "bit": 21, "mask": 2, "width": 1}, + {"i": 18, "op": "shfl", "dst": 7, "src": 3, "src2": 3, "imm": "0xa3818806", "imm2": "0x8f66b5c8", "rot": 14, "bit": 6, "mask": 4, "width": 1}, + {"i": 19, "op": "mul", "dst": 5, "src": 0, "src2": 1, "imm": "0xa00de107", "imm2": "0x77bfcaa5", "rot": 3, "bit": 10, "mask": 2, "width": 1}, + {"i": 20, "op": "shfl", "dst": 3, "src": 4, "src2": 7, "imm": "0x1d2b8cab", "imm2": "0x80b4f9a2", "rot": 14, "bit": 25, "mask": 2, "width": 1}, + {"i": 21, "op": "shfl", "dst": 2, "src": 4, "src2": 5, "imm": "0x3ac915d2", "imm2": "0x5fba7bc2", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 22, "op": "mulhi", "dst": 6, "src": 2, "src2": 0, "imm": "0xdc3ec8fd", "imm2": "0x599e2fa3", "rot": 22, "bit": 3, "mask": 2, "width": 1}, + {"i": 23, "op": "load", "dst": 6, "src": 1, "src2": 5, "imm": "0x2a6b16d5", "imm2": "0xd73e396f", "rot": 28, "bit": 29, "mask": 2, "width": 1}, + {"i": 24, "op": "mul", "dst": 5, "src": 0, "src2": 7, "imm": "0x376d0223", "imm2": "0xe1c2169a", "rot": 4, "bit": 16, "mask": 16, "width": 1}, + {"i": 25, "op": "rotl", "dst": 5, "src": 7, "src2": 3, "imm": "0x78ad8c60", "imm2": "0x6f5b77d5", "rot": 19, "bit": 11, "mask": 16, "width": 1}, + {"i": 26, "op": "shfl", "dst": 7, "src": 6, "src2": 5, "imm": "0x93915b9f", "imm2": "0x1e61fb6b", "rot": 28, "bit": 23, "mask": 2, "width": 1}, + {"i": 27, "op": "xor", "dst": 0, "src": 5, "src2": 7, "imm": "0x6378fe15", "imm2": "0x66c78f42", "rot": 12, "bit": 31, "mask": 8, "width": 1}, + {"i": 28, "op": "xor", "dst": 0, "src": 4, "src2": 7, "imm": "0x20a57fda", "imm2": "0x088c848e", "rot": 16, "bit": 13, "mask": 4, "width": 1}, + {"i": 29, "op": "sub", "dst": 3, "src": 0, "src2": 2, "imm": "0x49d95fd5", "imm2": "0x1a5f946a", "rot": 6, "bit": 12, "mask": 1, "width": 1}, + {"i": 30, "op": "mul", "dst": 5, "src": 1, "src2": 7, "imm": "0x0a816217", "imm2": "0x405c4f73", "rot": 13, "bit": 27, "mask": 4, "width": 1}, + {"i": 31, "op": "load", "dst": 7, "src": 2, "src2": 2, "imm": "0x09ed045e", "imm2": "0xd69c4715", "rot": 5, "bit": 9, "mask": 2, "width": 1}, + {"i": 32, "op": "hot", "dst": 1, "src": 0, "src2": 6, "imm": "0xeb79ea49", "imm2": "0xcc587f5a", "rot": 6, "bit": 8, "mask": 16, "width": 1}, + {"i": 33, "op": "xor", "dst": 5, "src": 6, "src2": 1, "imm": "0x3027401e", "imm2": "0x5f20c27e", "rot": 18, "bit": 9, "mask": 2, "width": 1}, + {"i": 34, "op": "hot", "dst": 5, "src": 1, "src2": 3, "imm": "0x0e1cab07", "imm2": "0x09356c5b", "rot": 19, "bit": 31, "mask": 1, "width": 1}, + {"i": 35, "op": "mulhi", "dst": 0, "src": 5, "src2": 2, "imm": "0x90e31357", "imm2": "0xabd32484", "rot": 26, "bit": 5, "mask": 8, "width": 1}, + {"i": 36, "op": "shfl", "dst": 5, "src": 2, "src2": 5, "imm": "0xee9a955f", "imm2": "0x31b3faed", "rot": 8, "bit": 24, "mask": 4, "width": 1}, + {"i": 37, "op": "load", "dst": 7, "src": 0, "src2": 7, "imm": "0x3ba2f832", "imm2": "0x1160dcd3", "rot": 4, "bit": 29, "mask": 1, "width": 1}, + {"i": 38, "op": "add", "dst": 3, "src": 1, "src2": 7, "imm": "0x75ba2fad", "imm2": "0x230c005c", "rot": 4, "bit": 27, "mask": 1, "width": 1}, + {"i": 39, "op": "shfl", "dst": 1, "src": 5, "src2": 3, "imm": "0xdbf37e75", "imm2": "0xb5ac1969", "rot": 30, "bit": 13, "mask": 4, "width": 1}, + {"i": 40, "op": "xor", "dst": 2, "src": 5, "src2": 5, "imm": "0x47f136c5", "imm2": "0x06ce9153", "rot": 19, "bit": 10, "mask": 2, "width": 1}, + {"i": 41, "op": "mad", "dst": 3, "src": 6, "src2": 3, "imm": "0xce13eff8", "imm2": "0x04cc1d55", "rot": 3, "bit": 1, "mask": 4, "width": 1}, + {"i": 42, "op": "sub", "dst": 6, "src": 7, "src2": 1, "imm": "0x6a65ab71", "imm2": "0x8fbc1bcd", "rot": 4, "bit": 1, "mask": 8, "width": 1}, + {"i": 43, "op": "xor", "dst": 7, "src": 0, "src2": 7, "imm": "0xdaeb4928", "imm2": "0xc0423027", "rot": 24, "bit": 11, "mask": 8, "width": 1}, + {"i": 44, "op": "load", "dst": 1, "src": 7, "src2": 7, "imm": "0x778f01c9", "imm2": "0x28cedcea", "rot": 12, "bit": 4, "mask": 16, "width": 1}, + {"i": 45, "op": "mul", "dst": 2, "src": 3, "src2": 2, "imm": "0xf4264f1b", "imm2": "0x0f627d56", "rot": 5, "bit": 28, "mask": 8, "width": 1}, + {"i": 46, "op": "mulhi", "dst": 1, "src": 5, "src2": 1, "imm": "0xffb2147a", "imm2": "0xccde9b05", "rot": 13, "bit": 9, "mask": 2, "width": 1}, + {"i": 47, "op": "sub", "dst": 4, "src": 3, "src2": 5, "imm": "0x73b36234", "imm2": "0x3f5d5997", "rot": 7, "bit": 18, "mask": 2, "width": 1}, + {"i": 48, "op": "rotr", "dst": 2, "src": 6, "src2": 3, "imm": "0x3a4d9aa9", "imm2": "0x212bec7b", "rot": 4, "bit": 29, "mask": 16, "width": 1}, + {"i": 49, "op": "load", "dst": 3, "src": 5, "src2": 2, "imm": "0x626f11df", "imm2": "0x56cd5bfd", "rot": 7, "bit": 1, "mask": 1, "width": 1}, + {"i": 50, "op": "add", "dst": 1, "src": 5, "src2": 6, "imm": "0x81b8bc2c", "imm2": "0x1907970c", "rot": 28, "bit": 7, "mask": 4, "width": 1}, + {"i": 51, "op": "mul", "dst": 0, "src": 2, "src2": 2, "imm": "0xa8848b30", "imm2": "0xef6ac348", "rot": 9, "bit": 15, "mask": 8, "width": 1}, + {"i": 52, "op": "add", "dst": 0, "src": 2, "src2": 0, "imm": "0x4f92b968", "imm2": "0x699fd448", "rot": 22, "bit": 6, "mask": 4, "width": 1}, + {"i": 53, "op": "add", "dst": 1, "src": 0, "src2": 2, "imm": "0x2bb965af", "imm2": "0x77b1520d", "rot": 2, "bit": 12, "mask": 8, "width": 1}, + {"i": 54, "op": "rotl", "dst": 7, "src": 1, "src2": 0, "imm": "0x553e678b", "imm2": "0x3cc8eae0", "rot": 14, "bit": 20, "mask": 2, "width": 1}, + {"i": 55, "op": "add", "dst": 3, "src": 7, "src2": 2, "imm": "0x7b0fe07a", "imm2": "0xa54c55a0", "rot": 10, "bit": 1, "mask": 1, "width": 1}, + {"i": 56, "op": "load", "dst": 6, "src": 7, "src2": 4, "imm": "0x01eba9aa", "imm2": "0x2758c0f7", "rot": 14, "bit": 15, "mask": 4, "width": 1}, + {"i": 57, "op": "rotr", "dst": 1, "src": 5, "src2": 2, "imm": "0x1f5267b3", "imm2": "0x236f5a27", "rot": 2, "bit": 31, "mask": 16, "width": 1}, + {"i": 58, "op": "load", "dst": 5, "src": 4, "src2": 3, "imm": "0xa9954a9b", "imm2": "0x6a54d4e8", "rot": 11, "bit": 10, "mask": 16, "width": 1}, + {"i": 59, "op": "hot", "dst": 6, "src": 2, "src2": 4, "imm": "0x9923ff88", "imm2": "0x9357254e", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 60, "op": "mad", "dst": 3, "src": 5, "src2": 0, "imm": "0xc0cc51a6", "imm2": "0x3fd7701b", "rot": 20, "bit": 1, "mask": 4, "width": 1}, + {"i": 61, "op": "add", "dst": 5, "src": 7, "src2": 7, "imm": "0xaf9dd72d", "imm2": "0xad7493e7", "rot": 7, "bit": 31, "mask": 16, "width": 1}, + {"i": 62, "op": "add", "dst": 4, "src": 6, "src2": 2, "imm": "0x89841d87", "imm2": "0x1e07c3d9", "rot": 6, "bit": 27, "mask": 1, "width": 1}, + {"i": 63, "op": "rotl", "dst": 5, "src": 4, "src2": 2, "imm": "0xae210f8d", "imm2": "0x8e499ba4", "rot": 19, "bit": 9, "mask": 1, "width": 1} + ] +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4/program.metal b/proto-cuda/packs-ca2-hot/hot96k4/program.metal new file mode 100644 index 00000000..a5296253 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4/program.metal @@ -0,0 +1,112 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (96 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01800000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + device const uint* hot [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ dataset[r2 & MASK]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ dataset[r1 & MASK]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ hot[mulhi(r1, HOT_WORDS)]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ dataset[r0 & MASK]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ dataset[r7 & MASK]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4/program_bound.metal b/proto-cuda/packs-ca2-hot/hot96k4/program_bound.metal new file mode 100644 index 00000000..987f291d --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4/program_bound.metal @@ -0,0 +1,114 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (96 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01800000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + device const uint* hot [[buffer(4)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ dataset[r2 & MASK]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ dataset[r1 & MASK]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ hot[mulhi(r1, HOT_WORDS)]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ dataset[r0 & MASK]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ dataset[r7 & MASK]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4/vectors.h b/proto-cuda/packs-ca2-hot/hot96k4/vectors.h new file mode 100644 index 00000000..bc373868 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4/vectors.h @@ -0,0 +1,67 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0x95847de49065f429ull, 0x983294df132660c1ull, 0x8cf35b7bbf61e453ull, 0xc04d397f351fdd4eull, 0x2b10fc021f6aea84ull, 0xb5591d0d87fc5eafull, 0x018a85ab6e325a15ull, 0x4f219f18bf55f435ull, + 0xdb04938b70d8fc69ull, 0x08e0cb2cf40d64a7ull, 0xd9aa606a36518d0bull, 0x827a1cd00d7f0a23ull, 0x271e519dd470b9c8ull, 0xa6938f1bf1488ee0ull, 0x2034a82470a14babull, 0x6188094c1269c4e4ull, + 0x0ae1302647de77cdull, 0x02d79731ec72d8f4ull, 0xfa6b22726a655761ull, 0x32f8f69ac8329daeull, 0xa2ea5de7f8b1594dull, 0xc0bf04c7dca99c0cull, 0xe6a53e5a0f10c953ull, 0x5dd892fad086cf60ull, + 0xd5ba31cceb072706ull, 0x8c4aa1b7c0bb12b4ull, 0xf2b9b796ea86a26full, 0xa9cd01a077ec5875ull, 0x68b529a99fae547full, 0xbb06696372460726ull, 0x4c3af022d242a559ull, 0xfa8c698cfde1d954ull + }, + { // base nonce 4096 + 0xb6f2d3fe8b047d57ull, 0x0ea1076659d8b7acull, 0x928ea485293e9fcfull, 0x2d1ab7a9ee6bdee7ull, 0x65c166a88f62b77dull, 0xc6be153a19a235b4ull, 0x1a2431743b715fe6ull, 0xbff2dc53b8aa1cf3ull, + 0x6545051f03d6a9fbull, 0x4999c409c24702e9ull, 0x14e5cb8d974ac5d2ull, 0x5d1768adcf8aba7full, 0x77e5b90b24663744ull, 0x83f9e42857dce25full, 0x797e152bfcbee580ull, 0x296d508ed98506a6ull, + 0x72d97df5bfccb6baull, 0x6ee38ffa21d697eaull, 0x1bacd341bf7d8608ull, 0xe743262b80cb9b94ull, 0xb90860f68731652dull, 0x043af90cc6ba4da5ull, 0x645dc8ed808377e3ull, 0xb8c20121544642adull, + 0x198f6d1e47df2d4bull, 0xa4da94bbc33a3998ull, 0xca72308b15e4e7c9ull, 0xb353fd44e86e3bd9ull, 0x9f441f8c41048905ull, 0x12f9710d754b1cfdull, 0x748701e5a0fcbfd8ull, 0x9c7e52f6397721f6ull + }, + { // base nonce 1000000 + 0xa403641ce530eb70ull, 0x62e8a999234b6ab9ull, 0x0b0fd8859e9ec3e7ull, 0xfd1a15a4bfd174a5ull, 0x1fe9299599b4ed7full, 0xe8a3d2e2b28a5ebdull, 0x1863fd4b1aa24d81ull, 0x4352e226ee0590acull, + 0x00b10083a0285d9dull, 0x937dd627f2f14211ull, 0xb66a7a50fb0f4bb0ull, 0x3270883959c2d5c7ull, 0x8847671b7b801481ull, 0x3c544c0f61491058ull, 0xf7bf297e001b48a2ull, 0x3d81db650d5b98a3ull, + 0xdf3c607b790f674eull, 0x0c2d334cd2be3e50ull, 0x9875cc4b8de6ef79ull, 0x7f602f155bcad8a4ull, 0x5fbd6a1f21faf66dull, 0x7c8d54057a8e551cull, 0xb092150d9c6308cfull, 0xb6ffb83489430ecaull, + 0x9a8cbc6a7c90d711ull, 0xe062d7e57bc91d20ull, 0xb852d7ee3cf0e27eull, 0xec081a517363cc33ull, 0x03e9344a2db8860cull, 0xbda7f825624bde06ull, 0x4184375356b1e7faull, 0x2975a40effe3f796ull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu, + 0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u, + 0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du, + 0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull; +// Hot table self-test (docs/plans/hot-table.md): hot[0..15], the last 16 words, and FNV-1a 64 over all IGNEUM_HOT_WORDS words. +static const uint32_t IGNEUM_HOT_HEAD[16] = { + 0x8068cc73u, 0x6036ebf9u, 0xb604cd25u, 0x8ffb840eu, 0xc54074a2u, 0x285c0695u, 0x77512425u, 0xc26a58a7u, + 0x72c88757u, 0xc10fca78u, 0x513825ddu, 0x30d6ccc8u, 0x9a05e7cfu, 0xb9533f50u, 0x4bac3ba0u, 0xa5c19528u +}; +static const uint32_t IGNEUM_HOT_LAST[16] = { + 0xa3fd89f9u, 0xeb098388u, 0x19ee38d9u, 0x3700a229u, 0xbc0602a9u, 0x231d2a70u, 0xc23c57ceu, 0x2ff8dc6au, + 0x39683a2du, 0xbbe7d264u, 0xb8b28a43u, 0xb830041du, 0x86ae51e2u, 0xb3d22ccfu, 0x0e53335fu, 0xb56e328du +}; +static const uint64_t IGNEUM_HOT_FNV64 = 0x79bcf436c4e5bc47ull; diff --git a/proto-cuda/packs-ca2-hot/hot96k4/vectors.json b/proto-cuda/packs-ca2-hot/hot96k4/vectors.json new file mode 100644 index 00000000..4c138315 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4/vectors.json @@ -0,0 +1,39 @@ +{ + "seed": "igneum-genesis", + "day": "2026-10-03", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0x95847de49065f429", "0x983294df132660c1", "0x8cf35b7bbf61e453", "0xc04d397f351fdd4e", "0x2b10fc021f6aea84", "0xb5591d0d87fc5eaf", "0x018a85ab6e325a15", "0x4f219f18bf55f435", + "0xdb04938b70d8fc69", "0x08e0cb2cf40d64a7", "0xd9aa606a36518d0b", "0x827a1cd00d7f0a23", "0x271e519dd470b9c8", "0xa6938f1bf1488ee0", "0x2034a82470a14bab", "0x6188094c1269c4e4", + "0x0ae1302647de77cd", "0x02d79731ec72d8f4", "0xfa6b22726a655761", "0x32f8f69ac8329dae", "0xa2ea5de7f8b1594d", "0xc0bf04c7dca99c0c", "0xe6a53e5a0f10c953", "0x5dd892fad086cf60", + "0xd5ba31cceb072706", "0x8c4aa1b7c0bb12b4", "0xf2b9b796ea86a26f", "0xa9cd01a077ec5875", "0x68b529a99fae547f", "0xbb06696372460726", "0x4c3af022d242a559", "0xfa8c698cfde1d954" + ]}, + {"base_nonce": 4096, "expected": [ + "0xb6f2d3fe8b047d57", "0x0ea1076659d8b7ac", "0x928ea485293e9fcf", "0x2d1ab7a9ee6bdee7", "0x65c166a88f62b77d", "0xc6be153a19a235b4", "0x1a2431743b715fe6", "0xbff2dc53b8aa1cf3", + "0x6545051f03d6a9fb", "0x4999c409c24702e9", "0x14e5cb8d974ac5d2", "0x5d1768adcf8aba7f", "0x77e5b90b24663744", "0x83f9e42857dce25f", "0x797e152bfcbee580", "0x296d508ed98506a6", + "0x72d97df5bfccb6ba", "0x6ee38ffa21d697ea", "0x1bacd341bf7d8608", "0xe743262b80cb9b94", "0xb90860f68731652d", "0x043af90cc6ba4da5", "0x645dc8ed808377e3", "0xb8c20121544642ad", + "0x198f6d1e47df2d4b", "0xa4da94bbc33a3998", "0xca72308b15e4e7c9", "0xb353fd44e86e3bd9", "0x9f441f8c41048905", "0x12f9710d754b1cfd", "0x748701e5a0fcbfd8", "0x9c7e52f6397721f6" + ]}, + {"base_nonce": 1000000, "expected": [ + "0xa403641ce530eb70", "0x62e8a999234b6ab9", "0x0b0fd8859e9ec3e7", "0xfd1a15a4bfd174a5", "0x1fe9299599b4ed7f", "0xe8a3d2e2b28a5ebd", "0x1863fd4b1aa24d81", "0x4352e226ee0590ac", + "0x00b10083a0285d9d", "0x937dd627f2f14211", "0xb66a7a50fb0f4bb0", "0x3270883959c2d5c7", "0x8847671b7b801481", "0x3c544c0f61491058", "0xf7bf297e001b48a2", "0x3d81db650d5b98a3", + "0xdf3c607b790f674e", "0x0c2d334cd2be3e50", "0x9875cc4b8de6ef79", "0x7f602f155bcad8a4", "0x5fbd6a1f21faf66d", "0x7c8d54057a8e551c", "0xb092150d9c6308cf", "0xb6ffb83489430eca", + "0x9a8cbc6a7c90d711", "0xe062d7e57bc91d20", "0xb852d7ee3cf0e27e", "0xec081a517363cc33", "0x03e9344a2db8860c", "0xbda7f825624bde06", "0x4184375356b1e7fa", "0x2975a40effe3f796" + ]} + ], + "dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"], + "dataset_last_index": 268435455, + "dataset_last": "0xa33ada72", + "dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}], + "cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"], + "cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"], + "cache_fnv1a64": "0x48c4f5bf24166b2e", + "hot_head": ["0x8068cc73", "0x6036ebf9", "0xb604cd25", "0x8ffb840e", "0xc54074a2", "0x285c0695", "0x77512425", "0xc26a58a7", "0x72c88757", "0xc10fca78", "0x513825dd", "0x30d6ccc8", "0x9a05e7cf", "0xb9533f50", "0x4bac3ba0", "0xa5c19528"], + "hot_last_line": ["0xa3fd89f9", "0xeb098388", "0x19ee38d9", "0x3700a229", "0xbc0602a9", "0x231d2a70", "0xc23c57ce", "0x2ff8dc6a", "0x39683a2d", "0xbbe7d264", "0xb8b28a43", "0xb830041d", "0x86ae51e2", "0xb3d22ccf", "0x0e53335f", "0xb56e328d"], + "hot_fnv1a64": "0x79bcf436c4e5bc47" +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4a/kernel.cl b/proto-cuda/packs-ca2-hot/hot96k4a/kernel.cl new file mode 100644 index 00000000..2d3e2ace --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4a/kernel.cl @@ -0,0 +1,305 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (96 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01800000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 96 MiB = 24576 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 1u); r7 = r7 ^ t_; } // 1 shfl + r0 = r0 | r2; // 2 or + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 16u); r3 = r3 ^ t_; } // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[mul_hi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = mul_hi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 8u); r3 = r3 ^ t_; } // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[mul_hi(r6, HOT_WORDS)]; // 34 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 2u); r4 = r4 ^ t_; } // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r6 = r6 ^ t_; } // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r0 = r0 ^ t_; } // 54 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 16u); r0 = r0 ^ t_; } // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r3 = r3 ^ t_; } // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-hot/hot96k4a/kernel.cu b/proto-cuda/packs-ca2-hot/hot96k4a/kernel.cu new file mode 100644 index 00000000..a3badefc --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4a/kernel.cu @@ -0,0 +1,179 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +// Hot table (96 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01800000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (ht_segment is in memhard.h): one thread per segment. +__global__ void igneum_hot_fill(uint32_t* hot, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r6 = r6 | r4; // 0 or + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r4, 1); // 1 shfl + r0 = r0 | r2; // 2 or + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r0, 16); // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[__umulhi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = __umulhi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 8); // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[__umulhi(r6, HOT_WORDS)]; // 34 hot + r4 = r4 ^ __shfl_xor_sync(0xffffffffu, r0, 2); // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 54 shfl + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 16); // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_hot_fill<<>>(hot, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4a/kernel_bound.cl b/proto-cuda/packs-ca2-hot/hot96k4a/kernel_bound.cl new file mode 100644 index 00000000..be2b83a5 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4a/kernel_bound.cl @@ -0,0 +1,399 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +// Hot table (96 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01800000u +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} +// Hot table (docs/plans/hot-table.md): 96 MiB = 24576 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +static inline void ht_segment(__global uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one work-item per segment. +__kernel void igneum_hot_fill(__global uint* hot, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) ht_segment(hot, seg); +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 1u); r7 = r7 ^ t_; } // 1 shfl + r0 = r0 | r2; // 2 or + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 16u); r3 = r3 ^ t_; } // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[mul_hi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = mul_hi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 8u); r3 = r3 ^ t_; } // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[mul_hi(r6, HOT_WORDS)]; // 34 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 2u); r4 = r4 ^ t_; } // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r6 = r6 ^ t_; } // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r0 = r0 ^ t_; } // 54 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 16u); r0 = r0 ^ t_; } // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r3 = r3 ^ t_; } // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw, __global const uint* hot) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 1u); r7 = r7 ^ t_; } // 1 shfl + r0 = r0 | r2; // 2 or + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 16u); r3 = r3 ^ t_; } // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[mul_hi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = mul_hi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 8u); r3 = r3 ^ t_; } // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[mul_hi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[mul_hi(r6, HOT_WORDS)]; // 34 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 2u); r4 = r4 ^ t_; } // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r6 = r6 ^ t_; } // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r0 = r0 ^ t_; } // 54 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 16u); r0 = r0 ^ t_; } // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[mul_hi(r2, HOT_WORDS)]; // 59 hot + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r3 = r3 ^ t_; } // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4a/kernel_bound.cu b/proto-cuda/packs-ca2-hot/hot96k4a/kernel_bound.cu new file mode 100644 index 00000000..dc4b0ed5 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4a/kernel_bound.cu @@ -0,0 +1,125 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +// Hot table (96 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01800000u +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw, const uint32_t* hot) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r6 = r6 | r4; // 0 or + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r4, 1); // 1 shfl + r0 = r0 | r2; // 2 or + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r0, 16); // 3 shfl + r7 = r7 ^ ds[r3 & mask]; // 4 load + r6 = r6 ^ ds[r0 & mask]; // 5 load + r1 = r1 * r4; // 6 mul + r0 = r0 - r1; // 7 sub + r3 = r3 ^ ds[r7 & mask]; // 8 load + r1 = r0 * r3 + r1; // 9 mad + r4 = r4 * r0; // 10 mul + r5 = r5 ^ ds[r4 & mask]; // 11 load + r1 = r1 ^ r7; // 12 xor + r1 = r1 ^ ds[r6 & mask]; // 13 load + r2 = r2 ^ ds[r1 & mask]; // 14 load + r3 = r3 * r1; // 15 mul + r6 = r6 ^ hot[__umulhi(r5, HOT_WORDS)]; // 16 hot + r0 = r0 ^ ds[r2 & mask]; // 17 load + r0 = r0 + r7 + ((((sel >> 26u) & 1u) != 0u) ? 0x535b545fu : 0xc035a4e6u); // 18 add + r5 = r5 - r0; // 19 sub + r2 = r2 * r4; // 20 mul + r2 = r2 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x925d5e63u : 0x1ecdd2cfu); // 21 add + r3 = r3 ^ r7; // 22 xor + r7 = r7 ^ ds[r2 & mask]; // 23 load + r2 = __umulhi(r2, r5); // 24 mulhi + r5 = rotr_var(r5, r2); // 25 rotr + r3 = r2 * r7 + r3; // 26 mad + r2 = r2 - r0; // 27 sub + r6 = r1 * r5 + r6; // 28 mad + r2 = r2 + r3 + ((((sel >> 16u) & 1u) != 0u) ? 0x46b1b505u : 0xbdc6da76u); // 29 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 8); // 30 shfl + r5 = r5 ^ ds[r7 & mask]; // 31 load + r2 = r2 ^ hot[__umulhi(r0, HOT_WORDS)]; // 32 hot + r6 = r6 | r3; // 33 or + r3 = r3 ^ hot[__umulhi(r6, HOT_WORDS)]; // 34 hot + r4 = r4 ^ __shfl_xor_sync(0xffffffffu, r0, 2); // 35 shfl + r5 = r5 ^ r2; // 36 xor + r3 = r3 ^ ds[r4 & mask]; // 37 load + r4 = r4 ^ ds[r3 & mask]; // 38 load + r1 = rotr_var(r1, r4); // 39 rotr + r3 = r3 + r6 + ((((sel >> 28u) & 1u) != 0u) ? 0x6328cb2cu : 0xbc3ff65fu); // 40 add + r5 = r5 ^ r1; // 41 xor + r5 = r5 | r0; // 42 or + r7 = r7 ^ r3; // 43 xor + r2 = r2 ^ ds[r5 & mask]; // 44 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 45 shfl + r5 = r5 ^ r3; // 46 xor + r7 = r7 + r6 + ((((sel >> 22u) & 1u) != 0u) ? 0x9342df0du : 0x5af3bd5bu); // 47 add + r3 = r3 + r4 + ((((sel >> 23u) & 1u) != 0u) ? 0x485614dbu : 0x9ff95776u); // 48 add + r5 = r5 ^ ds[r3 & mask]; // 49 load + r4 = rotr_var(r4, r5); // 50 rotr + r0 = r1 * r7 + r0; // 51 mad + r0 = r0 * r3; // 52 mul + r5 = rotr_var(r5, r4); // 53 rotr + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 54 shfl + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 16); // 55 shfl + r7 = r7 ^ ds[r4 & mask]; // 56 load + r7 = rotr_var(r7, r3); // 57 rotr + r0 = r0 ^ ds[r6 & mask]; // 58 load + r6 = r6 ^ hot[__umulhi(r2, HOT_WORDS)]; // 59 hot + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 60 shfl + r7 = r7 - r5; // 61 sub + r1 = rotl_imm(r1, 4u); // 62 rotl + r4 = r4 ^ ds[r5 & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, const uint32_t* hot, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw, hot); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4a/memhard.h b/proto-cuda/packs-ca2-hot/hot96k4a/memhard.h new file mode 100644 index 00000000..8691d33e --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4a/memhard.h @@ -0,0 +1,129 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint32_t r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Hot table (docs/plans/hot-table.md): 96 MiB = 24576 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +IGNEUM_HD void ht_segment(uint32_t* hot, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4a/memhard.metal b/proto-cuda/packs-ca2-hot/hot96k4a/memhard.metal new file mode 100644 index 00000000..0acb2b7e --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4a/memhard.metal @@ -0,0 +1,131 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + mh_mixer(s, 0x9E3779B9u * (r + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + mh_mixer(s, 0x9E3779B9u * 9u); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; +} + +// Hot table (docs/plans/hot-table.md): 96 MiB = 24576 segments of 64 chained ChaCha12 lines under the hot key KH = seed_words("igneum-hot/" || epoch seed bytes), tag "Igne" "umHT". The cache chain with another key and tag. +inline void ht_segment(device uint* hot, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3a48bef5u ^ prev[4]; + x[5] = 0x6b54b1a1u ^ prev[5]; + x[6] = 0x9ff897c3u ^ prev[6]; + x[7] = 0x7d5d85e4u ^ prev[7]; + x[8] = 0xcd35379fu ^ prev[8]; + x[9] = 0x9d76bb86u ^ prev[9]; + x[10] = 0xbe2affb1u ^ prev[10]; + x[11] = 0x0f8f80b4u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4854u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = hot + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} +// Hot table: one thread per segment (IGNEUM_HOT_SEGMENTS threads). +kernel void igneum_hot_fill(device uint* hot [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + ht_segment(hot, gid); +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4a/program.h b/proto-cuda/packs-ca2-hot/hot96k4a/program.h new file mode 100644 index 00000000..3b39a023 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4a/program.h @@ -0,0 +1,70 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-genesis" +#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973" +#define IGNEUM_GENERATOR 2 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x2ff8e52c952a62c2ull +#define IGNEUM_DAY_STRING "2026-10-03" +#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033" +#define IGNEUM_DAY0 0x3067619fu +#define IGNEUM_DAY1 0x3c269176u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 160 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 shfl=8 add=6 xor=6 mul=5 rotr=5 hot=4 mad=4 or=4 sub=4 mulhi=1 rotl=1" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "hot96k4a" +#define IGNEUM_LOAD_SLOTS 20 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// Hot table (hot-table experiment, 5 October 2026, docs/plans/hot-table.md): NOT the lottery hash. IGNEUM_HOT_SLOTS of the +// 16 load slots read H[mulhi(src, IGNEUM_HOT_WORDS)], H = IGNEUM_HOT_MB MiB of chained ChaCha12 lines (the cache chain) under +// KH = seed_words("igneum-hot/" || epoch seed bytes) and the tag "Igne" "umHT"; filled by igneum_hot_fill once per epoch. +#define IGNEUM_HOT_MB 96 +#define IGNEUM_HOT_WORDS 0x01800000u +#define IGNEUM_HOT_SEGMENTS 24576u +#define IGNEUM_HOT_SLOTS 4 // hot loads per program (32 per hash), added beside the dataset loads (16 of them) +#define IGNEUM_HOT_ADDED 1 +#define IGNEUM_HOT_KEY_INIT { 0x3a48bef5u, 0x6b54b1a1u, 0x9ff897c3u, 0x7d5d85e4u, 0xcd35379fu, 0x9d76bb86u, 0xbe2affb1u, 0x0f8f80b4u } +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u } +#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u } +#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u } +#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hot_fill(uint32_t* hot, uint32_t nSegments); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, const uint32_t* hot, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-hot/hot96k4a/program.json b/proto-cuda/packs-ca2-hot/hot96k4a/program.json new file mode 100644 index 00000000..a1140783 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4a/program.json @@ -0,0 +1,129 @@ +{ + "format": "igneum-program-pack-3", + "generator": 2, + "attempt": 0, + "program_id": "0x2ff8e52c952a62c2", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-genesis", + "seed_bytes": "69676e65756d2d67656e65736973", + "seed_words": ["0x67a9a7be", "0x1a155b25", "0xfddfb732", "0x4b5af2e8", "0xc55caf33", "0xa27c13b7", "0x06628a48", "0x03852469"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 160, + "load_class": "hot96k4a", + "load_slots": 20, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "hot_table": {"mb": 96, "words": 25165824, "segments": 24576, "slots": 4, "form": "added: k load slots added beside the class's, the dataset loads unchanged", "dataset_slots": 16, "hot_loads_per_hash": 32, "key": ["0x3a48bef5", "0x6b54b1a1", "0x9ff897c3", "0x7d5d85e4", "0xcd35379f", "0x9d76bb86", "0xbe2affb1", "0x0f8f80b4"], "key_derivation": "seed_words_from_bytes('igneum-hot/' || seed_bytes), the same for every attempt of the epoch", "tag": ["0x49676e65", "0x756d4854"], "chain": "the cache chain of dataset.cache with the hot key and tag: in_j = prev ^ (sigma || key || seg || j || tag); line_j = block(in_j); prev_0 = 0", "load": "dst = dst ^ hot[mulhi(src, words)] (the high 32 bits of the 64-bit product; the index lies in [0, words) for any size)", "slots_rule": "the first k load slots in the Fisher-Yates draw order after the scratch slots (a uniform k-subset); no extra draw, so with version 2 widths and no scratch the program is the version 2 program with k loads redirected", "acceptance_stand_in": "dataset_elem(mulhi(src, words), seed_words[2], seed_words[3])", "program_id": "the read-width id with 'hot/' || mb || k appended", "spec": "docs/plans/hot-table.md"}, + "op_mix": {"load": 16, "shfl": 8, "add": 6, "xor": 6, "mul": 5, "rotr": 5, "hot": 4, "mad": 4, "or": 4, "sub": 4, "mulhi": 1, "rotl": 1}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)", + "hot": "dst = dst ^ hot[mulhi(src, hot_table.words)] (hot-table experiment)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "2026-10-03", + "day_bytes": "6461792f323032362d31302d3033", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0x3067619f", + "d1": "0x3c269176", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "or", "dst": 6, "src": 4, "src2": 2, "imm": "0x535c5dd3", "imm2": "0xf8694615", "rot": 3, "bit": 1, "mask": 16, "width": 1}, + {"i": 1, "op": "shfl", "dst": 7, "src": 4, "src2": 6, "imm": "0x87bc9ee4", "imm2": "0xfdb0c856", "rot": 9, "bit": 25, "mask": 1, "width": 1}, + {"i": 2, "op": "or", "dst": 0, "src": 2, "src2": 4, "imm": "0xaf91f0c8", "imm2": "0xe2d5d1fa", "rot": 1, "bit": 24, "mask": 2, "width": 1}, + {"i": 3, "op": "shfl", "dst": 3, "src": 0, "src2": 0, "imm": "0x3044ba32", "imm2": "0x7b1a7ffe", "rot": 14, "bit": 10, "mask": 16, "width": 1}, + {"i": 4, "op": "load", "dst": 7, "src": 3, "src2": 6, "imm": "0x5d1ca2a2", "imm2": "0xe2481807", "rot": 24, "bit": 3, "mask": 1, "width": 1}, + {"i": 5, "op": "load", "dst": 6, "src": 0, "src2": 6, "imm": "0xaba3dbaa", "imm2": "0x987c017a", "rot": 16, "bit": 30, "mask": 1, "width": 1}, + {"i": 6, "op": "mul", "dst": 1, "src": 4, "src2": 6, "imm": "0x12a93d05", "imm2": "0x10761047", "rot": 17, "bit": 17, "mask": 4, "width": 1}, + {"i": 7, "op": "sub", "dst": 0, "src": 1, "src2": 0, "imm": "0x8884e389", "imm2": "0x31009b67", "rot": 5, "bit": 30, "mask": 1, "width": 1}, + {"i": 8, "op": "load", "dst": 3, "src": 7, "src2": 5, "imm": "0xfa6358b3", "imm2": "0xfea9038f", "rot": 17, "bit": 18, "mask": 2, "width": 1}, + {"i": 9, "op": "mad", "dst": 1, "src": 0, "src2": 3, "imm": "0xb154ae4f", "imm2": "0xe85f13f6", "rot": 6, "bit": 18, "mask": 16, "width": 1}, + {"i": 10, "op": "mul", "dst": 4, "src": 0, "src2": 4, "imm": "0x9edffbc3", "imm2": "0x643812d4", "rot": 24, "bit": 25, "mask": 4, "width": 1}, + {"i": 11, "op": "load", "dst": 5, "src": 4, "src2": 1, "imm": "0x163dbb1b", "imm2": "0x20c1f743", "rot": 27, "bit": 25, "mask": 16, "width": 1}, + {"i": 12, "op": "xor", "dst": 1, "src": 7, "src2": 4, "imm": "0x9cadb4f8", "imm2": "0xd9da785b", "rot": 8, "bit": 26, "mask": 4, "width": 1}, + {"i": 13, "op": "load", "dst": 1, "src": 6, "src2": 6, "imm": "0x1bb1b429", "imm2": "0x33d1e391", "rot": 20, "bit": 19, "mask": 16, "width": 1}, + {"i": 14, "op": "load", "dst": 2, "src": 1, "src2": 6, "imm": "0xa672cdd3", "imm2": "0x59a4829c", "rot": 22, "bit": 13, "mask": 16, "width": 1}, + {"i": 15, "op": "mul", "dst": 3, "src": 1, "src2": 4, "imm": "0xe3c4bf4d", "imm2": "0x028b4d37", "rot": 11, "bit": 28, "mask": 8, "width": 1}, + {"i": 16, "op": "hot", "dst": 6, "src": 5, "src2": 7, "imm": "0x71fbe6f2", "imm2": "0x61b6261e", "rot": 15, "bit": 9, "mask": 1, "width": 1}, + {"i": 17, "op": "load", "dst": 0, "src": 2, "src2": 5, "imm": "0x8e516fd3", "imm2": "0x2aa480c2", "rot": 19, "bit": 1, "mask": 8, "width": 1}, + {"i": 18, "op": "add", "dst": 0, "src": 7, "src2": 6, "imm": "0xc035a4e6", "imm2": "0x535b545f", "rot": 18, "bit": 26, "mask": 8, "width": 1}, + {"i": 19, "op": "sub", "dst": 5, "src": 0, "src2": 2, "imm": "0xde38f954", "imm2": "0xde1507bc", "rot": 31, "bit": 21, "mask": 4, "width": 1}, + {"i": 20, "op": "mul", "dst": 2, "src": 4, "src2": 1, "imm": "0x3f64b7c9", "imm2": "0x1f07754a", "rot": 23, "bit": 28, "mask": 4, "width": 1}, + {"i": 21, "op": "add", "dst": 2, "src": 1, "src2": 1, "imm": "0x1ecdd2cf", "imm2": "0x925d5e63", "rot": 6, "bit": 6, "mask": 8, "width": 1}, + {"i": 22, "op": "xor", "dst": 3, "src": 7, "src2": 3, "imm": "0x63c7f533", "imm2": "0xd312164d", "rot": 4, "bit": 14, "mask": 2, "width": 1}, + {"i": 23, "op": "load", "dst": 7, "src": 2, "src2": 5, "imm": "0x6d3ddc5a", "imm2": "0x433d8c2a", "rot": 28, "bit": 7, "mask": 2, "width": 1}, + {"i": 24, "op": "mulhi", "dst": 2, "src": 5, "src2": 0, "imm": "0xc7e9887a", "imm2": "0x19ec898f", "rot": 14, "bit": 9, "mask": 1, "width": 1}, + {"i": 25, "op": "rotr", "dst": 5, "src": 2, "src2": 3, "imm": "0x97acf9f2", "imm2": "0xc7fcfc8f", "rot": 8, "bit": 25, "mask": 1, "width": 1}, + {"i": 26, "op": "mad", "dst": 3, "src": 2, "src2": 7, "imm": "0x400383b6", "imm2": "0xe9bab735", "rot": 16, "bit": 18, "mask": 1, "width": 1}, + {"i": 27, "op": "sub", "dst": 2, "src": 0, "src2": 7, "imm": "0x8ea6cd8d", "imm2": "0x55b67f9f", "rot": 19, "bit": 8, "mask": 8, "width": 1}, + {"i": 28, "op": "mad", "dst": 6, "src": 1, "src2": 5, "imm": "0x17723e5a", "imm2": "0x00779664", "rot": 19, "bit": 2, "mask": 4, "width": 1}, + {"i": 29, "op": "add", "dst": 2, "src": 3, "src2": 4, "imm": "0xbdc6da76", "imm2": "0x46b1b505", "rot": 28, "bit": 16, "mask": 16, "width": 1}, + {"i": 30, "op": "shfl", "dst": 3, "src": 2, "src2": 3, "imm": "0xe586702c", "imm2": "0xd83a1462", "rot": 24, "bit": 6, "mask": 8, "width": 1}, + {"i": 31, "op": "load", "dst": 5, "src": 7, "src2": 1, "imm": "0x1cddad42", "imm2": "0xb0c6b0d0", "rot": 25, "bit": 25, "mask": 8, "width": 1}, + {"i": 32, "op": "hot", "dst": 2, "src": 0, "src2": 0, "imm": "0x2abcfdc6", "imm2": "0xfa4cc809", "rot": 22, "bit": 24, "mask": 2, "width": 1}, + {"i": 33, "op": "or", "dst": 6, "src": 3, "src2": 1, "imm": "0x61c9a38d", "imm2": "0x86597500", "rot": 15, "bit": 8, "mask": 2, "width": 1}, + {"i": 34, "op": "hot", "dst": 3, "src": 6, "src2": 7, "imm": "0xc37723fa", "imm2": "0xf3b024da", "rot": 16, "bit": 27, "mask": 16, "width": 1}, + {"i": 35, "op": "shfl", "dst": 4, "src": 0, "src2": 5, "imm": "0x84cad367", "imm2": "0xcc7972c4", "rot": 21, "bit": 17, "mask": 2, "width": 1}, + {"i": 36, "op": "xor", "dst": 5, "src": 2, "src2": 0, "imm": "0xc24a7d70", "imm2": "0x6591d24c", "rot": 21, "bit": 24, "mask": 1, "width": 1}, + {"i": 37, "op": "load", "dst": 3, "src": 4, "src2": 5, "imm": "0xd404cfe8", "imm2": "0x144ca538", "rot": 10, "bit": 11, "mask": 2, "width": 1}, + {"i": 38, "op": "load", "dst": 4, "src": 3, "src2": 3, "imm": "0x04b0080c", "imm2": "0xb938c290", "rot": 10, "bit": 1, "mask": 8, "width": 1}, + {"i": 39, "op": "rotr", "dst": 1, "src": 4, "src2": 5, "imm": "0x9fe93344", "imm2": "0xff7296e4", "rot": 29, "bit": 14, "mask": 1, "width": 1}, + {"i": 40, "op": "add", "dst": 3, "src": 6, "src2": 2, "imm": "0xbc3ff65f", "imm2": "0x6328cb2c", "rot": 4, "bit": 28, "mask": 16, "width": 1}, + {"i": 41, "op": "xor", "dst": 5, "src": 1, "src2": 1, "imm": "0xdcf4a02e", "imm2": "0x5ee3a976", "rot": 17, "bit": 0, "mask": 8, "width": 1}, + {"i": 42, "op": "or", "dst": 5, "src": 0, "src2": 1, "imm": "0x44846c7a", "imm2": "0x67338877", "rot": 27, "bit": 18, "mask": 1, "width": 1}, + {"i": 43, "op": "xor", "dst": 7, "src": 3, "src2": 3, "imm": "0x1b053acf", "imm2": "0x32e2d23d", "rot": 31, "bit": 23, "mask": 8, "width": 1}, + {"i": 44, "op": "load", "dst": 2, "src": 5, "src2": 4, "imm": "0xf8662282", "imm2": "0x10bb9e30", "rot": 8, "bit": 6, "mask": 2, "width": 1}, + {"i": 45, "op": "shfl", "dst": 6, "src": 4, "src2": 4, "imm": "0x87d9ef84", "imm2": "0x49087d74", "rot": 1, "bit": 2, "mask": 8, "width": 1}, + {"i": 46, "op": "xor", "dst": 5, "src": 3, "src2": 1, "imm": "0x74a59b7d", "imm2": "0xc4766ff1", "rot": 30, "bit": 3, "mask": 16, "width": 1}, + {"i": 47, "op": "add", "dst": 7, "src": 6, "src2": 2, "imm": "0x5af3bd5b", "imm2": "0x9342df0d", "rot": 26, "bit": 22, "mask": 2, "width": 1}, + {"i": 48, "op": "add", "dst": 3, "src": 4, "src2": 5, "imm": "0x9ff95776", "imm2": "0x485614db", "rot": 27, "bit": 23, "mask": 1, "width": 1}, + {"i": 49, "op": "load", "dst": 5, "src": 3, "src2": 1, "imm": "0xd022a812", "imm2": "0xfbe147d6", "rot": 15, "bit": 29, "mask": 4, "width": 1}, + {"i": 50, "op": "rotr", "dst": 4, "src": 5, "src2": 7, "imm": "0x7e099325", "imm2": "0x40d55d10", "rot": 29, "bit": 15, "mask": 16, "width": 1}, + {"i": 51, "op": "mad", "dst": 0, "src": 1, "src2": 7, "imm": "0xd8ab8843", "imm2": "0x7f842b90", "rot": 16, "bit": 9, "mask": 1, "width": 1}, + {"i": 52, "op": "mul", "dst": 0, "src": 3, "src2": 6, "imm": "0x4c30250b", "imm2": "0x9ee1681f", "rot": 20, "bit": 3, "mask": 8, "width": 1}, + {"i": 53, "op": "rotr", "dst": 5, "src": 4, "src2": 4, "imm": "0xc1c15026", "imm2": "0x588915e7", "rot": 1, "bit": 29, "mask": 16, "width": 1}, + {"i": 54, "op": "shfl", "dst": 0, "src": 6, "src2": 4, "imm": "0x636a9dc4", "imm2": "0xac023d9b", "rot": 22, "bit": 29, "mask": 1, "width": 1}, + {"i": 55, "op": "shfl", "dst": 0, "src": 7, "src2": 1, "imm": "0x5c933bd0", "imm2": "0x2baec8c9", "rot": 25, "bit": 27, "mask": 16, "width": 1}, + {"i": 56, "op": "load", "dst": 7, "src": 4, "src2": 7, "imm": "0xf62515d5", "imm2": "0x4a164f9f", "rot": 28, "bit": 30, "mask": 2, "width": 1}, + {"i": 57, "op": "rotr", "dst": 7, "src": 3, "src2": 7, "imm": "0x15ef64da", "imm2": "0x7149e3c9", "rot": 28, "bit": 30, "mask": 2, "width": 1}, + {"i": 58, "op": "load", "dst": 0, "src": 6, "src2": 2, "imm": "0xcde7100c", "imm2": "0x040fc0cf", "rot": 8, "bit": 28, "mask": 2, "width": 1}, + {"i": 59, "op": "hot", "dst": 6, "src": 2, "src2": 1, "imm": "0xcf8b48be", "imm2": "0x14cb1d3f", "rot": 16, "bit": 19, "mask": 1, "width": 1}, + {"i": 60, "op": "shfl", "dst": 3, "src": 2, "src2": 1, "imm": "0xae1529ee", "imm2": "0x732bc114", "rot": 4, "bit": 23, "mask": 1, "width": 1}, + {"i": 61, "op": "sub", "dst": 7, "src": 5, "src2": 7, "imm": "0x0a8f8168", "imm2": "0xf1c9b0ed", "rot": 29, "bit": 2, "mask": 8, "width": 1}, + {"i": 62, "op": "rotl", "dst": 1, "src": 2, "src2": 3, "imm": "0x9b7914dd", "imm2": "0xd14b33a3", "rot": 4, "bit": 16, "mask": 16, "width": 1}, + {"i": 63, "op": "load", "dst": 4, "src": 5, "src2": 1, "imm": "0xbbaa8e24", "imm2": "0xd15ec6a6", "rot": 24, "bit": 2, "mask": 2, "width": 1} + ] +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4a/program.metal b/proto-cuda/packs-ca2-hot/hot96k4a/program.metal new file mode 100644 index 00000000..ea4b66cb --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4a/program.metal @@ -0,0 +1,112 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (96 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01800000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + device const uint* hot [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 + r7 = r7 ^ simd_shuffle_xor(r4, (ushort)1); // 1 + r0 = r0 | r2; // 2 + r3 = r3 ^ simd_shuffle_xor(r0, (ushort)16); // 3 + r7 = r7 ^ dataset[r3 & MASK]; // 4 + r6 = r6 ^ dataset[r0 & MASK]; // 5 + r1 = r1 * r4; // 6 + r0 = r0 - r1; // 7 + r3 = r3 ^ dataset[r7 & MASK]; // 8 + r1 = r0 * r3 + r1; // 9 + r4 = r4 * r0; // 10 + r5 = r5 ^ dataset[r4 & MASK]; // 11 + r1 = r1 ^ r7; // 12 + r1 = r1 ^ dataset[r6 & MASK]; // 13 + r2 = r2 ^ dataset[r1 & MASK]; // 14 + r3 = r3 * r1; // 15 + r6 = r6 ^ hot[mulhi(r5, HOT_WORDS)]; // 16 + r0 = r0 ^ dataset[r2 & MASK]; // 17 + r0 = r0 + r7 + select(0xc035a4e6u, 0x535b545fu, ((sel >> 26u) & 1u) != 0u); // 18 + r5 = r5 - r0; // 19 + r2 = r2 * r4; // 20 + r2 = r2 + r1 + select(0x1ecdd2cfu, 0x925d5e63u, ((sel >> 6u) & 1u) != 0u); // 21 + r3 = r3 ^ r7; // 22 + r7 = r7 ^ dataset[r2 & MASK]; // 23 + r2 = mulhi(r2, r5); // 24 + r5 = rotr_var(r5, r2); // 25 + r3 = r2 * r7 + r3; // 26 + r2 = r2 - r0; // 27 + r6 = r1 * r5 + r6; // 28 + r2 = r2 + r3 + select(0xbdc6da76u, 0x46b1b505u, ((sel >> 16u) & 1u) != 0u); // 29 + r3 = r3 ^ simd_shuffle_xor(r2, (ushort)8); // 30 + r5 = r5 ^ dataset[r7 & MASK]; // 31 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r6 = r6 | r3; // 33 + r3 = r3 ^ hot[mulhi(r6, HOT_WORDS)]; // 34 + r4 = r4 ^ simd_shuffle_xor(r0, (ushort)2); // 35 + r5 = r5 ^ r2; // 36 + r3 = r3 ^ dataset[r4 & MASK]; // 37 + r4 = r4 ^ dataset[r3 & MASK]; // 38 + r1 = rotr_var(r1, r4); // 39 + r3 = r3 + r6 + select(0xbc3ff65fu, 0x6328cb2cu, ((sel >> 28u) & 1u) != 0u); // 40 + r5 = r5 ^ r1; // 41 + r5 = r5 | r0; // 42 + r7 = r7 ^ r3; // 43 + r2 = r2 ^ dataset[r5 & MASK]; // 44 + r6 = r6 ^ simd_shuffle_xor(r4, (ushort)8); // 45 + r5 = r5 ^ r3; // 46 + r7 = r7 + r6 + select(0x5af3bd5bu, 0x9342df0du, ((sel >> 22u) & 1u) != 0u); // 47 + r3 = r3 + r4 + select(0x9ff95776u, 0x485614dbu, ((sel >> 23u) & 1u) != 0u); // 48 + r5 = r5 ^ dataset[r3 & MASK]; // 49 + r4 = rotr_var(r4, r5); // 50 + r0 = r1 * r7 + r0; // 51 + r0 = r0 * r3; // 52 + r5 = rotr_var(r5, r4); // 53 + r0 = r0 ^ simd_shuffle_xor(r6, (ushort)1); // 54 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)16); // 55 + r7 = r7 ^ dataset[r4 & MASK]; // 56 + r7 = rotr_var(r7, r3); // 57 + r0 = r0 ^ dataset[r6 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r3 ^ simd_shuffle_xor(r2, (ushort)1); // 60 + r7 = r7 - r5; // 61 + r1 = rotl_imm(r1, 4u); // 62 + r4 = r4 ^ dataset[r5 & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4a/program_bound.metal b/proto-cuda/packs-ca2-hot/hot96k4a/program_bound.metal new file mode 100644 index 00000000..ca471ed1 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4a/program_bound.metal @@ -0,0 +1,114 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +// Hot table (96 MiB, 4 of the 16 load slots): dst ^= hot[mulhi(src, HOT_WORDS)], docs/plans/hot-table.md. +#define HOT_WORDS 0x01800000u +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + device const uint* hot [[buffer(4)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r6 = r6 | r4; // 0 + r7 = r7 ^ simd_shuffle_xor(r4, (ushort)1); // 1 + r0 = r0 | r2; // 2 + r3 = r3 ^ simd_shuffle_xor(r0, (ushort)16); // 3 + r7 = r7 ^ dataset[r3 & MASK]; // 4 + r6 = r6 ^ dataset[r0 & MASK]; // 5 + r1 = r1 * r4; // 6 + r0 = r0 - r1; // 7 + r3 = r3 ^ dataset[r7 & MASK]; // 8 + r1 = r0 * r3 + r1; // 9 + r4 = r4 * r0; // 10 + r5 = r5 ^ dataset[r4 & MASK]; // 11 + r1 = r1 ^ r7; // 12 + r1 = r1 ^ dataset[r6 & MASK]; // 13 + r2 = r2 ^ dataset[r1 & MASK]; // 14 + r3 = r3 * r1; // 15 + r6 = r6 ^ hot[mulhi(r5, HOT_WORDS)]; // 16 + r0 = r0 ^ dataset[r2 & MASK]; // 17 + r0 = r0 + r7 + select(0xc035a4e6u, 0x535b545fu, ((sel >> 26u) & 1u) != 0u); // 18 + r5 = r5 - r0; // 19 + r2 = r2 * r4; // 20 + r2 = r2 + r1 + select(0x1ecdd2cfu, 0x925d5e63u, ((sel >> 6u) & 1u) != 0u); // 21 + r3 = r3 ^ r7; // 22 + r7 = r7 ^ dataset[r2 & MASK]; // 23 + r2 = mulhi(r2, r5); // 24 + r5 = rotr_var(r5, r2); // 25 + r3 = r2 * r7 + r3; // 26 + r2 = r2 - r0; // 27 + r6 = r1 * r5 + r6; // 28 + r2 = r2 + r3 + select(0xbdc6da76u, 0x46b1b505u, ((sel >> 16u) & 1u) != 0u); // 29 + r3 = r3 ^ simd_shuffle_xor(r2, (ushort)8); // 30 + r5 = r5 ^ dataset[r7 & MASK]; // 31 + r2 = r2 ^ hot[mulhi(r0, HOT_WORDS)]; // 32 + r6 = r6 | r3; // 33 + r3 = r3 ^ hot[mulhi(r6, HOT_WORDS)]; // 34 + r4 = r4 ^ simd_shuffle_xor(r0, (ushort)2); // 35 + r5 = r5 ^ r2; // 36 + r3 = r3 ^ dataset[r4 & MASK]; // 37 + r4 = r4 ^ dataset[r3 & MASK]; // 38 + r1 = rotr_var(r1, r4); // 39 + r3 = r3 + r6 + select(0xbc3ff65fu, 0x6328cb2cu, ((sel >> 28u) & 1u) != 0u); // 40 + r5 = r5 ^ r1; // 41 + r5 = r5 | r0; // 42 + r7 = r7 ^ r3; // 43 + r2 = r2 ^ dataset[r5 & MASK]; // 44 + r6 = r6 ^ simd_shuffle_xor(r4, (ushort)8); // 45 + r5 = r5 ^ r3; // 46 + r7 = r7 + r6 + select(0x5af3bd5bu, 0x9342df0du, ((sel >> 22u) & 1u) != 0u); // 47 + r3 = r3 + r4 + select(0x9ff95776u, 0x485614dbu, ((sel >> 23u) & 1u) != 0u); // 48 + r5 = r5 ^ dataset[r3 & MASK]; // 49 + r4 = rotr_var(r4, r5); // 50 + r0 = r1 * r7 + r0; // 51 + r0 = r0 * r3; // 52 + r5 = rotr_var(r5, r4); // 53 + r0 = r0 ^ simd_shuffle_xor(r6, (ushort)1); // 54 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)16); // 55 + r7 = r7 ^ dataset[r4 & MASK]; // 56 + r7 = rotr_var(r7, r3); // 57 + r0 = r0 ^ dataset[r6 & MASK]; // 58 + r6 = r6 ^ hot[mulhi(r2, HOT_WORDS)]; // 59 + r3 = r3 ^ simd_shuffle_xor(r2, (ushort)1); // 60 + r7 = r7 - r5; // 61 + r1 = rotl_imm(r1, 4u); // 62 + r4 = r4 ^ dataset[r5 & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-hot/hot96k4a/vectors.h b/proto-cuda/packs-ca2-hot/hot96k4a/vectors.h new file mode 100644 index 00000000..18043ce0 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4a/vectors.h @@ -0,0 +1,67 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0xac19a15a875e6812ull, 0x57168da17e6d150bull, 0x40c24720e9782044ull, 0xdf31f7cca113338full, 0x1618866493c5bfb0ull, 0x6861843c8d4a1f9bull, 0x29f7222ca93b80b1ull, 0x380d16aea7ae1650ull, + 0x98e3b6ceb14d2029ull, 0xcd122ff3f3be095eull, 0xdfb361635ff84a9dull, 0xfc629557772b798cull, 0x2fac241df900a2a9ull, 0x15faaa85a53e234bull, 0xd7bcefa5b756274cull, 0xd6cfcdd9e64c9249ull, + 0x282fd82f83742f7cull, 0xfa86a0ee5c1553b5ull, 0x6febc7de42ed462dull, 0xcb28e0a1d59eeff9ull, 0x22896f7fe558b0ecull, 0x60a1f5ac4923b969ull, 0xbc52ad3abdcc5d51ull, 0xda76c8af7d2033beull, + 0x3a1d693159dad97full, 0x805dc813a7134b9bull, 0x5c4eecbc102e791full, 0x98f37407e3179ed8ull, 0x14a153370bb568f0ull, 0xc7c894ae380bae8full, 0x1fbd334958f05643ull, 0xf2d73b90c808fb39ull + }, + { // base nonce 4096 + 0x0c2989ad4df0e1cdull, 0x2d811ac3bb9e3f2full, 0x3cd0261958cc4404ull, 0x56f58aa7bb55cf62ull, 0x7c7ac451133e880full, 0x6fdaf187728cf641ull, 0x8bdf8d2b267158abull, 0x0d32af7cf3f776c5ull, + 0x70ae1ef3d1340be9ull, 0xca552a81c9458e68ull, 0x5c4e20ac46185947ull, 0x07b1c3f6fc4b3c3full, 0x8813092af249b0b8ull, 0x899470f1d3037960ull, 0xe32a6efe4ddfa953ull, 0x648c829bf0ab273full, + 0x1ff2f41db69683a0ull, 0x952b3549f6f743feull, 0xec54e41fd55dc8a7ull, 0xbea7a1a76e91146eull, 0x987b74d2ba2a529bull, 0x32b4114016d4a0e3ull, 0x95594a4164c4bc82ull, 0x8838ad8f5f8d4676ull, + 0x1fdf5118172bea81ull, 0xc0131630391256e0ull, 0x5581825ad7122ad7ull, 0x9a57d84307fc233dull, 0x7af8a1edaa80d692ull, 0xed126baaadab9becull, 0xe1f8cfb85ce5daa4ull, 0xba0fe02f1064a99cull + }, + { // base nonce 1000000 + 0x36e6ef3010df901dull, 0x85afd9fb1c568a06ull, 0xdde0164b224940c4ull, 0xaac7f87e8cb35df6ull, 0x882790cd2a1cf324ull, 0x95561dc7d227e745ull, 0x31990d0a651bf406ull, 0x0eb7c7c0efc14728ull, + 0xd2bdff62f5f88969ull, 0x8851f4b00a2607feull, 0x22930374c4de8c28ull, 0xc5addc6e021248c4ull, 0xf4d96093392f213dull, 0xbebe6d4007cc5351ull, 0xa8de4c82393572bbull, 0x4c11f846bdc055aaull, + 0xdf15ec1a6791fd28ull, 0x04cf5fe7b1c2a5c0ull, 0xa9491d11303b606eull, 0x5187e22028d3e7faull, 0x260f3ae9febc0da7ull, 0xd9676cd179690bd5ull, 0x982ba405a390bea4ull, 0x6e789a577618bbf6ull, + 0xac0c75a9eb8adb76ull, 0x6a76cb5b924f825full, 0x6849407291e20d93ull, 0x6c172e401c2f7327ull, 0xb961c7981cce34ebull, 0x95e3e701554d0979ull, 0xe427f16e4073f0baull, 0xfaa821d98d70e4c5ull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu, + 0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u, + 0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du, + 0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull; +// Hot table self-test (docs/plans/hot-table.md): hot[0..15], the last 16 words, and FNV-1a 64 over all IGNEUM_HOT_WORDS words. +static const uint32_t IGNEUM_HOT_HEAD[16] = { + 0x8068cc73u, 0x6036ebf9u, 0xb604cd25u, 0x8ffb840eu, 0xc54074a2u, 0x285c0695u, 0x77512425u, 0xc26a58a7u, + 0x72c88757u, 0xc10fca78u, 0x513825ddu, 0x30d6ccc8u, 0x9a05e7cfu, 0xb9533f50u, 0x4bac3ba0u, 0xa5c19528u +}; +static const uint32_t IGNEUM_HOT_LAST[16] = { + 0xa3fd89f9u, 0xeb098388u, 0x19ee38d9u, 0x3700a229u, 0xbc0602a9u, 0x231d2a70u, 0xc23c57ceu, 0x2ff8dc6au, + 0x39683a2du, 0xbbe7d264u, 0xb8b28a43u, 0xb830041du, 0x86ae51e2u, 0xb3d22ccfu, 0x0e53335fu, 0xb56e328du +}; +static const uint64_t IGNEUM_HOT_FNV64 = 0x79bcf436c4e5bc47ull; diff --git a/proto-cuda/packs-ca2-hot/hot96k4a/vectors.json b/proto-cuda/packs-ca2-hot/hot96k4a/vectors.json new file mode 100644 index 00000000..b651c8e9 --- /dev/null +++ b/proto-cuda/packs-ca2-hot/hot96k4a/vectors.json @@ -0,0 +1,39 @@ +{ + "seed": "igneum-genesis", + "day": "2026-10-03", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0xac19a15a875e6812", "0x57168da17e6d150b", "0x40c24720e9782044", "0xdf31f7cca113338f", "0x1618866493c5bfb0", "0x6861843c8d4a1f9b", "0x29f7222ca93b80b1", "0x380d16aea7ae1650", + "0x98e3b6ceb14d2029", "0xcd122ff3f3be095e", "0xdfb361635ff84a9d", "0xfc629557772b798c", "0x2fac241df900a2a9", "0x15faaa85a53e234b", "0xd7bcefa5b756274c", "0xd6cfcdd9e64c9249", + "0x282fd82f83742f7c", "0xfa86a0ee5c1553b5", "0x6febc7de42ed462d", "0xcb28e0a1d59eeff9", "0x22896f7fe558b0ec", "0x60a1f5ac4923b969", "0xbc52ad3abdcc5d51", "0xda76c8af7d2033be", + "0x3a1d693159dad97f", "0x805dc813a7134b9b", "0x5c4eecbc102e791f", "0x98f37407e3179ed8", "0x14a153370bb568f0", "0xc7c894ae380bae8f", "0x1fbd334958f05643", "0xf2d73b90c808fb39" + ]}, + {"base_nonce": 4096, "expected": [ + "0x0c2989ad4df0e1cd", "0x2d811ac3bb9e3f2f", "0x3cd0261958cc4404", "0x56f58aa7bb55cf62", "0x7c7ac451133e880f", "0x6fdaf187728cf641", "0x8bdf8d2b267158ab", "0x0d32af7cf3f776c5", + "0x70ae1ef3d1340be9", "0xca552a81c9458e68", "0x5c4e20ac46185947", "0x07b1c3f6fc4b3c3f", "0x8813092af249b0b8", "0x899470f1d3037960", "0xe32a6efe4ddfa953", "0x648c829bf0ab273f", + "0x1ff2f41db69683a0", "0x952b3549f6f743fe", "0xec54e41fd55dc8a7", "0xbea7a1a76e91146e", "0x987b74d2ba2a529b", "0x32b4114016d4a0e3", "0x95594a4164c4bc82", "0x8838ad8f5f8d4676", + "0x1fdf5118172bea81", "0xc0131630391256e0", "0x5581825ad7122ad7", "0x9a57d84307fc233d", "0x7af8a1edaa80d692", "0xed126baaadab9bec", "0xe1f8cfb85ce5daa4", "0xba0fe02f1064a99c" + ]}, + {"base_nonce": 1000000, "expected": [ + "0x36e6ef3010df901d", "0x85afd9fb1c568a06", "0xdde0164b224940c4", "0xaac7f87e8cb35df6", "0x882790cd2a1cf324", "0x95561dc7d227e745", "0x31990d0a651bf406", "0x0eb7c7c0efc14728", + "0xd2bdff62f5f88969", "0x8851f4b00a2607fe", "0x22930374c4de8c28", "0xc5addc6e021248c4", "0xf4d96093392f213d", "0xbebe6d4007cc5351", "0xa8de4c82393572bb", "0x4c11f846bdc055aa", + "0xdf15ec1a6791fd28", "0x04cf5fe7b1c2a5c0", "0xa9491d11303b606e", "0x5187e22028d3e7fa", "0x260f3ae9febc0da7", "0xd9676cd179690bd5", "0x982ba405a390bea4", "0x6e789a577618bbf6", + "0xac0c75a9eb8adb76", "0x6a76cb5b924f825f", "0x6849407291e20d93", "0x6c172e401c2f7327", "0xb961c7981cce34eb", "0x95e3e701554d0979", "0xe427f16e4073f0ba", "0xfaa821d98d70e4c5" + ]} + ], + "dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"], + "dataset_last_index": 268435455, + "dataset_last": "0xa33ada72", + "dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}], + "cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"], + "cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"], + "cache_fnv1a64": "0x48c4f5bf24166b2e", + "hot_head": ["0x8068cc73", "0x6036ebf9", "0xb604cd25", "0x8ffb840e", "0xc54074a2", "0x285c0695", "0x77512425", "0xc26a58a7", "0x72c88757", "0xc10fca78", "0x513825dd", "0x30d6ccc8", "0x9a05e7cf", "0xb9533f50", "0x4bac3ba0", "0xa5c19528"], + "hot_last_line": ["0xa3fd89f9", "0xeb098388", "0x19ee38d9", "0x3700a229", "0xbc0602a9", "0x231d2a70", "0xc23c57ce", "0x2ff8dc6a", "0x39683a2d", "0xbbe7d264", "0xb8b28a43", "0xb830041d", "0x86ae51e2", "0xb3d22ccf", "0x0e53335f", "0xb56e328d"], + "hot_fnv1a64": "0x79bcf436c4e5bc47" +} diff --git a/proto-metal/packbench.swift b/proto-metal/packbench.swift index b7eda2f3..a38012db 100644 --- a/proto-metal/packbench.swift +++ b/proto-metal/packbench.swift @@ -63,6 +63,12 @@ let scratchWordsPerLane = Int(defineU32("IGNEUM_SCRATCH_WORDS_PER_LANE") ?? 8192 let className = defineStr("IGNEUM_LOAD_CLASS") ?? "v2" // Counter ASIC 2.0 (5 October 2026): the mixer multiplier of the item derivation, 1 when absent (version 2), 4 under class v3 let mixerMult = Int(defineU32("IGNEUM_MIXER_MULT") ?? 1) +// hot-table experiment (docs/plans/hot-table.md): the epoch table, filled on the device from memhard.metal's igneum_hot_fill +let hotMb = Int(defineU32("IGNEUM_HOT_MB") ?? 0) +let hotWords = Int(defineU32("IGNEUM_HOT_WORDS") ?? 0) +let hotSegments = Int(defineU32("IGNEUM_HOT_SEGMENTS") ?? 0) +let hotSlots = Int(defineU32("IGNEUM_HOT_SLOTS") ?? 0) +if hotMb > 0 && (hotWords != hotMb << 18 || hotSegments != hotMb * 256) { fail("program.h hot table sizes disagree") } let seedString = defineStr("IGNEUM_SEED_STRING") ?? "?" let programId = defineStr("IGNEUM_PROGRAM_ID") ?? "" @@ -77,6 +83,7 @@ let dsHead = (vj["dataset_head"] as! [String]).map(hex32) let dsLastIndex = UInt32((vj["dataset_last_index"] as! NSNumber).uint64Value) let dsLast = hex32(vj["dataset_last"] as! String) let cacheFnvWant = hex64(vj["cache_fnv1a64"] as! String) +let hotFnvWant: UInt64? = (vj["hot_fnv1a64"] as? String).map(hex64) guard let device = MTLCreateSystemDefaultDevice(), let queue = device.makeCommandQueue() else { fail("no Metal device") } let words = 1 << datasetLog2 @@ -93,6 +100,11 @@ guard let fillFn = mhLib.makeFunction(name: "igneum_cache_fill"), let buildFn = let fillPipe = try! device.makeComputePipelineState(function: fillFn) let buildPipe = try! device.makeComputePipelineState(function: buildFn) let hashPipe = try! device.makeComputePipelineState(function: hashFn) +var hotPipe: MTLComputePipelineState? = nil +if hotMb > 0 { + guard let hotFn = mhLib.makeFunction(name: "igneum_hot_fill") else { fail("program.h says IGNEUM_HOT_MB \(hotMb) but memhard.metal has no igneum_hot_fill") } + hotPipe = try! device.makeComputePipelineState(function: hotFn) +} let compileMs = nowMs() - t0 if hashPipe.threadExecutionWidth != 32 { print("WARNING: threadExecutionWidth \(hashPipe.threadExecutionWidth), not 32") } @@ -134,6 +146,22 @@ func fnv1a64(_ p: UnsafeRawPointer, _ n: Int) -> UInt64 { let cacheCopy = blit(cache, 0, cacheWords * 4) let cacheFnv = fnv1a64(cacheCopy.contents(), cacheWords * 4) let cacheOk = cacheFnv == cacheFnvWant +// hot table: fill, then fingerprint against vectors.json +var hot: MTLBuffer? = nil +var hotGpu = 0.0, hotWall = 0.0 +var hotOk = true +var hotFnv: UInt64 = 0 +if hotMb > 0 { + guard let h = device.makeBuffer(length: hotWords * 4, options: .storageModePrivate) else { fail("hot table alloc of \(hotMb) MiB") } + hot = h + (hotWall, hotGpu) = run { enc in + enc.setComputePipelineState(hotPipe!); enc.setBuffer(h, offset: 0, index: 0) + enc.dispatchThreadgroups(MTLSize(width: hotSegments / 256, height: 1, depth: 1), threadsPerThreadgroup: MTLSize(width: 256, height: 1, depth: 1)) + } + let hotCopy = blit(h, 0, hotWords * 4) + hotFnv = fnv1a64(hotCopy.contents(), hotWords * 4) + if let want = hotFnvWant { hotOk = hotFnv == want } +} let headCopy = blit(dataset, 0, 64) let headPtr = headCopy.contents().bindMemory(to: UInt32.self, capacity: 16) var dsOk = (0..<16).allSatisfy { headPtr[$0] == dsHead[$0] } @@ -155,13 +183,16 @@ func encodeHash(_ enc: MTLComputeCommandEncoder, out: MTLBuffer, base: UInt32, n enc.setBuffer(dataset, offset: 0, index: 0) enc.setBuffer(out, offset: 0, index: 1) var b = base; enc.setBytes(&b, length: 4, index: 2) + // the hot table is buffer 3 (the scratch triple moves up by one when both are present) + let next = hot == nil ? 3 : 4 + if let h = hot { enc.setBuffer(h, offset: 0, index: 3) } if persistent { let units = nonces / 32 let nw = min(warpsN, units) if units % nw != 0 { fail("nonces \(nonces) is not a multiple of 32 x \(nw) warps") } - enc.setBuffer(scratch!, offset: 0, index: 3) - var g = UInt32(units); enc.setBytes(&g, length: 4, index: 4) - var s = salt; enc.setBytes(&s, length: 4, index: 5) + enc.setBuffer(scratch!, offset: 0, index: next) + var g = UInt32(units); enc.setBytes(&g, length: 4, index: next + 1) + var s = salt; enc.setBytes(&s, length: 4, index: next + 2) salt = salt &+ UInt32(units) let threads = nw * 32 let tg = min(group, threads) @@ -205,7 +236,8 @@ let packName = (opts.pack as NSString).lastPathComponent print("pack \(packName) seed \"\(seedString)\" id \(programId) class \(className): loads/hash \(loadsPerHash), dataset bytes/hash \(bytesPerHash), scratch ops/hash \(scratchOps * 8), mixer x\(mixerMult), cache 2^\(cacheLog2) words") print("device \(device.name); compile \(String(format: "%.0f", compileMs)) ms; cache fill \(String(format: "%.1f", cacheGpu)) ms GPU (\(String(format: "%.1f", cacheWall)) wall); dataset build \(String(format: "%.1f", buildGpu)) ms GPU (\(String(format: "%.1f", buildWall)) wall)") print("cache FNV-1a 64 \(String(format: "%016llx", cacheFnv)) \(cacheOk ? "PASS" : "FAIL"); dataset head and last \(dsOk ? "PASS" : "FAIL"); vectors standalone \(vecPass)/\(vecBases.count), in batch \(batchVecPass)/\(batchVecN)") +if hotMb > 0 { print("hot table \(hotMb) MiB (\(hotSlots) of 16 load slots): fill \(String(format: "%.2f", hotGpu)) ms GPU (\(String(format: "%.2f", hotWall)) wall), FNV-1a 64 \(String(format: "%016llx", hotFnv)) \(hotFnvWant == nil ? "(not in vectors.json)" : (hotOk ? "PASS" : "FAIL"))") } print("warm-up batch \(nonces) hashes: \(String(format: "%.1f", warmGpu)) ms GPU, \(String(format: "%.1f", warmWall)) ms wall") -let overall = cacheOk && dsOk && vecPass == vecBases.count && batchVecPass == batchVecN -print("RESULT pack=\(packName) class=\(className) device=\(device.name.replacingOccurrences(of: " ", with: "_")) group=\(opts.group) warps=\(warpsN) arena_mib=\(persistent ? warpsN * 32 * scratchWordsPerLane * 4 / 1048576 : 0) nonces=\(nonces) batches=\(opts.batches) vectors=\(vecPass)/\(vecBases.count) batch_vectors=\(batchVecPass)/\(batchVecN) cache=\(cacheOk ? "PASS" : "FAIL") dataset=\(dsOk ? "PASS" : "FAIL") fingerprint=\(String(format: "%016llx", fingerprint)) mhs_gpu=\(String(format: "%.3f", mhsGpu)) mhs_wall=\(String(format: "%.3f", mhsWall)) loads=\(loadsPerHash) bytes=\(bytesPerHash) scratch_ops=\(scratchOps * 8) overall=\(overall ? "PASS" : "FAIL")") +let overall = cacheOk && dsOk && hotOk && vecPass == vecBases.count && batchVecPass == batchVecN +print("RESULT pack=\(packName) class=\(className) device=\(device.name.replacingOccurrences(of: " ", with: "_")) group=\(opts.group) warps=\(warpsN) arena_mib=\(persistent ? warpsN * 32 * scratchWordsPerLane * 4 / 1048576 : 0) hot_mib=\(hotMb) hot_slots=\(hotSlots) hot_fill_ms=\(String(format: "%.2f", hotGpu)) hot=\(hotMb > 0 ? (hotOk ? "PASS" : "FAIL") : "none") nonces=\(nonces) batches=\(opts.batches) vectors=\(vecPass)/\(vecBases.count) batch_vectors=\(batchVecPass)/\(batchVecN) cache=\(cacheOk ? "PASS" : "FAIL") dataset=\(dsOk ? "PASS" : "FAIL") fingerprint=\(String(format: "%016llx", fingerprint)) mhs_gpu=\(String(format: "%.3f", mhsGpu)) mhs_wall=\(String(format: "%.3f", mhsWall)) loads=\(loadsPerHash) bytes=\(bytesPerHash) scratch_ops=\(scratchOps * 8) overall=\(overall ? "PASS" : "FAIL")") exit(overall ? 0 : 1) diff --git a/proto-opencl/host.c b/proto-opencl/host.c index 8423180a..8c87584a 100644 --- a/proto-opencl/host.c +++ b/proto-opencl/host.c @@ -1049,6 +1049,12 @@ typedef struct { cl_program prog; cl_kernel kHashBound, kCacheFill, kBuild; cl_mem cache, ds; + /* hot-table experiment (5 October 2026, docs/plans/hot-table.md): the epoch's table, filled on the device by the + * pack's igneum_hot_fill, the argument after the init words; hotWords 0 for a pack without one */ + cl_kernel kHotFill; + cl_mem hot; + uint32_t hotWords, hotSegments, hotMb, hotSlots; + double hotMs; double buildMs, cacheMs, datasetMs, checkMs; char check[1024]; /* the self-test verdict (packfile.h), one line */ int checked; @@ -1089,6 +1095,8 @@ static void releasePair(ServePair* p) { if (!p) return; if (p->ds) { clReleaseMemObject(p->ds); ++gMemReleased; } if (p->cache) { clReleaseMemObject(p->cache); ++gMemReleased; } + if (p->hot) { clReleaseMemObject(p->hot); ++gMemReleased; } + if (p->kHotFill) clReleaseKernel(p->kHotFill); if (p->kHashBound) clReleaseKernel(p->kHashBound); if (p->kCacheFill) clReleaseKernel(p->kCacheFill); if (p->kBuild) clReleaseKernel(p->kBuild); @@ -1124,6 +1132,8 @@ static int pairSelfTest(Device* dv, const DeviceInfo* di, cl_command_queue q, Se PfPack pk; char perr[256]; uint32_t cacheHead[16], cacheLast[16], dsHead[16], dsLast = 0; + uint32_t hotHead[16], hotLast[16]; + uint64_t hotFnv = 0; uint32_t samples[PF_MAX_SAMPLES]; uint64_t vec[PF_MAX_WARPS * 32]; uint64_t fnv; @@ -1131,6 +1141,7 @@ static int pairSelfTest(Device* dv, const DeviceInfo* di, cl_command_queue q, Se cl_mem out = NULL, init = NULL; cl_int e = 0; int w, i, ok; + cl_uint extra = 5; /* the first argument after the five of igneum_hash_bound: the hot table, then the scratch triple */ size_t local = (size_t)dv->groupSize, g = (size_t)dv->groupSize; /* one work-group; its first 32 lanes are the warp */ double tb = wallMs(); (void)di; @@ -1152,6 +1163,16 @@ static int pairSelfTest(Device* dv, const DeviceInfo* di, cl_command_queue q, Se if (e == CL_SUCCESS && pk.dsLastIndex < words) e = clEnqueueReadBuffer(q, p->ds, CL_TRUE, (size_t)pk.dsLastIndex * 4u, 4, &dsLast, 0, NULL, NULL); for (i = 0; i < pk.nSamples && e == CL_SUCCESS; ++i) { samples[i] = 0; if (pk.sampleIdx[i] < words) e = clEnqueueReadBuffer(q, p->ds, CL_TRUE, (size_t)pk.sampleIdx[i] * 4u, 4, &samples[i], 0, NULL, NULL); } if (e != CL_SUCCESS) { snprintf(err, errCap, "self-test: dataset read-back (%s)", clErrName(e)); return 0; } + if (p->hot) { + uint32_t* hw = (uint32_t*)malloc((size_t)p->hotWords * 4u); + if (!hw) { snprintf(err, errCap, "self-test: no host memory for the hot table read-back"); return 0; } + e = clEnqueueReadBuffer(q, p->hot, CL_TRUE, 0, (size_t)p->hotWords * 4u, hw, 0, NULL, NULL); + if (e != CL_SUCCESS) { free(hw); snprintf(err, errCap, "self-test: hot table read-back (%s)", clErrName(e)); return 0; } + memcpy(hotHead, hw, 64); + memcpy(hotLast, hw + p->hotWords - 16u, 64); + hotFnv = pf_fnv1a64(hw, (size_t)p->hotWords * 4u); + free(hw); + } out = clCreateBuffer(dv->ctx, CL_MEM_READ_WRITE, g * sizeof(uint64_t), NULL, &e); if (e == CL_SUCCESS) { ++gMemCreated; init = clCreateBuffer(dv->ctx, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, 32, pk.seedw, &e); } if (e == CL_SUCCESS) ++gMemCreated; @@ -1166,14 +1187,15 @@ static int pairSelfTest(Device* dv, const DeviceInfo* di, cl_command_queue q, Se if (e == CL_SUCCESS) e = clSetKernelArg(p->kHashBound, 2, sizeof(cl_uint), &base); if (e == CL_SUCCESS) e = clSetKernelArg(p->kHashBound, 3, sizeof(cl_uint), &mask); if (e == CL_SUCCESS) e = clSetKernelArg(p->kHashBound, 4, sizeof(cl_mem), &init); - if (e == CL_SUCCESS && pk.persistent) e = setScratchArgs(p->kHashBound, 5, 1u); + if (e == CL_SUCCESS && p->hot) { e = clSetKernelArg(p->kHashBound, 5, sizeof(cl_mem), &p->hot); extra = 6; } + if (e == CL_SUCCESS && pk.persistent) e = setScratchArgs(p->kHashBound, extra, 1u); if (e == CL_SUCCESS) e = clEnqueueNDRangeKernel(q, p->kHashBound, 1, NULL, &g, &local, 0, NULL, NULL); if (e == CL_SUCCESS) e = clEnqueueReadBuffer(q, out, CL_TRUE, 0, 32 * sizeof(uint64_t), &vec[w * 32], 0, NULL, NULL); } if (out) { clReleaseMemObject(out); ++gMemReleased; } if (init) { clReleaseMemObject(init); ++gMemReleased; } if (e != CL_SUCCESS) { snprintf(err, errCap, "self-test: vector warp (%s)", clErrName(e)); return 0; } - ok = pf_selftest(&pk, cacheHead, cacheLast, fnv, dsHead, dsLast, samples, vec, p->check, sizeof(p->check)); + ok = pf_selftest(&pk, cacheHead, cacheLast, fnv, dsHead, dsLast, samples, vec, p->hot ? hotHead : NULL, p->hot ? hotLast : NULL, hotFnv, p->check, sizeof(p->check)); p->checked = 1; p->checkMs = wallMs() - tb; if (!ok) { snprintf(err, errCap, "%s", p->check); return 0; } @@ -1211,9 +1233,37 @@ static int pairBuffers(Device* dv, const DeviceInfo* di, cl_command_queue q, Ser if (e == CL_SUCCESS) e = clFinish(q); if (e != CL_SUCCESS) { snprintf(err, errCap, "dataset build (%s)", clErrName(e)); return 0; } p->datasetMs = wallMs() - tb; + if (p->kHotFill && p->hotWords) { + /* hot-table experiment: the epoch's table from the pack's own fill kernel (one work-item per segment) */ + cl_uint nSegH = p->hotSegments; + tb = wallMs(); + p->hot = clCreateBuffer(dv->ctx, CL_MEM_READ_WRITE, (size_t)p->hotWords * 4u, NULL, &e); + if (e != CL_SUCCESS) { snprintf(err, errCap, "clCreateBuffer hot table (%s)", clErrName(e)); return 0; } + ++gMemCreated; + local = kernelMaxLocal(dv, p->kHotFill, di, 256); + g = ((nSegH + local - 1) / local) * local; + e = clSetKernelArg(p->kHotFill, 0, sizeof(cl_mem), &p->hot); + if (e == CL_SUCCESS) e = clSetKernelArg(p->kHotFill, 1, sizeof(cl_uint), &nSegH); + if (e == CL_SUCCESS) e = clEnqueueNDRangeKernel(q, p->kHotFill, 1, NULL, &g, &local, 0, NULL, NULL); + if (e == CL_SUCCESS) e = clFinish(q); + if (e != CL_SUCCESS) { snprintf(err, errCap, "hot table fill (%s)", clErrName(e)); return 0; } + p->hotMs = wallMs() - tb; + } return pairSelfTest(dv, di, q, p, words, cacheWords, packDir, err, errCap); } +/* The hot-table kernel and sizes of a pair whose program came from a pack directory (none when the pack has no hot table). */ +static int pairHotKernel(ServePair* p, const char* packDir, char* err, size_t errCap) { + PfPack pk; + char perr[256]; + cl_int e = 0; + if (!packDir || !packDir[0] || !pf_load(packDir, &pk, perr, sizeof(perr)) || !pk.hotMb) return 1; + p->kHotFill = clCreateKernel(p->prog, "igneum_hot_fill", &e); + if (e != CL_SUCCESS) { snprintf(err, errCap, "clCreateKernel igneum_hot_fill (%s): the pack says IGNEUM_HOT_MB %u but its kernel source has no hot fill", clErrName(e), pk.hotMb); return 0; } + p->hotWords = pk.hotWords; p->hotSegments = pk.hotSegments; p->hotMb = pk.hotMb; p->hotSlots = pk.hotSlots; + return 1; +} + /* Builds the pair for a prepare request. Runs on its own thread with its own command queue. */ static void prepareRun(PrepareTask* t) { cl_int err = 0; @@ -1257,6 +1307,7 @@ static void prepareRun(PrepareTask* t) { if (err != CL_SUCCESS) { prepareFail(t, "clCreateKernel igneum_cache_fill", err); releasePair(p); t->doneAt = wallMs(); t->done = 1; return; } p->kBuild = clCreateKernel(p->prog, "igneum_build", &err); if (err != CL_SUCCESS) { prepareFail(t, "clCreateKernel igneum_build", err); releasePair(p); t->doneAt = wallMs(); t->done = 1; return; } + if (!pairHotKernel(p, t->packDir, t->error, sizeof(t->error))) { releasePair(p); t->doneAt = wallMs(); t->done = 1; return; } q = clCreateCommandQueue(t->dv->ctx, t->di->device, 0, &err); if (err != CL_SUCCESS) { prepareFail(t, "clCreateCommandQueue (prepare)", err); releasePair(p); t->doneAt = wallMs(); t->done = 1; return; } if (!pairBuffers(t->dv, t->di, q, p, t->words, t->cacheWords, t->segments, t->packDir, t->error, sizeof(t->error))) { clReleaseCommandQueue(q); releasePair(p); t->doneAt = wallMs(); t->done = 1; return; } @@ -1295,8 +1346,8 @@ static int runBenchPack(Device* dv, const DeviceInfo* di, const Options* o) { uint64_t* hOut; ServePair* cur; char perr[512], devName[256]; - double t0 = wallMs(), sum = 0, warmMs; - uint64_t fp; + double t0 = wallMs(), sum = 0, warmMs = 0; + uint64_t fp = 0; int b, k; cl_uint warps = (cl_uint)(o->warps > 0 ? o->warps : 2048), units = nonces / 32u; if (!dv->kHashBound) { printf("FAIL: the kernel source has no igneum_hash_bound\n"); return 2; } @@ -1314,8 +1365,9 @@ static int runBenchPack(Device* dv, const DeviceInfo* di, const Options* o) { cur->kHashBound = dv->kHashBound; cur->kCacheFill = dv->kCacheFill; cur->kBuild = dv->kBuild; cur->prog = dv->prog; dv->kHashBound = dv->kCacheFill = dv->kBuild = NULL; dv->prog = NULL; memcpy(cur->sw, gPack.seedw, 32); memcpy(cur->kw, gPack.keyw, 32); + if (!pairHotKernel(cur, o->packDir, perr, sizeof(perr))) { printf("FAIL: pack %s: %s\n", o->packDir, perr); return 1; } if (!pairBuffers(dv, di, dv->q, cur, words, gServeCacheWords, gServeSegments, o->packDir, perr, sizeof(perr))) { printf("FAIL: pack %s: %s\n", o->packDir, perr); return 1; } - printf("pack %s: cache %.0f dataset %.0f check %.0f ms (%.0f ms in all); %s\n", o->packDir, cur->cacheMs, cur->datasetMs, cur->checkMs, wallMs() - t0, cur->check); + printf("pack %s: cache %.0f dataset %.0f hot %.0f check %.0f ms (%.0f ms in all); %s\n", o->packDir, cur->cacheMs, cur->datasetMs, cur->hotMs, cur->checkMs, wallMs() - t0, cur->check); printKernelInfo(di, cur->kHashBound, "igneum_hash_bound", (int)groupSize, ""); dOut = clCreateBuffer(dv->ctx, CL_MEM_READ_WRITE, (size_t)nonces * sizeof(uint64_t), NULL, &err); CL_CHECK_ERR(err, "clCreateBuffer out"); dInit = clCreateBuffer(dv->ctx, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, 32, cur->sw, &err); CL_CHECK_ERR(err, "clCreateBuffer init words"); @@ -1332,7 +1384,8 @@ static int runBenchPack(Device* dv, const DeviceInfo* di, const Options* o) { CL_CHECK(clSetKernelArg(cur->kHashBound, 2, sizeof(cl_uint), &base)); CL_CHECK(clSetKernelArg(cur->kHashBound, 3, sizeof(cl_uint), &mask)); CL_CHECK(clSetKernelArg(cur->kHashBound, 4, sizeof(cl_mem), &dInit)); - if (gPack.persistent) CL_CHECK(setScratchArgs(cur->kHashBound, 5, units)); + if (cur->hot) CL_CHECK(clSetKernelArg(cur->kHashBound, 5, sizeof(cl_mem), &cur->hot)); + if (gPack.persistent) CL_CHECK(setScratchArgs(cur->kHashBound, cur->hot ? 6 : 5, units)); { double w0 = wallMs(); CL_CHECK(clEnqueueNDRangeKernel(dv->q, cur->kHashBound, 1, NULL, &g, &groupSize, 0, NULL, &ev)); @@ -1348,9 +1401,9 @@ static int runBenchPack(Device* dv, const DeviceInfo* di, const Options* o) { } else sum += ms; } printf("warm-up dispatch (base 0): %.2f ms; %d timed dispatches of 2^%d nonces: mean %.2f ms\n", warmMs, o->batches, o->batchLog2, sum / o->batches); - printf("RESULT pack=%s class=%s device=%s platform=%s group=%d warps=%u arena_mib=%llu nonces=%u batches=%d check=%s fingerprint=%016llx mhs=%.3f loads=%u bytes=%u scratch_ops=%u time=%s\n", + printf("RESULT pack=%s class=%s device=%s platform=%s group=%d warps=%u arena_mib=%llu hot_mib=%u hot_slots=%u hot_fill_ms=%.2f nonces=%u batches=%d check=%s fingerprint=%016llx mhs=%.3f loads=%u bytes=%u scratch_ops=%u time=%s\n", o->packDir, gPack.loadClass, devName, strcmp(di->platformName, "Apple") == 0 ? "Apple" : "other", (int)groupSize, gPack.persistent ? warps : 0u, - gPack.persistent ? (unsigned long long)(((size_t)warps * 32u * gPack.scratchWordsPerLane * 4u) >> 20) : 0ull, nonces, o->batches, + gPack.persistent ? (unsigned long long)(((size_t)warps * 32u * gPack.scratchWordsPerLane * 4u) >> 20) : 0ull, cur->hotMb, cur->hotSlots, cur->hotMs, nonces, o->batches, cur->checked ? "PASS" : "skipped", (unsigned long long)fp, (double)nonces * (double)o->batches / (sum / 1000.0) / 1e6, gPack.loadsPerHash, gPack.bytesPerHash, gPack.scratchOps * 8u, o->timeWall ? "wall" : "event"); free(hOut); @@ -1395,6 +1448,7 @@ static int runServe(Device* dv, const DeviceInfo* di, const Options* o) { strncpy(cur->epochHex, gPack.epochHex, 64); cur->epochHex[64] = 0; strncpy(cur->dayHex, gPack.dayHex, sizeof(cur->dayHex) - 1); strncpy(cur->programClass, gPack.programClass, sizeof(cur->programClass) - 1); strncpy(cur->eraHex, gPack.eraHex, sizeof(cur->eraHex) - 1); + if (!pairHotKernel(cur, o->packDir, perr, sizeof(perr))) { printf("error 0 pack %s: %s\n", o->packDir, perr); fflush(stdout); return 1; } if (!pairBuffers(dv, di, dv->q, cur, words, gServeCacheWords, gServeSegments, o->packDir, perr, sizeof(perr))) { printf("error 0 pack %s: %s\n", o->packDir, perr); fflush(stdout); return 1; } printf("info first pack %s: cache %.0f dataset %.0f check %.0f ms (%.0f ms in all); %s\n", o->packDir, cur->cacheMs, cur->datasetMs, cur->checkMs, wallMs() - t0, cur->check); } else { @@ -1607,6 +1661,7 @@ static int runServe(Device* dv, const DeviceInfo* di, const Options* o) { SERVE_CHECK(jobId, clSetKernelArg(cur->kHashBound, 2, sizeof(cl_uint), &baseNonce)); SERVE_CHECK(jobId, clSetKernelArg(cur->kHashBound, 3, sizeof(cl_uint), &maskArg)); SERVE_CHECK(jobId, clSetKernelArg(cur->kHashBound, 4, sizeof(cl_mem), &dInit)); + if (cur->hot) SERVE_CHECK(jobId, clSetKernelArg(cur->kHashBound, 5, sizeof(cl_mem), &cur->hot)); ev = NULL; if (gFaultTestChunk >= 0 && (long)chunksSeen >= gFaultTestChunk) { /* IGNEUM_FAULT_TEST=N (test only): from chunk N on, behave like a runtime that answers every call with diff --git a/relay/playbooks/ca2-hot-5090-bench.ps1 b/relay/playbooks/ca2-hot-5090-bench.ps1 new file mode 100644 index 00000000..48f869f9 --- /dev/null +++ b/relay/playbooks/ca2-hot-5090-bench.ps1 @@ -0,0 +1,66 @@ +# Igneum run job: the hot-table experiment (Counter ASIC 2.0 layer 5, docs/plans/hot-table.md) on PC 2's RTX 5090 (machine 1ccfe586), 5 October 2026 (the probe at the hot sizes 32, 64 and 96 MiB, then the five hot packs). +# Published as a plain `run` job (NOT --stop-miners): the installed app keeps every other card mining; this script switches +# off ONLY the NVIDIA card in the app through POST api/cards, waits for its worker to stop, runs the fetched +# igneum-worker-cuda.exe (--memprobe, then --bench on every pack of the fetched packs folder), and switches the card back +# on with the settings it had. Every result line starts with RESULT so `node tools/jobs.mjs ` shows them. +$ErrorActionPreference = 'Continue' +function Say([string] $m) { Write-Host ("[" + (Get-Date -Format 'HH:mm:ss') + "] " + $m) } +$jobs = Split-Path $env:IGNEUM_JOB_DIR +$fetched = Join-Path $jobs 'fetch-ca2-hot-20261005' +$exe = Join-Path $fetched 'igneum-worker-cuda.exe' +$packs = Join-Path $fetched 'packs-ca2-hot' +if (-not (Test-Path $exe)) { Write-Output "RESULT error worker missing at $exe (the fetch job runs first)"; exit 2 } +if (-not (Test-Path $packs)) { Write-Output "RESULT error packs missing at $packs"; exit 2 } +$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-worker-cuda.exe') } | Select-Object -First 1 +if (-not $inst) { Write-Output 'RESULT error no installed igneum-worker-cuda.exe (the NVRTC DLLs come from there)'; exit 2 } +Get-ChildItem $inst -Filter 'nvrtc*.dll' | Copy-Item -Destination $fetched -Force +Write-Output "RESULT worker $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) with $((Get-ChildItem $fetched -Filter 'nvrtc*.dll').Count) NVRTC DLL(s) from $inst" + +# the app: switch off the NVIDIA card only, remember its settings +$appDir = $env:IGNEUM_APP_DIR +if (-not $appDir) { $appDir = Join-Path $env:LOCALAPPDATA 'igneum\app' } +$urlFile = Join-Path $appDir 'app.url' +$url = $null +if (Test-Path $urlFile) { $url = (Get-Content -LiteralPath $urlFile -Raw).Trim() } +$card = $null +if ($url) { + try { + $st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10 + $card = $st.mining.cards | Where-Object { $_.vendor -eq 'nvidia' } | Select-Object -First 1 + if (-not $card) { $card = $st.cards | Where-Object { $_.vendor -eq 'nvidia' } | Select-Object -First 1 } + } catch { Say ("api/state: " + $_.Exception.Message) } +} +if ($card) { + Write-Output ("RESULT card " + $card.key + " enabled=" + $card.enabled + " identities=" + $card.identities + " power_pct=" + $card.power_pct + " state=" + $card.state) + $body = @{ cards = @(@{ key = $card.key; enabled = $false; identities = [int]$card.identities; power_pct = [int]$card.power_pct }) } | ConvertTo-Json -Depth 5 + try { Invoke-RestMethod -Uri ($url + 'api/cards') -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; Say "card off requested" } catch { Say ("api/cards off: " + $_.Exception.Message) } + $t = 0 + while ($t -lt 90) { + Start-Sleep -Seconds 5; $t += 5 + try { $st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10; $c2 = $st.mining.cards | Where-Object { $_.key -eq $card.key }; if (-not $c2) { $c2 = $st.cards | Where-Object { $_.key -eq $card.key } }; if ($c2 -and $c2.state -eq 'off' -and $c2.pid -eq 0) { break } } catch { } + } + Write-Output ("RESULT card-off after " + $t + " s") + Start-Sleep -Seconds 5 +} else { Write-Output 'RESULT card none-found (the app is not running or has no NVIDIA card); measuring with whatever else runs on the GPU' } + +& nvidia-smi --query-gpu=name,driver_version,power.limit,clocks.sm,clocks.mem,memory.used,temperature.gpu --format=csv,noheader 2>&1 | ForEach-Object { "RESULT gpu-before $_" } +# the probe at the hot sizes (and 1024 MiB again, the same session): the random-read ceiling inside the 96 MiB L2 +foreach ($mib in @(32, 64, 96, 1024)) { + Write-Output "RESULT memprobe $mib start $(Get-Date -Format HH:mm:ss)" + & $exe --memprobe --probe-mib $mib 2>&1 | ForEach-Object { "RESULT $_" } +} +# the eight hot packs (five replaced, three added, docs/plans/hot-table.md 2.3): --bench builds the pack, fills the hot table on the device from the epoch seed, self-tests it +# (cache, dataset, hot table head, last line and FNV, 96 vector lanes) and times 5 dispatches of 2^24 nonces +foreach ($pk in @('hot32k4', 'hot64k4', 'hot96k4', 'hot64k2', 'hot64k8', 'hot32k4a', 'hot64k4a', 'hot96k4a')) { + $d = Join-Path $packs $pk + Write-Output "RESULT bench $pk start $(Get-Date -Format HH:mm:ss)" + & $exe --bench --pack $d --batches 5 --batch-log2 24 --block-warps 1 2>&1 | ForEach-Object { "RESULT $_" } + & $exe --bench --pack $d --batches 5 --batch-log2 24 --block-warps 8 2>&1 | Where-Object { $_ -match '^RESULT|error|FAIL' } | ForEach-Object { "RESULT $_" } +} +& nvidia-smi --query-gpu=power.draw,clocks.sm,clocks.mem,memory.used,temperature.gpu --format=csv,noheader 2>&1 | ForEach-Object { "RESULT gpu-after $_" } + +if ($card) { + $body = @{ cards = @(@{ key = $card.key; enabled = [bool]$card.enabled; identities = [int]$card.identities; power_pct = [int]$card.power_pct }) } | ConvertTo-Json -Depth 5 + try { Invoke-RestMethod -Uri ($url + 'api/cards') -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; Write-Output ("RESULT card restored enabled=" + $card.enabled) } catch { Write-Output ("RESULT error card restore: " + $_.Exception.Message) } +} +exit 0 diff --git a/relay/playbooks/ca2-hot-9070-bench.ps1 b/relay/playbooks/ca2-hot-9070-bench.ps1 new file mode 100644 index 00000000..f16bd665 --- /dev/null +++ b/relay/playbooks/ca2-hot-9070-bench.ps1 @@ -0,0 +1,85 @@ +# Igneum run job: the hot-table experiment (Counter ASIC 2.0 layer 5, docs/plans/hot-table.md) on PC 1's RX 9070 XT on the eGPU (machine ae432dc7), 5 October 2026 (the probe at the hot sizes 32, 64 and 96 MiB, then the five hot packs). +# Published as a plain `run` job (NOT --stop-miners): the installed app keeps every other card mining; this script switches +# off ONLY the NVIDIA card in the app through POST api/cards, waits for its worker to stop, runs the fetched +# igneum-worker-opencl.exe (--memprobe, then --bench on every pack of the fetched packs folder), and switches the card back +# on with the settings it had. Every result line starts with RESULT so `node tools/jobs.mjs ` shows them. +$ErrorActionPreference = 'Continue' +function Say([string] $m) { Write-Host ("[" + (Get-Date -Format 'HH:mm:ss') + "] " + $m) } +$jobs = Split-Path $env:IGNEUM_JOB_DIR +$fetched = Join-Path $jobs 'fetch-ca2-hot-20261005' +$exe = Join-Path $fetched 'igneum-worker-opencl.exe' +$packs = Join-Path $fetched 'packs-ca2-hot' +if (-not (Test-Path $exe)) { Write-Output "RESULT error worker missing at $exe (the fetch job runs first)"; exit 2 } +if (-not (Test-Path $packs)) { Write-Output "RESULT error packs missing at $packs"; exit 2 } +Write-Output "RESULT worker $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower())" +# the card's OpenCL device index on the current (3683.0) platform, from the worker's own list (the older platform's duplicate is marked dup) +$list = & $exe --list 2>&1 +$list | ForEach-Object { "RESULT list $_" } +$dev = $null +$cardName = 'gfx1201' +foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1201' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } } +# 5 October 2026, 20:40 UTC: the Sonnet eGPU box dropped off PC 1's USB4 link (gfx1201 absent at 20:45:34Z). Gate G1's +# AMD vendor is then the integrated gfx1036 (coordinator's ruling): bit-exactness only (vectors, hot table head, last +# line and FNV, the 2^24 fingerprint at base 0 equal to the 5090's and the Mac's); its probe and hash rate say nothing +# about the 9070 XT (2 MB of cache, approximate) and are skipped as owed. If gfx1201 is back at run time, it runs as planned. +if ($null -eq $dev) { + foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1036' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; $cardName = 'gfx1036'; break } } +} +if ($null -eq $dev) { Write-Output 'RESULT error neither gfx1201 nor gfx1036 in --list'; exit 2 } +Write-Output "RESULT amd-device $dev $cardName" +Write-Output "RESULT device $dev" + +# the app: switch off the NVIDIA card only, remember its settings +$appDir = $env:IGNEUM_APP_DIR +if (-not $appDir) { $appDir = Join-Path $env:LOCALAPPDATA 'igneum\app' } +$urlFile = Join-Path $appDir 'app.url' +$url = $null +if (Test-Path $urlFile) { $url = (Get-Content -LiteralPath $urlFile -Raw).Trim() } +$card = $null +if ($url) { + try { + $st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10 + $card = $st.mining.cards | Where-Object { ($_.vendor -eq 'amd' -and $_.key -match $cardName) } | Select-Object -First 1 + if (-not $card) { $card = $st.cards | Where-Object { ($_.vendor -eq 'amd' -and $_.key -match $cardName) } | Select-Object -First 1 } + } catch { Say ("api/state: " + $_.Exception.Message) } +} +if ($card) { + Write-Output ("RESULT card " + $card.key + " enabled=" + $card.enabled + " identities=" + $card.identities + " power_pct=" + $card.power_pct + " state=" + $card.state) + $body = @{ cards = @(@{ key = $card.key; enabled = $false; identities = [int]$card.identities; power_pct = [int]$card.power_pct }) } | ConvertTo-Json -Depth 5 + try { Invoke-RestMethod -Uri ($url + 'api/cards') -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; Say "card off requested" } catch { Say ("api/cards off: " + $_.Exception.Message) } + $t = 0 + while ($t -lt 90) { + Start-Sleep -Seconds 5; $t += 5 + try { $st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10; $c2 = $st.mining.cards | Where-Object { $_.key -eq $card.key }; if (-not $c2) { $c2 = $st.cards | Where-Object { $_.key -eq $card.key } }; if ($c2 -and $c2.state -eq 'off' -and $c2.pid -eq 0) { break } } catch { } + } + Write-Output ("RESULT card-off after " + $t + " s") + Start-Sleep -Seconds 5 +} else { Write-Output 'RESULT card none-found (the app is not running or has no NVIDIA card); measuring with whatever else runs on the GPU' } + +# the probe ran in run-readwidth-9070-20261005 (bench-log, 5 October 2026); this second run is the bench only +# the probe at the hot sizes (64 MiB exists from the 9070 XT entry: 9.17 G loads/s; 32 and 96 MiB are new; 1024 MiB again, the same session) +if ($cardName -eq 'gfx1201') { + foreach ($mib in @(32, 64, 96, 1024)) { + Write-Output "RESULT memprobe $mib start $(Get-Date -Format HH:mm:ss)" + & $exe --memprobe --probe-mib $mib --device $dev 2>&1 | ForEach-Object { "RESULT $_" } + } +} else { Write-Output 'RESULT memprobe skipped (gfx1036: its cache says nothing about the 9070 XT; the 32, 96 and 1024 MiB rows stay owed)' } +# the eight hot packs (five replaced, three added, docs/plans/hot-table.md 2.3): --bench-pack builds the pack, fills the hot table on the device from the epoch seed, self-tests it +# (cache, dataset, hot table head, last line and FNV, 96 vector lanes) and times 5 dispatches of 2^24 nonces +foreach ($pk in @('hot32k4', 'hot64k4', 'hot96k4', 'hot64k2', 'hot64k8', 'hot32k4a', 'hot64k4a', 'hot96k4a')) { + $d = Join-Path $packs $pk + Write-Output "RESULT bench $pk start $(Get-Date -Format HH:mm:ss)" + if ($cardName -eq 'gfx1201') { + & $exe --bench-pack --pack $d --batches 5 --batch-log2 24 --device $dev 2>&1 | ForEach-Object { "RESULT $_" } + & $exe --bench-pack --pack $d --batches 5 --batch-log2 24 --device $dev --group-warps 8 2>&1 | Where-Object { $_ -match '^RESULT|error|FAIL' } | ForEach-Object { "RESULT $_" } + } else { + # bit-exactness only: one timed dispatch after the warm-up, the same 2^24 nonces at base 0 as the 5090 and the Mac (about 6 s each at 3 MH/s); the mhs= field is not a number + & $exe --bench-pack --pack $d --batches 1 --batch-log2 24 --device $dev 2>&1 | ForEach-Object { "RESULT $_" } + } +} + +if ($card) { + $body = @{ cards = @(@{ key = $card.key; enabled = [bool]$card.enabled; identities = [int]$card.identities; power_pct = [int]$card.power_pct }) } | ConvertTo-Json -Depth 5 + try { Invoke-RestMethod -Uri ($url + 'api/cards') -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; Write-Output ("RESULT card restored enabled=" + $card.enabled) } catch { Write-Output ("RESULT error card restore: " + $_.Exception.Message) } +} +exit 0 From def920b1ed06a9c9714ee885c319ee15cd49218e Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:58:22 +0000 Subject: [PATCH 087/158] counter-asic-2-node.md: the CPU hash rate across the switch in run 2 (approximate) and the verifier slowdown the era agent measured, to be re-measured after the mixer fix Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-2-node.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index 1ab5164e..d1d00854 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -108,6 +108,8 @@ Binaries rebuilt on ca2-v3 b105a55 (era layout merged on the mixer: `V3_CLASS` = | Forks | sinks `712c1b212091dcdc` on all three nodes, block counts 303 / 303 / 303, one tip each | | Program and cache ready, one CPU core | v2 epoch 0 178 ms, the first v3 epoch 181 ms, an in-day swap 2 ms | +CPU hash rate across the switch, run 2, miner cpu0 (one thread, the Mac shared with other agents' builds, so approximate): the cumulative rate read 0.024 MH/s through the v2 epochs (30 to 151 s), then fell to 0.021 MH/s cumulative by 271 s (100 s under v3), which puts the v3 interval rate near 0.017 MH/s, about 30 percent under v2 on the CPU interpreter (the era's strided windowed loads and the mixer path). The node has no per-block verify timing line; the era agent's measurement of the CPU verifier (`igneum-pow`, 0.604 ms per warp on readwidth against 1.332 ms on 88dafbc, the mixer's `derive_items` path at m = 1) is the number to fix before the publish, and the before/after goes here when the mixer agent's commit lands. + Epoch 0's v2 id is the same in both runs (`8f8806638d59850f`: a v2 program is untouched by the era code, on the chain as in the packs); the v3 ids differ from run 1 because the era draw is now inside the class. The PC 2 suite job for the same tree: `build-20261005-215219` (fork 79bd8e10, main b105a55, published 21:52:19Z; its SUMMARY is the G6 evidence, recorded by the coordinator in the status file). From ae699a4af21b8ce487c391396720768d519abc84 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:58:34 +0000 Subject: [PATCH 088/158] release-0.3.10 plan: the merge to master, the console card's stale commit string, the site build on a conflicted journey.json Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.10.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/plans/release-0.3.10.md b/docs/plans/release-0.3.10.md index dda7f443..b825916e 100644 --- a/docs/plans/release-0.3.10.md +++ b/docs/plans/release-0.3.10.md @@ -484,4 +484,6 @@ The next cut (0.3.11), decided by the coordinator on 5 October 2026 night: | PC 2's prover after the 0.3.10 restart: every assigned shard fails at once with `CudaClientError: Connect(PermissionDenied)` (section 8), where 0.3.9's PC 2 proved shards from 17:36Z. The host, the pinned ids and the CUDA backend are detected as before; the connect that fails is the SP1 CUDA prover's client socket. The aggregation-cost agent's job `agg-cost-pc2-2` ran as root in WSL minutes before and its lines show no CUDA or socket change; the 0.3.10 app's prover path changed only to read the program ids (`--mode id`). Unexplained; handed to the coordinator and the Counter ASIC coordinator (PC 2's measurement agents) | open: PC 2 proves nothing until it is found; a restart of the WSL distro or of the CUDA prover service is the first thing to try | | PC 37ba0461 (the US laptop) started the 0.3.10 install at 21:40:53Z, stopped its miners and node at 21:41:16Z and had not come back by 21:56Z: the per-user installer on the owner's machine, nothing to drive remotely | open: the console shows when it returns; its 0.3.10 line and worker start are read then | | `dl/public/igneum-downloads.json` (the unsigned index the site's download page reads) alternates at the edge between the new bytes and the previous ones for over 20 minutes after the deploy (one fetch byte-identical at 21:52Z, the next three not): different edge nodes behind one hostname. The two signed manifests were byte-identical and verified from the first check. The ship's verify step counts it as a failure and refuses to post the console item, so the item was posted with `--from console` | open: the verify should accept the index after the signed manifests pass, or retry it for longer; the site serves the previous version's buttons from a stale edge until it settles | +| The console's machine card keeps a machine's LAST non-empty node commit string: PC 1's card read `node 2.1.0-a24ab01a` for 17 minutes after its node had restarted as the PC build (`igneumd/2.1.0`, no commit), while PC 2's card read `2.1.0` at once; the Mac's card kept node 1's a24ab01a after node 1 moved to 21d4c73c. A card's commit string is therefore not a fact about the running node until the app re-reads it; the node log is | open: the card should show the string the app last READ, with its time, or nothing | +| The site build, run on a tree with conflict markers in `site/journey.json`, fails silently and leaves the markers (it reads that file back, the non-idempotence above): the first merge commit of this cut to master carried markers in two site files for one minute and was amended (the pre-push hook would have refused it, but the check must not depend on the hook) | fixed by hand here; the build should refuse a journey.json that does not parse and say so | | The PC-built node binaries embed no commit (the build inputs zip has no git dir, `build-info` falls back to the bare version): the console shows PC 1 and PC 2 as node `2.1.0` with no commit after 0.3.10, as the 0.3.6 PC build did. The build inputs manifest records the fork commit (2f88a82f and later) | open: a commit stamp through the build job (`jobbuild.rs`) is an app change, not tonight | From e3b7ac869bc6c29b363ecbf29186e8d810f82a52 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:59:18 +0000 Subject: [PATCH 089/158] counter-asic-2-node.md: the cache merge done and checked, the mixer fix owed (54bbfcc is not it; its merge conflicts in bench-log.md and packbench.swift only) Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-2-node.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index d1d00854..b7029838 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -131,6 +131,7 @@ The PC 2 suite job for the same tree: `build-20261005-215219` (fork 79bd8e10, ma - The Metal worker has no class v3 generator (it regenerates from the seed in Swift): the integration branch adds generator 3 there, or the Mac mines v3 with the OpenCL worker from a pack. - The GPU workers' class refusal was checked by the C test of `pf_pack_class_ok` and the syntax of both hosts, not by a live worker on a v3 pack: the integration's bit-exact gate (G1) is where a real worker first builds a v3 pack. - `next_pair` keeps the current era seed for the next epoch; an epoch boundary that is also an era boundary (once per 180 days) would prepare the wrong era, and the job line then names the right one, so the worker refuses the prepared pair and the miner prepares again (one wasted compile, no wrong block). The VDF era seed replaces the stand-in before this matters. -- Owed: the ca2-cache code (2de19e5, measured-not-adopted, hot `None`) once the cache agent rebases it onto b105a55: its merge against the era and mixer tip conflicts in 8 files (35 hunks in generator.rs, emit.rs, tests/packs.rs, verify.rs, packfile.h, accept.rs, packbench.swift, bench-log.md), aborted here on 5 October 2026 at 21:45Z; the gate did not need it. +- Done 22:05Z: ca2-cache rebased as 1950661 fast-forwarded (the first attempt, 2de19e5 on 464d6e1, conflicted in 8 files and was aborted); igneum-pow 53 + 19 tests and the packfile test pass; the fork's kaspa-pow, miner and kaspad check clean against the merged crate (seam unchanged). `V3_CLASS = { era: None, hot: None, ..LoadClass::MX4 }`. +- Owed: the mixer agent's verifier fix (the 2.2x on the v2 path at m = 1). ca2-mixer's tip 54bbfcc (22:10Z) carries the MX8 candidate, tests/mixer.rs, tests/scratch.rs and the measure docs, not the fix; a merge of it into ca2-v3 conflicts in docs/bench-log.md and proto-metal/packbench.swift only (generator.rs and verify.rs auto-merge). Merged when the fix commit arrives, then a rebuild and gate run 3 if asked. - Owed (0.3.12, coordinator's ask of 5 October 2026 22:20Z): per-day dataset reuse in the CUDA and OpenCL workers (a `Day` object shared by consecutive pairs, the cache freed after the build); the Metal worker already keys datasets by day. Until then the iGPU tier mines v3 with a dataset rebuild per epoch on those two workers. - Wire compatibility: `RpcPowEpochInfo` gained five fields in its Borsh form (wRPC) and five proto fields (gRPC); the gRPC side reads an old node's zeros as v2 / never / none; the Borsh form is versioned by `GetBlockTemplateResponse` (version 2 carries the whole struct), so a 0.3.11 wRPC client against a 0.3.10 node reads short: the miner uses gRPC, the console reads JSON (serde defaults). From 23ab08202f3bfacedde39144a2091e6370c19688 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:00:37 +0000 Subject: [PATCH 090/158] plan: the 12 GB floor's cause (the shipped sp1-gpu-server refuses cards under 24 GB and allocates every prover at Setup; the prover-floor agent's source read) Co-Authored-By: Claude Fable 5.1 --- docs/plans/proving-v1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/proving-v1.md b/docs/plans/proving-v1.md index 06319a34..e213f37b 100644 --- a/docs/plans/proving-v1.md +++ b/docs/plans/proving-v1.md @@ -125,7 +125,7 @@ The GPU server of SP1 6.8.1 sets the memory, not the shard: a floor of 13.9 GB f | 32 GB (RTX 5090) | the prototype shard, 28.3 GB, 10.8 s; the v1 shard 20.4 GB, 4.3 s | the prototype shard 30.1 GB, 33 s; the v1 shard 22.2 GB, 13.2 s | on, mine and prove, today | | 24 GB (RTX 4090, 3090) | the v1 shard 20.4 GB; the prototype shard does NOT fit (28.3 GB) | the v1 shard 22.2 GB measured on the 5090's allocation (2.3 GB spare on a 24 GB card; approximate for the card itself) | on, mine and prove, with the line "until the devnet's fee switch its shards are the prototype size, which needs 32 GB, so this card proves from the switch on" | | 16 GB (RTX 5080, 4080) | an empty shard only (13.9 GB) | nothing (15.7 GB for an empty shard, no room for the display) | off, with the line | -| 12 GB (RTX 3060, 4070) | nothing: the floor is 13.9 GB | nothing | off; the project lead's "make sure we can prove on 12 GB cards" is OPEN: it needs a prover build with a smaller floor (an SP1 release or a fork of its GPU server), measured on a 12 GB card; the curve above is the evidence for that ask (D2) | +| 12 GB (RTX 3060, 4070) | nothing: the floor is 13.9 GB, and the shipped server refuses the card outright | nothing | off; the project lead's "make sure we can prove on 12 GB cards" is OPEN and in work: the prover-floor agent (branch prover-floor, 5 October night) read SP1 v6.8.1's GPU server source (`sp1-gpu/crates/prover_components/src/builder.rs` lines 35 to 39): it reads the card's memory, adds 4 and panics under 24 ("Unsupported GPU memory ... must be at least 24GB"), and builds its core (ELEMENT_THRESHOLD 2^28 + 2^27 elements + 2^21), recursion (2^27), shrink (2^25) and wrap (85 M element) provers at Setup whatever the mode, which is the 13.9 GB floor; no knob reaches them, so the fix is a server rebuilt from source on PC 2 (WSL2, nvcc 12.8, CUDA_ARCHS=120) with those sizes cut, measured on the same fixtures and recipe as the curve above (D2 carries the curve) | | under 12 GB | nothing | nothing | off, mine only | | AMD-only and Apple machines | nothing on the GPU: no zkVM proves on an AMD GPU today (`docs/analysis/amd-proving.md`, branch amd-prove); the CPU prover is about 5 minutes a shard at a 30 GB RSS whatever the shard size (PC 1, bench-log "the SP1 CPU prover on PC 1") | | off, "mines and does not prove"; the only non-NVIDIA path with a shipped backend is RISC Zero's Metal prover behind the `ProofSystem` seam (a second guest and pinned id, a verifier for both formats, no shared aggregation): an open item, not 0.3.11 | From a99fa6375ff60f23cdf96fc87d6db83e46a4e5d8 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:13:04 +0000 Subject: [PATCH 091/158] scratch soundness (layer 3 of Counter ASIC 2.0): verify.rs scratch trace hook; tests/scratch.rs: rewrite and fill bijections, written-word bias and re-hit rates per class, hand-built slot edge programs against a hand model, static scratch-mask check over every emitted kernel of every scr pack with six deliberate breaks, 200-program CPU fuzz and pack writer for the Metal runs; packbench --batch-base for the launch-level nonce wrap Co-Authored-By: Claude Fable 5.1 --- igneum-pow/src/verify.rs | 44 ++- igneum-pow/tests/scratch.rs | 765 ++++++++++++++++++++++++++++++++++++ proto-metal/packbench.swift | 14 +- 3 files changed, 815 insertions(+), 8 deletions(-) create mode 100644 igneum-pow/tests/scratch.rs diff --git a/igneum-pow/src/verify.rs b/igneum-pow/src/verify.rs index ba7338b7..91f9ee31 100644 --- a/igneum-pow/src/verify.rs +++ b/igneum-pow/src/verify.rs @@ -77,6 +77,22 @@ pub struct ScratchModel { data: Vec<[u32; 3]>, pub reads: usize, pub writes: usize, + /// Soundness tests (`tests/scratch.rs`, `docs/analysis/scratch-soundness.md`): when `Some`, every + /// read-modify-write is appended as it happened. `None` on every verification path. + pub trace: Option>, +} + +/// One scratch read-modify-write as the interpreter saw it (variant 5 soundness tests). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ScratchEvent { + pub lane: u8, + pub slot: u32, + /// The slot had been written earlier in this unit (a re-hit): the words read were a rewrite, not the fill. + pub hit: bool, + pub read: [u32; 3], + /// The fold result, the new value of `dst`. + pub x: u32, + pub written: [u32; 3], } impl ScratchModel { @@ -87,6 +103,7 @@ impl ScratchModel { data: vec![[0; 3]; LANES * slots_per_lane], reads: 0, writes: 0, + trace: None, } } /// Read slot `slot` of `lane`, then rewrite it from the fold result `x`. Returns the three words read. @@ -103,7 +120,11 @@ impl ScratchModel { ] }; let x = fold_words(dst, &w); - self.data[i] = scratch_rewrite(x, &w); + let out = scratch_rewrite(x, &w); + if let Some(t) = self.trace.as_mut() { + t.push(ScratchEvent { lane: lane as u8, slot, hit: self.written[i], read: w, x, written: out }); + } + self.data[i] = out; self.written[i] = true; self.reads += 1; self.writes += 1; @@ -305,6 +326,19 @@ pub fn interpret_warp(program: &Program, base_nonce: u32, ds: &DatasetSource) -> /// [`interpret_warp`] with explicit init words `I` (section 1.6 of the spec). The packs use `I = program.seed`; /// a block uses `I = bind::block_init_words(H, nonce)`. pub fn interpret_warp_init(program: &Program, seed: &[u32; 8], base_nonce: u32, ds: &DatasetSource) -> WarpResult { + interpret_warp_scratch(program, seed, base_nonce, ds, false).0 +} + +/// [`interpret_warp_init`] that also returns every scratch read-modify-write of the unit in execution order +/// (lane-minor within an instruction, as the interpreter runs them) when `trace` is set; empty otherwise and for +/// a class without a scratch. For the soundness tests of variant 5 only. +pub fn interpret_warp_scratch( + program: &Program, + seed: &[u32; 8], + base_nonce: u32, + ds: &DatasetSource, + trace: bool, +) -> (WarpResult, Vec) { let mask = ds.mask; let log2 = ds.log2_words; let era = program.class.era; @@ -323,6 +357,11 @@ pub fn interpret_warp_init(program: &Program, seed: &[u32; 8], base_nonce: u32, let mut idx = [0u32; LANES]; let mut val = [0u32; LANES]; let mut scratch = if program.has_scratch() { Some(ScratchModel::new(program.class.scratch_slots_per_lane())) } else { None }; + if trace { + if let Some(m) = scratch.as_mut() { + m.trace = Some(Vec::new()); + } + } let slot_mask = program.class.scratch_slot_mask(); if program.has_hot() { let h = ds.hot.as_ref().expect("a hot-table program needs the epoch's hot table on the dataset source"); @@ -348,7 +387,8 @@ pub fn interpret_warp_init(program: &Program, seed: &[u32; 8], base_nonce: u32, let hi = r[4][lane] ^ r[5][lane].rotate_left(9) ^ r[6][lane].rotate_left(18) ^ r[7][lane].rotate_left(27); hashes[lane] = ((hi as u64) << 32) | lo as u64; } - WarpResult { hashes, items_derived } + let events = scratch.and_then(|m| m.trace).unwrap_or_default(); + (WarpResult { hashes, items_derived }, events) } #[inline(always)] diff --git a/igneum-pow/tests/scratch.rs b/igneum-pow/tests/scratch.rs new file mode 100644 index 00000000..d00bbfe4 --- /dev/null +++ b/igneum-pow/tests/scratch.rs @@ -0,0 +1,765 @@ +//! Soundness tests of layer 3 of `docs/plans/counter-asic-2.md`: the per-warp scratch with read-modify-writes +//! (variant 5 of the read-width experiment, `LoadClass::scratch(k, kb)`). Analysis and results: +//! `docs/analysis/scratch-soundness.md`. Every test is parametric over the class's slot count +//! (`scratch_slots_per_lane()`), so the 32 and 128 KiB geometries and any later one run the same checks. +//! +//! What runs under plain `cargo test`: +//! 1. `rewrite_is_a_bijection_of_the_fold_value`, `fill_is_a_bijection_of_the_nonce`: the written words as +//! functions (question 1). +//! 2. `written_words_unbiased_and_rehit_rates`: bit bias of every written word over 2^11 units x 3 seeds per class +//! (the TESTS.md section 3 shape), and the measured slot re-hit rate against the birthday formula (question 2). +//! 3. `edge_programs_match_the_hand_model`: hand-built programs that drive every read-modify-write of a hash to +//! slot 0, slot MASK, through out-of-range registers, to one slot per lane, alternating two slots, and 16 +//! read-modify-writes per iteration on one slot; the interpreter against an independent hand model, and the +//! hand model shown to have teeth (question 3, CPU half). +//! 4. `scr_packs_regenerate_and_pass_the_static_scratch_check`: every emitted kernel of every scr pack under +//! `proto-cuda/packs-readwidth` regenerates from its program.json and passes the static scratch-mask check; +//! the check is shown to fail on four deliberate breaks (question 4). +//! 5. `fuzz_scr_programs_cpu`: 200 generated scratch programs over the six classes, generator contract on every +//! instruction, 4 units each at base nonces across the 32-bit range including the wrap; with +//! `IGNEUM_SCRATCH_PACKS_OUT=` it also writes the packs (and the edge packs) for the Metal runs of +//! `proto-metal/packbench` (question 3 GPU half, question 4, `TESTS.md` section 9 shape). + +use igneum_pow::emit::{ + cuda_kernel, cuda_kernel_bound, export_pack, metal_program, metal_program_bound, opencl_kernel, + opencl_kernel_bound, vectors_json, LoadSource, +}; +use igneum_pow::generator::{ + generate_class, generate_from_seed_bytes_class, Instr, LoadClass, Op, Program, GENERATOR_VERSION, INSTR_COUNT, + ITERATIONS, LANES, +}; +use igneum_pow::seed::{seed_words_from_bytes, SplitMix64}; +use igneum_pow::verify::{ + fold_words, interpret_warp_scratch, scratch_fill, scratch_rewrite, splitmix32, DatasetMode, DatasetSource, + Epoch, ScratchEvent, FOLD_MUL, FOLD_ROT, +}; +use serde_json::Value; +use std::collections::HashMap; +use std::path::PathBuf; + +/// The classes under study: the two capped geometries (32 and 128 KiB per warp: 64 and 256 slots per lane) at the +/// RMW shares the readwidth branch measures. +const CLASSES: [&str; 6] = ["scr2k32", "scr4k32", "scr8k32", "scr2k128", "scr4k128", "scr8k128"]; + +fn class(name: &str) -> LoadClass { + LoadClass::parse(name).unwrap_or_else(|| panic!("class {name}")) +} + +// --------------------------------------------------------------------------------------------------------------- +// 1. The written words as functions (question 1) +// --------------------------------------------------------------------------------------------------------------- + +/// For a fixed slot content `w`, each of the three rewritten words is a bijection of the fold value `x` +/// (`x ^ w1`, `rotl(x, 7) ^ w2`, `x + w0`), so the rewrite is injective in `x` and a uniform `x` gives a uniform +/// word in every position. Checked over 2^16 consecutive `x` for 16 random `w`. +#[test] +fn rewrite_is_a_bijection_of_the_fold_value() { + let mut rng = SplitMix64::new(0x7363_7261_7463_6801); + for _ in 0..16 { + let w = [rng.next() as u32, rng.next() as u32, rng.next() as u32]; + let x0 = rng.next() as u32; + let mut seen = [vec![false; 1 << 16], vec![false; 1 << 16], vec![false; 1 << 16]]; + for i in 0..(1u32 << 16) { + let x = x0.wrapping_add(i); + let out = scratch_rewrite(x, &w); + for j in 0..3 { + // a bijection of x maps 2^16 consecutive x to 2^16 distinct words; the low 16 bits alone are + // distinct for the xor words (x ^ c) and for the add word (x + c), since both act on the low 16 + // bits as bijections of the low 16 bits of x; the rotl word is checked on its rotated-back bits + let key = if j == 1 { out[j].rotate_right(7) & 0xffff } else { out[j] & 0xffff }; + assert!(!seen[j][key as usize], "word {j} repeats inside 2^16 consecutive x"); + seen[j][key as usize] = true; + } + } + } + // The rewrite inverts: from the old content and any ONE written word the fold value is recovered, so a + // rewritten slot carries exactly 32 bits of new state (the point of question 2's arithmetic). + let w = [0x1234_5678, 0x9abc_def0, 0x0fed_cba9]; + let x = 0xdead_beef; + let out = scratch_rewrite(x, &w); + assert_eq!(out[0] ^ w[1], x); + assert_eq!((out[1] ^ w[2]).rotate_right(7), x); + assert_eq!(out[2].wrapping_sub(w[0]), x); +} + +/// For a fixed (seed, slot, j) the fill is a bijection of the lane nonce: `splitmix32` is a bijection of its +/// 32-bit input and the input `((base + lane) ^ s) + c` is a bijection of `base + lane`. Over 2^16 consecutive +/// nonces no fill word repeats, for 8 slots x 3 words. +#[test] +fn fill_is_a_bijection_of_the_nonce() { + let seed = seed_words_from_bytes(b"igneum-genesis"); + for slot in [0u32, 1, 63, 64, 255, 1023, 2047] { + for j in 0..3u32 { + let mut words: Vec = (0..(1u32 << 16)).map(|n| scratch_fill(&seed, n, 0, slot, j)).collect(); + words.sort_unstable(); + words.dedup(); + assert_eq!(words.len(), 1 << 16, "slot {slot} word {j}: fill words of 2^16 consecutive nonces are distinct"); + } + } + // base + lane is the lane nonce: the fill of lane l at base b is the fill of lane 0 at base b + l + assert_eq!(scratch_fill(&seed, 0x1000, 7, 5, 2), scratch_fill(&seed, 0x1007, 0, 5, 2)); + // and it wraps with the nonce: base 0xffffffe0, lane 31 is nonce 0xffffffff; lane 32 would be nonce 0 + assert_eq!(scratch_fill(&seed, 0xffff_ffe0, 32, 5, 2), scratch_fill(&seed, 0, 0, 5, 2)); + // the three word positions of one slot and nonce are three different permutation outputs + let f: Vec = (0..3).map(|j| scratch_fill(&seed, 12345, 7, 17, j)).collect(); + assert!(f[0] != f[1] && f[1] != f[2] && f[0] != f[2]); +} + +// --------------------------------------------------------------------------------------------------------------- +// 2. Uniformity of the written words and the slot re-hit rate (questions 1 and 2) +// --------------------------------------------------------------------------------------------------------------- + +/// Birthday arithmetic: the expected number of distinct slots after `n` uniform draws from `s` slots. +fn expected_distinct(s: usize, n: usize) -> f64 { + let s = s as f64; + s * (1.0 - (1.0 - 1.0 / s).powi(n as i32)) +} + +struct ClassStats { + units: usize, + events: usize, + hits: usize, + /// ones count per bit of the written words, 3 x 32 + ones: [[u64; 32]; 3], + /// ones count per bit of written XOR read (the change the rewrite makes to the slot) + delta_ones: [[u64; 32]; 3], + /// re-hit depth histogram: how many earlier RMWs the slot had seen in this unit (0 = first touch) + depth: Vec, + max_depth: usize, + /// how often each slot index was addressed (the slot comes from a register's low bits) + slot_hist: Vec, +} + +fn class_stats(name: &str, seeds: &[&str], units_per_seed: usize) -> ClassStats { + let c = class(name); + let mut st = ClassStats { + units: 0, + events: 0, + hits: 0, + ones: [[0; 32]; 3], + delta_ones: [[0; 32]; 3], + depth: vec![0; 256], + max_depth: 0, + slot_hist: vec![0; c.scratch_slots_per_lane()], + }; + let ds = DatasetSource::new("2026-10-03", DatasetMode::ClosedForm, 28); + for seed in seeds { + let p = generate_class(seed, c); + assert_eq!(p.scratch_ops_per_hash(), c.scratch_slots() * ITERATIONS); + for u in 0..units_per_seed { + let base = (u as u32).wrapping_mul(32).wrapping_add(0x4000_0000); + let (_, ev) = interpret_warp_scratch(&p, &p.seed, base, &ds, true); + assert_eq!(ev.len(), p.scratch_ops_per_hash() * LANES); + let mut count: HashMap<(u8, u32), usize> = HashMap::new(); + for e in &ev { + assert!(e.slot < c.scratch_slots_per_lane() as u32, "slot inside the lane's scratch"); + let d = count.entry((e.lane, e.slot)).or_insert(0); + assert_eq!(e.hit, *d > 0, "hit flag agrees with the unit's own history"); + assert_eq!(e.written, scratch_rewrite(e.x, &e.read)); + if !e.hit { + let fill = [ + scratch_fill(&p.seed, base, e.lane as u32, e.slot, 0), + scratch_fill(&p.seed, base, e.lane as u32, e.slot, 1), + scratch_fill(&p.seed, base, e.lane as u32, e.slot, 2), + ]; + assert_eq!(e.read, fill, "a first touch reads the fill"); + } + st.depth[(*d).min(255)] += 1; + st.max_depth = st.max_depth.max(*d); + st.slot_hist[e.slot as usize] += 1; + *d += 1; + st.events += 1; + st.hits += e.hit as usize; + for j in 0..3 { + for b in 0..32 { + st.ones[j][b] += ((e.written[j] >> b) & 1) as u64; + st.delta_ones[j][b] += (((e.written[j] ^ e.read[j]) >> b) & 1) as u64; + } + } + } + st.units += 1; + } + } + st +} + +/// Bit bias of every written word (and of the change each rewrite makes) within 6 sigma of a fair coin, over +/// 3 seeds x 2^11 units per class (131,072 hashes per seed set); the slot re-hit rate against the birthday +/// formula within 3 percent relative. The table printed here is the one in the analysis. +#[test] +fn written_words_unbiased_and_rehit_rates() { + let seeds = ["igneum-genesis", "igneum-genesis/stats1", "igneum-genesis/stats2"]; + let units = 1usize << 11; + println!("class | slots/lane | RMW/hash | events | re-hits | re-hit % | birthday % | slot chi2 z (spread) | max depth | max bias sigma | max delta bias sigma"); + for name in CLASSES { + let c = class(name); + let st = class_stats(name, &seeds, units); + let n = st.events as f64; + let sigma = (n / 4.0).sqrt(); + let mut worst = 0.0f64; + let mut worst_delta = 0.0f64; + for j in 0..3 { + for b in 0..32 { + let z = (st.ones[j][b] as f64 - n / 2.0).abs() / sigma; + let zd = (st.delta_ones[j][b] as f64 - n / 2.0).abs() / sigma; + assert!(z <= 6.0, "{name}: written word {j} bit {b} biased: {z:.2} sigma"); + assert!(zd <= 6.0, "{name}: rewrite delta word {j} bit {b} biased: {zd:.2} sigma"); + worst = worst.max(z); + worst_delta = worst_delta.max(zd); + } + } + let per_lane_hash = c.scratch_slots() * ITERATIONS; + let s = c.scratch_slots_per_lane(); + let exp_hits = per_lane_hash as f64 - expected_distinct(s, per_lane_hash); + let exp_pct = 100.0 * exp_hits / per_lane_hash as f64; + let got_pct = 100.0 * st.hits as f64 / st.events as f64; + // chi-square of the slot histogram against uniform (df = s - 1): the slot is a register's low bits, and + // the measured re-hit rate runs above the uniform birthday rate (the finding of the analysis, question 2) + let expect_per_slot = n / s as f64; + let chi2: f64 = st.slot_hist.iter().map(|&h| (h as f64 - expect_per_slot).powi(2) / expect_per_slot).sum(); + let chi2_z = (chi2 - (s as f64 - 1.0)) / (2.0 * (s as f64 - 1.0)).sqrt(); + let hot = *st.slot_hist.iter().max().unwrap() as f64 / expect_per_slot; + let cold = *st.slot_hist.iter().min().unwrap() as f64 / expect_per_slot; + println!( + "{name} | {s} | {per_lane_hash} | {} | {} | {got_pct:.2} | {exp_pct:.2} | {chi2_z:.1} (hottest slot {hot:.2}x, coldest {cold:.2}x) | {} | {worst:.2} | {worst_delta:.2}", + st.events, st.hits, st.max_depth + ); + // a regression band, not a uniformity claim: the rate sits between the uniform birthday rate and twice it + assert!( + got_pct >= 0.9 * exp_pct && got_pct <= 2.0 * exp_pct, + "{name}: re-hit rate {got_pct:.2}% against birthday {exp_pct:.2}%" + ); + // depth histogram: the number of earlier RMWs a re-hit slot had seen in the unit + let shown: Vec = st.depth.iter().take(st.max_depth + 1).enumerate().map(|(d, n)| format!("{d}:{n}")).collect(); + println!(" depth histogram {}", shown.join(" ")); + } +} + +// --------------------------------------------------------------------------------------------------------------- +// 3. Hand-built edge programs against an independent hand model (question 3, CPU half) +// --------------------------------------------------------------------------------------------------------------- + +fn ins(op: Op, dst: u8, src: u8) -> Instr { + Instr { op, dst, src, src2: 0, imm: 0, imm2: 0, rot: 1, bit: 0, mask: 1, width: 1 } +} +fn add_imm(dst: u8, src: u8, imm: u32) -> Instr { + Instr { op: Op::Add, dst, src, src2: 0, imm, imm2: imm, rot: 1, bit: 0, mask: 1, width: 1 } +} + +/// A hand-built program of class `c` named `name` (its seed is the name, so its fill words and init words are +/// its own). These bypass the generator and the acceptance rule, like `TESTS.md` section 2; `sub r, r` zeroes a +/// register as the Swift edge set does. +fn edge(name: &str, c: LoadClass, instrs: Vec) -> Program { + let seed_string = format!("igneum-scratch-edge/{name}"); + let seed_bytes = seed_string.as_bytes().to_vec(); + let k = instrs.iter().filter(|i| i.op == Op::Scratch).count(); + assert_eq!(k, c.scratch_slots(), "{name}: the class carries the program's scratch count"); + Program { + seed: seed_words_from_bytes(&seed_bytes), + seed_string, + seed_bytes, + generator: GENERATOR_VERSION, + attempt: 0, + class: c, + instrs, + } +} + +/// The edge set for a scratch of `kb` KiB per warp. Each entry: (name, what it drives, program). +fn edge_programs(kb: u8) -> Vec<(String, &'static str, Program)> { + let m = LoadClass::scratch(1, kb).scratch_slot_mask(); + let dsts = [2u8, 3, 4, 5, 6, 7, 0, 2, 3, 4, 5, 6, 7, 0, 2, 3]; + let scr = |n: usize, src: u8| -> Vec { (0..n).map(|i| ins(Op::Scratch, dsts[i], src)).collect() }; + let mut v = Vec::new(); + // every RMW of the hash to slot 0 through a zero register: 64 dependent RMWs on one slot per lane + let mut p = vec![ins(Op::Sub, 1, 1)]; + p.extend(scr(8, 1)); + v.push(("slot0".to_string(), "r1 = 0: every RMW to slot 0", edge(&format!("slot0/k{kb}"), LoadClass::scratch(8, kb), p))); + // slot MASK through the in-range register MASK + let mut p = vec![ins(Op::Sub, 1, 1), ins(Op::Sub, 2, 2), add_imm(1, 2, m)]; + p.extend(scr(8, 1)); + v.push(("slotmask".to_string(), "r1 = MASK: every RMW to the last slot", edge(&format!("slotmask/k{kb}"), LoadClass::scratch(8, kb), p))); + // slot MASK through the out-of-range register 0xffffffff + let mut p = vec![ins(Op::Sub, 1, 1), ins(Op::Sub, 2, 2), add_imm(2, 1, 1), ins(Op::Sub, 1, 2)]; + p.extend(scr(8, 1)); + v.push(("ones".to_string(), "r1 = 0xffffffff: masked to the last slot", edge(&format!("ones/k{kb}"), LoadClass::scratch(8, kb), p))); + // slot 0 through the out-of-range register MASK + 1 + let mut p = vec![ins(Op::Sub, 1, 1), ins(Op::Sub, 2, 2), add_imm(1, 2, m.wrapping_add(1))]; + p.extend(scr(8, 1)); + v.push(("maskplus1".to_string(), "r1 = MASK + 1: masked to slot 0", edge(&format!("maskplus1/k{kb}"), LoadClass::scratch(8, kb), p))); + // 16 RMWs per iteration on slot 0: 128 dependent RMWs on one slot per lane per hash + let mut p = vec![ins(Op::Sub, 1, 1)]; + p.extend(scr(16, 1)); + v.push(("sixteen".to_string(), "16 RMWs per iteration on slot 0", edge(&format!("sixteen/k{kb}"), LoadClass::scratch(16, kb), p))); + // one slot per lane from the init words: lanes with equal slots would show any cross-lane aliasing + // (r5 is the slot register and is never a destination here) + let p: Vec = [0u8, 1, 2, 3, 4, 6, 7, 0].iter().map(|&d| ins(Op::Scratch, d, 5)).collect(); + v.push(("lanevar".to_string(), "r5 never written: one init-dependent slot per lane", edge(&format!("lanevar/k{kb}"), LoadClass::scratch(8, kb), p))); + // alternating slot 0 and slot MASK inside one iteration + let mut p = vec![ins(Op::Sub, 1, 1), ins(Op::Sub, 2, 2), add_imm(2, 1, m)]; + for (i, &d) in [3u8, 4, 5, 6, 7, 0, 3, 4].iter().enumerate() { + // r1 and r2 hold the two slots and are never destinations + p.push(ins(Op::Scratch, d, if i % 2 == 0 { 1 } else { 2 })); + } + v.push(("twoslots".to_string(), "slot 0 and slot MASK alternating", edge(&format!("twoslots/k{kb}"), LoadClass::scratch(8, kb), p))); + v +} + +/// The hand model: a second, minimal interpreter for the ops the edge programs use (sub, add, scratch), with its +/// own slot store keyed by (lane, slot). `mutate` swaps the rewrite's words to show the comparison has teeth. +fn hand_model(p: &Program, base: u32, mutate: bool) -> [u64; 32] { + let seed = &p.seed; + let m = p.class.scratch_slot_mask(); + let mut r = [[0u32; LANES]; 8]; + for lane in 0..LANES { + let nonce = base.wrapping_add(lane as u32); + for i in 0..8 { + let mut x = nonce ^ seed[i]; + x = x.wrapping_add(0x9e3779b9u32.wrapping_mul(i as u32 + 1)); + x = splitmix32(x); + r[i][lane] = x ^ seed[(i + 1) & 7]; + } + } + let mut store: HashMap<(usize, u32), [u32; 3]> = HashMap::new(); + for _ in 0..ITERATIONS { + let sel = r[0]; + for ins in &p.instrs { + let (d, a) = (ins.dst as usize, ins.src as usize); + match ins.op { + Op::Sub => { + for lane in 0..LANES { + r[d][lane] = r[d][lane].wrapping_sub(r[a][lane]); + } + } + Op::Add => { + for lane in 0..LANES { + let c = if (sel[lane] >> ins.bit) & 1 != 0 { ins.imm2 } else { ins.imm }; + r[d][lane] = r[d][lane].wrapping_add(r[a][lane]).wrapping_add(c); + } + } + Op::Scratch => { + for lane in 0..LANES { + let slot = r[a][lane] & m; + let w = *store.entry((lane, slot)).or_insert_with(|| { + let mut f = [0u32; 3]; + for j in 0..3u32 { + // the fill, written out in full rather than through verify::scratch_fill + let n = base.wrapping_add(lane as u32); + f[j as usize] = splitmix32( + (n ^ seed[j as usize]) + .wrapping_add(slot.wrapping_mul(0x9E37_79B1)) + .wrapping_add((j + 1).wrapping_mul(0x85EB_CA77)), + ); + } + f + }); + let mut x = r[d][lane] ^ w[0]; + x = x.rotate_left(FOLD_ROT).wrapping_mul(FOLD_MUL) ^ w[1]; + x = x.rotate_left(FOLD_ROT).wrapping_mul(FOLD_MUL) ^ w[2]; + r[d][lane] = x; + let out = if mutate { + [x.rotate_left(7) ^ w[2], x ^ w[1], x.wrapping_add(w[0])] + } else { + [x ^ w[1], x.rotate_left(7) ^ w[2], x.wrapping_add(w[0])] + }; + store.insert((lane, slot), out); + } + } + other => panic!("the hand model does not implement {other:?}"), + } + } + } + let mut out = [0u64; 32]; + for lane in 0..LANES { + let lo = r[0][lane] ^ r[1][lane].rotate_left(7) ^ r[2][lane].rotate_left(14) ^ r[3][lane].rotate_left(21); + let hi = r[4][lane] ^ r[5][lane].rotate_left(9) ^ r[6][lane].rotate_left(18) ^ r[7][lane].rotate_left(27); + out[lane] = ((hi as u64) << 32) | lo as u64; + } + out +} + +/// The four unit bases of every edge vector: 0 and 32 (two consecutive units, the pair a one-warp persistent +/// launch runs on one arena), a unit straddling 2^31, and the unit that wraps past 2^32. +const EDGE_BASES: [u32; 4] = [0, 32, 0x7fff_fff0, 0xffff_ffe0]; + +#[test] +fn edge_programs_match_the_hand_model() { + let ds = DatasetSource::new("2026-10-03", DatasetMode::ClosedForm, 24); + let mut cases = 0; + for kb in [32u8, 128] { + for (name, what, p) in edge_programs(kb) { + let slots = p.class.scratch_slots_per_lane(); + for base in EDGE_BASES { + let (res, ev) = interpret_warp_scratch(&p, &p.seed, base, &ds, true); + let hand = hand_model(&p, base, false); + assert_eq!(res.hashes, hand, "{name} k{kb} base {base:#x}: interpreter against the hand model ({what})"); + assert_ne!(res.hashes, hand_model(&p, base, true), "{name} k{kb}: the comparison has teeth"); + // the slots the trace saw are the ones the program was built to drive + let slot_set: std::collections::BTreeSet = ev.iter().map(|e| e.slot).collect(); + let m = (slots - 1) as u32; + match name.as_str() { + "slot0" | "maskplus1" | "sixteen" => assert_eq!(slot_set.into_iter().collect::>(), vec![0]), + "slotmask" | "ones" => assert_eq!(slot_set.into_iter().collect::>(), vec![m]), + "twoslots" => assert_eq!(slot_set.into_iter().collect::>(), vec![0, m]), + "lanevar" => { + for e in &ev { + assert!(e.slot <= m); + } + } + _ => unreachable!(), + } + // the chain depth on the driven slot: every RMW after the first per lane is a re-hit + let per_lane = p.scratch_ops_per_hash(); + let hits = ev.iter().filter(|e| e.hit).count(); + let expected_hits = match name.as_str() { + "twoslots" => (per_lane - 2) * LANES, + _ => (per_lane - 1) * LANES, + }; + assert_eq!(hits, expected_hits, "{name} k{kb}: re-hits"); + cases += 1; + } + } + } + assert_eq!(cases, 2 * 7 * 4); +} + +// --------------------------------------------------------------------------------------------------------------- +// 4. The static scratch check over every emitted kernel of every scr pack (question 4) +// --------------------------------------------------------------------------------------------------------------- + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Dialect { + Metal, + Cuda, + OpenCl, +} + +/// The static scratch check: every scratch read-modify-write in an emitted kernel has the one masked form the +/// emitter writes, the arena is the lane's own `slots x 4` words, the tag is `salt + unit`, and nothing else +/// touches the scratch. Like the dataset mask check of `TESTS.md` section 5 and `tests/packs.rs`, a text check: +/// the guarantee is that the emitter has one template and it masks. +pub fn scratch_text_check(text: &str, dialect: Dialect, k: usize, slots: usize, kernels: usize) -> Result<(), String> { + assert!(kernels >= 1); + // every count below is per hash kernel; an OpenCL bound file carries igneum_hash and igneum_hash_bound + let k = k * kernels; + assert!(slots.is_power_of_two() && slots >= 1); + let mask = (slots - 1) as u32; + let wpl = slots * 4; + let (u, load, store, ptr) = match dialect { + Dialect::Metal => ("uint", "uint4 v_ = *(device const uint4*)(arena + s_ * 4u);", "*(device uint4*)(arena + s_ * 4u) = uint4(tag, x_ ^ w1_, rotl_imm(x_, 7u) ^ w2_, x_ + w0_); }", "device uint* arena"), + Dialect::Cuda => ("uint32_t", "uint4 v_ = *(const uint4*)(arena + s_ * 4u);", "*(uint4*)(arena + s_ * 4u) = make_uint4(tag, x_ ^ w1_, rotl_imm(x_, 7u) ^ w2_, x_ + w0_); }", "uint32_t* arena"), + Dialect::OpenCl => ("uint", "uint4 v_ = vload4(s_, arena);", "vstore4(IGNEUM_U4(tag, x_ ^ w1_, rotl_imm(x_, 7u) ^ w2_, x_ + w0_), s_, arena); }", "__global uint* arena"), + }; + let count = |needle: &str| text.matches(needle).count(); + let mut errs = Vec::new(); + let mut expect = |what: &str, got: usize, want: usize| { + if got != want { + errs.push(format!("{what}: {got}, expected {want}")); + } + }; + // k slot computations, each masked with exactly the class's mask and immediately followed by the one load form + expect("slot definitions `{ u s_ = r`", count(&format!("{{ {u} s_ = r")), k); + expect("masked slot followed by the load", count(&format!(" & {mask}u; {load}")), k); + expect("stores of the tagged slot", count(store), k); + expect("tag compares", count("(v_.x == tag)"), k); + expect("fill calls (three per RMW)", count("scr_fill(gbase, lane, s_, "), 3 * k); + // the arena: one definition with the class's words per lane, and 2k uses (one load, one store per RMW) + expect("arena definition", count(&format!("{ptr} = scratch + ((size_t)warp_ * 32u + lane) * {wpl}u;")), kernels); + expect("arena mentions (definition + load + store per RMW)", count("arena"), kernels + 2 * k); + expect("tag definition `tag = salt + g_`", count(&format!("{u} tag = salt + g_;")), kernels); + expect("direct scratch indexing", count("scratch["), 0); + expect("scratch pointer arithmetic outside the arena definition", count("scratch +"), kernels); + // no other mask value on a slot: every `s_ = r` line carries the class mask and nothing else carries ` & Nu; uint4 v_` + let any_mask_load = count(&format!("u; {load}")); + expect("loads preceded by some mask (must all be the class mask)", any_mask_load, k); + if errs.is_empty() { + Ok(()) + } else { + Err(errs.join("; ")) + } +} + +fn packs_rw_dir() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda/packs-readwidth") +} + +fn scr_packs() -> Vec { + let mut v: Vec = std::fs::read_dir(packs_rw_dir()) + .unwrap() + .map(|d| d.unwrap().file_name().to_string_lossy().to_string()) + .filter(|n| n.starts_with("scr")) + .collect(); + v.sort(); + v +} + +fn read_pack(pack: &str, file: &str) -> String { + let p = packs_rw_dir().join(pack).join(file); + std::fs::read_to_string(&p).unwrap_or_else(|e| panic!("read {}: {e}", p.display())) +} + +/// Every scr pack regenerates from its program.json (seed bytes, class, day bytes, size) to the same six kernel +/// texts, byte for byte, and every one of those texts passes the static scratch check for the class's k and slot +/// count; the check fails on four deliberate breaks of a copy of the Metal text (mask dropped, mask changed, arena +/// stride changed, a stray scratch access) and on the OpenCL and CUDA twins of the first. +#[test] +fn scr_packs_regenerate_and_pass_the_static_scratch_check() { + let packs = scr_packs(); + assert!(packs.len() >= 6, "the scr packs: {packs:?}"); + let mut checked = 0; + let mut sample_metal = String::new(); + let mut sample_cl = String::new(); + let mut sample_cu = String::new(); + let mut sample_k = 0; + let mut sample_slots = 0; + for pack in &packs { + let j: Value = serde_json::from_str(&read_pack(pack, "program.json")).unwrap(); + let name = j["load_class"].as_str().unwrap(); + let c = class(name); + assert_eq!(&format!("{name}"), pack, "pack directory named after its class"); + let seed = j["seed"].as_str().unwrap(); + let seed_bytes = igneum_pow::bind::unhex(j["seed_bytes"].as_str().unwrap()).unwrap(); + let day_bytes = igneum_pow::bind::unhex(j["dataset"]["day_bytes"].as_str().unwrap()).unwrap(); + let log2 = j["dataset"]["log2_words"].as_u64().unwrap() as u32; + assert_eq!(j["dataset_mode"].as_str().unwrap(), "memory-hard"); + let program = generate_from_seed_bytes_class(seed, &seed_bytes, c); + assert_eq!(program.class, c); + assert_eq!(program.program_id(), u64::from_str_radix(j["program_id"].as_str().unwrap().trim_start_matches("0x"), 16).unwrap()); + let mut dataset = DatasetSource::from_key(seed_words_from_bytes(&day_bytes), DatasetMode::MemoryHard, log2); + dataset.key_bytes = day_bytes; + let e = Epoch { program, dataset }; + let p = &e.program; + let mp = e.dataset.memhard().map(|m| &m.params); + let k = c.scratch_slots(); + let slots = c.scratch_slots_per_lane(); + assert_eq!(p.scratch_ops_per_hash(), k * ITERATIONS); + for (file, text, dialect, kernels) in [ + ("program.metal", metal_program(p, log2, LoadSource::Stored), Dialect::Metal, 1), + ("program_bound.metal", metal_program_bound(p, log2), Dialect::Metal, 1), + ("kernel.cu", cuda_kernel(p, mp), Dialect::Cuda, 1), + ("kernel_bound.cu", cuda_kernel_bound(p, mp), Dialect::Cuda, 1), + ("kernel.cl", opencl_kernel(p, mp), Dialect::OpenCl, 1), + // the OpenCL bound file carries igneum_hash and igneum_hash_bound + ("kernel_bound.cl", opencl_kernel_bound(p, mp), Dialect::OpenCl, 2), + ] { + let on_disk = read_pack(pack, file); + assert_eq!(on_disk, text, "{pack}/{file}: the pack is the emitter's text"); + // scr0 is the persistent control: an arena and a tag, no read-modify-write; the check holds with k = 0 + scratch_text_check(&on_disk, dialect, k, slots, kernels).unwrap_or_else(|e| panic!("{pack}/{file}: {e}")); + checked += 1; + } + // the vectors of the pack are the CPU's + let v: Value = serde_json::from_str(&read_pack(pack, "vectors.json")).unwrap(); + for w in v["warps"].as_array().unwrap() { + let base = w["base_nonce"].as_u64().unwrap() as u32; + let got = e.hash_warp(base); + for (lane, x) in w["expected"].as_array().unwrap().iter().enumerate() { + let want = u64::from_str_radix(x.as_str().unwrap().trim_start_matches("0x"), 16).unwrap(); + assert_eq!(got[lane], want, "{pack}: base {base} lane {lane}"); + } + } + if k == 4 && slots == 64 { + sample_metal = read_pack(pack, "program.metal"); + sample_cl = read_pack(pack, "kernel.cl"); + sample_cu = read_pack(pack, "kernel.cu"); + sample_k = k; + sample_slots = slots; + } + } + assert_eq!(checked, packs.len() * 6); + println!("static scratch check: {checked} kernels over {} scr packs", packs.len()); + + // The deliberate breaks (the watcher rule of CLAUDE.md: a check is trusted once it fails on a known-broken + // case). Each must be caught; the message names what. + assert!(sample_k == 4 && sample_slots == 64, "scr4k32 is in the pack set"); + let mask = format!(" & {}u; uint4 v_", sample_slots - 1); + let broken_mask = sample_metal.replacen(&mask, "; uint4 v_", 1); + assert_ne!(broken_mask, sample_metal); + let e = scratch_text_check(&broken_mask, Dialect::Metal, 4, 64, 1).unwrap_err(); + assert!(e.contains("masked slot followed by the load: 3, expected 4"), "{e}"); + println!("break 1 (one mask dropped, Metal): {e}"); + let wrong_mask = sample_metal.replace(" & 63u;", " & 127u;"); + let e = scratch_text_check(&wrong_mask, Dialect::Metal, 4, 64, 1).unwrap_err(); + assert!(e.contains("masked slot followed by the load: 0, expected 4"), "{e}"); + println!("break 2 (mask 63 -> 127 on every RMW, Metal): {e}"); + let wrong_stride = sample_metal.replace("* 256u;", "* 128u;"); + let e = scratch_text_check(&wrong_stride, Dialect::Metal, 4, 64, 1).unwrap_err(); + assert!(e.contains("arena definition: 0, expected 1"), "{e}"); + println!("break 3 (arena stride 256 -> 128 words, Metal): {e}"); + let stray = format!("{sample_metal}\n// stray\n// arena[0] = 0u; scratch[1] = 1u;\n"); + let e = scratch_text_check(&stray, Dialect::Metal, 4, 64, 1).unwrap_err(); + assert!(e.contains("arena mentions") && e.contains("direct scratch indexing: 1, expected 0"), "{e}"); + println!("break 4 (a stray arena and scratch access, Metal): {e}"); + let e = scratch_text_check(&sample_cl.replacen(" & 63u; uint4 v_ = vload4", "; uint4 v_ = vload4", 1), Dialect::OpenCl, 4, 64, 1).unwrap_err(); + assert!(e.contains("masked slot followed by the load: 3, expected 4"), "{e}"); + println!("break 5 (one mask dropped, OpenCL): {e}"); + let e = scratch_text_check(&sample_cu.replacen(" & 63u; uint4 v_ = *(const uint4*)", "; uint4 v_ = *(const uint4*)", 1), Dialect::Cuda, 4, 64, 1).unwrap_err(); + assert!(e.contains("masked slot followed by the load: 3, expected 4"), "{e}"); + println!("break 6 (one mask dropped, CUDA): {e}"); + // and the unbroken texts pass under the same calls + scratch_text_check(&sample_metal, Dialect::Metal, 4, 64, 1).unwrap(); + scratch_text_check(&sample_cl, Dialect::OpenCl, 4, 64, 1).unwrap(); + scratch_text_check(&sample_cu, Dialect::Cuda, 4, 64, 1).unwrap(); + // a wrong slot count, RMW count or kernel count against a right text fails too (the check is tied to the class) + assert!(scratch_text_check(&sample_metal, Dialect::Metal, 4, 256, 1).is_err()); + assert!(scratch_text_check(&sample_metal, Dialect::Metal, 3, 64, 1).is_err()); + assert!(scratch_text_check(&sample_metal, Dialect::Metal, 4, 64, 2).is_err()); +} + +// --------------------------------------------------------------------------------------------------------------- +// 5. The fuzz: 200 generated scratch programs, contract on every instruction, 4 units each across the 32-bit +// range including the wrap; with IGNEUM_SCRATCH_PACKS_OUT the packs for the Metal runs (question 3, 4) +// --------------------------------------------------------------------------------------------------------------- + +/// Write a pack whose vectors.json carries `bases` (any number of units) instead of the three standard bases. +fn write_pack_with_bases(dir: &PathBuf, e: &Epoch, day: &str, bases: &[u32], source: &str) -> Vec<[u64; 32]> { + let mut pack = export_pack(e, day, source); + let outs: Vec<[u64; 32]> = bases.iter().map(|&b| e.hash_warp(b)).collect(); + let vj = vectors_json(&e.program, day, e.dataset.log2_words, bases, &outs, &pack.vectors, e.dataset.mask, source, true); + for f in pack.files.iter_mut() { + if f.0 == "vectors.json" { + f.1 = vj.clone(); + } + } + pack.write_to(dir).unwrap(); + outs +} + +fn contract(p: &Program) { + assert_eq!(p.instrs.len(), INSTR_COUNT); + assert_eq!(p.instrs.iter().filter(|i| i.op == Op::Load).count() + p.instrs.iter().filter(|i| i.op == Op::Scratch).count(), 16); + assert_eq!(p.instrs.iter().filter(|i| i.op == Op::Scratch).count(), p.class.scratch_slots()); + assert!(p.instrs[0].op != Op::Load && p.instrs[0].op != Op::Scratch, "instruction 0 is never a memory op"); + for (k, i) in p.instrs.iter().enumerate() { + assert!(i.src != i.dst, "#{k}: src == dst"); + assert!((1..=31).contains(&i.rot), "#{k}: rot {}", i.rot); + assert!([1u8, 2, 4, 8, 16].contains(&i.mask), "#{k}: mask {}", i.mask); + assert!(i.dst < 8 && i.src < 8 && i.src2 < 8); + assert_eq!(i.width, 1, "#{k}: a scratch class reads one-word loads"); + } + assert!(igneum_pow::accept::check(p).is_ok(), "an accepted program"); +} + +#[test] +fn fuzz_scr_programs_cpu() { + let n: usize = std::env::var("IGNEUM_SCRATCH_FUZZ").ok().and_then(|s| s.parse().ok()).unwrap_or(200); + let out = std::env::var("IGNEUM_SCRATCH_PACKS_OUT").ok().map(PathBuf::from); + let mut rng = SplitMix64::new(0x6967_6e65_756d_2d73); // "igneum-s" + let day = "2026-10-03"; + let closed = DatasetSource::new(day, DatasetMode::ClosedForm, 28); + // memory-hard sources per size, built once each (the cache fill is 0.2 s); only when packs are written + let mut mh: HashMap = HashMap::new(); + let mut manifest = String::from("pack\tclass\tlog2\tprogram_id\tscratch_ops_per_hash\tbases\n"); + let mut per_class: HashMap = HashMap::new(); + let mut units = 0usize; + let mut wraps = 0usize; + if let Some(dir) = &out { + std::fs::create_dir_all(dir).unwrap(); + // the edge packs first: 64 MiB datasets (no dataset load in them), the four edge bases + for kb in [32u8, 128] { + for (name, _what, p) in edge_programs(kb) { + let log2 = 24; + let ds = mh.remove(&log2).unwrap_or_else(|| DatasetSource::new(day, DatasetMode::MemoryHard, log2)); + let e = Epoch { program: p, dataset: ds }; + let pack_name = format!("edge-{name}-k{kb}"); + write_pack_with_bases(&dir.join(&pack_name), &e, day, &EDGE_BASES, "igneum-pow tests/scratch.rs edge"); + manifest.push_str(&format!( + "{pack_name}\t{}\t{log2}\t{:016x}\t{}\t{}\n", + e.program.class.name(), + e.program.program_id(), + e.program.scratch_ops_per_hash(), + EDGE_BASES.iter().map(|b| format!("{b}")).collect::>().join(",") + )); + mh.insert(log2, e.dataset); + } + } + } + for i in 0..n { + let name = CLASSES[rng.below(CLASSES.len() as u64) as usize]; + let c = class(name); + let seed = format!("igneum-scratch-fuzz/{i}"); + let p = generate_class(&seed, c); + contract(&p); + *per_class.entry(name.to_string()).or_insert(0) += 1; + // four bases: one inside a 256-nonce batch (in-batch check on the GPU), one straddling 2^31, one in + // the last 256 nonces (the unit wraps past 2^32 or ends on it), one uniform + let b0 = (rng.below(8) as u32) * 32; + let b1 = 0x8000_0000u32.wrapping_sub(256).wrapping_add((rng.below(16) as u32) * 32); + let b2 = 0xffff_ff00u32.wrapping_add((rng.below(8) as u32) * 32); + let b3 = (rng.next() as u32) & !31; + let bases = [b0, b1, b2, b3]; + // an aligned unit never straddles 2^32 (spec 1.9); the top unit ends on 0xffffffff and the persistent + // kernel's unit sequence wraps inside a launch, which the Metal run checks with packbench --batch-base + wraps += bases.iter().filter(|&&b| b >= 0xffff_ff00).count(); + // the CPU: the interpreter is deterministic and every scratch event is inside the lane's slots + for &b in &bases { + let (r1, ev) = interpret_warp_scratch(&p, &p.seed, b, &closed, true); + let r2 = interpret_warp_scratch(&p, &p.seed, b, &closed, false).0; + assert_eq!(r1.hashes, r2.hashes); + assert_eq!(ev.len(), p.scratch_ops_per_hash() * LANES); + assert!(ev.iter().all(|e: &ScratchEvent| e.slot < c.scratch_slots_per_lane() as u32)); + units += 1; + } + if let Some(dir) = &out { + let log2 = [24u32, 26, 28][rng.below(3) as usize]; + let ds = mh.remove(&log2).unwrap_or_else(|| DatasetSource::new(day, DatasetMode::MemoryHard, log2)); + let e = Epoch { program: p, dataset: ds }; + let pack_name = format!("fuzz-{i:03}-{name}-l{log2}"); + write_pack_with_bases(&dir.join(&pack_name), &e, day, &bases, "igneum-pow tests/scratch.rs fuzz"); + manifest.push_str(&format!( + "{pack_name}\t{name}\t{log2}\t{:016x}\t{}\t{}\n", + e.program.program_id(), + e.program.scratch_ops_per_hash(), + bases.iter().map(|b| format!("{b}")).collect::>().join(",") + )); + mh.insert(log2, e.dataset); + } else { + let _ = rng.below(3); + } + } + let mut classes: Vec<_> = per_class.iter().collect(); + classes.sort(); + println!("fuzz: {n} programs, {units} units on the CPU, {wraps} units in the top 256 nonces, classes {classes:?}"); + assert_eq!(units, 4 * n); + assert_eq!(wraps, n, "every program has a unit in the top 256 nonces"); + if let Some(dir) = &out { + std::fs::write(dir.join("manifest.tsv"), manifest).unwrap(); + println!("packs written to {}", dir.display()); + } +} + +/// The fold and rewrite, restated: a slot after `d` dependent RMWs holds 96 bits that are a function of the fill +/// (3 words, a pure function of nonce, slot and seed) and the `d` fold values; a chip that keeps the `d` fold +/// values (32 bits each) instead of the 96-bit slot recomputes the slot in `d` rewrites. This test pins the +/// arithmetic the analysis uses (question 2): the replay from the fold values reproduces the slot. +#[test] +fn slot_is_replayable_from_its_fold_values() { + let seed = seed_words_from_bytes(b"igneum-genesis"); + let (base, lane, slot) = (0x1234_5600u32, 5u32, 17u32); + let fill = [scratch_fill(&seed, base, lane, slot, 0), scratch_fill(&seed, base, lane, slot, 1), scratch_fill(&seed, base, lane, slot, 2)]; + let mut rng = SplitMix64::new(99); + let dsts: Vec = (0..64).map(|_| rng.next() as u32).collect(); + // the honest sequence: read, fold, rewrite, 64 times + let mut w = fill; + let mut xs = Vec::new(); + for &d in &dsts { + let x = fold_words(d, &w); + xs.push(x); + w = scratch_rewrite(x, &w); + } + // the replay: from the fill and the stored fold values alone + let mut w2 = fill; + for &x in &xs { + w2 = scratch_rewrite(x, &w2); + } + assert_eq!(w, w2); + // and nothing shorter: the fold value at step d depends on the slot content at step d, which depends on + // every earlier fold value (drop one and the chain diverges) + let mut w3 = fill; + for (i, &x) in xs.iter().enumerate() { + if i != 10 { + w3 = scratch_rewrite(x, &w3); + } + } + assert_ne!(w, w3); +} diff --git a/proto-metal/packbench.swift b/proto-metal/packbench.swift index a38012db..a378e736 100644 --- a/proto-metal/packbench.swift +++ b/proto-metal/packbench.swift @@ -5,7 +5,7 @@ // against the Rust CPU reference and timed without a Swift mirror of the generator. One file, no packages. // // swiftc -O -target arm64-apple-macos11 -o packbench packbench.swift -framework Metal -// ./packbench --pack [--batches 5] [--batch-log2 24] [--group 256] [--warps 2048] +// ./packbench --pack [--batches 5] [--batch-log2 24] [--group 256] [--warps 2048] [--batch-base 0] // // Prints one RESULT line per run: vectors, cache and dataset checks, the batch fingerprint (FNV-1a 64 over the 2^B // outputs at base nonce 0) and MH/s by wall and by GPU time. Variant 5 packs (IGNEUM_PERSISTENT_WARPS) are launched @@ -16,7 +16,7 @@ import Metal func nowMs() -> Double { return Double(DispatchTime.now().uptimeNanoseconds) / 1e6 } func fail(_ m: String) -> Never { print("FAIL: \(m)"); exit(1) } -struct Opts { var pack = ""; var batches = 5; var batchLog2 = 24; var group = 256; var warps = 2048 } +struct Opts { var pack = ""; var batches = 5; var batchLog2 = 24; var group = 256; var warps = 2048; var batchBase: UInt32 = 0 } var opts = Opts() var args = Array(CommandLine.arguments.dropFirst()) while !args.isEmpty { @@ -28,6 +28,7 @@ while !args.isEmpty { case "--batch-log2": opts.batchLog2 = Int(next())! case "--group": opts.group = Int(next())! case "--warps": opts.warps = Int(next())! + case "--batch-base": opts.batchBase = UInt32(next())! // base nonce of the fingerprint batch (default 0; a base near 2^32 makes the persistent unit sequence wrap inside the launch) default: fail("unknown argument \(a)") } } @@ -216,12 +217,13 @@ for (i, base) in vecBases.enumerated() { var nonces = 1 << opts.batchLog2 if persistent { let unit = 32 * min(warpsN, nonces / 32); nonces = (nonces / unit) * unit } let out = device.makeBuffer(length: nonces * 8, options: .storageModeShared)! -let (warmWall, warmGpu) = run { enc in encodeHash(enc, out: out, base: 0, nonces: nonces, group: opts.group) } +let (warmWall, warmGpu) = run { enc in encodeHash(enc, out: out, base: opts.batchBase, nonces: nonces, group: opts.group) } let outPtr = out.contents().bindMemory(to: UInt64.self, capacity: nonces) var batchVecPass = 0, batchVecN = 0 -for (i, base) in vecBases.enumerated() where Int(base) + 32 <= nonces { +for (i, base) in vecBases.enumerated() where Int(base &- opts.batchBase) + 32 <= nonces { // the batch window, wrapping past 2^32 + let off = Int(base &- opts.batchBase) batchVecN += 1 - if (0..<32).allSatisfy({ outPtr[Int(base) + $0] == vecOuts[i][$0] }) { batchVecPass += 1 } + if (0..<32).allSatisfy({ outPtr[off + $0] == vecOuts[i][$0] }) { batchVecPass += 1 } } let fingerprint = fnv1a64(out.contents(), nonces * 8) // Timed batches @@ -239,5 +241,5 @@ print("cache FNV-1a 64 \(String(format: "%016llx", cacheFnv)) \(cacheOk ? "PASS" if hotMb > 0 { print("hot table \(hotMb) MiB (\(hotSlots) of 16 load slots): fill \(String(format: "%.2f", hotGpu)) ms GPU (\(String(format: "%.2f", hotWall)) wall), FNV-1a 64 \(String(format: "%016llx", hotFnv)) \(hotFnvWant == nil ? "(not in vectors.json)" : (hotOk ? "PASS" : "FAIL"))") } print("warm-up batch \(nonces) hashes: \(String(format: "%.1f", warmGpu)) ms GPU, \(String(format: "%.1f", warmWall)) ms wall") let overall = cacheOk && dsOk && hotOk && vecPass == vecBases.count && batchVecPass == batchVecN -print("RESULT pack=\(packName) class=\(className) device=\(device.name.replacingOccurrences(of: " ", with: "_")) group=\(opts.group) warps=\(warpsN) arena_mib=\(persistent ? warpsN * 32 * scratchWordsPerLane * 4 / 1048576 : 0) hot_mib=\(hotMb) hot_slots=\(hotSlots) hot_fill_ms=\(String(format: "%.2f", hotGpu)) hot=\(hotMb > 0 ? (hotOk ? "PASS" : "FAIL") : "none") nonces=\(nonces) batches=\(opts.batches) vectors=\(vecPass)/\(vecBases.count) batch_vectors=\(batchVecPass)/\(batchVecN) cache=\(cacheOk ? "PASS" : "FAIL") dataset=\(dsOk ? "PASS" : "FAIL") fingerprint=\(String(format: "%016llx", fingerprint)) mhs_gpu=\(String(format: "%.3f", mhsGpu)) mhs_wall=\(String(format: "%.3f", mhsWall)) loads=\(loadsPerHash) bytes=\(bytesPerHash) scratch_ops=\(scratchOps * 8) overall=\(overall ? "PASS" : "FAIL")") +print("RESULT pack=\(packName) class=\(className) device=\(device.name.replacingOccurrences(of: " ", with: "_")) group=\(opts.group) warps=\(warpsN) batch_base=\(opts.batchBase) arena_mib=\(persistent ? warpsN * 32 * scratchWordsPerLane * 4 / 1048576 : 0) hot_mib=\(hotMb) hot_slots=\(hotSlots) hot_fill_ms=\(String(format: "%.2f", hotGpu)) hot=\(hotMb > 0 ? (hotOk ? "PASS" : "FAIL") : "none") nonces=\(nonces) batches=\(opts.batches) vectors=\(vecPass)/\(vecBases.count) batch_vectors=\(batchVecPass)/\(batchVecN) cache=\(cacheOk ? "PASS" : "FAIL") dataset=\(dsOk ? "PASS" : "FAIL") fingerprint=\(String(format: "%016llx", fingerprint)) mhs_gpu=\(String(format: "%.3f", mhsGpu)) mhs_wall=\(String(format: "%.3f", mhsWall)) loads=\(loadsPerHash) bytes=\(bytesPerHash) scratch_ops=\(scratchOps * 8) overall=\(overall ? "PASS" : "FAIL")") exit(overall ? 0 : 1) From 36ef07fda24516ef592e4b8ee2857df7ff961114 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:21:25 +0000 Subject: [PATCH 092/158] mixer x4: tests/mixer.rs (200-program v3 fuzz with the Metal pack writer, stats beside v2, dataset edges at every multiplier, determinism against the pinned pack); scratch.rs edge literal gains era_bytes; mixer-x4.md: the v3 vector tables, the v2 diff, the Metal and Apple OpenCL bit-exactness rows, the soundness table so far Co-Authored-By: Claude Fable 5.1 --- docs/plans/mixer-x4.md | 104 +++++++++++++++- igneum-pow/tests/mixer.rs | 237 ++++++++++++++++++++++++++++++++++++ igneum-pow/tests/scratch.rs | 1 + 3 files changed, 339 insertions(+), 3 deletions(-) create mode 100644 igneum-pow/tests/mixer.rs diff --git a/docs/plans/mixer-x4.md b/docs/plans/mixer-x4.md index 2635da05..3f812f56 100644 --- a/docs/plans/mixer-x4.md +++ b/docs/plans/mixer-x4.md @@ -125,8 +125,57 @@ node agent). ## 4. Vectors (class v3, `proto-cuda/packs-ca2-mixer/`) -Filled in section 6 from the exported packs: `mx4-genesis` (seed `igneum-genesis`, day `2026-10-03`, 2^28 words, -2^26-word cache) and `mx4-devnet-epoch0` (the devnet genesis hash as the epoch seed, day bytes of 2026-10-04). +Produced by `igneum-pow export --program-class v3` (the Rust CPU interpreter, 5 October 2026, commit 66eeba3) and +checked on the GPUs in section 6. The program of each pack is the version 2 program of the same seed instruction +for instruction (`tests/packs.rs`, `v3_packs_are_the_v2_seeds_under_mixer_x4`); the cache is the version 2 cache +(day 0 of the growth rule); the dataset words and the hashes are new. The 64 sampled indices are those of every +pack (`emit::sample_indices`). + +Pack `mx4-genesis` (seed igneum-genesis, day 2026-10-03, generator 3, class mx4, program id e323b9dcaf283a6f, 2^28 words, 2^26-word cache, cache FNV-1a 64 `48c4f5bf24166b2e` as under v2): + +``` +dataset words 0..15 (item 0): + 61ff2180 0d4c7e6c 2177d443 60df9025 cf8b2e10 63675bfb 25289e58 9c45dc42 + 2d271c54 9652369b 2dd77508 5921392c 3afa60ee c640ad68 f2bb56ff cfa46438 +dataset[0x0fffffff] = 5020180e +sampled words (the first 8 of the 64 in vectors.json): + dataset[59471966] = de85726d dataset[217795994] = 7cfc31c7 dataset[208353206] = d3cd5289 dataset[42483309] = 6ebbeef1 + dataset[172547758] = 9858413b dataset[148076330] = e786f141 dataset[183853158] = 64f13833 dataset[214389424] = ca229d04 +unit at base nonce 0, lanes 0..31: + 63acd2d273f475ba e929c78b34b80d4b 0b1011cb19982558 1457a0df5497aa11 + 957d0f3bb71d98fb ac16901e6e6f6057 8ea1c6279f4b177a f28146e60bd08ba9 + fe5b8cfe87f8e65b 49f87240566ace62 6ef6d6b7bdea8e41 46d9c0dc29a97b9c + 111fe30128db9398 66dc39084f0946d4 8ee11bdfd35fecf2 2861fcfc75db6677 + 31c7667d4bde8556 c5989c48858b4ce0 276395e734a9d30d 84217b41e91368ff + 3604861e34d9f697 9f51d8ee16bf3639 c89e47bafa84401c 7ae78c1f10b70e19 + 0b8c947157a29a48 d67192e8cfb43842 05a4c6d182c8c675 188e2661f3263f2e + a2df24238f7fea2e ed69ea7e13ad3a48 2d44ae509bab91b8 adad61931ea4fb70 +unit at base nonce 4096: lane 0 edd508ac57e5699a, lane 1 4eefd56d526cdaeb, lane 31 8892f8604733b1e0 +unit at base nonce 1000000: lane 0 8b3183778a49f59c, lane 1 1831b72a8797e895, lane 31 75eae55eba53a506 +``` + +Pack `mx4-devnet-epoch0` (seed the devnet genesis hash, day bytes of 2026-10-04, generator 3, class mx4, program id 73bcbfe8ccf988f1, 2^28 words, 2^26-word cache, cache FNV-1a 64 `448274a57f508cbc` as under v2): + +``` +dataset words 0..15 (item 0): + afe80d67 b9fbd029 6c79f193 95139ad9 96310aff 4609f8b1 75279e63 28235be1 + 47b17dcb 718e0ef2 a52588c8 a8bf49d5 19cf243e 5ec8905e a4851f66 af9cd9f3 +dataset[0x0fffffff] = e6a99c7a +sampled words (the first 8 of the 64 in vectors.json): + dataset[59471966] = 57642b58 dataset[217795994] = c279badd dataset[208353206] = cbccbaad dataset[42483309] = 32cce392 + dataset[172547758] = 71fdb4c6 dataset[148076330] = f5a268ce dataset[183853158] = 3ca1d676 dataset[214389424] = 7977b03d +unit at base nonce 0, lanes 0..31: + 212c6442b51e87ae c374795c00839331 b6036a220a98f4b3 eb8b8013e637367b + db5866e9b73930fd f3f3d01f46e90333 9d913991ab8ed428 7ccb1d8fa100a800 + 3cf45ba44f09a0fe 91acf48ef1a63082 6ea46c69fb082f99 581f0218977a9d72 + 9a4623a5c62ddf2d ab6eb5e768f0feb4 07b70bdccf8aca12 d666311ae5e4311e + 53114757d669f0a4 bd5d6ace87ce2ce4 fb712015e8189192 a32cec81103e134b + 83f3d18c3289c124 fe29f1984b132b3d c9ffcf4e3774497a ac99c9243dc63809 + d78a7e8217a32f3c ab81ad63d242fc31 0e4c30b7e00024af ce014289fff6778d + 64a1292e2a8b4a91 d5b8c90e681d7e3a 06078117673030fd 51bf77b280173930 +unit at base nonce 4096: lane 0 3c797978566b5950, lane 1 7c759e60185b6411, lane 31 96a903eb9a0ca390 +unit at base nonce 1000000: lane 0 d5a8da0568df8ee7, lane 1 68cb69c04208285a, lane 31 f8ca84a1a5d78cf5 +``` ## 5. The v2 path is byte-identical @@ -136,7 +185,56 @@ section 6 also records a fresh `igneum-pow export` of both packs diffed against ## 6. Measurements -Filled as they land. Every row names the machine, the date, the command and the lock mode. +Machine: Apple M5 Max, 64 GiB, Darwin 25.6.0, 5 October 2026 (night), other agents' builds running beside every +run; a timing row says which lock it ran under (`measure` is exclusive; `run` and `build` are not timings). + +### 6.1 The v2 path, byte for byte (no lock needed) + +`igneum-pow export --seed igneum-genesis --day 2026-10-03` and `igneum-pow export --epoch-hex edc4fa84...fb07 +--day-hex 69676e65756d2d6461792ffa50000000000000` on commit 66eeba3, `diff -r` against +`proto-cuda/packs/igneum-genesis-mh` and `igneum-devnet-v4-epoch0`: IDENTICAL, both (twelve files each). The +crate tests regenerate the same files and compare them on every run (`tests/packs.rs`, 12 of 12 pass). + +### 6.2 Bit-exactness of the class v3 construction on the GPUs (`with-lock.sh run`, 22:05 UTC) + +`packbench --pack --batches 1 --batch-log2 24 --group 256` (Metal, built from this branch) and +`igneum-bench-cl-igneum-genesis-mh --bench-pack --pack --batches 1 --batch-log2 24` (Apple OpenCL, built from +this branch's host.c). Vectors are the Rust interpreter's; the fingerprint is FNV-1a 64 over the 2^24 outputs at +base nonce 0. + +| Pack | Harness | Cache FNV-1a 64 | Dataset head, word MASK, 64 samples | Vectors standalone / in batch | Fingerprint 2^24 | MH/s (GPU time; not a measurement, the run lock) | +|---|---|---|---|---|---|---| +| mx4-genesis | Metal | 48c4f5bf24166b2e PASS | PASS | 3/3, 3/3 | 6f48d5a2aa0dbe5f | 27.6 | +| mx4-genesis | Apple OpenCL | PASS | PASS | 96 of 96 lanes | 6f48d5a2aa0dbe5f | 27.7 (wall) | +| mx4-devnet-epoch0 | Metal | 448274a57f508cbc PASS | PASS | 3/3, 3/3 | 73caaebb28e808fe | 27.5 | +| mx4-devnet-epoch0 | Apple OpenCL | PASS | PASS | 96 of 96 lanes | 73caaebb28e808fe | 27.6 (wall) | + +Reading: the Rust interpreter, Metal and Apple OpenCL agree on the v3 dataset (head, MASK word, 64 samples), on +every vector lane and on the 2^24-output fingerprint of each pack; the hash rate is the v2 rate (27.7 MH/s on this +card tonight, readwidth table), as it must be: the hash kernel only loads, the mixer is paid in the build. + +Dataset build under the run lock (indicative only; the measured rows are in 6.4): Metal 30.2 ms GPU (mx4-genesis) +and 21.7 ms GPU (mx4-devnet-epoch0) for 1 GiB; Apple OpenCL 55 and 56 ms wall. The readwidth entry's v2 figure on +this card is 0.6 to 2.1 ms cache fill and a 1 GiB build the bench-log's memory-hard entry puts at 13 to 30 ms; the +x4 build on the GPU is the row the measure lock will settle. + +### 6.3 Soundness suite on the v3 construction (commit 66eeba3 and after; `with-lock.sh build` for cargo, `run` for the GPU) + +| Suite | Command | Result | +|---|---|---| +| Crate lib tests (memhard schedule table, the by-hand multiplied mixer, the seam, the generator) | `cargo test -j4 --release` | 44 of 44 pass | +| Pinned packs, v2 and v3 (`tests/packs.rs`: programs, ids, dataset words, 96 vectors per pack, every emitted file byte for byte, 16 masked loads per kernel, the v3 packs as the v2 seeds under mixer x4) | same | 12 of 12 pass | +| Scratch soundness tests of branch ca2-soundness (cherry-pick 0d8f745, one conflict in packbench.swift's RESULT line resolved by hand, `era_bytes: None` added to the edge program literal) | `cargo test -j4 --release --test scratch` | 7 of 7 pass: bijections, re-hit rates, 56 of 56 edge units, 42 of 42 emitted scr kernels, 200 scratch programs and 800 units on the CPU | +| v3 fuzz on the CPU (`tests/mixer.rs`): 200 programs through the seam, the contract on every instruction (each is the v2 program of its seed), 4 units each across the 32-bit range with one unit in the top 256 nonces, interpreted twice | `IGNEUM_MIXER_PACKS_OUT= cargo test -j4 --release --test mixer` | 200 of 200, 800 of 800 units; 200 packs written for the GPU runs (82 s with the three cache fills) | +| v3 stats beside v2 (`tests/mixer.rs`): 8,192 outputs per seed, bit balance, single-bit avalanche within the unit and across units, duplicates | same | igneum-genesis v3: avalanche 49.99 percent, worst bit z 1.92, 0 duplicates (v2: 49.87, z 2.25); igneum-genesis/stats1 v3: 49.97, z 3.09 (v2: 49.98, z 2.30) | +| v3 edge (`tests/mixer.rs`): items 0, 1, 2^28 - 1 and 2^32 - 1 by hand at m = 1, 2, 4, 8 on a 2^14-word cache; words 0, 15, 16, 17, MASK - 1, MASK through the interpreter's fetch path; the index wrap at MASK + 1 | same | pass | +| v3 determinism (`tests/mixer.rs`): two independent epochs, every vector and every emitted file equal, and equal to the pinned pack | same | pass | +| Metal and Apple OpenCL on the two pinned v3 packs | section 6.2 | 3/3 standalone, 3/3 in batch, 96 of 96 lanes, dataset head, MASK word and 64 samples, one fingerprint per pack across both harnesses | +| Metal fuzz: the 200 packs, 4 units each standalone and the top-256 unit inside a 512-nonce batch at base 4,294,967,040; every tenth pack on Apple OpenCL as well | `packbench --pack --batches 1 --batch-log2 9 --batch-base 4294967040` | see below | + +### 6.4 Timings (`with-lock.sh measure`) + +Owed until the measure lock frees. ## 7. The chip model diff --git a/igneum-pow/tests/mixer.rs b/igneum-pow/tests/mixer.rs new file mode 100644 index 00000000..bd40c188 --- /dev/null +++ b/igneum-pow/tests/mixer.rs @@ -0,0 +1,237 @@ +//! The class v3 dataset construction (mixer x4, cache growth option C; `docs/plans/mixer-x4.md`): the soundness +//! runs the brief asks for, on the CPU, with the packs for the GPU runs written on request. +//! +//! 1. Fuzz: `IGNEUM_MIXER_FUZZ` (default 200) programs through the seam (`ProgramClass::V3`), the contract on every +//! instruction (the v2 program of the seed, instruction for instruction), 4 units each across the 32-bit range +//! including the wrap, interpreted twice on the CPU; with `IGNEUM_MIXER_PACKS_OUT=` every program is written +//! as a pack with its 4 bases in vectors.json for `packbench` and the OpenCL host (the Metal fuzz). +//! 2. Stats: bit balance and single-bit-flip avalanche of the v3 hash against v2 on the same programs and nonces. +//! 3. Edge: the dataset at word 0, word MASK and the item boundary, derived through the interpreter's fetch path and +//! by hand at every multiplier 1, 2, 4, 8, on a small cache. +//! 4. Determinism: two independent epochs of the same seed and day agree on every vector and every emitted file. + +use igneum_pow::emit::{export_pack, vectors_json}; +use igneum_pow::generator::{generate_from_seed_bytes, generate_from_seed_bytes_program_class, Op, Program, ProgramClass, GENERATOR_VERSION_V3, INSTR_COUNT, V3_CLASS}; +use igneum_pow::memhard::{derive_item, mixer, round_key, Cache, MixParams, Shape}; +use igneum_pow::seed::{day_key, SplitMix64}; +use igneum_pow::verify::{DatasetMode, DatasetSource, Epoch}; +use std::collections::HashMap; +use std::path::PathBuf; + +const DAY: &str = "2026-10-03"; + +fn contract(p: &Program, seed: &str) { + assert_eq!(p.generator, GENERATOR_VERSION_V3); + assert_eq!(p.class, V3_CLASS); + assert_eq!(p.instrs.len(), INSTR_COUNT); + assert_eq!(p.instrs.iter().filter(|i| i.op == Op::Load).count(), 16); + for (k, i) in p.instrs.iter().enumerate() { + assert!(i.src != i.dst, "#{k}: src == dst"); + assert!((1..=31).contains(&i.rot), "#{k}: rot {}", i.rot); + assert!([1u8, 2, 4, 8, 16].contains(&i.mask), "#{k}: mask {}", i.mask); + assert!(i.dst < 8 && i.src < 8 && i.src2 < 8); + assert_eq!(i.width, 1, "#{k}: a v3 load reads one word"); + } + assert!(igneum_pow::accept::check(p).is_ok(), "an accepted program"); + let v2 = generate_from_seed_bytes(seed, seed.as_bytes()); + assert_eq!(p.instrs, v2.instrs, "the v2 program of the seed under the v3 construction"); + assert_eq!(p.attempt, v2.attempt); +} + +/// Write a pack whose vectors.json carries `bases` instead of the three standard bases (packbench and the OpenCL +/// host check every unit standalone and the ones inside the batch window). +fn write_pack_with_bases(dir: &PathBuf, e: &Epoch, day: &str, bases: &[u32], source: &str) { + let mut pack = export_pack(e, day, source); + let outs: Vec<[u64; 32]> = bases.iter().map(|&b| e.hash_warp(b)).collect(); + let vj = vectors_json(&e.program, day, e.dataset.log2_words, bases, &outs, &pack.vectors, e.dataset.mask, source, true); + for f in pack.files.iter_mut() { + if f.0 == "vectors.json" { + f.1 = vj.clone(); + } + } + pack.write_to(dir).unwrap(); +} + +#[test] +fn fuzz_v3_programs_cpu() { + let n: usize = std::env::var("IGNEUM_MIXER_FUZZ").ok().and_then(|s| s.parse().ok()).unwrap_or(200); + let out = std::env::var("IGNEUM_MIXER_PACKS_OUT").ok().map(PathBuf::from); + let mut rng = SplitMix64::new(0x6967_6e65_756d_2d6d); // "igneum-m" + let shape = Shape::for_class(&V3_CLASS); + assert_eq!(shape, Shape { mixer_mult: 4, cache_log2_words: 26 }); + // one memory-hard source per dataset size (the 256 MiB cache fill is 0.2 s each) + let mut mh: HashMap = HashMap::new(); + let mut manifest = String::from("pack\tlog2\tprogram_id\tbases\n"); + let mut units = 0usize; + let mut wraps = 0usize; + for i in 0..n { + let seed = format!("igneum-mixer-fuzz/{i}"); + let p = generate_from_seed_bytes_program_class(&seed, seed.as_bytes(), ProgramClass::V3, None); + contract(&p, &seed); + let b0 = (rng.below(8) as u32) * 32; + let b1 = 0x8000_0000u32.wrapping_sub(256).wrapping_add((rng.below(16) as u32) * 32); + let b2 = 0xffff_ff00u32.wrapping_add((rng.below(8) as u32) * 32); + let b3 = (rng.next() as u32) & !31; + let bases = [b0, b1, b2, b3]; + wraps += bases.iter().filter(|&&b| b >= 0xffff_ff00).count(); + let log2 = [24u32, 26, 28][rng.below(3) as usize]; + let ds = mh.remove(&log2).unwrap_or_else(|| DatasetSource::new_shape(DAY, DatasetMode::MemoryHard, log2, shape)); + let e = Epoch { program: p, dataset: ds }; + for &b in &bases { + let r1 = e.interpret_warp(b); + let r2 = e.interpret_warp(b); + assert_eq!(r1.hashes, r2.hashes); + assert!(r1.items_derived >= 120 * 32 / 32 && r1.items_derived <= 4_096, "{seed}: {} items", r1.items_derived); + units += 1; + } + if let Some(dir) = &out { + let pack_name = format!("fuzz-{i:03}-mx4-l{log2}"); + write_pack_with_bases(&dir.join(&pack_name), &e, DAY, &bases, "igneum-pow tests/mixer.rs fuzz"); + manifest.push_str(&format!( + "{pack_name}\t{log2}\t{:016x}\t{}\n", + e.program.program_id(), + bases.iter().map(|b| format!("{b}")).collect::>().join(",") + )); + } + mh.insert(log2, e.dataset); + } + println!("fuzz: {n} v3 programs, {units} units on the CPU, {wraps} units in the top 256 nonces"); + assert_eq!(units, 4 * n); + assert_eq!(wraps, n); + if let Some(dir) = &out { + std::fs::create_dir_all(dir).unwrap(); + std::fs::write(dir.join("manifest.tsv"), manifest).unwrap(); + println!("packs written to {}", dir.display()); + } +} + +/// Bit balance and avalanche of the v3 hash beside v2 on the same program (the TESTS.md section 3 shape, on the +/// CPU, 2^13 nonces per seed): every output bit within 5 sigma of half ones; a single nonce-bit flip moves 50 percent +/// of the output bits within 2 points; no duplicate among the outputs. +#[test] +fn stats_v3_against_v2() { + let n_warps = 256usize; // 8,192 nonces + for seed in ["igneum-genesis", "igneum-genesis/stats1"] { + let v3 = Epoch { + program: generate_from_seed_bytes_program_class(seed, seed.as_bytes(), ProgramClass::V3, None), + dataset: DatasetSource::new_shape(DAY, DatasetMode::MemoryHard, 24, Shape::for_class(&V3_CLASS)), + }; + let v2 = Epoch::new(seed, DAY, DatasetMode::MemoryHard, 24); + for (name, e) in [("v3", &v3), ("v2", &v2)] { + let mut ones = [0u64; 64]; + let mut outs = Vec::with_capacity(n_warps * 32); + for w in 0..n_warps { + let h = e.hash_warp(w as u32 * 32); + for &x in &h { + outs.push(x); + for b in 0..64 { + ones[b] += (x >> b) & 1; + } + } + } + let total = (n_warps * 32) as f64; + let sigma = (total / 4.0).sqrt(); + for (b, &c) in ones.iter().enumerate() { + let z = (c as f64 - total / 2.0).abs() / sigma; + assert!(z < 5.0, "{seed} {name}: bit {b} ones {c} of {total}, z {z:.2}"); + } + // avalanche: flip one bit of the nonce within the unit (lanes 0..31 differ in the low 5 bits) and across + // units (bit 5 and up): compare lane l of unit u with lane l ^ (1 << k) and with unit u ^ (1 << k) + let mut flips = 0u64; + let mut moved = 0u64; + for w in 0..64usize { + let h = e.hash_warp(w as u32 * 32); + for k in 0..5 { + for l in 0..32usize { + moved += (h[l] ^ h[l ^ (1 << k)]).count_ones() as u64; + flips += 1; + } + } + let h2 = e.hash_warp((w ^ 1) as u32 * 32); + for l in 0..32usize { + moved += (h[l] ^ h2[l]).count_ones() as u64; + flips += 1; + } + } + let avg = moved as f64 / flips as f64 / 64.0 * 100.0; + assert!((avg - 50.0).abs() < 2.0, "{seed} {name}: avalanche {avg:.2} percent"); + outs.sort_unstable(); + let dups = outs.windows(2).filter(|p| p[0] == p[1]).count(); + assert_eq!(dups, 0, "{seed} {name}: duplicate outputs"); + println!("{seed} {name}: {} outputs, avalanche {avg:.2} percent, worst bit z {:.2}", outs.len(), ones.iter().map(|&c| (c as f64 - total / 2.0).abs() / sigma).fold(0.0, f64::max)); + } + assert_ne!(v3.hash_warp(0), v2.hash_warp(0)); + } +} + +/// The dataset edges under every multiplier on a small cache: word 0, word MASK, the last word of item 0 and the +/// first of item 1, through `DatasetSource::word` and by hand. +#[test] +fn edge_items_every_multiplier() { + let key = day_key(DAY); + let cache = Cache::fill_log2(key, 14); + for m in [1u32, 2, 4, 8] { + let mp = MixParams::with_shape(key, Shape { mixer_mult: m, cache_log2_words: 14 }); + let by_hand = |t: u32| -> [u32; 16] { + let mut s = [0u32; 16]; + s[..8].copy_from_slice(&key); + for i in 0..8 { + s[8 + i] = t.wrapping_mul(mp.mul[i]).wrapping_add(mp.rc[i]); + } + for r in 0..8usize { + for j in 0..m as usize { + mixer(&mut s, round_key(r * m as usize + j), &mp); + } + let line = cache.line(s[0]); + for i in 0..16 { + s[i] ^= line[i]; + } + } + for j in 0..m as usize { + mixer(&mut s, round_key(8 * m as usize + j), &mp); + } + s + }; + for t in [0u32, 1, 0x0fff_ffff, 0xffff_ffff] { + assert_eq!(derive_item(t, &mp, &cache), by_hand(t), "m {m} item {t}"); + } + } + // the interpreter's fetch path at the genesis cache: words 0, 15, 16 and MASK of a 2^20-word dataset agree with + // the item derivation, under v3 + let ds = DatasetSource::new_shape(DAY, DatasetMode::MemoryHard, 20, Shape::for_class(&V3_CLASS)); + let m = ds.memhard().unwrap(); + for w in [0u32, 15, 16, 17, ds.mask - 1, ds.mask] { + assert_eq!(ds.word(w), derive_item(w >> 4, &m.params, &m.cache)[(w & 15) as usize]); + assert_eq!(ds.word(w), m.word(w)); + } + // a load at an out-of-range register masks to the dataset: the word at mask + 1 is the word at 0 + assert_eq!(ds.word(ds.mask.wrapping_add(1)), ds.word(0)); +} + +/// Two independent epochs of the same seed and day: every vector and every emitted file identical; the pinned v3 +/// pack is what a third export writes. +#[test] +fn determinism_v3() { + let build = || Epoch { + program: generate_from_seed_bytes_program_class("igneum-genesis", b"igneum-genesis", ProgramClass::V3, None), + dataset: DatasetSource::new_shape(DAY, DatasetMode::MemoryHard, 28, Shape::for_class(&V3_CLASS)), + }; + let a = build(); + let b = build(); + let pa = export_pack(&a, DAY, "a"); + let pb = export_pack(&b, DAY, "a"); + assert_eq!(pa.outs, pb.outs); + assert_eq!(pa.vectors, pb.vectors); + assert_eq!(pa.files, pb.files); + for (w, warp) in [(0u32, 0usize), (4096, 1), (1_000_000, 2)] { + assert_eq!(a.hash_warp(w), pa.outs[warp]); + } + let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda/packs-ca2-mixer/mx4-genesis"); + for (name, text) in &pa.files { + if name == "vectors.json" || name == "vectors.h" { + continue; // the source string differs ("a" here) + } + let on_disk = std::fs::read_to_string(dir.join(name)).unwrap(); + assert_eq!(&on_disk, text, "{name}"); + } +} diff --git a/igneum-pow/tests/scratch.rs b/igneum-pow/tests/scratch.rs index d00bbfe4..75e48f58 100644 --- a/igneum-pow/tests/scratch.rs +++ b/igneum-pow/tests/scratch.rs @@ -261,6 +261,7 @@ fn edge(name: &str, c: LoadClass, instrs: Vec) -> Program { generator: GENERATOR_VERSION, attempt: 0, class: c, + era_bytes: None, instrs, } } From 86ef4eab1ee0eacb1d61882e103a7003fd452911 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:24:34 +0000 Subject: [PATCH 093/158] chip-model-v3.md: the hot table in the added form priced with the Mac's g (0.93 / 0.87 at 32 / 64 MiB, the 5090's g pending the PC rows): 1.98x and 2.12x at the equal integer budget, 1.60x and 1.67x with the SRAM deducted; the margin section says which convention keeps the claim Co-Authored-By: Claude Fable 5.1 --- docs/analysis/chip-model-v3.md | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/analysis/chip-model-v3.md b/docs/analysis/chip-model-v3.md index 8a48ca7a..8de60663 100644 --- a/docs/analysis/chip-model-v3.md +++ b/docs/analysis/chip-model-v3.md @@ -35,10 +35,10 @@ silicon" = bare x (750 - SRAM) / 750 x 3: the SRAM takes die area the logic does | v2 as shipped (the M16 and scratch-soundness row) | x1 | 149,760 | 334 MH/s | 256 MiB | 128 / $46 | 2.45x (2.39x against 139.7) | 7.4x | 6.1x | | v2 at w16 (not adopted; the chip's cost is items, not bytes: unchanged) | x1 | 149,760 | 334 | 256 MiB | 128 / $46 | 2.39x against 139.8 | 7.2x | 5.9x | | v3: mixer x4 | x4 | 599,040 | 83.5 MH/s | 256 MiB | 128 / $46 | 0.61x | 1.84x | 1.53x | -| v3 plus a 32 MiB hot table, added form (16 dataset loads and k hot loads) | x4 | 599,040 (a hot load is one SRAM read, no item) | 83.5 | 288 MiB | 144 / $53 | 0.61x or below (owed: the 5090's added-form rate; the hot loads cost it something, the chip nothing) | 1.84x or below | 1.49x | -| v3 plus a 64 MiB hot table, added form | x4 | 599,040 | 83.5 | 320 MiB | 160 / $61 | 0.61x or below (owed) | 1.84x or below | 1.45x | -| v3 at year 4 (cache 512 MiB under option C, dataset 4 GiB), 64 MiB hot table | x4 | 599,040 | 83.5 | 576 MiB | 287 / $130 | 0.61x | 1.84x | 1.14x | -| v3 at year 12 (cache 1 GiB, dataset 8 GiB), 64 MiB hot table | x4 | 599,040 | 83.5 | 1,088 MiB | 542 / $330 | 0.61x | 1.84x | 0.51x | +| v3 plus a 32 MiB hot table, added form (16 dataset loads and k hot loads): the honest card pays the hot loads, this chip pays SRAM only | x4 | 599,040 (a hot load is one SRAM read, no item) | 83.5 | 288 MiB | 144 / $53 | 0.66x at the Mac's g = 0.93 (denominator 126.6 MH/s; the 5090's own g is pending the PC rows) | 1.98x | 1.60x | +| v3 plus a 64 MiB hot table, added form | x4 | 599,040 | 83.5 | 320 MiB | 160 / $61 | 0.71x at the Mac's g = 0.87 (118.4 MH/s; 5090 g pending) | 2.12x | 1.67x | +| v3 at year 4 (cache 512 MiB under option C, dataset 4 GiB), 64 MiB hot table | x4 | 599,040 | 83.5 | 576 MiB | 287 / $130 | 0.71x | 2.12x | 1.31x | +| v3 at year 12 (cache 1 GiB, dataset 8 GiB), 64 MiB hot table | x4 | 599,040 | 83.5 | 1,088 MiB | 542 / $330 | 0.71x | 2.12x | 0.59x | | v3 with the mixer at x8 instead (the next lever, not adopted) | x8 | 1,198,080 | 41.7 | 256 MiB | 128 / $46 | 0.31x | 0.92x | 0.76x | The era draws of spec 1.13.1 cost the chip nothing in this model: the mixer round count is not drawn, the op @@ -49,15 +49,24 @@ chip's cost not at all. Arithmetic, row v3: 36 x 130 = 4,680 ops per item; x 128 = 599,040 per hash; 50 x 10^12 / 599,040 = 83.5 x 10^6 hashes per second; 83.5 / 136.1 = 0.613; x 3 = 1.84; equal silicon (750 - 128) / 750 = 0.829, x 1.84 = 1.53. Hot table rows: 32 MiB x 0.49 mm^2 per MB = 16 mm^2, 64 MiB = 32 mm^2 (the 96 MB column of `sram-mirror.md` -scaled linearly); (750 - 144) / 750 = 0.808 and (750 - 160) / 750 = 0.787. Year 4 and 12 rows: the mirror of +scaled linearly); (750 - 144) / 750 = 0.808 and (750 - 160) / 750 = 0.787. The honest denominator in the added +form is the v2 rate times `g`, the card's measured ratio with the hot loads added: on the M5 Max tonight +`g = 0.93 / 0.87 / 0.83` at 32 / 64 / 96 MiB (the cache agent, relayed by the coordinator at 21:23 UTC; +`docs/plans/hot-table.md` carries the runs); the 5090's and the 9070 XT's `g` are the PC rows, owed, and until they +land the row carries the Mac's `g` against the 5090's rate, which is a mixed figure and is marked so. Year 4 and 12 rows: the mirror of `sram-mirror.md` section 4 at N5 for 512 MiB and 1 GiB plus the 64 MiB table, at today's density (the node of those years is denser by about 1.8x at year 10 on the trend the same file cites; the row is a floor on the area, not a forecast). ## 3. The margin, plainly -The combined headline row reads 1.84x with the 3x factor at an equal integer budget, 1.5x with the SRAM area -deducted. The claim is "under 2x", and the margin is thin: +The combined headline row without the hot table reads 1.84x with the 3x factor at an equal integer budget, 1.5x +with the SRAM area deducted. With the hot table in the added form it reads 1.98x (32 MiB) and 2.12x (64 MiB) at +an equal integer budget on the Mac's `g`, 1.60x and 1.67x with the SRAM deducted: against THIS chip the added hot +table is a cost to the honest card and none to the chip, so it moves the row the wrong way, by the card's own +`g`; whether it is adopted is the DRAM-only chip's question (`docs/plans/hot-table.md`), not this one's. The claim +is "under 2x" on the equal-silicon convention and on the equal-budget convention without the hot table; with a +64 MiB table on the equal-budget convention it is not, and the margin everywhere is thin: - the 3x fixed-function factor is approximate and from memory; at 3.3x the equal-budget row reads 2.0x; - the denominator is one card's measured rate on one night (136.1 against 139.7 the night before: 2.6% apart); @@ -76,8 +85,9 @@ order: at the edge of the 10 ms gate; the measured v3 row of `docs/plans/mixer-x4.md` section 6 is what to scale from now, and whether a 2019-class laptop core (unmeasured, O-1.14) passes 10 ms is what decides it. 2. The hot table: adopted or not on the PC rows (`docs/plans/hot-table.md`); in the added form it costs the GPU - nothing it was not already paying in cache misses and the chip die area only, so it is the second lever for the - chip only through area and the first against a DRAM-only chip. + 7 to 17 percent on the Mac and the chip die area only, so against this chip it is a lever in the wrong + direction and against a DRAM-only chip the first lever; if it is adopted, the mixer must carry the extra `1/g` + (x8 at g = 0.87 reads 1.06x at the equal budget, 0.84x with the SRAM deducted). ## 4. What this does not settle From 147db8db44cef8a90f1fbb34d3dbff2746a83940 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:38:50 +0000 Subject: [PATCH 094/158] mixer x8 candidate beside x4 (coordinator's rule, 5 October 2026 21:30 UTC): LoadClass::MX8 ("mx8", same stream, keys (r m + j + 1) x 0x9E3779B9), the two candidate packs mx8-genesis and mx8-devnet-epoch0 (generator 2 with the class in the id; the vectors are re-cut through the seam after the x4/x8 choice), tests/mixer.rs fuzz takes IGNEUM_MIXER_CLASS, the PC playbooks carry the x8 packs and the gfx1036 fallback; mixer-x4.md: the Metal fuzz row Co-Authored-By: Claude Fable 5.1 --- docs/plans/mixer-x4.md | 2 +- igneum-pow/src/generator.rs | 14 +- igneum-pow/tests/mixer.rs | 30 +- .../mx8-devnet-epoch0/kernel.cl | 279 +++++++++++++ .../mx8-devnet-epoch0/kernel.cu | 164 ++++++++ .../mx8-devnet-epoch0/kernel_bound.cl | 373 ++++++++++++++++++ .../mx8-devnet-epoch0/kernel_bound.cu | 123 ++++++ .../mx8-devnet-epoch0/memhard.h | 109 +++++ .../mx8-devnet-epoch0/memhard.metal | 107 +++++ .../mx8-devnet-epoch0/program.h | 63 +++ .../mx8-devnet-epoch0/program.json | 131 ++++++ .../mx8-devnet-epoch0/program.metal | 109 +++++ .../mx8-devnet-epoch0/program_bound.metal | 111 ++++++ .../mx8-devnet-epoch0/vectors.h | 57 +++ .../mx8-devnet-epoch0/vectors.json | 36 ++ .../packs-ca2-mixer/mx8-genesis/kernel.cl | 279 +++++++++++++ .../packs-ca2-mixer/mx8-genesis/kernel.cu | 164 ++++++++ .../mx8-genesis/kernel_bound.cl | 373 ++++++++++++++++++ .../mx8-genesis/kernel_bound.cu | 123 ++++++ .../packs-ca2-mixer/mx8-genesis/memhard.h | 109 +++++ .../packs-ca2-mixer/mx8-genesis/memhard.metal | 107 +++++ .../packs-ca2-mixer/mx8-genesis/program.h | 63 +++ .../packs-ca2-mixer/mx8-genesis/program.json | 131 ++++++ .../packs-ca2-mixer/mx8-genesis/program.metal | 109 +++++ .../mx8-genesis/program_bound.metal | 111 ++++++ .../packs-ca2-mixer/mx8-genesis/vectors.h | 57 +++ .../packs-ca2-mixer/mx8-genesis/vectors.json | 36 ++ relay/playbooks/mixer-x4-5090-bench.ps1 | 4 +- relay/playbooks/mixer-x4-9070-bench.ps1 | 8 +- 29 files changed, 3365 insertions(+), 17 deletions(-) create mode 100644 proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cl create mode 100644 proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cu create mode 100644 proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.h create mode 100644 proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.metal create mode 100644 proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.h create mode 100644 proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.json create mode 100644 proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.metal create mode 100644 proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program_bound.metal create mode 100644 proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.h create mode 100644 proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.json create mode 100644 proto-cuda/packs-ca2-mixer/mx8-genesis/kernel.cl create mode 100644 proto-cuda/packs-ca2-mixer/mx8-genesis/kernel.cu create mode 100644 proto-cuda/packs-ca2-mixer/mx8-genesis/kernel_bound.cl create mode 100644 proto-cuda/packs-ca2-mixer/mx8-genesis/kernel_bound.cu create mode 100644 proto-cuda/packs-ca2-mixer/mx8-genesis/memhard.h create mode 100644 proto-cuda/packs-ca2-mixer/mx8-genesis/memhard.metal create mode 100644 proto-cuda/packs-ca2-mixer/mx8-genesis/program.h create mode 100644 proto-cuda/packs-ca2-mixer/mx8-genesis/program.json create mode 100644 proto-cuda/packs-ca2-mixer/mx8-genesis/program.metal create mode 100644 proto-cuda/packs-ca2-mixer/mx8-genesis/program_bound.metal create mode 100644 proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.h create mode 100644 proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.json diff --git a/docs/plans/mixer-x4.md b/docs/plans/mixer-x4.md index 3f812f56..2d9d013d 100644 --- a/docs/plans/mixer-x4.md +++ b/docs/plans/mixer-x4.md @@ -230,7 +230,7 @@ x4 build on the GPU is the row the measure lock will settle. | v3 edge (`tests/mixer.rs`): items 0, 1, 2^28 - 1 and 2^32 - 1 by hand at m = 1, 2, 4, 8 on a 2^14-word cache; words 0, 15, 16, 17, MASK - 1, MASK through the interpreter's fetch path; the index wrap at MASK + 1 | same | pass | | v3 determinism (`tests/mixer.rs`): two independent epochs, every vector and every emitted file equal, and equal to the pinned pack | same | pass | | Metal and Apple OpenCL on the two pinned v3 packs | section 6.2 | 3/3 standalone, 3/3 in batch, 96 of 96 lanes, dataset head, MASK word and 64 samples, one fingerprint per pack across both harnesses | -| Metal fuzz: the 200 packs, 4 units each standalone and the top-256 unit inside a 512-nonce batch at base 4,294,967,040; every tenth pack on Apple OpenCL as well | `packbench --pack --batches 1 --batch-log2 9 --batch-base 4294967040` | see below | +| Metal fuzz: the 200 packs, 4 units each standalone and the top-256 unit inside a 512-nonce batch at base 4,294,967,040; every tenth pack on Apple OpenCL as well | `packbench --pack --batches 1 --batch-log2 9 --batch-base 4294967040` (Metal), `igneum-bench-cl-igneum-genesis-mh --bench-pack --pack --batches 1 --batch-log2 10` (Apple OpenCL), `with-lock.sh run`, 21:22 to 21:24 UTC | Metal 200 of 200 packs PASS (800 of 800 standalone units, 200 of 200 inside the wrapping window, cache and dataset self-tests on every pack); Apple OpenCL 20 of 20 packs PASS (the three vectors.h units, the self-tests); a first run with a packbench built before the `--batch-base` cherry-pick reported 200 of 200 FAIL on an empty RESULT line and was read as such (the watcher rule), the harness rebuilt and the run repeated | ### 6.4 Timings (`with-lock.sh measure`) diff --git a/igneum-pow/src/generator.rs b/igneum-pow/src/generator.rs index 0c22b784..00a05124 100644 --- a/igneum-pow/src/generator.rs +++ b/igneum-pow/src/generator.rs @@ -400,6 +400,12 @@ impl LoadClass { self.era.map(|e| e.layout()).unwrap_or(crate::memhard::Layout::LINEAR) } + /// The x8 candidate beside [`LoadClass::MX4`] (coordinator's rule of 5 October 2026, 21:30 UTC: x8 enters v3 if + /// the per-warp verify stays under 10 ms on one Mac core and the daily 1 GiB build under 1 s on every card): + /// the same loads and growth rule, the mixer applied 8 times per round. Name "mx8". + pub const MX8: LoadClass = + LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 8, growth: true }; + /// A fixed width (1, 4 or 16 words) with `load_slots` loads per program. pub fn fixed(width_words: u8, load_slots: u8) -> LoadClass { let mut mix = [0u8; 3]; @@ -496,6 +502,9 @@ impl LoadClass { if s == "mx4" { return Some(LoadClass::MX4); } + if s == "mx8" { + return Some(LoadClass::MX8); + } // the mixer suffix: "...m" then an optional "g" let (s, growth) = match s.strip_suffix('g') { Some(base) if base.rsplit_once('m').map(|(_, d)| !d.is_empty() && d.bytes().all(|b| b.is_ascii_digit())).unwrap_or(false) => (base, true), @@ -589,7 +598,7 @@ impl LoadClass { *self == LoadClass::V2 } - /// "v2", "w4", "w16", "w64", "w64x4", "mix50-35-15", "mix25-50-25x8", "scr4k32"; "mx4" for the v3 construction; + /// "v2", "w4", "w16", "w64", "w64x4", "mix50-35-15", "mix25-50-25x8", "scr4k32"; "mx4" for the v3 construction, "mx8" for its x8 candidate; /// any other mixer setting appends "m" and, with the growth rule, "g" ("v2m2", "w16m4g"). /// An era class is the base name with "-era" appended ("w4-era401998a5", "mx4-era..."). /// A hot class appends "hotk[a]" ("hot64k4", "scr4k32+hot64k4a"; measured and not adopted). @@ -615,6 +624,9 @@ impl LoadClass { if *self == LoadClass::MX4 { return "mx4".to_string(); } + if *self == LoadClass::MX8 { + return "mx8".to_string(); + } let loads = LoadClass { mixer_mult: 1, growth: false, ..*self }; let base = if loads.is_v2() { "v2".to_string() diff --git a/igneum-pow/tests/mixer.rs b/igneum-pow/tests/mixer.rs index bd40c188..78de4768 100644 --- a/igneum-pow/tests/mixer.rs +++ b/igneum-pow/tests/mixer.rs @@ -11,7 +11,7 @@ //! 4. Determinism: two independent epochs of the same seed and day agree on every vector and every emitted file. use igneum_pow::emit::{export_pack, vectors_json}; -use igneum_pow::generator::{generate_from_seed_bytes, generate_from_seed_bytes_program_class, Op, Program, ProgramClass, GENERATOR_VERSION_V3, INSTR_COUNT, V3_CLASS}; +use igneum_pow::generator::{generate_from_seed_bytes, generate_from_seed_bytes_class, generate_from_seed_bytes_program_class, LoadClass, Op, Program, ProgramClass, GENERATOR_VERSION_V3, INSTR_COUNT, V3_CLASS}; use igneum_pow::memhard::{derive_item, mixer, round_key, Cache, MixParams, Shape}; use igneum_pow::seed::{day_key, SplitMix64}; use igneum_pow::verify::{DatasetMode, DatasetSource, Epoch}; @@ -20,9 +20,11 @@ use std::path::PathBuf; const DAY: &str = "2026-10-03"; -fn contract(p: &Program, seed: &str) { - assert_eq!(p.generator, GENERATOR_VERSION_V3); - assert_eq!(p.class, V3_CLASS); +fn contract(p: &Program, seed: &str, class: LoadClass) { + if class == V3_CLASS { + assert_eq!(p.generator, GENERATOR_VERSION_V3); + } + assert_eq!(p.class, class); assert_eq!(p.instrs.len(), INSTR_COUNT); assert_eq!(p.instrs.iter().filter(|i| i.op == Op::Load).count(), 16); for (k, i) in p.instrs.iter().enumerate() { @@ -56,9 +58,13 @@ fn write_pack_with_bases(dir: &PathBuf, e: &Epoch, day: &str, bases: &[u32], sou fn fuzz_v3_programs_cpu() { let n: usize = std::env::var("IGNEUM_MIXER_FUZZ").ok().and_then(|s| s.parse().ok()).unwrap_or(200); let out = std::env::var("IGNEUM_MIXER_PACKS_OUT").ok().map(PathBuf::from); + // IGNEUM_MIXER_CLASS=mx8 fuzzes the x8 candidate as a load class (generator 2 with the class in the id); the + // default is V3_CLASS through the seam + let class = std::env::var("IGNEUM_MIXER_CLASS").ok().map(|s| LoadClass::parse(&s).expect("a load class")).unwrap_or(V3_CLASS); let mut rng = SplitMix64::new(0x6967_6e65_756d_2d6d); // "igneum-m" - let shape = Shape::for_class(&V3_CLASS); - assert_eq!(shape, Shape { mixer_mult: 4, cache_log2_words: 26 }); + let shape = Shape::for_class(&class); + assert_eq!(shape.cache_log2_words, 26); + assert!(shape.mixer_mult > 1); // one memory-hard source per dataset size (the 256 MiB cache fill is 0.2 s each) let mut mh: HashMap = HashMap::new(); let mut manifest = String::from("pack\tlog2\tprogram_id\tbases\n"); @@ -66,8 +72,12 @@ fn fuzz_v3_programs_cpu() { let mut wraps = 0usize; for i in 0..n { let seed = format!("igneum-mixer-fuzz/{i}"); - let p = generate_from_seed_bytes_program_class(&seed, seed.as_bytes(), ProgramClass::V3, None); - contract(&p, &seed); + let p = if class == V3_CLASS { + generate_from_seed_bytes_program_class(&seed, seed.as_bytes(), ProgramClass::V3, None) + } else { + generate_from_seed_bytes_class(&seed, seed.as_bytes(), class) + }; + contract(&p, &seed, class); let b0 = (rng.below(8) as u32) * 32; let b1 = 0x8000_0000u32.wrapping_sub(256).wrapping_add((rng.below(16) as u32) * 32); let b2 = 0xffff_ff00u32.wrapping_add((rng.below(8) as u32) * 32); @@ -85,7 +95,7 @@ fn fuzz_v3_programs_cpu() { units += 1; } if let Some(dir) = &out { - let pack_name = format!("fuzz-{i:03}-mx4-l{log2}"); + let pack_name = format!("fuzz-{i:03}-{}-l{log2}", class.name()); write_pack_with_bases(&dir.join(&pack_name), &e, DAY, &bases, "igneum-pow tests/mixer.rs fuzz"); manifest.push_str(&format!( "{pack_name}\t{log2}\t{:016x}\t{}\n", @@ -95,7 +105,7 @@ fn fuzz_v3_programs_cpu() { } mh.insert(log2, e.dataset); } - println!("fuzz: {n} v3 programs, {units} units on the CPU, {wraps} units in the top 256 nonces"); + println!("fuzz: {n} {} programs, {units} units on the CPU, {wraps} units in the top 256 nonces", class.name()); assert_eq!(units, 4 * n); assert_eq!(wraps, n); if let Some(dir) = &out { diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cl b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cl new file mode 100644 index 00000000..e4f3bb63 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cl @@ -0,0 +1,279 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 8 x seed-parameterised mixer + one 64-byte cache read, then 8 x final mixer (class v3, mixer multiplier 8, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 8 x mixer + cache line s[0] & mask; 8 x final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 8u + j + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = mul_hi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = mul_hi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = mul_hi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = mul_hi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = mul_hi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = mul_hi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = mul_hi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = mul_hi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = mul_hi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cu b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cu new file mode 100644 index 00000000..c129edee --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cu @@ -0,0 +1,164 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 4); // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = __umulhi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = __umulhi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = __umulhi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = __umulhi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = __umulhi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = __umulhi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r7, 8); // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = __umulhi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = __umulhi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = __umulhi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cl b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cl new file mode 100644 index 00000000..51b5d85d --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cl @@ -0,0 +1,373 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 8 x seed-parameterised mixer + one 64-byte cache read, then 8 x final mixer (class v3, mixer multiplier 8, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 8 x mixer + cache line s[0] & mask; 8 x final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 8u + j + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x667d0fbdu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x7b8e5963u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x7b8e5963u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x31c67e5eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0x31c67e5eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4529ddc6u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4529ddc6u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xef19d6d8u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xef19d6d8u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xaccf6211u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xaccf6211u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xda0aed32u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0xda0aed32u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xabc6df31u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0xabc6df31u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x667d0fbdu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = mul_hi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = mul_hi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = mul_hi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = mul_hi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = mul_hi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = mul_hi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = mul_hi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = mul_hi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = mul_hi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = mul_hi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = mul_hi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = mul_hi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = mul_hi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = mul_hi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = mul_hi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = mul_hi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = mul_hi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = mul_hi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cu b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cu new file mode 100644 index 00000000..9a46b748 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cu @@ -0,0 +1,123 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 4); // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = __umulhi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = __umulhi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = __umulhi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = __umulhi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = __umulhi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = __umulhi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r7, 8); // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = __umulhi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = __umulhi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = __umulhi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.h b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.h new file mode 100644 index 00000000..b16f7a4b --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.h @@ -0,0 +1,109 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 8 x seed-parameterised mixer + one 64-byte cache read, then 8 x final mixer (class v3, mixer multiplier 8, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 8 x mixer + cache line s[0] & mask; 8 x final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint32_t r = 0u; r < 8u; ++r) { + for (uint32_t j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 8u + j + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint32_t j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.metal b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.metal new file mode 100644 index 00000000..d9fed7f5 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.metal @@ -0,0 +1,107 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 8 x seed-parameterised mixer + one 64-byte cache read, then 8 x final mixer (class v3, mixer multiplier 8, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0xceed56d7u ^ prev[4]; + x[5] = 0x9ba270d2u ^ prev[5]; + x[6] = 0x82caab2du ^ prev[6]; + x[7] = 0x81ebce0eu ^ prev[7]; + x[8] = 0x12b6ecf1u ^ prev[8]; + x[9] = 0xd0f3fd7cu ^ prev[9]; + x[10] = 0xd872eefeu ^ prev[10]; + x[11] = 0xc158c7bdu ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xc6892460u + rk)) * 0xf351d601u; + s[1] = (s[1] ^ (0x25b7228au + rk)) * 0xa3bb398fu; + s[2] = (s[2] ^ (0xcd515004u + rk)) * 0xb5a09e35u; + s[3] = (s[3] ^ (0x2846527au + rk)) * 0x7509c9c1u; + s[4] = (s[4] ^ (0xa6324241u + rk)) * 0x6bbf31e9u; + s[5] = (s[5] ^ (0x36e3ec53u + rk)) * 0xfc849a79u; + s[6] = (s[6] ^ (0x82961bacu + rk)) * 0xded91851u; + s[7] = (s[7] ^ (0x0f97ba7du + rk)) * 0x8d9113d1u; + s[8] = (s[8] ^ (0xb6f921a9u + rk)) * 0x0ff15225u; + s[9] = (s[9] ^ (0x3ada24e5u + rk)) * 0x3a5bdd41u; + s[10] = (s[10] ^ (0xde20ab91u + rk)) * 0xab533435u; + s[11] = (s[11] ^ (0x5378eeb2u + rk)) * 0xe1c55ad5u; + s[12] = (s[12] ^ (0x7d161662u + rk)) * 0xe6d3bd0du; + s[13] = (s[13] ^ (0x89353cc1u + rk)) * 0x9d9ffbbdu; + s[14] = (s[14] ^ (0xb1aa03a2u + rk)) * 0xbb2a3cf3u; + s[15] = (s[15] ^ (0x788acae6u + rk)) * 0x50a7c08du; + MH_QR(s[0], s[4], s[8], s[12], 17u, 12u, 20u, 23u) MH_QR(s[1], s[5], s[9], s[13], 17u, 12u, 20u, 23u) + MH_QR(s[2], s[6], s[10], s[14], 17u, 12u, 20u, 23u) MH_QR(s[3], s[7], s[11], s[15], 17u, 12u, 20u, 23u) + MH_QR(s[0], s[5], s[10], s[15], 7u, 3u, 27u, 16u) MH_QR(s[1], s[6], s[11], s[12], 7u, 3u, 27u, 16u) + MH_QR(s[2], s[7], s[8], s[13], 7u, 3u, 27u, 16u) MH_QR(s[3], s[4], s[9], s[14], 7u, 3u, 27u, 16u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 8 x mixer + cache line s[0] & mask; 8 x final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0xceed56d7u; + s[1] = 0x9ba270d2u; + s[2] = 0x82caab2du; + s[3] = 0x81ebce0eu; + s[4] = 0x12b6ecf1u; + s[5] = 0xd0f3fd7cu; + s[6] = 0xd872eefeu; + s[7] = 0xc158c7bdu; + s[8] = t * 0xf351d601u + 0xc6892460u; + s[9] = t * 0xa3bb398fu + 0x25b7228au; + s[10] = t * 0xb5a09e35u + 0xcd515004u; + s[11] = t * 0x7509c9c1u + 0x2846527au; + s[12] = t * 0x6bbf31e9u + 0xa6324241u; + s[13] = t * 0xfc849a79u + 0x36e3ec53u; + s[14] = t * 0xded91851u + 0x82961bacu; + s[15] = t * 0x8d9113d1u + 0x0f97ba7du; + for (uint r = 0u; r < 8u; ++r) { + for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 8u + j + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.h b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.h new file mode 100644 index 00000000..3eacb559 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.h @@ -0,0 +1,63 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_SEED_BYTES_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" +#define IGNEUM_GENERATOR 2 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0xa696c464d9e9649bull +#define IGNEUM_DAY_STRING "bytes:69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY_BYTES_HEX "69676e65756d2d6461792ffa50000000000000" +#define IGNEUM_DAY0 0xceed56d7u +#define IGNEUM_DAY1 0x9ba270d2u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 add=13 mulhi=9 shfl=5 sub=5 mad=4 rotl=4 xor=3 or=2 rotr=2 mul=1" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "mx8" +#define IGNEUM_CLASS_MIXER_MULT 8 +#define IGNEUM_CACHE_GROWTH 1 // 1: cache words = 2^(26 + doublings(day)), doublings = floor(log2(1 + day / 1460)) +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u } +#define IGNEUM_KEY_INIT { 0xceed56d7u, 0x9ba270d2u, 0x82caab2du, 0x81ebce0eu, 0x12b6ecf1u, 0xd0f3fd7cu, 0xd872eefeu, 0xc158c7bdu } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIXER_MULT 8 // mixer applications per round and after the last read (class v3, docs/plans/mixer-x4.md) +#define IGNEUM_MIX_ROT_INIT { 17u, 12u, 20u, 23u, 7u, 3u, 27u, 16u } +#define IGNEUM_MIX_MUL_INIT { 0xf351d601u, 0xa3bb398fu, 0xb5a09e35u, 0x7509c9c1u, 0x6bbf31e9u, 0xfc849a79u, 0xded91851u, 0x8d9113d1u, 0x0ff15225u, 0x3a5bdd41u, 0xab533435u, 0xe1c55ad5u, 0xe6d3bd0du, 0x9d9ffbbdu, 0xbb2a3cf3u, 0x50a7c08du } +#define IGNEUM_MIX_RC_INIT { 0xc6892460u, 0x25b7228au, 0xcd515004u, 0x2846527au, 0xa6324241u, 0x36e3ec53u, 0x82961bacu, 0x0f97ba7du, 0xb6f921a9u, 0x3ada24e5u, 0xde20ab91u, 0x5378eeb2u, 0x7d161662u, 0x89353cc1u, 0xb1aa03a2u, 0x788acae6u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.json b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.json new file mode 100644 index 00000000..3f423788 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.json @@ -0,0 +1,131 @@ +{ + "format": "igneum-program-pack-3", + "generator": 2, + "attempt": 0, + "program_id": "0xa696c464d9e9649b", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07", + "seed_words": ["0x667d0fbd", "0x7b8e5963", "0x31c67e5e", "0x4529ddc6", "0xef19d6d8", "0xaccf6211", "0xda0aed32", "0xabc6df31"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "load_class": "mx8", + "mixer_mult": 8, + "cache_growth": true, + "mixer": "class v3 (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): every mixer application of the item derivation is 8 applications with round keys (r * 8 + j + 1) * 0x9E3779B9, the 8 dependent cache reads per item unchanged; cache growth rule option C: cache words = 2^(26 + doublings(day)), dataset words = 2^(genesis_log2 + doublings(day)), doublings(day) = floor(log2(1 + day / 1460)) for day = days since genesis", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "op_mix": {"load": 16, "add": 13, "mulhi": 9, "shfl": 5, "sub": 5, "mad": 4, "rotl": 4, "xor": 3, "or": 2, "rotr": 2, "mul": 1}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "day_bytes": "69676e65756d2d6461792ffa50000000000000", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0xceed56d7", + "d1": "0x9ba270d2", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0xceed56d7", "0x9ba270d2", "0x82caab2d", "0x81ebce0e", "0x12b6ecf1", "0xd0f3fd7c", "0xd872eefe", "0xc158c7bd"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [17, 12, 20, 23, 7, 3, 27, 16], "mul": ["0xf351d601", "0xa3bb398f", "0xb5a09e35", "0x7509c9c1", "0x6bbf31e9", "0xfc849a79", "0xded91851", "0x8d9113d1", "0x0ff15225", "0x3a5bdd41", "0xab533435", "0xe1c55ad5", "0xe6d3bd0d", "0x9d9ffbbd", "0xbb2a3cf3", "0x50a7c08d"], "rc": ["0xc6892460", "0x25b7228a", "0xcd515004", "0x2846527a", "0xa6324241", "0x36e3ec53", "0x82961bac", "0x0f97ba7d", "0xb6f921a9", "0x3ada24e5", "0xde20ab91", "0x5378eeb2", "0x7d161662", "0x89353cc1", "0xb1aa03a2", "0x788acae6"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "mixer_mult": 8, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: for j in 0..7: s = M(s, rk = (r * 8 + j + 1) * 0x9E3779B9); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then for j in 0..7: s = M(s, rk = (64 + j + 1) * 0x9E3779B9); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1}, + {"i": 1, "op": "shfl", "dst": 2, "src": 0, "src2": 7, "imm": "0xe3c2f9cb", "imm2": "0xde0bea4e", "rot": 6, "bit": 9, "mask": 4, "width": 1}, + {"i": 2, "op": "add", "dst": 3, "src": 2, "src2": 0, "imm": "0x2cccb6ca", "imm2": "0x642e66db", "rot": 27, "bit": 10, "mask": 2, "width": 1}, + {"i": 3, "op": "rotl", "dst": 0, "src": 2, "src2": 1, "imm": "0xb59e83b2", "imm2": "0x19c26fb9", "rot": 19, "bit": 20, "mask": 4, "width": 1}, + {"i": 4, "op": "rotr", "dst": 7, "src": 6, "src2": 5, "imm": "0x6f055f55", "imm2": "0x550e4ea1", "rot": 31, "bit": 20, "mask": 4, "width": 1}, + {"i": 5, "op": "add", "dst": 7, "src": 4, "src2": 7, "imm": "0xee02465f", "imm2": "0xc1535555", "rot": 31, "bit": 21, "mask": 4, "width": 1}, + {"i": 6, "op": "mulhi", "dst": 1, "src": 7, "src2": 5, "imm": "0x7826a6a7", "imm2": "0x946f7818", "rot": 18, "bit": 21, "mask": 8, "width": 1}, + {"i": 7, "op": "load", "dst": 4, "src": 2, "src2": 0, "imm": "0x5d080878", "imm2": "0xdf885578", "rot": 5, "bit": 18, "mask": 4, "width": 1}, + {"i": 8, "op": "load", "dst": 7, "src": 4, "src2": 1, "imm": "0x875bbb36", "imm2": "0x594a838f", "rot": 24, "bit": 4, "mask": 2, "width": 1}, + {"i": 9, "op": "load", "dst": 0, "src": 3, "src2": 1, "imm": "0xe5e607c2", "imm2": "0xa5cd9f75", "rot": 26, "bit": 28, "mask": 4, "width": 1}, + {"i": 10, "op": "load", "dst": 5, "src": 1, "src2": 3, "imm": "0xea3f7b43", "imm2": "0x10c8d4e7", "rot": 5, "bit": 29, "mask": 8, "width": 1}, + {"i": 11, "op": "load", "dst": 1, "src": 5, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1}, + {"i": 12, "op": "mulhi", "dst": 3, "src": 5, "src2": 7, "imm": "0xbfd5615c", "imm2": "0xd8224ae2", "rot": 21, "bit": 12, "mask": 2, "width": 1}, + {"i": 13, "op": "load", "dst": 1, "src": 3, "src2": 5, "imm": "0x35c07cc5", "imm2": "0xe82db54f", "rot": 7, "bit": 6, "mask": 8, "width": 1}, + {"i": 14, "op": "sub", "dst": 0, "src": 3, "src2": 0, "imm": "0x587ee0f1", "imm2": "0xfd23eefd", "rot": 16, "bit": 21, "mask": 16, "width": 1}, + {"i": 15, "op": "mad", "dst": 5, "src": 1, "src2": 3, "imm": "0x6974dd29", "imm2": "0xc8148960", "rot": 3, "bit": 11, "mask": 2, "width": 1}, + {"i": 16, "op": "mulhi", "dst": 6, "src": 1, "src2": 7, "imm": "0x3072c3c6", "imm2": "0x55ee21f8", "rot": 26, "bit": 1, "mask": 1, "width": 1}, + {"i": 17, "op": "add", "dst": 5, "src": 2, "src2": 2, "imm": "0x697b3d00", "imm2": "0x8b965b57", "rot": 9, "bit": 28, "mask": 1, "width": 1}, + {"i": 18, "op": "mulhi", "dst": 0, "src": 6, "src2": 3, "imm": "0x2910cacb", "imm2": "0x6ac79431", "rot": 7, "bit": 6, "mask": 4, "width": 1}, + {"i": 19, "op": "rotr", "dst": 5, "src": 3, "src2": 0, "imm": "0xed96a94a", "imm2": "0x4c988c10", "rot": 24, "bit": 21, "mask": 8, "width": 1}, + {"i": 20, "op": "mulhi", "dst": 5, "src": 2, "src2": 7, "imm": "0x40d2fc76", "imm2": "0x2f7c7eca", "rot": 13, "bit": 8, "mask": 4, "width": 1}, + {"i": 21, "op": "add", "dst": 1, "src": 0, "src2": 5, "imm": "0xebcf247a", "imm2": "0x6d7e8d05", "rot": 31, "bit": 1, "mask": 16, "width": 1}, + {"i": 22, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1}, + {"i": 23, "op": "mulhi", "dst": 1, "src": 5, "src2": 7, "imm": "0xfdfe72fd", "imm2": "0x735eeb8d", "rot": 30, "bit": 25, "mask": 8, "width": 1}, + {"i": 24, "op": "sub", "dst": 2, "src": 5, "src2": 0, "imm": "0x32cf1258", "imm2": "0xd813deb6", "rot": 30, "bit": 6, "mask": 16, "width": 1}, + {"i": 25, "op": "add", "dst": 7, "src": 4, "src2": 2, "imm": "0x08ffa6c7", "imm2": "0x699ef1bb", "rot": 7, "bit": 2, "mask": 16, "width": 1}, + {"i": 26, "op": "shfl", "dst": 3, "src": 4, "src2": 5, "imm": "0x6f53c70d", "imm2": "0x3357513f", "rot": 3, "bit": 26, "mask": 2, "width": 1}, + {"i": 27, "op": "add", "dst": 7, "src": 1, "src2": 4, "imm": "0xe60fea84", "imm2": "0xb4ead2fb", "rot": 14, "bit": 14, "mask": 1, "width": 1}, + {"i": 28, "op": "add", "dst": 3, "src": 1, "src2": 0, "imm": "0x65c76dab", "imm2": "0x8f30d21d", "rot": 24, "bit": 6, "mask": 1, "width": 1}, + {"i": 29, "op": "load", "dst": 2, "src": 1, "src2": 2, "imm": "0x82fad9a6", "imm2": "0x8c6358db", "rot": 7, "bit": 31, "mask": 2, "width": 1}, + {"i": 30, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x6e947ee0", "imm2": "0xaf9a2dda", "rot": 2, "bit": 30, "mask": 4, "width": 1}, + {"i": 31, "op": "load", "dst": 2, "src": 5, "src2": 1, "imm": "0x608bb7ce", "imm2": "0x4be663db", "rot": 19, "bit": 1, "mask": 16, "width": 1}, + {"i": 32, "op": "shfl", "dst": 1, "src": 7, "src2": 6, "imm": "0x88e52e20", "imm2": "0x77647269", "rot": 20, "bit": 25, "mask": 4, "width": 1}, + {"i": 33, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1}, + {"i": 34, "op": "add", "dst": 4, "src": 2, "src2": 3, "imm": "0x0480debe", "imm2": "0xc7ce690c", "rot": 12, "bit": 21, "mask": 16, "width": 1}, + {"i": 35, "op": "shfl", "dst": 3, "src": 7, "src2": 5, "imm": "0xa73f59de", "imm2": "0x84b9e329", "rot": 21, "bit": 27, "mask": 8, "width": 1}, + {"i": 36, "op": "add", "dst": 7, "src": 1, "src2": 7, "imm": "0xc53b542e", "imm2": "0xe10c2c95", "rot": 21, "bit": 2, "mask": 4, "width": 1}, + {"i": 37, "op": "xor", "dst": 5, "src": 7, "src2": 4, "imm": "0x81cd7b0e", "imm2": "0x21a51823", "rot": 12, "bit": 10, "mask": 2, "width": 1}, + {"i": 38, "op": "or", "dst": 2, "src": 1, "src2": 3, "imm": "0x7894e657", "imm2": "0xf8e4b972", "rot": 18, "bit": 9, "mask": 4, "width": 1}, + {"i": 39, "op": "mulhi", "dst": 1, "src": 0, "src2": 4, "imm": "0xbee8421f", "imm2": "0x070888a8", "rot": 20, "bit": 28, "mask": 4, "width": 1}, + {"i": 40, "op": "rotl", "dst": 6, "src": 1, "src2": 4, "imm": "0x4609857a", "imm2": "0xaeecb156", "rot": 19, "bit": 21, "mask": 1, "width": 1}, + {"i": 41, "op": "mulhi", "dst": 4, "src": 6, "src2": 0, "imm": "0x1a84e1e9", "imm2": "0x9b26bb72", "rot": 28, "bit": 19, "mask": 1, "width": 1}, + {"i": 42, "op": "sub", "dst": 6, "src": 0, "src2": 6, "imm": "0xbf62908e", "imm2": "0xdf03ea88", "rot": 11, "bit": 27, "mask": 16, "width": 1}, + {"i": 43, "op": "shfl", "dst": 6, "src": 3, "src2": 4, "imm": "0xa966241c", "imm2": "0x9c639efa", "rot": 12, "bit": 4, "mask": 4, "width": 1}, + {"i": 44, "op": "load", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1}, + {"i": 45, "op": "xor", "dst": 1, "src": 3, "src2": 6, "imm": "0x006193d0", "imm2": "0xfc2acc3f", "rot": 25, "bit": 1, "mask": 1, "width": 1}, + {"i": 46, "op": "load", "dst": 7, "src": 0, "src2": 6, "imm": "0x4777c4f8", "imm2": "0x3cf0a02f", "rot": 11, "bit": 14, "mask": 4, "width": 1}, + {"i": 47, "op": "load", "dst": 3, "src": 1, "src2": 3, "imm": "0x10692532", "imm2": "0x1a292ea5", "rot": 20, "bit": 23, "mask": 1, "width": 1}, + {"i": 48, "op": "mul", "dst": 5, "src": 3, "src2": 7, "imm": "0xadf5bd13", "imm2": "0xb999de2e", "rot": 23, "bit": 10, "mask": 4, "width": 1}, + {"i": 49, "op": "sub", "dst": 1, "src": 5, "src2": 4, "imm": "0x68ff101e", "imm2": "0xbdaaf46a", "rot": 25, "bit": 23, "mask": 16, "width": 1}, + {"i": 50, "op": "rotl", "dst": 2, "src": 6, "src2": 4, "imm": "0x92d9a412", "imm2": "0x0daf96ea", "rot": 8, "bit": 4, "mask": 4, "width": 1}, + {"i": 51, "op": "add", "dst": 1, "src": 5, "src2": 0, "imm": "0xa900fec4", "imm2": "0x77b9bd43", "rot": 6, "bit": 23, "mask": 2, "width": 1}, + {"i": 52, "op": "load", "dst": 4, "src": 7, "src2": 1, "imm": "0x51392a72", "imm2": "0x99e8bb36", "rot": 11, "bit": 9, "mask": 1, "width": 1}, + {"i": 53, "op": "sub", "dst": 2, "src": 7, "src2": 2, "imm": "0x0ffe2ac7", "imm2": "0x030743df", "rot": 9, "bit": 30, "mask": 16, "width": 1}, + {"i": 54, "op": "xor", "dst": 4, "src": 0, "src2": 4, "imm": "0x9123ff15", "imm2": "0x10c329a7", "rot": 28, "bit": 15, "mask": 2, "width": 1}, + {"i": 55, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1}, + {"i": 56, "op": "load", "dst": 2, "src": 4, "src2": 3, "imm": "0x239de52c", "imm2": "0xf80bae18", "rot": 15, "bit": 24, "mask": 1, "width": 1}, + {"i": 57, "op": "mad", "dst": 0, "src": 1, "src2": 4, "imm": "0x31dede8e", "imm2": "0xd3f619e6", "rot": 29, "bit": 7, "mask": 2, "width": 1}, + {"i": 58, "op": "load", "dst": 3, "src": 5, "src2": 3, "imm": "0x206437d6", "imm2": "0x28d1c290", "rot": 17, "bit": 28, "mask": 4, "width": 1}, + {"i": 59, "op": "or", "dst": 5, "src": 6, "src2": 3, "imm": "0x8fffd674", "imm2": "0x0507903a", "rot": 26, "bit": 27, "mask": 2, "width": 1}, + {"i": 60, "op": "mad", "dst": 6, "src": 5, "src2": 7, "imm": "0xf572bdb9", "imm2": "0xeda2af31", "rot": 21, "bit": 8, "mask": 2, "width": 1}, + {"i": 61, "op": "rotl", "dst": 4, "src": 2, "src2": 7, "imm": "0x84f12ddf", "imm2": "0x81ef22e1", "rot": 28, "bit": 30, "mask": 1, "width": 1}, + {"i": 62, "op": "mulhi", "dst": 5, "src": 0, "src2": 6, "imm": "0xf6bb45ee", "imm2": "0x6bfb632d", "rot": 22, "bit": 0, "mask": 4, "width": 1}, + {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0x50ec702a", "imm2": "0xae6ee96e", "rot": 20, "bit": 25, "mask": 2, "width": 1} + ] +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.metal b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.metal new file mode 100644 index 00000000..b899adec --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r2 = r2 ^ simd_shuffle_xor(r0, (ushort)4); // 1 + r3 = r3 + r2 + select(0x2cccb6cau, 0x642e66dbu, ((sel >> 10u) & 1u) != 0u); // 2 + r0 = rotl_imm(r0, 19u); // 3 + r7 = rotr_var(r7, r6); // 4 + r7 = r7 + r4 + select(0xee02465fu, 0xc1535555u, ((sel >> 21u) & 1u) != 0u); // 5 + r1 = mulhi(r1, r7); // 6 + r4 = r4 ^ dataset[r2 & MASK]; // 7 + r7 = r7 ^ dataset[r4 & MASK]; // 8 + r0 = r0 ^ dataset[r3 & MASK]; // 9 + r5 = r5 ^ dataset[r1 & MASK]; // 10 + r1 = r1 ^ dataset[r5 & MASK]; // 11 + r3 = mulhi(r3, r5); // 12 + r1 = r1 ^ dataset[r3 & MASK]; // 13 + r0 = r0 - r3; // 14 + r5 = r1 * r3 + r5; // 15 + r6 = mulhi(r6, r1); // 16 + r5 = r5 + r2 + select(0x697b3d00u, 0x8b965b57u, ((sel >> 28u) & 1u) != 0u); // 17 + r0 = mulhi(r0, r6); // 18 + r5 = rotr_var(r5, r3); // 19 + r5 = mulhi(r5, r2); // 20 + r1 = r1 + r0 + select(0xebcf247au, 0x6d7e8d05u, ((sel >> 1u) & 1u) != 0u); // 21 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 22 + r1 = mulhi(r1, r5); // 23 + r2 = r2 - r5; // 24 + r7 = r7 + r4 + select(0x08ffa6c7u, 0x699ef1bbu, ((sel >> 2u) & 1u) != 0u); // 25 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 26 + r7 = r7 + r1 + select(0xe60fea84u, 0xb4ead2fbu, ((sel >> 14u) & 1u) != 0u); // 27 + r3 = r3 + r1 + select(0x65c76dabu, 0x8f30d21du, ((sel >> 6u) & 1u) != 0u); // 28 + r2 = r2 ^ dataset[r1 & MASK]; // 29 + r5 = r5 ^ dataset[r7 & MASK]; // 30 + r2 = r2 ^ dataset[r5 & MASK]; // 31 + r1 = r1 ^ simd_shuffle_xor(r7, (ushort)4); // 32 + r4 = r5 * r7 + r4; // 33 + r4 = r4 + r2 + select(0x0480debeu, 0xc7ce690cu, ((sel >> 21u) & 1u) != 0u); // 34 + r3 = r3 ^ simd_shuffle_xor(r7, (ushort)8); // 35 + r7 = r7 + r1 + select(0xc53b542eu, 0xe10c2c95u, ((sel >> 2u) & 1u) != 0u); // 36 + r5 = r5 ^ r7; // 37 + r2 = r2 | r1; // 38 + r1 = mulhi(r1, r0); // 39 + r6 = rotl_imm(r6, 19u); // 40 + r4 = mulhi(r4, r6); // 41 + r6 = r6 - r0; // 42 + r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r4 = r4 ^ dataset[r2 & MASK]; // 44 + r1 = r1 ^ r3; // 45 + r7 = r7 ^ dataset[r0 & MASK]; // 46 + r3 = r3 ^ dataset[r1 & MASK]; // 47 + r5 = r5 * r3; // 48 + r1 = r1 - r5; // 49 + r2 = rotl_imm(r2, 8u); // 50 + r1 = r1 + r5 + select(0xa900fec4u, 0x77b9bd43u, ((sel >> 23u) & 1u) != 0u); // 51 + r4 = r4 ^ dataset[r7 & MASK]; // 52 + r2 = r2 - r7; // 53 + r4 = r4 ^ r0; // 54 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 55 + r2 = r2 ^ dataset[r4 & MASK]; // 56 + r0 = r1 * r4 + r0; // 57 + r3 = r3 ^ dataset[r5 & MASK]; // 58 + r5 = r5 | r6; // 59 + r6 = r5 * r7 + r6; // 60 + r4 = rotl_imm(r4, 28u); // 61 + r5 = mulhi(r5, r0); // 62 + r3 = r3 ^ dataset[r6 & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program_bound.metal b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program_bound.metal new file mode 100644 index 00000000..2f021814 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program_bound.metal @@ -0,0 +1,111 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x667d0fbdu, 0x7b8e5963u, 0x31c67e5eu, 0x4529ddc6u, 0xef19d6d8u, 0xaccf6211u, 0xda0aed32u, 0xabc6df31u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 + r2 = r2 ^ simd_shuffle_xor(r0, (ushort)4); // 1 + r3 = r3 + r2 + select(0x2cccb6cau, 0x642e66dbu, ((sel >> 10u) & 1u) != 0u); // 2 + r0 = rotl_imm(r0, 19u); // 3 + r7 = rotr_var(r7, r6); // 4 + r7 = r7 + r4 + select(0xee02465fu, 0xc1535555u, ((sel >> 21u) & 1u) != 0u); // 5 + r1 = mulhi(r1, r7); // 6 + r4 = r4 ^ dataset[r2 & MASK]; // 7 + r7 = r7 ^ dataset[r4 & MASK]; // 8 + r0 = r0 ^ dataset[r3 & MASK]; // 9 + r5 = r5 ^ dataset[r1 & MASK]; // 10 + r1 = r1 ^ dataset[r5 & MASK]; // 11 + r3 = mulhi(r3, r5); // 12 + r1 = r1 ^ dataset[r3 & MASK]; // 13 + r0 = r0 - r3; // 14 + r5 = r1 * r3 + r5; // 15 + r6 = mulhi(r6, r1); // 16 + r5 = r5 + r2 + select(0x697b3d00u, 0x8b965b57u, ((sel >> 28u) & 1u) != 0u); // 17 + r0 = mulhi(r0, r6); // 18 + r5 = rotr_var(r5, r3); // 19 + r5 = mulhi(r5, r2); // 20 + r1 = r1 + r0 + select(0xebcf247au, 0x6d7e8d05u, ((sel >> 1u) & 1u) != 0u); // 21 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 22 + r1 = mulhi(r1, r5); // 23 + r2 = r2 - r5; // 24 + r7 = r7 + r4 + select(0x08ffa6c7u, 0x699ef1bbu, ((sel >> 2u) & 1u) != 0u); // 25 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 26 + r7 = r7 + r1 + select(0xe60fea84u, 0xb4ead2fbu, ((sel >> 14u) & 1u) != 0u); // 27 + r3 = r3 + r1 + select(0x65c76dabu, 0x8f30d21du, ((sel >> 6u) & 1u) != 0u); // 28 + r2 = r2 ^ dataset[r1 & MASK]; // 29 + r5 = r5 ^ dataset[r7 & MASK]; // 30 + r2 = r2 ^ dataset[r5 & MASK]; // 31 + r1 = r1 ^ simd_shuffle_xor(r7, (ushort)4); // 32 + r4 = r5 * r7 + r4; // 33 + r4 = r4 + r2 + select(0x0480debeu, 0xc7ce690cu, ((sel >> 21u) & 1u) != 0u); // 34 + r3 = r3 ^ simd_shuffle_xor(r7, (ushort)8); // 35 + r7 = r7 + r1 + select(0xc53b542eu, 0xe10c2c95u, ((sel >> 2u) & 1u) != 0u); // 36 + r5 = r5 ^ r7; // 37 + r2 = r2 | r1; // 38 + r1 = mulhi(r1, r0); // 39 + r6 = rotl_imm(r6, 19u); // 40 + r4 = mulhi(r4, r6); // 41 + r6 = r6 - r0; // 42 + r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r4 = r4 ^ dataset[r2 & MASK]; // 44 + r1 = r1 ^ r3; // 45 + r7 = r7 ^ dataset[r0 & MASK]; // 46 + r3 = r3 ^ dataset[r1 & MASK]; // 47 + r5 = r5 * r3; // 48 + r1 = r1 - r5; // 49 + r2 = rotl_imm(r2, 8u); // 50 + r1 = r1 + r5 + select(0xa900fec4u, 0x77b9bd43u, ((sel >> 23u) & 1u) != 0u); // 51 + r4 = r4 ^ dataset[r7 & MASK]; // 52 + r2 = r2 - r7; // 53 + r4 = r4 ^ r0; // 54 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 55 + r2 = r2 ^ dataset[r4 & MASK]; // 56 + r0 = r1 * r4 + r0; // 57 + r3 = r3 ^ dataset[r5 & MASK]; // 58 + r5 = r5 | r6; // 59 + r6 = r5 * r7 + r6; // 60 + r4 = rotl_imm(r4, 28u); // 61 + r5 = mulhi(r5, r0); // 62 + r3 = r3 ^ dataset[r6 & MASK]; // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.h b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.h new file mode 100644 index 00000000..f7899436 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.h @@ -0,0 +1,57 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0x731250f1008c6f8eull, 0xb89b396aa5e33597ull, 0xbc96ebcdb47c61e4ull, 0xac1414cd0fb91152ull, 0x1cc2d1847875555eull, 0x071f703c82efff0cull, 0x21147b8c58bc96e4ull, 0xf412422862c643e6ull, + 0xb8a5c330668e9283ull, 0x994558a2b24a6557ull, 0xb2fb321e884a1fedull, 0xa8f928a38622b10eull, 0x9ea4cfcfc2c96d8bull, 0xc082abc94d5dfb18ull, 0x695239a4a852e0f9ull, 0xef804c526240ce15ull, + 0x624303d31812df0dull, 0xcfd82ed4955f140aull, 0x6569ae6a4a436a9aull, 0xc17f46cf926a7a0cull, 0x721fe9c93267d13bull, 0xf09fe97d5ce6bfbbull, 0x455d853d3913d6a3ull, 0x4fbb5f43bd1af7eaull, + 0x864d689410bb8ed0ull, 0xe8c93188d7f02b04ull, 0xebaafb6120560360ull, 0xde81676bff855560ull, 0xd0b3ae8111aab7c3ull, 0xacd97035767867caull, 0x1970c0b030b99c03ull, 0x09fed95e63215205ull + }, + { // base nonce 4096 + 0x3d4f7e76fa55a96aull, 0xd54574ccb1a27843ull, 0xeb1635ddd5f1e181ull, 0x1e49a6cf24085bd9ull, 0x9f929d79ad731c0eull, 0x5caca654a4bd4f0full, 0x33eb9defb51424b9ull, 0x04f1f3c93663c27eull, + 0xad1c775687a46553ull, 0xc57c600167bc7571ull, 0xaf0b8e2f9a316b22ull, 0xa01088974b376d8eull, 0x14555ccee9288fddull, 0x0d1d576ea396fbe7ull, 0x8cf63b15a58299bfull, 0x49f530db99fd5f01ull, + 0x30324c49b12da61bull, 0xeb6fc871f103ad7bull, 0x8ac3271ce7edfc29ull, 0x2804099c65265affull, 0xc4d9377a427c3337ull, 0x566c1e580b940448ull, 0x790b966beb7bebb3ull, 0xba5da14cc619efe0ull, + 0x5da22ad9ae88fcd7ull, 0x24221e506877ef84ull, 0xfcda89486e6e78caull, 0xfd9a09b3587ba8fbull, 0x4fdb52f5b31ca54aull, 0x9cf4a2730edbf331ull, 0xb28717ae699944bbull, 0x0889c8c6adecb410ull + }, + { // base nonce 1000000 + 0x377dfe4959f008b5ull, 0xd1bc9ed622919f80ull, 0x7c8c576aebc618bbull, 0x7930d463eceb4becull, 0xd7c8081c372c0e96ull, 0xff093d43f46685c2ull, 0x64d59d5e4abdef5cull, 0xd0b6c0699e0d51f8ull, + 0xbf73d4c2e7d7bb5cull, 0xb461b9770521b1b8ull, 0x224eaf296658b9c0ull, 0x81233334fb928726ull, 0xa10c5ee0aecc8251ull, 0xcd1a7e1b3cb970e7ull, 0x573c61ddaaa8fcccull, 0x67dd7ae97a699881ull, + 0x6df554eae8e0482aull, 0x61f528d7eba4cf89ull, 0xc1f241f1f41fc34dull, 0x25e1b51094413da0ull, 0x9be1d90ff6f51065ull, 0x21fc77cf9089f049ull, 0x1fadecae2996b343ull, 0xbd170b4f84703b24ull, + 0x0a7493c2ebd8bfb5ull, 0xd1d6e43170ced136ull, 0xe87fadf2a2c02a52ull, 0x2f873ddc2cf9f022ull, 0xa3963afdf94f3307ull, 0x3dfff37956161da1ull, 0xa60a7a56137b4231ull, 0xb8abcc39f8cb021aull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0x19c6837fu, 0xdf3adfb8u, 0xc5f0629fu, 0x03e38ae8u, 0xc5de2525u, 0xa34a4906u, 0x898367cbu, 0x35b56808u, + 0xb886e673u, 0x1ee29eceu, 0x0cc36c88u, 0xc984181bu, 0x3d9d15f6u, 0x27c7d52au, 0xc268cf69u, 0x99b3c5c2u +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0x8d66c390u; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0xe53754ceu, 0xc6792663u, 0x0ca9fdceu, 0x96159926u, 0xfca71449u, 0x54522663u, 0xd0160df7u, 0x301be5a9u, 0xb0de07c5u, 0xb8f1c964u, 0xcef19286u, 0xc9c7985du, 0x9db14da9u, 0xda9f746du, 0x8747e5a1u, 0x545158e0u, 0xdf31afbbu, 0x4daa9b4cu, 0x1f0762ffu, 0x80a2d158u, 0xf762ad72u, 0x7ee04a69u, 0xd07df0e9u, 0x87cfc4abu, 0x9de7bb05u, 0xcd214af2u, 0xbd15dc85u, 0x89f48a42u, 0xd49c8a27u, 0xbc60a7e1u, 0xafc36693u, 0x4876871fu, 0x703246c4u, 0xbb823e84u, 0xa13f71a1u, 0xf2dd8540u, 0x74b039d1u, 0x84e8a275u, 0x750744bbu, 0x3f690b5fu, 0x5e75ecbdu, 0x36a11cc7u, 0x65e888cfu, 0x2e8538c8u, 0x14f5fec9u, 0x7329f625u, 0x42ad9261u, 0x120aea25u, 0x5374b09fu, 0x4fa85aa3u, 0xcb856f4eu, 0x526707bau, 0x82d847dau, 0xaed963e5u, 0x1516404bu, 0x2e565dddu, 0x46d1dacfu, 0xf7bb9e4cu, 0x61ae3b87u, 0x9daeed43u, 0xcf6bc86fu, 0x24f111d5u, 0x4356a558u, 0x0890de16u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0xebd9055cu, 0x7eaf6b21u, 0x9b610855u, 0x134a8562u, 0xb10059bau, 0x7f459e58u, 0x8f3c7873u, 0x3523e609u, + 0x75b8f4cau, 0x750d31b4u, 0x0dae0782u, 0x26f10015u, 0x7ba87a41u, 0x0efd8543u, 0x691d6368u, 0x8929d967u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x51474edfu, 0xc3cfba93u, 0xf21454cfu, 0x9b79baacu, 0x4d4fce23u, 0xd701dfd5u, 0x37357ab3u, 0x1be693fau, + 0xa701cb3bu, 0x7467c620u, 0x428184e8u, 0xf4010df0u, 0xe33aa88fu, 0xc78d6d62u, 0xae9ba9c0u, 0xf4bba97eu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x448274a57f508cbcull; diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.json b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.json new file mode 100644 index 00000000..4e8720b3 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.json @@ -0,0 +1,36 @@ +{ + "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", + "day": "bytes:69676e65756d2d6461792ffa50000000000000", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0x731250f1008c6f8e", "0xb89b396aa5e33597", "0xbc96ebcdb47c61e4", "0xac1414cd0fb91152", "0x1cc2d1847875555e", "0x071f703c82efff0c", "0x21147b8c58bc96e4", "0xf412422862c643e6", + "0xb8a5c330668e9283", "0x994558a2b24a6557", "0xb2fb321e884a1fed", "0xa8f928a38622b10e", "0x9ea4cfcfc2c96d8b", "0xc082abc94d5dfb18", "0x695239a4a852e0f9", "0xef804c526240ce15", + "0x624303d31812df0d", "0xcfd82ed4955f140a", "0x6569ae6a4a436a9a", "0xc17f46cf926a7a0c", "0x721fe9c93267d13b", "0xf09fe97d5ce6bfbb", "0x455d853d3913d6a3", "0x4fbb5f43bd1af7ea", + "0x864d689410bb8ed0", "0xe8c93188d7f02b04", "0xebaafb6120560360", "0xde81676bff855560", "0xd0b3ae8111aab7c3", "0xacd97035767867ca", "0x1970c0b030b99c03", "0x09fed95e63215205" + ]}, + {"base_nonce": 4096, "expected": [ + "0x3d4f7e76fa55a96a", "0xd54574ccb1a27843", "0xeb1635ddd5f1e181", "0x1e49a6cf24085bd9", "0x9f929d79ad731c0e", "0x5caca654a4bd4f0f", "0x33eb9defb51424b9", "0x04f1f3c93663c27e", + "0xad1c775687a46553", "0xc57c600167bc7571", "0xaf0b8e2f9a316b22", "0xa01088974b376d8e", "0x14555ccee9288fdd", "0x0d1d576ea396fbe7", "0x8cf63b15a58299bf", "0x49f530db99fd5f01", + "0x30324c49b12da61b", "0xeb6fc871f103ad7b", "0x8ac3271ce7edfc29", "0x2804099c65265aff", "0xc4d9377a427c3337", "0x566c1e580b940448", "0x790b966beb7bebb3", "0xba5da14cc619efe0", + "0x5da22ad9ae88fcd7", "0x24221e506877ef84", "0xfcda89486e6e78ca", "0xfd9a09b3587ba8fb", "0x4fdb52f5b31ca54a", "0x9cf4a2730edbf331", "0xb28717ae699944bb", "0x0889c8c6adecb410" + ]}, + {"base_nonce": 1000000, "expected": [ + "0x377dfe4959f008b5", "0xd1bc9ed622919f80", "0x7c8c576aebc618bb", "0x7930d463eceb4bec", "0xd7c8081c372c0e96", "0xff093d43f46685c2", "0x64d59d5e4abdef5c", "0xd0b6c0699e0d51f8", + "0xbf73d4c2e7d7bb5c", "0xb461b9770521b1b8", "0x224eaf296658b9c0", "0x81233334fb928726", "0xa10c5ee0aecc8251", "0xcd1a7e1b3cb970e7", "0x573c61ddaaa8fccc", "0x67dd7ae97a699881", + "0x6df554eae8e0482a", "0x61f528d7eba4cf89", "0xc1f241f1f41fc34d", "0x25e1b51094413da0", "0x9be1d90ff6f51065", "0x21fc77cf9089f049", "0x1fadecae2996b343", "0xbd170b4f84703b24", + "0x0a7493c2ebd8bfb5", "0xd1d6e43170ced136", "0xe87fadf2a2c02a52", "0x2f873ddc2cf9f022", "0xa3963afdf94f3307", "0x3dfff37956161da1", "0xa60a7a56137b4231", "0xb8abcc39f8cb021a" + ]} + ], + "dataset_head": ["0x19c6837f", "0xdf3adfb8", "0xc5f0629f", "0x03e38ae8", "0xc5de2525", "0xa34a4906", "0x898367cb", "0x35b56808", "0xb886e673", "0x1ee29ece", "0x0cc36c88", "0xc984181b", "0x3d9d15f6", "0x27c7d52a", "0xc268cf69", "0x99b3c5c2"], + "dataset_last_index": 268435455, + "dataset_last": "0x8d66c390", + "dataset_samples": [{"index": 59471966, "value": "0xe53754ce"}, {"index": 217795994, "value": "0xc6792663"}, {"index": 208353206, "value": "0x0ca9fdce"}, {"index": 42483309, "value": "0x96159926"}, {"index": 172547758, "value": "0xfca71449"}, {"index": 148076330, "value": "0x54522663"}, {"index": 183853158, "value": "0xd0160df7"}, {"index": 214389424, "value": "0x301be5a9"}, {"index": 267488061, "value": "0xb0de07c5"}, {"index": 169781097, "value": "0xb8f1c964"}, {"index": 184093494, "value": "0xcef19286"}, {"index": 153880993, "value": "0xc9c7985d"}, {"index": 84977930, "value": "0x9db14da9"}, {"index": 46426879, "value": "0xda9f746d"}, {"index": 3093825, "value": "0x8747e5a1"}, {"index": 225364072, "value": "0x545158e0"}, {"index": 44593546, "value": "0xdf31afbb"}, {"index": 260713159, "value": "0x4daa9b4c"}, {"index": 168250303, "value": "0x1f0762ff"}, {"index": 52384140, "value": "0x80a2d158"}, {"index": 223401610, "value": "0xf762ad72"}, {"index": 45554030, "value": "0x7ee04a69"}, {"index": 95410555, "value": "0xd07df0e9"}, {"index": 175039924, "value": "0x87cfc4ab"}, {"index": 79171087, "value": "0x9de7bb05"}, {"index": 267580473, "value": "0xcd214af2"}, {"index": 24168642, "value": "0xbd15dc85"}, {"index": 37981670, "value": "0x89f48a42"}, {"index": 171551130, "value": "0xd49c8a27"}, {"index": 195559979, "value": "0xbc60a7e1"}, {"index": 204611762, "value": "0xafc36693"}, {"index": 140997658, "value": "0x4876871f"}, {"index": 138925853, "value": "0x703246c4"}, {"index": 86637313, "value": "0xbb823e84"}, {"index": 20736778, "value": "0xa13f71a1"}, {"index": 219665210, "value": "0xf2dd8540"}, {"index": 160430336, "value": "0x74b039d1"}, {"index": 264654675, "value": "0x84e8a275"}, {"index": 8013395, "value": "0x750744bb"}, {"index": 228945585, "value": "0x3f690b5f"}, {"index": 213884386, "value": "0x5e75ecbd"}, {"index": 104419827, "value": "0x36a11cc7"}, {"index": 44185464, "value": "0x65e888cf"}, {"index": 142737231, "value": "0x2e8538c8"}, {"index": 99284897, "value": "0x14f5fec9"}, {"index": 132475900, "value": "0x7329f625"}, {"index": 61861762, "value": "0x42ad9261"}, {"index": 132056166, "value": "0x120aea25"}, {"index": 262388043, "value": "0x5374b09f"}, {"index": 91878046, "value": "0x4fa85aa3"}, {"index": 117353561, "value": "0xcb856f4e"}, {"index": 124768597, "value": "0x526707ba"}, {"index": 71352993, "value": "0x82d847da"}, {"index": 190698941, "value": "0xaed963e5"}, {"index": 46055428, "value": "0x1516404b"}, {"index": 55281366, "value": "0x2e565ddd"}, {"index": 165145231, "value": "0x46d1dacf"}, {"index": 106810753, "value": "0xf7bb9e4c"}, {"index": 171985651, "value": "0x61ae3b87"}, {"index": 232085256, "value": "0x9daeed43"}, {"index": 159510492, "value": "0xcf6bc86f"}, {"index": 40072060, "value": "0x24f111d5"}, {"index": 209107596, "value": "0x4356a558"}, {"index": 39023794, "value": "0x0890de16"}], + "cache_head": ["0xebd9055c", "0x7eaf6b21", "0x9b610855", "0x134a8562", "0xb10059ba", "0x7f459e58", "0x8f3c7873", "0x3523e609", "0x75b8f4ca", "0x750d31b4", "0x0dae0782", "0x26f10015", "0x7ba87a41", "0x0efd8543", "0x691d6368", "0x8929d967"], + "cache_last_line": ["0x51474edf", "0xc3cfba93", "0xf21454cf", "0x9b79baac", "0x4d4fce23", "0xd701dfd5", "0x37357ab3", "0x1be693fa", "0xa701cb3b", "0x7467c620", "0x428184e8", "0xf4010df0", "0xe33aa88f", "0xc78d6d62", "0xae9ba9c0", "0xf4bba97e"], + "cache_fnv1a64": "0x448274a57f508cbc" +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/kernel.cl b/proto-cuda/packs-ca2-mixer/mx8-genesis/kernel.cl new file mode 100644 index 00000000..a8ccd9f8 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/kernel.cl @@ -0,0 +1,279 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 8 x seed-parameterised mixer + one 64-byte cache read, then 8 x final mixer (class v3, mixer multiplier 8, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 8 x mixer + cache line s[0] & mask; 8 x final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 8u + j + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ ds[r0 & mask]; // 16 load + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ ds[r1 & mask]; // 34 load + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/kernel.cu b/proto-cuda/packs-ca2-mixer/mx8-genesis/kernel.cu new file mode 100644 index 00000000..e2247231 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/kernel.cu @@ -0,0 +1,164 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal). +// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC. +#include +#include +#include "program.h" +#include "memhard.h" + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31. +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x. +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) { + uint32_t x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item. +// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host. +__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) { + uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x; + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; + if (t < nItems) { + uint32_t s[16]; + mh_item(cache, t, s); + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} + +// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every +// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a +// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid. +__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint32_t x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint32_t x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint32_t x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint32_t x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint32_t x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint32_t x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint32_t x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ ds[r0 & mask]; // 16 load + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ ds[r1 & mask]; // 34 load + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +// Host-side launch wrappers. Declared in program.h, called from host.cu. +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments) { + if (nSegments == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nSegments + block - 1u) / block; + igneum_cache_fill<<>>(cache, nSegments); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) { + if (nItems == 0u) return cudaErrorInvalidValue; + uint32_t block = 256u; + uint32_t grid = (nItems + block - 1u) / block; + igneum_build<<>>(ds, cache, nItems); + return cudaGetLastError(); +} + +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash<<>>(ds, out, baseNonce, mask); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/kernel_bound.cl b/proto-cuda/packs-ca2-mixer/mx8-genesis/kernel_bound.cl new file mode 100644 index 00000000..a8164a82 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/kernel_bound.cl @@ -0,0 +1,373 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal). +// Built from source at runtime by proto-opencl/host.c, which passes these defines: +// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit) +// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default) +// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32 +// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition +// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units; +// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32. +#ifndef IGNEUM_GROUP +#define IGNEUM_GROUP 32 +#endif +#ifndef IGNEUM_EXCHANGE +#define IGNEUM_EXCHANGE 0 +#endif +#ifdef __OPENCL_VERSION__ +#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1))) +#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n] +#if IGNEUM_EXCHANGE == 1 +#ifdef cl_khr_subgroups +#pragma OPENCL EXTENSION cl_khr_subgroups : enable +#endif +#ifdef cl_khr_subgroup_shuffle +#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable +#endif +#elif IGNEUM_EXCHANGE == 2 +#pragma OPENCL EXTENSION cl_intel_subgroups : enable +#endif +#else +// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros. +#include "emu_opencl.h" +#endif + +#if IGNEUM_EXCHANGE == 1 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#elif IGNEUM_EXCHANGE == 2 +#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m)) +#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u) +#else +// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per +// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane +// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's +// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier. +#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; } +#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; } +#endif + +static inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +// n is a literal in 1..31 at every call site. OpenCL rotate() rotates left by n modulo 32. +static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); } +// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x. +static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); } +static inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 8 x seed-parameterised mixer + one 64-byte cache read, then 8 x final mixer (class v3, mixer multiplier 8, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +static inline void mh_chacha_block(const uint* x, uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +static inline void mh_cache_segment(__global uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + __global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +static inline void mh_mixer(uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 8 x mixer + cache line s[0] & mask; 8 x final mixer. +static inline void mh_item(__global const uint* cache, uint t, uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 8u + j + 1u)); + __global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. +// The same constants as memhard.h in this pack (one emitter, three dialects). +__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) { + uint seg = (uint)get_global_id(0); + if (seg < nSegments) mh_cache_segment(cache, seg); +} +__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) { + uint t = (uint)get_global_id(0); + if (t < nItems) { + uint s[16]; + mh_item(cache, t, s); + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + } +} + +// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the +// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and +// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all). +IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ 0x67a9a7beu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x1a155b25u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1] + { uint x = nonce ^ 0x1a155b25u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xfddfb732u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2] + { uint x = nonce ^ 0xfddfb732u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x4b5af2e8u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3] + { uint x = nonce ^ 0x4b5af2e8u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xc55caf33u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4] + { uint x = nonce ^ 0xc55caf33u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xa27c13b7u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5] + { uint x = nonce ^ 0xa27c13b7u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x06628a48u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6] + { uint x = nonce ^ 0x06628a48u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x03852469u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7] + { uint x = nonce ^ 0x03852469u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x67a9a7beu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0] + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ ds[r0 & mask]; // 16 load + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ ds[r1 & mask]; // 34 load + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} + +#if IGNEUM_EXCHANGE != 0 +// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the +// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a +// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md. +IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) { + if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); } +} +#endif + +// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash. +IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) { + uint gid = (uint)get_global_id(0); + uint lid = (uint)get_local_id(0); + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7]; +#if IGNEUM_EXCHANGE == 0 + IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP); + uint xk = 0u; +#else + (void)lid; +#endif + { uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; } + { uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; } + { uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; } + { uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; } + { uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; } + { uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; } + { uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; } + { uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r1 = r1 ^ t_; } // 6 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 8u); r7 = r7 ^ t_; } // 7 shfl + r1 = mul_hi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = mul_hi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ ds[r0 & mask]; // 16 load + r7 = r7 ^ ds[r2 & mask]; // 17 load + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 18 shfl + r5 = r5 * r0; // 19 mul + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 20 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r2 = r2 ^ t_; } // 21 shfl + r6 = mul_hi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r7 = r7 ^ t_; } // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ ds[r1 & mask]; // 34 load + r0 = mul_hi(r0, r5); // 35 mulhi + { uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 4u); r1 = r1 ^ t_; } // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = mul_hi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/kernel_bound.cu b/proto-cuda/packs-ca2-mixer/mx8-genesis/kernel_bound.cu new file mode 100644 index 00000000..e8f1a9a7 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/kernel_bound.cu @@ -0,0 +1,123 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW. +// Host declarations (also in program_bound.h if present): +// struct IgneumInitWords { uint32_t w[8]; }; +// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, +// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps); +// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#include +#include +#include "program.h" + +struct IgneumInitWords { uint32_t w[8]; }; + +__device__ __forceinline__ uint32_t splitmix32(uint32_t x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } +__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } + +__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) { + uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x; + uint32_t nonce = baseNonce + gid; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + { uint32_t x = nonce ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; } + { uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; } + { uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; } + { uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; } + { uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; } + { uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; } + { uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; } + { uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; } + + for (uint32_t it = 0u; it < 8u; ++it) { + uint32_t sel = r0; + r2 = r3 * r4 + r2; // 0 mad + r2 = r1 * r1 + r2; // 1 mad + r2 = r3 * r2 + r2; // 2 mad + r3 = r3 ^ r5; // 3 xor + r7 = r7 ^ ds[r2 & mask]; // 4 load + r5 = r5 ^ ds[r7 & mask]; // 5 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 6 shfl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 7 shfl + r1 = __umulhi(r1, r5); // 8 mulhi + r6 = rotr_var(r6, r3); // 9 rotr + r3 = r3 | r4; // 10 or + r4 = r4 ^ ds[r3 & mask]; // 11 load + r0 = __umulhi(r0, r4); // 12 mulhi + r5 = r5 + r1 + ((((sel >> 30u) & 1u) != 0u) ? 0xd3177981u : 0xc7934706u); // 13 add + r0 = r0 ^ ds[r4 & mask]; // 14 load + r2 = r2 - r4; // 15 sub + r2 = r2 ^ ds[r0 & mask]; // 16 load + r7 = r7 ^ ds[r2 & mask]; // 17 load + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 18 shfl + r5 = r5 * r0; // 19 mul + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 20 shfl + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 21 shfl + r6 = __umulhi(r6, r2); // 22 mulhi + r6 = r6 ^ ds[r1 & mask]; // 23 load + r5 = r5 * r0; // 24 mul + r5 = rotl_imm(r5, 19u); // 25 rotl + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 26 shfl + r0 = r0 ^ r5; // 27 xor + r0 = r0 ^ r4; // 28 xor + r3 = r3 - r0; // 29 sub + r5 = r5 * r1; // 30 mul + r7 = r7 ^ ds[r2 & mask]; // 31 load + r1 = r1 ^ ds[r0 & mask]; // 32 load + r5 = r5 ^ r6; // 33 xor + r5 = r5 ^ ds[r1 & mask]; // 34 load + r0 = __umulhi(r0, r5); // 35 mulhi + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 36 shfl + r7 = r7 ^ ds[r0 & mask]; // 37 load + r3 = r3 + r1 + ((((sel >> 27u) & 1u) != 0u) ? 0x230c005cu : 0x75ba2fadu); // 38 add + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 4); // 39 shfl + r2 = r2 ^ r5; // 40 xor + r3 = r6 * r3 + r3; // 41 mad + r6 = r6 - r7; // 42 sub + r7 = r7 ^ r0; // 43 xor + r1 = r1 ^ ds[r7 & mask]; // 44 load + r2 = r2 * r3; // 45 mul + r1 = __umulhi(r1, r5); // 46 mulhi + r4 = r4 - r3; // 47 sub + r2 = rotr_var(r2, r6); // 48 rotr + r3 = r3 ^ ds[r5 & mask]; // 49 load + r1 = r1 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x1907970cu : 0x81b8bc2cu); // 50 add + r0 = r0 * r2; // 51 mul + r0 = r0 + r2 + ((((sel >> 6u) & 1u) != 0u) ? 0x699fd448u : 0x4f92b968u); // 52 add + r1 = r1 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0x77b1520du : 0x2bb965afu); // 53 add + r7 = rotl_imm(r7, 14u); // 54 rotl + r3 = r3 + r7 + ((((sel >> 1u) & 1u) != 0u) ? 0xa54c55a0u : 0x7b0fe07au); // 55 add + r6 = r6 ^ ds[r7 & mask]; // 56 load + r1 = rotr_var(r1, r5); // 57 rotr + r5 = r5 ^ ds[r4 & mask]; // 58 load + r6 = r6 ^ ds[r2 & mask]; // 59 load + r3 = r5 * r0 + r3; // 60 mad + r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xad7493e7u : 0xaf9dd72du); // 61 add + r4 = r4 + r6 + ((((sel >> 27u) & 1u) != 0u) ? 0x1e07c3d9u : 0x89841d87u); // 62 add + r5 = rotl_imm(r5, 19u); // 63 rotl + } + uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo; +} + +cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) { + if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue; + uint32_t block = 32u * blockWarps; + if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue; + igneum_hash_bound<<>>(ds, out, baseNonce, mask, iw); + return cudaGetLastError(); +} + +cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) { + cudaFuncAttributes attr; + cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound); + if (e != cudaSuccess) return e; + *numRegs = attr.numRegs; + return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0); +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/memhard.h b/proto-cuda/packs-ca2-mixer/mx8-genesis/memhard.h new file mode 100644 index 00000000..f7f34c73 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/memhard.h @@ -0,0 +1,109 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against. +// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference). +// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#if defined(__CUDACC__) +#define IGNEUM_HD __host__ __device__ __forceinline__ +#elif defined(_MSC_VER) && !defined(__cplusplus) +#define IGNEUM_HD static __inline +#else +#define IGNEUM_HD static inline +#endif +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 8 x seed-parameterised mixer + one 64-byte cache read, then 8 x final mixer (class v3, mixer multiplier 8, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) { + for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint32_t r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) { + uint32_t prev[16]; uint32_t x[16]; uint32_t y[16]; + for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 8 x mixer + cache line s[0] & mask; 8 x final mixer. +IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint32_t r = 0u; r < 8u; ++r) { + for (uint32_t j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 8u + j + 1u)); + const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint32_t j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/memhard.metal b/proto-cuda/packs-ca2-mixer/mx8-genesis/memhard.metal new file mode 100644 index 00000000..01b263d6 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/memhard.metal @@ -0,0 +1,107 @@ +#include +using namespace metal; +// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines. +// Item: 8 rounds of 8 x seed-parameterised mixer + one 64-byte cache read, then 8 x final mixer (class v3, mixer multiplier 8, +// docs/plans/mixer-x4.md: the round key of application j of round r is 0x9E3779B9 * (r * m + j + 1)). All parameters are literals. +#define MH_CACHE_LINE_MASK 0x003fffffu +#define MH_SEGMENT_LINES 64u +#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); } +inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site + +// y = ChaCha12 core(x) + x +inline void mh_chacha_block(const thread uint* x, thread uint* y) { + for (uint i = 0u; i < 16u; ++i) y[i] = x[i]; + for (uint r = 0u; r < 6u; ++r) { + MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u) + MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u) + MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u) + } + for (uint i = 0u; i < 16u; ++i) y[i] += x[i]; +} + +// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0. +inline void mh_cache_segment(device uint* cache, uint seg) { + uint prev[16]; uint x[16]; uint y[16]; + for (uint i = 0u; i < 16u; ++i) prev[i] = 0u; + for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) { + x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3]; + x[4] = 0x3067619fu ^ prev[4]; + x[5] = 0x3c269176u ^ prev[5]; + x[6] = 0x84a03b03u ^ prev[6]; + x[7] = 0xf8c63294u ^ prev[7]; + x[8] = 0xff977c5bu ^ prev[8]; + x[9] = 0xe60def3eu ^ prev[9]; + x[10] = 0x63630141u ^ prev[10]; + x[11] = 0xb8fbcb58u ^ prev[11]; + x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15]; + mh_chacha_block(x, y); + device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u); + for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; } + } +} + +// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations. +inline void mh_mixer(thread uint* s, uint rk) { + s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u; + s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu; + s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u; + s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu; + s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u; + s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u; + s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u; + s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u; + s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du; + s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u; + s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du; + s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu; + s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du; + s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu; + s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u; + s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u; + MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u) + MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u) + MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u) + MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u) +} + +// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of 8 x mixer + cache line s[0] & mask; 8 x final mixer. +inline void mh_item(device const uint* cache, uint t, thread uint* s) { + s[0] = 0x3067619fu; + s[1] = 0x3c269176u; + s[2] = 0x84a03b03u; + s[3] = 0xf8c63294u; + s[4] = 0xff977c5bu; + s[5] = 0xe60def3eu; + s[6] = 0x63630141u; + s[7] = 0xb8fbcb58u; + s[8] = t * 0x42146205u + 0xbab68293u; + s[9] = t * 0x52cbe0fbu + 0xcc162340u; + s[10] = t * 0x7ecf4a03u + 0x6ce151ccu; + s[11] = t * 0x6728907fu + 0xe62b8997u; + s[12] = t * 0xd81d9751u + 0xc9c80297u; + s[13] = t * 0x132952c3u + 0xf74a1654u; + s[14] = t * 0xf60de277u + 0x3d704af5u; + s[15] = t * 0x05358035u + 0x3cf522b7u; + for (uint r = 0u; r < 8u; ++r) { + for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 8u + j + 1u)); + device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u); + for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i]; + } + for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u)); +} +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } + +// One thread per segment (2^16 threads). +kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { + mh_cache_segment(cache, gid); +} +// One thread per 64-byte item (dataset words / 16 threads). +kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]], + uint gid [[thread_position_in_grid]]) { + uint s[16]; + mh_item(cache, gid, s); + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/program.h b/proto-cuda/packs-ca2-mixer/mx8-genesis/program.h new file mode 100644 index 00000000..9c99a169 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/program.h @@ -0,0 +1,63 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Program metadata for host.cu plus the launch wrappers defined in kernel.cu. +// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros. +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif +#ifndef IGNEUM_NO_CUDA +#include +#endif + +#define IGNEUM_SEED_STRING "igneum-genesis" +#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973" +#define IGNEUM_GENERATOR 2 +#define IGNEUM_PROGRAM_ATTEMPT 0 +#define IGNEUM_PROGRAM_ID 0x9544515847736361ull +#define IGNEUM_DAY_STRING "2026-10-03" +#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033" +#define IGNEUM_DAY0 0x3067619fu +#define IGNEUM_DAY1 0x3c269176u +#define IGNEUM_DATASET_LOG2 28 +#define IGNEUM_MASK 0x0fffffffu +#define IGNEUM_LANES 32 +#define IGNEUM_ITERATIONS 8 +#define IGNEUM_INSTR_COUNT 64 +#define IGNEUM_LOADS_PER_HASH 128 +#define IGNEUM_WIDE_LOADS_PER_HASH 0 +#define IGNEUM_OP_MIX "load=16 add=8 shfl=8 xor=6 mad=5 mul=5 mulhi=5 sub=4 rotl=3 rotr=3 or=1" +// Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item +// derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. +#define IGNEUM_LOAD_CLASS "mx8" +#define IGNEUM_CLASS_MIXER_MULT 8 +#define IGNEUM_CACHE_GROWTH 1 // 1: cache words = 2^(26 + doublings(day)), doublings = floor(log2(1 + day / 1460)) +#define IGNEUM_LOAD_SLOTS 16 +#define IGNEUM_LOAD_MIX { 100, 0, 0 } +#define IGNEUM_LOAD_WIDTH_COUNTS { 16, 0, 0 } // loads of 4, 16, 64 bytes per program +#define IGNEUM_BYTES_PER_HASH 512 +#define IGNEUM_FOLD_ROT 11 +#define IGNEUM_FOLD_MUL 0x9e3779b1u +// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) +#define IGNEUM_DATASET_MODE 1 + +#define IGNEUM_SEEDW_INIT { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u } +#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u } +#define IGNEUM_CACHE_LOG2_WORDS 26 +#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6 +#define IGNEUM_CACHE_SEGMENTS 65536u +#define IGNEUM_ITEM_ROUNDS 8 +#define IGNEUM_MIXER_MULT 8 // mixer applications per round and after the last read (class v3, docs/plans/mixer-x4.md) +#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u } +#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u } +#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u } + +#ifndef IGNEUM_NO_CUDA +// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError(). +cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments); +cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems); +cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, + uint32_t nonces, uint32_t blockWarps); +cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps); +#endif diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/program.json b/proto-cuda/packs-ca2-mixer/mx8-genesis/program.json new file mode 100644 index 00000000..d4e1c079 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/program.json @@ -0,0 +1,131 @@ +{ + "format": "igneum-program-pack-3", + "generator": 2, + "attempt": 0, + "program_id": "0x9544515847736361", + "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", + "dataset_mode": "memory-hard", + "seed": "igneum-genesis", + "seed_bytes": "69676e65756d2d67656e65736973", + "seed_words": ["0x67a9a7be", "0x1a155b25", "0xfddfb732", "0x4b5af2e8", "0xc55caf33", "0xa27c13b7", "0x06628a48", "0x03852469"], + "seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32", + "generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried", + "lanes": 32, + "registers": 8, + "iterations": 8, + "instruction_count": 64, + "loads_per_hash": 128, + "load_class": "mx8", + "mixer_mult": 8, + "cache_growth": true, + "mixer": "class v3 (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): every mixer application of the item derivation is 8 applications with round keys (r * 8 + j + 1) * 0x9E3779B9, the 8 dependent cache reads per item unchanged; cache growth rule option C: cache words = 2^(26 + doublings(day)), dataset words = 2^(genesis_log2 + doublings(day)), doublings(day) = floor(log2(1 + day / 1460)) for day = days since genesis", + "load_slots": 16, + "load_mix_percent_4_16_64": [100, 0, 0], + "load_width_counts_4_16_64": [16, 0, 0], + "bytes_per_hash": 512, + "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", + "op_mix": {"load": 16, "add": 8, "shfl": 8, "xor": 6, "mad": 5, "mul": 5, "mulhi": 5, "sub": 4, "rotl": 3, "rotr": 3, "or": 1}, + "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", + "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", + "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", + "output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo", + "op_semantics": { + "add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)", + "sub": "dst = dst - src", + "mul": "dst = dst * src (low 32)", + "mulhi": "dst = high 32 bits of dst * src", + "xor": "dst = dst ^ src", + "or": "dst = dst | src", + "rotl": "dst = rotl(dst, rot), rot in 1..31", + "rotr": "dst = rotr(dst, src & 31)", + "mad": "dst = src * src2 + dst", + "shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp", + "load": "dst = dst ^ dataset[src & dataset.mask]", + "wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)" + }, + "dataset": { + "log2_words": 28, + "bytes": 1073741824, + "mask": "0x0fffffff", + "day": "2026-10-03", + "day_bytes": "6461792f323032362d31302d3033", + "day_words_from": "seed_words_from_bytes(day_bytes)", + "d0": "0x3067619f", + "d1": "0x3c269176", + "mode": "memory-hard", + "spec": "proto-metal/MEMHARD.md", + "key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"], + "key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]", + "cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"}, + "mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"}, + "mixer_mult": 8, + "item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: for j in 0..7: s = M(s, rk = (r * 8 + j + 1) * 0x9E3779B9); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then for j in 0..7: s = M(s, rk = (64 + j + 1) * 0x9E3779B9); item(t) = s", + "word": "dataset[w] = item(w >> 4)[w & 15]" + }, + "instructions": [ + {"i": 0, "op": "mad", "dst": 2, "src": 3, "src2": 4, "imm": "0xbf7b174d", "imm2": "0x337b762e", "rot": 17, "bit": 2, "mask": 2, "width": 1}, + {"i": 1, "op": "mad", "dst": 2, "src": 1, "src2": 1, "imm": "0xdd04a5da", "imm2": "0x42da7657", "rot": 15, "bit": 30, "mask": 16, "width": 1}, + {"i": 2, "op": "mad", "dst": 2, "src": 3, "src2": 2, "imm": "0x734003fa", "imm2": "0x5bb67700", "rot": 3, "bit": 20, "mask": 1, "width": 1}, + {"i": 3, "op": "xor", "dst": 3, "src": 5, "src2": 5, "imm": "0xc55a1b1c", "imm2": "0xa19720f3", "rot": 7, "bit": 8, "mask": 1, "width": 1}, + {"i": 4, "op": "load", "dst": 7, "src": 2, "src2": 5, "imm": "0xad572dd7", "imm2": "0x9ceb3ea7", "rot": 18, "bit": 30, "mask": 2, "width": 1}, + {"i": 5, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x769a53be", "imm2": "0x80f9067e", "rot": 12, "bit": 22, "mask": 1, "width": 1}, + {"i": 6, "op": "shfl", "dst": 1, "src": 4, "src2": 0, "imm": "0xd3613d88", "imm2": "0x262fb219", "rot": 10, "bit": 30, "mask": 8, "width": 1}, + {"i": 7, "op": "shfl", "dst": 7, "src": 3, "src2": 4, "imm": "0xce38e42f", "imm2": "0xb868b818", "rot": 11, "bit": 8, "mask": 8, "width": 1}, + {"i": 8, "op": "mulhi", "dst": 1, "src": 5, "src2": 2, "imm": "0xa5eebca5", "imm2": "0x5703a72b", "rot": 13, "bit": 13, "mask": 16, "width": 1}, + {"i": 9, "op": "rotr", "dst": 6, "src": 3, "src2": 4, "imm": "0x17a5a9c7", "imm2": "0xdcfb93a1", "rot": 20, "bit": 27, "mask": 2, "width": 1}, + {"i": 10, "op": "or", "dst": 3, "src": 4, "src2": 1, "imm": "0xccb7d785", "imm2": "0xc335364c", "rot": 14, "bit": 12, "mask": 4, "width": 1}, + {"i": 11, "op": "load", "dst": 4, "src": 3, "src2": 2, "imm": "0x88cb9af3", "imm2": "0x4e7dc10d", "rot": 24, "bit": 17, "mask": 4, "width": 1}, + {"i": 12, "op": "mulhi", "dst": 0, "src": 4, "src2": 4, "imm": "0x45374321", "imm2": "0x3cd91989", "rot": 11, "bit": 4, "mask": 2, "width": 1}, + {"i": 13, "op": "add", "dst": 5, "src": 1, "src2": 2, "imm": "0xc7934706", "imm2": "0xd3177981", "rot": 16, "bit": 30, "mask": 2, "width": 1}, + {"i": 14, "op": "load", "dst": 0, "src": 4, "src2": 3, "imm": "0xfd7f56bb", "imm2": "0x65e14f52", "rot": 13, "bit": 22, "mask": 2, "width": 1}, + {"i": 15, "op": "sub", "dst": 2, "src": 4, "src2": 4, "imm": "0x35a80b49", "imm2": "0x060f2d13", "rot": 16, "bit": 20, "mask": 16, "width": 1}, + {"i": 16, "op": "load", "dst": 2, "src": 0, "src2": 2, "imm": "0xae0a32c2", "imm2": "0x4c2a4cfe", "rot": 8, "bit": 31, "mask": 16, "width": 1}, + {"i": 17, "op": "load", "dst": 7, "src": 2, "src2": 6, "imm": "0x82a84cc3", "imm2": "0x21a38d68", "rot": 15, "bit": 21, "mask": 2, "width": 1}, + {"i": 18, "op": "shfl", "dst": 7, "src": 3, "src2": 3, "imm": "0xa3818806", "imm2": "0x8f66b5c8", "rot": 14, "bit": 6, "mask": 4, "width": 1}, + {"i": 19, "op": "mul", "dst": 5, "src": 0, "src2": 1, "imm": "0xa00de107", "imm2": "0x77bfcaa5", "rot": 3, "bit": 10, "mask": 2, "width": 1}, + {"i": 20, "op": "shfl", "dst": 3, "src": 4, "src2": 7, "imm": "0x1d2b8cab", "imm2": "0x80b4f9a2", "rot": 14, "bit": 25, "mask": 2, "width": 1}, + {"i": 21, "op": "shfl", "dst": 2, "src": 4, "src2": 5, "imm": "0x3ac915d2", "imm2": "0x5fba7bc2", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 22, "op": "mulhi", "dst": 6, "src": 2, "src2": 0, "imm": "0xdc3ec8fd", "imm2": "0x599e2fa3", "rot": 22, "bit": 3, "mask": 2, "width": 1}, + {"i": 23, "op": "load", "dst": 6, "src": 1, "src2": 5, "imm": "0x2a6b16d5", "imm2": "0xd73e396f", "rot": 28, "bit": 29, "mask": 2, "width": 1}, + {"i": 24, "op": "mul", "dst": 5, "src": 0, "src2": 7, "imm": "0x376d0223", "imm2": "0xe1c2169a", "rot": 4, "bit": 16, "mask": 16, "width": 1}, + {"i": 25, "op": "rotl", "dst": 5, "src": 7, "src2": 3, "imm": "0x78ad8c60", "imm2": "0x6f5b77d5", "rot": 19, "bit": 11, "mask": 16, "width": 1}, + {"i": 26, "op": "shfl", "dst": 7, "src": 6, "src2": 5, "imm": "0x93915b9f", "imm2": "0x1e61fb6b", "rot": 28, "bit": 23, "mask": 2, "width": 1}, + {"i": 27, "op": "xor", "dst": 0, "src": 5, "src2": 7, "imm": "0x6378fe15", "imm2": "0x66c78f42", "rot": 12, "bit": 31, "mask": 8, "width": 1}, + {"i": 28, "op": "xor", "dst": 0, "src": 4, "src2": 7, "imm": "0x20a57fda", "imm2": "0x088c848e", "rot": 16, "bit": 13, "mask": 4, "width": 1}, + {"i": 29, "op": "sub", "dst": 3, "src": 0, "src2": 2, "imm": "0x49d95fd5", "imm2": "0x1a5f946a", "rot": 6, "bit": 12, "mask": 1, "width": 1}, + {"i": 30, "op": "mul", "dst": 5, "src": 1, "src2": 7, "imm": "0x0a816217", "imm2": "0x405c4f73", "rot": 13, "bit": 27, "mask": 4, "width": 1}, + {"i": 31, "op": "load", "dst": 7, "src": 2, "src2": 2, "imm": "0x09ed045e", "imm2": "0xd69c4715", "rot": 5, "bit": 9, "mask": 2, "width": 1}, + {"i": 32, "op": "load", "dst": 1, "src": 0, "src2": 6, "imm": "0xeb79ea49", "imm2": "0xcc587f5a", "rot": 6, "bit": 8, "mask": 16, "width": 1}, + {"i": 33, "op": "xor", "dst": 5, "src": 6, "src2": 1, "imm": "0x3027401e", "imm2": "0x5f20c27e", "rot": 18, "bit": 9, "mask": 2, "width": 1}, + {"i": 34, "op": "load", "dst": 5, "src": 1, "src2": 3, "imm": "0x0e1cab07", "imm2": "0x09356c5b", "rot": 19, "bit": 31, "mask": 1, "width": 1}, + {"i": 35, "op": "mulhi", "dst": 0, "src": 5, "src2": 2, "imm": "0x90e31357", "imm2": "0xabd32484", "rot": 26, "bit": 5, "mask": 8, "width": 1}, + {"i": 36, "op": "shfl", "dst": 5, "src": 2, "src2": 5, "imm": "0xee9a955f", "imm2": "0x31b3faed", "rot": 8, "bit": 24, "mask": 4, "width": 1}, + {"i": 37, "op": "load", "dst": 7, "src": 0, "src2": 7, "imm": "0x3ba2f832", "imm2": "0x1160dcd3", "rot": 4, "bit": 29, "mask": 1, "width": 1}, + {"i": 38, "op": "add", "dst": 3, "src": 1, "src2": 7, "imm": "0x75ba2fad", "imm2": "0x230c005c", "rot": 4, "bit": 27, "mask": 1, "width": 1}, + {"i": 39, "op": "shfl", "dst": 1, "src": 5, "src2": 3, "imm": "0xdbf37e75", "imm2": "0xb5ac1969", "rot": 30, "bit": 13, "mask": 4, "width": 1}, + {"i": 40, "op": "xor", "dst": 2, "src": 5, "src2": 5, "imm": "0x47f136c5", "imm2": "0x06ce9153", "rot": 19, "bit": 10, "mask": 2, "width": 1}, + {"i": 41, "op": "mad", "dst": 3, "src": 6, "src2": 3, "imm": "0xce13eff8", "imm2": "0x04cc1d55", "rot": 3, "bit": 1, "mask": 4, "width": 1}, + {"i": 42, "op": "sub", "dst": 6, "src": 7, "src2": 1, "imm": "0x6a65ab71", "imm2": "0x8fbc1bcd", "rot": 4, "bit": 1, "mask": 8, "width": 1}, + {"i": 43, "op": "xor", "dst": 7, "src": 0, "src2": 7, "imm": "0xdaeb4928", "imm2": "0xc0423027", "rot": 24, "bit": 11, "mask": 8, "width": 1}, + {"i": 44, "op": "load", "dst": 1, "src": 7, "src2": 7, "imm": "0x778f01c9", "imm2": "0x28cedcea", "rot": 12, "bit": 4, "mask": 16, "width": 1}, + {"i": 45, "op": "mul", "dst": 2, "src": 3, "src2": 2, "imm": "0xf4264f1b", "imm2": "0x0f627d56", "rot": 5, "bit": 28, "mask": 8, "width": 1}, + {"i": 46, "op": "mulhi", "dst": 1, "src": 5, "src2": 1, "imm": "0xffb2147a", "imm2": "0xccde9b05", "rot": 13, "bit": 9, "mask": 2, "width": 1}, + {"i": 47, "op": "sub", "dst": 4, "src": 3, "src2": 5, "imm": "0x73b36234", "imm2": "0x3f5d5997", "rot": 7, "bit": 18, "mask": 2, "width": 1}, + {"i": 48, "op": "rotr", "dst": 2, "src": 6, "src2": 3, "imm": "0x3a4d9aa9", "imm2": "0x212bec7b", "rot": 4, "bit": 29, "mask": 16, "width": 1}, + {"i": 49, "op": "load", "dst": 3, "src": 5, "src2": 2, "imm": "0x626f11df", "imm2": "0x56cd5bfd", "rot": 7, "bit": 1, "mask": 1, "width": 1}, + {"i": 50, "op": "add", "dst": 1, "src": 5, "src2": 6, "imm": "0x81b8bc2c", "imm2": "0x1907970c", "rot": 28, "bit": 7, "mask": 4, "width": 1}, + {"i": 51, "op": "mul", "dst": 0, "src": 2, "src2": 2, "imm": "0xa8848b30", "imm2": "0xef6ac348", "rot": 9, "bit": 15, "mask": 8, "width": 1}, + {"i": 52, "op": "add", "dst": 0, "src": 2, "src2": 0, "imm": "0x4f92b968", "imm2": "0x699fd448", "rot": 22, "bit": 6, "mask": 4, "width": 1}, + {"i": 53, "op": "add", "dst": 1, "src": 0, "src2": 2, "imm": "0x2bb965af", "imm2": "0x77b1520d", "rot": 2, "bit": 12, "mask": 8, "width": 1}, + {"i": 54, "op": "rotl", "dst": 7, "src": 1, "src2": 0, "imm": "0x553e678b", "imm2": "0x3cc8eae0", "rot": 14, "bit": 20, "mask": 2, "width": 1}, + {"i": 55, "op": "add", "dst": 3, "src": 7, "src2": 2, "imm": "0x7b0fe07a", "imm2": "0xa54c55a0", "rot": 10, "bit": 1, "mask": 1, "width": 1}, + {"i": 56, "op": "load", "dst": 6, "src": 7, "src2": 4, "imm": "0x01eba9aa", "imm2": "0x2758c0f7", "rot": 14, "bit": 15, "mask": 4, "width": 1}, + {"i": 57, "op": "rotr", "dst": 1, "src": 5, "src2": 2, "imm": "0x1f5267b3", "imm2": "0x236f5a27", "rot": 2, "bit": 31, "mask": 16, "width": 1}, + {"i": 58, "op": "load", "dst": 5, "src": 4, "src2": 3, "imm": "0xa9954a9b", "imm2": "0x6a54d4e8", "rot": 11, "bit": 10, "mask": 16, "width": 1}, + {"i": 59, "op": "load", "dst": 6, "src": 2, "src2": 4, "imm": "0x9923ff88", "imm2": "0x9357254e", "rot": 16, "bit": 1, "mask": 16, "width": 1}, + {"i": 60, "op": "mad", "dst": 3, "src": 5, "src2": 0, "imm": "0xc0cc51a6", "imm2": "0x3fd7701b", "rot": 20, "bit": 1, "mask": 4, "width": 1}, + {"i": 61, "op": "add", "dst": 5, "src": 7, "src2": 7, "imm": "0xaf9dd72d", "imm2": "0xad7493e7", "rot": 7, "bit": 31, "mask": 16, "width": 1}, + {"i": 62, "op": "add", "dst": 4, "src": 6, "src2": 2, "imm": "0x89841d87", "imm2": "0x1e07c3d9", "rot": 6, "bit": 27, "mask": 1, "width": 1}, + {"i": 63, "op": "rotl", "dst": 5, "src": 4, "src2": 2, "imm": "0xae210f8d", "imm2": "0x8e499ba4", "rot": 19, "bit": 9, "mask": 1, "width": 1} + ] +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/program.metal b/proto-cuda/packs-ca2-mixer/mx8-genesis/program.metal new file mode 100644 index 00000000..d90be2f7 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/program.metal @@ -0,0 +1,109 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +kernel void igneum_hash(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; } + { uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; } + { uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; } + { uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; } + { uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; } + { uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; } + { uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; } + { uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ dataset[r2 & MASK]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ dataset[r0 & MASK]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ dataset[r1 & MASK]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ dataset[r0 & MASK]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ dataset[r1 & MASK]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ dataset[r0 & MASK]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ dataset[r7 & MASK]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ dataset[r2 & MASK]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/program_bound.metal b/proto-cuda/packs-ca2-mixer/mx8-genesis/program_bound.metal new file mode 100644 index 00000000..8fafce71 --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/program_bound.metal @@ -0,0 +1,111 @@ +#include +using namespace metal; + +#define MASK 0x0fffffffu +constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u }; + +inline uint splitmix32(uint x) { + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} +inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 +inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); } +inline uint ds_elem(uint i, uint d0, uint d1) { + uint x = i ^ d0; + x *= 0x9E3779B1u; x ^= x >> 15; + x += d1; + x *= 0x85EBCA77u; x ^= x >> 13; + x *= 0xC2B2AE3Du; x ^= x >> 16; + return x; +} + +// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW. +kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], + device ulong* out [[buffer(1)]], + constant uint& baseNonce [[buffer(2)]], + constant uint* initw [[buffer(3)]], + uint gid [[thread_position_in_grid]]) { + uint nonce = baseNonce + gid; + uint r0, r1, r2, r3, r4, r5, r6, r7; + { uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; } + { uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; } + { uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; } + { uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; } + { uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; } + { uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; } + { uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; } + { uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; } + + for (uint it = 0u; it < 8u; ++it) { + uint sel = r0; + r2 = r3 * r4 + r2; // 0 + r2 = r1 * r1 + r2; // 1 + r2 = r3 * r2 + r2; // 2 + r3 = r3 ^ r5; // 3 + r7 = r7 ^ dataset[r2 & MASK]; // 4 + r5 = r5 ^ dataset[r7 & MASK]; // 5 + r1 = r1 ^ simd_shuffle_xor(r4, (ushort)8); // 6 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)8); // 7 + r1 = mulhi(r1, r5); // 8 + r6 = rotr_var(r6, r3); // 9 + r3 = r3 | r4; // 10 + r4 = r4 ^ dataset[r3 & MASK]; // 11 + r0 = mulhi(r0, r4); // 12 + r5 = r5 + r1 + select(0xc7934706u, 0xd3177981u, ((sel >> 30u) & 1u) != 0u); // 13 + r0 = r0 ^ dataset[r4 & MASK]; // 14 + r2 = r2 - r4; // 15 + r2 = r2 ^ dataset[r0 & MASK]; // 16 + r7 = r7 ^ dataset[r2 & MASK]; // 17 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 18 + r5 = r5 * r0; // 19 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 20 + r2 = r2 ^ simd_shuffle_xor(r4, (ushort)16); // 21 + r6 = mulhi(r6, r2); // 22 + r6 = r6 ^ dataset[r1 & MASK]; // 23 + r5 = r5 * r0; // 24 + r5 = rotl_imm(r5, 19u); // 25 + r7 = r7 ^ simd_shuffle_xor(r6, (ushort)2); // 26 + r0 = r0 ^ r5; // 27 + r0 = r0 ^ r4; // 28 + r3 = r3 - r0; // 29 + r5 = r5 * r1; // 30 + r7 = r7 ^ dataset[r2 & MASK]; // 31 + r1 = r1 ^ dataset[r0 & MASK]; // 32 + r5 = r5 ^ r6; // 33 + r5 = r5 ^ dataset[r1 & MASK]; // 34 + r0 = mulhi(r0, r5); // 35 + r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 36 + r7 = r7 ^ dataset[r0 & MASK]; // 37 + r3 = r3 + r1 + select(0x75ba2fadu, 0x230c005cu, ((sel >> 27u) & 1u) != 0u); // 38 + r1 = r1 ^ simd_shuffle_xor(r5, (ushort)4); // 39 + r2 = r2 ^ r5; // 40 + r3 = r6 * r3 + r3; // 41 + r6 = r6 - r7; // 42 + r7 = r7 ^ r0; // 43 + r1 = r1 ^ dataset[r7 & MASK]; // 44 + r2 = r2 * r3; // 45 + r1 = mulhi(r1, r5); // 46 + r4 = r4 - r3; // 47 + r2 = rotr_var(r2, r6); // 48 + r3 = r3 ^ dataset[r5 & MASK]; // 49 + r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 50 + r0 = r0 * r2; // 51 + r0 = r0 + r2 + select(0x4f92b968u, 0x699fd448u, ((sel >> 6u) & 1u) != 0u); // 52 + r1 = r1 + r0 + select(0x2bb965afu, 0x77b1520du, ((sel >> 12u) & 1u) != 0u); // 53 + r7 = rotl_imm(r7, 14u); // 54 + r3 = r3 + r7 + select(0x7b0fe07au, 0xa54c55a0u, ((sel >> 1u) & 1u) != 0u); // 55 + r6 = r6 ^ dataset[r7 & MASK]; // 56 + r1 = rotr_var(r1, r5); // 57 + r5 = r5 ^ dataset[r4 & MASK]; // 58 + r6 = r6 ^ dataset[r2 & MASK]; // 59 + r3 = r5 * r0 + r3; // 60 + r5 = r5 + r7 + select(0xaf9dd72du, 0xad7493e7u, ((sel >> 31u) & 1u) != 0u); // 61 + r4 = r4 + r6 + select(0x89841d87u, 0x1e07c3d9u, ((sel >> 27u) & 1u) != 0u); // 62 + r5 = rotl_imm(r5, 19u); // 63 + } + uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); + uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); + out[gid] = ((ulong)hi << 32) | (ulong)lo; +} diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.h b/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.h new file mode 100644 index 00000000..141affee --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.h @@ -0,0 +1,57 @@ +// Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset +#pragma once +#ifdef __cplusplus +#include +#else +#include +#endif + +#define IGNEUM_VEC_WARPS 3 +static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; +static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { + { // base nonce 0 + 0x19b56348bc85304dull, 0xb08a9cfb44aa720full, 0xe1f8f627f780eff7ull, 0x2ff3e86ff1696161ull, 0xb65e0578c257e8acull, 0xf37b5705c2bebaaeull, 0xb19fef670982389eull, 0x2374331b28827a11ull, + 0xf492cdd05dda7f88ull, 0x37700f1385e19885ull, 0xa27524f3010b3e87ull, 0x6c8a24de8b938c43ull, 0x3dc017c8820cfd16ull, 0x5c80d37146657b68ull, 0x214082cac03f734aull, 0x1d67c665145d72f3ull, + 0x9fb0ae736eb87ff5ull, 0xcd7d416ae3a6be61ull, 0x0069cda85c6de58full, 0x0b7d82e717224baaull, 0x600960f6d7be30f0ull, 0xeb29032663b0c4d2ull, 0xf29583a5766408f1ull, 0x6c9532a99ad7314dull, + 0x9a949fd0959cc80full, 0xa20258520e7f6c25ull, 0xf2ab11f9bb032e38ull, 0xcc967bcd0c8d07c1ull, 0x37745267bb3231f2ull, 0x35a046048c2b69b3ull, 0xaa51834cd3f364f3ull, 0x359192708e4f754aull + }, + { // base nonce 4096 + 0x62fb132a9943127aull, 0x0b703e577e7f4ecaull, 0xf9f24f5522ce7593ull, 0x3cf5c516abc4332aull, 0xd25523f5f6d7a127ull, 0xd2081a002f983682ull, 0xbf46c54e9b3c4254ull, 0xca362e291e5e5f4dull, + 0x6039712f10f457a3ull, 0x8a34b7cabf97c23bull, 0xa473c6a2e0bf59bcull, 0x6cf3926513a4b069ull, 0x297ec2998376a40dull, 0x8efd7f601a8f28dbull, 0x8e72532dfdc1e544ull, 0x917c2b2ebe2a7e00ull, + 0x923fbb2d2f635c25ull, 0xce864ea5c0dedad9ull, 0x4b8ec7e874e446efull, 0x1b69b69465449196ull, 0x5ef3a8a6edb369cfull, 0x06c263ef9ce63fc4ull, 0x9c2048fd9d9e2639ull, 0x457fdd96ca4a138eull, + 0xd1904018b8d7b6e3ull, 0x8682312fb2e96ab8ull, 0xdc3257e0d0f979a5ull, 0xa51b0a8519d87db5ull, 0x334f08ec056e618bull, 0x3464ce71dc65119dull, 0x6a4d6df066332e04ull, 0x7d7866cb9cfca8ffull + }, + { // base nonce 1000000 + 0x86b6cb0e13d89b03ull, 0x96299a3f19d7ef15ull, 0x67d2c55100d2f876ull, 0x0a4dfe97d671b728ull, 0x41e4489014d42595ull, 0xf11cb1958c0c0e82ull, 0xf8b70b0c0a03175full, 0x632299df87d5063eull, + 0xe198417776130492ull, 0x8ffc5449290d7be2ull, 0x5f2e264eb1311f1bull, 0x988376463ac88586ull, 0x83969eadda489c26ull, 0xbed0a2c3f255d306ull, 0x1a949d271961a819ull, 0x5bce06eb6984725cull, + 0x94d5d6a1b0ffd4e9ull, 0xf3c78bae6c2182b4ull, 0xb97e9fe1bbfcdd55ull, 0x70262d1d4c0eccb2ull, 0x1fc93b427dba28d9ull, 0x02b2e3c4317f2a2dull, 0x54d3d42a588edcb9ull, 0x79998677846e7cceull, + 0x486522a5425f821aull, 0x95fa88e933360e52ull, 0xc8bae2da2b883f6cull, 0xbe3eb610ad33614full, 0x20efb3c4de82907full, 0xd6b650cfedfb26b7ull, 0x8c24447a646dba26ull, 0x9c004678515e44ecull + } +}; + +// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). +static const uint32_t IGNEUM_DS_HEAD[16] = { + 0xfdad4319u, 0x1a7b68e1u, 0xde6db608u, 0x13d73892u, 0xd17f447au, 0xb2221ccfu, 0x9db004bdu, 0x57d7d367u, + 0xdbc4cf34u, 0x697c009au, 0xc43af1d4u, 0x97f12b2eu, 0x74c37cd0u, 0xc651ea15u, 0x665a6d29u, 0x22330a2du +}; +static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; +static const uint32_t IGNEUM_DS_LAST = 0xa83e7aa6u; +// 64 sampled dataset words (index, value) computed on the Mac. +#define IGNEUM_DS_SAMPLES 64 +static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { + 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u +}; +static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { + 0x3230bc7bu, 0x7fbfe2c9u, 0xb2690991u, 0x1745c7c5u, 0x0ab0ccafu, 0x1bf87d6bu, 0x160139fdu, 0x719817acu, 0x0155df4bu, 0xbe1e86c3u, 0x680bcd6cu, 0x79c3dc6cu, 0x181e7e5fu, 0x0713a109u, 0xc705dd9fu, 0x3933b7a8u, 0xdd1c0431u, 0x50522b30u, 0xa0020b38u, 0xbff39e96u, 0x21b67e18u, 0x740f8db3u, 0x2baba568u, 0x2c9bef83u, 0x0ad9b671u, 0xc4327869u, 0x7b4fd7d0u, 0x2c29965fu, 0xec56f15fu, 0x61111746u, 0x303a1d6eu, 0xbddcfd1au, 0xf829a355u, 0x6d5df2a9u, 0x01ab8e44u, 0x06d13507u, 0xda8dcfc6u, 0x01a703e1u, 0xafe7d2c1u, 0xc091c3a2u, 0xac1814feu, 0x6e6ff62au, 0x8fdf01bau, 0xdd3f7159u, 0xdfa0d75cu, 0x26684c35u, 0x7f441e63u, 0x88df2570u, 0x8aa4d5ebu, 0xcc816c05u, 0x434df890u, 0xcd392ad6u, 0x1ab4cb63u, 0x595926fau, 0x7cd76b41u, 0x20cb95c4u, 0x13cf823fu, 0xf9daf901u, 0xff9af40au, 0x2c7dfa51u, 0x871206dbu, 0x938c116cu, 0xb64bf199u, 0x5751f874u +}; +// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. +static const uint32_t IGNEUM_CACHE_HEAD[16] = { + 0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u, + 0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u +}; +static const uint32_t IGNEUM_CACHE_LAST[16] = { + 0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du, + 0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu +}; +static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull; diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.json b/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.json new file mode 100644 index 00000000..916b155f --- /dev/null +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.json @@ -0,0 +1,36 @@ +{ + "seed": "igneum-genesis", + "day": "2026-10-03", + "dataset_mode": "memory-hard", + "dataset_log2_words": 28, + "mask": "0x0fffffff", + "lanes": 32, + "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", + "warps": [ + {"base_nonce": 0, "expected": [ + "0x19b56348bc85304d", "0xb08a9cfb44aa720f", "0xe1f8f627f780eff7", "0x2ff3e86ff1696161", "0xb65e0578c257e8ac", "0xf37b5705c2bebaae", "0xb19fef670982389e", "0x2374331b28827a11", + "0xf492cdd05dda7f88", "0x37700f1385e19885", "0xa27524f3010b3e87", "0x6c8a24de8b938c43", "0x3dc017c8820cfd16", "0x5c80d37146657b68", "0x214082cac03f734a", "0x1d67c665145d72f3", + "0x9fb0ae736eb87ff5", "0xcd7d416ae3a6be61", "0x0069cda85c6de58f", "0x0b7d82e717224baa", "0x600960f6d7be30f0", "0xeb29032663b0c4d2", "0xf29583a5766408f1", "0x6c9532a99ad7314d", + "0x9a949fd0959cc80f", "0xa20258520e7f6c25", "0xf2ab11f9bb032e38", "0xcc967bcd0c8d07c1", "0x37745267bb3231f2", "0x35a046048c2b69b3", "0xaa51834cd3f364f3", "0x359192708e4f754a" + ]}, + {"base_nonce": 4096, "expected": [ + "0x62fb132a9943127a", "0x0b703e577e7f4eca", "0xf9f24f5522ce7593", "0x3cf5c516abc4332a", "0xd25523f5f6d7a127", "0xd2081a002f983682", "0xbf46c54e9b3c4254", "0xca362e291e5e5f4d", + "0x6039712f10f457a3", "0x8a34b7cabf97c23b", "0xa473c6a2e0bf59bc", "0x6cf3926513a4b069", "0x297ec2998376a40d", "0x8efd7f601a8f28db", "0x8e72532dfdc1e544", "0x917c2b2ebe2a7e00", + "0x923fbb2d2f635c25", "0xce864ea5c0dedad9", "0x4b8ec7e874e446ef", "0x1b69b69465449196", "0x5ef3a8a6edb369cf", "0x06c263ef9ce63fc4", "0x9c2048fd9d9e2639", "0x457fdd96ca4a138e", + "0xd1904018b8d7b6e3", "0x8682312fb2e96ab8", "0xdc3257e0d0f979a5", "0xa51b0a8519d87db5", "0x334f08ec056e618b", "0x3464ce71dc65119d", "0x6a4d6df066332e04", "0x7d7866cb9cfca8ff" + ]}, + {"base_nonce": 1000000, "expected": [ + "0x86b6cb0e13d89b03", "0x96299a3f19d7ef15", "0x67d2c55100d2f876", "0x0a4dfe97d671b728", "0x41e4489014d42595", "0xf11cb1958c0c0e82", "0xf8b70b0c0a03175f", "0x632299df87d5063e", + "0xe198417776130492", "0x8ffc5449290d7be2", "0x5f2e264eb1311f1b", "0x988376463ac88586", "0x83969eadda489c26", "0xbed0a2c3f255d306", "0x1a949d271961a819", "0x5bce06eb6984725c", + "0x94d5d6a1b0ffd4e9", "0xf3c78bae6c2182b4", "0xb97e9fe1bbfcdd55", "0x70262d1d4c0eccb2", "0x1fc93b427dba28d9", "0x02b2e3c4317f2a2d", "0x54d3d42a588edcb9", "0x79998677846e7cce", + "0x486522a5425f821a", "0x95fa88e933360e52", "0xc8bae2da2b883f6c", "0xbe3eb610ad33614f", "0x20efb3c4de82907f", "0xd6b650cfedfb26b7", "0x8c24447a646dba26", "0x9c004678515e44ec" + ]} + ], + "dataset_head": ["0xfdad4319", "0x1a7b68e1", "0xde6db608", "0x13d73892", "0xd17f447a", "0xb2221ccf", "0x9db004bd", "0x57d7d367", "0xdbc4cf34", "0x697c009a", "0xc43af1d4", "0x97f12b2e", "0x74c37cd0", "0xc651ea15", "0x665a6d29", "0x22330a2d"], + "dataset_last_index": 268435455, + "dataset_last": "0xa83e7aa6", + "dataset_samples": [{"index": 59471966, "value": "0x3230bc7b"}, {"index": 217795994, "value": "0x7fbfe2c9"}, {"index": 208353206, "value": "0xb2690991"}, {"index": 42483309, "value": "0x1745c7c5"}, {"index": 172547758, "value": "0x0ab0ccaf"}, {"index": 148076330, "value": "0x1bf87d6b"}, {"index": 183853158, "value": "0x160139fd"}, {"index": 214389424, "value": "0x719817ac"}, {"index": 267488061, "value": "0x0155df4b"}, {"index": 169781097, "value": "0xbe1e86c3"}, {"index": 184093494, "value": "0x680bcd6c"}, {"index": 153880993, "value": "0x79c3dc6c"}, {"index": 84977930, "value": "0x181e7e5f"}, {"index": 46426879, "value": "0x0713a109"}, {"index": 3093825, "value": "0xc705dd9f"}, {"index": 225364072, "value": "0x3933b7a8"}, {"index": 44593546, "value": "0xdd1c0431"}, {"index": 260713159, "value": "0x50522b30"}, {"index": 168250303, "value": "0xa0020b38"}, {"index": 52384140, "value": "0xbff39e96"}, {"index": 223401610, "value": "0x21b67e18"}, {"index": 45554030, "value": "0x740f8db3"}, {"index": 95410555, "value": "0x2baba568"}, {"index": 175039924, "value": "0x2c9bef83"}, {"index": 79171087, "value": "0x0ad9b671"}, {"index": 267580473, "value": "0xc4327869"}, {"index": 24168642, "value": "0x7b4fd7d0"}, {"index": 37981670, "value": "0x2c29965f"}, {"index": 171551130, "value": "0xec56f15f"}, {"index": 195559979, "value": "0x61111746"}, {"index": 204611762, "value": "0x303a1d6e"}, {"index": 140997658, "value": "0xbddcfd1a"}, {"index": 138925853, "value": "0xf829a355"}, {"index": 86637313, "value": "0x6d5df2a9"}, {"index": 20736778, "value": "0x01ab8e44"}, {"index": 219665210, "value": "0x06d13507"}, {"index": 160430336, "value": "0xda8dcfc6"}, {"index": 264654675, "value": "0x01a703e1"}, {"index": 8013395, "value": "0xafe7d2c1"}, {"index": 228945585, "value": "0xc091c3a2"}, {"index": 213884386, "value": "0xac1814fe"}, {"index": 104419827, "value": "0x6e6ff62a"}, {"index": 44185464, "value": "0x8fdf01ba"}, {"index": 142737231, "value": "0xdd3f7159"}, {"index": 99284897, "value": "0xdfa0d75c"}, {"index": 132475900, "value": "0x26684c35"}, {"index": 61861762, "value": "0x7f441e63"}, {"index": 132056166, "value": "0x88df2570"}, {"index": 262388043, "value": "0x8aa4d5eb"}, {"index": 91878046, "value": "0xcc816c05"}, {"index": 117353561, "value": "0x434df890"}, {"index": 124768597, "value": "0xcd392ad6"}, {"index": 71352993, "value": "0x1ab4cb63"}, {"index": 190698941, "value": "0x595926fa"}, {"index": 46055428, "value": "0x7cd76b41"}, {"index": 55281366, "value": "0x20cb95c4"}, {"index": 165145231, "value": "0x13cf823f"}, {"index": 106810753, "value": "0xf9daf901"}, {"index": 171985651, "value": "0xff9af40a"}, {"index": 232085256, "value": "0x2c7dfa51"}, {"index": 159510492, "value": "0x871206db"}, {"index": 40072060, "value": "0x938c116c"}, {"index": 209107596, "value": "0xb64bf199"}, {"index": 39023794, "value": "0x5751f874"}], + "cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"], + "cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"], + "cache_fnv1a64": "0x48c4f5bf24166b2e" +} diff --git a/relay/playbooks/mixer-x4-5090-bench.ps1 b/relay/playbooks/mixer-x4-5090-bench.ps1 index 74f4a184..c0aaa4f4 100644 --- a/relay/playbooks/mixer-x4-5090-bench.ps1 +++ b/relay/playbooks/mixer-x4-5090-bench.ps1 @@ -4,7 +4,7 @@ # igneum-worker-cuda.exe on the v2 pack and the two v3 packs of the fetched packs folder (the dataset build time per pack is the # number this job is for: the worker's own `cache ... dataset ... ms` line, the same line the readwidth round printed), # and switches the card back on with the settings it had. Every result line starts with RESULT so `node tools/jobs.mjs ` shows them. -# The packs folder of the fetch job: proto-cuda/packs-ca2-mixer/mx4-genesis and mx4-devnet-epoch0 from branch ca2-mixer, plus +# The packs folder of the fetch job: proto-cuda/packs-ca2-mixer/mx4-genesis, mx4-devnet-epoch0, mx8-genesis and mx8-devnet-epoch0 (the x8 candidate) from branch ca2-mixer, plus # proto-cuda/packs/igneum-genesis-mh copied in as v2-genesis-mh (the version 2 control, same card, same run). $ErrorActionPreference = 'Continue' function Say([string] $m) { Write-Host ("[" + (Get-Date -Format 'HH:mm:ss') + "] " + $m) } @@ -48,7 +48,7 @@ if ($card) { & nvidia-smi --query-gpu=name,driver_version,power.limit,clocks.sm,clocks.mem,memory.used,temperature.gpu --format=csv,noheader 2>&1 | ForEach-Object { "RESULT gpu-before $_" } # the probe ran in run-readwidth-5090-20261005 (bench-log, 5 October 2026); this second run is the bench only -foreach ($pk in @('v2-genesis-mh', 'mx4-genesis', 'mx4-devnet-epoch0')) { +foreach ($pk in @('v2-genesis-mh', 'mx4-genesis', 'mx4-devnet-epoch0', 'mx8-genesis', 'mx8-devnet-epoch0')) { $d = Join-Path $packs $pk Write-Output "RESULT bench $pk start $(Get-Date -Format HH:mm:ss)" & $exe --bench --pack $d --batches 5 --batch-log2 24 --block-warps 1 2>&1 | ForEach-Object { "RESULT $_" } diff --git a/relay/playbooks/mixer-x4-9070-bench.ps1 b/relay/playbooks/mixer-x4-9070-bench.ps1 index 50df5b65..4d808e14 100644 --- a/relay/playbooks/mixer-x4-9070-bench.ps1 +++ b/relay/playbooks/mixer-x4-9070-bench.ps1 @@ -4,7 +4,7 @@ # igneum-worker-opencl.exe on the v2 pack and the two v3 packs of the fetched packs folder (the dataset build time per pack is the # number this job is for: the worker's own `cache ... dataset ... ms` line, the same line the readwidth round printed), # and switches the card back on with the settings it had. Every result line starts with RESULT so `node tools/jobs.mjs ` shows them. -# The packs folder of the fetch job: proto-cuda/packs-ca2-mixer/mx4-genesis and mx4-devnet-epoch0 from branch ca2-mixer, plus +# The packs folder of the fetch job: proto-cuda/packs-ca2-mixer/mx4-genesis, mx4-devnet-epoch0, mx8-genesis and mx8-devnet-epoch0 (the x8 candidate) from branch ca2-mixer, plus # proto-cuda/packs/igneum-genesis-mh copied in as v2-genesis-mh (the version 2 control, same card, same run). $ErrorActionPreference = 'Continue' function Say([string] $m) { Write-Host ("[" + (Get-Date -Format 'HH:mm:ss') + "] " + $m) } @@ -20,7 +20,9 @@ $list = & $exe --list 2>&1 $list | ForEach-Object { "RESULT list $_" } $dev = $null foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1201' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } } -if ($null -eq $dev) { Write-Output 'RESULT error no gfx1201 device in --list'; exit 2 } +# the 9070 XT when it is on the bus, else the integrated gfx1036 (the coordinator's rule for the AMD build-time row) +if ($null -eq $dev) { foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1036' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; Write-Output 'RESULT device-fallback gfx1036 (no gfx1201 on the bus)'; break } } } +if ($null -eq $dev) { Write-Output 'RESULT error no gfx1201 or gfx1036 device in --list'; exit 2 } Write-Output "RESULT device $dev" # the app: switch off the NVIDIA card only, remember its settings @@ -51,7 +53,7 @@ if ($card) { } else { Write-Output 'RESULT card none-found (the app is not running or has no NVIDIA card); measuring with whatever else runs on the GPU' } # the probe ran in run-readwidth-9070-20261005 (bench-log, 5 October 2026); this second run is the bench only -foreach ($pk in @('v2-genesis-mh', 'mx4-genesis', 'mx4-devnet-epoch0')) { +foreach ($pk in @('v2-genesis-mh', 'mx4-genesis', 'mx4-devnet-epoch0', 'mx8-genesis', 'mx8-devnet-epoch0')) { $d = Join-Path $packs $pk Write-Output "RESULT bench $pk start $(Get-Date -Format HH:mm:ss)" & $exe --bench-pack --pack $d --batches 5 --batch-log2 24 --device $dev 2>&1 | ForEach-Object { "RESULT $_" } From d2d5f5d6f275cb43dfeb3330f8509ace4afd5e93 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:40:00 +0000 Subject: [PATCH 095/158] mixer-x4.md: the x8 packs' Metal and Apple OpenCL rows, the daily-build table per tier (5090, 9070 XT, M5 Max, gfx1036 per prepare, a scaled 8 GB-class row) and the x4 / x8 rule; chip-model-v3.md: the mixer row alone as the headline (layer 5 measured, not adopted, with the 5090 and 9070 XT g beside the Mac's), x8 rows at year 0 and year 4 Co-Authored-By: Claude Fable 5.1 --- docs/analysis/chip-model-v3.md | 26 ++++++++++++++------------ docs/plans/mixer-x4.md | 31 ++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/docs/analysis/chip-model-v3.md b/docs/analysis/chip-model-v3.md index 8de60663..0c005d97 100644 --- a/docs/analysis/chip-model-v3.md +++ b/docs/analysis/chip-model-v3.md @@ -35,11 +35,12 @@ silicon" = bare x (750 - SRAM) / 750 x 3: the SRAM takes die area the logic does | v2 as shipped (the M16 and scratch-soundness row) | x1 | 149,760 | 334 MH/s | 256 MiB | 128 / $46 | 2.45x (2.39x against 139.7) | 7.4x | 6.1x | | v2 at w16 (not adopted; the chip's cost is items, not bytes: unchanged) | x1 | 149,760 | 334 | 256 MiB | 128 / $46 | 2.39x against 139.8 | 7.2x | 5.9x | | v3: mixer x4 | x4 | 599,040 | 83.5 MH/s | 256 MiB | 128 / $46 | 0.61x | 1.84x | 1.53x | -| v3 plus a 32 MiB hot table, added form (16 dataset loads and k hot loads): the honest card pays the hot loads, this chip pays SRAM only | x4 | 599,040 (a hot load is one SRAM read, no item) | 83.5 | 288 MiB | 144 / $53 | 0.66x at the Mac's g = 0.93 (denominator 126.6 MH/s; the 5090's own g is pending the PC rows) | 1.98x | 1.60x | -| v3 plus a 64 MiB hot table, added form | x4 | 599,040 | 83.5 | 320 MiB | 160 / $61 | 0.71x at the Mac's g = 0.87 (118.4 MH/s; 5090 g pending) | 2.12x | 1.67x | -| v3 at year 4 (cache 512 MiB under option C, dataset 4 GiB), 64 MiB hot table | x4 | 599,040 | 83.5 | 576 MiB | 287 / $130 | 0.71x | 2.12x | 1.31x | -| v3 at year 12 (cache 1 GiB, dataset 8 GiB), 64 MiB hot table | x4 | 599,040 | 83.5 | 1,088 MiB | 542 / $330 | 0.71x | 2.12x | 0.59x | -| v3 with the mixer at x8 instead (the next lever, not adopted) | x8 | 1,198,080 | 41.7 | 256 MiB | 128 / $46 | 0.31x | 0.92x | 0.76x | +| MEASURED, NOT ADOPTED (layer 5 decided out of v3 on the PC rows, coordinator 21:40 UTC): v3 plus a 32 MiB hot table, added form (16 dataset loads and k hot loads): the honest card pays the hot loads, this chip pays SRAM only | x4 | 599,040 (a hot load is one SRAM read, no item) | 83.5 | 288 MiB | 144 / $53 | 0.66x at the Mac's g = 0.93 (126.6 MH/s); 0.70x at the 5090's g = 0.87 (118.4); the 9070 XT's g 0.84 | 1.98x (Mac g), 2.11x (5090 g) | 1.60x, 1.71x | +| MEASURED, NOT ADOPTED: v3 plus a 64 MiB hot table, added form | x4 | 599,040 | 83.5 | 320 MiB | 160 / $61 | 0.71x at the Mac's g = 0.87 (118.4 MH/s); 0.73x at the 5090's g = 0.84 (114.3); the 9070 XT's g 0.80 | 2.12x (Mac g), 2.19x (5090 g) | 1.67x, 1.73x | +| v3 at year 4 (cache 512 MiB under option C, dataset 4 GiB), no hot table | x4 | 599,040 | 83.5 | 512 MiB | 255 / $111 | 0.61x | 1.84x | 1.21x | +| v3 at year 12 (cache 1 GiB, dataset 8 GiB) | x4 | 599,040 | 83.5 | 1 GiB | 510 / $306 | 0.61x | 1.84x | 0.59x | +| v3 with the mixer at x8 (the candidate under the coordinator's rule of 21:30 UTC: in if the verify stays under 10 ms per warp on one Mac core and the daily 1 GiB build under 1 s on every discrete card) | x8 | 1,198,080 | 41.7 | 256 MiB | 128 / $46 | 0.31x | 0.92x | 0.76x | +| x8 at year 4 | x8 | 1,198,080 | 41.7 | 512 MiB | 255 / $111 | 0.31x | 0.92x | 0.61x | The era draws of spec 1.13.1 cost the chip nothing in this model: the mixer round count is not drawn, the op weights and fold rotations change the program, not the item derivation, so the chip's ops per hash stand. The @@ -60,13 +61,14 @@ not a forecast). ## 3. The margin, plainly -The combined headline row without the hot table reads 1.84x with the 3x factor at an equal integer budget, 1.5x -with the SRAM area deducted. With the hot table in the added form it reads 1.98x (32 MiB) and 2.12x (64 MiB) at -an equal integer budget on the Mac's `g`, 1.60x and 1.67x with the SRAM deducted: against THIS chip the added hot -table is a cost to the honest card and none to the chip, so it moves the row the wrong way, by the card's own -`g`; whether it is adopted is the DRAM-only chip's question (`docs/plans/hot-table.md`), not this one's. The claim -is "under 2x" on the equal-silicon convention and on the equal-budget convention without the hot table; with a -64 MiB table on the equal-budget convention it is not, and the margin everywhere is thin: +The combined headline row is the mixer row alone (layer 5 is out: the added form costs the 5090 13 to 16 percent +and the 9070 XT 16 to 20 percent against the 0.97 bar, coordinator 21:40 UTC; the width stays 4 bytes; the era +draws and the cache growth cost this chip nothing at year 0). At x4 it reads 1.84x with the 3x factor at an equal +integer budget, 1.53x with the SRAM area deducted; at x8 0.92x and 0.76x. The hot-table rows above are kept as +measured, not adopted: against THIS chip an added hot table is a cost to the honest card and none to the chip, so +it would have moved the row the wrong way by the card's own `g` (2.11x to 2.19x at the 5090's g). The claim is +"under 2x" at x4 on both conventions, with the margin thin on the equal-budget one; at x8 the chip is under 1x on +both: - the 3x fixed-function factor is approximate and from memory; at 3.3x the equal-budget row reads 2.0x; - the denominator is one card's measured rate on one night (136.1 against 139.7 the night before: 2.6% apart); diff --git a/docs/plans/mixer-x4.md b/docs/plans/mixer-x4.md index 2d9d013d..f4c92f58 100644 --- a/docs/plans/mixer-x4.md +++ b/docs/plans/mixer-x4.md @@ -208,6 +208,10 @@ base nonce 0. | mx4-genesis | Apple OpenCL | PASS | PASS | 96 of 96 lanes | 6f48d5a2aa0dbe5f | 27.7 (wall) | | mx4-devnet-epoch0 | Metal | 448274a57f508cbc PASS | PASS | 3/3, 3/3 | 73caaebb28e808fe | 27.5 | | mx4-devnet-epoch0 | Apple OpenCL | PASS | PASS | 96 of 96 lanes | 73caaebb28e808fe | 27.6 (wall) | +| mx8-genesis (the x8 candidate, 21:45 UTC) | Metal | 48c4f5bf24166b2e PASS | PASS | 3/3, 3/3 | 7c28cfb06c5c65a9 | 27.7 | +| mx8-genesis | Apple OpenCL | PASS | PASS | 96 of 96 lanes | 7c28cfb06c5c65a9 | 27.6 (wall) | +| mx8-devnet-epoch0 | Metal | 448274a57f508cbc PASS | PASS | 3/3, 3/3 | bbb183f72692f840 | 27.7 | +| mx8-devnet-epoch0 | Apple OpenCL | PASS | PASS | 96 of 96 lanes | bbb183f72692f840 | 27.7 (wall) | Reading: the Rust interpreter, Metal and Apple OpenCL agree on the v3 dataset (head, MASK word, 64 samples), on every vector lane and on the 2^24-output fingerprint of each pack; the hash rate is the v2 rate (27.7 MH/s on this @@ -234,7 +238,32 @@ x4 build on the GPU is the row the measure lock will settle. ### 6.4 Timings (`with-lock.sh measure`) -Owed until the measure lock frees. +Owed until the measure lock frees (the session script takes v2, x4 and x8 together: verifier avg of 50 and the +three cold units, the 256 MiB fill on one core, the Metal 1 GiB build, two rounds each). + +### 6.5 The daily build per tier, and the x4 / x8 rule + +The coordinator's rule (21:30 UTC): x8 enters v3 if the per-warp verify stays under 10 ms on one Mac core AND the +daily 1 GiB build stays under 1 s on every discrete card we own; else x4 with the thin margin stated and x8 named +as the next lever. The integrated tier is decided beside it (consequences row C23): its build is per prepare, not +per day, so its consequence is per-day dataset reuse in the workers or a restart per epoch. + +| Card | Build at x1 | x4 | x8 | Source | +|---|---|---|---|---| +| RTX 5090 (PC 2) | 13.4 ms (1 GiB) | owed (PC job, section 8) | owed | bench-log, 3 October, memory-hard entry | +| RX 9070 XT (PC 1, eGPU) | owed | owed | owed | PC job, section 8 | +| M5 Max, Metal | 13 to 30 ms (the two runs of the memory-hard entry; tonight's run-lock figures 21.7 to 30.2 ms at x4 and 22.0 to 30.0 at x8 say the Mac's build is latency-bound, not mixer-bound) | section 6.4 | section 6.4 | this file | +| Radeon integrated gfx1036 (PC 2), OpenCL, per prepare | 6.9 / 9.4 / 11.7 s prepare total with the 1 GiB build inside | about 28 to 47 s (approximate: scaled x4; the iGPU's build is arithmetic-bound at x1 already) | about 55 to 94 s (approximate) | `docs/plans/epoch-length.md` section 7 (branch ca2-epoch), M11 table | +| gfx1036 beside WSL build jobs (PC 1) | 55 / 116 / 124 s | about 4 to 8 min (approximate) | about 7 to 17 min (approximate) | same | +| 8 GB-class discrete card (not owned; about a tenth of the 5090's rate, approximate) | about 0.13 s | about 0.5 s | about 1 s, on the edge of the rule | scaled from the 5090 row, approximate | + +Reading, before the measured rows: on the discrete cards the rule is about the 5090 and 9070 XT rows (owed, the PC +job). The integrated tier misses the rule at x4 already: a per-prepare build of 28 to 47 s is a tenth to a quarter +of the 600-DAA-second lead the devnet gives the next program (spec 1.12), and under load it is the whole lead; so +if x4 or x8 goes in, the iGPU tier needs the workers to build the day's dataset once a day and keep it across +epochs (today a prepare rebuilds it: `proto-opencl/host.c` prepareTask builds the pair's cache and dataset per +prepare), or to restart per epoch. The node agent is asked whether the per-day reuse is bounded tonight +(coordinator, 21:45 UTC); until then the iGPU consequence stands as written. ## 7. The chip model From 829687b1abce41567c4ab8a0704b1a6342c0b1b5 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:43:01 +0000 Subject: [PATCH 096/158] mixer x4: the measure session (v2 / x4 / x8 verifier 1.33 / 1.94 / 2.79 ms per unit on a loaded core, 1.45x and 2.1x; the 256 MiB fill 172 to 175 ms; the Metal 1 GiB build flat at 21 ms, latency-bound), the verification-throughput consequences (C19), what is unverified and what is owed; the bench-log entry; the PC 1 two-card playbook; the measured verifier row in the chip model Co-Authored-By: Claude Fable 5.1 --- docs/analysis/chip-model-v3.md | 1 + docs/bench-log.md | 19 ++++++ docs/plans/mixer-x4.md | 83 ++++++++++++++++++++-- relay/playbooks/mixer-x4-pc1-bench.ps1 | 95 ++++++++++++++++++++++++++ 4 files changed, 193 insertions(+), 5 deletions(-) create mode 100644 relay/playbooks/mixer-x4-pc1-bench.ps1 diff --git a/docs/analysis/chip-model-v3.md b/docs/analysis/chip-model-v3.md index 0c005d97..0510d7ce 100644 --- a/docs/analysis/chip-model-v3.md +++ b/docs/analysis/chip-model-v3.md @@ -23,6 +23,7 @@ is a measurement of a chip; every GPU figure says where it was measured. "Approx | Cache mirror plus a 96 MB hot table, N5 headline | 175 mm^2, $68 | same, so a hot table costs 0.49 mm^2 and $0.23 per MB (linear, approximate) | | 512 MiB and 1 GiB mirrors, N5 headline | 255 mm^2 and 510 mm^2; $111 to $306 | same, section 4 (the growth rule's cache at years 4 and 12, priced at today's node) | | GPU-class die | 750 mm^2 (the equal-silicon comparison) | M16 section 3 | +| CPU verifier, one M5 Max core (loaded, load average 5.6; ratios are the measurement) | v2 1.31 to 1.36 ms per unit, x4 1.92 to 1.96 (1.45x), x8 2.79 (2.1x); worst cold 1.58 / 2.04 / 2.94 ms | `docs/plans/mixer-x4.md` section 6.4, 5 October 2026 21:40 UTC | ## 2. The rows diff --git a/docs/bench-log.md b/docs/bench-log.md index 8a82a73b..8550d873 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -1645,3 +1645,22 @@ Rates (5 dispatches of 2^24 after a warm-up; 5090 `--bench --block-warps 1`, 907 | hot96k4a | 114.4 | 0.84 | 14.56 | 0.80 | 1 | Reading: the probe promises a full hit rate on the 5090 (every S inside the 96 MiB L2 at one ceiling, 6.4x DRAM) and the hash gets 2 to 8% at k = 4 and 20% at k = 8; the 9070 XT the same shape. The dataset's random lines evict the table from the shared cache on every card. The added form costs 13 to 20% of the rate. Recommendation in `docs/plans/hot-table.md` section 6.4: do not adopt layer 5 in either form on these measurements. +## 5 October 2026 (night), mixer x4 and the cache growth rule: the class v3 dataset construction, with the x8 candidate (Counter ASIC 2.0; branch ca2-mixer on ca2-v3 6c75dad; cryptographer's lane) + +Machine: Apple M5 Max, 64 GiB, Darwin 25.6.0. Write-up `docs/plans/mixer-x4.md`; chip model `docs/analysis/chip-model-v3.md`; code `igneum-pow` (LoadClass mixer_mult and growth, memhard::Shape, the schedule, the three emitters), packs `proto-cuda/packs-ca2-mixer/`, tests `igneum-pow/tests/mixer.rs` and `tests/packs.rs`. Commits 0fc0ad1, 66eeba3, e4c04a7, 7ce8d1e, 504cae4, fe4e193 and this entry's. + +What changed. Under program class v3 (`V3_CLASS = LoadClass::MX4`) every mixer application of the item derivation is `m = 4` applications with round keys `(r m + j + 1) x 0x9E3779B9`, the 8 dependent cache reads per item unchanged; the cache doubles when the dataset doubles (`growth_doublings(d) = floor(log2(1 + d / 1460))`: 2^26 words to day 1,459, 2^27 from day 1,460, 2^28 from day 4,380). Version 2 is byte-identical: fresh exports of igneum-genesis-mh and igneum-devnet-v4-epoch0 `diff -r` IDENTICAL against the checked-in packs, and the crate tests regenerate every pinned file. A v3 program of a seed is the v2 program of that seed instruction for instruction (v2 loads take no width roll); only the dataset words and the hashes change. + +Bit-exactness, `with-lock.sh run`, 22:05 and 21:45 UTC: the two pinned v3 packs (mx4-genesis, mx4-devnet-epoch0: dataset words 0..15 `61ff2180 0d4c7e6c ...` and `afe80d67 b9fbd029 ...`, word MASK `5020180e` and `e6a99c7a`, unit at base 0 lane 0 `63acd2d273f475ba` and `212c6442b51e87ae`) and the two x8 candidate packs on Metal (`packbench`, built from this branch) and Apple OpenCL (`igneum-bench-cl --bench-pack`): 3/3 standalone and 3/3 in batch, 96 of 96 lanes, cache FNV-1a 64 unchanged from v2 (48c4f5bf24166b2e, 448274a57f508cbc), dataset head, word MASK and 64 samples PASS, one 2^24 fingerprint per pack across both harnesses (mx4 6f48d5a2aa0dbe5f and 73caaebb28e808fe; mx8 7c28cfb06c5c65a9 and bbb183f72692f840); hash rate the v2 rate (27.5 to 27.7 MH/s GPU time, the hash kernel is unchanged). Fuzz: 200 class v3 programs (4 units each across the 32-bit range, one in the top 256 nonces) interpreted twice on the CPU, 800 of 800; the same 200 packs on Metal 200 of 200 (`--batch-log2 9 --batch-base 4294967040`, the wrapping unit inside the window), every tenth on Apple OpenCL 20 of 20; x8: 50 of 50 on Metal, 5 of 5 on OpenCL. Stats (8,192 outputs per seed, two seeds): v3 avalanche 49.97 to 49.99 percent, worst bit z 1.92 to 3.09, 0 duplicates (v2 beside it 49.87 to 49.98, z 2.25 to 2.30). Edges: items 0, 1, 2^28 - 1, 2^32 - 1 by hand at m = 1, 2, 4, 8; words 0, 15, 16, 17, MASK - 1, MASK through the fetch path. Determinism: two epochs, every vector and file equal and equal to the pinned pack. The scratch soundness tests of ca2-soundness (cherry-pick 0d8f745) 7 of 7 on this tree. Crate: 44 lib + 12 packs + 4 mixer + 7 scratch tests pass. A first Metal fuzz run reported 200 of 200 FAIL on an empty RESULT line (a packbench built before the `--batch-base` cherry-pick); it was read as a failure, the harness rebuilt, the run repeated. + +Timings, `with-lock.sh measure`, one session 21:40:12 to 21:40:23 UTC, one core, two rounds; the box carried a load average of 5.6 (one minute) and 26 (fifteen minutes) from unlocked processes, so the absolute figures are about 2.2x the quiet readwidth night's 0.604 ms v2 row and the ratios are the measurement: + +| Construction | Verifier ms per 32-lane unit, avg of 50 (two rounds) | Worst cold unit | Against v2 | 256 MiB fill, one core | Metal 1 GiB build, GPU ms | +|---|---|---|---|---|---| +| v2 | 1.361 / 1.310 | 1.579 | 1 | 172 to 173 ms | 29.7 (first touch) / 21.0 | +| x4 (class v3) | 1.956 / 1.923 | 2.043 | 1.45x | 172 to 175 ms | 20.9 / 21.0 | +| x8 (candidate) | 2.785 / 2.790 | 2.942 | 2.09x | 172 ms | 21.9 / 21.9 | + +Reading: the mixer multiplies the verifier's ALU part only (the 8 dependent misses per item are unchanged), hence 1.45x and 2.1x and not 4x and 8x; the Mac's GPU build is latency-bound and does not move with the mixer, so the "under 1 s on every discrete card" half of the x8 rule is the PC job (five packs, `relay/playbooks/mixer-x4-pc1-bench.ps1`, waiting for the go). Verification throughput (C19): a quiet 2026 core serves about 1,100 shares per second at x4 and 800 at x8 (1,660 at v2, re-cutting spec 09's 2,270), a 22,000-member pool at one share per 10 s needs 2 cores at x4 and 3 at x8, IBD over 108,000 headers is 1.6 min at x4 and 2.3 at x8 on that core; the 10 ms gate keeps 8.0 ms (x4) and 7.1 ms (x8) of margin on the loaded core, 6 to 7 ms on a 2019-class laptop core (approximate, unmeasured, O-1.14). + +Chip model (`docs/analysis/chip-model-v3.md`): the on-die-cache recompute chip at 50 T op/s against the 5090's measured 136.1 MH/s: v2 334 MH/s, 2.45x bare, 7.4x with the 3x fixed-function factor; x4 83.5 MH/s, 0.61x bare, 1.84x with the factor, 1.53x with the 128 mm^2 N5 mirror deducted at equal silicon; x8 41.7 MH/s, 0.31x, 0.92x, 0.76x. The claim at x4 is "under 2x" with the margin thin on the equal-budget convention (a 3.3x factor or a 10 percent larger budget reads 2.0x); the hot table in the added form would have raised it to 2.1x to 2.2x at the 5090's g (kept as measured, not adopted). Nothing here is a measurement of a chip. diff --git a/docs/plans/mixer-x4.md b/docs/plans/mixer-x4.md index f4c92f58..ce91b34d 100644 --- a/docs/plans/mixer-x4.md +++ b/docs/plans/mixer-x4.md @@ -236,10 +236,61 @@ x4 build on the GPU is the row the measure lock will settle. | Metal and Apple OpenCL on the two pinned v3 packs | section 6.2 | 3/3 standalone, 3/3 in batch, 96 of 96 lanes, dataset head, MASK word and 64 samples, one fingerprint per pack across both harnesses | | Metal fuzz: the 200 packs, 4 units each standalone and the top-256 unit inside a 512-nonce batch at base 4,294,967,040; every tenth pack on Apple OpenCL as well | `packbench --pack --batches 1 --batch-log2 9 --batch-base 4294967040` (Metal), `igneum-bench-cl-igneum-genesis-mh --bench-pack --pack --batches 1 --batch-log2 10` (Apple OpenCL), `with-lock.sh run`, 21:22 to 21:24 UTC | Metal 200 of 200 packs PASS (800 of 800 standalone units, 200 of 200 inside the wrapping window, cache and dataset self-tests on every pack); Apple OpenCL 20 of 20 packs PASS (the three vectors.h units, the self-tests); a first run with a packbench built before the `--batch-base` cherry-pick reported 200 of 200 FAIL on an empty RESULT line and was read as such (the watcher rule), the harness rebuilt and the run repeated | -### 6.4 Timings (`with-lock.sh measure`) +### 6.4 Timings (`with-lock.sh measure`, one session, 21:40:12 to 21:40:23 UTC, commit 504cae4) -Owed until the measure lock frees (the session script takes v2, x4 and x8 together: verifier avg of 50 and the -three cold units, the 256 MiB fill on one core, the Metal 1 GiB build, two rounds each). +Script `measure-v3.sh` (session scratchpad): `igneum-pow bench --seed igneum-genesis --day 2026-10-03 --warps 50` +(v2), `... --program-class v3` (x4), `... --class mx8` (x8), two rounds each, then the devnet seeds, then +`packbench --pack --batches 2 --batch-log2 22 --group 256` on igneum-genesis-mh, mx4-genesis and mx8-genesis, +two rounds. The lock was exclusive among the agents' builds and measurements, but the box was not quiet: load +average 5.6 (one minute) and 26 (fifteen minutes) at the start, from unlocked processes (the devnet node, other +agents' editors); the v2 row reads 1.31 to 1.36 ms where the quiet readwidth night read 0.604 to 0.626. So the +absolute numbers below are a loaded-core figure, about 2.2x the quiet one, and the ratios between the rows are the +measurement (two rounds within 4 percent). A quiet-box re-run is owed (section 9). + +| Construction | Verifier, ms per 32-lane unit, avg of 50 (round 1 / round 2) | Worst cold unit of three | Against v2 | 256 MiB cache fill, one core | Metal 1 GiB dataset build, GPU ms (round 1 / round 2) | +|---|---|---|---|---|---| +| v2 (igneum-genesis) | 1.361 / 1.310 | 1.579 | 1 | 172.1 / 172.6 ms | 29.7 / 21.0 | +| x4 (mx4, class v3) | 1.956 / 1.923 | 2.043 | 1.45x | 175.3 / 172.3 ms | 20.9 / 21.0 | +| x8 (mx8) | 2.785 / 2.790 | 2.942 | 2.09x | 172.3 / 172.3 ms | 21.9 / 21.9 | +| x4, the devnet seeds (mx4-devnet-epoch0) | 1.923 | 2.012 | | 173.9 ms | | +| x8, the devnet seeds | 2.972 | 2.885 | | 173.6 ms | | + +Reading. The verifier's ALU part is what grows: x4 adds 0.6 ms per unit for 27 more mixer applications on each of +4,096 items (110,592 applications, about 5.5 ns each on this core, the lanes' chains interleaved), x8 another +0.85 ms for 36 more; the latency part (8 dependent misses per item) is the same in every row, which is why the +measured ratios are 1.45x and 2.1x and not the 4x and 8x of the M16 table's scaling. Shape B (32 rounds of one +read, section 1) would have multiplied the latency part too; x4 is under the 4.8 ms bar even on the loaded core, so +B stays unimplemented. The cache fill does not depend on the mixer (it is the ChaCha chain): 172 to 175 ms, the +spec's 175 to 181 ms of 1.8.3. The Metal 1 GiB build does not move with the mixer at all (21 ms at v2, x4 and x8 +once warm; the 29.7 ms first v2 run is the first-touch cost the hosts fill twice for): on this card the build is +bound by the 8 dependent cache-line reads per item, not by the arithmetic, so the Mac says nothing about whether +the 5090's or the 9070 XT's build is arithmetic-bound; that is the PC job (section 8). + +Against the x4 / x8 rule (section 6.5): the verifier half passes for x8 with 7.1 ms of the 10 ms gate to spare on +this loaded core (worst cold 2.94 ms; the quiet-core figure would be about 1.3 ms, scaled by the 2.2x of the v2 +row, approximate); x4 leaves 8.0 ms. The build half waits on the PC rows. + +### 6.5 Verification throughput per tier (consequences row C19), from the loaded-core figures above + +Warps verified per second on one core = 1,000 / (ms per warp); a pool core verifying members' shares handles that +many shares per second; a node verifies a block with one unit (plus the header path, under 0.1 ms, not measured +here); IBD over the 108,000-header pruning window (spec 02) on one core = 108,000 x ms per warp. + +| Figure | v2 | x4 | x8 | Note | +|---|---|---|---|---| +| ms per warp, steady (this session, loaded core) | 1.33 | 1.94 | 2.79 | avg of the two rounds | +| ms per warp, quiet M5 Max core (scaled by 0.604 / 1.33 = 0.45, approximate) | 0.60 | 0.88 | 1.26 | the readwidth night's v2 figure is measured; x4 and x8 scaled | +| ms per warp, 2019-class laptop core (approximate: 2.5x the quiet M5 Max figure, the ratio the design document assumes for the gate; unmeasured, O-1.14) | 1.5 | 2.2 | 3.2 | the figure that fixes the gate is a measurement, not this row | +| Shares per second per core (loaded / quiet, approximate) | 750 / 1,660 | 515 / 1,140 | 358 / 790 | spec 09 section 9.8 item 5 carried 2,270 at v2; re-cut from the quiet row: 1,660 | +| Cores for a 22,000-member pool at one share per member per 10 s (2,200 shares per second), loaded / quiet | 2.9 / 1.3 | 4.3 / 1.9 | 6.1 / 2.8 | | +| Node: worst cold single unit (loaded core) | 1.58 ms | 2.04 ms | 2.94 ms | per block | +| IBD over 108,000 headers on one core, loaded / quiet, minutes | 2.4 / 1.1 | 3.5 / 1.6 | 5.0 / 2.3 | laptop (approximate): 2.7 / 4.0 / 5.8 min; a seed VM core (unmeasured) sits between the laptop and the quiet M5 Max | +| Margin left under the 10 ms gate for Counter ASIC 3.0 (worst cold, loaded core) | 8.4 ms | 8.0 ms | 7.1 ms | on the 2019-class laptop row (approximate) 7.5 / 6.8 / 5.9 ms steady | + +Reading: at x4 a pool core serves about 1,100 shares per second on a quiet 2026 core (a 22,000-member pool needs +two cores); at x8 about 800 (three cores). A node's block verification stays a few milliseconds. The gate's +remaining margin is what Counter ASIC 3.0 has to spend, and on the unmeasured laptop core it is 6 to 7 ms at x4 and +about 6 at x8, which is the number the 2019-class measurement (O-1.14) must confirm before x8 is final. ### 6.5 The daily build per tier, and the x4 / x8 rule @@ -271,8 +322,30 @@ prepare), or to restart per epoch. The node agent is asked whether the per-day r ## 8. What is unverified -Filled at the end. +1. The 5090's and the 9070 XT's dataset build at x4 and x8 (the "under 1 s on every discrete card" half of the x8 + rule): the PC 1 job (`relay/playbooks/mixer-x4-pc1-bench.ps1`, package `mixer-x4-pcjob.zip` sha256 + ec3be97e...bdf87, five packs, the worker's `cache ... dataset ... ms` line per pack) waits for the coordinator's + go after the era job; by the M16 arithmetic the 5090 is 54 ms at x4 and 107 ms at x8 if its build is + arithmetic-bound and 13.4 ms if it is latency-bound like the Mac, both far under 1 s; the gfx1036 is the tier + that fails (section 6.5 of the build table), and its consequence (per-day dataset reuse in the workers) is with + the node agent. +2. The absolute verifier figures were taken on a loaded core (load average 5.6); the ratios are the measurement + and the quiet-core figures are scaled. A 2019-class laptop core has not run any construction (O-1.14). +3. The mixer has had no cryptanalysis (spec 1.8.4); `m` applications with distinct round keys is `m` times the + work only if no shortcut composes them, which is the same open question as for one application. +4. The x8 packs are generator 2 with the class in the id (`--class mx8`); if x8 is chosen, the pinned v3 packs are + re-cut through the seam (`V3_CLASS = MX8`, generator 3) and the tests re-pinned, one commit. +5. The 5090's rate for the v3 program is the v2 rate by construction (the hash kernel is unchanged, the Mac shows + 27.7 MH/s at v2, x4 and x8); the chip row's denominator stays the readwidth table's 136.1 MH/s until a v3 pack + runs on the card, which the PC job also gives. ## 9. Owed -Filled at the end. +| Item | Owner | When | +|---|---|---| +| PC 1 run of the five packs (the build-time rows, the v3 fingerprints on NVIDIA and AMD) | ca2-mixer, on the coordinator's go | after the era job, about 22:05 UTC | +| Quiet-box re-run of the verifier session for absolute numbers | ca2-mixer | when the Mac is quiet | +| The x4 / x8 choice recorded from the rule, then the vectors re-cut once through the seam | coordinator, then ca2-mixer | after the PC rows | +| `Epoch::chain_dataset_day` wired to the genesis day index in the node (`days_since_genesis(day_index(header), day_index(genesis))`) and `pow_genesis_dataset_log2` in the override | ca2-node | the integration | +| The spec text of section 2 into `docs/spec/01-lottery-hash.md` 1.8.5 and 1.13.3 (with the v3 vectors into 1.17) | the integration | after the choice | +| The 2019-class laptop core measurement that fixes the gate (O-1.14) | cryptographer | gate 1 | diff --git a/relay/playbooks/mixer-x4-pc1-bench.ps1 b/relay/playbooks/mixer-x4-pc1-bench.ps1 new file mode 100644 index 00000000..6a92de28 --- /dev/null +++ b/relay/playbooks/mixer-x4-pc1-bench.ps1 @@ -0,0 +1,95 @@ +# Igneum run job: the class v3 dataset construction, mixer x4 and the x8 candidate (docs/plans/mixer-x4.md), on PC 1 +# (machine ae432dc7): the RTX 5090 through igneum-worker-cuda.exe --bench, then the AMD card through +# igneum-worker-opencl.exe --bench-pack, each card switched off in the app ONLY while it is under test and restored +# after with the settings it had (the ca2-era-pc1.ps1 shape). 5 October 2026. The AMD card: the RX 9070 XT (gfx1201) +# when its eGPU box is on the bus, else the integrated gfx1036. The number this job is for: the worker's own +# `cache ... dataset ... ms` line per pack, the daily 1 GiB dataset build time at x1, x4 and x8 (the coordinator's rule: +# x8 enters v3 only if that build stays under 1 s on every discrete card we own), plus the vectors and the 2^24 +# fingerprint per pack (bit-exactness against the Mac: mx4-genesis 6f48d5a2aa0dbe5f, mx4-devnet-epoch0 73caaebb28e808fe, +# mx8-genesis 7c28cfb06c5c65a9, mx8-devnet-epoch0 bbb183f72692f840, v2-genesis-mh 25f96e7dce90bd4e). +# Published as a plain `run` job (NOT --stop-miners): the installed app keeps every other card mining. Packs: +# proto-cuda/packs-ca2-mixer/{mx4-genesis, mx4-devnet-epoch0, mx8-genesis, mx8-devnet-epoch0} from branch ca2-mixer and +# proto-cuda/packs/igneum-genesis-mh as v2-genesis-mh (the control). Every result line starts with RESULT. +$ErrorActionPreference = 'Continue' +function Say([string] $m) { Write-Host ("[" + (Get-Date -Format 'HH:mm:ss') + "] " + $m) } +$jobs = Split-Path $env:IGNEUM_JOB_DIR +$fetched = Join-Path $jobs 'fetch-mixer-x4-20261005' +$cuda = Join-Path $fetched 'igneum-worker-cuda.exe' +$ocl = Join-Path $fetched 'igneum-worker-opencl.exe' +$packs = Join-Path $fetched 'packs-ca2-mixer' +$packList = @('v2-genesis-mh', 'mx4-genesis', 'mx4-devnet-epoch0', 'mx8-genesis', 'mx8-devnet-epoch0') +if (-not (Test-Path $cuda)) { Write-Output "RESULT error cuda worker missing at $cuda (the fetch job runs first)"; exit 2 } +if (-not (Test-Path $ocl)) { Write-Output "RESULT error opencl worker missing at $ocl"; exit 2 } +if (-not (Test-Path $packs)) { Write-Output "RESULT error packs missing at $packs"; exit 2 } +foreach ($pk in $packList) { if (-not (Test-Path (Join-Path $packs $pk))) { Write-Output "RESULT error pack $pk missing"; exit 2 } } +$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-worker-cuda.exe') } | Select-Object -First 1 +if (-not $inst) { Write-Output 'RESULT error no installed igneum-worker-cuda.exe (the NVRTC DLLs come from there)'; exit 2 } +Get-ChildItem $inst -Filter 'nvrtc*.dll' | Copy-Item -Destination $fetched -Force +Write-Output "RESULT worker-cuda $cuda sha256 $((Get-FileHash -Algorithm SHA256 $cuda).Hash.ToLower()) with $((Get-ChildItem $fetched -Filter 'nvrtc*.dll').Count) NVRTC DLL(s) from $inst" +Write-Output "RESULT worker-opencl $ocl sha256 $((Get-FileHash -Algorithm SHA256 $ocl).Hash.ToLower())" + +# the app +$appDir = $env:IGNEUM_APP_DIR +if (-not $appDir) { $appDir = Join-Path $env:LOCALAPPDATA 'igneum\app' } +$urlFile = Join-Path $appDir 'app.url' +$url = $null +if (Test-Path $urlFile) { $url = (Get-Content -LiteralPath $urlFile -Raw).Trim() } +function Find-Card([string] $vendor, [string] $keyMatch) { + if (-not $url) { return $null } + try { + $st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10 + $c = $st.mining.cards | Where-Object { $_.vendor -eq $vendor -and $_.key -match $keyMatch } | Select-Object -First 1 + if (-not $c) { $c = $st.cards | Where-Object { $_.vendor -eq $vendor -and $_.key -match $keyMatch } | Select-Object -First 1 } + return $c + } catch { Say ("api/state: " + $_.Exception.Message); return $null } +} +function Card-Off($card) { + Write-Output ("RESULT card " + $card.key + " enabled=" + $card.enabled + " identities=" + $card.identities + " power_pct=" + $card.power_pct + " state=" + $card.state) + $body = @{ cards = @(@{ key = $card.key; enabled = $false; identities = [int]$card.identities; power_pct = [int]$card.power_pct }) } | ConvertTo-Json -Depth 5 + try { Invoke-RestMethod -Uri ($url + 'api/cards') -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; Say "card off requested" } catch { Say ("api/cards off: " + $_.Exception.Message) } + $t = 0 + while ($t -lt 90) { + Start-Sleep -Seconds 5; $t += 5 + try { $st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10; $c2 = $st.mining.cards | Where-Object { $_.key -eq $card.key }; if (-not $c2) { $c2 = $st.cards | Where-Object { $_.key -eq $card.key } }; if ($c2 -and $c2.state -eq 'off' -and $c2.pid -eq 0) { break } } catch { } + } + Write-Output ("RESULT card-off " + $card.key + " after " + $t + " s") + Start-Sleep -Seconds 5 +} +function Card-Restore($card) { + $body = @{ cards = @(@{ key = $card.key; enabled = [bool]$card.enabled; identities = [int]$card.identities; power_pct = [int]$card.power_pct }) } | ConvertTo-Json -Depth 5 + try { Invoke-RestMethod -Uri ($url + 'api/cards') -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; Write-Output ("RESULT card restored " + $card.key + " enabled=" + $card.enabled) } catch { Write-Output ("RESULT error card restore " + $card.key + ": " + $_.Exception.Message) } +} + +# ---- the RTX 5090 (CUDA, NVRTC) ---- +$nv = Find-Card 'nvidia' '.' +if ($nv) { Card-Off $nv } else { Write-Output 'RESULT card none-found nvidia (the app is not running or has no NVIDIA card); measuring with whatever else runs on the GPU' } +& nvidia-smi --query-gpu=name,driver_version,power.limit,clocks.sm,clocks.mem,memory.used,temperature.gpu --format=csv,noheader 2>&1 | ForEach-Object { "RESULT gpu-before $_" } +foreach ($pk in $packList) { + $d = Join-Path $packs $pk + Write-Output "RESULT bench-5090 $pk start $(Get-Date -Format HH:mm:ss)" + & $cuda --bench --pack $d --batches 5 --batch-log2 24 --block-warps 1 2>&1 | ForEach-Object { "RESULT $_" } + & $cuda --bench --pack $d --batches 5 --batch-log2 24 --block-warps 8 2>&1 | Where-Object { $_ -match '^RESULT|error|FAIL|dataset' } | ForEach-Object { "RESULT $_" } +} +& nvidia-smi --query-gpu=power.draw,clocks.sm,clocks.mem,memory.used,temperature.gpu --format=csv,noheader 2>&1 | ForEach-Object { "RESULT gpu-after $_" } +if ($nv) { Card-Restore $nv } + +# ---- the AMD card (OpenCL): the gfx1201 on the eGPU when present, else the integrated gfx1036 ---- +$list = & $ocl --list 2>&1 +$list | ForEach-Object { "RESULT list $_" } +$dev = $null; $gfx = $null +foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1201' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; $gfx = 'gfx1201'; break } } +if ($null -eq $dev) { foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1036' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; $gfx = 'gfx1036'; break } } } +if ($null -eq $dev) { Write-Output 'RESULT error no gfx1201 and no gfx1036 device in --list'; exit 2 } +Write-Output "RESULT device $dev $gfx" +$amd = Find-Card 'amd' $gfx +if ($amd) { Card-Off $amd } else { Write-Output "RESULT card none-found $gfx (the app is not running or has no such card); measuring with whatever else runs on the GPU" } +# the 9070 XT: the full shape; the gfx1036 (about 3 MH/s): the same 2^24 fingerprint range, 2 timed dispatches, one shape +$batches = 5; if ($gfx -eq 'gfx1036') { $batches = 2 } +foreach ($pk in $packList) { + $d = Join-Path $packs $pk + Write-Output "RESULT bench-$gfx $pk start $(Get-Date -Format HH:mm:ss)" + & $ocl --bench-pack --pack $d --batches $batches --batch-log2 24 --device $dev 2>&1 | ForEach-Object { "RESULT $_" } + if ($gfx -eq 'gfx1201') { & $ocl --bench-pack --pack $d --batches 5 --batch-log2 24 --device $dev --group-warps 8 2>&1 | Where-Object { $_ -match '^RESULT|error|FAIL|dataset' } | ForEach-Object { "RESULT $_" } } +} +if ($amd) { Card-Restore $amd } +exit 0 From 4f0b9f5475df01f2455db475fe60dcf83ecd2537 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:03:06 +0000 Subject: [PATCH 097/158] bench-log: the root-socket recurrence of 21:25Z and the fix job of 22:01Z (the prover proved again at 22:02Z) Co-Authored-By: Claude Fable 5.1 --- docs/bench-log.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/bench-log.md b/docs/bench-log.md index 74ebabd0..2976bd59 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -1666,7 +1666,7 @@ Reading. The GPU memory of a compressed shard proof is **13.9 GB for a shard of Job `memminer-pc2-pv1` (`tools/proving-v1/pc2-memory-miner-on.ps1`), 20:28 to 20:30Z, the miner at full rate on the card, the live prover off for the run, the same 1-s sampler: the full shard at `S_p` (60.4 M cycles) peaked at **30,039 MiB** and took 33.0 s (28,295 MiB and 11.4 s with the card to itself: the miner costs the prover 2.9x in time and 1.7 GB of memory); the empty shard **15,670 MiB** and 7.7 s (13,863 and 2.3 s alone). So a 32 GB card mines and proves the prototype shard with 2.5 GB to spare; a 24 GB card cannot prove it even alone (28.3 GB); a 16 GB card cannot hold even the empty shard beside the miner (15.7 GB, the display and driver on top). `sp1-gpu-server --help` prints only `--version`: it has no options of its own, and its strings carry no memory setting (`CUDA_OUT_OF_MEMORY` is an error name). The shard SIZE is therefore the only lever left on this build, measured next as the S_p curve. -The root-socket fault (the class, fixed the same evening). The chain and memory jobs ran the host as root inside WSL2; the first `sp1-gpu-server` they started left `/tmp/sp1-cuda-0.sock` owned by root, and the live prover (the app's own WSL user) then failed every shard with `CudaClientError: Connect(Os { code: 13, kind: PermissionDenied })` (PC 2 app log 1791230456, 20:00:56Z) until the socket was gone. Every pv1 playbook now kills the server and unlinks `/tmp/sp1-cuda-*.sock` at its start and end, `tools/ci/prover-socket-check.sh` fails CI on any playbook that runs a prove mode as root without both lines (shown failing on `pc2-prover-cost.ps1` before its `--mode id`-only exemption, passing after), and the plan carries the rule: a prover job on a shared card runs as the app's user or cleans its socket. Playbooks that run the host: `tools/proving-v1/pc2-chain.ps1`, `pc2-memory-sweep.ps1`, `pc2-memory-miner-on.ps1`, `pc2-sp-curve.ps1` (all root, all with the cleanup now; the first two chain and sweep runs had none), `pc2-prover-cost.ps1` (`--mode id` only), `relay/playbooks/shard-test.ps1` and `proving/windows-wsl2/prove-shard.sh`, `prove-block.sh` (the app's user, not root), `tools/proving-v0/run.mjs` (the Mac, no server). +The root-socket fault (the class, fixed the same evening). The chain and memory jobs ran the host as root inside WSL2; the first `sp1-gpu-server` they started left `/tmp/sp1-cuda-0.sock` owned by root, and the live prover (the app's own WSL user) then failed every shard with `CudaClientError: Connect(Os { code: 13, kind: PermissionDenied })` (PC 2 app log 1791230456, 20:00:56Z) until the socket was gone. Every pv1 playbook now kills the server and unlinks `/tmp/sp1-cuda-*.sock` at its start and end, `tools/ci/prover-socket-check.sh` fails CI on any playbook that runs a prove mode as root without both lines (shown failing on `pc2-prover-cost.ps1` before its `--mode id`-only exemption, passing after), and the plan carries the rule: a prover job on a shared card runs as the app's user or cleans its socket. It recurred at 21:25Z from another agent's job (agg-cost-pc2-1, the same root-run shape) and survived the 0.3.10 restart at 21:49:41Z; the fix job `socketfix-pc2-pv1` (`tools/proving-v1/pc2-socket-fix.ps1`, 22:01:14 to 22:02:12Z) found `/tmp/sp1-cuda-0.sock` owned by root, removed it, switched the prover off and on, and the app's next shard (block 89011 shard 0) was proven and submitted in 34 s and paid 0.93 IGN at 22:02:24Z. Playbooks that run the host: `tools/proving-v1/pc2-chain.ps1`, `pc2-memory-sweep.ps1`, `pc2-memory-miner-on.ps1`, `pc2-sp-curve.ps1` (all root, all with the cleanup now; the first two chain and sweep runs had none), `pc2-prover-cost.ps1` (`--mode id` only), `relay/playbooks/shard-test.ps1` and `proving/windows-wsl2/prove-shard.sh`, `prove-block.sh` (the app's user, not root), `tools/proving-v0/run.mjs` (the Mac, no server). ### The S_p curve: peak GPU memory against shard size against time, the card to itself (the first of the two curve jobs) From 92ab4582b33b73124ba4c0421a7e13d375c9b482 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:04:29 +0000 Subject: [PATCH 098/158] counter-asic-2-node.md: the three PC 2 suite jobs (the flake, the kaspa-consensus rerun passed, job 3 pending) Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-2-node.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index b7029838..bb3a827c 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -112,7 +112,13 @@ CPU hash rate across the switch, run 2, miner cpu0 (one thread, the Mac shared w Epoch 0's v2 id is the same in both runs (`8f8806638d59850f`: a v2 program is untouched by the era code, on the chain as in the packs); the v3 ids differ from run 1 because the era draw is now inside the class. -The PC 2 suite job for the same tree: `build-20261005-215219` (fork 79bd8e10, main b105a55, published 21:52:19Z; its SUMMARY is the G6 evidence, recorded by the coordinator in the status file). +The PC 2 suite jobs for the same fork (79bd8e10), the G6 evidence (the coordinator's status file carries the SUMMARY lines): + +| Job | Tree | What | Result | +|---|---|---|---| +| `build-20261005-215219` | main b105a55 | the Linux node, the six node suites, the app tests | Linux build and app tests passed; `kaspa-consensus` failed on the known flake (`ban_is_decided_by_the_carrying_block`, `UnexpectedDifficulty` in `mine_on_all`, the 0.3.10 cut's section 11 case) | +| `build-20261005-215712` | main b105a55 | `kaspa-consensus` alone | 97 passed, 0 failed, 3 ignored, `ban_is_decided ...` ok, 21:59:45Z | +| `build-20261005-220351` | main 8ea6740 | the other five crates (`kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows`) and the app tests | published 22:03:51Z, SUMMARY pending | ## 6. Tests From 301ad547ba1927839ce313d8212c544f586f0315 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:05:31 +0000 Subject: [PATCH 099/158] generator.rs: LoadClass::MX8 carries the era and hot fields (None) the merge of ca2-mixer 829687b onto the era and cache tree left out Co-Authored-By: Claude Fable 5.1 --- igneum-pow/src/generator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/igneum-pow/src/generator.rs b/igneum-pow/src/generator.rs index 00a05124..1ca1d894 100644 --- a/igneum-pow/src/generator.rs +++ b/igneum-pow/src/generator.rs @@ -404,7 +404,7 @@ impl LoadClass { /// the per-warp verify stays under 10 ms on one Mac core and the daily 1 GiB build under 1 s on every card): /// the same loads and growth rule, the mixer applied 8 times per round. Name "mx8". pub const MX8: LoadClass = - LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 8, growth: true }; + LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 8, growth: true, era: None, hot: None }; /// A fixed width (1, 4 or 16 words) with `load_slots` loads per program. pub fn fixed(width_words: u8, load_slots: u8) -> LoadClass { From 2fe2626d94f1f3fcc965ff84d04396f5373a509a Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:05:57 +0000 Subject: [PATCH 100/158] tests/scratch.rs: the Instr literals carry the era fields win and off (0), left out by the merge of ca2-mixer onto the era tree Co-Authored-By: Claude Fable 5.1 --- igneum-pow/tests/scratch.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/igneum-pow/tests/scratch.rs b/igneum-pow/tests/scratch.rs index 75e48f58..acf376d1 100644 --- a/igneum-pow/tests/scratch.rs +++ b/igneum-pow/tests/scratch.rs @@ -240,10 +240,10 @@ fn written_words_unbiased_and_rehit_rates() { // --------------------------------------------------------------------------------------------------------------- fn ins(op: Op, dst: u8, src: u8) -> Instr { - Instr { op, dst, src, src2: 0, imm: 0, imm2: 0, rot: 1, bit: 0, mask: 1, width: 1 } + Instr { op, dst, src, src2: 0, imm: 0, imm2: 0, rot: 1, bit: 0, mask: 1, width: 1, win: 0, off: 0 } } fn add_imm(dst: u8, src: u8, imm: u32) -> Instr { - Instr { op: Op::Add, dst, src, src2: 0, imm, imm2: imm, rot: 1, bit: 0, mask: 1, width: 1 } + Instr { op: Op::Add, dst, src, src2: 0, imm, imm2: imm, rot: 1, bit: 0, mask: 1, width: 1, win: 0, off: 0 } } /// A hand-built program of class `c` named `name` (its seed is the name, so its fill words and init words are From c302a5738e18d41c8eaa9c156a43c9451eb08af9 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:08:44 +0000 Subject: [PATCH 101/158] counter-asic-2-node.md: suite job 3's result (kaspa-pow test rewritten for the era-in-class rule), the G6 reading corrected (the PC runs the feature-gated tests) Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-2-node.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index bb3a827c..717c1af4 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -118,7 +118,9 @@ The PC 2 suite jobs for the same fork (79bd8e10), the G6 evidence (the coordinat |---|---|---|---| | `build-20261005-215219` | main b105a55 | the Linux node, the six node suites, the app tests | Linux build and app tests passed; `kaspa-consensus` failed on the known flake (`ban_is_decided_by_the_carrying_block`, `UnexpectedDifficulty` in `mine_on_all`, the 0.3.10 cut's section 11 case) | | `build-20261005-215712` | main b105a55 | `kaspa-consensus` alone | 97 passed, 0 failed, 3 ignored, `ban_is_decided ...` ok, 21:59:45Z | -| `build-20261005-220351` | main 8ea6740 | the other five crates (`kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows`) and the app tests | published 22:03:51Z, SUMMARY pending | +| `build-20261005-220351` | main 8ea6740 | the other five crates (`kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows`) and the app tests | 22:04 to 22:06:55Z: igneum-exec 17, igneum-miner 18, consensus-core and p2p-flows green, the app 78 + 26 + 8; `kaspa-pow` FAILED on `program_class_v3_seeds_hash_their_own_program_over_their_own_cache` (the fork test compared a chain v3 program's class to `V3_CLASS` with `era: None`; on the era-merged crate the class carries the drawn era inside). Fixed on the fork at 89dfcb95 (the class minus its era is `V3_CLASS`, the era is drawn, another era seed keeps the program id and hashes another program over the same day cache). Re-run owed with the mixer fix job | + +The PC's test stage builds the fork's test binaries with the plan's feature set, so the v3 engine test DID run on PC 2 (the earlier reading that it was Mac-only is withdrawn): the PC job is the G6 evidence. ## 6. Tests @@ -127,7 +129,7 @@ The PC 2 suite jobs for the same fork (79bd8e10), the G6 evidence (the coordinat | igneum-pow `cargo test --release` | 42 unit + 11 pack tests, including `program_classes`, `program_class_and_era_are_checked`, the pinned-pack diffs | pass (Mac, 5 Oct 2026) | | `proto-cuda/nvrtc/emu/packfile-test.sh` | 13 checks: the attempt rule, the generator rule, a v3 pack with its era, the token matcher | pass (Mac) | | `host.c`, `worker.cpp` (emulation), `main.swift` | syntax / compile | pass (Mac) | -| fork `cargo test -p kaspa-pow --features igneum-pow` | 14 engine tests including `program_class_v3_seeds_hash_their_own_program_over_their_own_cache` | pass (Mac, fork 79bd8e10) | +| fork `cargo test -p kaspa-pow --features igneum-pow` | 14 engine tests including `program_class_v3_seeds_hash_their_own_program_over_their_own_cache` (rewritten at 89dfcb95 for the era-in-class rule) | pass (Mac, fork 79bd8e10 on the pre-era crate; 89dfcb95 on the era crate: see the final report) | | fork `cargo test -p kaspa-consensus-core` | 108 + 7: the switch rounding, the era clock, the params, digest and fast-time file tests | pass (Mac, fork 79bd8e10) | | PC 2 `build-job.mjs` suites | `kaspa-consensus-core igneum-exec kaspa-pow kaspa-consensus igneum-miner` + `igneum-app` | the coordinator publishes on its go | From 57249583d5a369634c4d09a415547e921a11781c Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:10:16 +0000 Subject: [PATCH 102/158] Consequences ledger round 4: C26 the rebuilt SP1 server's arch list and packaging, C27 checks at publish time; sweep 8 notes; D5 x8 decided Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 9 +++++++++ docs/plans/consequences-decisions.md | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index c7092be5..ccdcbb46 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -49,3 +49,12 @@ Sweep 5 (21:08 Mac clock) notes: the coordinator applied the public proving line | C25 | Ember Tune: the signed manifest carries per-card-model tuning priors (power limit and core clock) that a new card applies and confirms in two steps; K1 signs it | ember-tune.md sections 4 and 5; bench-log Ember Tune entry | every NVIDIA and AMD card on the app; the keys | The manifest now sets clocks and power limits on every user's card, so the signing key's blast radius grew: a signed prior can underclock the fleet or push a card model to its power ceiling. The plan's clamps (inside power.min_limit / max_limit and clocks.max.gr, the confirm step, a faulted step reverted) are the bound; keys.md's "what K1 signs" table (ota-k2 00fcbb5) predates the priors and does not list them | ember-tune.md section 5 states the bound in one line (a prior can never set a value outside the card's own reported limits, and never a memory clock), with the test that proves it; keys.md's table gains the tuning priors under K1 with that bound | Ember Tune a855dcc4bd05e0615; OTA key agent (the table row) | closed (Ember Tune 5d7ced9: the rule as a row in section 5 with the two tests named, a prior of 9,000 MHz at 30% clamps to 3,090 MHz at 50%, a bad prior costs one confirm step per card; the OTA agent has the keys.md note: tuning priors and the kill switch under K1 with that bound) | Sweep 7 (21:49 UTC) notes, no new row: the hot table is measured and NOT adopted (g 0.93 to 0.96 on the Mac against the 0.97 rule, bdab8df); the era draw passes the 5% rule on the Mac (spread 0.8%, c570da3) and its chip line says the union of a program's 16 windows covered the whole dataset in 300 of 300 programs, so a chip mirrors the whole dataset or nothing; the mixer x8 passes the verifier half of its rule (C19) and the PC build rows decide the other half about 22:10; PC 2's miners have been off since a job's /api/resume at 21:25 answered ok without restarting them (the devnet short PC 2's rate, the aggregation-cost mining phases void, a next-cut defect: a resume re-checks the miner processes), all stated by the coordinator at 21:45; PC 1 restarted on 0.3.10 at 21:40:41Z with no measurement straddling it. + +## Round 4 (22:00 to 22:30 UTC) + +| # | Number | Source | Tier affected | Consequence | Action | Owner | State | +|---|---|---|---|---|---|---|---| +| C26 | The 13.9 GB floor's cause: the shipped sp1-gpu-server 6.8.1 panics on any card under 24 GB (builder.rs 35 to 39) and allocates its core, recursion, shrink and wrap provers at Setup; the prover-floor agent rebuilds it from source on PC 2 with those sizes cut, CUDA_ARCHS=120 | proving-v1.md 4c82e56; status 22:01 | 12 and 16 GB NVIDIA cards (RTX 3060, 4070, 5070, 5080, 4060 Ti 16 GB): the tier the project lead asked for; packaging and signing | A server built for CUDA_ARCHS=120 runs on the 5090 only; the 12 GB tier is sm_86 (3060) and sm_89 (4070), the 16 GB tier sm_89 and sm_120, so a cut-size server measured on the 5090 proves nothing about a 3060 until the on-order 3060 runs it, and the build must list sm_86, sm_89, sm_120 (sm_100 is datacentre) to serve the tier at all. Shipping our own 250 MB CUDA server means the project signs and distributes a build of someone else's prover: it enters the DMG and the WSL2 package, the K1-signed inputs, the SBOM-style notes in evidence.md, and every SP1 upgrade is re-done by hand. Cut buffer sizes do not change the verifying key (prover-side chunking), so no guest re-pin, but the recipe must say so with a verify-segment run on a proof from the rebuilt server | The prover-floor measurement states its arch list and the card it ran on; the 12 GB claim waits for the 3060; the rebuilt server's packaging path (who builds, who signs, where it lands) is a row in the proving plan before 0.3.12, and the public line keeps "24 GB" until the 3060 proves on it | prover-floor agent (through the coordinator); proving v1 | sent | +| C27 | The root-socket fault recurred at 21:25Z from another agent's job (agg-cost-pc2-1) after the class fix and CI check landed; PC 2's prover was dark 37 minutes; a resume at 21:25 answered ok without restarting the miners | bench-log 1d78979; status 21:45, 22:03 | every PC job; the devnet's proving and hash rate tonight | The class check lives in CI, but PC jobs are published from worktrees by `publish-jobs.sh` and never pass through CI before they run, so a job written on a branch without the check runs the old shape. The check must run where the job is published, not only where the repo is tested | `packaging/ota/publish-jobs.sh add` runs `tools/ci/prover-socket-check.sh` and the bash-body check on the script it publishes and refuses on a failure; the sub-agent on the bash-body check wires both; the resume defect is on the next-cut list (stated) | sub-agent bash-body-check (the wiring); coordinator (the rule) | sent | + +Sweep 8 (22:09 UTC) notes: mixer x8 DECIDED into v3 on the PC rows (the daily 1 GiB build latency-bound on every card: 5090 23 to 25 ms, 9070 XT 72 to 77 ms at every multiplier; the chip row 0.92x with the 3x factor), so the public claim holds with margin (D5 re-cut); the verifier regression (2.2x) bisected to inlining in the mixer's fetch loop and fixed, so the quiet-core figures of C19 return to about 0.6 / 0.9 / 1.3 ms; G6 job 3 failed on a stale fork test (era inside the class), job 4 on the final tree; the integration merge into master has three known conflicts (bench-log append-only, packfile.h and host.c take the ca2-v3 side). diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index cfa46bad..17b0e14a 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -8,7 +8,7 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | D2 | C3 | The litepaper says "12 GB or more proves full shards"; the only measurement puts the prover alone at 13.8 GB on a 32 GB card | The sweep and the S_p curve are in (proving agent, 5 October late): no knob moves the floor; 12 GB proves nothing on SP1 6.8.1, 16 GB proves only empty shards alone, 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner), 32 GB proves everything. Change the sentence to "24 GB or more proves; 32 GB proves and mines on one card" and evidence row 16 to tested-by-the-team on those rows; the 12 GB figure returns only if a smaller GPU server or a smaller shard measures under 12 GB. The proving agent has drafted the two replacement sentences on its branch (app 440fd59, litepaper and evidence rows 15 and 16); nothing is pushed, so the project lead approves or rewrites the draft rather than starting from the measurement. Two drafts exist: the proving agent's litepaper sentences (proving-v1 440fd59) and the coordinator's site, litepaper and miner-page line (ca2-coord 1c8439f); the integrator keeps one. One marker is owed on either: the 24 GB figure is the 5090's allocation pattern on a 32 GB card (22.2 GB beside the miner), not a measurement on a 24 GB card, and evidence rule 3 wants that said until a 4090 or a 5080-class 24 GB card runs it | A public number that the first 3060 owner disproves is the FUD the ledger exists to prevent | | D3 | C8, C13 | The homepage says "Any 4 GB card" and the litepaper says a 4 GB card mines for about four years and an 8 GB card for more than a decade; the tile says "4B hard cap" while the rule mints about 3.96 billion | Replace with the lifetime table's numbers once the sub-agent lands it: "4 GB cards mine at launch; 8 GB for about six years; 12 GB for about fourteen; the dataset grows half a gigabyte a year" and "cap 4 billion, about 3.96 billion ever minted" | The schedule is public and the arithmetic is one line; a reader will do it | | D4 | C9, C8 | Index mapping at gate 1, now with the lifetime table (`docs/analysis/card-lifetime-2026-10-05.md`): (a) multiply-shift, continuous growth: 4 GB cards out at 1.0 to 1.5 years, 8 GB at 6.3 to 7.5, 12 GB at 12 to 13.5, Apple 8 GB at 2.6 to 3.4; (b) power-of-two steps at years 4, 12, 28, 60: 4 GB to year 4, 8 GB to year 12, 12 GB to year 28 (the cache freed after the build, decided by the coordinator at 22:50), each tier ending on a step day | (b), with the step calendar published on the miners page from day one (the years 4, 12, 28 and 60 named beside the tiers), and the 1 GiB vectors kept. Revised from (a) at 23:0x: the table shows (b) is the only mapping under which the litepaper's "4 GB about four years, 8 GB more than a decade" holds, and a step day known twelve years ahead is a schedule, not an event; the coordinator recommends the same | Under (a) both public sentences are wrong today by 2.5 to 5 years; under (b) they hold and the cliffs are dated | -| D5 | C10 | The site's "under 2x" chip claim: the scratch layer leaves the on-die-cache recompute chip at 2.4x at every share; the mixer multiplier x2 brings it to 1.2x at twice the CPU verify cost (about 0.9 ms per warp, half the pool members per core) | Overtaken by the coordinator's delegated decisions (21:50 to 21:28 Mac clock): the public claim was qualified, the mixer x4 is in class v3 (chip row 0.61x bare, 1.84x with a 3x fixed-function factor, 1.53x at equal silicon with the mirror deducted: "under 2x" holds on the equal-silicon convention and is thin), and x8 is built and measured beside it (0.92x with the factor, from the M16 table); x8 goes in if the verify stays under 10 ms and the daily build under 1 s on every card we own (C23 asks that the iGPU tier be named in that rule). Measured at 21:40 UTC (ca2-mixer 54bbfcc): x8 is 2.1x the v2 verifier (2.79 ms per warp on a loaded core, 1.26 quiet by scaling), 7.1 ms of the 10 ms gate left, the Mac 1 GiB build flat at 21 ms; the verify half of the x8 rule passes, the PC build rows are owed. For the project lead: the public sentence after tonight reads "under 2x at equal silicon, measured against the strongest chip we can model" with the convention named, or it does not read "under 2x" | The claim is the project's first public sentence on chips; it is either true by a measured lever or it is marked | +| D5 | C10 | The site's "under 2x" chip claim: the scratch layer leaves the on-die-cache recompute chip at 2.4x at every share; the mixer multiplier x2 brings it to 1.2x at twice the CPU verify cost (about 0.9 ms per warp, half the pool members per core) | Overtaken by the coordinator's delegated decisions (21:50 to 21:28 Mac clock): the public claim was qualified, the mixer x4 is in class v3 (chip row 0.61x bare, 1.84x with a 3x fixed-function factor, 1.53x at equal silicon with the mirror deducted: "under 2x" holds on the equal-silicon convention and is thin), and x8 is built and measured beside it (0.92x with the factor, from the M16 table); x8 goes in if the verify stays under 10 ms and the daily build under 1 s on every card we own (C23 asks that the iGPU tier be named in that rule). Measured at 21:40 UTC (ca2-mixer 54bbfcc): x8 is 2.1x the v2 verifier (2.79 ms per warp on a loaded core, 1.26 quiet by scaling), 7.1 ms of the 10 ms gate left, the Mac 1 GiB build flat at 21 ms; the verify half of the x8 rule passes, the PC build rows are owed. Then at 22:06 UTC x8 was decided into v3 on the PC build rows (latency-bound on every card): the chip row reads 0.92x with the 3x fixed-function factor, so "under 2x" holds with margin and the level 3 page can state the convention and the margin. For the project lead: confirm "under 2x" stays, now with the measured row behind it | The claim is the project's first public sentence on chips; it is either true by a measured lever or it is marked | | D6 | C11 | Whether class v3 should favour AMD (fewer, wider loads per hash) at a cost to the 5090's latency-bound share, or accept that AMD cards mine at about a seventh of a 5090 and 4.5x the electricity per hash | Accept it for v3 and say so on the miners page ("NVIDIA first; AMD mines at a lower rate per watt on this class"); open the AMD question as a Counter ASIC 3.0 item with its own measurement | Tonight's rule was the project lead's and the 5090 margin is the anti-chip argument; AMD's position is a public-copy question, not a gate | | D7 | C13 | Same as D3's supply wording | with D3 | | | D8 | C7 | The miner page says "One click: install, start, the card mines and proves" (site/miner.html 7, 13, 21; site/index.html 484). On HiveOS and the rig installer a rig mines only until a Linux prover build is published, and on the app a 12 GB card mines only, a 16 GB card proves with the miner paused, 20 GB and up does both (the sweep of 5 October, before the v1-shard row) | Qualify the sentence on the miner page and the homepage card: "the card mines; 24 GB cards prove too, 32 GB does both at once; rigs mine until the Linux prover ships". The same page's "a visible 1% software fee you can switch off" becomes "solo mining carries a 1% software fee you can switch off; in a pool the pool's own fee is the only one" once pool-v0 ships (the pool agent fixed the rule: no software dev fee in pool mode) | The sentence is the product's first promise and tonight's measurement bounds it by card | From 2b1328132411285b9b3d3811753a7f4d03445836 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:10:47 +0000 Subject: [PATCH 103/158] Consequences ledger: C26 with the prover-floor agent, C27 wiring rules Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index ccdcbb46..b8ab4e8a 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -54,7 +54,7 @@ Sweep 7 (21:49 UTC) notes, no new row: the hot table is measured and NOT adopted | # | Number | Source | Tier affected | Consequence | Action | Owner | State | |---|---|---|---|---|---|---|---| -| C26 | The 13.9 GB floor's cause: the shipped sp1-gpu-server 6.8.1 panics on any card under 24 GB (builder.rs 35 to 39) and allocates its core, recursion, shrink and wrap provers at Setup; the prover-floor agent rebuilds it from source on PC 2 with those sizes cut, CUDA_ARCHS=120 | proving-v1.md 4c82e56; status 22:01 | 12 and 16 GB NVIDIA cards (RTX 3060, 4070, 5070, 5080, 4060 Ti 16 GB): the tier the project lead asked for; packaging and signing | A server built for CUDA_ARCHS=120 runs on the 5090 only; the 12 GB tier is sm_86 (3060) and sm_89 (4070), the 16 GB tier sm_89 and sm_120, so a cut-size server measured on the 5090 proves nothing about a 3060 until the on-order 3060 runs it, and the build must list sm_86, sm_89, sm_120 (sm_100 is datacentre) to serve the tier at all. Shipping our own 250 MB CUDA server means the project signs and distributes a build of someone else's prover: it enters the DMG and the WSL2 package, the K1-signed inputs, the SBOM-style notes in evidence.md, and every SP1 upgrade is re-done by hand. Cut buffer sizes do not change the verifying key (prover-side chunking), so no guest re-pin, but the recipe must say so with a verify-segment run on a proof from the rebuilt server | The prover-floor measurement states its arch list and the card it ran on; the 12 GB claim waits for the 3060; the rebuilt server's packaging path (who builds, who signs, where it lands) is a row in the proving plan before 0.3.12, and the public line keeps "24 GB" until the 3060 proves on it | prover-floor agent (through the coordinator); proving v1 | sent | -| C27 | The root-socket fault recurred at 21:25Z from another agent's job (agg-cost-pc2-1) after the class fix and CI check landed; PC 2's prover was dark 37 minutes; a resume at 21:25 answered ok without restarting the miners | bench-log 1d78979; status 21:45, 22:03 | every PC job; the devnet's proving and hash rate tonight | The class check lives in CI, but PC jobs are published from worktrees by `publish-jobs.sh` and never pass through CI before they run, so a job written on a branch without the check runs the old shape. The check must run where the job is published, not only where the repo is tested | `packaging/ota/publish-jobs.sh add` runs `tools/ci/prover-socket-check.sh` and the bash-body check on the script it publishes and refuses on a failure; the sub-agent on the bash-body check wires both; the resume defect is on the next-cut list (stated) | sub-agent bash-body-check (the wiring); coordinator (the rule) | sent | +| C26 | The 13.9 GB floor's cause: the shipped sp1-gpu-server 6.8.1 panics on any card under 24 GB (builder.rs 35 to 39) and allocates its core, recursion, shrink and wrap provers at Setup; the prover-floor agent rebuilds it from source on PC 2 with those sizes cut, CUDA_ARCHS=120 | proving-v1.md 4c82e56; status 22:01 | 12 and 16 GB NVIDIA cards (RTX 3060, 4070, 5070, 5080, 4060 Ti 16 GB): the tier the project lead asked for; packaging and signing | A server built for CUDA_ARCHS=120 runs on the 5090 only; the 12 GB tier is sm_86 (3060) and sm_89 (4070), the 16 GB tier sm_89 and sm_120, so a cut-size server measured on the 5090 proves nothing about a 3060 until the on-order 3060 runs it, and the build must list sm_86, sm_89, sm_120 (sm_100 is datacentre) to serve the tier at all. Shipping our own 250 MB CUDA server means the project signs and distributes a build of someone else's prover: it enters the DMG and the WSL2 package, the K1-signed inputs, the SBOM-style notes in evidence.md, and every SP1 upgrade is re-done by hand. Cut buffer sizes do not change the verifying key (prover-side chunking), so no guest re-pin, but the recipe must say so with a verify-segment run on a proof from the rebuilt server | The prover-floor measurement states its arch list and the card it ran on; the 12 GB claim waits for the 3060; the rebuilt server's packaging path (who builds, who signs, where it lands) is a row in the proving plan before 0.3.12, and the public line keeps "24 GB" until the 3060 proves on it | prover-floor agent (through the coordinator); proving v1 | sent in full to the prover-floor agent abefda4c3872f866f by the coordinator (arch list, the packaging row before 0.3.12, the verify-segment run, "24 GB" kept until the 3060) | +| C27 | The root-socket fault recurred at 21:25Z from another agent's job (agg-cost-pc2-1) after the class fix and CI check landed; PC 2's prover was dark 37 minutes; a resume at 21:25 answered ok without restarting the miners | bench-log 1d78979; status 21:45, 22:03 | every PC job; the devnet's proving and hash rate tonight | The class check lives in CI, but PC jobs are published from worktrees by `publish-jobs.sh` and never pass through CI before they run, so a job written on a branch without the check runs the old shape. The check must run where the job is published, not only where the repo is tested | `packaging/ota/publish-jobs.sh add` runs `tools/ci/prover-socket-check.sh` and the bash-body check on the script it publishes and refuses on a failure; the sub-agent on the bash-body check wires both; the resume defect is on the next-cut list (stated) | sub-agent bash-body-check (the wiring); coordinator (the rule) | in work (sub-agent wiring both checks into publish-jobs.sh add; the coordinator: no collision, jobs without a script pass trivially, an unextractable body fails) | Sweep 8 (22:09 UTC) notes: mixer x8 DECIDED into v3 on the PC rows (the daily 1 GiB build latency-bound on every card: 5090 23 to 25 ms, 9070 XT 72 to 77 ms at every multiplier; the chip row 0.92x with the 3x factor), so the public claim holds with margin (D5 re-cut); the verifier regression (2.2x) bisected to inlining in the mixer's fetch loop and fixed, so the quiet-core figures of C19 return to about 0.6 / 0.9 / 1.3 ms; G6 job 3 failed on a stale fork test (era inside the class), job 4 on the final tree; the integration merge into master has three known conflicts (bench-log append-only, packfile.h and host.c take the ca2-v3 side). From 9597374c4dbd9f54a5681281e597775c6584f5e4 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:10:55 +0000 Subject: [PATCH 104/158] verifier regression of e08909f fixed (derive_items out of line, one instance per cache size with the line mask a constant: v2 0.609 ms per unit against readwidth's 0.607, was 1.33); x8 into class v3 under the delegated rule (V3_CLASS = MX8; mx8-genesis and mx8-devnet-epoch0 re-exported through the seam, the devnet one with the era inside; the mx4 packs kept as the x4 record, generator 2); tests/packs.rs and tests/mixer.rs on the x8 class; mixer-x4.md 6.2a, 6.4a, 6.5 decision, 6.6 the regression; chip-model-v3.md headline x8 (0.92x with the factor); bench-log addendum with the PC 1 build rows Co-Authored-By: Claude Fable 5.1 --- docs/analysis/chip-model-v3.md | 27 +- docs/bench-log.md | 3 + docs/plans/mixer-x4.md | 95 ++++++- igneum-pow/src/generator.rs | 4 +- igneum-pow/src/memhard.rs | 30 +- igneum-pow/tests/mixer.rs | 2 +- igneum-pow/tests/packs.rs | 81 ++++-- .../mx4-devnet-epoch0/kernel.cl | 137 +++++---- .../mx4-devnet-epoch0/kernel.cu | 129 ++++----- .../mx4-devnet-epoch0/kernel_bound.cl | 263 +++++++++--------- .../mx4-devnet-epoch0/kernel_bound.cu | 126 ++++----- .../mx4-devnet-epoch0/memhard.h | 8 +- .../mx4-devnet-epoch0/memhard.metal | 11 +- .../mx4-devnet-epoch0/program.h | 24 +- .../mx4-devnet-epoch0/program.json | 152 +++++----- .../mx4-devnet-epoch0/program.metal | 126 ++++----- .../mx4-devnet-epoch0/program_bound.metal | 126 ++++----- .../mx4-devnet-epoch0/vectors.h | 32 +-- .../mx4-devnet-epoch0/vectors.json | 30 +- .../packs-ca2-mixer/mx4-genesis/program.h | 7 +- .../packs-ca2-mixer/mx4-genesis/program.json | 5 +- .../packs-ca2-mixer/mx4-genesis/vectors.h | 2 +- .../packs-ca2-mixer/mx4-genesis/vectors.json | 2 +- .../mx8-devnet-epoch0/kernel.cl | 137 ++++----- .../mx8-devnet-epoch0/kernel.cu | 129 +++++---- .../mx8-devnet-epoch0/kernel_bound.cl | 263 +++++++++--------- .../mx8-devnet-epoch0/kernel_bound.cu | 126 ++++----- .../mx8-devnet-epoch0/memhard.h | 8 +- .../mx8-devnet-epoch0/memhard.metal | 11 +- .../mx8-devnet-epoch0/program.h | 24 +- .../mx8-devnet-epoch0/program.json | 152 +++++----- .../mx8-devnet-epoch0/program.metal | 126 ++++----- .../mx8-devnet-epoch0/program_bound.metal | 126 ++++----- .../mx8-devnet-epoch0/vectors.h | 32 +-- .../mx8-devnet-epoch0/vectors.json | 30 +- .../packs-ca2-mixer/mx8-genesis/program.h | 7 +- .../packs-ca2-mixer/mx8-genesis/program.json | 5 +- .../packs-ca2-mixer/mx8-genesis/vectors.h | 2 +- .../packs-ca2-mixer/mx8-genesis/vectors.json | 2 +- 39 files changed, 1363 insertions(+), 1239 deletions(-) diff --git a/docs/analysis/chip-model-v3.md b/docs/analysis/chip-model-v3.md index 0510d7ce..1d47b2f7 100644 --- a/docs/analysis/chip-model-v3.md +++ b/docs/analysis/chip-model-v3.md @@ -35,12 +35,12 @@ silicon" = bare x (750 - SRAM) / 750 x 3: the SRAM takes die area the logic does |---|---|---|---|---|---|---|---|---| | v2 as shipped (the M16 and scratch-soundness row) | x1 | 149,760 | 334 MH/s | 256 MiB | 128 / $46 | 2.45x (2.39x against 139.7) | 7.4x | 6.1x | | v2 at w16 (not adopted; the chip's cost is items, not bytes: unchanged) | x1 | 149,760 | 334 | 256 MiB | 128 / $46 | 2.39x against 139.8 | 7.2x | 5.9x | -| v3: mixer x4 | x4 | 599,040 | 83.5 MH/s | 256 MiB | 128 / $46 | 0.61x | 1.84x | 1.53x | +| x4 (the candidate measured beside v3; not v3) | x4 | 599,040 | 83.5 MH/s | 256 MiB | 128 / $46 | 0.61x | 1.84x | 1.53x | | MEASURED, NOT ADOPTED (layer 5 decided out of v3 on the PC rows, coordinator 21:40 UTC): v3 plus a 32 MiB hot table, added form (16 dataset loads and k hot loads): the honest card pays the hot loads, this chip pays SRAM only | x4 | 599,040 (a hot load is one SRAM read, no item) | 83.5 | 288 MiB | 144 / $53 | 0.66x at the Mac's g = 0.93 (126.6 MH/s); 0.70x at the 5090's g = 0.87 (118.4); the 9070 XT's g 0.84 | 1.98x (Mac g), 2.11x (5090 g) | 1.60x, 1.71x | | MEASURED, NOT ADOPTED: v3 plus a 64 MiB hot table, added form | x4 | 599,040 | 83.5 | 320 MiB | 160 / $61 | 0.71x at the Mac's g = 0.87 (118.4 MH/s); 0.73x at the 5090's g = 0.84 (114.3); the 9070 XT's g 0.80 | 2.12x (Mac g), 2.19x (5090 g) | 1.67x, 1.73x | | v3 at year 4 (cache 512 MiB under option C, dataset 4 GiB), no hot table | x4 | 599,040 | 83.5 | 512 MiB | 255 / $111 | 0.61x | 1.84x | 1.21x | | v3 at year 12 (cache 1 GiB, dataset 8 GiB) | x4 | 599,040 | 83.5 | 1 GiB | 510 / $306 | 0.61x | 1.84x | 0.59x | -| v3 with the mixer at x8 (the candidate under the coordinator's rule of 21:30 UTC: in if the verify stays under 10 ms per warp on one Mac core and the daily 1 GiB build under 1 s on every discrete card) | x8 | 1,198,080 | 41.7 | 256 MiB | 128 / $46 | 0.31x | 0.92x | 0.76x | +| **v3: mixer x8** (decided 22:05 UTC under the delegated rule: verify 2.1 ms per unit on one Mac core against the 10 ms gate, the daily 1 GiB build 23 to 77 ms on the 5090 and the 9070 XT) | x8 | 1,198,080 | 41.7 | 256 MiB | 128 / $46 | 0.31x | 0.92x | 0.76x | | x8 at year 4 | x8 | 1,198,080 | 41.7 | 512 MiB | 255 / $111 | 0.31x | 0.92x | 0.61x | The era draws of spec 1.13.1 cost the chip nothing in this model: the mixer round count is not drawn, the op @@ -64,12 +64,13 @@ not a forecast). The combined headline row is the mixer row alone (layer 5 is out: the added form costs the 5090 13 to 16 percent and the 9070 XT 16 to 20 percent against the 0.97 bar, coordinator 21:40 UTC; the width stays 4 bytes; the era -draws and the cache growth cost this chip nothing at year 0). At x4 it reads 1.84x with the 3x factor at an equal -integer budget, 1.53x with the SRAM area deducted; at x8 0.92x and 0.76x. The hot-table rows above are kept as -measured, not adopted: against THIS chip an added hot table is a cost to the honest card and none to the chip, so -it would have moved the row the wrong way by the card's own `g` (2.11x to 2.19x at the 5090's g). The claim is -"under 2x" at x4 on both conventions, with the margin thin on the equal-budget one; at x8 the chip is under 1x on -both: +draws and the cache growth cost this chip nothing at year 0), and class v3 is x8 (decided 22:05 UTC). The headline: +**the on-die-cache recompute chip at 50 T op/s reaches 41.7 MH/s against the 5090's 136.1, 0.31x bare, 0.92x with +the 3x fixed-function factor, 0.76x with the mirror's area deducted: under 1x with the factor, 0.92x, a margin of 8 +percent on the factor (a 3.3x factor reads 1.0x) and of 9 percent on the budget (55 T op/s reads 1.0x).** The x4 +candidate, measured beside it, read 1.84x and 1.53x. The hot-table rows above are kept as measured, not adopted: +against THIS chip an added hot table is a cost to the honest card and none to the chip, so it would have moved the +row the wrong way by the card's own `g`. The margin, plainly: - the 3x fixed-function factor is approximate and from memory; at 3.3x the equal-budget row reads 2.0x; - the denominator is one card's measured rate on one night (136.1 against 139.7 the night before: 2.6% apart); @@ -78,15 +79,15 @@ both: the PC rows), which raises the chip's gain by the same share, 1.84x or more if the hot loads are free, higher if not; the hot table's only cost to this chip is 16 to 32 mm^2 of die. -What keeps it under 2x is the mixer, and nothing else in Counter ASIC 2.0 moves this chip (the scratch at any share +What keeps it under 1x is the mixer, and nothing else in Counter ASIC 2.0 moves this chip (the scratch at any share gave 2.4x, `docs/analysis/scratch-soundness.md` section 3.4; the hot table taxes the DRAM-only chip, not this one; the cache growth taxes it only in die area, which is cheap at year 0 and real at year 12). The next levers, in order: -1. Mixer x8: 0.18x bare and 0.55x with the factor in the M16 table (0.31x and 0.92x against 136.1 here; the M16 - table's denominator is 229 MH/s); the CPU verifier at 3.3 to 9.6 ms per warp scaled from the version 1 range, - at the edge of the 10 ms gate; the measured v3 row of `docs/plans/mixer-x4.md` section 6 is what to scale from - now, and whether a 2019-class laptop core (unmeasured, O-1.14) passes 10 ms is what decides it. +1. Mixer x16 (the next step of the same lever): 0.16x bare and 0.46x with the factor against 136.1; the verifier + by the measured increments (+0.63 ms at x4, +1.46 at x8 on the M5 Max core: about +3.1 ms at x16, 3.7 ms per + unit, 9 ms on a 2.5x slower laptop core, approximate) is at the edge of the 10 ms gate, so a 2019-class laptop + core measurement (O-1.14) decides it, not this model. 2. The hot table: adopted or not on the PC rows (`docs/plans/hot-table.md`); in the added form it costs the GPU 7 to 17 percent on the Mac and the chip die area only, so against this chip it is a lever in the wrong direction and against a DRAM-only chip the first lever; if it is adopted, the mixer must carry the extra `1/g` diff --git a/docs/bench-log.md b/docs/bench-log.md index 8550d873..f18651b3 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -1664,3 +1664,6 @@ Timings, `with-lock.sh measure`, one session 21:40:12 to 21:40:23 UTC, one core, Reading: the mixer multiplies the verifier's ALU part only (the 8 dependent misses per item are unchanged), hence 1.45x and 2.1x and not 4x and 8x; the Mac's GPU build is latency-bound and does not move with the mixer, so the "under 1 s on every discrete card" half of the x8 rule is the PC job (five packs, `relay/playbooks/mixer-x4-pc1-bench.ps1`, waiting for the go). Verification throughput (C19): a quiet 2026 core serves about 1,100 shares per second at x4 and 800 at x8 (1,660 at v2, re-cutting spec 09's 2,270), a 22,000-member pool at one share per 10 s needs 2 cores at x4 and 3 at x8, IBD over 108,000 headers is 1.6 min at x4 and 2.3 at x8 on that core; the 10 ms gate keeps 8.0 ms (x4) and 7.1 ms (x8) of margin on the loaded core, 6 to 7 ms on a 2019-class laptop core (approximate, unmeasured, O-1.14). Chip model (`docs/analysis/chip-model-v3.md`): the on-die-cache recompute chip at 50 T op/s against the 5090's measured 136.1 MH/s: v2 334 MH/s, 2.45x bare, 7.4x with the 3x fixed-function factor; x4 83.5 MH/s, 0.61x bare, 1.84x with the factor, 1.53x with the 128 mm^2 N5 mirror deducted at equal silicon; x8 41.7 MH/s, 0.31x, 0.92x, 0.76x. The claim at x4 is "under 2x" with the margin thin on the equal-budget convention (a 3.3x factor or a 10 percent larger budget reads 2.0x); the hot table in the added form would have raised it to 2.1x to 2.2x at the 5090's g (kept as measured, not adopted). Nothing here is a measurement of a chip. + +**Addendum, 22:15 UTC: the verifier regression, the PC 1 build rows, and x8 into v3.** The era agent measured the same v2 input with readwidth's binary (0.604 ms) and ca2-v3 HEAD's (1.33) in one minute; bisected under the measure lock to this branch's 0fc0ad1 (seam 6c75dad 0.610, 0fc0ad1 1.332; the "loaded box" reading above was wrong by that factor, the load was real but the 2x was the code). Cause: the item loop (`derive_items`) inlined into `MemhardCpu::fetch`; the mask hoisted, the mask constant, and the constant-mask loop inlined all stayed at 1.33, the same loop `#[inline(never)]` read 0.60 to 0.62. Fix: `derive_items_mask`, out of line, one instance per cache size with the line mask a constant. Measured the era agent's way (readwidth's binary beside the fixed one, same input, same minute, 22:07 UTC): v2 0.607 / 0.610 against 0.609 / 0.611; on the fixed binary x4 1.238 / 1.237 (2.0x), x8 2.077 / 2.058 (3.4x), worst cold 2.15 ms; the increments (+0.63, +1.46 ms per unit) equal the slow binary's. Lesson, the class: an inlined item loop costs 2.2x and nothing in the suite sees it; a verifier benchmark with a pinned bound in the crate's CI is filed for the next cut, and until then every change to the item loop is measured against the previous binary on the same input in the same minute. PC 1 (job run-mixer-x4-pc1-20261005, 22:00 to 22:04 UTC, the worker's `cache ... dataset ... ms` wall line): RTX 5090 dataset 23 to 25 ms at v2, x4 and x8; RX 9070 XT (gfx1201) 72 to 77 ms at all three; every fingerprint equal to the Mac's; rates the v2 rate (136.5 to 137.4 and 18.0 to 18.2 MH/s). Decision under the delegated rule (coordinator, 22:05 UTC): x8 enters class v3 (`V3_CLASS = MX8`); pinned packs mx8-genesis (7c28cfb06c5c65a9) and mx8-devnet-epoch0 through the chain path with the era inside (90f794dd556f7a3b, Metal and Apple OpenCL, 22:12 UTC); the x4 packs kept as the candidate's record. Chip headline at x8: 41.7 MH/s, 0.31x bare, 0.92x with the 3x factor, 0.76x at equal silicon (`docs/analysis/chip-model-v3.md`). + diff --git a/docs/plans/mixer-x4.md b/docs/plans/mixer-x4.md index ce91b34d..cc5ae206 100644 --- a/docs/plans/mixer-x4.md +++ b/docs/plans/mixer-x4.md @@ -222,6 +222,18 @@ and 21.7 ms GPU (mx4-devnet-epoch0) for 1 GiB; Apple OpenCL 55 and 56 ms wall. T this card is 0.6 to 2.1 ms cache fill and a 1 GiB build the bench-log's memory-hard entry puts at 13 to 30 ms; the x4 build on the GPU is the row the measure lock will settle. +### 6.2a The packs as pinned after the x8 decision (`with-lock.sh run`, 22:12 UTC, the fixed binary's exports) + +| Pack | How exported | Program id | Unit 0 lane 0 | Fingerprint 2^24 (Metal = Apple OpenCL) | Vectors, self-tests | +|---|---|---|---|---|---| +| mx8-genesis | `--program-class v3` (generator 3, V3_CLASS = mx8) | e323b9dcaf283a6f | 19b56348bc85304d | 7c28cfb06c5c65a9 | 3/3 + 3/3, 96 of 96, PASS | +| mx8-devnet-epoch0 | the chain path, `--program-class v3 --era-hex ` (the era drawn inside the class, load class `mx8-erad810f22d`) | 73bcbfe8ccf988f1 | d424577fce4a7a60 | 90f794dd556f7a3b | 3/3 + 3/3, 96 of 96, PASS | +| mx4-genesis (the x4 record) | `--class mx4` (generator 2, the class in the id) | 951b89584750bd75 | 63acd2d273f475ba | 6f48d5a2aa0dbe5f | 3/3 + 3/3, 96 of 96, PASS | +| mx4-devnet-epoch0 (the x4 record) | `--class mx4` on the chain seeds | (program.json) | 212c6442b51e87ae | 73caaebb28e808fe | 3/3 + 3/3, 96 of 96, PASS | + +The PC 1 rows of 6.5 ran the earlier mx8-devnet-epoch0 (the load-class export, fingerprint bbb183f72692f840, era +not inside); the composed pack's PC fingerprints are owed with the next PC round (section 9). + ### 6.3 Soundness suite on the v3 construction (commit 66eeba3 and after; `with-lock.sh build` for cargo, `run` for the GPU) | Suite | Command | Result | @@ -292,6 +304,26 @@ two cores); at x8 about 800 (three cores). A node's block verification stays a f remaining margin is what Counter ASIC 3.0 has to spend, and on the unmeasured laptop core it is 6 to 7 ms at x4 and about 6 at x8, which is the number the 2019-class measurement (O-1.14) must confirm before x8 is final. +### 6.4a The same session on the fixed binary (section 6.6), `with-lock.sh measure`, 22:06:59 to 22:07:06 UTC + +The verifier of section 6.4 was measured on a binary that carried the inlining regression of section 6.6; after the +fix, readwidth's binary and the fixed one on the same v2 input in the same minute (checksum 19297e99c7b9a55e), then +x4 and x8 on the fixed binary; load average 5.5 (the same box, so the ratios of 6.4 stand and the absolute row is +now the measured one): + +| Construction | Verifier, ms per unit, avg of 50 (round 1 / round 2) | Worst cold unit of three | Against v2 | +|---|---|---|---| +| v2, readwidth e752fc7's binary | 0.607 / 0.610 | 0.666 | 1 | +| v2, the fixed binary | 0.609 / 0.611 | 0.657 | 1.00 | +| x4 (mx4) | 1.238 / 1.237 | 1.396 | 2.03x | +| x8 (mx8, class v3) | 2.077 / 2.058 | 2.145 | 3.4x | +| x4, the devnet seeds | 1.240 | 1.289 | | +| x8, the devnet seeds | 2.058 | 2.144 | | + +The added cost per unit is the same as on the slow binary (x4 + 0.63 ms, x8 + 1.46 ms: the regression was a +constant 0.72 ms per unit in the shared item loop), so the ALU reading of 6.4 holds; the ratios against v2 are +2.0x and 3.4x once v2 is back at 0.61. The 10 ms gate keeps 7.9 ms at x8 (worst cold 2.15 ms) on this core. + ### 6.5 The daily build per tier, and the x4 / x8 rule The coordinator's rule (21:30 UTC): x8 enters v3 if the per-warp verify stays under 10 ms on one Mac core AND the @@ -301,34 +333,68 @@ per day, so its consequence is per-day dataset reuse in the workers or a restart | Card | Build at x1 | x4 | x8 | Source | |---|---|---|---|---| -| RTX 5090 (PC 2) | 13.4 ms (1 GiB) | owed (PC job, section 8) | owed | bench-log, 3 October, memory-hard entry | -| RX 9070 XT (PC 1, eGPU) | owed | owed | owed | PC job, section 8 | +| RTX 5090 (PC 1, job run-mixer-x4-pc1-20261005, 22:00 to 22:04 UTC, the worker's `cache ... dataset ... ms` wall line, two dispatches per pack) | 23 to 25 ms | 23 to 25 ms | 23 ms | the PC 1 job (13.4 ms GPU time on 3 October: the wall line carries the launch) | +| RX 9070 XT (PC 1, gfx1201 on the eGPU, the same job) | 74 ms | 73 to 77 ms | 72 to 76 ms | the PC 1 job | | M5 Max, Metal | 13 to 30 ms (the two runs of the memory-hard entry; tonight's run-lock figures 21.7 to 30.2 ms at x4 and 22.0 to 30.0 at x8 say the Mac's build is latency-bound, not mixer-bound) | section 6.4 | section 6.4 | this file | | Radeon integrated gfx1036 (PC 2), OpenCL, per prepare | 6.9 / 9.4 / 11.7 s prepare total with the 1 GiB build inside | about 28 to 47 s (approximate: scaled x4; the iGPU's build is arithmetic-bound at x1 already) | about 55 to 94 s (approximate) | `docs/plans/epoch-length.md` section 7 (branch ca2-epoch), M11 table | | gfx1036 beside WSL build jobs (PC 1) | 55 / 116 / 124 s | about 4 to 8 min (approximate) | about 7 to 17 min (approximate) | same | | 8 GB-class discrete card (not owned; about a tenth of the 5090's rate, approximate) | about 0.13 s | about 0.5 s | about 1 s, on the edge of the rule | scaled from the 5090 row, approximate | -Reading, before the measured rows: on the discrete cards the rule is about the 5090 and 9070 XT rows (owed, the PC -job). The integrated tier misses the rule at x4 already: a per-prepare build of 28 to 47 s is a tenth to a quarter +Decision (coordinator under the delegated rule, 22:05 UTC, on these rows): x8 enters class v3. Both halves pass: +the verifier at x8 is 2.1 ms per unit on one M5 Max core (6.4a) against the 10 ms gate, and the daily 1 GiB build +does not move with the mixer on any discrete card we own (5090 23 to 25 ms, 9070 XT 72 to 77 ms, M5 Max 21 ms at +x1, x4 and x8: latency-bound), 13x to 40x under the 1 s bar. `V3_CLASS = { era: None, hot: None, ..LoadClass::MX8 }`; +the pinned v3 packs are mx8-genesis and mx8-devnet-epoch0 (the latter through the chain path with the era inside the +class); the x4 packs stay pinned as the candidate's record (generator 2, the class in the id). + +Reading of the integrated tier: on the discrete cards the rule is settled by the 5090 and 9070 XT rows above. The integrated tier misses the rule at x4 already: a per-prepare build of 28 to 47 s is a tenth to a quarter of the 600-DAA-second lead the devnet gives the next program (spec 1.12), and under load it is the whole lead; so if x4 or x8 goes in, the iGPU tier needs the workers to build the day's dataset once a day and keep it across epochs (today a prepare rebuilds it: `proto-opencl/host.c` prepareTask builds the pair's cache and dataset per prepare), or to restart per epoch. The node agent is asked whether the per-day reuse is bounded tonight (coordinator, 21:45 UTC); until then the iGPU consequence stands as written. +### 6.6 The verifier regression of 0fc0ad1, found and fixed (5 October 2026, 21:59 to 22:07 UTC) + +The era agent measured the same v2 input with two binaries in one minute: readwidth's 0.604 ms per unit, ca2-v3 +HEAD's 1.33. Bisected under the measure lock (one session, four binaries, two rounds, checksum 19297e99c7b9a55e): +readwidth e752fc7 0.607 / 0.609; the ca2-v3 seam 6c75dad (before this branch) 0.610 / 0.609; this branch's 0fc0ad1 +1.332 / 1.316; ca2-v3 88dafbc 1.325 / 1.347. So the 2.2x was in 0fc0ad1's `derive_items`, on the version 2 path the +devnet verifies with, and section 6.4's "loaded box" reading was wrong: the load was real (the same session shows +it) but the 2x was the code. + +Variants, each a one-change copy measured against readwidth's binary in the same session: + +| Variant | ms per unit | Reading | +|---|---|---| +| 0fc0ad1 as written (the line mask read from the cache at run time, the loop inlined into `MemhardCpu::fetch`) | 1.33 | the regression | +| the mask hoisted into a local before the item loop | 1.32 to 1.37 | not the reload | +| `Cache::line` with the constant mask, on the 0fc0ad1 tree | 0.60 to 0.65 | fixed there | +| the same constant mask on the merged ca2-v3 tree | 1.33 to 1.46 | not the mask either | +| one instance per cache size with the mask a constant, `#[inline(always)]` | 1.32 to 1.39 | not the mask | +| the same instances `#[inline(never)]` | 0.604 / 0.617 / 0.618 / 0.624 | the fix | + +So it is inlining: the item loop inlined into its callers (`fetch`, `fetch_wide`, `word_at`) runs at 2.2x the +out-of-line loop, and which small change tips LLVM's decision depends on the rest of the tree (the constant mask +tipped it on one tree and not on the other). The fix (`memhard::derive_items`): the loop is `derive_items_mask`, +`#[inline(never)]`, one instance per cache size the growth rule reaches (2^26 to 2^30 words) with the line mask a +constant, and a run-time-mask instance for every other size (tests). Measured in 6.4a: 0.609 / 0.611 against +readwidth's 0.607 / 0.610. + +The class, not the instance: a verifier benchmark with a pinned bound in the crate's CI (the v2 unit at a known +input against a stored ms-per-unit on a named core, failing on a 1.3x drift) would have caught this at the first +commit; filed for the next cut (section 9). Until then the era agent's two-binary check (same input, same minute) +is the rule for every change that touches the item loop. + ## 7. The chip model `docs/analysis/chip-model-v3.md`. ## 8. What is unverified -1. The 5090's and the 9070 XT's dataset build at x4 and x8 (the "under 1 s on every discrete card" half of the x8 - rule): the PC 1 job (`relay/playbooks/mixer-x4-pc1-bench.ps1`, package `mixer-x4-pcjob.zip` sha256 - ec3be97e...bdf87, five packs, the worker's `cache ... dataset ... ms` line per pack) waits for the coordinator's - go after the era job; by the M16 arithmetic the 5090 is 54 ms at x4 and 107 ms at x8 if its build is - arithmetic-bound and 13.4 ms if it is latency-bound like the Mac, both far under 1 s; the gfx1036 is the tier - that fails (section 6.5 of the build table), and its consequence (per-day dataset reuse in the workers) is with - the node agent. +1. The 5090's and the 9070 XT's dataset build at x4 and x8 are measured (6.5, the PC 1 job): both latency-bound, + under 0.1 s. The gfx1036 is the tier that fails the per-prepare build (6.5), and its consequence (per-day dataset + reuse in the workers) is with the node agent. 2. The absolute verifier figures were taken on a loaded core (load average 5.6); the ratios are the measurement and the quiet-core figures are scaled. A 2019-class laptop core has not run any construction (O-1.14). 3. The mixer has had no cryptanalysis (spec 1.8.4); `m` applications with distinct round keys is `m` times the @@ -343,9 +409,10 @@ prepare), or to restart per epoch. The node agent is asked whether the per-day r | Item | Owner | When | |---|---|---| -| PC 1 run of the five packs (the build-time rows, the v3 fingerprints on NVIDIA and AMD) | ca2-mixer, on the coordinator's go | after the era job, about 22:05 UTC | -| Quiet-box re-run of the verifier session for absolute numbers | ca2-mixer | when the Mac is quiet | -| The x4 / x8 choice recorded from the rule, then the vectors re-cut once through the seam | coordinator, then ca2-mixer | after the PC rows | +| PC 1 run of the five packs | done 22:04 UTC (run-mixer-x4-pc1-20261005; 6.5 and 6.2a) | | +| A verifier benchmark with a pinned bound in the crate's CI (section 6.6, the class rule) | ca2-mixer | the next cut | +| GPU bit-exactness of the re-exported mx8-devnet-epoch0 (the composed class, the era inside) and the mx4 record packs on the PCs (the Mac rows are in 6.2a) | ca2-mixer | the next PC round | +| The x4 / x8 choice recorded from the rule, then the vectors re-cut once through the seam | done 22:05 UTC: x8 (6.5), V3_CLASS = MX8, mx8 packs re-exported | | | `Epoch::chain_dataset_day` wired to the genesis day index in the node (`days_since_genesis(day_index(header), day_index(genesis))`) and `pow_genesis_dataset_log2` in the override | ca2-node | the integration | | The spec text of section 2 into `docs/spec/01-lottery-hash.md` 1.8.5 and 1.13.3 (with the v3 vectors into 1.17) | the integration | after the choice | | The 2019-class laptop core measurement that fixes the gate (O-1.14) | cryptographer | gate 1 | diff --git a/igneum-pow/src/generator.rs b/igneum-pow/src/generator.rs index 1ca1d894..5201d950 100644 --- a/igneum-pow/src/generator.rs +++ b/igneum-pow/src/generator.rs @@ -404,7 +404,7 @@ impl LoadClass { /// the per-warp verify stays under 10 ms on one Mac core and the daily 1 GiB build under 1 s on every card): /// the same loads and growth rule, the mixer applied 8 times per round. Name "mx8". pub const MX8: LoadClass = - LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None, scratch_kb: 0, mixer_mult: 8, growth: true, era: None, hot: None }; + LoadClass { mixer_mult: 8, ..LoadClass::MX4 }; /// A fixed width (1, 4 or 16 words) with `load_slots` loads per program. pub fn fixed(width_words: u8, load_slots: u8) -> LoadClass { @@ -701,7 +701,7 @@ pub enum ProgramClass { /// placeholder of the seam (w16) is replaced here; nothing else in the seam names the class. /// Composed on 5 October 2026 (branch ca2-era): the era layout of `docs/plans/era-layout.md` is drawn inside this class by /// [`generate_from_seed_bytes_program_class`] (`LoadClass::era(V3_CLASS, era, &V3_ALLOWED)`); here `era` is `None`. -pub const V3_CLASS: LoadClass = LoadClass { era: None, hot: None, ..LoadClass::MX4 }; +pub const V3_CLASS: LoadClass = LoadClass { era: None, hot: None, ..LoadClass::MX8 }; /// The width set class v3's era draw chooses from: 4 bytes only (the read-width decision of 5 October 2026; the /// draw is consumed, so widening the set at genesis keeps the derivation). diff --git a/igneum-pow/src/memhard.rs b/igneum-pow/src/memhard.rs index 5a0ecd8a..4458ab86 100644 --- a/igneum-pow/src/memhard.rs +++ b/igneum-pow/src/memhard.rs @@ -384,6 +384,13 @@ impl Cache { &self.words[o..o + 16] } + /// [`Cache::line`] with the mask as a constant (the verifier's hot path, see [`derive_items`]). + #[inline(always)] + pub fn line_const(&self, a: u32) -> &[u32] { + let o = (a & LINE_MASK) as usize * 16; + &self.words[o..o + 16] + } + /// FNV-1a 64 over the cache as little-endian bytes (what `vectors.h` carries as `IGNEUM_CACHE_FNV64`). pub fn fnv1a64(&self) -> u64 { fnv1a64_words(&self.words) @@ -474,8 +481,29 @@ impl HotTable { /// (`mp.shape.mixer_mult`) round `r` applies `M` with keys `round_key(r m + j)` for `j = 0 .. m - 1` before its /// one cache read; the final mixer applies `M` with keys `round_key(8 m + j)`. `m = 1` is version 2. pub fn derive_items(ts: &[u32], mp: &MixParams, cache: &Cache, out: &mut [[u32; 16]]) { + // The item loop lives in its own function, one instance per cache size the growth rule can reach with the line + // mask a constant, never inlined into the callers. Inlined into `MemhardCpu::fetch` it ran at 1.33 ms per unit + // against 0.61 out of line (the version 2 verifier, bisected on one core under the measure lock, 5 October 2026, + // `docs/plans/mixer-x4.md` section 6.6: the constant mask alone, or the mask hoisted into a local, or the + // constant with the loop still inlined, all stayed at 1.33; the out-of-line instances read 0.60 to 0.62). Any + // other cache size (tests) takes the instance with the run-time mask. + match cache.log2_words { + 26 => derive_items_mask::<{ (1u32 << 22) - 1 }>(ts, mp, cache, out), + 27 => derive_items_mask::<{ (1u32 << 23) - 1 }>(ts, mp, cache, out), + 28 => derive_items_mask::<{ (1u32 << 24) - 1 }>(ts, mp, cache, out), + 29 => derive_items_mask::<{ (1u32 << 25) - 1 }>(ts, mp, cache, out), + 30 => derive_items_mask::<{ (1u32 << 26) - 1 }>(ts, mp, cache, out), + _ => derive_items_mask::<0>(ts, mp, cache, out), + } +} + +/// [`derive_items`] with the cache line mask as a constant (`LINE_MASK = 0`: the cache's own run-time mask). Kept +/// out of line on purpose (see [`derive_items`]). +#[inline(never)] +fn derive_items_mask(ts: &[u32], mp: &MixParams, cache: &Cache, out: &mut [[u32; 16]]) { let n = ts.len(); debug_assert!(out.len() >= n); + debug_assert!(LINE_MASK == 0 || LINE_MASK == cache.line_mask); let m = mp.shape.mixer_mult as usize; for k in 0..n { let s = &mut out[k]; @@ -493,7 +521,7 @@ pub fn derive_items(ts: &[u32], mp: &MixParams, cache: &Cache, out: &mut [[u32; } } for s in out[..n].iter_mut() { - let line = cache.line(s[0]); + let line = if LINE_MASK != 0 { cache.line_const::(s[0]) } else { cache.line(s[0]) }; for i in 0..16 { s[i] ^= line[i]; } diff --git a/igneum-pow/tests/mixer.rs b/igneum-pow/tests/mixer.rs index 78de4768..e3f090b8 100644 --- a/igneum-pow/tests/mixer.rs +++ b/igneum-pow/tests/mixer.rs @@ -236,7 +236,7 @@ fn determinism_v3() { for (w, warp) in [(0u32, 0usize), (4096, 1), (1_000_000, 2)] { assert_eq!(a.hash_warp(w), pa.outs[warp]); } - let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda/packs-ca2-mixer/mx4-genesis"); + let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda/packs-ca2-mixer/mx8-genesis"); for (name, text) in &pa.files { if name == "vectors.json" || name == "vectors.h" { continue; // the source string differs ("a" here) diff --git a/igneum-pow/tests/packs.rs b/igneum-pow/tests/packs.rs index f03cf643..d461bae4 100644 --- a/igneum-pow/tests/packs.rs +++ b/igneum-pow/tests/packs.rs @@ -5,10 +5,11 @@ //! //! Packs: igneum-genesis-mh and igneum-devnet-v4-epoch0 (memory-hard; the latter from the devnet genesis hash as //! the epoch seed and the day bytes of 2026-10-04), igneum-genesis and igneum-hourly (closed-form dataset, -//! interpreter regression only); and, under `proto-cuda/packs-ca2-mixer/`, the class v3 packs mx4-genesis and -//! mx4-devnet-epoch0 (Counter ASIC 2.0, 5 October 2026: generator 3 on `V3_CLASS` = mixer x4 with the cache growth -//! rule, the same seeds and days as the two memory-hard v2 packs, so the v2 program and cache carry over and only -//! the dataset words and the hashes change). +//! interpreter regression only); and, under `proto-cuda/packs-ca2-mixer/`, the class v3 packs mx8-genesis and +//! mx8-devnet-epoch0 (Counter ASIC 2.0, 5 October 2026: generator 3 on `V3_CLASS` = mixer x8 with the cache growth +//! rule, decided 22:05 UTC under the delegated rule; the same seeds and days as the two memory-hard v2 packs, so the +//! v2 program and cache carry over and only the dataset words and the hashes change) and the x4 candidate's packs +//! mx4-genesis and mx4-devnet-epoch0 (generator 2 with the load class in the id, the record of the x4 rows). use igneum_pow::accept; use igneum_pow::emit::{ @@ -22,10 +23,18 @@ use serde_json::Value; use std::path::PathBuf; use std::sync::OnceLock; -const PACKS: [&str; 6] = - ["igneum-genesis-mh", "igneum-devnet-v4-epoch0", "igneum-genesis", "igneum-hourly", "mx4-genesis", "mx4-devnet-epoch0"]; -/// The class v3 packs (the last two of `PACKS`). -const PACKS_V3: [&str; 2] = ["mx4-genesis", "mx4-devnet-epoch0"]; +const PACKS: [&str; 8] = [ + "igneum-genesis-mh", + "igneum-devnet-v4-epoch0", + "igneum-genesis", + "igneum-hourly", + "mx8-genesis", + "mx8-devnet-epoch0", + "mx4-genesis", + "mx4-devnet-epoch0", +]; +/// The class v3 packs (generator 3 through the seam). +const PACKS_V3: [&str; 2] = ["mx8-genesis", "mx8-devnet-epoch0"]; fn packs_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda/packs") @@ -33,7 +42,7 @@ fn packs_dir() -> PathBuf { /// The directory a pack lives in: the class v3 packs under packs-ca2-mixer, the rest under packs. fn pack_dir(pack: &str) -> PathBuf { - if pack.starts_with("mx4-") { + if pack.starts_with("mx4-") || pack.starts_with("mx8-") { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda/packs-ca2-mixer").join(pack) } else { packs_dir().join(pack) @@ -84,7 +93,11 @@ fn epoch(pack: &str) -> &'static Epoch { let era = j.get("era_seed_bytes").map(unhex); generate_from_seed_bytes_program_class(seed, &seed_bytes, ProgramClass::V3, era.as_deref()) } - _ => generate_from_seed_bytes(seed, &seed_bytes), + // a generator 2 pack with a load class (the x4 candidate's packs): the class from program.json + _ => match j.get("load_class").and_then(|c| c.as_str()) { + Some(c) => generate_from_seed_bytes_class(seed, &seed_bytes, LoadClass::parse(c).expect("a load class name")), + None => generate_from_seed_bytes(seed, &seed_bytes), + }, }; let shape = Shape::for_class(&program.class); let mut dataset = @@ -154,7 +167,7 @@ fn genesis_program_shape() { #[test] fn mixer_params_match_pack() { - for pack in ["igneum-genesis-mh", "igneum-devnet-v4-epoch0", "mx4-genesis", "mx4-devnet-epoch0"] { + for pack in ["igneum-genesis-mh", "igneum-devnet-v4-epoch0", "mx8-genesis", "mx8-devnet-epoch0", "mx4-genesis", "mx4-devnet-epoch0"] { let j = json(pack, "program.json"); let mp = &epoch(pack).dataset.memhard().unwrap().params; let key: Vec = j["dataset"]["key"].as_array().unwrap().iter().map(hex32).collect(); @@ -318,24 +331,25 @@ fn emitted_sources_match_all_packs() { } /// The whole pack as `export` writes it: vectors.json and vectors.h match, and the file list is the full set. -/// The class v3 packs (Counter ASIC 2.0, `docs/plans/mixer-x4.md`): generator 3 on V3_CLASS = mx4; the program of +/// The class v3 packs (Counter ASIC 2.0, `docs/plans/mixer-x4.md`): generator 3 on V3_CLASS = mx8; the program of /// each is the v2 program of the same seed instruction for instruction (v2 loads take no width roll); the cache is /// the v2 cache (day 0 of the growth rule: 2^26 words, the same FNV-1a 64); the dataset words differ from v2's; /// program.json, program.h and the emitted memhard core say so; the id carries generator 3. #[test] -fn v3_packs_are_the_v2_seeds_under_mixer_x4() { - assert_eq!(V3_CLASS.name(), "mx4"); - assert_eq!(V3_CLASS.mixer_mult, 4); +fn v3_packs_are_the_v2_seeds_under_mixer_x8() { + assert_eq!(V3_CLASS.name(), "mx8"); + assert_eq!(V3_CLASS.mixer_mult, 8); assert!(V3_CLASS.growth); - for (v3, v2) in [("mx4-genesis", "igneum-genesis-mh"), ("mx4-devnet-epoch0", "igneum-devnet-v4-epoch0")] { + assert_eq!(LoadClass { mixer_mult: 4, ..V3_CLASS }, LoadClass::MX4, "the x4 candidate differs from v3 in the multiplier alone"); + for (v3, v2) in [("mx8-genesis", "igneum-genesis-mh"), ("mx8-devnet-epoch0", "igneum-devnet-v4-epoch0")] { let e3 = epoch(v3); let e2 = epoch(v2); let j = json(v3, "program.json"); assert_eq!(j["program_class"].as_str().unwrap(), "v3"); - assert!(j["load_class"].as_str().unwrap().starts_with("mx4"), "{v3}: mx4, or mx4 with the era inside"); - assert_eq!(j["mixer_mult"].as_u64().unwrap(), 4); + assert!(j["load_class"].as_str().unwrap().starts_with("mx8"), "{v3}: mx8, or mx8 with the era inside"); + assert_eq!(j["mixer_mult"].as_u64().unwrap(), 8); assert_eq!(j["cache_growth"].as_bool().unwrap(), true); - assert_eq!(j["dataset"]["mixer_mult"].as_u64().unwrap(), 4); + assert_eq!(j["dataset"]["mixer_mult"].as_u64().unwrap(), 8); assert_eq!(j["dataset"]["cache"]["log2_words"].as_u64().unwrap(), 26); assert_eq!(e3.program.generator, GENERATOR_VERSION_V3); if e3.program.era_bytes.is_some() { @@ -355,7 +369,7 @@ fn v3_packs_are_the_v2_seeds_under_mixer_x4() { assert_eq!(e3.program.program_id(), igneum_pow::generator::program_id(GENERATOR_VERSION_V3, &e3.program.seed, e3.program.attempt)); let m3 = e3.dataset.memhard().unwrap(); let m2 = e2.dataset.memhard().unwrap(); - assert_eq!(m3.shape(), Shape { mixer_mult: 4, cache_log2_words: 26 }); + assert_eq!(m3.shape(), Shape { mixer_mult: 8, cache_log2_words: 26 }); assert_eq!(m3.cache.fnv1a64(), m2.cache.fnv1a64(), "{v3}: the same cache as v2 on day 0"); assert_eq!(m3.params.rot, m2.params.rot); assert_eq!(e3.dataset.log2_words, 28); @@ -364,25 +378,36 @@ fn v3_packs_are_the_v2_seeds_under_mixer_x4() { let h = read(v3, "program.h"); assert!(h.contains("#define IGNEUM_GENERATOR 3\n")); assert!(h.contains("#define IGNEUM_PROGRAM_CLASS \"v3\"\n")); - assert!(h.contains("#define IGNEUM_MIXER_MULT 4")); + assert!(h.contains("#define IGNEUM_MIXER_MULT 8")); assert!(h.contains("#define IGNEUM_CACHE_GROWTH 1")); assert!(h.contains("#define IGNEUM_CACHE_LOG2_WORDS 26\n")); - assert!(h.contains("#define IGNEUM_LOAD_CLASS \"mx4"), "mx4, or mx4 with the era inside"); + assert!(h.contains("#define IGNEUM_LOAD_CLASS \"mx8"), "mx8, or mx8 with the era inside"); for file in ["memhard.h", "memhard.metal", "kernel.cl"] { let text = read(v3, file); - assert_eq!(text.matches("j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 4u + j + 1u))").count(), 1, "{v3}/{file}"); - assert_eq!(text.matches("j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u))").count(), 1, "{v3}/{file}"); + assert_eq!(text.matches("j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 8u + j + 1u))").count(), 1, "{v3}/{file}"); + assert_eq!(text.matches("j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u))").count(), 1, "{v3}/{file}"); } for file in ["memhard.h", "memhard.metal", "kernel.cl"] { let text = read(v2, file); - assert_eq!(text.matches("j < 4u").count(), 0, "{v2}/{file}: the v2 text has no multiplier loop"); + assert_eq!(text.matches("j < 8u").count(), 0, "{v2}/{file}: the v2 text has no multiplier loop"); } } // the devnet v3 pack records era 0's stand-in, the devnet genesis hash - let j = json("mx4-devnet-epoch0", "program.json"); + let j = json("mx8-devnet-epoch0", "program.json"); assert_eq!(unhex(&j["era_seed_bytes"]), unhex(&j["seed_bytes"])); - assert!(read("mx4-devnet-epoch0", "program.h").contains("#define IGNEUM_ERA_SEED_HEX \"edc4fa844da9dc98")); - assert!(json("mx4-genesis", "program.json").get("era_seed_bytes").is_none()); + assert!(read("mx8-devnet-epoch0", "program.h").contains("#define IGNEUM_ERA_SEED_HEX \"edc4fa844da9dc98")); + assert!(json("mx8-genesis", "program.json").get("era_seed_bytes").is_none()); + // the x4 candidate's packs: generator 2, the class in the id, the v2 program of the seed, mixer x4 + for (x4, v2) in [("mx4-genesis", "igneum-genesis-mh"), ("mx4-devnet-epoch0", "igneum-devnet-v4-epoch0")] { + let e4 = epoch(x4); + let j = json(x4, "program.json"); + assert_eq!(e4.program.generator, GENERATOR_VERSION); + assert_eq!(j["load_class"].as_str().unwrap(), "mx4"); + assert_eq!(e4.program.class, LoadClass::MX4); + assert_eq!(e4.program.instrs, epoch(v2).program.instrs); + assert_eq!(e4.dataset.memhard().unwrap().shape(), Shape { mixer_mult: 4, cache_log2_words: 26 }); + assert!(read(x4, "memhard.h").contains("j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (r * 4u + j + 1u))")); + } } fn check_export(pack: &str) { diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cl b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cl index 68d88081..a32be499 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cl +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cl @@ -155,12 +155,8 @@ static inline void mh_item(__global const uint* cache, uint t, uint* s) { } for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); } -// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 12 13 of w. -static inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 12u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } -static inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x00000fffu) | ((w >> 13u) << 12u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } -static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 12u) << 13u) | (w & 0x00000fffu) | (((j >> 2u) & 1u) << 12u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } -// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). -static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } // Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. // The same constants as memhard.h in this pack (one emitter, three dialects). @@ -173,7 +169,8 @@ __kernel void igneum_build(__global uint* ds, __global const uint* cache, uint n if (t < nItems) { uint s[16]; mh_item(cache, t, s); - for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; } } @@ -203,69 +200,69 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - r7 = r7 ^ r0; // 1 xor - { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl - r4 = r4 - r1; // 3 sub - r2 = r0 * r4 + r2; // 4 mad - r4 = rotl_imm(r4, 9u); // 5 rotl - r0 = rotr_var(r0, r2); // 6 rotr - r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load - r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load - r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load - r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load - r5 = r5 * r4; // 12 mul - r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load - { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl - { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl - r2 = r2 | r7; // 16 or - r0 = rotr_var(r0, r6); // 17 rotr - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add - r7 = rotl_imm(r7, 24u); // 19 rotl - r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add - r2 = r2 | r6; // 21 or - r6 = r5 * r4 + r6; // 22 mad - r1 = r1 | r0; // 23 or - r6 = r6 ^ r1; // 24 xor - r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add - r7 = rotr_var(r7, r0); // 26 rotr - r4 = r5 * r7 + r4; // 27 mad - r3 = r2 * r4 + r3; // 28 mad - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load - r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load - r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load - r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add - r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add - r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add - r2 = mul_hi(r2, r5); // 35 mulhi - r4 = r4 ^ r2; // 36 xor - r6 = r6 * r5; // 37 mul - r7 = r7 ^ r0; // 38 xor - r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add - r2 = rotr_var(r2, r3); // 40 rotr - r7 = r7 - r0; // 41 sub - r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add - { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl - r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add - r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load - r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load - r4 = mul_hi(r4, r2); // 48 mulhi - r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl - r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add - r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load - r6 = rotl_imm(r6, 12u); // 53 rotl - r3 = rotl_imm(r3, 12u); // 54 rotl - r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load - r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add - r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load - r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add - r3 = mul_hi(r3, r2); // 60 mulhi - r6 = r6 | r4; // 61 or - { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl - r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = mul_hi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = mul_hi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = mul_hi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = mul_hi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = mul_hi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = mul_hi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = mul_hi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = mul_hi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = mul_hi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cu b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cu index 2a042276..c129edee 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cu +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel.cu @@ -36,7 +36,8 @@ __global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItem if (t < nItems) { uint32_t s[16]; mh_item(cache, t, s); - for (uint32_t i = 0u; i < 16u; ++i) ds[(size_t)mh_addr(t, i)] = s[i]; + uint32_t* d = ds + (size_t)t * 16u; + for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; } } @@ -59,69 +60,69 @@ __global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonc for (uint32_t it = 0u; it < 8u; ++it) { uint32_t sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - r7 = r7 ^ r0; // 1 xor - r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl - r4 = r4 - r1; // 3 sub - r2 = r0 * r4 + r2; // 4 mad - r4 = rotl_imm(r4, 9u); // 5 rotl - r0 = rotr_var(r0, r2); // 6 rotr - r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load - r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load - r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load - r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load - r5 = r5 * r4; // 12 mul - r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load - r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl - r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl - r2 = r2 | r7; // 16 or - r0 = rotr_var(r0, r6); // 17 rotr - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add - r7 = rotl_imm(r7, 24u); // 19 rotl - r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add - r2 = r2 | r6; // 21 or - r6 = r5 * r4 + r6; // 22 mad - r1 = r1 | r0; // 23 or - r6 = r6 ^ r1; // 24 xor - r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add - r7 = rotr_var(r7, r0); // 26 rotr - r4 = r5 * r7 + r4; // 27 mad - r3 = r2 * r4 + r3; // 28 mad - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load - r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load - r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load - r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add - r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add - r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add - r2 = __umulhi(r2, r5); // 35 mulhi - r4 = r4 ^ r2; // 36 xor - r6 = r6 * r5; // 37 mul - r7 = r7 ^ r0; // 38 xor - r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add - r2 = rotr_var(r2, r3); // 40 rotr - r7 = r7 - r0; // 41 sub - r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add - r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl - r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add - r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load - r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load - r4 = __umulhi(r4, r2); // 48 mulhi - r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add - r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl - r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add - r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load - r6 = rotl_imm(r6, 12u); // 53 rotl - r3 = rotl_imm(r3, 12u); // 54 rotl - r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load - r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add - r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load - r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add - r3 = __umulhi(r3, r2); // 60 mulhi - r6 = r6 | r4; // 61 or - r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl - r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 4); // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = __umulhi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = __umulhi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = __umulhi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = __umulhi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = __umulhi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = __umulhi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r7, 8); // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = __umulhi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = __umulhi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = __umulhi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load } uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cl b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cl index bc66d9eb..3ae5ef11 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cl +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cl @@ -155,12 +155,8 @@ static inline void mh_item(__global const uint* cache, uint t, uint* s) { } for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); } -// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 12 13 of w. -static inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 12u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } -static inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x00000fffu) | ((w >> 13u) << 12u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } -static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 12u) << 13u) | (w & 0x00000fffu) | (((j >> 2u) & 1u) << 12u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } -// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). -static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } // Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. // The same constants as memhard.h in this pack (one emitter, three dialects). @@ -173,7 +169,8 @@ __kernel void igneum_build(__global uint* ds, __global const uint* cache, uint n if (t < nItems) { uint s[16]; mh_item(cache, t, s); - for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; + __global uint* d = ds + ((ulong)t * 16u); + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; } } @@ -203,69 +200,69 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - r7 = r7 ^ r0; // 1 xor - { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl - r4 = r4 - r1; // 3 sub - r2 = r0 * r4 + r2; // 4 mad - r4 = rotl_imm(r4, 9u); // 5 rotl - r0 = rotr_var(r0, r2); // 6 rotr - r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load - r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load - r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load - r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load - r5 = r5 * r4; // 12 mul - r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load - { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl - { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl - r2 = r2 | r7; // 16 or - r0 = rotr_var(r0, r6); // 17 rotr - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add - r7 = rotl_imm(r7, 24u); // 19 rotl - r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add - r2 = r2 | r6; // 21 or - r6 = r5 * r4 + r6; // 22 mad - r1 = r1 | r0; // 23 or - r6 = r6 ^ r1; // 24 xor - r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add - r7 = rotr_var(r7, r0); // 26 rotr - r4 = r5 * r7 + r4; // 27 mad - r3 = r2 * r4 + r3; // 28 mad - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load - r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load - r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load - r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add - r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add - r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add - r2 = mul_hi(r2, r5); // 35 mulhi - r4 = r4 ^ r2; // 36 xor - r6 = r6 * r5; // 37 mul - r7 = r7 ^ r0; // 38 xor - r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add - r2 = rotr_var(r2, r3); // 40 rotr - r7 = r7 - r0; // 41 sub - r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add - { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl - r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add - r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load - r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load - r4 = mul_hi(r4, r2); // 48 mulhi - r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl - r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add - r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load - r6 = rotl_imm(r6, 12u); // 53 rotl - r3 = rotl_imm(r3, 12u); // 54 rotl - r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load - r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add - r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load - r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add - r3 = mul_hi(r3, r2); // 60 mulhi - r6 = r6 | r4; // 61 or - { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl - r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = mul_hi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = mul_hi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = mul_hi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = mul_hi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = mul_hi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = mul_hi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = mul_hi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = mul_hi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = mul_hi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); @@ -306,69 +303,69 @@ IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulon for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - r7 = r7 ^ r0; // 1 xor - { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl - r4 = r4 - r1; // 3 sub - r2 = r0 * r4 + r2; // 4 mad - r4 = rotl_imm(r4, 9u); // 5 rotl - r0 = rotr_var(r0, r2); // 6 rotr - r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load - r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load - r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load - r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load - r5 = r5 * r4; // 12 mul - r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load - { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl - { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl - r2 = r2 | r7; // 16 or - r0 = rotr_var(r0, r6); // 17 rotr - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add - r7 = rotl_imm(r7, 24u); // 19 rotl - r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add - r2 = r2 | r6; // 21 or - r6 = r5 * r4 + r6; // 22 mad - r1 = r1 | r0; // 23 or - r6 = r6 ^ r1; // 24 xor - r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add - r7 = rotr_var(r7, r0); // 26 rotr - r4 = r5 * r7 + r4; // 27 mad - r3 = r2 * r4 + r3; // 28 mad - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load - r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load - r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load - r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add - r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add - r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add - r2 = mul_hi(r2, r5); // 35 mulhi - r4 = r4 ^ r2; // 36 xor - r6 = r6 * r5; // 37 mul - r7 = r7 ^ r0; // 38 xor - r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add - r2 = rotr_var(r2, r3); // 40 rotr - r7 = r7 - r0; // 41 sub - r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add - { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl - r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add - r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load - r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load - r4 = mul_hi(r4, r2); // 48 mulhi - r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl - r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add - r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load - r6 = rotl_imm(r6, 12u); // 53 rotl - r3 = rotl_imm(r3, 12u); // 54 rotl - r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load - r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add - r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load - r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add - r3 = mul_hi(r3, r2); // 60 mulhi - r6 = r6 | r4; // 61 or - { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl - r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = mul_hi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = mul_hi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = mul_hi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = mul_hi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = mul_hi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = mul_hi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = mul_hi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = mul_hi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = mul_hi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cu b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cu index af900376..9a46b748 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cu +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/kernel_bound.cu @@ -36,69 +36,69 @@ __global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t ba for (uint32_t it = 0u; it < 8u; ++it) { uint32_t sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - r7 = r7 ^ r0; // 1 xor - r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl - r4 = r4 - r1; // 3 sub - r2 = r0 * r4 + r2; // 4 mad - r4 = rotl_imm(r4, 9u); // 5 rotl - r0 = rotr_var(r0, r2); // 6 rotr - r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load - r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load - r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load - r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load - r5 = r5 * r4; // 12 mul - r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load - r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl - r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl - r2 = r2 | r7; // 16 or - r0 = rotr_var(r0, r6); // 17 rotr - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add - r7 = rotl_imm(r7, 24u); // 19 rotl - r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add - r2 = r2 | r6; // 21 or - r6 = r5 * r4 + r6; // 22 mad - r1 = r1 | r0; // 23 or - r6 = r6 ^ r1; // 24 xor - r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add - r7 = rotr_var(r7, r0); // 26 rotr - r4 = r5 * r7 + r4; // 27 mad - r3 = r2 * r4 + r3; // 28 mad - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load - r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load - r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load - r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add - r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add - r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add - r2 = __umulhi(r2, r5); // 35 mulhi - r4 = r4 ^ r2; // 36 xor - r6 = r6 * r5; // 37 mul - r7 = r7 ^ r0; // 38 xor - r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add - r2 = rotr_var(r2, r3); // 40 rotr - r7 = r7 - r0; // 41 sub - r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add - r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl - r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add - r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load - r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load - r4 = __umulhi(r4, r2); // 48 mulhi - r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add - r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl - r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add - r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load - r6 = rotl_imm(r6, 12u); // 53 rotl - r3 = rotl_imm(r3, 12u); // 54 rotl - r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add - r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load - r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add - r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load - r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add - r3 = __umulhi(r3, r2); // 60 mulhi - r6 = r6 | r4; // 61 or - r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl - r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load + r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 4); // 1 shfl + r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add + r0 = rotl_imm(r0, 19u); // 3 rotl + r7 = rotr_var(r7, r6); // 4 rotr + r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add + r1 = __umulhi(r1, r7); // 6 mulhi + r4 = r4 ^ ds[r2 & mask]; // 7 load + r7 = r7 ^ ds[r4 & mask]; // 8 load + r0 = r0 ^ ds[r3 & mask]; // 9 load + r5 = r5 ^ ds[r1 & mask]; // 10 load + r1 = r1 ^ ds[r5 & mask]; // 11 load + r3 = __umulhi(r3, r5); // 12 mulhi + r1 = r1 ^ ds[r3 & mask]; // 13 load + r0 = r0 - r3; // 14 sub + r5 = r1 * r3 + r5; // 15 mad + r6 = __umulhi(r6, r1); // 16 mulhi + r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add + r0 = __umulhi(r0, r6); // 18 mulhi + r5 = rotr_var(r5, r3); // 19 rotr + r5 = __umulhi(r5, r2); // 20 mulhi + r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add + r1 = __umulhi(r1, r5); // 23 mulhi + r2 = r2 - r5; // 24 sub + r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 26 shfl + r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add + r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add + r2 = r2 ^ ds[r1 & mask]; // 29 load + r5 = r5 ^ ds[r7 & mask]; // 30 load + r2 = r2 ^ ds[r5 & mask]; // 31 load + r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 32 shfl + r4 = r5 * r7 + r4; // 33 mad + r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r7, 8); // 35 shfl + r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add + r5 = r5 ^ r7; // 37 xor + r2 = r2 | r1; // 38 or + r1 = __umulhi(r1, r0); // 39 mulhi + r6 = rotl_imm(r6, 19u); // 40 rotl + r4 = __umulhi(r4, r6); // 41 mulhi + r6 = r6 - r0; // 42 sub + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r4 = r4 ^ ds[r2 & mask]; // 44 load + r1 = r1 ^ r3; // 45 xor + r7 = r7 ^ ds[r0 & mask]; // 46 load + r3 = r3 ^ ds[r1 & mask]; // 47 load + r5 = r5 * r3; // 48 mul + r1 = r1 - r5; // 49 sub + r2 = rotl_imm(r2, 8u); // 50 rotl + r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add + r4 = r4 ^ ds[r7 & mask]; // 52 load + r2 = r2 - r7; // 53 sub + r4 = r4 ^ r0; // 54 xor + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add + r2 = r2 ^ ds[r4 & mask]; // 56 load + r0 = r1 * r4 + r0; // 57 mad + r3 = r3 ^ ds[r5 & mask]; // 58 load + r5 = r5 | r6; // 59 or + r6 = r5 * r7 + r6; // 60 mad + r4 = rotl_imm(r4, 28u); // 61 rotl + r5 = __umulhi(r5, r0); // 62 mulhi + r3 = r3 ^ ds[r6 & mask]; // 63 load } uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.h b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.h index 79d760f4..bbe76f94 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.h +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.h @@ -105,9 +105,5 @@ IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { } for (uint32_t j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); } -// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 12 13 of w. -IGNEUM_HD uint32_t mh_j(uint32_t w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 12u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } -IGNEUM_HD uint32_t mh_t(uint32_t w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x00000fffu) | ((w >> 13u) << 12u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } -IGNEUM_HD uint32_t mh_addr(uint32_t t, uint32_t j) { uint32_t w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 12u) << 13u) | (w & 0x00000fffu) | (((j >> 2u) & 1u) << 12u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } -// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). -IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.metal b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.metal index 89171a04..57022480 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.metal +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/memhard.metal @@ -90,12 +90,8 @@ inline void mh_item(device const uint* cache, uint t, thread uint* s) { } for (uint j = 0u; j < 4u; ++j) mh_mixer(s, 0x9E3779B9u * (32u + j + 1u)); } -// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 12 13 of w. -inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 12u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } -inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x00000fffu) | ((w >> 13u) << 12u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } -inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 12u) << 13u) | (w & 0x00000fffu) | (((j >> 2u) & 1u) << 12u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } -// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). -inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } +// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } // One thread per segment (2^16 threads). kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { @@ -106,5 +102,6 @@ kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* da uint gid [[thread_position_in_grid]]) { uint s[16]; mh_item(cache, gid, s); - for (uint i = 0u; i < 16u; ++i) dataset[mh_addr(gid, i)] = s[i]; + device uint* d = dataset + gid * 16u; + for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; } diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.h b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.h index e353c0a0..354b473c 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.h +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.h @@ -13,9 +13,9 @@ #define IGNEUM_SEED_STRING "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000" #define IGNEUM_SEED_BYTES_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" -#define IGNEUM_GENERATOR 3 +#define IGNEUM_GENERATOR 2 #define IGNEUM_PROGRAM_ATTEMPT 0 -#define IGNEUM_PROGRAM_ID 0x73bcbfe8ccf988f1ull +#define IGNEUM_PROGRAM_ID 0xa6892c64d9ddd7f7ull #define IGNEUM_DAY_STRING "bytes:69676e65756d2d6461792ffa50000000000000" #define IGNEUM_DAY_BYTES_HEX "69676e65756d2d6461792ffa50000000000000" #define IGNEUM_DAY0 0xceed56d7u @@ -27,14 +27,10 @@ #define IGNEUM_INSTR_COUNT 64 #define IGNEUM_LOADS_PER_HASH 128 #define IGNEUM_WIDE_LOADS_PER_HASH 0 -#define IGNEUM_OP_MIX "load=16 add=15 shfl=6 mad=4 or=4 rotl=4 rotr=4 xor=4 mulhi=3 mul=2 sub=2" -// Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that -// runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). -#define IGNEUM_PROGRAM_CLASS "v3" -#define IGNEUM_ERA_SEED_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" +#define IGNEUM_OP_MIX "load=16 add=13 mulhi=9 shfl=5 sub=5 mad=4 rotl=4 xor=3 or=2 rotr=2 mul=1" // Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item // derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. -#define IGNEUM_LOAD_CLASS "mx4-erad810f22d" +#define IGNEUM_LOAD_CLASS "mx4" #define IGNEUM_CLASS_MIXER_MULT 4 #define IGNEUM_CACHE_GROWTH 1 // 1: cache words = 2^(26 + doublings(day)), doublings = floor(log2(1 + day / 1460)) #define IGNEUM_LOAD_SLOTS 16 @@ -43,18 +39,6 @@ #define IGNEUM_BYTES_PER_HASH 512 #define IGNEUM_FOLD_ROT 11 #define IGNEUM_FOLD_MUL 0x9e3779b1u -// Era layout (5 October 2026, docs/plans/era-layout.md): NOT the lottery hash. Every dataset load reads -// idx = ((rotl(src * STRIDE_MUL, STRIDE_ROT) & window mask) | window offset) & MASK; the window of a load site is the -// dataset, a half or a quarter of it (IGNEUM_ERA_WINDOWS: site:shrink:offset); dataset word w holds word j(w) of item -// t(w) with j's bits at the INTERLEAVE positions (memhard.h: mh_t, mh_j, mh_addr). -#define IGNEUM_ERA_LABEL "d810f22d" -#define IGNEUM_ERA_SEED_WORDS { 0xd810f22du, 0xcf9dc883u, 0x5f3e571fu, 0x2b7d97fcu, 0x810012c4u, 0x002d9798u, 0xa4180c41u, 0xa4562c21u } -#define IGNEUM_ERA_ALLOWED_WIDTHS { 1, 0, 0 } // words, ascending, 0 = unused; one entry pins the width -#define IGNEUM_ERA_WIDTH_WORDS 1 -#define IGNEUM_ERA_STRIDE_MUL 0x9ad30d99u -#define IGNEUM_ERA_STRIDE_ROT 29 -#define IGNEUM_ERA_INTERLEAVE { 0, 2, 12, 13 } -#define IGNEUM_ERA_WINDOWS "7:2:1 8:1:1 9:1:1 10:1:1 11:0:0 13:1:1 29:0:0 30:2:2 31:1:1 44:1:1 46:2:0 47:0:0 52:0:0 56:0:0 58:2:0 63:1:1" // 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) #define IGNEUM_DATASET_MODE 1 diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.json b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.json index a78da713..dcce0542 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.json +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.json @@ -1,8 +1,8 @@ { "format": "igneum-program-pack-3", - "generator": 3, + "generator": 2, "attempt": 0, - "program_id": "0x73bcbfe8ccf988f1", + "program_id": "0xa6892c64d9ddd7f7", "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", "dataset_mode": "memory-hard", "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", @@ -15,9 +15,7 @@ "iterations": 8, "instruction_count": 64, "loads_per_hash": 128, - "program_class": "v3", - "era_seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07", - "load_class": "mx4-erad810f22d", + "load_class": "mx4", "mixer_mult": 4, "cache_growth": true, "mixer": "class v3 (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): every mixer application of the item derivation is 4 applications with round keys (r * 4 + j + 1) * 0x9E3779B9, the 8 dependent cache reads per item unchanged; cache growth rule option C: cache words = 2^(26 + doublings(day)), dataset words = 2^(genesis_log2 + doublings(day)), doublings(day) = floor(log2(1 + day / 1460)) for day = days since genesis", @@ -26,21 +24,7 @@ "load_width_counts_4_16_64": [16, 0, 0], "bytes_per_hash": 512, "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", - "era": { - "label": "d810f22d", - "seed_words": ["0xd810f22d", "0xcf9dc883", "0x5f3e571f", "0x2b7d97fc", "0x810012c4", "0x002d9798", "0xa4180c41", "0xa4562c21"], - "draw": "docs/plans/era-layout.md 1.1: SplitMix64 seeded with seed_words[0] | seed_words[1] << 32 of seed_words_from_bytes('igneum-era/' || n_le64 || E_n); width = allowed[below(|allowed|)], stride_mul = low32(next()) | 1, stride_rot = 1 + below(31), then four next() draws for a partial Fisher-Yates over positions log2(W)..15 of which 4 - log2(W) are used", - "allowed_widths": [1], - "width_words": 1, - "stride_mul": "0x9ad30d99", - "stride_rot": 29, - "interleave": [0, 2, 12, 13], - "address": "y = rotl(src * stride_mul, stride_rot); k = min(win, D - 26); idx = ((y & (mask >> k)) | ((off & (2^k - 1)) << (D - k))) & mask; a wide load aligns idx down to W words", - "windows": "per instruction, after the width roll: win = below(3), off = low32(next()) & (2^win - 1); used on a load slot (the instruction's win and off fields)", - "dataset_word": "dataset[w] = item(t(w))[j(w)]: j(w) gathers the bits of w at the interleave positions, t(w) is w with those bits removed", - "program_id_suffix": "'era/' || allowed[3] || width_words || stride_mul_le32 || stride_rot_le32 || interleave[4]" - }, - "op_mix": {"load": 16, "add": 15, "shfl": 6, "mad": 4, "or": 4, "rotl": 4, "rotr": 4, "xor": 4, "mulhi": 3, "mul": 2, "sub": 2}, + "op_mix": {"load": 16, "add": 13, "mulhi": 9, "shfl": 5, "sub": 5, "mad": 4, "rotl": 4, "xor": 3, "or": 2, "rotr": 2, "mul": 1}, "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", @@ -79,69 +63,69 @@ "word": "dataset[w] = item(w >> 4)[w & 15]" }, "instructions": [ - {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1, "win": 0, "off": 0}, - {"i": 1, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xbaab6229", "imm2": "0xed861989", "rot": 26, "bit": 22, "mask": 4, "width": 1, "win": 0, "off": 0}, - {"i": 2, "op": "shfl", "dst": 3, "src": 6, "src2": 2, "imm": "0x5b623116", "imm2": "0xff12e5b2", "rot": 12, "bit": 24, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 3, "op": "sub", "dst": 4, "src": 1, "src2": 1, "imm": "0xe99741c7", "imm2": "0xf5fa5009", "rot": 1, "bit": 21, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 4, "op": "mad", "dst": 2, "src": 0, "src2": 4, "imm": "0x673c2157", "imm2": "0xee02465f", "rot": 20, "bit": 22, "mask": 2, "width": 1, "win": 0, "off": 0}, - {"i": 5, "op": "rotl", "dst": 4, "src": 7, "src2": 7, "imm": "0x946f7818", "imm2": "0x45d3399e", "rot": 9, "bit": 2, "mask": 16, "width": 1, "win": 0, "off": 0}, - {"i": 6, "op": "rotr", "dst": 0, "src": 2, "src2": 4, "imm": "0x5f6a0ed2", "imm2": "0x7043a636", "rot": 19, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, - {"i": 7, "op": "load", "dst": 6, "src": 7, "src2": 1, "imm": "0x5c61dcf7", "imm2": "0x7466aa40", "rot": 19, "bit": 9, "mask": 2, "width": 1, "win": 2, "off": 1}, - {"i": 8, "op": "load", "dst": 1, "src": 4, "src2": 5, "imm": "0x85668475", "imm2": "0xdb8cc483", "rot": 29, "bit": 7, "mask": 4, "width": 1, "win": 1, "off": 1}, - {"i": 9, "op": "load", "dst": 1, "src": 2, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1, "win": 1, "off": 1}, - {"i": 10, "op": "load", "dst": 7, "src": 0, "src2": 2, "imm": "0xe075297c", "imm2": "0x5779c44c", "rot": 10, "bit": 22, "mask": 2, "width": 1, "win": 1, "off": 1}, - {"i": 11, "op": "load", "dst": 7, "src": 1, "src2": 6, "imm": "0x65aa4311", "imm2": "0x4fe48ea9", "rot": 15, "bit": 9, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 12, "op": "mul", "dst": 5, "src": 4, "src2": 2, "imm": "0x1383d3ad", "imm2": "0xf3094b29", "rot": 8, "bit": 9, "mask": 2, "width": 1, "win": 0, "off": 0}, - {"i": 13, "op": "load", "dst": 7, "src": 6, "src2": 2, "imm": "0xed8a496f", "imm2": "0x3072c3c6", "rot": 28, "bit": 19, "mask": 8, "width": 1, "win": 1, "off": 1}, - {"i": 14, "op": "shfl", "dst": 6, "src": 5, "src2": 0, "imm": "0x8b965b57", "imm2": "0xcfeca6c1", "rot": 12, "bit": 27, "mask": 16, "width": 1, "win": 0, "off": 0}, - {"i": 15, "op": "shfl", "dst": 3, "src": 5, "src2": 3, "imm": "0x877c7586", "imm2": "0xa9cb2a03", "rot": 2, "bit": 29, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 16, "op": "or", "dst": 2, "src": 7, "src2": 3, "imm": "0xb740221a", "imm2": "0x89d38d6d", "rot": 6, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, - {"i": 17, "op": "rotr", "dst": 0, "src": 6, "src2": 1, "imm": "0x26f3ad8a", "imm2": "0x27256f15", "rot": 18, "bit": 5, "mask": 2, "width": 1, "win": 0, "off": 0}, - {"i": 18, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1, "win": 0, "off": 0}, - {"i": 19, "op": "rotl", "dst": 7, "src": 0, "src2": 5, "imm": "0x849ae6ee", "imm2": "0x02b358f9", "rot": 24, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, - {"i": 20, "op": "add", "dst": 6, "src": 7, "src2": 6, "imm": "0x52334d12", "imm2": "0x8c9f0ef8", "rot": 11, "bit": 23, "mask": 4, "width": 1, "win": 0, "off": 0}, - {"i": 21, "op": "or", "dst": 2, "src": 6, "src2": 5, "imm": "0xb1871e63", "imm2": "0xb2e40191", "rot": 5, "bit": 13, "mask": 4, "width": 1, "win": 0, "off": 0}, - {"i": 22, "op": "mad", "dst": 6, "src": 5, "src2": 4, "imm": "0x97df29e4", "imm2": "0xe60fea84", "rot": 11, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, - {"i": 23, "op": "or", "dst": 1, "src": 0, "src2": 3, "imm": "0x8f30d21d", "imm2": "0x2df685a0", "rot": 31, "bit": 0, "mask": 16, "width": 1, "win": 0, "off": 0}, - {"i": 24, "op": "xor", "dst": 6, "src": 1, "src2": 2, "imm": "0xd2c4025f", "imm2": "0x5269eb4d", "rot": 31, "bit": 21, "mask": 16, "width": 1, "win": 0, "off": 0}, - {"i": 25, "op": "add", "dst": 2, "src": 6, "src2": 5, "imm": "0x659fc3d3", "imm2": "0x9cec0e12", "rot": 6, "bit": 17, "mask": 4, "width": 1, "win": 0, "off": 0}, - {"i": 26, "op": "rotr", "dst": 7, "src": 0, "src2": 1, "imm": "0xd89ef484", "imm2": "0x20be3846", "rot": 12, "bit": 9, "mask": 16, "width": 1, "win": 0, "off": 0}, - {"i": 27, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 28, "op": "mad", "dst": 3, "src": 2, "src2": 4, "imm": "0xc5c46d76", "imm2": "0x700044b5", "rot": 22, "bit": 10, "mask": 2, "width": 1, "win": 0, "off": 0}, - {"i": 29, "op": "load", "dst": 1, "src": 4, "src2": 3, "imm": "0x0fbaf177", "imm2": "0xfff4f2ed", "rot": 20, "bit": 31, "mask": 2, "width": 1, "win": 0, "off": 0}, - {"i": 30, "op": "load", "dst": 2, "src": 3, "src2": 0, "imm": "0xe90eb2e5", "imm2": "0xb0f9eb79", "rot": 27, "bit": 14, "mask": 4, "width": 1, "win": 2, "off": 2}, - {"i": 31, "op": "load", "dst": 1, "src": 5, "src2": 2, "imm": "0x97ba3fc3", "imm2": "0x7894e657", "rot": 3, "bit": 30, "mask": 16, "width": 1, "win": 1, "off": 1}, - {"i": 32, "op": "add", "dst": 7, "src": 2, "src2": 7, "imm": "0x070888a8", "imm2": "0xe403240e", "rot": 2, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, - {"i": 33, "op": "add", "dst": 2, "src": 0, "src2": 5, "imm": "0xf2e46d55", "imm2": "0x29701828", "rot": 31, "bit": 28, "mask": 8, "width": 1, "win": 0, "off": 0}, - {"i": 34, "op": "add", "dst": 2, "src": 3, "src2": 0, "imm": "0x58f75b87", "imm2": "0x343b7aee", "rot": 12, "bit": 14, "mask": 4, "width": 1, "win": 0, "off": 0}, - {"i": 35, "op": "mulhi", "dst": 2, "src": 5, "src2": 6, "imm": "0x5892a9e6", "imm2": "0xc9824c94", "rot": 19, "bit": 26, "mask": 4, "width": 1, "win": 0, "off": 0}, - {"i": 36, "op": "xor", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, - {"i": 37, "op": "mul", "dst": 6, "src": 5, "src2": 7, "imm": "0xccf564a5", "imm2": "0x873ad101", "rot": 7, "bit": 11, "mask": 4, "width": 1, "win": 0, "off": 0}, - {"i": 38, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xcac8f06d", "imm2": "0x6b97c683", "rot": 18, "bit": 28, "mask": 2, "width": 1, "win": 0, "off": 0}, - {"i": 39, "op": "add", "dst": 7, "src": 2, "src2": 4, "imm": "0xb8180e9d", "imm2": "0x32bbd117", "rot": 23, "bit": 19, "mask": 4, "width": 1, "win": 0, "off": 0}, - {"i": 40, "op": "rotr", "dst": 2, "src": 3, "src2": 0, "imm": "0x2d6070bc", "imm2": "0x68ff101e", "rot": 13, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 41, "op": "sub", "dst": 7, "src": 0, "src2": 2, "imm": "0x0daf96ea", "imm2": "0x36f37be1", "rot": 5, "bit": 0, "mask": 8, "width": 1, "win": 0, "off": 0}, - {"i": 42, "op": "add", "dst": 4, "src": 3, "src2": 6, "imm": "0x6ced15b7", "imm2": "0x6df7aed4", "rot": 19, "bit": 4, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 43, "op": "shfl", "dst": 7, "src": 3, "src2": 5, "imm": "0x8ace05f3", "imm2": "0xd378ec12", "rot": 23, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, - {"i": 44, "op": "load", "dst": 0, "src": 7, "src2": 4, "imm": "0xb0607786", "imm2": "0xc4acabbc", "rot": 13, "bit": 7, "mask": 16, "width": 1, "win": 1, "off": 1}, - {"i": 45, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1, "win": 0, "off": 0}, - {"i": 46, "op": "load", "dst": 3, "src": 1, "src2": 0, "imm": "0x63cc1e4e", "imm2": "0xa1be8118", "rot": 12, "bit": 6, "mask": 2, "width": 1, "win": 2, "off": 0}, - {"i": 47, "op": "load", "dst": 6, "src": 3, "src2": 7, "imm": "0x353f1d79", "imm2": "0x3b2e7456", "rot": 18, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 48, "op": "mulhi", "dst": 4, "src": 2, "src2": 7, "imm": "0x00d8a3cd", "imm2": "0x231866d2", "rot": 21, "bit": 20, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 49, "op": "add", "dst": 5, "src": 0, "src2": 2, "imm": "0xa8bae6df", "imm2": "0xf572bdb9", "rot": 14, "bit": 7, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 50, "op": "shfl", "dst": 0, "src": 7, "src2": 7, "imm": "0x81ef22e1", "imm2": "0x74438fc5", "rot": 28, "bit": 18, "mask": 4, "width": 1, "win": 0, "off": 0}, - {"i": 51, "op": "add", "dst": 6, "src": 0, "src2": 6, "imm": "0x383b9260", "imm2": "0x11e17c61", "rot": 12, "bit": 19, "mask": 16, "width": 1, "win": 0, "off": 0}, - {"i": 52, "op": "load", "dst": 5, "src": 2, "src2": 2, "imm": "0xfb84f451", "imm2": "0x11cd863e", "rot": 21, "bit": 20, "mask": 8, "width": 1, "win": 0, "off": 0}, - {"i": 53, "op": "rotl", "dst": 6, "src": 5, "src2": 4, "imm": "0xb1a7db6b", "imm2": "0x76686b9b", "rot": 12, "bit": 4, "mask": 16, "width": 1, "win": 0, "off": 0}, - {"i": 54, "op": "rotl", "dst": 3, "src": 6, "src2": 3, "imm": "0x6f981f52", "imm2": "0xd99aeba2", "rot": 12, "bit": 27, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 55, "op": "add", "dst": 2, "src": 1, "src2": 2, "imm": "0xac6be8e3", "imm2": "0x18d67dbb", "rot": 26, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, - {"i": 56, "op": "load", "dst": 1, "src": 4, "src2": 0, "imm": "0x7e7f6a00", "imm2": "0x6f0747da", "rot": 25, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 57, "op": "add", "dst": 5, "src": 0, "src2": 4, "imm": "0xf03673fe", "imm2": "0xa75cd60d", "rot": 16, "bit": 12, "mask": 8, "width": 1, "win": 0, "off": 0}, - {"i": 58, "op": "load", "dst": 5, "src": 0, "src2": 2, "imm": "0x227f94a6", "imm2": "0x0e8344f9", "rot": 20, "bit": 10, "mask": 2, "width": 1, "win": 2, "off": 0}, - {"i": 59, "op": "add", "dst": 1, "src": 4, "src2": 3, "imm": "0xdecd4794", "imm2": "0x8dfb96bb", "rot": 21, "bit": 7, "mask": 4, "width": 1, "win": 0, "off": 0}, - {"i": 60, "op": "mulhi", "dst": 3, "src": 2, "src2": 2, "imm": "0x0dd268e0", "imm2": "0x53034ca9", "rot": 1, "bit": 8, "mask": 8, "width": 1, "win": 0, "off": 0}, - {"i": 61, "op": "or", "dst": 6, "src": 4, "src2": 7, "imm": "0x3a45a321", "imm2": "0x9bc59a5f", "rot": 25, "bit": 11, "mask": 1, "width": 1, "win": 0, "off": 0}, - {"i": 62, "op": "shfl", "dst": 5, "src": 4, "src2": 2, "imm": "0x8f229cc1", "imm2": "0xcaac64a2", "rot": 17, "bit": 13, "mask": 8, "width": 1, "win": 0, "off": 0}, - {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0xb2574178", "imm2": "0xcbcc798d", "rot": 28, "bit": 0, "mask": 16, "width": 1, "win": 1, "off": 1} + {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1}, + {"i": 1, "op": "shfl", "dst": 2, "src": 0, "src2": 7, "imm": "0xe3c2f9cb", "imm2": "0xde0bea4e", "rot": 6, "bit": 9, "mask": 4, "width": 1}, + {"i": 2, "op": "add", "dst": 3, "src": 2, "src2": 0, "imm": "0x2cccb6ca", "imm2": "0x642e66db", "rot": 27, "bit": 10, "mask": 2, "width": 1}, + {"i": 3, "op": "rotl", "dst": 0, "src": 2, "src2": 1, "imm": "0xb59e83b2", "imm2": "0x19c26fb9", "rot": 19, "bit": 20, "mask": 4, "width": 1}, + {"i": 4, "op": "rotr", "dst": 7, "src": 6, "src2": 5, "imm": "0x6f055f55", "imm2": "0x550e4ea1", "rot": 31, "bit": 20, "mask": 4, "width": 1}, + {"i": 5, "op": "add", "dst": 7, "src": 4, "src2": 7, "imm": "0xee02465f", "imm2": "0xc1535555", "rot": 31, "bit": 21, "mask": 4, "width": 1}, + {"i": 6, "op": "mulhi", "dst": 1, "src": 7, "src2": 5, "imm": "0x7826a6a7", "imm2": "0x946f7818", "rot": 18, "bit": 21, "mask": 8, "width": 1}, + {"i": 7, "op": "load", "dst": 4, "src": 2, "src2": 0, "imm": "0x5d080878", "imm2": "0xdf885578", "rot": 5, "bit": 18, "mask": 4, "width": 1}, + {"i": 8, "op": "load", "dst": 7, "src": 4, "src2": 1, "imm": "0x875bbb36", "imm2": "0x594a838f", "rot": 24, "bit": 4, "mask": 2, "width": 1}, + {"i": 9, "op": "load", "dst": 0, "src": 3, "src2": 1, "imm": "0xe5e607c2", "imm2": "0xa5cd9f75", "rot": 26, "bit": 28, "mask": 4, "width": 1}, + {"i": 10, "op": "load", "dst": 5, "src": 1, "src2": 3, "imm": "0xea3f7b43", "imm2": "0x10c8d4e7", "rot": 5, "bit": 29, "mask": 8, "width": 1}, + {"i": 11, "op": "load", "dst": 1, "src": 5, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1}, + {"i": 12, "op": "mulhi", "dst": 3, "src": 5, "src2": 7, "imm": "0xbfd5615c", "imm2": "0xd8224ae2", "rot": 21, "bit": 12, "mask": 2, "width": 1}, + {"i": 13, "op": "load", "dst": 1, "src": 3, "src2": 5, "imm": "0x35c07cc5", "imm2": "0xe82db54f", "rot": 7, "bit": 6, "mask": 8, "width": 1}, + {"i": 14, "op": "sub", "dst": 0, "src": 3, "src2": 0, "imm": "0x587ee0f1", "imm2": "0xfd23eefd", "rot": 16, "bit": 21, "mask": 16, "width": 1}, + {"i": 15, "op": "mad", "dst": 5, "src": 1, "src2": 3, "imm": "0x6974dd29", "imm2": "0xc8148960", "rot": 3, "bit": 11, "mask": 2, "width": 1}, + {"i": 16, "op": "mulhi", "dst": 6, "src": 1, "src2": 7, "imm": "0x3072c3c6", "imm2": "0x55ee21f8", "rot": 26, "bit": 1, "mask": 1, "width": 1}, + {"i": 17, "op": "add", "dst": 5, "src": 2, "src2": 2, "imm": "0x697b3d00", "imm2": "0x8b965b57", "rot": 9, "bit": 28, "mask": 1, "width": 1}, + {"i": 18, "op": "mulhi", "dst": 0, "src": 6, "src2": 3, "imm": "0x2910cacb", "imm2": "0x6ac79431", "rot": 7, "bit": 6, "mask": 4, "width": 1}, + {"i": 19, "op": "rotr", "dst": 5, "src": 3, "src2": 0, "imm": "0xed96a94a", "imm2": "0x4c988c10", "rot": 24, "bit": 21, "mask": 8, "width": 1}, + {"i": 20, "op": "mulhi", "dst": 5, "src": 2, "src2": 7, "imm": "0x40d2fc76", "imm2": "0x2f7c7eca", "rot": 13, "bit": 8, "mask": 4, "width": 1}, + {"i": 21, "op": "add", "dst": 1, "src": 0, "src2": 5, "imm": "0xebcf247a", "imm2": "0x6d7e8d05", "rot": 31, "bit": 1, "mask": 16, "width": 1}, + {"i": 22, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1}, + {"i": 23, "op": "mulhi", "dst": 1, "src": 5, "src2": 7, "imm": "0xfdfe72fd", "imm2": "0x735eeb8d", "rot": 30, "bit": 25, "mask": 8, "width": 1}, + {"i": 24, "op": "sub", "dst": 2, "src": 5, "src2": 0, "imm": "0x32cf1258", "imm2": "0xd813deb6", "rot": 30, "bit": 6, "mask": 16, "width": 1}, + {"i": 25, "op": "add", "dst": 7, "src": 4, "src2": 2, "imm": "0x08ffa6c7", "imm2": "0x699ef1bb", "rot": 7, "bit": 2, "mask": 16, "width": 1}, + {"i": 26, "op": "shfl", "dst": 3, "src": 4, "src2": 5, "imm": "0x6f53c70d", "imm2": "0x3357513f", "rot": 3, "bit": 26, "mask": 2, "width": 1}, + {"i": 27, "op": "add", "dst": 7, "src": 1, "src2": 4, "imm": "0xe60fea84", "imm2": "0xb4ead2fb", "rot": 14, "bit": 14, "mask": 1, "width": 1}, + {"i": 28, "op": "add", "dst": 3, "src": 1, "src2": 0, "imm": "0x65c76dab", "imm2": "0x8f30d21d", "rot": 24, "bit": 6, "mask": 1, "width": 1}, + {"i": 29, "op": "load", "dst": 2, "src": 1, "src2": 2, "imm": "0x82fad9a6", "imm2": "0x8c6358db", "rot": 7, "bit": 31, "mask": 2, "width": 1}, + {"i": 30, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x6e947ee0", "imm2": "0xaf9a2dda", "rot": 2, "bit": 30, "mask": 4, "width": 1}, + {"i": 31, "op": "load", "dst": 2, "src": 5, "src2": 1, "imm": "0x608bb7ce", "imm2": "0x4be663db", "rot": 19, "bit": 1, "mask": 16, "width": 1}, + {"i": 32, "op": "shfl", "dst": 1, "src": 7, "src2": 6, "imm": "0x88e52e20", "imm2": "0x77647269", "rot": 20, "bit": 25, "mask": 4, "width": 1}, + {"i": 33, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1}, + {"i": 34, "op": "add", "dst": 4, "src": 2, "src2": 3, "imm": "0x0480debe", "imm2": "0xc7ce690c", "rot": 12, "bit": 21, "mask": 16, "width": 1}, + {"i": 35, "op": "shfl", "dst": 3, "src": 7, "src2": 5, "imm": "0xa73f59de", "imm2": "0x84b9e329", "rot": 21, "bit": 27, "mask": 8, "width": 1}, + {"i": 36, "op": "add", "dst": 7, "src": 1, "src2": 7, "imm": "0xc53b542e", "imm2": "0xe10c2c95", "rot": 21, "bit": 2, "mask": 4, "width": 1}, + {"i": 37, "op": "xor", "dst": 5, "src": 7, "src2": 4, "imm": "0x81cd7b0e", "imm2": "0x21a51823", "rot": 12, "bit": 10, "mask": 2, "width": 1}, + {"i": 38, "op": "or", "dst": 2, "src": 1, "src2": 3, "imm": "0x7894e657", "imm2": "0xf8e4b972", "rot": 18, "bit": 9, "mask": 4, "width": 1}, + {"i": 39, "op": "mulhi", "dst": 1, "src": 0, "src2": 4, "imm": "0xbee8421f", "imm2": "0x070888a8", "rot": 20, "bit": 28, "mask": 4, "width": 1}, + {"i": 40, "op": "rotl", "dst": 6, "src": 1, "src2": 4, "imm": "0x4609857a", "imm2": "0xaeecb156", "rot": 19, "bit": 21, "mask": 1, "width": 1}, + {"i": 41, "op": "mulhi", "dst": 4, "src": 6, "src2": 0, "imm": "0x1a84e1e9", "imm2": "0x9b26bb72", "rot": 28, "bit": 19, "mask": 1, "width": 1}, + {"i": 42, "op": "sub", "dst": 6, "src": 0, "src2": 6, "imm": "0xbf62908e", "imm2": "0xdf03ea88", "rot": 11, "bit": 27, "mask": 16, "width": 1}, + {"i": 43, "op": "shfl", "dst": 6, "src": 3, "src2": 4, "imm": "0xa966241c", "imm2": "0x9c639efa", "rot": 12, "bit": 4, "mask": 4, "width": 1}, + {"i": 44, "op": "load", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1}, + {"i": 45, "op": "xor", "dst": 1, "src": 3, "src2": 6, "imm": "0x006193d0", "imm2": "0xfc2acc3f", "rot": 25, "bit": 1, "mask": 1, "width": 1}, + {"i": 46, "op": "load", "dst": 7, "src": 0, "src2": 6, "imm": "0x4777c4f8", "imm2": "0x3cf0a02f", "rot": 11, "bit": 14, "mask": 4, "width": 1}, + {"i": 47, "op": "load", "dst": 3, "src": 1, "src2": 3, "imm": "0x10692532", "imm2": "0x1a292ea5", "rot": 20, "bit": 23, "mask": 1, "width": 1}, + {"i": 48, "op": "mul", "dst": 5, "src": 3, "src2": 7, "imm": "0xadf5bd13", "imm2": "0xb999de2e", "rot": 23, "bit": 10, "mask": 4, "width": 1}, + {"i": 49, "op": "sub", "dst": 1, "src": 5, "src2": 4, "imm": "0x68ff101e", "imm2": "0xbdaaf46a", "rot": 25, "bit": 23, "mask": 16, "width": 1}, + {"i": 50, "op": "rotl", "dst": 2, "src": 6, "src2": 4, "imm": "0x92d9a412", "imm2": "0x0daf96ea", "rot": 8, "bit": 4, "mask": 4, "width": 1}, + {"i": 51, "op": "add", "dst": 1, "src": 5, "src2": 0, "imm": "0xa900fec4", "imm2": "0x77b9bd43", "rot": 6, "bit": 23, "mask": 2, "width": 1}, + {"i": 52, "op": "load", "dst": 4, "src": 7, "src2": 1, "imm": "0x51392a72", "imm2": "0x99e8bb36", "rot": 11, "bit": 9, "mask": 1, "width": 1}, + {"i": 53, "op": "sub", "dst": 2, "src": 7, "src2": 2, "imm": "0x0ffe2ac7", "imm2": "0x030743df", "rot": 9, "bit": 30, "mask": 16, "width": 1}, + {"i": 54, "op": "xor", "dst": 4, "src": 0, "src2": 4, "imm": "0x9123ff15", "imm2": "0x10c329a7", "rot": 28, "bit": 15, "mask": 2, "width": 1}, + {"i": 55, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1}, + {"i": 56, "op": "load", "dst": 2, "src": 4, "src2": 3, "imm": "0x239de52c", "imm2": "0xf80bae18", "rot": 15, "bit": 24, "mask": 1, "width": 1}, + {"i": 57, "op": "mad", "dst": 0, "src": 1, "src2": 4, "imm": "0x31dede8e", "imm2": "0xd3f619e6", "rot": 29, "bit": 7, "mask": 2, "width": 1}, + {"i": 58, "op": "load", "dst": 3, "src": 5, "src2": 3, "imm": "0x206437d6", "imm2": "0x28d1c290", "rot": 17, "bit": 28, "mask": 4, "width": 1}, + {"i": 59, "op": "or", "dst": 5, "src": 6, "src2": 3, "imm": "0x8fffd674", "imm2": "0x0507903a", "rot": 26, "bit": 27, "mask": 2, "width": 1}, + {"i": 60, "op": "mad", "dst": 6, "src": 5, "src2": 7, "imm": "0xf572bdb9", "imm2": "0xeda2af31", "rot": 21, "bit": 8, "mask": 2, "width": 1}, + {"i": 61, "op": "rotl", "dst": 4, "src": 2, "src2": 7, "imm": "0x84f12ddf", "imm2": "0x81ef22e1", "rot": 28, "bit": 30, "mask": 1, "width": 1}, + {"i": 62, "op": "mulhi", "dst": 5, "src": 0, "src2": 6, "imm": "0xf6bb45ee", "imm2": "0x6bfb632d", "rot": 22, "bit": 0, "mask": 4, "width": 1}, + {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0x50ec702a", "imm2": "0xae6ee96e", "rot": 20, "bit": 25, "mask": 2, "width": 1} ] } diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.metal b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.metal index 8c0a2d1c..b899adec 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.metal +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program.metal @@ -39,69 +39,69 @@ kernel void igneum_hash(device const uint* dataset [[buffer(0)]], for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 - r7 = r7 ^ r0; // 1 - r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 - r4 = r4 - r1; // 3 - r2 = r0 * r4 + r2; // 4 - r4 = rotl_imm(r4, 9u); // 5 - r0 = rotr_var(r0, r2); // 6 - r6 = r6 ^ dataset[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 - r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 - r1 = r1 ^ dataset[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 - r7 = r7 ^ dataset[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 - r7 = r7 ^ dataset[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 - r5 = r5 * r4; // 12 - r7 = r7 ^ dataset[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 - r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 - r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 - r2 = r2 | r7; // 16 - r0 = rotr_var(r0, r6); // 17 - r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 - r7 = rotl_imm(r7, 24u); // 19 - r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 - r2 = r2 | r6; // 21 - r6 = r5 * r4 + r6; // 22 - r1 = r1 | r0; // 23 - r6 = r6 ^ r1; // 24 - r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 - r7 = rotr_var(r7, r0); // 26 - r4 = r5 * r7 + r4; // 27 - r3 = r2 * r4 + r3; // 28 - r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 - r2 = r2 ^ dataset[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 - r1 = r1 ^ dataset[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 - r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 - r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 - r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 - r2 = mulhi(r2, r5); // 35 - r4 = r4 ^ r2; // 36 - r6 = r6 * r5; // 37 - r7 = r7 ^ r0; // 38 - r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 - r2 = rotr_var(r2, r3); // 40 - r7 = r7 - r0; // 41 - r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 - r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 - r0 = r0 ^ dataset[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 - r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 - r3 = r3 ^ dataset[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 - r6 = r6 ^ dataset[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 - r4 = mulhi(r4, r2); // 48 - r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 - r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 - r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 - r5 = r5 ^ dataset[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 - r6 = rotl_imm(r6, 12u); // 53 - r3 = rotl_imm(r3, 12u); // 54 - r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 - r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 - r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 - r5 = r5 ^ dataset[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 - r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 - r3 = mulhi(r3, r2); // 60 - r6 = r6 | r4; // 61 - r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 - r3 = r3 ^ dataset[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + r2 = r2 ^ simd_shuffle_xor(r0, (ushort)4); // 1 + r3 = r3 + r2 + select(0x2cccb6cau, 0x642e66dbu, ((sel >> 10u) & 1u) != 0u); // 2 + r0 = rotl_imm(r0, 19u); // 3 + r7 = rotr_var(r7, r6); // 4 + r7 = r7 + r4 + select(0xee02465fu, 0xc1535555u, ((sel >> 21u) & 1u) != 0u); // 5 + r1 = mulhi(r1, r7); // 6 + r4 = r4 ^ dataset[r2 & MASK]; // 7 + r7 = r7 ^ dataset[r4 & MASK]; // 8 + r0 = r0 ^ dataset[r3 & MASK]; // 9 + r5 = r5 ^ dataset[r1 & MASK]; // 10 + r1 = r1 ^ dataset[r5 & MASK]; // 11 + r3 = mulhi(r3, r5); // 12 + r1 = r1 ^ dataset[r3 & MASK]; // 13 + r0 = r0 - r3; // 14 + r5 = r1 * r3 + r5; // 15 + r6 = mulhi(r6, r1); // 16 + r5 = r5 + r2 + select(0x697b3d00u, 0x8b965b57u, ((sel >> 28u) & 1u) != 0u); // 17 + r0 = mulhi(r0, r6); // 18 + r5 = rotr_var(r5, r3); // 19 + r5 = mulhi(r5, r2); // 20 + r1 = r1 + r0 + select(0xebcf247au, 0x6d7e8d05u, ((sel >> 1u) & 1u) != 0u); // 21 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 22 + r1 = mulhi(r1, r5); // 23 + r2 = r2 - r5; // 24 + r7 = r7 + r4 + select(0x08ffa6c7u, 0x699ef1bbu, ((sel >> 2u) & 1u) != 0u); // 25 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 26 + r7 = r7 + r1 + select(0xe60fea84u, 0xb4ead2fbu, ((sel >> 14u) & 1u) != 0u); // 27 + r3 = r3 + r1 + select(0x65c76dabu, 0x8f30d21du, ((sel >> 6u) & 1u) != 0u); // 28 + r2 = r2 ^ dataset[r1 & MASK]; // 29 + r5 = r5 ^ dataset[r7 & MASK]; // 30 + r2 = r2 ^ dataset[r5 & MASK]; // 31 + r1 = r1 ^ simd_shuffle_xor(r7, (ushort)4); // 32 + r4 = r5 * r7 + r4; // 33 + r4 = r4 + r2 + select(0x0480debeu, 0xc7ce690cu, ((sel >> 21u) & 1u) != 0u); // 34 + r3 = r3 ^ simd_shuffle_xor(r7, (ushort)8); // 35 + r7 = r7 + r1 + select(0xc53b542eu, 0xe10c2c95u, ((sel >> 2u) & 1u) != 0u); // 36 + r5 = r5 ^ r7; // 37 + r2 = r2 | r1; // 38 + r1 = mulhi(r1, r0); // 39 + r6 = rotl_imm(r6, 19u); // 40 + r4 = mulhi(r4, r6); // 41 + r6 = r6 - r0; // 42 + r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r4 = r4 ^ dataset[r2 & MASK]; // 44 + r1 = r1 ^ r3; // 45 + r7 = r7 ^ dataset[r0 & MASK]; // 46 + r3 = r3 ^ dataset[r1 & MASK]; // 47 + r5 = r5 * r3; // 48 + r1 = r1 - r5; // 49 + r2 = rotl_imm(r2, 8u); // 50 + r1 = r1 + r5 + select(0xa900fec4u, 0x77b9bd43u, ((sel >> 23u) & 1u) != 0u); // 51 + r4 = r4 ^ dataset[r7 & MASK]; // 52 + r2 = r2 - r7; // 53 + r4 = r4 ^ r0; // 54 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 55 + r2 = r2 ^ dataset[r4 & MASK]; // 56 + r0 = r1 * r4 + r0; // 57 + r3 = r3 ^ dataset[r5 & MASK]; // 58 + r5 = r5 | r6; // 59 + r6 = r5 * r7 + r6; // 60 + r4 = rotl_imm(r4, 28u); // 61 + r5 = mulhi(r5, r0); // 62 + r3 = r3 ^ dataset[r6 & MASK]; // 63 } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program_bound.metal b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program_bound.metal index bc806a1e..2f021814 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program_bound.metal +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/program_bound.metal @@ -41,69 +41,69 @@ kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 - r7 = r7 ^ r0; // 1 - r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 - r4 = r4 - r1; // 3 - r2 = r0 * r4 + r2; // 4 - r4 = rotl_imm(r4, 9u); // 5 - r0 = rotr_var(r0, r2); // 6 - r6 = r6 ^ dataset[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 - r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 - r1 = r1 ^ dataset[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 - r7 = r7 ^ dataset[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 - r7 = r7 ^ dataset[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 - r5 = r5 * r4; // 12 - r7 = r7 ^ dataset[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 - r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 - r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 - r2 = r2 | r7; // 16 - r0 = rotr_var(r0, r6); // 17 - r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 - r7 = rotl_imm(r7, 24u); // 19 - r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 - r2 = r2 | r6; // 21 - r6 = r5 * r4 + r6; // 22 - r1 = r1 | r0; // 23 - r6 = r6 ^ r1; // 24 - r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 - r7 = rotr_var(r7, r0); // 26 - r4 = r5 * r7 + r4; // 27 - r3 = r2 * r4 + r3; // 28 - r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 - r2 = r2 ^ dataset[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 - r1 = r1 ^ dataset[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 - r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 - r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 - r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 - r2 = mulhi(r2, r5); // 35 - r4 = r4 ^ r2; // 36 - r6 = r6 * r5; // 37 - r7 = r7 ^ r0; // 38 - r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 - r2 = rotr_var(r2, r3); // 40 - r7 = r7 - r0; // 41 - r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 - r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 - r0 = r0 ^ dataset[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 - r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 - r3 = r3 ^ dataset[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 - r6 = r6 ^ dataset[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 - r4 = mulhi(r4, r2); // 48 - r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 - r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 - r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 - r5 = r5 ^ dataset[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 - r6 = rotl_imm(r6, 12u); // 53 - r3 = rotl_imm(r3, 12u); // 54 - r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 - r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 - r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 - r5 = r5 ^ dataset[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 - r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 - r3 = mulhi(r3, r2); // 60 - r6 = r6 | r4; // 61 - r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 - r3 = r3 ^ dataset[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 + r2 = r2 ^ simd_shuffle_xor(r0, (ushort)4); // 1 + r3 = r3 + r2 + select(0x2cccb6cau, 0x642e66dbu, ((sel >> 10u) & 1u) != 0u); // 2 + r0 = rotl_imm(r0, 19u); // 3 + r7 = rotr_var(r7, r6); // 4 + r7 = r7 + r4 + select(0xee02465fu, 0xc1535555u, ((sel >> 21u) & 1u) != 0u); // 5 + r1 = mulhi(r1, r7); // 6 + r4 = r4 ^ dataset[r2 & MASK]; // 7 + r7 = r7 ^ dataset[r4 & MASK]; // 8 + r0 = r0 ^ dataset[r3 & MASK]; // 9 + r5 = r5 ^ dataset[r1 & MASK]; // 10 + r1 = r1 ^ dataset[r5 & MASK]; // 11 + r3 = mulhi(r3, r5); // 12 + r1 = r1 ^ dataset[r3 & MASK]; // 13 + r0 = r0 - r3; // 14 + r5 = r1 * r3 + r5; // 15 + r6 = mulhi(r6, r1); // 16 + r5 = r5 + r2 + select(0x697b3d00u, 0x8b965b57u, ((sel >> 28u) & 1u) != 0u); // 17 + r0 = mulhi(r0, r6); // 18 + r5 = rotr_var(r5, r3); // 19 + r5 = mulhi(r5, r2); // 20 + r1 = r1 + r0 + select(0xebcf247au, 0x6d7e8d05u, ((sel >> 1u) & 1u) != 0u); // 21 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 22 + r1 = mulhi(r1, r5); // 23 + r2 = r2 - r5; // 24 + r7 = r7 + r4 + select(0x08ffa6c7u, 0x699ef1bbu, ((sel >> 2u) & 1u) != 0u); // 25 + r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 26 + r7 = r7 + r1 + select(0xe60fea84u, 0xb4ead2fbu, ((sel >> 14u) & 1u) != 0u); // 27 + r3 = r3 + r1 + select(0x65c76dabu, 0x8f30d21du, ((sel >> 6u) & 1u) != 0u); // 28 + r2 = r2 ^ dataset[r1 & MASK]; // 29 + r5 = r5 ^ dataset[r7 & MASK]; // 30 + r2 = r2 ^ dataset[r5 & MASK]; // 31 + r1 = r1 ^ simd_shuffle_xor(r7, (ushort)4); // 32 + r4 = r5 * r7 + r4; // 33 + r4 = r4 + r2 + select(0x0480debeu, 0xc7ce690cu, ((sel >> 21u) & 1u) != 0u); // 34 + r3 = r3 ^ simd_shuffle_xor(r7, (ushort)8); // 35 + r7 = r7 + r1 + select(0xc53b542eu, 0xe10c2c95u, ((sel >> 2u) & 1u) != 0u); // 36 + r5 = r5 ^ r7; // 37 + r2 = r2 | r1; // 38 + r1 = mulhi(r1, r0); // 39 + r6 = rotl_imm(r6, 19u); // 40 + r4 = mulhi(r4, r6); // 41 + r6 = r6 - r0; // 42 + r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r4 = r4 ^ dataset[r2 & MASK]; // 44 + r1 = r1 ^ r3; // 45 + r7 = r7 ^ dataset[r0 & MASK]; // 46 + r3 = r3 ^ dataset[r1 & MASK]; // 47 + r5 = r5 * r3; // 48 + r1 = r1 - r5; // 49 + r2 = rotl_imm(r2, 8u); // 50 + r1 = r1 + r5 + select(0xa900fec4u, 0x77b9bd43u, ((sel >> 23u) & 1u) != 0u); // 51 + r4 = r4 ^ dataset[r7 & MASK]; // 52 + r2 = r2 - r7; // 53 + r4 = r4 ^ r0; // 54 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 55 + r2 = r2 ^ dataset[r4 & MASK]; // 56 + r0 = r1 * r4 + r0; // 57 + r3 = r3 ^ dataset[r5 & MASK]; // 58 + r5 = r5 | r6; // 59 + r6 = r5 * r7 + r6; // 60 + r4 = rotl_imm(r4, 28u); // 61 + r5 = mulhi(r5, r0); // 62 + r3 = r3 ^ dataset[r6 & MASK]; // 63 } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.h b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.h index 9f27cdee..e111bcdd 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.h +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.h @@ -1,5 +1,5 @@ // Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. -// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset #pragma once #ifdef __cplusplus #include @@ -11,29 +11,29 @@ static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { { // base nonce 0 - 0xc478aa7b014b314full, 0x5f3b248ea1746690ull, 0x8b8e8d9ea2b748aaull, 0x5feb8eb98d85f482ull, 0x45c41509215309bdull, 0x5e2867e2fe2eed4aull, 0x8aab5a828a90a606ull, 0x570e96ddd777c289ull, - 0x6286e93f7c85d849ull, 0x566c00af118a621aull, 0x5a90210268f469cfull, 0x485984b2c57fc4c3ull, 0x1b42cd7bf2052143ull, 0x61754006818ac84bull, 0x5ab1a0391203c228ull, 0x75085f9f4fec3a81ull, - 0x2d02e728ee7f7b3cull, 0x2bc86c904068d09eull, 0x5e41fedd34afbd06ull, 0xa7792d807213f5b5ull, 0xe22d665d69e17c16ull, 0xb52880c283c7da56ull, 0xdb005f50a716a881ull, 0xea72f35c9f5309bbull, - 0x240401c19d2cab92ull, 0xc83d663994524ce8ull, 0x771c1e871be79867ull, 0xda09af3bd47125f5ull, 0x64d4b67009887d73ull, 0xd6373f5d4afd9c07ull, 0x61ef84e034a884ddull, 0x14c268b894bcd1a4ull + 0x212c6442b51e87aeull, 0xc374795c00839331ull, 0xb6036a220a98f4b3ull, 0xeb8b8013e637367bull, 0xdb5866e9b73930fdull, 0xf3f3d01f46e90333ull, 0x9d913991ab8ed428ull, 0x7ccb1d8fa100a800ull, + 0x3cf45ba44f09a0feull, 0x91acf48ef1a63082ull, 0x6ea46c69fb082f99ull, 0x581f0218977a9d72ull, 0x9a4623a5c62ddf2dull, 0xab6eb5e768f0feb4ull, 0x07b70bdccf8aca12ull, 0xd666311ae5e4311eull, + 0x53114757d669f0a4ull, 0xbd5d6ace87ce2ce4ull, 0xfb712015e8189192ull, 0xa32cec81103e134bull, 0x83f3d18c3289c124ull, 0xfe29f1984b132b3dull, 0xc9ffcf4e3774497aull, 0xac99c9243dc63809ull, + 0xd78a7e8217a32f3cull, 0xab81ad63d242fc31ull, 0x0e4c30b7e00024afull, 0xce014289fff6778dull, 0x64a1292e2a8b4a91ull, 0xd5b8c90e681d7e3aull, 0x06078117673030fdull, 0x51bf77b280173930ull }, { // base nonce 4096 - 0x613fdff51675cbc5ull, 0x33542a1c13240331ull, 0xc12327f1c985c157ull, 0x5983d76c84850677ull, 0x07b876d962db9aeeull, 0x061556b249a2f4edull, 0x7a6a864253cbb666ull, 0x202a4c3f7caf4842ull, - 0xaf47fb1b6f8fd897ull, 0xed86312481032b66ull, 0xe10287d2dc43f393ull, 0x4366d593f75c8a9eull, 0xc265f45fc11562f9ull, 0x23a5ae8634848667ull, 0xb75271a5e58e2e07ull, 0xe2dca00457ff11eeull, - 0x12610d35f18ee235ull, 0x13c1d91de3a5590dull, 0x7af3bbc2e82ce300ull, 0x2725a442d7e70923ull, 0x3938551cfcce0b5eull, 0xb525335b7e13a279ull, 0xf2951f26f6130dcdull, 0x807acf761deff319ull, - 0xafcb894ac5e8cba9ull, 0x1408e0631222a04dull, 0x43a6f79feff89569ull, 0xba577b357fd3b1fcull, 0x424ce7a84318939full, 0xef8f70968010b59eull, 0x8c8a5bcfb16c5a97ull, 0xf2aae270614f2559ull + 0x3c797978566b5950ull, 0x7c759e60185b6411ull, 0x199136b84028d653ull, 0x0ad7b60cb722da50ull, 0x93e5fd443b85acc5ull, 0x4a0fadca2f0b7dc6ull, 0x91b5f2478c1fbd4full, 0xe3debc1f1cdc889aull, + 0x9c39b1635a6aec09ull, 0xbeaea0ab408df45aull, 0x080283b6f52a494aull, 0x9acae9a878c16f2eull, 0xc37dc4e098fee807ull, 0xf3daf9ff09a2c0e5ull, 0xfff82c3572c8f362ull, 0xd96414dfa0b4bd51ull, + 0xecf77498c26cc5abull, 0x72945c7a2e52a90cull, 0xe5b6749102690fa4ull, 0x90eddb84d13a2b9full, 0x6e9186a56dc002c1ull, 0xfa2047ec0ae61fe3ull, 0xa8cd4efd68b7eff5ull, 0xd23b650a6345bf73ull, + 0xe1141a33ea09bae1ull, 0x0e9aae812f5d762aull, 0x9ae3811dcc68797full, 0x0a4dab24c1efff6full, 0xa22d476bc42b9064ull, 0xc7a16d6f59414a2eull, 0x5aff92ecae652329ull, 0x96a903eb9a0ca390ull }, { // base nonce 1000000 - 0xc3b0f31bc9c9a941ull, 0x3dc9db6098337122ull, 0x88ed65e7030bf6e7ull, 0xc3afffd7ef432358ull, 0x4d63429d0816cad6ull, 0xb606c57fc50dfd1bull, 0xbdb00eeb9bd26950ull, 0x379d208ba4c59b68ull, - 0x1b16aa85bffae547ull, 0xf7985ad46430cf69ull, 0x8b5c01051cc55d79ull, 0x0aae8efd1fad2942ull, 0x1256bc0bdfa48f58ull, 0xe657eab8150aa145ull, 0xef6dad537975a2c8ull, 0x805fbcfbccd6710cull, - 0x40be9f4eb86765c6ull, 0x84a5731b7e4ecdacull, 0xd728fb6b057d6ef5ull, 0xa40663757a846c1bull, 0xa38a545e91fd339cull, 0x31629cfa5d2d5d6bull, 0x5d0f4f101daa520full, 0xe57bb2de8033df0full, - 0x892443887a68e483ull, 0xe5066fc7e6b374bfull, 0x5f443c99c6d0f851ull, 0xeaa61aa7109c0fe5ull, 0x9b4a43a24c80dfe6ull, 0x69ad31dea437a76eull, 0x7cb849f7209c482cull, 0x0c42712849a48773ull + 0xd5a8da0568df8ee7ull, 0x68cb69c04208285aull, 0xe3e0c57190a809dbull, 0x87ba27d76aa356d5ull, 0x5a30616d4b8bfcf2ull, 0x66f5d2c31997fd24ull, 0x0447779e26a2854aull, 0x1225eb67933b2faeull, + 0xde78d94be53c669bull, 0x02a3fe00eb77c8cdull, 0x4fd029ba4cfc526aull, 0x2ef8ea603a0463d6ull, 0x345cff9b465b6f12ull, 0x03bb80d5a700b7feull, 0xf7da33ca2094ae26ull, 0xb3adaaa8383ad2e9ull, + 0x68d5eafde2924d62ull, 0xeab53a1b039835e0ull, 0xfd04e678835cb646ull, 0xf25b86a707f333c0ull, 0xa3bb24e6981917deull, 0x791d38b29dd6055aull, 0xfd18f01f064bdbcaull, 0xee051af1de1b70f9ull, + 0x246bfe79acd966eaull, 0xbcb8105b8a73efedull, 0x9b7e8c6fcc25fa92ull, 0xa0ffe2d9e4717892ull, 0x22f52e2324035114ull, 0xcdac38103479920dull, 0x49477ffa08ac4a6cull, 0xf8ca84a1a5d78cf5ull } }; // Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). static const uint32_t IGNEUM_DS_HEAD[16] = { - 0xafe80d67u, 0xb9fbd029u, 0x7155550bu, 0x32965d99u, 0x6c79f193u, 0x95139ad9u, 0x71bdb5ddu, 0x7de50b28u, - 0xf4980f73u, 0x01fd5739u, 0x5b410497u, 0xe4c84638u, 0x08d1d374u, 0x29058a56u, 0xee171b93u, 0xa4fed894u + 0xafe80d67u, 0xb9fbd029u, 0x6c79f193u, 0x95139ad9u, 0x96310affu, 0x4609f8b1u, 0x75279e63u, 0x28235be1u, + 0x47b17dcbu, 0x718e0ef2u, 0xa52588c8u, 0xa8bf49d5u, 0x19cf243eu, 0x5ec8905eu, 0xa4851f66u, 0xaf9cd9f3u }; static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; static const uint32_t IGNEUM_DS_LAST = 0xe6a99c7au; @@ -43,7 +43,7 @@ static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u }; static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { - 0xaade6dc9u, 0x1da7efe9u, 0xc41ae56eu, 0xf25480a3u, 0x0d628857u, 0xc506bba7u, 0xfd9b5dc6u, 0xac36cef7u, 0xabb0c725u, 0x2169d0a5u, 0x79f374c8u, 0xd4517909u, 0x105cc0a8u, 0xa5c081f8u, 0x5d91aef0u, 0x13c5930au, 0x9d49f3fcu, 0x9da43ab7u, 0x28bc6d8fu, 0x135b0b0bu, 0x428a8bd8u, 0xc9f90868u, 0x83328bcfu, 0xb02ef21eu, 0xee8098cdu, 0xf0c365feu, 0x32138523u, 0x26c4c59au, 0xd11deb96u, 0x9e1cd011u, 0x0a83dcd4u, 0x6fbf8ce3u, 0x7bf6dd56u, 0xf079569eu, 0x98d970acu, 0xd6feefdfu, 0xbe12559bu, 0x90e64549u, 0x81238e75u, 0x66b80a1du, 0x6637db36u, 0x6682fda0u, 0x0b0b6b0fu, 0xdebcaa14u, 0x78b382b9u, 0xa92cda07u, 0x693ae44bu, 0x13d96cceu, 0x0ad96728u, 0x4615abe5u, 0xc8ef2953u, 0x226c9b04u, 0xea5fe9b4u, 0xa57e1812u, 0x0cfc72c0u, 0x35dfac56u, 0x8d0ae5a7u, 0xfd635e70u, 0xf14b2d3fu, 0x56fb5ff4u, 0x2340e755u, 0x4a4a0aadu, 0x1abd16edu, 0xa7790ffeu + 0x57642b58u, 0xc279baddu, 0xcbccbaadu, 0x32cce392u, 0x71fdb4c6u, 0xf5a268ceu, 0x3ca1d676u, 0x7977b03du, 0xb73a6cb1u, 0xe773c5c9u, 0x2533a3f1u, 0xd7c48638u, 0xfc80830au, 0xab3874e9u, 0xc093812eu, 0xb71e49d6u, 0x0e151e7bu, 0x395af161u, 0x7156ef4eu, 0x9202463fu, 0x562e88c8u, 0xb9b0c5b3u, 0xe3f6102fu, 0xe3a94cbbu, 0x9e26c494u, 0x47ace326u, 0xe20ea113u, 0xce4eefa1u, 0x97a41cc9u, 0xa6d51cb5u, 0x71f3df27u, 0x115df551u, 0x20e273b8u, 0x50bf99ffu, 0xc9b1ae13u, 0x342160c6u, 0xc1b8eb36u, 0x9b11737bu, 0x4b73d48fu, 0x455e9b9eu, 0xe5213a1cu, 0xf7049bc3u, 0x0a026f93u, 0x156a8a99u, 0x1029fe6cu, 0x4f29d205u, 0x273315edu, 0x483e85a2u, 0xec802349u, 0x76023cccu, 0xe37e8135u, 0x0881d277u, 0x56ba9c69u, 0x31ad3b78u, 0xc8ad9363u, 0x8647e3c0u, 0x12e78b02u, 0x2d3a1b5fu, 0xa75e9390u, 0xbe0cebd8u, 0x759af86du, 0x9b4bd567u, 0xd272d0aeu, 0x7a18d82eu }; // Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. static const uint32_t IGNEUM_CACHE_HEAD[16] = { diff --git a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.json b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.json index fa018775..1bc9a0b5 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.json +++ b/proto-cuda/packs-ca2-mixer/mx4-devnet-epoch0/vectors.json @@ -5,31 +5,31 @@ "dataset_log2_words": 28, "mask": "0x0fffffff", "lanes": 32, - "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", + "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", "warps": [ {"base_nonce": 0, "expected": [ - "0xc478aa7b014b314f", "0x5f3b248ea1746690", "0x8b8e8d9ea2b748aa", "0x5feb8eb98d85f482", "0x45c41509215309bd", "0x5e2867e2fe2eed4a", "0x8aab5a828a90a606", "0x570e96ddd777c289", - "0x6286e93f7c85d849", "0x566c00af118a621a", "0x5a90210268f469cf", "0x485984b2c57fc4c3", "0x1b42cd7bf2052143", "0x61754006818ac84b", "0x5ab1a0391203c228", "0x75085f9f4fec3a81", - "0x2d02e728ee7f7b3c", "0x2bc86c904068d09e", "0x5e41fedd34afbd06", "0xa7792d807213f5b5", "0xe22d665d69e17c16", "0xb52880c283c7da56", "0xdb005f50a716a881", "0xea72f35c9f5309bb", - "0x240401c19d2cab92", "0xc83d663994524ce8", "0x771c1e871be79867", "0xda09af3bd47125f5", "0x64d4b67009887d73", "0xd6373f5d4afd9c07", "0x61ef84e034a884dd", "0x14c268b894bcd1a4" + "0x212c6442b51e87ae", "0xc374795c00839331", "0xb6036a220a98f4b3", "0xeb8b8013e637367b", "0xdb5866e9b73930fd", "0xf3f3d01f46e90333", "0x9d913991ab8ed428", "0x7ccb1d8fa100a800", + "0x3cf45ba44f09a0fe", "0x91acf48ef1a63082", "0x6ea46c69fb082f99", "0x581f0218977a9d72", "0x9a4623a5c62ddf2d", "0xab6eb5e768f0feb4", "0x07b70bdccf8aca12", "0xd666311ae5e4311e", + "0x53114757d669f0a4", "0xbd5d6ace87ce2ce4", "0xfb712015e8189192", "0xa32cec81103e134b", "0x83f3d18c3289c124", "0xfe29f1984b132b3d", "0xc9ffcf4e3774497a", "0xac99c9243dc63809", + "0xd78a7e8217a32f3c", "0xab81ad63d242fc31", "0x0e4c30b7e00024af", "0xce014289fff6778d", "0x64a1292e2a8b4a91", "0xd5b8c90e681d7e3a", "0x06078117673030fd", "0x51bf77b280173930" ]}, {"base_nonce": 4096, "expected": [ - "0x613fdff51675cbc5", "0x33542a1c13240331", "0xc12327f1c985c157", "0x5983d76c84850677", "0x07b876d962db9aee", "0x061556b249a2f4ed", "0x7a6a864253cbb666", "0x202a4c3f7caf4842", - "0xaf47fb1b6f8fd897", "0xed86312481032b66", "0xe10287d2dc43f393", "0x4366d593f75c8a9e", "0xc265f45fc11562f9", "0x23a5ae8634848667", "0xb75271a5e58e2e07", "0xe2dca00457ff11ee", - "0x12610d35f18ee235", "0x13c1d91de3a5590d", "0x7af3bbc2e82ce300", "0x2725a442d7e70923", "0x3938551cfcce0b5e", "0xb525335b7e13a279", "0xf2951f26f6130dcd", "0x807acf761deff319", - "0xafcb894ac5e8cba9", "0x1408e0631222a04d", "0x43a6f79feff89569", "0xba577b357fd3b1fc", "0x424ce7a84318939f", "0xef8f70968010b59e", "0x8c8a5bcfb16c5a97", "0xf2aae270614f2559" + "0x3c797978566b5950", "0x7c759e60185b6411", "0x199136b84028d653", "0x0ad7b60cb722da50", "0x93e5fd443b85acc5", "0x4a0fadca2f0b7dc6", "0x91b5f2478c1fbd4f", "0xe3debc1f1cdc889a", + "0x9c39b1635a6aec09", "0xbeaea0ab408df45a", "0x080283b6f52a494a", "0x9acae9a878c16f2e", "0xc37dc4e098fee807", "0xf3daf9ff09a2c0e5", "0xfff82c3572c8f362", "0xd96414dfa0b4bd51", + "0xecf77498c26cc5ab", "0x72945c7a2e52a90c", "0xe5b6749102690fa4", "0x90eddb84d13a2b9f", "0x6e9186a56dc002c1", "0xfa2047ec0ae61fe3", "0xa8cd4efd68b7eff5", "0xd23b650a6345bf73", + "0xe1141a33ea09bae1", "0x0e9aae812f5d762a", "0x9ae3811dcc68797f", "0x0a4dab24c1efff6f", "0xa22d476bc42b9064", "0xc7a16d6f59414a2e", "0x5aff92ecae652329", "0x96a903eb9a0ca390" ]}, {"base_nonce": 1000000, "expected": [ - "0xc3b0f31bc9c9a941", "0x3dc9db6098337122", "0x88ed65e7030bf6e7", "0xc3afffd7ef432358", "0x4d63429d0816cad6", "0xb606c57fc50dfd1b", "0xbdb00eeb9bd26950", "0x379d208ba4c59b68", - "0x1b16aa85bffae547", "0xf7985ad46430cf69", "0x8b5c01051cc55d79", "0x0aae8efd1fad2942", "0x1256bc0bdfa48f58", "0xe657eab8150aa145", "0xef6dad537975a2c8", "0x805fbcfbccd6710c", - "0x40be9f4eb86765c6", "0x84a5731b7e4ecdac", "0xd728fb6b057d6ef5", "0xa40663757a846c1b", "0xa38a545e91fd339c", "0x31629cfa5d2d5d6b", "0x5d0f4f101daa520f", "0xe57bb2de8033df0f", - "0x892443887a68e483", "0xe5066fc7e6b374bf", "0x5f443c99c6d0f851", "0xeaa61aa7109c0fe5", "0x9b4a43a24c80dfe6", "0x69ad31dea437a76e", "0x7cb849f7209c482c", "0x0c42712849a48773" + "0xd5a8da0568df8ee7", "0x68cb69c04208285a", "0xe3e0c57190a809db", "0x87ba27d76aa356d5", "0x5a30616d4b8bfcf2", "0x66f5d2c31997fd24", "0x0447779e26a2854a", "0x1225eb67933b2fae", + "0xde78d94be53c669b", "0x02a3fe00eb77c8cd", "0x4fd029ba4cfc526a", "0x2ef8ea603a0463d6", "0x345cff9b465b6f12", "0x03bb80d5a700b7fe", "0xf7da33ca2094ae26", "0xb3adaaa8383ad2e9", + "0x68d5eafde2924d62", "0xeab53a1b039835e0", "0xfd04e678835cb646", "0xf25b86a707f333c0", "0xa3bb24e6981917de", "0x791d38b29dd6055a", "0xfd18f01f064bdbca", "0xee051af1de1b70f9", + "0x246bfe79acd966ea", "0xbcb8105b8a73efed", "0x9b7e8c6fcc25fa92", "0xa0ffe2d9e4717892", "0x22f52e2324035114", "0xcdac38103479920d", "0x49477ffa08ac4a6c", "0xf8ca84a1a5d78cf5" ]} ], - "dataset_head": ["0xafe80d67", "0xb9fbd029", "0x7155550b", "0x32965d99", "0x6c79f193", "0x95139ad9", "0x71bdb5dd", "0x7de50b28", "0xf4980f73", "0x01fd5739", "0x5b410497", "0xe4c84638", "0x08d1d374", "0x29058a56", "0xee171b93", "0xa4fed894"], + "dataset_head": ["0xafe80d67", "0xb9fbd029", "0x6c79f193", "0x95139ad9", "0x96310aff", "0x4609f8b1", "0x75279e63", "0x28235be1", "0x47b17dcb", "0x718e0ef2", "0xa52588c8", "0xa8bf49d5", "0x19cf243e", "0x5ec8905e", "0xa4851f66", "0xaf9cd9f3"], "dataset_last_index": 268435455, "dataset_last": "0xe6a99c7a", - "dataset_samples": [{"index": 59471966, "value": "0xaade6dc9"}, {"index": 217795994, "value": "0x1da7efe9"}, {"index": 208353206, "value": "0xc41ae56e"}, {"index": 42483309, "value": "0xf25480a3"}, {"index": 172547758, "value": "0x0d628857"}, {"index": 148076330, "value": "0xc506bba7"}, {"index": 183853158, "value": "0xfd9b5dc6"}, {"index": 214389424, "value": "0xac36cef7"}, {"index": 267488061, "value": "0xabb0c725"}, {"index": 169781097, "value": "0x2169d0a5"}, {"index": 184093494, "value": "0x79f374c8"}, {"index": 153880993, "value": "0xd4517909"}, {"index": 84977930, "value": "0x105cc0a8"}, {"index": 46426879, "value": "0xa5c081f8"}, {"index": 3093825, "value": "0x5d91aef0"}, {"index": 225364072, "value": "0x13c5930a"}, {"index": 44593546, "value": "0x9d49f3fc"}, {"index": 260713159, "value": "0x9da43ab7"}, {"index": 168250303, "value": "0x28bc6d8f"}, {"index": 52384140, "value": "0x135b0b0b"}, {"index": 223401610, "value": "0x428a8bd8"}, {"index": 45554030, "value": "0xc9f90868"}, {"index": 95410555, "value": "0x83328bcf"}, {"index": 175039924, "value": "0xb02ef21e"}, {"index": 79171087, "value": "0xee8098cd"}, {"index": 267580473, "value": "0xf0c365fe"}, {"index": 24168642, "value": "0x32138523"}, {"index": 37981670, "value": "0x26c4c59a"}, {"index": 171551130, "value": "0xd11deb96"}, {"index": 195559979, "value": "0x9e1cd011"}, {"index": 204611762, "value": "0x0a83dcd4"}, {"index": 140997658, "value": "0x6fbf8ce3"}, {"index": 138925853, "value": "0x7bf6dd56"}, {"index": 86637313, "value": "0xf079569e"}, {"index": 20736778, "value": "0x98d970ac"}, {"index": 219665210, "value": "0xd6feefdf"}, {"index": 160430336, "value": "0xbe12559b"}, {"index": 264654675, "value": "0x90e64549"}, {"index": 8013395, "value": "0x81238e75"}, {"index": 228945585, "value": "0x66b80a1d"}, {"index": 213884386, "value": "0x6637db36"}, {"index": 104419827, "value": "0x6682fda0"}, {"index": 44185464, "value": "0x0b0b6b0f"}, {"index": 142737231, "value": "0xdebcaa14"}, {"index": 99284897, "value": "0x78b382b9"}, {"index": 132475900, "value": "0xa92cda07"}, {"index": 61861762, "value": "0x693ae44b"}, {"index": 132056166, "value": "0x13d96cce"}, {"index": 262388043, "value": "0x0ad96728"}, {"index": 91878046, "value": "0x4615abe5"}, {"index": 117353561, "value": "0xc8ef2953"}, {"index": 124768597, "value": "0x226c9b04"}, {"index": 71352993, "value": "0xea5fe9b4"}, {"index": 190698941, "value": "0xa57e1812"}, {"index": 46055428, "value": "0x0cfc72c0"}, {"index": 55281366, "value": "0x35dfac56"}, {"index": 165145231, "value": "0x8d0ae5a7"}, {"index": 106810753, "value": "0xfd635e70"}, {"index": 171985651, "value": "0xf14b2d3f"}, {"index": 232085256, "value": "0x56fb5ff4"}, {"index": 159510492, "value": "0x2340e755"}, {"index": 40072060, "value": "0x4a4a0aad"}, {"index": 209107596, "value": "0x1abd16ed"}, {"index": 39023794, "value": "0xa7790ffe"}], + "dataset_samples": [{"index": 59471966, "value": "0x57642b58"}, {"index": 217795994, "value": "0xc279badd"}, {"index": 208353206, "value": "0xcbccbaad"}, {"index": 42483309, "value": "0x32cce392"}, {"index": 172547758, "value": "0x71fdb4c6"}, {"index": 148076330, "value": "0xf5a268ce"}, {"index": 183853158, "value": "0x3ca1d676"}, {"index": 214389424, "value": "0x7977b03d"}, {"index": 267488061, "value": "0xb73a6cb1"}, {"index": 169781097, "value": "0xe773c5c9"}, {"index": 184093494, "value": "0x2533a3f1"}, {"index": 153880993, "value": "0xd7c48638"}, {"index": 84977930, "value": "0xfc80830a"}, {"index": 46426879, "value": "0xab3874e9"}, {"index": 3093825, "value": "0xc093812e"}, {"index": 225364072, "value": "0xb71e49d6"}, {"index": 44593546, "value": "0x0e151e7b"}, {"index": 260713159, "value": "0x395af161"}, {"index": 168250303, "value": "0x7156ef4e"}, {"index": 52384140, "value": "0x9202463f"}, {"index": 223401610, "value": "0x562e88c8"}, {"index": 45554030, "value": "0xb9b0c5b3"}, {"index": 95410555, "value": "0xe3f6102f"}, {"index": 175039924, "value": "0xe3a94cbb"}, {"index": 79171087, "value": "0x9e26c494"}, {"index": 267580473, "value": "0x47ace326"}, {"index": 24168642, "value": "0xe20ea113"}, {"index": 37981670, "value": "0xce4eefa1"}, {"index": 171551130, "value": "0x97a41cc9"}, {"index": 195559979, "value": "0xa6d51cb5"}, {"index": 204611762, "value": "0x71f3df27"}, {"index": 140997658, "value": "0x115df551"}, {"index": 138925853, "value": "0x20e273b8"}, {"index": 86637313, "value": "0x50bf99ff"}, {"index": 20736778, "value": "0xc9b1ae13"}, {"index": 219665210, "value": "0x342160c6"}, {"index": 160430336, "value": "0xc1b8eb36"}, {"index": 264654675, "value": "0x9b11737b"}, {"index": 8013395, "value": "0x4b73d48f"}, {"index": 228945585, "value": "0x455e9b9e"}, {"index": 213884386, "value": "0xe5213a1c"}, {"index": 104419827, "value": "0xf7049bc3"}, {"index": 44185464, "value": "0x0a026f93"}, {"index": 142737231, "value": "0x156a8a99"}, {"index": 99284897, "value": "0x1029fe6c"}, {"index": 132475900, "value": "0x4f29d205"}, {"index": 61861762, "value": "0x273315ed"}, {"index": 132056166, "value": "0x483e85a2"}, {"index": 262388043, "value": "0xec802349"}, {"index": 91878046, "value": "0x76023ccc"}, {"index": 117353561, "value": "0xe37e8135"}, {"index": 124768597, "value": "0x0881d277"}, {"index": 71352993, "value": "0x56ba9c69"}, {"index": 190698941, "value": "0x31ad3b78"}, {"index": 46055428, "value": "0xc8ad9363"}, {"index": 55281366, "value": "0x8647e3c0"}, {"index": 165145231, "value": "0x12e78b02"}, {"index": 106810753, "value": "0x2d3a1b5f"}, {"index": 171985651, "value": "0xa75e9390"}, {"index": 232085256, "value": "0xbe0cebd8"}, {"index": 159510492, "value": "0x759af86d"}, {"index": 40072060, "value": "0x9b4bd567"}, {"index": 209107596, "value": "0xd272d0ae"}, {"index": 39023794, "value": "0x7a18d82e"}], "cache_head": ["0xebd9055c", "0x7eaf6b21", "0x9b610855", "0x134a8562", "0xb10059ba", "0x7f459e58", "0x8f3c7873", "0x3523e609", "0x75b8f4ca", "0x750d31b4", "0x0dae0782", "0x26f10015", "0x7ba87a41", "0x0efd8543", "0x691d6368", "0x8929d967"], "cache_last_line": ["0x51474edf", "0xc3cfba93", "0xf21454cf", "0x9b79baac", "0x4d4fce23", "0xd701dfd5", "0x37357ab3", "0x1be693fa", "0xa701cb3b", "0x7467c620", "0x428184e8", "0xf4010df0", "0xe33aa88f", "0xc78d6d62", "0xae9ba9c0", "0xf4bba97e"], "cache_fnv1a64": "0x448274a57f508cbc" diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/program.h b/proto-cuda/packs-ca2-mixer/mx4-genesis/program.h index f70b83cc..b4fa96b1 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-genesis/program.h +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/program.h @@ -13,9 +13,9 @@ #define IGNEUM_SEED_STRING "igneum-genesis" #define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973" -#define IGNEUM_GENERATOR 3 +#define IGNEUM_GENERATOR 2 #define IGNEUM_PROGRAM_ATTEMPT 0 -#define IGNEUM_PROGRAM_ID 0xe323b9dcaf283a6full +#define IGNEUM_PROGRAM_ID 0x951b89584750bd75ull #define IGNEUM_DAY_STRING "2026-10-03" #define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033" #define IGNEUM_DAY0 0x3067619fu @@ -28,9 +28,6 @@ #define IGNEUM_LOADS_PER_HASH 128 #define IGNEUM_WIDE_LOADS_PER_HASH 0 #define IGNEUM_OP_MIX "load=16 add=8 shfl=8 xor=6 mad=5 mul=5 mulhi=5 sub=4 rotl=3 rotr=3 or=1" -// Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that -// runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). -#define IGNEUM_PROGRAM_CLASS "v3" // Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item // derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. #define IGNEUM_LOAD_CLASS "mx4" diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/program.json b/proto-cuda/packs-ca2-mixer/mx4-genesis/program.json index 0971d8d7..8b247276 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-genesis/program.json +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/program.json @@ -1,8 +1,8 @@ { "format": "igneum-program-pack-3", - "generator": 3, + "generator": 2, "attempt": 0, - "program_id": "0xe323b9dcaf283a6f", + "program_id": "0x951b89584750bd75", "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", "dataset_mode": "memory-hard", "seed": "igneum-genesis", @@ -15,7 +15,6 @@ "iterations": 8, "instruction_count": 64, "loads_per_hash": 128, - "program_class": "v3", "load_class": "mx4", "mixer_mult": 4, "cache_growth": true, diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.h b/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.h index 6ed53fb1..f25b26e1 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.h +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.h @@ -1,5 +1,5 @@ // Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. -// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset #pragma once #ifdef __cplusplus #include diff --git a/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.json b/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.json index 0667ecf1..32b421bf 100644 --- a/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.json +++ b/proto-cuda/packs-ca2-mixer/mx4-genesis/vectors.json @@ -5,7 +5,7 @@ "dataset_log2_words": 28, "mask": "0x0fffffff", "lanes": 32, - "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", + "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", "warps": [ {"base_nonce": 0, "expected": [ "0x63acd2d273f475ba", "0xe929c78b34b80d4b", "0x0b1011cb19982558", "0x1457a0df5497aa11", "0x957d0f3bb71d98fb", "0xac16901e6e6f6057", "0x8ea1c6279f4b177a", "0xf28146e60bd08ba9", diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cl b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cl index e4f3bb63..f2cccc4a 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cl +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cl @@ -155,8 +155,12 @@ static inline void mh_item(__global const uint* cache, uint t, uint* s) { } for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u)); } -// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. -static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 12 13 of w. +static inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 12u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x00000fffu) | ((w >> 13u) << 12u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 12u) << 13u) | (w & 0x00000fffu) | (((j >> 2u) & 1u) << 12u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } // Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. // The same constants as memhard.h in this pack (one emitter, three dialects). @@ -169,8 +173,7 @@ __kernel void igneum_build(__global uint* ds, __global const uint* cache, uint n if (t < nItems) { uint s[16]; mh_item(cache, t, s); - __global uint* d = ds + ((ulong)t * 16u); - for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; } } @@ -200,69 +203,69 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl - r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add - r0 = rotl_imm(r0, 19u); // 3 rotl - r7 = rotr_var(r7, r6); // 4 rotr - r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add - r1 = mul_hi(r1, r7); // 6 mulhi - r4 = r4 ^ ds[r2 & mask]; // 7 load - r7 = r7 ^ ds[r4 & mask]; // 8 load - r0 = r0 ^ ds[r3 & mask]; // 9 load - r5 = r5 ^ ds[r1 & mask]; // 10 load - r1 = r1 ^ ds[r5 & mask]; // 11 load - r3 = mul_hi(r3, r5); // 12 mulhi - r1 = r1 ^ ds[r3 & mask]; // 13 load - r0 = r0 - r3; // 14 sub - r5 = r1 * r3 + r5; // 15 mad - r6 = mul_hi(r6, r1); // 16 mulhi - r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add - r0 = mul_hi(r0, r6); // 18 mulhi - r5 = rotr_var(r5, r3); // 19 rotr - r5 = mul_hi(r5, r2); // 20 mulhi - r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add - r1 = mul_hi(r1, r5); // 23 mulhi - r2 = r2 - r5; // 24 sub - r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add - { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl - r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add - r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add - r2 = r2 ^ ds[r1 & mask]; // 29 load - r5 = r5 ^ ds[r7 & mask]; // 30 load - r2 = r2 ^ ds[r5 & mask]; // 31 load - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl - r4 = r5 * r7 + r4; // 33 mad - r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl - r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add - r5 = r5 ^ r7; // 37 xor - r2 = r2 | r1; // 38 or - r1 = mul_hi(r1, r0); // 39 mulhi - r6 = rotl_imm(r6, 19u); // 40 rotl - r4 = mul_hi(r4, r6); // 41 mulhi - r6 = r6 - r0; // 42 sub - { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl - r4 = r4 ^ ds[r2 & mask]; // 44 load - r1 = r1 ^ r3; // 45 xor - r7 = r7 ^ ds[r0 & mask]; // 46 load - r3 = r3 ^ ds[r1 & mask]; // 47 load - r5 = r5 * r3; // 48 mul - r1 = r1 - r5; // 49 sub - r2 = rotl_imm(r2, 8u); // 50 rotl - r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add - r4 = r4 ^ ds[r7 & mask]; // 52 load - r2 = r2 - r7; // 53 sub - r4 = r4 ^ r0; // 54 xor - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add - r2 = r2 ^ ds[r4 & mask]; // 56 load - r0 = r1 * r4 + r0; // 57 mad - r3 = r3 ^ ds[r5 & mask]; // 58 load - r5 = r5 | r6; // 59 or - r6 = r5 * r7 + r6; // 60 mad - r4 = rotl_imm(r4, 28u); // 61 rotl - r5 = mul_hi(r5, r0); // 62 mulhi - r3 = r3 ^ ds[r6 & mask]; // 63 load + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cu b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cu index c129edee..2a042276 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cu +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel.cu @@ -36,8 +36,7 @@ __global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItem if (t < nItems) { uint32_t s[16]; mh_item(cache, t, s); - uint32_t* d = ds + (size_t)t * 16u; - for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i]; + for (uint32_t i = 0u; i < 16u; ++i) ds[(size_t)mh_addr(t, i)] = s[i]; } } @@ -60,69 +59,69 @@ __global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonc for (uint32_t it = 0u; it < 8u; ++it) { uint32_t sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 4); // 1 shfl - r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add - r0 = rotl_imm(r0, 19u); // 3 rotl - r7 = rotr_var(r7, r6); // 4 rotr - r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add - r1 = __umulhi(r1, r7); // 6 mulhi - r4 = r4 ^ ds[r2 & mask]; // 7 load - r7 = r7 ^ ds[r4 & mask]; // 8 load - r0 = r0 ^ ds[r3 & mask]; // 9 load - r5 = r5 ^ ds[r1 & mask]; // 10 load - r1 = r1 ^ ds[r5 & mask]; // 11 load - r3 = __umulhi(r3, r5); // 12 mulhi - r1 = r1 ^ ds[r3 & mask]; // 13 load - r0 = r0 - r3; // 14 sub - r5 = r1 * r3 + r5; // 15 mad - r6 = __umulhi(r6, r1); // 16 mulhi - r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add - r0 = __umulhi(r0, r6); // 18 mulhi - r5 = rotr_var(r5, r3); // 19 rotr - r5 = __umulhi(r5, r2); // 20 mulhi - r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add - r1 = __umulhi(r1, r5); // 23 mulhi - r2 = r2 - r5; // 24 sub - r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add - r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 26 shfl - r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add - r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add - r2 = r2 ^ ds[r1 & mask]; // 29 load - r5 = r5 ^ ds[r7 & mask]; // 30 load - r2 = r2 ^ ds[r5 & mask]; // 31 load - r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 32 shfl - r4 = r5 * r7 + r4; // 33 mad - r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add - r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r7, 8); // 35 shfl - r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add - r5 = r5 ^ r7; // 37 xor - r2 = r2 | r1; // 38 or - r1 = __umulhi(r1, r0); // 39 mulhi - r6 = rotl_imm(r6, 19u); // 40 rotl - r4 = __umulhi(r4, r6); // 41 mulhi - r6 = r6 - r0; // 42 sub - r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl - r4 = r4 ^ ds[r2 & mask]; // 44 load - r1 = r1 ^ r3; // 45 xor - r7 = r7 ^ ds[r0 & mask]; // 46 load - r3 = r3 ^ ds[r1 & mask]; // 47 load - r5 = r5 * r3; // 48 mul - r1 = r1 - r5; // 49 sub - r2 = rotl_imm(r2, 8u); // 50 rotl - r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add - r4 = r4 ^ ds[r7 & mask]; // 52 load - r2 = r2 - r7; // 53 sub - r4 = r4 ^ r0; // 54 xor - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add - r2 = r2 ^ ds[r4 & mask]; // 56 load - r0 = r1 * r4 + r0; // 57 mad - r3 = r3 ^ ds[r5 & mask]; // 58 load - r5 = r5 | r6; // 59 or - r6 = r5 * r7 + r6; // 60 mad - r4 = rotl_imm(r4, 28u); // 61 rotl - r5 = __umulhi(r5, r0); // 62 mulhi - r3 = r3 ^ ds[r6 & mask]; // 63 load + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load } uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cl b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cl index 51b5d85d..d4dedee8 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cl +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cl @@ -155,8 +155,12 @@ static inline void mh_item(__global const uint* cache, uint t, uint* s) { } for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u)); } -// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. -static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 12 13 of w. +static inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 12u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +static inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x00000fffu) | ((w >> 13u) << 12u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +static inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 12u) << 13u) | (w & 0x00000fffu) | (((j >> 2u) & 1u) << 12u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } // Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item. // The same constants as memhard.h in this pack (one emitter, three dialects). @@ -169,8 +173,7 @@ __kernel void igneum_build(__global uint* ds, __global const uint* cache, uint n if (t < nItems) { uint s[16]; mh_item(cache, t, s); - __global uint* d = ds + ((ulong)t * 16u); - for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + for (uint i = 0u; i < 16u; ++i) ds[(ulong)mh_addr(t, i)] = s[i]; } } @@ -200,69 +203,69 @@ IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl - r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add - r0 = rotl_imm(r0, 19u); // 3 rotl - r7 = rotr_var(r7, r6); // 4 rotr - r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add - r1 = mul_hi(r1, r7); // 6 mulhi - r4 = r4 ^ ds[r2 & mask]; // 7 load - r7 = r7 ^ ds[r4 & mask]; // 8 load - r0 = r0 ^ ds[r3 & mask]; // 9 load - r5 = r5 ^ ds[r1 & mask]; // 10 load - r1 = r1 ^ ds[r5 & mask]; // 11 load - r3 = mul_hi(r3, r5); // 12 mulhi - r1 = r1 ^ ds[r3 & mask]; // 13 load - r0 = r0 - r3; // 14 sub - r5 = r1 * r3 + r5; // 15 mad - r6 = mul_hi(r6, r1); // 16 mulhi - r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add - r0 = mul_hi(r0, r6); // 18 mulhi - r5 = rotr_var(r5, r3); // 19 rotr - r5 = mul_hi(r5, r2); // 20 mulhi - r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add - r1 = mul_hi(r1, r5); // 23 mulhi - r2 = r2 - r5; // 24 sub - r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add - { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl - r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add - r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add - r2 = r2 ^ ds[r1 & mask]; // 29 load - r5 = r5 ^ ds[r7 & mask]; // 30 load - r2 = r2 ^ ds[r5 & mask]; // 31 load - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl - r4 = r5 * r7 + r4; // 33 mad - r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl - r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add - r5 = r5 ^ r7; // 37 xor - r2 = r2 | r1; // 38 or - r1 = mul_hi(r1, r0); // 39 mulhi - r6 = rotl_imm(r6, 19u); // 40 rotl - r4 = mul_hi(r4, r6); // 41 mulhi - r6 = r6 - r0; // 42 sub - { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl - r4 = r4 ^ ds[r2 & mask]; // 44 load - r1 = r1 ^ r3; // 45 xor - r7 = r7 ^ ds[r0 & mask]; // 46 load - r3 = r3 ^ ds[r1 & mask]; // 47 load - r5 = r5 * r3; // 48 mul - r1 = r1 - r5; // 49 sub - r2 = rotl_imm(r2, 8u); // 50 rotl - r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add - r4 = r4 ^ ds[r7 & mask]; // 52 load - r2 = r2 - r7; // 53 sub - r4 = r4 ^ r0; // 54 xor - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add - r2 = r2 ^ ds[r4 & mask]; // 56 load - r0 = r1 * r4 + r0; // 57 mad - r3 = r3 ^ ds[r5 & mask]; // 58 load - r5 = r5 | r6; // 59 or - r6 = r5 * r7 + r6; // 60 mad - r4 = rotl_imm(r4, 28u); // 61 rotl - r5 = mul_hi(r5, r0); // 62 mulhi - r3 = r3 ^ ds[r6 & mask]; // 63 load + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); @@ -303,69 +306,69 @@ IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulon for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - { uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 1 shfl - r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add - r0 = rotl_imm(r0, 19u); // 3 rotl - r7 = rotr_var(r7, r6); // 4 rotr - r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add - r1 = mul_hi(r1, r7); // 6 mulhi - r4 = r4 ^ ds[r2 & mask]; // 7 load - r7 = r7 ^ ds[r4 & mask]; // 8 load - r0 = r0 ^ ds[r3 & mask]; // 9 load - r5 = r5 ^ ds[r1 & mask]; // 10 load - r1 = r1 ^ ds[r5 & mask]; // 11 load - r3 = mul_hi(r3, r5); // 12 mulhi - r1 = r1 ^ ds[r3 & mask]; // 13 load - r0 = r0 - r3; // 14 sub - r5 = r1 * r3 + r5; // 15 mad - r6 = mul_hi(r6, r1); // 16 mulhi - r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add - r0 = mul_hi(r0, r6); // 18 mulhi - r5 = rotr_var(r5, r3); // 19 rotr - r5 = mul_hi(r5, r2); // 20 mulhi - r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add - r1 = mul_hi(r1, r5); // 23 mulhi - r2 = r2 - r5; // 24 sub - r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add - { uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r3 = r3 ^ t_; } // 26 shfl - r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add - r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add - r2 = r2 ^ ds[r1 & mask]; // 29 load - r5 = r5 ^ ds[r7 & mask]; // 30 load - r2 = r2 ^ ds[r5 & mask]; // 31 load - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r1 = r1 ^ t_; } // 32 shfl - r4 = r5 * r7 + r4; // 33 mad - r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add - { uint t_; IGNEUM_SHFL_XOR(t_, r7, 8u); r3 = r3 ^ t_; } // 35 shfl - r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add - r5 = r5 ^ r7; // 37 xor - r2 = r2 | r1; // 38 or - r1 = mul_hi(r1, r0); // 39 mulhi - r6 = rotl_imm(r6, 19u); // 40 rotl - r4 = mul_hi(r4, r6); // 41 mulhi - r6 = r6 - r0; // 42 sub - { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 43 shfl - r4 = r4 ^ ds[r2 & mask]; // 44 load - r1 = r1 ^ r3; // 45 xor - r7 = r7 ^ ds[r0 & mask]; // 46 load - r3 = r3 ^ ds[r1 & mask]; // 47 load - r5 = r5 * r3; // 48 mul - r1 = r1 - r5; // 49 sub - r2 = rotl_imm(r2, 8u); // 50 rotl - r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add - r4 = r4 ^ ds[r7 & mask]; // 52 load - r2 = r2 - r7; // 53 sub - r4 = r4 ^ r0; // 54 xor - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add - r2 = r2 ^ ds[r4 & mask]; // 56 load - r0 = r1 * r4 + r0; // 57 mad - r3 = r3 ^ ds[r5 & mask]; // 58 load - r5 = r5 | r6; // 59 or - r6 = r5 * r7 + r6; // 60 mad - r4 = rotl_imm(r4, 28u); // 61 rotl - r5 = mul_hi(r5, r0); // 62 mulhi - r3 = r3 ^ ds[r6 & mask]; // 63 load + r7 = r7 ^ r0; // 1 xor + { uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r3 = r3 ^ t_; } // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r6 = r6 ^ t_; } // 14 shfl + { uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r3 = r3 ^ t_; } // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = mul_hi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + { uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r7 = r7 ^ t_; } // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = mul_hi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + { uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = mul_hi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + { uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cu b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cu index 9a46b748..af900376 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cu +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/kernel_bound.cu @@ -36,69 +36,69 @@ __global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t ba for (uint32_t it = 0u; it < 8u; ++it) { uint32_t sel = r0; r4 = r4 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x5810667au : 0xea86e152u); // 0 add - r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 4); // 1 shfl - r3 = r3 + r2 + ((((sel >> 10u) & 1u) != 0u) ? 0x642e66dbu : 0x2cccb6cau); // 2 add - r0 = rotl_imm(r0, 19u); // 3 rotl - r7 = rotr_var(r7, r6); // 4 rotr - r7 = r7 + r4 + ((((sel >> 21u) & 1u) != 0u) ? 0xc1535555u : 0xee02465fu); // 5 add - r1 = __umulhi(r1, r7); // 6 mulhi - r4 = r4 ^ ds[r2 & mask]; // 7 load - r7 = r7 ^ ds[r4 & mask]; // 8 load - r0 = r0 ^ ds[r3 & mask]; // 9 load - r5 = r5 ^ ds[r1 & mask]; // 10 load - r1 = r1 ^ ds[r5 & mask]; // 11 load - r3 = __umulhi(r3, r5); // 12 mulhi - r1 = r1 ^ ds[r3 & mask]; // 13 load - r0 = r0 - r3; // 14 sub - r5 = r1 * r3 + r5; // 15 mad - r6 = __umulhi(r6, r1); // 16 mulhi - r5 = r5 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0x8b965b57u : 0x697b3d00u); // 17 add - r0 = __umulhi(r0, r6); // 18 mulhi - r5 = rotr_var(r5, r3); // 19 rotr - r5 = __umulhi(r5, r2); // 20 mulhi - r1 = r1 + r0 + ((((sel >> 1u) & 1u) != 0u) ? 0x6d7e8d05u : 0xebcf247au); // 21 add - r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 22 add - r1 = __umulhi(r1, r5); // 23 mulhi - r2 = r2 - r5; // 24 sub - r7 = r7 + r4 + ((((sel >> 2u) & 1u) != 0u) ? 0x699ef1bbu : 0x08ffa6c7u); // 25 add - r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 26 shfl - r7 = r7 + r1 + ((((sel >> 14u) & 1u) != 0u) ? 0xb4ead2fbu : 0xe60fea84u); // 27 add - r3 = r3 + r1 + ((((sel >> 6u) & 1u) != 0u) ? 0x8f30d21du : 0x65c76dabu); // 28 add - r2 = r2 ^ ds[r1 & mask]; // 29 load - r5 = r5 ^ ds[r7 & mask]; // 30 load - r2 = r2 ^ ds[r5 & mask]; // 31 load - r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 32 shfl - r4 = r5 * r7 + r4; // 33 mad - r4 = r4 + r2 + ((((sel >> 21u) & 1u) != 0u) ? 0xc7ce690cu : 0x0480debeu); // 34 add - r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r7, 8); // 35 shfl - r7 = r7 + r1 + ((((sel >> 2u) & 1u) != 0u) ? 0xe10c2c95u : 0xc53b542eu); // 36 add - r5 = r5 ^ r7; // 37 xor - r2 = r2 | r1; // 38 or - r1 = __umulhi(r1, r0); // 39 mulhi - r6 = rotl_imm(r6, 19u); // 40 rotl - r4 = __umulhi(r4, r6); // 41 mulhi - r6 = r6 - r0; // 42 sub - r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl - r4 = r4 ^ ds[r2 & mask]; // 44 load - r1 = r1 ^ r3; // 45 xor - r7 = r7 ^ ds[r0 & mask]; // 46 load - r3 = r3 ^ ds[r1 & mask]; // 47 load - r5 = r5 * r3; // 48 mul - r1 = r1 - r5; // 49 sub - r2 = rotl_imm(r2, 8u); // 50 rotl - r1 = r1 + r5 + ((((sel >> 23u) & 1u) != 0u) ? 0x77b9bd43u : 0xa900fec4u); // 51 add - r4 = r4 ^ ds[r7 & mask]; // 52 load - r2 = r2 - r7; // 53 sub - r4 = r4 ^ r0; // 54 xor - r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 55 add - r2 = r2 ^ ds[r4 & mask]; // 56 load - r0 = r1 * r4 + r0; // 57 mad - r3 = r3 ^ ds[r5 & mask]; // 58 load - r5 = r5 | r6; // 59 or - r6 = r5 * r7 + r6; // 60 mad - r4 = rotl_imm(r4, 28u); // 61 rotl - r5 = __umulhi(r5, r0); // 62 mulhi - r3 = r3 ^ ds[r6 & mask]; // 63 load + r7 = r7 ^ r0; // 1 xor + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 2 shfl + r4 = r4 - r1; // 3 sub + r2 = r0 * r4 + r2; // 4 mad + r4 = rotl_imm(r4, 9u); // 5 rotl + r0 = rotr_var(r0, r2); // 6 rotr + r6 = r6 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & mask]; // 7 load + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 8 load + r1 = r1 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 9 load + r7 = r7 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 10 load + r7 = r7 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 11 load + r5 = r5 * r4; // 12 mul + r7 = r7 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 13 load + r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 14 shfl + r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 15 shfl + r2 = r2 | r7; // 16 or + r0 = rotr_var(r0, r6); // 17 rotr + r7 = r7 + r5 + ((((sel >> 12u) & 1u) != 0u) ? 0xb9e3577eu : 0xf66e7017u); // 18 add + r7 = rotl_imm(r7, 24u); // 19 rotl + r6 = r6 + r7 + ((((sel >> 23u) & 1u) != 0u) ? 0x8c9f0ef8u : 0x52334d12u); // 20 add + r2 = r2 | r6; // 21 or + r6 = r5 * r4 + r6; // 22 mad + r1 = r1 | r0; // 23 or + r6 = r6 ^ r1; // 24 xor + r2 = r2 + r6 + ((((sel >> 17u) & 1u) != 0u) ? 0x9cec0e12u : 0x659fc3d3u); // 25 add + r7 = rotr_var(r7, r0); // 26 rotr + r4 = r5 * r7 + r4; // 27 mad + r3 = r2 * r4 + r3; // 28 mad + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 29 load + r2 = r2 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & mask]; // 30 load + r1 = r1 ^ ds[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 31 load + r7 = r7 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xe403240eu : 0x070888a8u); // 32 add + r2 = r2 + r0 + ((((sel >> 28u) & 1u) != 0u) ? 0x29701828u : 0xf2e46d55u); // 33 add + r2 = r2 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x343b7aeeu : 0x58f75b87u); // 34 add + r2 = __umulhi(r2, r5); // 35 mulhi + r4 = r4 ^ r2; // 36 xor + r6 = r6 * r5; // 37 mul + r7 = r7 ^ r0; // 38 xor + r7 = r7 + r2 + ((((sel >> 19u) & 1u) != 0u) ? 0x32bbd117u : 0xb8180e9du); // 39 add + r2 = rotr_var(r2, r3); // 40 rotr + r7 = r7 - r0; // 41 sub + r4 = r4 + r3 + ((((sel >> 4u) & 1u) != 0u) ? 0x6df7aed4u : 0x6ced15b7u); // 42 add + r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 43 shfl + r0 = r0 ^ ds[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 44 load + r1 = r1 + r6 + ((((sel >> 14u) & 1u) != 0u) ? 0x83e825bfu : 0xe09f54e9u); // 45 add + r3 = r3 ^ ds[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 46 load + r6 = r6 ^ ds[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 47 load + r4 = __umulhi(r4, r2); // 48 mulhi + r5 = r5 + r0 + ((((sel >> 7u) & 1u) != 0u) ? 0xf572bdb9u : 0xa8bae6dfu); // 49 add + r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 50 shfl + r6 = r6 + r0 + ((((sel >> 19u) & 1u) != 0u) ? 0x11e17c61u : 0x383b9260u); // 51 add + r5 = r5 ^ ds[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 52 load + r6 = rotl_imm(r6, 12u); // 53 rotl + r3 = rotl_imm(r3, 12u); // 54 rotl + r2 = r2 + r1 + ((((sel >> 10u) & 1u) != 0u) ? 0x18d67dbbu : 0xac6be8e3u); // 55 add + r1 = r1 ^ ds[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & mask]; // 56 load + r5 = r5 + r0 + ((((sel >> 12u) & 1u) != 0u) ? 0xa75cd60du : 0xf03673feu); // 57 add + r5 = r5 ^ ds[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & mask]; // 58 load + r1 = r1 + r4 + ((((sel >> 7u) & 1u) != 0u) ? 0x8dfb96bbu : 0xdecd4794u); // 59 add + r3 = __umulhi(r3, r2); // 60 mulhi + r6 = r6 | r4; // 61 or + r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 62 shfl + r3 = r3 ^ ds[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & mask]; // 63 load } uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.h b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.h index b16f7a4b..03fac4c8 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.h +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.h @@ -105,5 +105,9 @@ IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) { } for (uint32_t j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u)); } -// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. -IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 12 13 of w. +IGNEUM_HD uint32_t mh_j(uint32_t w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 12u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +IGNEUM_HD uint32_t mh_t(uint32_t w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x00000fffu) | ((w >> 13u) << 12u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +IGNEUM_HD uint32_t mh_addr(uint32_t t, uint32_t j) { uint32_t w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 12u) << 13u) | (w & 0x00000fffu) | (((j >> 2u) & 1u) << 12u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.metal b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.metal index d9fed7f5..1e2971b7 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.metal +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/memhard.metal @@ -90,8 +90,12 @@ inline void mh_item(device const uint* cache, uint t, thread uint* s) { } for (uint j = 0u; j < 8u; ++j) mh_mixer(s, 0x9E3779B9u * (64u + j + 1u)); } -// dataset[w] without the dataset: derive item w >> 4 and take word w & 15. -inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; } +// Era layout (docs/plans/era-layout.md 1.2): dataset word w holds word mh_j(w) of item mh_t(w); j's bits sit at positions 0 2 12 13 of w. +inline uint mh_j(uint w) { return ((w >> 0u) & 1u) | (((w >> 2u) & 1u) << 1) | (((w >> 12u) & 1u) << 2) | (((w >> 13u) & 1u) << 3); } +inline uint mh_t(uint w) { w = (w & 0x00001fffu) | ((w >> 14u) << 13u); w = (w & 0x00000fffu) | ((w >> 13u) << 12u); w = (w & 0x00000003u) | ((w >> 3u) << 2u); w = (w & 0x00000000u) | ((w >> 1u) << 0u); return w; } +inline uint mh_addr(uint t, uint j) { uint w = t; w = ((w >> 0u) << 1u) | (w & 0x00000000u) | (((j >> 0u) & 1u) << 0u); w = ((w >> 2u) << 3u) | (w & 0x00000003u) | (((j >> 1u) & 1u) << 2u); w = ((w >> 12u) << 13u) | (w & 0x00000fffu) | (((j >> 2u) & 1u) << 12u); w = ((w >> 13u) << 14u) | (w & 0x00001fffu) | (((j >> 3u) & 1u) << 13u); return w; } +// dataset[w] without the dataset: derive item mh_t(w) and take word mh_j(w). +inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, mh_t(w), s); return s[mh_j(w)]; } // One thread per segment (2^16 threads). kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) { @@ -102,6 +106,5 @@ kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* da uint gid [[thread_position_in_grid]]) { uint s[16]; mh_item(cache, gid, s); - device uint* d = dataset + gid * 16u; - for (uint i = 0u; i < 16u; ++i) d[i] = s[i]; + for (uint i = 0u; i < 16u; ++i) dataset[mh_addr(gid, i)] = s[i]; } diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.h b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.h index 3eacb559..5e6e8a13 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.h +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.h @@ -13,9 +13,9 @@ #define IGNEUM_SEED_STRING "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000" #define IGNEUM_SEED_BYTES_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" -#define IGNEUM_GENERATOR 2 +#define IGNEUM_GENERATOR 3 #define IGNEUM_PROGRAM_ATTEMPT 0 -#define IGNEUM_PROGRAM_ID 0xa696c464d9e9649bull +#define IGNEUM_PROGRAM_ID 0x73bcbfe8ccf988f1ull #define IGNEUM_DAY_STRING "bytes:69676e65756d2d6461792ffa50000000000000" #define IGNEUM_DAY_BYTES_HEX "69676e65756d2d6461792ffa50000000000000" #define IGNEUM_DAY0 0xceed56d7u @@ -27,10 +27,14 @@ #define IGNEUM_INSTR_COUNT 64 #define IGNEUM_LOADS_PER_HASH 128 #define IGNEUM_WIDE_LOADS_PER_HASH 0 -#define IGNEUM_OP_MIX "load=16 add=13 mulhi=9 shfl=5 sub=5 mad=4 rotl=4 xor=3 or=2 rotr=2 mul=1" +#define IGNEUM_OP_MIX "load=16 add=15 shfl=6 mad=4 or=4 rotl=4 rotr=4 xor=4 mulhi=3 mul=2 sub=2" +// Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that +// runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). +#define IGNEUM_PROGRAM_CLASS "v3" +#define IGNEUM_ERA_SEED_HEX "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07" // Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item // derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. -#define IGNEUM_LOAD_CLASS "mx8" +#define IGNEUM_LOAD_CLASS "mx8-erad810f22d" #define IGNEUM_CLASS_MIXER_MULT 8 #define IGNEUM_CACHE_GROWTH 1 // 1: cache words = 2^(26 + doublings(day)), doublings = floor(log2(1 + day / 1460)) #define IGNEUM_LOAD_SLOTS 16 @@ -39,6 +43,18 @@ #define IGNEUM_BYTES_PER_HASH 512 #define IGNEUM_FOLD_ROT 11 #define IGNEUM_FOLD_MUL 0x9e3779b1u +// Era layout (5 October 2026, docs/plans/era-layout.md): NOT the lottery hash. Every dataset load reads +// idx = ((rotl(src * STRIDE_MUL, STRIDE_ROT) & window mask) | window offset) & MASK; the window of a load site is the +// dataset, a half or a quarter of it (IGNEUM_ERA_WINDOWS: site:shrink:offset); dataset word w holds word j(w) of item +// t(w) with j's bits at the INTERLEAVE positions (memhard.h: mh_t, mh_j, mh_addr). +#define IGNEUM_ERA_LABEL "d810f22d" +#define IGNEUM_ERA_SEED_WORDS { 0xd810f22du, 0xcf9dc883u, 0x5f3e571fu, 0x2b7d97fcu, 0x810012c4u, 0x002d9798u, 0xa4180c41u, 0xa4562c21u } +#define IGNEUM_ERA_ALLOWED_WIDTHS { 1, 0, 0 } // words, ascending, 0 = unused; one entry pins the width +#define IGNEUM_ERA_WIDTH_WORDS 1 +#define IGNEUM_ERA_STRIDE_MUL 0x9ad30d99u +#define IGNEUM_ERA_STRIDE_ROT 29 +#define IGNEUM_ERA_INTERLEAVE { 0, 2, 12, 13 } +#define IGNEUM_ERA_WINDOWS "7:2:1 8:1:1 9:1:1 10:1:1 11:0:0 13:1:1 29:0:0 30:2:2 31:1:1 44:1:1 46:2:0 47:0:0 52:0:0 56:0:0 58:2:0 63:1:1" // 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h) #define IGNEUM_DATASET_MODE 1 diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.json b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.json index 3f423788..49d433d0 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.json +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.json @@ -1,8 +1,8 @@ { "format": "igneum-program-pack-3", - "generator": 2, + "generator": 3, "attempt": 0, - "program_id": "0xa696c464d9e9649b", + "program_id": "0x73bcbfe8ccf988f1", "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", "dataset_mode": "memory-hard", "seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000", @@ -15,7 +15,9 @@ "iterations": 8, "instruction_count": 64, "loads_per_hash": 128, - "load_class": "mx8", + "program_class": "v3", + "era_seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07", + "load_class": "mx8-erad810f22d", "mixer_mult": 8, "cache_growth": true, "mixer": "class v3 (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): every mixer application of the item derivation is 8 applications with round keys (r * 8 + j + 1) * 0x9E3779B9, the 8 dependent cache reads per item unchanged; cache growth rule option C: cache words = 2^(26 + doublings(day)), dataset words = 2^(genesis_log2 + doublings(day)), doublings(day) = floor(log2(1 + day / 1460)) for day = days since genesis", @@ -24,7 +26,21 @@ "load_width_counts_4_16_64": [16, 0, 0], "bytes_per_hash": 512, "wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots", - "op_mix": {"load": 16, "add": 13, "mulhi": 9, "shfl": 5, "sub": 5, "mad": 4, "rotl": 4, "xor": 3, "or": 2, "rotr": 2, "mul": 1}, + "era": { + "label": "d810f22d", + "seed_words": ["0xd810f22d", "0xcf9dc883", "0x5f3e571f", "0x2b7d97fc", "0x810012c4", "0x002d9798", "0xa4180c41", "0xa4562c21"], + "draw": "docs/plans/era-layout.md 1.1: SplitMix64 seeded with seed_words[0] | seed_words[1] << 32 of seed_words_from_bytes('igneum-era/' || n_le64 || E_n); width = allowed[below(|allowed|)], stride_mul = low32(next()) | 1, stride_rot = 1 + below(31), then four next() draws for a partial Fisher-Yates over positions log2(W)..15 of which 4 - log2(W) are used", + "allowed_widths": [1], + "width_words": 1, + "stride_mul": "0x9ad30d99", + "stride_rot": 29, + "interleave": [0, 2, 12, 13], + "address": "y = rotl(src * stride_mul, stride_rot); k = min(win, D - 26); idx = ((y & (mask >> k)) | ((off & (2^k - 1)) << (D - k))) & mask; a wide load aligns idx down to W words", + "windows": "per instruction, after the width roll: win = below(3), off = low32(next()) & (2^win - 1); used on a load slot (the instruction's win and off fields)", + "dataset_word": "dataset[w] = item(t(w))[j(w)]: j(w) gathers the bits of w at the interleave positions, t(w) is w with those bits removed", + "program_id_suffix": "'era/' || allowed[3] || width_words || stride_mul_le32 || stride_rot_le32 || interleave[4]" + }, + "op_mix": {"load": 16, "add": 15, "shfl": 6, "mad": 4, "or": 4, "rotl": 4, "rotr": 4, "xor": 4, "mulhi": 3, "mul": 2, "sub": 2}, "register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]", "splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16", "iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order", @@ -63,69 +79,69 @@ "word": "dataset[w] = item(w >> 4)[w & 15]" }, "instructions": [ - {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1}, - {"i": 1, "op": "shfl", "dst": 2, "src": 0, "src2": 7, "imm": "0xe3c2f9cb", "imm2": "0xde0bea4e", "rot": 6, "bit": 9, "mask": 4, "width": 1}, - {"i": 2, "op": "add", "dst": 3, "src": 2, "src2": 0, "imm": "0x2cccb6ca", "imm2": "0x642e66db", "rot": 27, "bit": 10, "mask": 2, "width": 1}, - {"i": 3, "op": "rotl", "dst": 0, "src": 2, "src2": 1, "imm": "0xb59e83b2", "imm2": "0x19c26fb9", "rot": 19, "bit": 20, "mask": 4, "width": 1}, - {"i": 4, "op": "rotr", "dst": 7, "src": 6, "src2": 5, "imm": "0x6f055f55", "imm2": "0x550e4ea1", "rot": 31, "bit": 20, "mask": 4, "width": 1}, - {"i": 5, "op": "add", "dst": 7, "src": 4, "src2": 7, "imm": "0xee02465f", "imm2": "0xc1535555", "rot": 31, "bit": 21, "mask": 4, "width": 1}, - {"i": 6, "op": "mulhi", "dst": 1, "src": 7, "src2": 5, "imm": "0x7826a6a7", "imm2": "0x946f7818", "rot": 18, "bit": 21, "mask": 8, "width": 1}, - {"i": 7, "op": "load", "dst": 4, "src": 2, "src2": 0, "imm": "0x5d080878", "imm2": "0xdf885578", "rot": 5, "bit": 18, "mask": 4, "width": 1}, - {"i": 8, "op": "load", "dst": 7, "src": 4, "src2": 1, "imm": "0x875bbb36", "imm2": "0x594a838f", "rot": 24, "bit": 4, "mask": 2, "width": 1}, - {"i": 9, "op": "load", "dst": 0, "src": 3, "src2": 1, "imm": "0xe5e607c2", "imm2": "0xa5cd9f75", "rot": 26, "bit": 28, "mask": 4, "width": 1}, - {"i": 10, "op": "load", "dst": 5, "src": 1, "src2": 3, "imm": "0xea3f7b43", "imm2": "0x10c8d4e7", "rot": 5, "bit": 29, "mask": 8, "width": 1}, - {"i": 11, "op": "load", "dst": 1, "src": 5, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1}, - {"i": 12, "op": "mulhi", "dst": 3, "src": 5, "src2": 7, "imm": "0xbfd5615c", "imm2": "0xd8224ae2", "rot": 21, "bit": 12, "mask": 2, "width": 1}, - {"i": 13, "op": "load", "dst": 1, "src": 3, "src2": 5, "imm": "0x35c07cc5", "imm2": "0xe82db54f", "rot": 7, "bit": 6, "mask": 8, "width": 1}, - {"i": 14, "op": "sub", "dst": 0, "src": 3, "src2": 0, "imm": "0x587ee0f1", "imm2": "0xfd23eefd", "rot": 16, "bit": 21, "mask": 16, "width": 1}, - {"i": 15, "op": "mad", "dst": 5, "src": 1, "src2": 3, "imm": "0x6974dd29", "imm2": "0xc8148960", "rot": 3, "bit": 11, "mask": 2, "width": 1}, - {"i": 16, "op": "mulhi", "dst": 6, "src": 1, "src2": 7, "imm": "0x3072c3c6", "imm2": "0x55ee21f8", "rot": 26, "bit": 1, "mask": 1, "width": 1}, - {"i": 17, "op": "add", "dst": 5, "src": 2, "src2": 2, "imm": "0x697b3d00", "imm2": "0x8b965b57", "rot": 9, "bit": 28, "mask": 1, "width": 1}, - {"i": 18, "op": "mulhi", "dst": 0, "src": 6, "src2": 3, "imm": "0x2910cacb", "imm2": "0x6ac79431", "rot": 7, "bit": 6, "mask": 4, "width": 1}, - {"i": 19, "op": "rotr", "dst": 5, "src": 3, "src2": 0, "imm": "0xed96a94a", "imm2": "0x4c988c10", "rot": 24, "bit": 21, "mask": 8, "width": 1}, - {"i": 20, "op": "mulhi", "dst": 5, "src": 2, "src2": 7, "imm": "0x40d2fc76", "imm2": "0x2f7c7eca", "rot": 13, "bit": 8, "mask": 4, "width": 1}, - {"i": 21, "op": "add", "dst": 1, "src": 0, "src2": 5, "imm": "0xebcf247a", "imm2": "0x6d7e8d05", "rot": 31, "bit": 1, "mask": 16, "width": 1}, - {"i": 22, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1}, - {"i": 23, "op": "mulhi", "dst": 1, "src": 5, "src2": 7, "imm": "0xfdfe72fd", "imm2": "0x735eeb8d", "rot": 30, "bit": 25, "mask": 8, "width": 1}, - {"i": 24, "op": "sub", "dst": 2, "src": 5, "src2": 0, "imm": "0x32cf1258", "imm2": "0xd813deb6", "rot": 30, "bit": 6, "mask": 16, "width": 1}, - {"i": 25, "op": "add", "dst": 7, "src": 4, "src2": 2, "imm": "0x08ffa6c7", "imm2": "0x699ef1bb", "rot": 7, "bit": 2, "mask": 16, "width": 1}, - {"i": 26, "op": "shfl", "dst": 3, "src": 4, "src2": 5, "imm": "0x6f53c70d", "imm2": "0x3357513f", "rot": 3, "bit": 26, "mask": 2, "width": 1}, - {"i": 27, "op": "add", "dst": 7, "src": 1, "src2": 4, "imm": "0xe60fea84", "imm2": "0xb4ead2fb", "rot": 14, "bit": 14, "mask": 1, "width": 1}, - {"i": 28, "op": "add", "dst": 3, "src": 1, "src2": 0, "imm": "0x65c76dab", "imm2": "0x8f30d21d", "rot": 24, "bit": 6, "mask": 1, "width": 1}, - {"i": 29, "op": "load", "dst": 2, "src": 1, "src2": 2, "imm": "0x82fad9a6", "imm2": "0x8c6358db", "rot": 7, "bit": 31, "mask": 2, "width": 1}, - {"i": 30, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x6e947ee0", "imm2": "0xaf9a2dda", "rot": 2, "bit": 30, "mask": 4, "width": 1}, - {"i": 31, "op": "load", "dst": 2, "src": 5, "src2": 1, "imm": "0x608bb7ce", "imm2": "0x4be663db", "rot": 19, "bit": 1, "mask": 16, "width": 1}, - {"i": 32, "op": "shfl", "dst": 1, "src": 7, "src2": 6, "imm": "0x88e52e20", "imm2": "0x77647269", "rot": 20, "bit": 25, "mask": 4, "width": 1}, - {"i": 33, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1}, - {"i": 34, "op": "add", "dst": 4, "src": 2, "src2": 3, "imm": "0x0480debe", "imm2": "0xc7ce690c", "rot": 12, "bit": 21, "mask": 16, "width": 1}, - {"i": 35, "op": "shfl", "dst": 3, "src": 7, "src2": 5, "imm": "0xa73f59de", "imm2": "0x84b9e329", "rot": 21, "bit": 27, "mask": 8, "width": 1}, - {"i": 36, "op": "add", "dst": 7, "src": 1, "src2": 7, "imm": "0xc53b542e", "imm2": "0xe10c2c95", "rot": 21, "bit": 2, "mask": 4, "width": 1}, - {"i": 37, "op": "xor", "dst": 5, "src": 7, "src2": 4, "imm": "0x81cd7b0e", "imm2": "0x21a51823", "rot": 12, "bit": 10, "mask": 2, "width": 1}, - {"i": 38, "op": "or", "dst": 2, "src": 1, "src2": 3, "imm": "0x7894e657", "imm2": "0xf8e4b972", "rot": 18, "bit": 9, "mask": 4, "width": 1}, - {"i": 39, "op": "mulhi", "dst": 1, "src": 0, "src2": 4, "imm": "0xbee8421f", "imm2": "0x070888a8", "rot": 20, "bit": 28, "mask": 4, "width": 1}, - {"i": 40, "op": "rotl", "dst": 6, "src": 1, "src2": 4, "imm": "0x4609857a", "imm2": "0xaeecb156", "rot": 19, "bit": 21, "mask": 1, "width": 1}, - {"i": 41, "op": "mulhi", "dst": 4, "src": 6, "src2": 0, "imm": "0x1a84e1e9", "imm2": "0x9b26bb72", "rot": 28, "bit": 19, "mask": 1, "width": 1}, - {"i": 42, "op": "sub", "dst": 6, "src": 0, "src2": 6, "imm": "0xbf62908e", "imm2": "0xdf03ea88", "rot": 11, "bit": 27, "mask": 16, "width": 1}, - {"i": 43, "op": "shfl", "dst": 6, "src": 3, "src2": 4, "imm": "0xa966241c", "imm2": "0x9c639efa", "rot": 12, "bit": 4, "mask": 4, "width": 1}, - {"i": 44, "op": "load", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1}, - {"i": 45, "op": "xor", "dst": 1, "src": 3, "src2": 6, "imm": "0x006193d0", "imm2": "0xfc2acc3f", "rot": 25, "bit": 1, "mask": 1, "width": 1}, - {"i": 46, "op": "load", "dst": 7, "src": 0, "src2": 6, "imm": "0x4777c4f8", "imm2": "0x3cf0a02f", "rot": 11, "bit": 14, "mask": 4, "width": 1}, - {"i": 47, "op": "load", "dst": 3, "src": 1, "src2": 3, "imm": "0x10692532", "imm2": "0x1a292ea5", "rot": 20, "bit": 23, "mask": 1, "width": 1}, - {"i": 48, "op": "mul", "dst": 5, "src": 3, "src2": 7, "imm": "0xadf5bd13", "imm2": "0xb999de2e", "rot": 23, "bit": 10, "mask": 4, "width": 1}, - {"i": 49, "op": "sub", "dst": 1, "src": 5, "src2": 4, "imm": "0x68ff101e", "imm2": "0xbdaaf46a", "rot": 25, "bit": 23, "mask": 16, "width": 1}, - {"i": 50, "op": "rotl", "dst": 2, "src": 6, "src2": 4, "imm": "0x92d9a412", "imm2": "0x0daf96ea", "rot": 8, "bit": 4, "mask": 4, "width": 1}, - {"i": 51, "op": "add", "dst": 1, "src": 5, "src2": 0, "imm": "0xa900fec4", "imm2": "0x77b9bd43", "rot": 6, "bit": 23, "mask": 2, "width": 1}, - {"i": 52, "op": "load", "dst": 4, "src": 7, "src2": 1, "imm": "0x51392a72", "imm2": "0x99e8bb36", "rot": 11, "bit": 9, "mask": 1, "width": 1}, - {"i": 53, "op": "sub", "dst": 2, "src": 7, "src2": 2, "imm": "0x0ffe2ac7", "imm2": "0x030743df", "rot": 9, "bit": 30, "mask": 16, "width": 1}, - {"i": 54, "op": "xor", "dst": 4, "src": 0, "src2": 4, "imm": "0x9123ff15", "imm2": "0x10c329a7", "rot": 28, "bit": 15, "mask": 2, "width": 1}, - {"i": 55, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1}, - {"i": 56, "op": "load", "dst": 2, "src": 4, "src2": 3, "imm": "0x239de52c", "imm2": "0xf80bae18", "rot": 15, "bit": 24, "mask": 1, "width": 1}, - {"i": 57, "op": "mad", "dst": 0, "src": 1, "src2": 4, "imm": "0x31dede8e", "imm2": "0xd3f619e6", "rot": 29, "bit": 7, "mask": 2, "width": 1}, - {"i": 58, "op": "load", "dst": 3, "src": 5, "src2": 3, "imm": "0x206437d6", "imm2": "0x28d1c290", "rot": 17, "bit": 28, "mask": 4, "width": 1}, - {"i": 59, "op": "or", "dst": 5, "src": 6, "src2": 3, "imm": "0x8fffd674", "imm2": "0x0507903a", "rot": 26, "bit": 27, "mask": 2, "width": 1}, - {"i": 60, "op": "mad", "dst": 6, "src": 5, "src2": 7, "imm": "0xf572bdb9", "imm2": "0xeda2af31", "rot": 21, "bit": 8, "mask": 2, "width": 1}, - {"i": 61, "op": "rotl", "dst": 4, "src": 2, "src2": 7, "imm": "0x84f12ddf", "imm2": "0x81ef22e1", "rot": 28, "bit": 30, "mask": 1, "width": 1}, - {"i": 62, "op": "mulhi", "dst": 5, "src": 0, "src2": 6, "imm": "0xf6bb45ee", "imm2": "0x6bfb632d", "rot": 22, "bit": 0, "mask": 4, "width": 1}, - {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0x50ec702a", "imm2": "0xae6ee96e", "rot": 20, "bit": 25, "mask": 2, "width": 1} + {"i": 0, "op": "add", "dst": 4, "src": 5, "src2": 7, "imm": "0xea86e152", "imm2": "0x5810667a", "rot": 27, "bit": 13, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 1, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xbaab6229", "imm2": "0xed861989", "rot": 26, "bit": 22, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 2, "op": "shfl", "dst": 3, "src": 6, "src2": 2, "imm": "0x5b623116", "imm2": "0xff12e5b2", "rot": 12, "bit": 24, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 3, "op": "sub", "dst": 4, "src": 1, "src2": 1, "imm": "0xe99741c7", "imm2": "0xf5fa5009", "rot": 1, "bit": 21, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 4, "op": "mad", "dst": 2, "src": 0, "src2": 4, "imm": "0x673c2157", "imm2": "0xee02465f", "rot": 20, "bit": 22, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 5, "op": "rotl", "dst": 4, "src": 7, "src2": 7, "imm": "0x946f7818", "imm2": "0x45d3399e", "rot": 9, "bit": 2, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 6, "op": "rotr", "dst": 0, "src": 2, "src2": 4, "imm": "0x5f6a0ed2", "imm2": "0x7043a636", "rot": 19, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 7, "op": "load", "dst": 6, "src": 7, "src2": 1, "imm": "0x5c61dcf7", "imm2": "0x7466aa40", "rot": 19, "bit": 9, "mask": 2, "width": 1, "win": 2, "off": 1}, + {"i": 8, "op": "load", "dst": 1, "src": 4, "src2": 5, "imm": "0x85668475", "imm2": "0xdb8cc483", "rot": 29, "bit": 7, "mask": 4, "width": 1, "win": 1, "off": 1}, + {"i": 9, "op": "load", "dst": 1, "src": 2, "src2": 4, "imm": "0x4454980f", "imm2": "0xebd31581", "rot": 10, "bit": 28, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 10, "op": "load", "dst": 7, "src": 0, "src2": 2, "imm": "0xe075297c", "imm2": "0x5779c44c", "rot": 10, "bit": 22, "mask": 2, "width": 1, "win": 1, "off": 1}, + {"i": 11, "op": "load", "dst": 7, "src": 1, "src2": 6, "imm": "0x65aa4311", "imm2": "0x4fe48ea9", "rot": 15, "bit": 9, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 12, "op": "mul", "dst": 5, "src": 4, "src2": 2, "imm": "0x1383d3ad", "imm2": "0xf3094b29", "rot": 8, "bit": 9, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 13, "op": "load", "dst": 7, "src": 6, "src2": 2, "imm": "0xed8a496f", "imm2": "0x3072c3c6", "rot": 28, "bit": 19, "mask": 8, "width": 1, "win": 1, "off": 1}, + {"i": 14, "op": "shfl", "dst": 6, "src": 5, "src2": 0, "imm": "0x8b965b57", "imm2": "0xcfeca6c1", "rot": 12, "bit": 27, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 15, "op": "shfl", "dst": 3, "src": 5, "src2": 3, "imm": "0x877c7586", "imm2": "0xa9cb2a03", "rot": 2, "bit": 29, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 16, "op": "or", "dst": 2, "src": 7, "src2": 3, "imm": "0xb740221a", "imm2": "0x89d38d6d", "rot": 6, "bit": 31, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 17, "op": "rotr", "dst": 0, "src": 6, "src2": 1, "imm": "0x26f3ad8a", "imm2": "0x27256f15", "rot": 18, "bit": 5, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 18, "op": "add", "dst": 7, "src": 5, "src2": 7, "imm": "0xf66e7017", "imm2": "0xb9e3577e", "rot": 9, "bit": 12, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 19, "op": "rotl", "dst": 7, "src": 0, "src2": 5, "imm": "0x849ae6ee", "imm2": "0x02b358f9", "rot": 24, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 20, "op": "add", "dst": 6, "src": 7, "src2": 6, "imm": "0x52334d12", "imm2": "0x8c9f0ef8", "rot": 11, "bit": 23, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 21, "op": "or", "dst": 2, "src": 6, "src2": 5, "imm": "0xb1871e63", "imm2": "0xb2e40191", "rot": 5, "bit": 13, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 22, "op": "mad", "dst": 6, "src": 5, "src2": 4, "imm": "0x97df29e4", "imm2": "0xe60fea84", "rot": 11, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 23, "op": "or", "dst": 1, "src": 0, "src2": 3, "imm": "0x8f30d21d", "imm2": "0x2df685a0", "rot": 31, "bit": 0, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 24, "op": "xor", "dst": 6, "src": 1, "src2": 2, "imm": "0xd2c4025f", "imm2": "0x5269eb4d", "rot": 31, "bit": 21, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 25, "op": "add", "dst": 2, "src": 6, "src2": 5, "imm": "0x659fc3d3", "imm2": "0x9cec0e12", "rot": 6, "bit": 17, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 26, "op": "rotr", "dst": 7, "src": 0, "src2": 1, "imm": "0xd89ef484", "imm2": "0x20be3846", "rot": 12, "bit": 9, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 27, "op": "mad", "dst": 4, "src": 5, "src2": 7, "imm": "0xb48420ae", "imm2": "0x3d1f2485", "rot": 14, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 28, "op": "mad", "dst": 3, "src": 2, "src2": 4, "imm": "0xc5c46d76", "imm2": "0x700044b5", "rot": 22, "bit": 10, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 29, "op": "load", "dst": 1, "src": 4, "src2": 3, "imm": "0x0fbaf177", "imm2": "0xfff4f2ed", "rot": 20, "bit": 31, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 30, "op": "load", "dst": 2, "src": 3, "src2": 0, "imm": "0xe90eb2e5", "imm2": "0xb0f9eb79", "rot": 27, "bit": 14, "mask": 4, "width": 1, "win": 2, "off": 2}, + {"i": 31, "op": "load", "dst": 1, "src": 5, "src2": 2, "imm": "0x97ba3fc3", "imm2": "0x7894e657", "rot": 3, "bit": 30, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 32, "op": "add", "dst": 7, "src": 2, "src2": 7, "imm": "0x070888a8", "imm2": "0xe403240e", "rot": 2, "bit": 16, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 33, "op": "add", "dst": 2, "src": 0, "src2": 5, "imm": "0xf2e46d55", "imm2": "0x29701828", "rot": 31, "bit": 28, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 34, "op": "add", "dst": 2, "src": 3, "src2": 0, "imm": "0x58f75b87", "imm2": "0x343b7aee", "rot": 12, "bit": 14, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 35, "op": "mulhi", "dst": 2, "src": 5, "src2": 6, "imm": "0x5892a9e6", "imm2": "0xc9824c94", "rot": 19, "bit": 26, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 36, "op": "xor", "dst": 4, "src": 2, "src2": 3, "imm": "0x7b5b5474", "imm2": "0x45cfc5dd", "rot": 17, "bit": 18, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 37, "op": "mul", "dst": 6, "src": 5, "src2": 7, "imm": "0xccf564a5", "imm2": "0x873ad101", "rot": 7, "bit": 11, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 38, "op": "xor", "dst": 7, "src": 0, "src2": 6, "imm": "0xcac8f06d", "imm2": "0x6b97c683", "rot": 18, "bit": 28, "mask": 2, "width": 1, "win": 0, "off": 0}, + {"i": 39, "op": "add", "dst": 7, "src": 2, "src2": 4, "imm": "0xb8180e9d", "imm2": "0x32bbd117", "rot": 23, "bit": 19, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 40, "op": "rotr", "dst": 2, "src": 3, "src2": 0, "imm": "0x2d6070bc", "imm2": "0x68ff101e", "rot": 13, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 41, "op": "sub", "dst": 7, "src": 0, "src2": 2, "imm": "0x0daf96ea", "imm2": "0x36f37be1", "rot": 5, "bit": 0, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 42, "op": "add", "dst": 4, "src": 3, "src2": 6, "imm": "0x6ced15b7", "imm2": "0x6df7aed4", "rot": 19, "bit": 4, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 43, "op": "shfl", "dst": 7, "src": 3, "src2": 5, "imm": "0x8ace05f3", "imm2": "0xd378ec12", "rot": 23, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 44, "op": "load", "dst": 0, "src": 7, "src2": 4, "imm": "0xb0607786", "imm2": "0xc4acabbc", "rot": 13, "bit": 7, "mask": 16, "width": 1, "win": 1, "off": 1}, + {"i": 45, "op": "add", "dst": 1, "src": 6, "src2": 5, "imm": "0xe09f54e9", "imm2": "0x83e825bf", "rot": 23, "bit": 14, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 46, "op": "load", "dst": 3, "src": 1, "src2": 0, "imm": "0x63cc1e4e", "imm2": "0xa1be8118", "rot": 12, "bit": 6, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 47, "op": "load", "dst": 6, "src": 3, "src2": 7, "imm": "0x353f1d79", "imm2": "0x3b2e7456", "rot": 18, "bit": 18, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 48, "op": "mulhi", "dst": 4, "src": 2, "src2": 7, "imm": "0x00d8a3cd", "imm2": "0x231866d2", "rot": 21, "bit": 20, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 49, "op": "add", "dst": 5, "src": 0, "src2": 2, "imm": "0xa8bae6df", "imm2": "0xf572bdb9", "rot": 14, "bit": 7, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 50, "op": "shfl", "dst": 0, "src": 7, "src2": 7, "imm": "0x81ef22e1", "imm2": "0x74438fc5", "rot": 28, "bit": 18, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 51, "op": "add", "dst": 6, "src": 0, "src2": 6, "imm": "0x383b9260", "imm2": "0x11e17c61", "rot": 12, "bit": 19, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 52, "op": "load", "dst": 5, "src": 2, "src2": 2, "imm": "0xfb84f451", "imm2": "0x11cd863e", "rot": 21, "bit": 20, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 53, "op": "rotl", "dst": 6, "src": 5, "src2": 4, "imm": "0xb1a7db6b", "imm2": "0x76686b9b", "rot": 12, "bit": 4, "mask": 16, "width": 1, "win": 0, "off": 0}, + {"i": 54, "op": "rotl", "dst": 3, "src": 6, "src2": 3, "imm": "0x6f981f52", "imm2": "0xd99aeba2", "rot": 12, "bit": 27, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 55, "op": "add", "dst": 2, "src": 1, "src2": 2, "imm": "0xac6be8e3", "imm2": "0x18d67dbb", "rot": 26, "bit": 10, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 56, "op": "load", "dst": 1, "src": 4, "src2": 0, "imm": "0x7e7f6a00", "imm2": "0x6f0747da", "rot": 25, "bit": 28, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 57, "op": "add", "dst": 5, "src": 0, "src2": 4, "imm": "0xf03673fe", "imm2": "0xa75cd60d", "rot": 16, "bit": 12, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 58, "op": "load", "dst": 5, "src": 0, "src2": 2, "imm": "0x227f94a6", "imm2": "0x0e8344f9", "rot": 20, "bit": 10, "mask": 2, "width": 1, "win": 2, "off": 0}, + {"i": 59, "op": "add", "dst": 1, "src": 4, "src2": 3, "imm": "0xdecd4794", "imm2": "0x8dfb96bb", "rot": 21, "bit": 7, "mask": 4, "width": 1, "win": 0, "off": 0}, + {"i": 60, "op": "mulhi", "dst": 3, "src": 2, "src2": 2, "imm": "0x0dd268e0", "imm2": "0x53034ca9", "rot": 1, "bit": 8, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 61, "op": "or", "dst": 6, "src": 4, "src2": 7, "imm": "0x3a45a321", "imm2": "0x9bc59a5f", "rot": 25, "bit": 11, "mask": 1, "width": 1, "win": 0, "off": 0}, + {"i": 62, "op": "shfl", "dst": 5, "src": 4, "src2": 2, "imm": "0x8f229cc1", "imm2": "0xcaac64a2", "rot": 17, "bit": 13, "mask": 8, "width": 1, "win": 0, "off": 0}, + {"i": 63, "op": "load", "dst": 3, "src": 6, "src2": 6, "imm": "0xb2574178", "imm2": "0xcbcc798d", "rot": 28, "bit": 0, "mask": 16, "width": 1, "win": 1, "off": 1} ] } diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.metal b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.metal index b899adec..8c0a2d1c 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.metal +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program.metal @@ -39,69 +39,69 @@ kernel void igneum_hash(device const uint* dataset [[buffer(0)]], for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 - r2 = r2 ^ simd_shuffle_xor(r0, (ushort)4); // 1 - r3 = r3 + r2 + select(0x2cccb6cau, 0x642e66dbu, ((sel >> 10u) & 1u) != 0u); // 2 - r0 = rotl_imm(r0, 19u); // 3 - r7 = rotr_var(r7, r6); // 4 - r7 = r7 + r4 + select(0xee02465fu, 0xc1535555u, ((sel >> 21u) & 1u) != 0u); // 5 - r1 = mulhi(r1, r7); // 6 - r4 = r4 ^ dataset[r2 & MASK]; // 7 - r7 = r7 ^ dataset[r4 & MASK]; // 8 - r0 = r0 ^ dataset[r3 & MASK]; // 9 - r5 = r5 ^ dataset[r1 & MASK]; // 10 - r1 = r1 ^ dataset[r5 & MASK]; // 11 - r3 = mulhi(r3, r5); // 12 - r1 = r1 ^ dataset[r3 & MASK]; // 13 - r0 = r0 - r3; // 14 - r5 = r1 * r3 + r5; // 15 - r6 = mulhi(r6, r1); // 16 - r5 = r5 + r2 + select(0x697b3d00u, 0x8b965b57u, ((sel >> 28u) & 1u) != 0u); // 17 - r0 = mulhi(r0, r6); // 18 - r5 = rotr_var(r5, r3); // 19 - r5 = mulhi(r5, r2); // 20 - r1 = r1 + r0 + select(0xebcf247au, 0x6d7e8d05u, ((sel >> 1u) & 1u) != 0u); // 21 - r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 22 - r1 = mulhi(r1, r5); // 23 - r2 = r2 - r5; // 24 - r7 = r7 + r4 + select(0x08ffa6c7u, 0x699ef1bbu, ((sel >> 2u) & 1u) != 0u); // 25 - r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 26 - r7 = r7 + r1 + select(0xe60fea84u, 0xb4ead2fbu, ((sel >> 14u) & 1u) != 0u); // 27 - r3 = r3 + r1 + select(0x65c76dabu, 0x8f30d21du, ((sel >> 6u) & 1u) != 0u); // 28 - r2 = r2 ^ dataset[r1 & MASK]; // 29 - r5 = r5 ^ dataset[r7 & MASK]; // 30 - r2 = r2 ^ dataset[r5 & MASK]; // 31 - r1 = r1 ^ simd_shuffle_xor(r7, (ushort)4); // 32 - r4 = r5 * r7 + r4; // 33 - r4 = r4 + r2 + select(0x0480debeu, 0xc7ce690cu, ((sel >> 21u) & 1u) != 0u); // 34 - r3 = r3 ^ simd_shuffle_xor(r7, (ushort)8); // 35 - r7 = r7 + r1 + select(0xc53b542eu, 0xe10c2c95u, ((sel >> 2u) & 1u) != 0u); // 36 - r5 = r5 ^ r7; // 37 - r2 = r2 | r1; // 38 - r1 = mulhi(r1, r0); // 39 - r6 = rotl_imm(r6, 19u); // 40 - r4 = mulhi(r4, r6); // 41 - r6 = r6 - r0; // 42 - r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 43 - r4 = r4 ^ dataset[r2 & MASK]; // 44 - r1 = r1 ^ r3; // 45 - r7 = r7 ^ dataset[r0 & MASK]; // 46 - r3 = r3 ^ dataset[r1 & MASK]; // 47 - r5 = r5 * r3; // 48 - r1 = r1 - r5; // 49 - r2 = rotl_imm(r2, 8u); // 50 - r1 = r1 + r5 + select(0xa900fec4u, 0x77b9bd43u, ((sel >> 23u) & 1u) != 0u); // 51 - r4 = r4 ^ dataset[r7 & MASK]; // 52 - r2 = r2 - r7; // 53 - r4 = r4 ^ r0; // 54 - r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 55 - r2 = r2 ^ dataset[r4 & MASK]; // 56 - r0 = r1 * r4 + r0; // 57 - r3 = r3 ^ dataset[r5 & MASK]; // 58 - r5 = r5 | r6; // 59 - r6 = r5 * r7 + r6; // 60 - r4 = rotl_imm(r4, 28u); // 61 - r5 = mulhi(r5, r0); // 62 - r3 = r3 ^ dataset[r6 & MASK]; // 63 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program_bound.metal b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program_bound.metal index 2f021814..bc806a1e 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program_bound.metal +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/program_bound.metal @@ -41,69 +41,69 @@ kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]], for (uint it = 0u; it < 8u; ++it) { uint sel = r0; r4 = r4 + r5 + select(0xea86e152u, 0x5810667au, ((sel >> 13u) & 1u) != 0u); // 0 - r2 = r2 ^ simd_shuffle_xor(r0, (ushort)4); // 1 - r3 = r3 + r2 + select(0x2cccb6cau, 0x642e66dbu, ((sel >> 10u) & 1u) != 0u); // 2 - r0 = rotl_imm(r0, 19u); // 3 - r7 = rotr_var(r7, r6); // 4 - r7 = r7 + r4 + select(0xee02465fu, 0xc1535555u, ((sel >> 21u) & 1u) != 0u); // 5 - r1 = mulhi(r1, r7); // 6 - r4 = r4 ^ dataset[r2 & MASK]; // 7 - r7 = r7 ^ dataset[r4 & MASK]; // 8 - r0 = r0 ^ dataset[r3 & MASK]; // 9 - r5 = r5 ^ dataset[r1 & MASK]; // 10 - r1 = r1 ^ dataset[r5 & MASK]; // 11 - r3 = mulhi(r3, r5); // 12 - r1 = r1 ^ dataset[r3 & MASK]; // 13 - r0 = r0 - r3; // 14 - r5 = r1 * r3 + r5; // 15 - r6 = mulhi(r6, r1); // 16 - r5 = r5 + r2 + select(0x697b3d00u, 0x8b965b57u, ((sel >> 28u) & 1u) != 0u); // 17 - r0 = mulhi(r0, r6); // 18 - r5 = rotr_var(r5, r3); // 19 - r5 = mulhi(r5, r2); // 20 - r1 = r1 + r0 + select(0xebcf247au, 0x6d7e8d05u, ((sel >> 1u) & 1u) != 0u); // 21 - r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 22 - r1 = mulhi(r1, r5); // 23 - r2 = r2 - r5; // 24 - r7 = r7 + r4 + select(0x08ffa6c7u, 0x699ef1bbu, ((sel >> 2u) & 1u) != 0u); // 25 - r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 26 - r7 = r7 + r1 + select(0xe60fea84u, 0xb4ead2fbu, ((sel >> 14u) & 1u) != 0u); // 27 - r3 = r3 + r1 + select(0x65c76dabu, 0x8f30d21du, ((sel >> 6u) & 1u) != 0u); // 28 - r2 = r2 ^ dataset[r1 & MASK]; // 29 - r5 = r5 ^ dataset[r7 & MASK]; // 30 - r2 = r2 ^ dataset[r5 & MASK]; // 31 - r1 = r1 ^ simd_shuffle_xor(r7, (ushort)4); // 32 - r4 = r5 * r7 + r4; // 33 - r4 = r4 + r2 + select(0x0480debeu, 0xc7ce690cu, ((sel >> 21u) & 1u) != 0u); // 34 - r3 = r3 ^ simd_shuffle_xor(r7, (ushort)8); // 35 - r7 = r7 + r1 + select(0xc53b542eu, 0xe10c2c95u, ((sel >> 2u) & 1u) != 0u); // 36 - r5 = r5 ^ r7; // 37 - r2 = r2 | r1; // 38 - r1 = mulhi(r1, r0); // 39 - r6 = rotl_imm(r6, 19u); // 40 - r4 = mulhi(r4, r6); // 41 - r6 = r6 - r0; // 42 - r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 43 - r4 = r4 ^ dataset[r2 & MASK]; // 44 - r1 = r1 ^ r3; // 45 - r7 = r7 ^ dataset[r0 & MASK]; // 46 - r3 = r3 ^ dataset[r1 & MASK]; // 47 - r5 = r5 * r3; // 48 - r1 = r1 - r5; // 49 - r2 = rotl_imm(r2, 8u); // 50 - r1 = r1 + r5 + select(0xa900fec4u, 0x77b9bd43u, ((sel >> 23u) & 1u) != 0u); // 51 - r4 = r4 ^ dataset[r7 & MASK]; // 52 - r2 = r2 - r7; // 53 - r4 = r4 ^ r0; // 54 - r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 55 - r2 = r2 ^ dataset[r4 & MASK]; // 56 - r0 = r1 * r4 + r0; // 57 - r3 = r3 ^ dataset[r5 & MASK]; // 58 - r5 = r5 | r6; // 59 - r6 = r5 * r7 + r6; // 60 - r4 = rotl_imm(r4, 28u); // 61 - r5 = mulhi(r5, r0); // 62 - r3 = r3 ^ dataset[r6 & MASK]; // 63 + r7 = r7 ^ r0; // 1 + r3 = r3 ^ simd_shuffle_xor(r6, (ushort)1); // 2 + r4 = r4 - r1; // 3 + r2 = r0 * r4 + r2; // 4 + r4 = rotl_imm(r4, 9u); // 5 + r0 = rotr_var(r0, r2); // 6 + r6 = r6 ^ dataset[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x04000000u) & MASK]; // 7 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 8 + r1 = r1 ^ dataset[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 9 + r7 = r7 ^ dataset[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 10 + r7 = r7 ^ dataset[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 11 + r5 = r5 * r4; // 12 + r7 = r7 ^ dataset[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 13 + r6 = r6 ^ simd_shuffle_xor(r5, (ushort)16); // 14 + r3 = r3 ^ simd_shuffle_xor(r5, (ushort)1); // 15 + r2 = r2 | r7; // 16 + r0 = rotr_var(r0, r6); // 17 + r7 = r7 + r5 + select(0xf66e7017u, 0xb9e3577eu, ((sel >> 12u) & 1u) != 0u); // 18 + r7 = rotl_imm(r7, 24u); // 19 + r6 = r6 + r7 + select(0x52334d12u, 0x8c9f0ef8u, ((sel >> 23u) & 1u) != 0u); // 20 + r2 = r2 | r6; // 21 + r6 = r5 * r4 + r6; // 22 + r1 = r1 | r0; // 23 + r6 = r6 ^ r1; // 24 + r2 = r2 + r6 + select(0x659fc3d3u, 0x9cec0e12u, ((sel >> 17u) & 1u) != 0u); // 25 + r7 = rotr_var(r7, r0); // 26 + r4 = r5 * r7 + r4; // 27 + r3 = r2 * r4 + r3; // 28 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 29 + r2 = r2 ^ dataset[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x08000000u) & MASK]; // 30 + r1 = r1 ^ dataset[((rotl_imm(r5 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 31 + r7 = r7 + r2 + select(0x070888a8u, 0xe403240eu, ((sel >> 16u) & 1u) != 0u); // 32 + r2 = r2 + r0 + select(0xf2e46d55u, 0x29701828u, ((sel >> 28u) & 1u) != 0u); // 33 + r2 = r2 + r3 + select(0x58f75b87u, 0x343b7aeeu, ((sel >> 14u) & 1u) != 0u); // 34 + r2 = mulhi(r2, r5); // 35 + r4 = r4 ^ r2; // 36 + r6 = r6 * r5; // 37 + r7 = r7 ^ r0; // 38 + r7 = r7 + r2 + select(0xb8180e9du, 0x32bbd117u, ((sel >> 19u) & 1u) != 0u); // 39 + r2 = rotr_var(r2, r3); // 40 + r7 = r7 - r0; // 41 + r4 = r4 + r3 + select(0x6ced15b7u, 0x6df7aed4u, ((sel >> 4u) & 1u) != 0u); // 42 + r7 = r7 ^ simd_shuffle_xor(r3, (ushort)4); // 43 + r0 = r0 ^ dataset[((rotl_imm(r7 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 44 + r1 = r1 + r6 + select(0xe09f54e9u, 0x83e825bfu, ((sel >> 14u) & 1u) != 0u); // 45 + r3 = r3 ^ dataset[((rotl_imm(r1 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 46 + r6 = r6 ^ dataset[((rotl_imm(r3 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 47 + r4 = mulhi(r4, r2); // 48 + r5 = r5 + r0 + select(0xa8bae6dfu, 0xf572bdb9u, ((sel >> 7u) & 1u) != 0u); // 49 + r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 50 + r6 = r6 + r0 + select(0x383b9260u, 0x11e17c61u, ((sel >> 19u) & 1u) != 0u); // 51 + r5 = r5 ^ dataset[((rotl_imm(r2 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 52 + r6 = rotl_imm(r6, 12u); // 53 + r3 = rotl_imm(r3, 12u); // 54 + r2 = r2 + r1 + select(0xac6be8e3u, 0x18d67dbbu, ((sel >> 10u) & 1u) != 0u); // 55 + r1 = r1 ^ dataset[((rotl_imm(r4 * 0x9ad30d99u, 29u) & 0x0fffffffu) | 0x00000000u) & MASK]; // 56 + r5 = r5 + r0 + select(0xf03673feu, 0xa75cd60du, ((sel >> 12u) & 1u) != 0u); // 57 + r5 = r5 ^ dataset[((rotl_imm(r0 * 0x9ad30d99u, 29u) & 0x03ffffffu) | 0x00000000u) & MASK]; // 58 + r1 = r1 + r4 + select(0xdecd4794u, 0x8dfb96bbu, ((sel >> 7u) & 1u) != 0u); // 59 + r3 = mulhi(r3, r2); // 60 + r6 = r6 | r4; // 61 + r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 62 + r3 = r3 ^ dataset[((rotl_imm(r6 * 0x9ad30d99u, 29u) & 0x07ffffffu) | 0x08000000u) & MASK]; // 63 } uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u); uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u); diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.h b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.h index f7899436..acd68d25 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.h +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.h @@ -1,5 +1,5 @@ // Generated by igneum-pow export (generator v2) for seed "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000". Do not edit by hand. -// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset #pragma once #ifdef __cplusplus #include @@ -11,29 +11,29 @@ static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u }; static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = { { // base nonce 0 - 0x731250f1008c6f8eull, 0xb89b396aa5e33597ull, 0xbc96ebcdb47c61e4ull, 0xac1414cd0fb91152ull, 0x1cc2d1847875555eull, 0x071f703c82efff0cull, 0x21147b8c58bc96e4ull, 0xf412422862c643e6ull, - 0xb8a5c330668e9283ull, 0x994558a2b24a6557ull, 0xb2fb321e884a1fedull, 0xa8f928a38622b10eull, 0x9ea4cfcfc2c96d8bull, 0xc082abc94d5dfb18ull, 0x695239a4a852e0f9ull, 0xef804c526240ce15ull, - 0x624303d31812df0dull, 0xcfd82ed4955f140aull, 0x6569ae6a4a436a9aull, 0xc17f46cf926a7a0cull, 0x721fe9c93267d13bull, 0xf09fe97d5ce6bfbbull, 0x455d853d3913d6a3ull, 0x4fbb5f43bd1af7eaull, - 0x864d689410bb8ed0ull, 0xe8c93188d7f02b04ull, 0xebaafb6120560360ull, 0xde81676bff855560ull, 0xd0b3ae8111aab7c3ull, 0xacd97035767867caull, 0x1970c0b030b99c03ull, 0x09fed95e63215205ull + 0xd424577fce4a7a60ull, 0x07a04a71b7dc9e24ull, 0x9f3738f74b5781a0ull, 0x3e14bf6f995474dcull, 0xa3341f1327cededfull, 0xbb8340ed0f96a60aull, 0x26bc9988fddcafe8ull, 0x6711837d288eb5b1ull, + 0x2528efd21aaea539ull, 0x9a9e5e921805ad1aull, 0xf8f603042c024e9dull, 0x2023b9f4d4b69a6full, 0x0f68fa939013c11eull, 0x4b2db89527dd4246ull, 0xe30b45f6467fe6daull, 0x09a4ce72a8670daaull, + 0x9b373bcbd8ac276dull, 0x32409c8adad8122bull, 0xf3c9573d9b139a2eull, 0x6ce5852802493cffull, 0x7ec131a18ffea8d3ull, 0x9eca44ce48fcd206ull, 0x9720d6ef2d21f096ull, 0xff5a9e8b9c875129ull, + 0xf090b543042dcc0bull, 0xbdb38142d8f1866aull, 0xa65a997ce2fe84f9ull, 0x82b38b1f4b0389f3ull, 0x744c612dfa85b844ull, 0x1549edd4ebe3e1a1ull, 0x32528ae5d72962b7ull, 0x5eb43d7efa91792aull }, { // base nonce 4096 - 0x3d4f7e76fa55a96aull, 0xd54574ccb1a27843ull, 0xeb1635ddd5f1e181ull, 0x1e49a6cf24085bd9ull, 0x9f929d79ad731c0eull, 0x5caca654a4bd4f0full, 0x33eb9defb51424b9ull, 0x04f1f3c93663c27eull, - 0xad1c775687a46553ull, 0xc57c600167bc7571ull, 0xaf0b8e2f9a316b22ull, 0xa01088974b376d8eull, 0x14555ccee9288fddull, 0x0d1d576ea396fbe7ull, 0x8cf63b15a58299bfull, 0x49f530db99fd5f01ull, - 0x30324c49b12da61bull, 0xeb6fc871f103ad7bull, 0x8ac3271ce7edfc29ull, 0x2804099c65265affull, 0xc4d9377a427c3337ull, 0x566c1e580b940448ull, 0x790b966beb7bebb3ull, 0xba5da14cc619efe0ull, - 0x5da22ad9ae88fcd7ull, 0x24221e506877ef84ull, 0xfcda89486e6e78caull, 0xfd9a09b3587ba8fbull, 0x4fdb52f5b31ca54aull, 0x9cf4a2730edbf331ull, 0xb28717ae699944bbull, 0x0889c8c6adecb410ull + 0xb1a4dad6dac881e3ull, 0xa7c70bd09b22e232ull, 0xfb717dd0ee8c7b3bull, 0x75d13d70eecb7027ull, 0x37e718c5d2899b3dull, 0x4782272ccca87766ull, 0xf1729c252c3e0938ull, 0x5f0f04471bfcb956ull, + 0x093dcb86bfd512c7ull, 0x7a94acb9059c450cull, 0x331dcdd972d3690full, 0x54af5b1f80d22001ull, 0x4ee4a70cac47cb2bull, 0x99250b8d1cc2c845ull, 0xe4c21f96fc9bc9c6ull, 0x0b2f74431eae748aull, + 0x28477c082bd7ce0bull, 0x97b639e1f7ece650ull, 0xbc0eb237c118abdfull, 0xbf4b66de93c3d605ull, 0xec595dd050696fc7ull, 0xca188fcb2e52bf3bull, 0xce6c1500d389901cull, 0x70d98a68e5629e8bull, + 0xb1a99d60c977035aull, 0xb9ca4f57bd634b62ull, 0xab823071d8714434ull, 0x4a64d6115c4a68c3ull, 0x83b8e36475aa97dbull, 0x76a86a5c0b8eb3c3ull, 0x80b50965cb384920ull, 0xebd51aa25f279b52ull }, { // base nonce 1000000 - 0x377dfe4959f008b5ull, 0xd1bc9ed622919f80ull, 0x7c8c576aebc618bbull, 0x7930d463eceb4becull, 0xd7c8081c372c0e96ull, 0xff093d43f46685c2ull, 0x64d59d5e4abdef5cull, 0xd0b6c0699e0d51f8ull, - 0xbf73d4c2e7d7bb5cull, 0xb461b9770521b1b8ull, 0x224eaf296658b9c0ull, 0x81233334fb928726ull, 0xa10c5ee0aecc8251ull, 0xcd1a7e1b3cb970e7ull, 0x573c61ddaaa8fcccull, 0x67dd7ae97a699881ull, - 0x6df554eae8e0482aull, 0x61f528d7eba4cf89ull, 0xc1f241f1f41fc34dull, 0x25e1b51094413da0ull, 0x9be1d90ff6f51065ull, 0x21fc77cf9089f049ull, 0x1fadecae2996b343ull, 0xbd170b4f84703b24ull, - 0x0a7493c2ebd8bfb5ull, 0xd1d6e43170ced136ull, 0xe87fadf2a2c02a52ull, 0x2f873ddc2cf9f022ull, 0xa3963afdf94f3307ull, 0x3dfff37956161da1ull, 0xa60a7a56137b4231ull, 0xb8abcc39f8cb021aull + 0x0075777e728c0393ull, 0xa7fb5870a6991c5cull, 0xfdb4737b88c1fc4eull, 0x20459d2a43909ba9ull, 0x2d05fcdfd72f04b4ull, 0x33a44106a40eded3ull, 0xc365f536aa848b35ull, 0xe4fba4d51458e902ull, + 0x4d490be32f29d621ull, 0x95c5cd90faa84cfaull, 0xe6b90105cb7bec0dull, 0xaf106086897d202eull, 0x74e0a8d95386cab8ull, 0xd82da2ceda8b3794ull, 0x4efb171e065a3ffaull, 0x350246d8be1ac9c4ull, + 0x4134d472c2ecc1f9ull, 0xf57ea25951f560d3ull, 0xad70814322094688ull, 0x7162d5a0882f8aecull, 0x925135af6dd034f0ull, 0x36d01dcac09c0535ull, 0xae05981d39512765ull, 0x347bf7a59a4788aeull, + 0x39cd25ee0ffadb23ull, 0xbcf8c0280e4db403ull, 0x203323bfd810035full, 0x13a3354a07423e16ull, 0xcb70b4756f46eb2eull, 0x1c9941fe799cf7b6ull, 0x007b2b0035a673aeull, 0x4587d1a7e011792cull } }; // Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455). static const uint32_t IGNEUM_DS_HEAD[16] = { - 0x19c6837fu, 0xdf3adfb8u, 0xc5f0629fu, 0x03e38ae8u, 0xc5de2525u, 0xa34a4906u, 0x898367cbu, 0x35b56808u, - 0xb886e673u, 0x1ee29eceu, 0x0cc36c88u, 0xc984181bu, 0x3d9d15f6u, 0x27c7d52au, 0xc268cf69u, 0x99b3c5c2u + 0x19c6837fu, 0xdf3adfb8u, 0xbd3f6aedu, 0xb8bede0du, 0xc5f0629fu, 0x03e38ae8u, 0xc435a60eu, 0xfadee916u, + 0x827e59afu, 0x8cf592f5u, 0x60286061u, 0x5d9abc1cu, 0xa85d0c39u, 0x4247a100u, 0xd41a5b0du, 0x3f33dc64u }; static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u; static const uint32_t IGNEUM_DS_LAST = 0x8d66c390u; @@ -43,7 +43,7 @@ static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = { 59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u }; static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = { - 0xe53754ceu, 0xc6792663u, 0x0ca9fdceu, 0x96159926u, 0xfca71449u, 0x54522663u, 0xd0160df7u, 0x301be5a9u, 0xb0de07c5u, 0xb8f1c964u, 0xcef19286u, 0xc9c7985du, 0x9db14da9u, 0xda9f746du, 0x8747e5a1u, 0x545158e0u, 0xdf31afbbu, 0x4daa9b4cu, 0x1f0762ffu, 0x80a2d158u, 0xf762ad72u, 0x7ee04a69u, 0xd07df0e9u, 0x87cfc4abu, 0x9de7bb05u, 0xcd214af2u, 0xbd15dc85u, 0x89f48a42u, 0xd49c8a27u, 0xbc60a7e1u, 0xafc36693u, 0x4876871fu, 0x703246c4u, 0xbb823e84u, 0xa13f71a1u, 0xf2dd8540u, 0x74b039d1u, 0x84e8a275u, 0x750744bbu, 0x3f690b5fu, 0x5e75ecbdu, 0x36a11cc7u, 0x65e888cfu, 0x2e8538c8u, 0x14f5fec9u, 0x7329f625u, 0x42ad9261u, 0x120aea25u, 0x5374b09fu, 0x4fa85aa3u, 0xcb856f4eu, 0x526707bau, 0x82d847dau, 0xaed963e5u, 0x1516404bu, 0x2e565dddu, 0x46d1dacfu, 0xf7bb9e4cu, 0x61ae3b87u, 0x9daeed43u, 0xcf6bc86fu, 0x24f111d5u, 0x4356a558u, 0x0890de16u + 0xd088e877u, 0x2f25cbd2u, 0x9e26cf29u, 0xd3b102e0u, 0x3250b4a2u, 0xa0716e94u, 0x6497643cu, 0xa9b78313u, 0x69d951fcu, 0xa1b35f16u, 0xfee8e051u, 0x6098ad8cu, 0x27f0b64eu, 0x5c464cb0u, 0x1a3f0ce6u, 0xdbe10965u, 0x41d9309eu, 0xac9f9678u, 0x03d686a8u, 0x67544111u, 0xa7cad073u, 0x4e9c542du, 0xe7c3c2c7u, 0x9c624803u, 0x71780762u, 0xde7bcf5eu, 0xe319f472u, 0x89a3bc8au, 0xa4a01afbu, 0x72c52455u, 0xe877adf6u, 0xea1e321cu, 0xa518e572u, 0x8526fe55u, 0x8bde9bd3u, 0xe5fd50dfu, 0x3f994c85u, 0x02972af2u, 0x8bad5ffdu, 0xfc0becf6u, 0xc03e2465u, 0x0bbe949eu, 0x1e696b57u, 0x593feba2u, 0x1c0b992au, 0x4905aca0u, 0xf6e70116u, 0x427aa5f2u, 0x128898cfu, 0x6acfe21eu, 0x9a0ae2e4u, 0xc025b697u, 0x91f0a3d0u, 0x053041bbu, 0xf7d767d8u, 0xcb7b7b2bu, 0x53597840u, 0xdc45ee7fu, 0x07484a2eu, 0x635c8369u, 0x09a65a56u, 0xd216baeeu, 0x9cbd726cu, 0x5e566286u }; // Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words. static const uint32_t IGNEUM_CACHE_HEAD[16] = { diff --git a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.json b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.json index 4e8720b3..20e7fde2 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.json +++ b/proto-cuda/packs-ca2-mixer/mx8-devnet-epoch0/vectors.json @@ -5,31 +5,31 @@ "dataset_log2_words": 28, "mask": "0x0fffffff", "lanes": 32, - "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", + "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", "warps": [ {"base_nonce": 0, "expected": [ - "0x731250f1008c6f8e", "0xb89b396aa5e33597", "0xbc96ebcdb47c61e4", "0xac1414cd0fb91152", "0x1cc2d1847875555e", "0x071f703c82efff0c", "0x21147b8c58bc96e4", "0xf412422862c643e6", - "0xb8a5c330668e9283", "0x994558a2b24a6557", "0xb2fb321e884a1fed", "0xa8f928a38622b10e", "0x9ea4cfcfc2c96d8b", "0xc082abc94d5dfb18", "0x695239a4a852e0f9", "0xef804c526240ce15", - "0x624303d31812df0d", "0xcfd82ed4955f140a", "0x6569ae6a4a436a9a", "0xc17f46cf926a7a0c", "0x721fe9c93267d13b", "0xf09fe97d5ce6bfbb", "0x455d853d3913d6a3", "0x4fbb5f43bd1af7ea", - "0x864d689410bb8ed0", "0xe8c93188d7f02b04", "0xebaafb6120560360", "0xde81676bff855560", "0xd0b3ae8111aab7c3", "0xacd97035767867ca", "0x1970c0b030b99c03", "0x09fed95e63215205" + "0xd424577fce4a7a60", "0x07a04a71b7dc9e24", "0x9f3738f74b5781a0", "0x3e14bf6f995474dc", "0xa3341f1327cededf", "0xbb8340ed0f96a60a", "0x26bc9988fddcafe8", "0x6711837d288eb5b1", + "0x2528efd21aaea539", "0x9a9e5e921805ad1a", "0xf8f603042c024e9d", "0x2023b9f4d4b69a6f", "0x0f68fa939013c11e", "0x4b2db89527dd4246", "0xe30b45f6467fe6da", "0x09a4ce72a8670daa", + "0x9b373bcbd8ac276d", "0x32409c8adad8122b", "0xf3c9573d9b139a2e", "0x6ce5852802493cff", "0x7ec131a18ffea8d3", "0x9eca44ce48fcd206", "0x9720d6ef2d21f096", "0xff5a9e8b9c875129", + "0xf090b543042dcc0b", "0xbdb38142d8f1866a", "0xa65a997ce2fe84f9", "0x82b38b1f4b0389f3", "0x744c612dfa85b844", "0x1549edd4ebe3e1a1", "0x32528ae5d72962b7", "0x5eb43d7efa91792a" ]}, {"base_nonce": 4096, "expected": [ - "0x3d4f7e76fa55a96a", "0xd54574ccb1a27843", "0xeb1635ddd5f1e181", "0x1e49a6cf24085bd9", "0x9f929d79ad731c0e", "0x5caca654a4bd4f0f", "0x33eb9defb51424b9", "0x04f1f3c93663c27e", - "0xad1c775687a46553", "0xc57c600167bc7571", "0xaf0b8e2f9a316b22", "0xa01088974b376d8e", "0x14555ccee9288fdd", "0x0d1d576ea396fbe7", "0x8cf63b15a58299bf", "0x49f530db99fd5f01", - "0x30324c49b12da61b", "0xeb6fc871f103ad7b", "0x8ac3271ce7edfc29", "0x2804099c65265aff", "0xc4d9377a427c3337", "0x566c1e580b940448", "0x790b966beb7bebb3", "0xba5da14cc619efe0", - "0x5da22ad9ae88fcd7", "0x24221e506877ef84", "0xfcda89486e6e78ca", "0xfd9a09b3587ba8fb", "0x4fdb52f5b31ca54a", "0x9cf4a2730edbf331", "0xb28717ae699944bb", "0x0889c8c6adecb410" + "0xb1a4dad6dac881e3", "0xa7c70bd09b22e232", "0xfb717dd0ee8c7b3b", "0x75d13d70eecb7027", "0x37e718c5d2899b3d", "0x4782272ccca87766", "0xf1729c252c3e0938", "0x5f0f04471bfcb956", + "0x093dcb86bfd512c7", "0x7a94acb9059c450c", "0x331dcdd972d3690f", "0x54af5b1f80d22001", "0x4ee4a70cac47cb2b", "0x99250b8d1cc2c845", "0xe4c21f96fc9bc9c6", "0x0b2f74431eae748a", + "0x28477c082bd7ce0b", "0x97b639e1f7ece650", "0xbc0eb237c118abdf", "0xbf4b66de93c3d605", "0xec595dd050696fc7", "0xca188fcb2e52bf3b", "0xce6c1500d389901c", "0x70d98a68e5629e8b", + "0xb1a99d60c977035a", "0xb9ca4f57bd634b62", "0xab823071d8714434", "0x4a64d6115c4a68c3", "0x83b8e36475aa97db", "0x76a86a5c0b8eb3c3", "0x80b50965cb384920", "0xebd51aa25f279b52" ]}, {"base_nonce": 1000000, "expected": [ - "0x377dfe4959f008b5", "0xd1bc9ed622919f80", "0x7c8c576aebc618bb", "0x7930d463eceb4bec", "0xd7c8081c372c0e96", "0xff093d43f46685c2", "0x64d59d5e4abdef5c", "0xd0b6c0699e0d51f8", - "0xbf73d4c2e7d7bb5c", "0xb461b9770521b1b8", "0x224eaf296658b9c0", "0x81233334fb928726", "0xa10c5ee0aecc8251", "0xcd1a7e1b3cb970e7", "0x573c61ddaaa8fccc", "0x67dd7ae97a699881", - "0x6df554eae8e0482a", "0x61f528d7eba4cf89", "0xc1f241f1f41fc34d", "0x25e1b51094413da0", "0x9be1d90ff6f51065", "0x21fc77cf9089f049", "0x1fadecae2996b343", "0xbd170b4f84703b24", - "0x0a7493c2ebd8bfb5", "0xd1d6e43170ced136", "0xe87fadf2a2c02a52", "0x2f873ddc2cf9f022", "0xa3963afdf94f3307", "0x3dfff37956161da1", "0xa60a7a56137b4231", "0xb8abcc39f8cb021a" + "0x0075777e728c0393", "0xa7fb5870a6991c5c", "0xfdb4737b88c1fc4e", "0x20459d2a43909ba9", "0x2d05fcdfd72f04b4", "0x33a44106a40eded3", "0xc365f536aa848b35", "0xe4fba4d51458e902", + "0x4d490be32f29d621", "0x95c5cd90faa84cfa", "0xe6b90105cb7bec0d", "0xaf106086897d202e", "0x74e0a8d95386cab8", "0xd82da2ceda8b3794", "0x4efb171e065a3ffa", "0x350246d8be1ac9c4", + "0x4134d472c2ecc1f9", "0xf57ea25951f560d3", "0xad70814322094688", "0x7162d5a0882f8aec", "0x925135af6dd034f0", "0x36d01dcac09c0535", "0xae05981d39512765", "0x347bf7a59a4788ae", + "0x39cd25ee0ffadb23", "0xbcf8c0280e4db403", "0x203323bfd810035f", "0x13a3354a07423e16", "0xcb70b4756f46eb2e", "0x1c9941fe799cf7b6", "0x007b2b0035a673ae", "0x4587d1a7e011792c" ]} ], - "dataset_head": ["0x19c6837f", "0xdf3adfb8", "0xc5f0629f", "0x03e38ae8", "0xc5de2525", "0xa34a4906", "0x898367cb", "0x35b56808", "0xb886e673", "0x1ee29ece", "0x0cc36c88", "0xc984181b", "0x3d9d15f6", "0x27c7d52a", "0xc268cf69", "0x99b3c5c2"], + "dataset_head": ["0x19c6837f", "0xdf3adfb8", "0xbd3f6aed", "0xb8bede0d", "0xc5f0629f", "0x03e38ae8", "0xc435a60e", "0xfadee916", "0x827e59af", "0x8cf592f5", "0x60286061", "0x5d9abc1c", "0xa85d0c39", "0x4247a100", "0xd41a5b0d", "0x3f33dc64"], "dataset_last_index": 268435455, "dataset_last": "0x8d66c390", - "dataset_samples": [{"index": 59471966, "value": "0xe53754ce"}, {"index": 217795994, "value": "0xc6792663"}, {"index": 208353206, "value": "0x0ca9fdce"}, {"index": 42483309, "value": "0x96159926"}, {"index": 172547758, "value": "0xfca71449"}, {"index": 148076330, "value": "0x54522663"}, {"index": 183853158, "value": "0xd0160df7"}, {"index": 214389424, "value": "0x301be5a9"}, {"index": 267488061, "value": "0xb0de07c5"}, {"index": 169781097, "value": "0xb8f1c964"}, {"index": 184093494, "value": "0xcef19286"}, {"index": 153880993, "value": "0xc9c7985d"}, {"index": 84977930, "value": "0x9db14da9"}, {"index": 46426879, "value": "0xda9f746d"}, {"index": 3093825, "value": "0x8747e5a1"}, {"index": 225364072, "value": "0x545158e0"}, {"index": 44593546, "value": "0xdf31afbb"}, {"index": 260713159, "value": "0x4daa9b4c"}, {"index": 168250303, "value": "0x1f0762ff"}, {"index": 52384140, "value": "0x80a2d158"}, {"index": 223401610, "value": "0xf762ad72"}, {"index": 45554030, "value": "0x7ee04a69"}, {"index": 95410555, "value": "0xd07df0e9"}, {"index": 175039924, "value": "0x87cfc4ab"}, {"index": 79171087, "value": "0x9de7bb05"}, {"index": 267580473, "value": "0xcd214af2"}, {"index": 24168642, "value": "0xbd15dc85"}, {"index": 37981670, "value": "0x89f48a42"}, {"index": 171551130, "value": "0xd49c8a27"}, {"index": 195559979, "value": "0xbc60a7e1"}, {"index": 204611762, "value": "0xafc36693"}, {"index": 140997658, "value": "0x4876871f"}, {"index": 138925853, "value": "0x703246c4"}, {"index": 86637313, "value": "0xbb823e84"}, {"index": 20736778, "value": "0xa13f71a1"}, {"index": 219665210, "value": "0xf2dd8540"}, {"index": 160430336, "value": "0x74b039d1"}, {"index": 264654675, "value": "0x84e8a275"}, {"index": 8013395, "value": "0x750744bb"}, {"index": 228945585, "value": "0x3f690b5f"}, {"index": 213884386, "value": "0x5e75ecbd"}, {"index": 104419827, "value": "0x36a11cc7"}, {"index": 44185464, "value": "0x65e888cf"}, {"index": 142737231, "value": "0x2e8538c8"}, {"index": 99284897, "value": "0x14f5fec9"}, {"index": 132475900, "value": "0x7329f625"}, {"index": 61861762, "value": "0x42ad9261"}, {"index": 132056166, "value": "0x120aea25"}, {"index": 262388043, "value": "0x5374b09f"}, {"index": 91878046, "value": "0x4fa85aa3"}, {"index": 117353561, "value": "0xcb856f4e"}, {"index": 124768597, "value": "0x526707ba"}, {"index": 71352993, "value": "0x82d847da"}, {"index": 190698941, "value": "0xaed963e5"}, {"index": 46055428, "value": "0x1516404b"}, {"index": 55281366, "value": "0x2e565ddd"}, {"index": 165145231, "value": "0x46d1dacf"}, {"index": 106810753, "value": "0xf7bb9e4c"}, {"index": 171985651, "value": "0x61ae3b87"}, {"index": 232085256, "value": "0x9daeed43"}, {"index": 159510492, "value": "0xcf6bc86f"}, {"index": 40072060, "value": "0x24f111d5"}, {"index": 209107596, "value": "0x4356a558"}, {"index": 39023794, "value": "0x0890de16"}], + "dataset_samples": [{"index": 59471966, "value": "0xd088e877"}, {"index": 217795994, "value": "0x2f25cbd2"}, {"index": 208353206, "value": "0x9e26cf29"}, {"index": 42483309, "value": "0xd3b102e0"}, {"index": 172547758, "value": "0x3250b4a2"}, {"index": 148076330, "value": "0xa0716e94"}, {"index": 183853158, "value": "0x6497643c"}, {"index": 214389424, "value": "0xa9b78313"}, {"index": 267488061, "value": "0x69d951fc"}, {"index": 169781097, "value": "0xa1b35f16"}, {"index": 184093494, "value": "0xfee8e051"}, {"index": 153880993, "value": "0x6098ad8c"}, {"index": 84977930, "value": "0x27f0b64e"}, {"index": 46426879, "value": "0x5c464cb0"}, {"index": 3093825, "value": "0x1a3f0ce6"}, {"index": 225364072, "value": "0xdbe10965"}, {"index": 44593546, "value": "0x41d9309e"}, {"index": 260713159, "value": "0xac9f9678"}, {"index": 168250303, "value": "0x03d686a8"}, {"index": 52384140, "value": "0x67544111"}, {"index": 223401610, "value": "0xa7cad073"}, {"index": 45554030, "value": "0x4e9c542d"}, {"index": 95410555, "value": "0xe7c3c2c7"}, {"index": 175039924, "value": "0x9c624803"}, {"index": 79171087, "value": "0x71780762"}, {"index": 267580473, "value": "0xde7bcf5e"}, {"index": 24168642, "value": "0xe319f472"}, {"index": 37981670, "value": "0x89a3bc8a"}, {"index": 171551130, "value": "0xa4a01afb"}, {"index": 195559979, "value": "0x72c52455"}, {"index": 204611762, "value": "0xe877adf6"}, {"index": 140997658, "value": "0xea1e321c"}, {"index": 138925853, "value": "0xa518e572"}, {"index": 86637313, "value": "0x8526fe55"}, {"index": 20736778, "value": "0x8bde9bd3"}, {"index": 219665210, "value": "0xe5fd50df"}, {"index": 160430336, "value": "0x3f994c85"}, {"index": 264654675, "value": "0x02972af2"}, {"index": 8013395, "value": "0x8bad5ffd"}, {"index": 228945585, "value": "0xfc0becf6"}, {"index": 213884386, "value": "0xc03e2465"}, {"index": 104419827, "value": "0x0bbe949e"}, {"index": 44185464, "value": "0x1e696b57"}, {"index": 142737231, "value": "0x593feba2"}, {"index": 99284897, "value": "0x1c0b992a"}, {"index": 132475900, "value": "0x4905aca0"}, {"index": 61861762, "value": "0xf6e70116"}, {"index": 132056166, "value": "0x427aa5f2"}, {"index": 262388043, "value": "0x128898cf"}, {"index": 91878046, "value": "0x6acfe21e"}, {"index": 117353561, "value": "0x9a0ae2e4"}, {"index": 124768597, "value": "0xc025b697"}, {"index": 71352993, "value": "0x91f0a3d0"}, {"index": 190698941, "value": "0x053041bb"}, {"index": 46055428, "value": "0xf7d767d8"}, {"index": 55281366, "value": "0xcb7b7b2b"}, {"index": 165145231, "value": "0x53597840"}, {"index": 106810753, "value": "0xdc45ee7f"}, {"index": 171985651, "value": "0x07484a2e"}, {"index": 232085256, "value": "0x635c8369"}, {"index": 159510492, "value": "0x09a65a56"}, {"index": 40072060, "value": "0xd216baee"}, {"index": 209107596, "value": "0x9cbd726c"}, {"index": 39023794, "value": "0x5e566286"}], "cache_head": ["0xebd9055c", "0x7eaf6b21", "0x9b610855", "0x134a8562", "0xb10059ba", "0x7f459e58", "0x8f3c7873", "0x3523e609", "0x75b8f4ca", "0x750d31b4", "0x0dae0782", "0x26f10015", "0x7ba87a41", "0x0efd8543", "0x691d6368", "0x8929d967"], "cache_last_line": ["0x51474edf", "0xc3cfba93", "0xf21454cf", "0x9b79baac", "0x4d4fce23", "0xd701dfd5", "0x37357ab3", "0x1be693fa", "0xa701cb3b", "0x7467c620", "0x428184e8", "0xf4010df0", "0xe33aa88f", "0xc78d6d62", "0xae9ba9c0", "0xf4bba97e"], "cache_fnv1a64": "0x448274a57f508cbc" diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/program.h b/proto-cuda/packs-ca2-mixer/mx8-genesis/program.h index 9c99a169..22647af8 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-genesis/program.h +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/program.h @@ -13,9 +13,9 @@ #define IGNEUM_SEED_STRING "igneum-genesis" #define IGNEUM_SEED_BYTES_HEX "69676e65756d2d67656e65736973" -#define IGNEUM_GENERATOR 2 +#define IGNEUM_GENERATOR 3 #define IGNEUM_PROGRAM_ATTEMPT 0 -#define IGNEUM_PROGRAM_ID 0x9544515847736361ull +#define IGNEUM_PROGRAM_ID 0xe323b9dcaf283a6full #define IGNEUM_DAY_STRING "2026-10-03" #define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033" #define IGNEUM_DAY0 0x3067619fu @@ -28,6 +28,9 @@ #define IGNEUM_LOADS_PER_HASH 128 #define IGNEUM_WIDE_LOADS_PER_HASH 0 #define IGNEUM_OP_MIX "load=16 add=8 shfl=8 xor=6 mad=5 mul=5 mulhi=5 sub=4 rotl=3 rotr=3 or=1" +// Program class v3 (Counter ASIC 2.0, docs/plans/counter-asic-2-rollout.md): generator version 3; a worker that +// runs another class refuses this pack, and a job line names the class it wants (class=v3 era=). +#define IGNEUM_PROGRAM_CLASS "v3" // Class v3 construction (Counter ASIC 2.0, 5 October 2026, docs/plans/mixer-x4.md): version 2 loads; the dataset item // derivation applies the mixer IGNEUM_MIXER_MULT times per round (memhard.h), and the cache follows the growth rule. #define IGNEUM_LOAD_CLASS "mx8" diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/program.json b/proto-cuda/packs-ca2-mixer/mx8-genesis/program.json index d4e1c079..1875e9b3 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-genesis/program.json +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/program.json @@ -1,8 +1,8 @@ { "format": "igneum-program-pack-3", - "generator": 2, + "generator": 3, "attempt": 0, - "program_id": "0x9544515847736361", + "program_id": "0xe323b9dcaf283a6f", "program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32", "dataset_mode": "memory-hard", "seed": "igneum-genesis", @@ -15,6 +15,7 @@ "iterations": 8, "instruction_count": 64, "loads_per_hash": 128, + "program_class": "v3", "load_class": "mx8", "mixer_mult": 8, "cache_growth": true, diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.h b/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.h index 141affee..caadb5e2 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.h +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.h @@ -1,5 +1,5 @@ // Generated by igneum-pow export (generator v2) for seed "igneum-genesis". Do not edit by hand. -// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset +// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset #pragma once #ifdef __cplusplus #include diff --git a/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.json b/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.json index 916b155f..134f4e1a 100644 --- a/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.json +++ b/proto-cuda/packs-ca2-mixer/mx8-genesis/vectors.json @@ -5,7 +5,7 @@ "dataset_log2_words": 28, "mask": "0x0fffffff", "lanes": 32, - "source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset", + "source": "igneum-pow (Rust) CPU interpreter, generator v3, memory-hard dataset", "warps": [ {"base_nonce": 0, "expected": [ "0x19b56348bc85304d", "0xb08a9cfb44aa720f", "0xe1f8f627f780eff7", "0x2ff3e86ff1696161", "0xb65e0578c257e8ac", "0xf37b5705c2bebaae", "0xb19fef670982389e", "0x2374331b28827a11", From e041fb5e7341c9fa66d6c790b2adfaa448c99c15 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:10:55 +0000 Subject: [PATCH 105/158] plan: the self-built CUDA server's packaging row before 0.3.12 (arch list, builder, signing, DMG and WSL2 placement, evidence note, rebuild at each SP1 upgrade, the verifying keys unchanged) Co-Authored-By: Claude Fable 5.1 --- docs/plans/proving-v1.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/plans/proving-v1.md b/docs/plans/proving-v1.md index e213f37b..8e8d8c4e 100644 --- a/docs/plans/proving-v1.md +++ b/docs/plans/proving-v1.md @@ -114,6 +114,10 @@ Reading. Nobody pays an aggregator as a separate role: Aztec's 30% goes to whoev The resume path (5 October 2026, the 0.3.11 app): `POST /api/resume` on 0.3.9 re-armed only FAULTED cards (`stop_miners("paused")` clears every slot's `restart_at`), so a healthy paused card stayed "off" at 0 MH/s until the app was relaunched: PC 2 at 21:25:11Z (the aggregation-cost job's pause and resume; `[ok] mining resumed` then `0.00 MH/s, waiting` for 20 minutes), the Mac that afternoon. Now every slot without a live worker is re-armed and its pack exported again before the start, and 90 s later `resume_check` logs `resume: is not mining 90 s after resume (state ..., pid ...)` for every enabled card without a hash rate (`engine.rs`, three unit tests: the state machine, the 21:25:11Z case against the old rule, the check). +### A self-built CUDA server (the 12 GB path), before 0.3.12 (consequences C26) + +If the prover-floor agent's rebuilt `sp1-gpu-server` (the Setup sizes cut, built on PC 2 under WSL2) proves a shard under 11 GB, it becomes a shipped artefact and needs its own row of rules before 0.3.12: it is built from a pinned SP1 source tag with `CUDA_ARCHS` covering sm_86, sm_89 and sm_120 (the 12 and 16 GB tiers are Ampere and Ada, not only the 5090's Blackwell; one card family per measured row), by the packaging path that builds the Windows payload (PC 1's build job for the Linux binary, the Mac signs the manifest as it does the DMG), lands in the DMG and the WSL2 package beside the host as `wsl2/bin/sp1-gpu-server` with its sha256 in `payload-inputs.json`, is named in `evidence.md` beside the prover rows ("prover built from SP1 at "), is rebuilt and re-measured at every SP1 upgrade, and ships only after `--mode verify-segment` and `--mode verify` on proofs it made show the pinned verifying keys unchanged (the server changes allocation, not the circuit; the ids `0x2b1a81cb...` and `0x474678f3...` must still verify them). The 12 GB claim itself waits for the on-order RTX 3060 to run that server on the same fixtures and recipe as the curve; until then the public line stays at 24 GB. + A prover job on a shared card runs as the app's user or cleans its socket (`pkill -f sp1-gpu-server; rm -f /tmp/sp1-cuda-*.sock` at the start and the end; `tools/ci/prover-socket-check.sh`): the root-socket fault of 20:00Z, bench-log. ### The prover profiles: the tiers from the S_p curve (bench-log, "proving v1", the sweep, the miner-on pair and the curve) From ac2a4f986209fefa3f325f54f60a226e36a9f3c4 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:11:05 +0000 Subject: [PATCH 106/158] Consequences ledger: C26 taken into the proving plan Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index b8ab4e8a..6f03ee73 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -54,7 +54,7 @@ Sweep 7 (21:49 UTC) notes, no new row: the hot table is measured and NOT adopted | # | Number | Source | Tier affected | Consequence | Action | Owner | State | |---|---|---|---|---|---|---|---| -| C26 | The 13.9 GB floor's cause: the shipped sp1-gpu-server 6.8.1 panics on any card under 24 GB (builder.rs 35 to 39) and allocates its core, recursion, shrink and wrap provers at Setup; the prover-floor agent rebuilds it from source on PC 2 with those sizes cut, CUDA_ARCHS=120 | proving-v1.md 4c82e56; status 22:01 | 12 and 16 GB NVIDIA cards (RTX 3060, 4070, 5070, 5080, 4060 Ti 16 GB): the tier the project lead asked for; packaging and signing | A server built for CUDA_ARCHS=120 runs on the 5090 only; the 12 GB tier is sm_86 (3060) and sm_89 (4070), the 16 GB tier sm_89 and sm_120, so a cut-size server measured on the 5090 proves nothing about a 3060 until the on-order 3060 runs it, and the build must list sm_86, sm_89, sm_120 (sm_100 is datacentre) to serve the tier at all. Shipping our own 250 MB CUDA server means the project signs and distributes a build of someone else's prover: it enters the DMG and the WSL2 package, the K1-signed inputs, the SBOM-style notes in evidence.md, and every SP1 upgrade is re-done by hand. Cut buffer sizes do not change the verifying key (prover-side chunking), so no guest re-pin, but the recipe must say so with a verify-segment run on a proof from the rebuilt server | The prover-floor measurement states its arch list and the card it ran on; the 12 GB claim waits for the 3060; the rebuilt server's packaging path (who builds, who signs, where it lands) is a row in the proving plan before 0.3.12, and the public line keeps "24 GB" until the 3060 proves on it | prover-floor agent (through the coordinator); proving v1 | sent in full to the prover-floor agent abefda4c3872f866f by the coordinator (arch list, the packaging row before 0.3.12, the verify-segment run, "24 GB" kept until the 3060) | +| C26 | The 13.9 GB floor's cause: the shipped sp1-gpu-server 6.8.1 panics on any card under 24 GB (builder.rs 35 to 39) and allocates its core, recursion, shrink and wrap provers at Setup; the prover-floor agent rebuilds it from source on PC 2 with those sizes cut, CUDA_ARCHS=120 | proving-v1.md 4c82e56; status 22:01 | 12 and 16 GB NVIDIA cards (RTX 3060, 4070, 5070, 5080, 4060 Ti 16 GB): the tier the project lead asked for; packaging and signing | A server built for CUDA_ARCHS=120 runs on the 5090 only; the 12 GB tier is sm_86 (3060) and sm_89 (4070), the 16 GB tier sm_89 and sm_120, so a cut-size server measured on the 5090 proves nothing about a 3060 until the on-order 3060 runs it, and the build must list sm_86, sm_89, sm_120 (sm_100 is datacentre) to serve the tier at all. Shipping our own 250 MB CUDA server means the project signs and distributes a build of someone else's prover: it enters the DMG and the WSL2 package, the K1-signed inputs, the SBOM-style notes in evidence.md, and every SP1 upgrade is re-done by hand. Cut buffer sizes do not change the verifying key (prover-side chunking), so no guest re-pin, but the recipe must say so with a verify-segment run on a proof from the rebuilt server | The prover-floor measurement states its arch list and the card it ran on; the 12 GB claim waits for the 3060; the rebuilt server's packaging path (who builds, who signs, where it lands) is a row in the proving plan before 0.3.12, and the public line keeps "24 GB" until the 3060 proves on it | prover-floor agent (through the coordinator); proving v1 | taken (the proving plan carries "A self-built CUDA server (the 12 GB path), before 0.3.12": arch list sm_86 / sm_89 / sm_120 with one measured row per family, the build on PC 1 from a pinned SP1 tag, the Mac signs, placement as wsl2/bin/sp1-gpu-server with its sha256 in payload-inputs.json and the DMG, the evidence.md note, the rebuild at each SP1 upgrade, the gate that verify-segment and verify show the pinned keys unchanged; the 12 GB claim waits for the 3060; the prover-floor agent abefda4c3872f866f has the measurement side) | | C27 | The root-socket fault recurred at 21:25Z from another agent's job (agg-cost-pc2-1) after the class fix and CI check landed; PC 2's prover was dark 37 minutes; a resume at 21:25 answered ok without restarting the miners | bench-log 1d78979; status 21:45, 22:03 | every PC job; the devnet's proving and hash rate tonight | The class check lives in CI, but PC jobs are published from worktrees by `publish-jobs.sh` and never pass through CI before they run, so a job written on a branch without the check runs the old shape. The check must run where the job is published, not only where the repo is tested | `packaging/ota/publish-jobs.sh add` runs `tools/ci/prover-socket-check.sh` and the bash-body check on the script it publishes and refuses on a failure; the sub-agent on the bash-body check wires both; the resume defect is on the next-cut list (stated) | sub-agent bash-body-check (the wiring); coordinator (the rule) | in work (sub-agent wiring both checks into publish-jobs.sh add; the coordinator: no collision, jobs without a script pass trivially, an unextractable body fails) | Sweep 8 (22:09 UTC) notes: mixer x8 DECIDED into v3 on the PC rows (the daily 1 GiB build latency-bound on every card: 5090 23 to 25 ms, 9070 XT 72 to 77 ms at every multiplier; the chip row 0.92x with the 3x factor), so the public claim holds with margin (D5 re-cut); the verifier regression (2.2x) bisected to inlining in the mixer's fetch loop and fixed, so the quiet-core figures of C19 return to about 0.6 / 0.9 / 1.3 ms; G6 job 3 failed on a stale fork test (era inside the class), job 4 on the final tree; the integration merge into master has three known conflicts (bench-log append-only, packfile.h and host.c take the ca2-v3 side). From 2ed3dabe665c3d07a8997240bc4828552f182820 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:14:19 +0000 Subject: [PATCH 107/158] Jobs publisher: the class checks run on the script before it is signed (row C27) A PC job is published from a worktree by packaging/ota/publish-jobs.sh and never passes CI before it runs; tonight the root-socket fault came back from a job on a branch without the check. `add --kind run` now runs, on the script being published and before anything is signed: tools/ci/bash-body-check.sh for a PowerShell script (every inline bash body parses; a body it cannot read fails, never skips), `bash -n` for a .sh script, and tools/ci/prover-socket-check.sh for both (a root prover run kills sp1-gpu-server and unlinks its socket). A failure refuses the publish with the check's output; a missing check file refuses too. Kinds without a script (fetch, collect, restart, update-now, shard-benchmark, build) are untouched. tools/ci/prover-socket-check.sh is copied from proving-v1 (344cba8; master lacks it) with two additions: file arguments check those files only (the publisher's call), and an allow list for packaging/ota/test-publish-jobs.sh, which carries a known-bad root prover script on purpose. Its ci.yml step is left to proving-v1 to avoid a duplicate. packaging/ota/test-publish-jobs.sh: four refusals (a lost quote in a PowerShell bash body, an unreadable body, a .sh with a lost quote, a root prover script without the cleanup) and the envelope unchanged after a refusal. 32 passed, 0 failed on this Mac with the main checkout's signer. packaging/README-ship.md: the publish-time gate. Co-Authored-By: Claude Fable 5.1 --- packaging/README-ship.md | 5 ++++- packaging/ota/publish-jobs.sh | 13 +++++++++++++ packaging/ota/test-publish-jobs.sh | 20 +++++++++++++++++++- tools/ci/prover-socket-check.sh | 24 ++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100755 tools/ci/prover-socket-check.sh diff --git a/packaging/README-ship.md b/packaging/README-ship.md index 3da24790..0d62e74a 100644 --- a/packaging/README-ship.md +++ b/packaging/README-ship.md @@ -117,4 +117,7 @@ program) or failed in 4 s (the 0.3.10 installer job). The file shape keeps the b `pc1-cpu-prove.ps1` does that in the job itself. `tools/ci/bash-body-check.sh` fails CI on an inline body that does not parse: it finds every body handed to bash (`bash -c "..."`, `bash -lc $var`, a `+` concatenation, a here-string written to a file and run), unescapes it the way PowerShell would, and runs `bash -n` on it. A body it sees but cannot read fails too. -`--self-test` shows it firing on the fixtures under `tools/ci/fixtures/`. +`--self-test` shows it firing on the fixtures under `tools/ci/fixtures/`. A job script is published from a worktree and +never passes CI before it runs, so `packaging/ota/publish-jobs.sh add --kind run` runs the same check (and +`tools/ci/prover-socket-check.sh`, the root-socket class; `bash -n` for a `.sh` script) on the script before anything is +signed, and refuses the publish with the check's output. `packaging/ota/test-publish-jobs.sh` proves the refusals. diff --git a/packaging/ota/publish-jobs.sh b/packaging/ota/publish-jobs.sh index 4e26f0ed..8d444d5f 100755 --- a/packaging/ota/publish-jobs.sh +++ b/packaging/ota/publish-jobs.sh @@ -256,6 +256,19 @@ if [ "$CMD" = add ]; then [ -n "$SCRIPT" ] && [ -f "$SCRIPT" ] || { echo "run: --script is required" >&2; exit 2; } [ -n "$SHELL_KIND" ] || { case "$SCRIPT" in *.sh) SHELL_KIND=bash ;; *) SHELL_KIND=powershell ;; esac; } [ -n "$PLATFORM" ] || { [ "$SHELL_KIND" = powershell ] && PLATFORM=windows || PLATFORM=any; } + # A job script is published from a worktree and never passes CI before it runs (5 October 2026: the root-socket + # fault came back from a branch without the check), so the class checks run here on the script itself, before + # anything is signed. A failure refuses the publish with the check's output; a missing check refuses it too. + # PowerShell: every inline bash body must parse (tools/ci/bash-body-check.sh, the lost-quote class; a body it + # cannot read fails, never skips). Bash: the script itself must parse. Both: a root prover run kills the GPU + # server and unlinks its socket (tools/ci/prover-socket-check.sh, the root-socket class). A check file that is + # missing from this tree refuses too (bash exits 127 with the reason), so no job goes out unchecked. + if [ "$SHELL_KIND" = powershell ]; then + out="$(bash "$ROOT/tools/ci/bash-body-check.sh" "$SCRIPT" 2>&1)" || { echo "run: bash-body-check.sh refuses $SCRIPT:" >&2; printf '%s\n' "$out" >&2; exit 1; } + else + out="$(bash -n "$SCRIPT" 2>&1)" || { echo "run: bash -n refuses $SCRIPT (the lost-quote class):" >&2; printf '%s\n' "$out" >&2; exit 1; } + fi + out="$(bash "$ROOT/tools/ci/prover-socket-check.sh" "$SCRIPT" 2>&1)" || { echo "run: prover-socket-check.sh refuses $SCRIPT:" >&2; printf '%s\n' "$out" >&2; exit 1; } PARAMS="$(python3 -c 'import json,sys; print(json.dumps({"script": open(sys.argv[1]).read(), "shell": sys.argv[2], "elevated": sys.argv[3]=="1", "stop_miners_first": sys.argv[4]=="1", **({"timeout_minutes": int(sys.argv[5])} if sys.argv[5] else {})}))' "$SCRIPT" "$SHELL_KIND" "$ELEVATED" "$STOP_MINERS" "$TIMEOUT_MIN")" [ -n "$TITLE" ] || TITLE="run $(basename "$SCRIPT")" ;; diff --git a/packaging/ota/test-publish-jobs.sh b/packaging/ota/test-publish-jobs.sh index d3fbc968..c5f8b545 100755 --- a/packaging/ota/test-publish-jobs.sh +++ b/packaging/ota/test-publish-jobs.sh @@ -8,7 +8,10 @@ # byte and its sig IS the plain signature; the signer reads the envelope back; a second `add` (a new publish) gives # a new envelope; an envelope made by hand from the FIRST file and the SECOND signature (the stale pair an edge can # serve across a deploy) is REFUSED by the signer with the words the app logs; a tampered inner byte is refused; -# `sign` rewrites all three consistently; and the real OTA key is the key the app embeds. +# `sign` rewrites all three consistently; a `run` script that fails a class check (a lost quote in an inline bash +# body, a body the check cannot read, a bash script that does not parse, a root prover run without the socket cleanup) +# is REFUSED before anything is signed and the envelope is left as it was; and the real OTA key is the key the app +# embeds. # # A check is trusted only once it has fired on a known-good and a known-bad case (standing rule, 4 October 2026), so # every negative case here must FAIL for the run to pass. Needs ~/.config/igneum/ota-signing-key (the publisher's own @@ -96,6 +99,21 @@ PY grep -q "^inner-identical True$" "$T/inner2.txt" && grep -q "^sig-identical True$" "$T/inner2.txt" && ok "after sign: the envelope still holds the plain file and its signature" || bad "after sign: the envelope and the pair differ" expect_ok "list reads the folder" "$PUBLISH" list --dest "$D" +echo "== the class checks refuse a bad script before anything is signed (5 October 2026: a job never passes CI first)" +cp "$D/igneum-jobs.signed.json" "$T/before.signed" +printf '& wsl.exe -d Ubuntu-24.04 -- bash -c '"'"'echo "started; ls /opt/igneum'"'"' 2>&1\n' > "$T/lost-quote.ps1" +expect_fail "a PowerShell script with a lost quote in its bash body" "$PUBLISH" add --kind run --target 1ccfe586 --script "$T/lost-quote.ps1" --id test-lost-quote --dest "$D" --base-url https://example.invalid/dl/t +grep -q "unexpected EOF while looking for matching" "$T/out" && ok "refused with the bash -n error" || bad "another reason: $(tail -1 "$T/out")" +printf '$cmd = (Get-Content body.txt) -join "; "\n& wsl.exe -- bash -c $cmd\n' > "$T/unreadable.ps1" +expect_fail "a PowerShell script whose bash body the check cannot read" "$PUBLISH" add --kind run --target 1ccfe586 --script "$T/unreadable.ps1" --id test-unreadable --dest "$D" --base-url https://example.invalid/dl/t +grep -q "unextractable body" "$T/out" && ok "refused as unextractable, not skipped" || bad "another reason: $(tail -1 "$T/out")" +printf 'echo "started; ls /opt/igneum\n' > "$T/lost-quote.sh" +expect_fail "a bash script with a lost quote" "$PUBLISH" add --kind run --target 1ccfe586 --script "$T/lost-quote.sh" --id test-lost-quote-sh --dest "$D" --base-url https://example.invalid/dl/t +printf '& wsl.exe -d Ubuntu-24.04 -u root -- bash /mnt/c/prove.sh\n# igneum-prove-host --mode shard --shard 0\n' > "$T/root-prover.ps1" +expect_fail "a root prover script without the socket cleanup" "$PUBLISH" add --kind run --target 1ccfe586 --script "$T/root-prover.ps1" --id test-root-socket --dest "$D" --base-url https://example.invalid/dl/t +grep -q "prover-socket" "$T/out" && ok "refused by the socket check" || bad "another reason: $(tail -1 "$T/out")" +cmp -s "$T/before.signed" "$D/igneum-jobs.signed.json" && ok "a refused publish leaves the envelope untouched" || bad "a refused publish changed the envelope" + echo "== the key" EMB="$("$SIGNER" embedded | head -1)" [ "$EMB" = "$(tr -d '[:space:]' < "$PUB")" ] && ok "the embedded key is the Mac's OTA public key" || bad "the embedded key is not $PUB" diff --git a/tools/ci/prover-socket-check.sh b/tools/ci/prover-socket-check.sh new file mode 100755 index 00000000..ddd10e56 --- /dev/null +++ b/tools/ci/prover-socket-check.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# The root-socket class (5 October 2026, 20:00Z): a PC 2 job ran igneum-prove-host as root inside WSL2, which started +# an sp1-gpu-server whose socket /tmp/sp1-cuda-0.sock stayed root-owned after the job; the live prover (the app's user) +# then failed every shard with "CudaClientError: Connect(PermissionDenied)" until the socket was gone. Rule: every +# playbook that runs the prover host as root on a shared card kills the server AND unlinks its socket (at the start +# and at the end), or runs as the app's user. This check fails CI when a script runs `igneum-prove-host` under a +# `-u root` WSL session without both lines. With file arguments it checks those files only (the jobs publisher runs it +# on the script being published, packaging/ota/publish-jobs.sh add --kind run; a PC job never passes CI before it runs). +set -euo pipefail +cd "$(dirname "$0")/../.." +fail=0 +# the publisher's test writes a known-bad root prover script on purpose, to prove the publish refuses it +ALLOW='^packaging/ota/test-publish-jobs\.sh$' +list_files() { if [ $# -gt 0 ]; then printf '%s\n' "$@"; else git ls-files 'tools/**' 'relay/playbooks/**' 'proving/**' 'packaging/**' | grep -E '\.(sh|ps1|mjs)$'; fi; } +while IFS= read -r f; do + [[ "$f" =~ $ALLOW ]] && continue + # a playbook that only runs `--mode id` or `--mode verify` starts no GPU server; the prove modes do + if grep -qE 'igneum-prove-host' "$f" && grep -qE -- '--mode (compressed|chain|aggregate|block|shard|all)\b' "$f" && grep -qE -- '-u root' "$f"; then + if ! grep -qE 'pkill -f sp1-gpu-server' "$f"; then echo "prover-socket: $f runs the prover host as root without killing sp1-gpu-server"; fail=1; fi + if ! grep -qE 'rm -f /tmp/sp1-cuda-' "$f"; then echo "prover-socket: $f runs the prover host as root without unlinking /tmp/sp1-cuda-*.sock"; fail=1; fi + fi +done < <(list_files "$@") +[ "$fail" = 0 ] && echo "prover-socket: every root prover playbook kills the GPU server and unlinks its socket" +exit $fail From 6bbe8fbdb40bbe81056007225a4a33a38021a39a Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:14:37 +0000 Subject: [PATCH 108/158] Consequences ledger: C24 and C27 closed on branch bash-body-check (7adb1ca, 6805125) with the merge notes Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 6f03ee73..82987e70 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -45,7 +45,7 @@ Sweep 5 (21:08 Mac clock) notes: the coordinator applied the public proving line | # | Number | Source | Tier affected | Consequence | Action | Owner | State | |---|---|---|---|---|---|---|---| | C23 | Mixer x4 or x8: the 5090's daily dataset build 13.4 ms at x1 (54 ms at x4, about 107 ms at x8); the integrated gfx1036 builds the dataset per PREPARE, 7 to 12 s at x1 and 55 to 124 s under CPU load (epoch-length.md section 7); the x8 rule: "the daily build under 1 s on every card we own" | status 21:25 and 21:27; epoch-length.md section 7 | integrated GPUs (the iGPU tier), 8 GB cards (about a tenth of a 5090's rate), rigs with one weak card | The x8 rule names "every card we own": the gfx1036 is one, and at x8 its per-prepare build is 56 to 96 s per epoch (7 to 16 min under load), so it misses every epoch boundary and falls to the exit-42 path; at x4 it is 28 to 48 s per hour (0.8 to 1.3%) or 4 to 8 min under load. An 8 GB discrete card scales at about a tenth of the 5090: about 1 s at x8, on the rule's edge. The per-day dataset reuse in the worker (owed in epoch-length.md section 9) is what makes the mixer cheap for the iGPU tier; without it the mixer multiplies a per-epoch cost | The mixer decision names the gfx1036 and an 8 GB-class scaled row beside the 5090, M5 Max and 9070 XT in the "under 1 s" check, and the per-day dataset reuse in the worker lands before (or with) class v3, or the iGPU tier is stated as "mines v3 with a restart per epoch" on the level 3 page | ca2-mixer af345b1e2c541ffbb; coordinator | taken (coordinator and ca2-mixer, mixer-x4.md build-time table: the x8 table carries the gfx1036 row and a scaled 8 GB-class row; the under-1-s rule applies to the discrete cards' daily build; for the integrated tier either the per-day dataset reuse in the three workers lands with class v3, asked of the mixer and node agents as a bounded change tonight, or the level 3 page says the iGPU tier mines v3 with a restart per epoch; recorded with the x4/x8 choice) | -| C24 | Two PowerShell job scripts lost a quote inside an inline bash body tonight: amd-prove's awk program (cpu-prove-pc1-small, exit 0 with nothing proved) and the 0.3.10 installer's `bash -c` string (fb-installer-pc1-3, exit 2 in 4 s); amd-prove added `tools/amd-prove/check-job-bash.sh` (bash -n on its own scripts' bash bodies) | amd-proving.md section 2; status 21:28 | every PC job; the morning's rollouts | The class (CLAUDE.md, 5 October: fix the class the same day, add a check that fails when the shape comes back) is "a bash body inside a PowerShell job string"; the check exists for one agent's scripts and did not cover the shipper's, which failed the same way an hour later | A repo-wide CI check: every `.ps1` under relay/playbooks and tools that carries a bash body (`wsl ... bash -c`, `bash -lc`, here-strings fed to bash) has that body extracted and passed through `bash -n`; the shipper's rule (the WSL part as a file run with `bash `) written in packaging/README-ship.md as the convention | sub-agent (bounded, no owner); coordinator told | in work (sub-agent bash-body-check; the coordinator: no collision with PC 1, the shipper moved its WSL part to a file for attempt 4, the repro agent added a PowerShell 5.1 parse gate for bench/; the check fails on an unextractable body rather than skipping it) | +| C24 | Two PowerShell job scripts lost a quote inside an inline bash body tonight: amd-prove's awk program (cpu-prove-pc1-small, exit 0 with nothing proved) and the 0.3.10 installer's `bash -c` string (fb-installer-pc1-3, exit 2 in 4 s); amd-prove added `tools/amd-prove/check-job-bash.sh` (bash -n on its own scripts' bash bodies) | amd-proving.md section 2; status 21:28 | every PC job; the morning's rollouts | The class (CLAUDE.md, 5 October: fix the class the same day, add a check that fails when the shape comes back) is "a bash body inside a PowerShell job string"; the check exists for one agent's scripts and did not cover the shipper's, which failed the same way an hour later | A repo-wide CI check: every `.ps1` under relay/playbooks and tools that carries a bash body (`wsl ... bash -c`, `bash -lc`, here-strings fed to bash) has that body extracted and passed through `bash -n`; the shipper's rule (the WSL part as a file run with `bash `) written in packaging/README-ship.md as the convention | sub-agent (bounded, no owner); coordinator told | closed on branch bash-body-check 7adb1ca (tools/ci/bash-body-check.sh with fixtures and self-test, ci.yml, the convention in packaging/README-ship.md; the flagged existing playbooks are in the sub-agent's report for their owners) | | C25 | Ember Tune: the signed manifest carries per-card-model tuning priors (power limit and core clock) that a new card applies and confirms in two steps; K1 signs it | ember-tune.md sections 4 and 5; bench-log Ember Tune entry | every NVIDIA and AMD card on the app; the keys | The manifest now sets clocks and power limits on every user's card, so the signing key's blast radius grew: a signed prior can underclock the fleet or push a card model to its power ceiling. The plan's clamps (inside power.min_limit / max_limit and clocks.max.gr, the confirm step, a faulted step reverted) are the bound; keys.md's "what K1 signs" table (ota-k2 00fcbb5) predates the priors and does not list them | ember-tune.md section 5 states the bound in one line (a prior can never set a value outside the card's own reported limits, and never a memory clock), with the test that proves it; keys.md's table gains the tuning priors under K1 with that bound | Ember Tune a855dcc4bd05e0615; OTA key agent (the table row) | closed (Ember Tune 5d7ced9: the rule as a row in section 5 with the two tests named, a prior of 9,000 MHz at 30% clamps to 3,090 MHz at 50%, a bad prior costs one confirm step per card; the OTA agent has the keys.md note: tuning priors and the kill switch under K1 with that bound) | Sweep 7 (21:49 UTC) notes, no new row: the hot table is measured and NOT adopted (g 0.93 to 0.96 on the Mac against the 0.97 rule, bdab8df); the era draw passes the 5% rule on the Mac (spread 0.8%, c570da3) and its chip line says the union of a program's 16 windows covered the whole dataset in 300 of 300 programs, so a chip mirrors the whole dataset or nothing; the mixer x8 passes the verifier half of its rule (C19) and the PC build rows decide the other half about 22:10; PC 2's miners have been off since a job's /api/resume at 21:25 answered ok without restarting them (the devnet short PC 2's rate, the aggregation-cost mining phases void, a next-cut defect: a resume re-checks the miner processes), all stated by the coordinator at 21:45; PC 1 restarted on 0.3.10 at 21:40:41Z with no measurement straddling it. @@ -55,6 +55,6 @@ Sweep 7 (21:49 UTC) notes, no new row: the hot table is measured and NOT adopted | # | Number | Source | Tier affected | Consequence | Action | Owner | State | |---|---|---|---|---|---|---|---| | C26 | The 13.9 GB floor's cause: the shipped sp1-gpu-server 6.8.1 panics on any card under 24 GB (builder.rs 35 to 39) and allocates its core, recursion, shrink and wrap provers at Setup; the prover-floor agent rebuilds it from source on PC 2 with those sizes cut, CUDA_ARCHS=120 | proving-v1.md 4c82e56; status 22:01 | 12 and 16 GB NVIDIA cards (RTX 3060, 4070, 5070, 5080, 4060 Ti 16 GB): the tier the project lead asked for; packaging and signing | A server built for CUDA_ARCHS=120 runs on the 5090 only; the 12 GB tier is sm_86 (3060) and sm_89 (4070), the 16 GB tier sm_89 and sm_120, so a cut-size server measured on the 5090 proves nothing about a 3060 until the on-order 3060 runs it, and the build must list sm_86, sm_89, sm_120 (sm_100 is datacentre) to serve the tier at all. Shipping our own 250 MB CUDA server means the project signs and distributes a build of someone else's prover: it enters the DMG and the WSL2 package, the K1-signed inputs, the SBOM-style notes in evidence.md, and every SP1 upgrade is re-done by hand. Cut buffer sizes do not change the verifying key (prover-side chunking), so no guest re-pin, but the recipe must say so with a verify-segment run on a proof from the rebuilt server | The prover-floor measurement states its arch list and the card it ran on; the 12 GB claim waits for the 3060; the rebuilt server's packaging path (who builds, who signs, where it lands) is a row in the proving plan before 0.3.12, and the public line keeps "24 GB" until the 3060 proves on it | prover-floor agent (through the coordinator); proving v1 | taken (the proving plan carries "A self-built CUDA server (the 12 GB path), before 0.3.12": arch list sm_86 / sm_89 / sm_120 with one measured row per family, the build on PC 1 from a pinned SP1 tag, the Mac signs, placement as wsl2/bin/sp1-gpu-server with its sha256 in payload-inputs.json and the DMG, the evidence.md note, the rebuild at each SP1 upgrade, the gate that verify-segment and verify show the pinned keys unchanged; the 12 GB claim waits for the 3060; the prover-floor agent abefda4c3872f866f has the measurement side) | -| C27 | The root-socket fault recurred at 21:25Z from another agent's job (agg-cost-pc2-1) after the class fix and CI check landed; PC 2's prover was dark 37 minutes; a resume at 21:25 answered ok without restarting the miners | bench-log 1d78979; status 21:45, 22:03 | every PC job; the devnet's proving and hash rate tonight | The class check lives in CI, but PC jobs are published from worktrees by `publish-jobs.sh` and never pass through CI before they run, so a job written on a branch without the check runs the old shape. The check must run where the job is published, not only where the repo is tested | `packaging/ota/publish-jobs.sh add` runs `tools/ci/prover-socket-check.sh` and the bash-body check on the script it publishes and refuses on a failure; the sub-agent on the bash-body check wires both; the resume defect is on the next-cut list (stated) | sub-agent bash-body-check (the wiring); coordinator (the rule) | in work (sub-agent wiring both checks into publish-jobs.sh add; the coordinator: no collision, jobs without a script pass trivially, an unextractable body fails) | +| C27 | The root-socket fault recurred at 21:25Z from another agent's job (agg-cost-pc2-1) after the class fix and CI check landed; PC 2's prover was dark 37 minutes; a resume at 21:25 answered ok without restarting the miners | bench-log 1d78979; status 21:45, 22:03 | every PC job; the devnet's proving and hash rate tonight | The class check lives in CI, but PC jobs are published from worktrees by `publish-jobs.sh` and never pass through CI before they run, so a job written on a branch without the check runs the old shape. The check must run where the job is published, not only where the repo is tested | `packaging/ota/publish-jobs.sh add` runs `tools/ci/prover-socket-check.sh` and the bash-body check on the script it publishes and refuses on a failure; the sub-agent on the bash-body check wires both; the resume defect is on the next-cut list (stated) | sub-agent bash-body-check (the wiring); coordinator (the rule) | closed on branch bash-body-check 6805125 (publish-jobs.sh add --kind run runs the bash-body check and prover-socket-check.sh before signing, refuses with the output, never skips; test-publish-jobs.sh 32 passed with four new refusals). Merge notes for the integrator: prover-socket-check.sh exists on both proving-v1 c2544be and this branch (add/add, take the superset here); the socket grep flags tools/amd-prove/pc1-cpu-prove.ps1 (CPU-only, -u root, no GPU server) so that job needs the cleanup lines or an allow-list entry before its next publish, told to the coordinator | Sweep 8 (22:09 UTC) notes: mixer x8 DECIDED into v3 on the PC rows (the daily 1 GiB build latency-bound on every card: 5090 23 to 25 ms, 9070 XT 72 to 77 ms at every multiplier; the chip row 0.92x with the 3x factor), so the public claim holds with margin (D5 re-cut); the verifier regression (2.2x) bisected to inlining in the mixer's fetch loop and fixed, so the quiet-core figures of C19 return to about 0.6 / 0.9 / 1.3 ms; G6 job 3 failed on a stale fork test (era inside the class), job 4 on the final tree; the integration merge into master has three known conflicts (bench-log append-only, packfile.h and host.c take the ca2-v3 side). From 37dab3f5a5b8b110d398c04109e112e561d79ffc Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:15:18 +0000 Subject: [PATCH 109/158] counter-asic-2-node.md: the verifier before/after table (the mixer fix), class v3 = x8, the merges and checks on d5070e7 Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-2-node.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index 717c1af4..24287a9a 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -108,7 +108,15 @@ Binaries rebuilt on ca2-v3 b105a55 (era layout merged on the mixer: `V3_CLASS` = | Forks | sinks `712c1b212091dcdc` on all three nodes, block counts 303 / 303 / 303, one tip each | | Program and cache ready, one CPU core | v2 epoch 0 178 ms, the first v3 epoch 181 ms, an in-day swap 2 ms | -CPU hash rate across the switch, run 2, miner cpu0 (one thread, the Mac shared with other agents' builds, so approximate): the cumulative rate read 0.024 MH/s through the v2 epochs (30 to 151 s), then fell to 0.021 MH/s cumulative by 271 s (100 s under v3), which puts the v3 interval rate near 0.017 MH/s, about 30 percent under v2 on the CPU interpreter (the era's strided windowed loads and the mixer path). The node has no per-block verify timing line; the era agent's measurement of the CPU verifier (`igneum-pow`, 0.604 ms per warp on readwidth against 1.332 ms on 88dafbc, the mixer's `derive_items` path at m = 1) is the number to fix before the publish, and the before/after goes here when the mixer agent's commit lands. +CPU hash rate across the switch, run 2, miner cpu0 (one thread, the Mac shared with other agents' builds, so approximate): the cumulative rate read 0.024 MH/s through the v2 epochs (30 to 151 s), then fell to 0.021 MH/s cumulative by 271 s (100 s under v3), which puts the v3 interval rate near 0.017 MH/s, about 30 percent under v2 on the CPU interpreter (the era's strided windowed loads and the mixer path). The node has no per-block verify timing line; the CPU verifier is measured in `igneum-pow` (the mixer agent, one M5 Max core, ms per 32-lane unit, same minute, cited from ca2-mixer 1ab8b21's message of 5 October 2026 22:10Z): + +| Path | readwidth | ca2-v3 88dafbc (before the fix) | ca2-v3 d233fa1 (after) | +|---|---|---|---| +| v2 (the live devnet) | 0.607 / 0.610 | 1.332 | 0.609 / 0.611 | +| v3 at x4 | | | 1.238 | +| v3 at x8 (the class) | | | 2.077 (worst cold 2.15) | + +The regression was `memhard::derive_items` at m = 1 (2.2x); the fix dispatches to an out-of-line `derive_items_mask`, one instance per cache size with the line mask a constant. A v3 block costs the node about 3.4x a v2 block to verify (2.08 against 0.61 ms per unit). Epoch 0's v2 id is the same in both runs (`8f8806638d59850f`: a v2 program is untouched by the era code, on the chain as in the packs); the v3 ids differ from run 1 because the era draw is now inside the class. @@ -140,6 +148,6 @@ The PC's test stage builds the fork's test binaries with the plan's feature set, - The GPU workers' class refusal was checked by the C test of `pf_pack_class_ok` and the syntax of both hosts, not by a live worker on a v3 pack: the integration's bit-exact gate (G1) is where a real worker first builds a v3 pack. - `next_pair` keeps the current era seed for the next epoch; an epoch boundary that is also an era boundary (once per 180 days) would prepare the wrong era, and the job line then names the right one, so the worker refuses the prepared pair and the miner prepares again (one wasted compile, no wrong block). The VDF era seed replaces the stand-in before this matters. - Done 22:05Z: ca2-cache rebased as 1950661 fast-forwarded (the first attempt, 2de19e5 on 464d6e1, conflicted in 8 files and was aborted); igneum-pow 53 + 19 tests and the packfile test pass; the fork's kaspa-pow, miner and kaspad check clean against the merged crate (seam unchanged). `V3_CLASS = { era: None, hot: None, ..LoadClass::MX4 }`. -- Owed: the mixer agent's verifier fix (the 2.2x on the v2 path at m = 1). ca2-mixer's tip 54bbfcc (22:10Z) carries the MX8 candidate, tests/mixer.rs, tests/scratch.rs and the measure docs, not the fix; a merge of it into ca2-v3 conflicts in docs/bench-log.md and proto-metal/packbench.swift only (generator.rs and verify.rs auto-merge). Merged when the fix commit arrives, then a rebuild and gate run 3 if asked. +- Done 22:12Z: ca2-mixer 16dfd1e (MX8 candidate, tests) merged at 4e733bb with two one-line fixes the merge needed (3a7fba7 `MX8` gets `era: None, hot: None`; 795472e tests/scratch.rs's `Instr` literals get `win: 0, off: 0`); then ca2-mixer 1ab8b21 (the verifier fix, `V3_CLASS = { era: None, hot: None, ..LoadClass::MX8 }`: class v3 is x8 by the coordinator's decision of 22:05Z) merged at d233fa1. Checks on d233fa1: igneum-pow 53 + 4 + 19 + 7, packfile 0 failures, the fork's `cargo test -p kaspa-pow --features igneum-pow` 14 pass, the release rebuild 1 min 57 s. - Owed (0.3.12, coordinator's ask of 5 October 2026 22:20Z): per-day dataset reuse in the CUDA and OpenCL workers (a `Day` object shared by consecutive pairs, the cache freed after the build); the Metal worker already keys datasets by day. Until then the iGPU tier mines v3 with a dataset rebuild per epoch on those two workers. - Wire compatibility: `RpcPowEpochInfo` gained five fields in its Borsh form (wRPC) and five proto fields (gRPC); the gRPC side reads an old node's zeros as v2 / never / none; the Borsh form is versioned by `GetBlockTemplateResponse` (version 2 carries the whole struct), so a 0.3.11 wRPC client against a 0.3.10 node reads short: the miner uses gRPC, the console reads JSON (serde defaults). From 40a94996f1a73568e0a7f416886b66be7c64d635 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:19:04 +0000 Subject: [PATCH 110/158] Analysis: proving methods, the memory anatomy of the SP1 prover, the survey of every proving route, and the ranked path to 12 GB cards the project lead, 5 October 2026 22:05 UTC: if the GPU-server patch does not enable 12 GB cards, research proving and see if there are different methods. docs/analysis/proving-methods.md: (1) the memory anatomy of SP1 6.8.1 from its code (the 20 GB panic in sp1-gpu builder.rs, the maximum-sized trace buffers, the mempool that never releases, the fixed recursion shape), the theoretical floor for the adopted shard (about 10 to 11 GB, approximate); (2) the survey: SP1, RISC Zero, Airbender, ZisK, OpenVM, Pico, Ziren, Stwo, Jolt, Ceno, Nexus, Valida, Powdr, Binius, the 2026 entrants, the sumcheck and GKR family, folding, continuations, distributed proving, AMD and Apple backends, every claim cited; (3) per route the change to the guest, aggregator and node verifier, the cost in agent days, the risk, and whether 12 GB under 60 s is reached; (4) the ranking: re-size SP1's server with S_p as the dial and one server per card (no consensus change), RISC Zero as version 2 for the fallback and Apple, the sumcheck family in five years; (5) the tier consequences and the public line. Co-Authored-By: Claude Fable 5.1 --- docs/analysis/proving-methods.md | 411 +++++++++++++++++++++++++++++++ 1 file changed, 411 insertions(+) create mode 100644 docs/analysis/proving-methods.md diff --git a/docs/analysis/proving-methods.md b/docs/analysis/proving-methods.md new file mode 100644 index 00000000..59157332 --- /dev/null +++ b/docs/analysis/proving-methods.md @@ -0,0 +1,411 @@ +# Proving methods: why the prover needs 14 GB, what else exists, and how a 12 GB card gets to prove + +5 October 2026, from the project lead at 22:05 UTC: "if this doesn't enable 12 GB cards, then do a full deep research task on proving +and see if there are different methods." "This" is the prover-floor agent's patch of SP1's GPU server (branch +`prover-floor`), running tonight. This document is research and reading, not measurement: every number of ours is from +`docs/bench-log.md` with its entry named; every claim about another system cites its repository file, its documentation +page or its paper, or is labelled approximate. Status words follow `docs/spec/00-overview.md` 0.2. Day estimates follow +the project lead's rule of 3 October 2026: hours of agent time, never weeks. + +The facts this starts from (bench-log, "proving v1", 5 October 2026; `docs/plans/proving-v1.md`; `docs/analysis/amd-proving.md`): + +| Fact | Number | +|---|---| +| SP1 6.8.1's GPU server, an empty shard (280,706 cycles), the card to itself | 13,874 MiB peak, 2.2 s compressed | +| The adopted v1 shard (`S_p` 30,000 pgas, 4,717,439 cycles) | 20,434 MiB, 4.3 s; 22,210 MiB and 13.2 s beside the miner | +| The prototype shard (6.75 M pgas, 60.4 M cycles) | 28,307 MiB, 10.8 s; flat at 28.3 GB from 20 M cycles up | +| Aggregation, chained, per block, on a mining 5090 | 9.6 to 9.7 s; 2.2 to 2.5 s with the card to itself | +| The CPU path (PC 1, 16 cores) | 282 s a shard whatever its size, 29.5 to 30.5 GB RSS | +| AMD and Apple GPUs | no zkVM proves on AMD; RISC Zero has a Metal prover, SP1 does not | +| The promise | `site/litepaper.html`: "Target: shard size will be set so a 12 GB card proves one shard in about 20 seconds"; the design goal is every block proven within about a minute by the miners' own cards | + +## 1. The memory anatomy of a STARK-based zkVM prover, and why the floor is where it is + +### 1.1 What SP1 6.8.1 is + +SP1 6.x is not the univariate FRI STARK of the earlier SP1 releases (Succinct calls Hypercube the "first zkVM built entirely on a multilinear polynomial-based proof system", blog.succinct.xyz, sp1-hypercube, 20 May 2025). The crates it pulls say what it is: `slop-multilinear`, `slop-sumcheck`, +`slop-jagged`, `slop-stacked`, `slop-basefold`, `slop-whir` (the `~/.cargo/registry` of this Mac; `proving/igneum-prove/Cargo.toml` +pins `sp1-sdk = "=6.8.1"`). The architecture Succinct calls Hypercube: the execution trace is a set of multilinear +polynomials over the 31-bit KoalaBear field (`sp1-hypercube-6.8.1/src/verifier/config.rs`: `SP1BasefoldConfig = +Poseidon2KoalaBear16BasefoldConfig`), the constraints are checked by a zerocheck sumcheck and the lookups by a LogUp GKR +(`sp1-gpu/crates/zerocheck`, `sp1-gpu/crates/logup_gkr`), and the polynomial commitment is "jagged": every table's +columns, whatever their heights, are concatenated into one long vector, stacked into rows of height `2^log_stacking_height` +and committed with BaseFold, a FRI-like folding over a Reed-Solomon code (`sp1-gpu/crates/basefold/src/fri.rs`, +`slop-basefold-6.8.1/src/verifier.rs`). The proof system parameters, from `sp1-primitives-6.8.1/src/fri_params.rs` and +`sp1-prover-6.8.1/src/components.rs`: + +| Parameter | Value | Where | +|---|---|---| +| Field | KoalaBear, 31 bits, 4 bytes an element; extension degree 4 (16 bytes) | `sp1-primitives` | +| Core stage: Reed-Solomon blowup | `CORE_LOG_BLOWUP = 2`, so the codeword is 4x the data | `fri_params.rs:5` | +| Core stage: stacking height, maximum rows per table | `CORE_LOG_STACKING_HEIGHT = 21`, `CORE_MAX_LOG_ROW_COUNT = 22` | `components.rs:16,17` | +| Core shard limits (the executor's cut) | `MAX_SHARD_SIZE = 2^24` cycles, `ELEMENT_THRESHOLD = 2^28 + 2^27 = 402,653,184` trace elements, `HEIGHT_THRESHOLD = 2^22` rows | `sp1-core-executor-6.8.1/src/opts.rs:9-12` | +| Recursion (compress) stage | blowup 2 (`RECURSION_LOG_BLOWUP = 2`), stacking height 20, max rows 2^21 | `fri_params.rs:6`, `sp1-verifier-6.8.1/src/compressed/config.rs:1,2` | +| Shrink and wrap stages | blowup 3 (8x), 22 bits of grinding, stacking 18 and 21 | `fri_params.rs:17,18,7`, `components.rs:37-40` | +| Recursion arity | 4 proofs per compose step (`DEFAULT_MAX_COMPOSE_ARITY = 4`, `DEFAULT_MAX_REDUCE_ARITY = 4`) | `sp1-prover-6.8.1/src/worker/config.rs:183,193` | +| Workers | 4 core workers, 8 recursion prover workers, 4 recursion executors, 4 deferred workers, buffers of 4 to 8 | `worker/config.rs:188-205` | + +The stages a shard goes through (`sp1-prover-6.8.1/src/worker/controller/*.rs`): execute (the RISC-V executor cuts the +run into core shards at the thresholds above); core (one jagged-PCS proof per core shard, on the GPU); normalize and +compose (each core proof is verified inside a recursion program, then proofs are folded 4 at a time until one remains, +the "compressed" proof, 1,272,897 bytes for every shard we have proven, bench-log 4 and 5 October); deferred (what the +aggregator uses: `verify_sp1_proof` inside a guest, `proving/igneum-prove/aggregator/src/main.rs`); shrink and wrap +(to a BN254 STARK, then Groth16 or Plonk; not run here, ledger P3). + +### 1.2 The terms, and which scale with the shard + +Every STARK-family prover holds these buffers on the device at its peak, in some order and with some overlap. The +sizes below are from the constants of 1.1 and the allocation code of `sp1-gpu`; where a buffer's size is the actual +trace rather than the maximum, the row says so. + +| Term | What it is | Size rule | SP1 6.8.1 on a 32 GB card | Scales with the shard? | +|---|---|---|---|---| +| Main trace | the witness: one element per cell of every table the shard touched | `cells x 4 bytes`, where cells = sum over tables of rows x columns; the executor cuts a new core shard at 402,653,184 cells | the device buffer is allocated at the MAXIMUM, not the actual trace: `allocate_and_initialize_traces` takes `max_trace_size` and allocates `max_trace_size` felts plus `max_trace_size / 2` u32 of index (`sp1-gpu/crates/jagged_tracegen/src/lib.rs:484-503`), 6 bytes a cell; the core prover's `max_trace_size` is `element_threshold + 2^21` (`prover_components/src/builder.rs:70-71`): **2.26 GiB** on a card over 30 GB, 1.61 GiB on a 24 GB card (the threshold drops by 2^26 + 2^25 + 2^24 when memory is 30 GB or under, `builder.rs:41-45`) | no: fixed at the maximum shard, whatever the trace | +| Preprocessed trace | the program's own tables (the ELF as a `Program` AIR, the byte and range tables) | program size x its columns plus 2 x 2^16-class tables | small for a 2.8 MB guest ELF (`elf/manifest.json`); not isolated | with the guest, not the shard | +| Codeword (the LDE) | the stacked polynomial encoded at rate 1/4 for BaseFold | `stacked cells x 4 (blowup) x 4 bytes`; the stacked length is the actual cell count padded to a multiple of 2^21 | 4.7 M cycles: approximate, the actual trace; 60 M cycles: the shard is 7 to 15 core shards of up to 402 M cells, each encoded to 6 GiB at the blowup, one or more in flight | yes, up to the core-shard cap; past the cap the shard count grows and the per-shard term stays | +| Merkle commitment | Poseidon2 hashes of the codeword rows | `rows x 8 elements x 4 bytes x 2`, rows = 2^21 x blowup | about 0.5 GiB at full stacking, approximate | with the stacked rows | +| Zerocheck and GKR | the constraint sumcheck over the extension field, and the LogUp GKR layers | extension elements are 16 bytes; the sumcheck holds a folded copy of the trace in the extension field, which is 4x the base trace at the first round and halves each round | up to about 4x the live trace in the first round, approximate (`sp1-gpu/crates/zerocheck/src/primitives.rs:174,287`: `Buffer` of `new_total_length`) | yes | +| Recursion traces | the normalize and compose programs' own traces, verifying core proofs | fixed-shape programs: `RECURSION_TRACE_ALLOCATION = 2^27` cells (`builder.rs:15`), allocated at 6 bytes a cell: **0.75 GiB** per recursion prove, at blowup 4 a 2 GiB codeword plus its own zerocheck | fixed per recursion step; the number of steps is log4 of the core-shard count | no (per step) | +| Shrink and wrap traces | the two last stages, not run by us | 2^25 and 85,376,340 cells (`builder.rs:16,19`): 0.19 and 0.48 GiB | only when wrapping | no | +| Proving keys and program cache | the recursion programs (`vk_map.bin`, the normalize cache of 5 programs) and the shard program's setup | `DEFAULT_NORMALIZE_PROGRAM_CACHE_SIZE = 5` (`worker/config.rs:192`); the key setup took 14.6 s on the 5090 (bench-log 4 October) | not isolated | no | +| Pinned host buffers | the staging copies on the PC side | 4 core workers x `max_trace_size` x 4 bytes = **6.0 GiB** of pinned RAM, plus 4 x 0.5 GiB for recursion (`prover_components/src/components.rs:99-103`, `builder.rs:76,95`) | this is the 7.9 GB WSL2 working set measured on 5 October | no | +| The allocator | CUDA's default memory pool with its release threshold set to `u64::MAX` (`sp1-gpu/crates/cuda/src/task.rs:152,190-199`): freed blocks are never returned to the driver | `nvidia-smi` therefore reports the high-water mark of everything above, and it stays until the server exits | this is why the memory curve is flat between shards of different size | no | + +Two facts from this table explain the measurements: + +1. **The server refuses small cards by code.** `local_gpu_opts()` reads the card's total memory, adds 4 GB, and panics + under 24: `"Unsupported GPU memory: {gpu_memory_gb}, must be at least 24GB"` (`sp1-gpu/crates/prover_components/src/builder.rs:35-38`). + A 16 GB card (16 + 4 = 20) and a 12 GB card (16) never start; a 20 GB card is the smallest that does. The 13.9 GB + floor measured on the 5090 is therefore not the whole story for a 12 GB card: on this build the card is refused + before any buffer is allocated. Any route through SP1's GPU server starts by removing this line. +2. **The environment knobs do not reach the floor** because the same function overwrites `element_threshold` with the + compile-time constant (`builder.rs:41-48`); only `HEIGHT_THRESHOLD` passes through, which is why the sweep's + `ELEMENT_THRESHOLD 2^26` rows changed nothing and `HEIGHT_THRESHOLD 2^20` took 5.4 GB off the 60 M-cycle shard + (bench-log, "the 12 GB requirement", 5 October 2026). The worker counts only slow the proof (11.4 s to 20.8 s) + because the device buffers are sized by `max_trace_size`, not by the worker count. + +### 1.3 Why the floor is 13.9 GB for an empty shard + +With the release threshold at `u64::MAX`, the peak is the high-water mark over the whole pipeline. For an empty shard +the core trace is small (280,706 cycles), so the fixed-shape terms dominate: the 2.26 GiB main-trace buffer allocated +at the maximum, the recursion step over a fixed-shape normalize program (a 0.75 GiB trace buffer, its 4x codeword in +the extension field for the zerocheck, its Merkle tree), the proving-key and program caches, and the deferred and +compose machinery that a compressed proof always runs once. The decomposition of the 13.9 GB into those terms is +approximate until the prover-floor agent's profile lands (branch `prover-floor`, tonight): the figure that is not +approximate is that none of it is the witness (5 to 22 KB a shard) and none of it is the shard's cycles (the same +13.9 GB at 280 k cycles and 556 k cycles, bench-log "the S_p curve"). + +The step from 13.9 GB (empty) to 20.4 GB (4.7 M cycles) is the live trace: the 4.7 M-cycle shard is one core shard +(its trace area is under 402 M cells, so it was not split; approximate from the memory curve, the cell count is not +logged by the host), and its codeword, zerocheck and GKR buffers are sized by its actual cells. The step from 20.4 GB +to 28.3 GB (20 M cycles and up) is the second and later core shards in flight at once: 4 core workers with a buffer of +4 (`worker/config.rs:188,189`) let several core shards' codewords exist at the same time; past 20 M cycles the pipeline +is full and the peak is flat, which is what the curve shows (28,371 MiB at 20 M cycles, 28,307 at 40 M and 60 M). + +### 1.4 The theoretical floor for our guest at the adopted shard + +If every buffer were sized to the shard rather than to the maximum, the adopted v1 shard (4.7 M cycles) would need, +approximate, from the rules of 1.2: + +| Term | Rule | Approximate bytes | +|---|---|---| +| Main trace, actual | 4.7 M cycles x about 60 cells a cycle (the `Add` and `Addi` tables cost 33 and 30 columns a row, a memory access adds 20 and a global interaction 241: `sp1-core-executor-6.8.1/src/artifacts/rv64im_costs.json`) | about 280 M cells, 1.1 GB | +| Codeword at blowup 4 | 4x | 4.5 GB | +| Zerocheck first round in the extension field | 4x base, halving each round | 4.5 GB at the peak round, falling | +| Merkle tree | rows x 32 bytes x 2 | 0.3 GB | +| Recursion step, fixed | 2^27 cells x 6 bytes plus its 4x codeword and extension copies | 2 to 3 GB, approximate | +| Keys and caches | | under 1 GB, approximate | +| Peak, if the core stage and the recursion stage do not overlap and the pool releases | | **about 10 to 11 GB**; about 6 GB if the blowup-4 codeword is replaced by a rate the sumcheck does not need (see 2.4) | + +So the adopted shard is, on paper, a 12 GB card's shard with the server re-sized and nothing else changed, and it is a +12 GB card's shard with 4 GB to spare if the shard is halved (`S_p` 15,000 pgas, 2.4 M cycles: the planner cuts at +transaction boundaries to any budget, `core/src/plan.rs`, and the fee switch of 5 October already moved `S_p` once). +What the paper figure does not say is the time: a smaller card proves slower, and 60 s with the miner running is the +bound (section 3). The prover-floor agent is measuring the real figure; this section says what it should find and why. + +### 1.5 RISC Zero's anatomy, for comparison + +RISC Zero is the FRI STARK the textbooks describe, and its constants make the same table easy to read +(`~/.cargo/registry`, `risc0-zkp-3.0.4/src/lib.rs`, `risc0-circuit-rv32im-4.0.4/src/zirgen/defs.rs.inc`): + +| Term | Value | Where | +|---|---|---| +| Field | BabyBear, 31 bits; extension degree 4 | `risc0-core` | +| Segment size | `DEFAULT_SEGMENT_LIMIT_PO2 = 20` (1,048,576 cycles), `MIN_CYCLES_PO2 = 13`, `MAX_CYCLES_PO2 = 24`; `DEFAULT_MAX_PO2 = 22` for the verifier | `risc0-circuit-rv32im-4.0.4/src/execute/mod.rs:39`, `risc0-zkp-3.0.4/src/lib.rs:35-38`, `risc0-zkvm-3.0.4/src/receipt.rs:884` | +| Trace width | data 211 + accum 103 + code 1 = 315 columns; globals 90, mix 36 | `defs.rs.inc:7-11` | +| Blowup | `INV_RATE = 4`; 50 queries; FRI fold 16 | `risc0-zkp-3.0.4/src/lib.rs:41-51` | +| Recursion | lift, join and resolve programs at `RECURSION_PO2 = 18` rows | `risc0-zkvm-3.0.4/src/host/recursion/prove/mod.rs:58` | +| The GPU buffers | `check + ctrl + data + accum + mix + out` elements x 4 bytes, printed by the CUDA HAL at `eval_check` | `risc0-circuit-rv32im-4.0.4/src/prove/hal/cuda.rs:181-200` | + +From those constants the trace of a segment is `2^po2 x 315 x 4` bytes and its LDE 4x that, so, approximate: po2 18 is +0.3 GB of trace and 1.5 GB with the LDE, po2 19 is 3.1 GB, po2 20 is 6.2 GB, po2 21 is 12.3 GB, before the check +polynomial, the extension-field accumulators and the Merkle trees. Two things follow. A RISC Zero segment at the +default 2^20 is in the same class as one SP1 core shard, not smaller. And a RISC Zero segment at 2^18 or 2^19 is a +2 to 4 GB object: the only reason a 12 GB card could not prove one is the fixed overhead of the recursion circuits +(2^18 rows each) and the allocator, which is the measurement the prover-floor agent takes on PC 2 if SP1 cannot go +under 11 GB. Section 2.2 carries the documented numbers. + +### 1.6 Which terms the shard size can move, and which it cannot + +| Lever | Moves | Does not move | +|---|---|---| +| Our `S_p` (pgas per shard) | the live trace, the codeword, the zerocheck: everything in 1.2 marked "yes" | the maximum-sized buffers, the recursion step, the keys, the pool | +| SP1's `HEIGHT_THRESHOLD` (the one knob the server honours) | the rows per table in one core shard, so the live buffers | the fixed terms (measured: 13,861 MiB on the empty shard with every knob at its minimum) | +| A server patch: size `max_trace_size` to the shard, release the pool, one core worker | the 2.26 GiB buffer, the high-water mark, the in-flight count | the recursion step's fixed shape and the key caches | +| A different proof system | the blowup (sumcheck-only and linear-code systems have none, 2.4), the recursion shape | the trace itself: a RISC-V cycle costs tens of cells in every zkVM | + +## 2. Every current proving route + +Read 5 October 2026, 22:10 to 23:00 UTC, by four research agents and this one; every cell names its page or file. +"Not documented" means the project publishes no figure, which for a memory floor is itself the finding. + +### 2.1 The zkVMs with a GPU prover + +| Prover | Proof system, field, chunk | GPU support and the documented minimum memory | Throughput, on what | Verification of the recursive proof; wrapper | Licence | State, October 2026 | +|---|---|---|---|---|---|---| +| **SP1 6.8.1** (ours) | Hypercube: multilinear, jagged PCS, BaseFold, LogUp GKR; KoalaBear; core shards of up to 2^24 cycles and 402 M cells (section 1) | CUDA only. Docs: "24GB or more VRAM", compute capability 8.0+, Linux x86_64 (docs.succinct.xyz, hardware-acceleration page). Code: panic under 20 GB physical (`builder.rs:35-38`). Measured here: 13.9 GB floor, 20.4 GB at the adopted shard, 28.3 GB at the prototype shard. Issue #2950: two clients on a 48 GB L40S hold 41 to 43 GB; a single 6 GiB tensor allocation failed | 4.3 s for the adopted shard, 10.8 s for the prototype one on a 5090 (bench-log); Succinct: 99.7% of Ethereum blocks under 12 s on 16 x RTX 5090 (blog.succinct.xyz, 18 Nov 2025) | compressed proof 1,272,897 bytes, verified in 0.032 to 0.040 s here (`--mode verify-segment`); Groth16 about 260 bytes and about 270 k gas, Plonk about 868 bytes and 300 k gas (docs, proof-types page); the Groth16 wrap needs about 14 GB of host RAM, Plonk about 60 GB (hardware-requirements page) | Apache-2.0 or MIT for the repository including `sp1-gpu/` (`LICENSE-APACHE`, `LICENSE-MIT` at the root; no separate licence under `sp1-gpu/`); `sp1-cluster` is Business Source 1.1 | v6.8.1 of 24 Sep 2026 is the latest tag; mainnet for Ethereum proving since 19 Feb 2026 (blog); AMD port PR #2668 closed unmerged 20 Mar 2026; no Metal, Vulkan or WebGPU | +| **RISC Zero 3.0.x** | FRI STARK (DEEP-ALI), BabyBear, Poseidon2, blowup 4, 50 queries; segments of `2^po2` cycles, default po2 20, allowed 13 to 24 (section 1.5); lift, join, resolve recursion at 2^18 rows; keccak as a separate circuit | CUDA and **Metal** (`risc0/sys/kernels/zkp/metal/`; on Apple silicon the Metal path is on automatically, `risc0/zkvm/build.rs`). Documented memory per segment: Bento design page, 1 M cycles 9 to 10 GB, 2 M 17 to 18 GB, 4 M 32 to 34 GB; Boundless performance page, the largest `SEGMENT_SIZE` per card: 8 GB card po2 19, 16 GB po2 20, 20 GB po2 21, 40 GB po2 22; docs: "less than 10 GB available: change the segment size limit" (dev.risczero.com, local proving); `env.rs:190-192`: "lowering this value by 1 will cut memory consumption by about half". PR #3761 (June 2026): po2 22 did not fit a 24 GB 4090 until the `low_vram` buffer reuse | 4090: 808 kHz at po2 21, 1,207 kHz at po2 22 with PR #3761 (end to end to a succinct receipt); Apple M2 Pro about 14 kHz on the 2023 datasheet, approximate (the page was unreachable tonight); real-time Ethereum on about 160 x 4090 (blog, approximate) | succinct receipt 222,668 bytes, constant; about 100 ms to verify, approximate (`gsr-stark-verifier` PR #5, mirrored docs); Groth16 seal 256 bytes, about 200 to 300 k gas, approximate; the Groth16 wrapper is x86 only, not on Apple silicon (docs) | Apache-2.0 or MIT, CUDA and Metal kernels included (`risc0/sys/kernels/zkp/cuda/eltwise.cu:1-13`); Bento is BSL 1.1 with a change date already passed | v3.0.6 of 17 Jul 2026 on the maintained line; `main` is 5.0.0 with no release body; `RISC0_PROVER=actor` multi-GPU scheduler experimental since 3.0.1 (`r0vm/src/actors/factory.rs:183-195` carries measured per-po2 memory tokens: po2 18 = 8, 19 = 10, 20 = 15, 21 = 24; lift and join = 3) | +| **Airbender** (Matter Labs) | DEEP STARK, FRI, Mersenne31; chunks of 2^22 cycles; Boojum then FFLONK wrap (docs.zksync.io, airbender page) | CUDA only. "any GPU with 22GB RAM" for production (zksync.io/airbender); the code has memory presets `GiB21` (24 GB cards) and `GiB30`, raised from 29 because Ethereum blocks failed to allocate at 29 GiB (PR #448, `gpu/execution_prover/src/prover/config.rs`); the final SNARK is CPU with about 150 GB of RAM (`docs/gpu.md`) | one H100: 21.8 MHz base layer, 8.5 MHz end to end, about 35 s an Ethereum block (June 2025 post); ethproofs.org today: 4 x 5090 2.3 s average | FFLONK over BN254 on chain; gas not published | MIT or Apache-2.0 | v0.6.0-rc.2; Veridise audit Feb to Apr 2026; live for ZKsync Atlas chains | +| **ZisK** (Polygon spin-out) | eSTARK over Goldilocks (pil2-stark), Poseidon2, approximate; main instance 2^22 to 2^23 rows, chunks of up to 2^22 steps (PR #1238) | CUDA only, CUDA 12.9+; **no VRAM floor documented**; workers need about 32 GB of host RAM, the assembly emulator 64 GB (docs, limits and distributed pages); Cysic's Venus fork submits from one RTX 4090 | 4 x 5090: p99 9.62 s on Ethereum blocks (Aug 2026); 24 x 5090 6.56 s average (Nov 2025) | PLONK wrapper verified by Solidity (`zisk-contracts`); 128-bit claimed | Apache-2.0 or MIT | v1.3.1-alpha, 30 Sep 2026, "undergoing security and correctness audits" (README) | +| **OpenVM 2.0** (Axiom) | SWIRL: sumcheck, zerocheck, LogUp GKR, stacked reduction into WHIR; BabyBear; segments by metered trace height (blog.openvm.dev/2.0) | CUDA only; "at least 24GB of VRAM": L40, 4090, L40S, 5090 (blog.openvm.dev/openvm-gpu) | 11.4 MHz on one 5090, 139 MHz on 16; 2.1 preview: 4 x 5090 p99 9.7 s | STARK proof under 300 KB; Halo2-KZG wrapper, 316 k gas; the Halo2 wrap 8.1 s on a 5090 | MIT or Apache-2.0, GPU prover included | v2.0.2 of 14 Aug 2026; zkSecurity audit of SWIRL; Scroll's prover builds on it | +| **Pico** (Brevis) | Plonky3 STARK, KoalaBear default; chunk size a parameter with no documented default | CUDA via `pico-gpu`; **no VRAM figure published**; every run on 32 GB 5090s | Prism 2.1: 16 x 5090 over two machines, 4.87 s average on Ethereum blocks | Groth16 via gnark | core MIT or Apache; **`pico-gpu` is BUSL-1.1** and "not recommended for production" (its README) | v2.1.2, Aug 2026; Sherlock audit | +| **Ziren** (ZKM, MIPS) | Plonky3-class, KoalaBear, LogUp GKR, WHIR | CUDA 12, compute capability 8.6+, "24 GB VRAM or higher"; the GPU prover is a Docker image pinned by digest, source "planned H1 2026" (docs.zkm.io prover page; an independent evaluation of v1.1.4 says the GPU path is not open) | one 5090: 5.9 MHz on a 288 M-cycle block; 4 GPUs 3.1 to 3.3x | compressed proof 603 KiB; Groth16 or PLONK | core MIT or Apache; GPU image licence unspecified | v1.2.7; no public audit cited | +| **Stwo / S-two** (StarkWare) | Circle STARK over Mersenne31; blowup 1 (rate 1/2), 70 queries, 26 bits of grinding | **CPU SIMD first** (AVX2, AVX-512, NEON, WASM); GPU through ICICLE-Stwo (Ingonyama): about 3 GB of trace in GPU memory, out of memory from 2^23 rows; a WebGPU port of the constraint evaluation (zkSecurity blog, April 2025); client-side proving under 1 GB after a spill allocator (third-party PR) | 620 k Poseidon2 a second on an M3 laptop | via a Cairo verifier (proofs of proofs); sizes not published here | Apache-2.0 | live on Starknet mainnet since 3 Nov 2025; no RISC-V guest of its own (Nexus 3.0 is the RISC-V zkVM on it, BUSL-1.1 until 2029) | +| **Jolt** (a16z) | sumcheck and lookups (Lasso lineage, Twist and Shout memory checking); PCS Dory over BN254 by default, or **Akita**, a lattice commitment over a 128-bit prime field (Sep 2026, "Lattice Jolt"); RV64IMAC; no continuations (the book's recursion page is "under construction") | **No CUDA in the public repository** (LayerZero's "Jolt Pro" CUDA port is private); **Metal**: PR #1938 merged 30 Sep 2026 (the `jolt-metal` runtime crate), PR #1733 (the full prover on Metal) still a draft. Memory: "about 200 bytes per cycle" with Akita (a16z substack, Sep 2026); the book: "under 2 GB of memory per million cycles"; a streaming prover bounded to "a few GBs" is planned, not shipped | over 2 M cycles a second on a laptop CPU with Akita, over 10 M with Metal on a MacBook (a16z substack, Sep 2026); PR #1733: M5 Max, 2^25 cycles in 19.8 s, 2^27 in 77 s at an 89.4 GiB footprint | proof about 50 KB (Dory) or 65 to 80 KB (Akita); verify sub-second, approximate; on-chain 1.3 to 2 M gas estimated in 2024; no Groth16 wrapper shipped | MIT or Apache-2.0 | `v0.3.0-alpha` is the last tag (1 Oct 2025); README: "not suitable for production use"; no audit | +| **Ceno** (Scroll) | GKR tower prover, BabyBear, WHIR or BaseFold PCS; RV32IM | CUDA, but the real HAL is in a **private** `ceno-gpu` repository (the public one is a mock); no memory numbers | 2 GPUs 1.6x over one (PR #1403, Sep 2026) | via OpenVM recursion to Halo2 | Apache-2.0 | README: "under construction and not suitable for use in production" | +| **Nexus 3.0** | on Stwo (Circle STARK, M31) | no GPU path documented | none published | not published | **BUSL-1.1** until 10 Feb 2029 | last push 6 Jan 2026; folding (Nova family) abandoned June 2025 for the STARK | +| **Valida** (Lita) | Plonky3 STARK | no GPU; a CUDA port "underway" in July 2025 | none current | not published | Apache or MIT | dormant since Sep 2025; documented soundness issues in its own benchmarks page | +| **Powdr** | no longer a zkVM: `powdrVM` archived; powdr is autoprecompiles on OpenVM | OpenVM's | OpenVM's | OpenVM's | MIT or Apache | tooling layer; "DO NOT USE FOR PRODUCTION" | +| **Binius / Binius64** (Irreducible) | binary-field SNARK, BaseFold-style FRI over GF(2^64) words | CPU SIMD only; the FPGA work was dropped 9 Sep 2025 ("FPGAs underperformed GPUs"); no GPU | ECDSA aggregation about 5x over SP1 and R0VM on L40S GPUs, on CPU (the page carries methodology corrections) | hash-based; no recursion shipped | Apache-2.0 or MIT | **the company shut down 12 Nov 2025**; the only zkVM on it (PetraVM) is archived | +| 2026 entrants | Cysic Venus (a ZisK fork with cudaGraph tuning and an FPGA backend, Apache or MIT, "do not use in production"); Zilkworm (Erigon's C++ guest on Airbender, 2 x 5090 9.3 s); zkDTVM (evmone guest, 4 x 5090 4.7 s, no public docs); Delphinus zkWasm (Halo2 on BN254, a 4090 minimum plus 58 GB of host RAM); Miden (Goldilocks STARK, client-side, Metal via `miden-gpu`, mainnet alpha planned); Boojum (2023 claim of proving on a 16 GB card, superseded by Airbender) | none states a floor under 24 GB on a GPU | | | | | + +The reading of the table. No shipped zkVM documents a GPU floor under 24 GB except RISC Zero, whose memory is a +function of a runtime knob (`segment_limit_po2`) and is published per card size by Boundless. SP1's 24 GB is a line +of code, not a property of the proof system: Airbender, OpenVM and Pico all pad to the card they tune on, and all +three say 24 or 32 GB because their market is Ethereum blocks on 5090 clusters. The real-time race has collapsed to 2 +to 4 consumer cards per block (ethproofs.org, 5 October 2026), which is why nobody is tuning for a 12 GB card: the +customer buys 5090s. Igneum's customer is the miner who already owns the card, so Igneum has to do the tuning itself. + +### 2.2 The sumcheck and GKR family against FRI STARKs, in memory terms + +| Family | What it holds at the peak | Blowup | The GPU figure today | Source | +|---|---|---|---|---| +| FRI STARK (RISC Zero, Airbender, ZisK, Pico, Stwo, SP1 3 and 4) | trace, its Reed-Solomon codeword at the blowup, the Merkle trees, the DEEP quotient in the extension field | 4x (RISC Zero, Airbender), 2x (SP1 3 and 4, approximate), 2x (Stwo at rate 1/2) | RISC Zero: 9 to 10 GB per 1 M cycles (Bento) | section 1.5; Boundless pages | +| Sumcheck with a hash-based PCS (SP1 Hypercube, OpenVM SWIRL, Ceno, Ziren) | the trace as multilinears, the extension-field folded copies of the zerocheck and GKR, and the BaseFold or WHIR codeword of the stacked polynomial (still a Reed-Solomon encoding, at 4x in SP1, section 1.1) | 4x of the stacked data in SP1; WHIR's rate is a parameter | SP1: the fixed 13.9 GB plus about 6.5 GB for a 4.7 M-cycle shard (measured) | section 1 | +| Sumcheck with a curve or lattice PCS (Jolt) | the trace and the one-hot columns; **no codeword at all**: Dory commits by MSM and Akita by lattice hashing, so memory is bytes per cycle with no blowup | none | no GPU figure: 200 bytes a cycle on CPU (Akita), so the adopted 4.7 M-cycle shard is about 0.9 GB of prover RAM, approximate (derived) | a16z substack, Sep 2026; the Jolt book, streaming page | +| GKR (Expander, Ceno) | the circuit witness layer by layer; no codeword for the inner layers | none inside; a PCS for the inputs | Expander: 16 MB per Keccak, approximate | Polyhedra blog (returned 530 tonight) | +| Linear-code PCS (Ligero, Brakedown, Ligerito, Blaze) | one encoded matrix and one Merkle tree; linear time, no FFT | rate 1/2 to 1/4 | no prover memory benchmarks found; Linea's Vortex is the only production use | eprint 2021/1043, 2025/1187, 2024/1609; `linea-monorepo/prover/protocol/compiler/vortex` | +| Binius (binary field) | words of GF(2^64) and a BaseFold FRI | 2x to 4x | none; CPU only; company closed | irreducible.com posts | + +The memory law in one line: a FRI or BaseFold prover holds `blowup x trace` plus the trace itself plus extension-field +working copies, so 8 to 12 bytes per cell at the peak; a Jolt-class prover holds the trace and its lookups at about 4 +bytes per cell and commits without encoding. The figure that matters for us is not the ratio but the absolute: our +adopted shard is small enough (about 280 M cells, section 1.4) that a FRI-class prover sized to it fits a 12 GB card, +and a Jolt-class one fits a phone. The reason SP1 does not fit today is section 1.3, not the proof system. + +### 2.3 Folding schemes + +| Scheme | Prover memory per step | The verifier at the end | Field | GPU | Fit for Igneum | +|---|---|---|---|---|---| +| Nova, SuperNova, HyperNova, ProtoStar, Mova; Sonobe as the library | one step's witness plus the running instance: tiny by construction (eprint 2021/370) | an IVC proof of O(F) group elements, compressed by a SNARK: Sonobe's decider is Groth16 over BN254 with KZG, about 11.9 M constraints for a 500 k-constraint step (sonobe.pse.dev, decider page); MicroNova about 2.2 M gas (eprint 2024/2099) | curve cycles (Pasta, BN254 and Grumpkin) | partial: sppark MSM on Pasta, a GPL-3 `cuda-nova` for BN254; Sonobe lists GPU as a plan | **no**: a RISC-V step over a 256-bit curve cycle costs two MSMs per step, the opposite of the hash-based consumer-card design decision (design 5.6), and the verifier changes to pairings | +| Nexus zkVM 1 and 2 | the prover ran "on as little as 1 GB of RAM" (whitepaper, approximate) | a curve SNARK | curve cycle | none | **abandoned by its own author**: Nexus 3.0 (25 June 2025) moved to a Circle STARK, "proofs are smaller, faster to generate" (StarkWare blog) | +| LatticeFold, LatticeFold+, Neo, SuperNeo; Nightstream as the zkVM | one step plus an accumulator, lattice commitments over 64-bit fields | a Spartan-class decider | Goldilocks named; BabyBear and KoalaBear not | none; Nethermind's LatticeFold is a "proof-of-concept prototype" whose benches take 48 h; Nightstream is "research software, not production-ready" with its RV32IM prototype removed | **not before 2027 at the earliest**; the first candidate that folds small-field STARK steps | +| Arc, WARP (hash-based accumulation of Reed-Solomon proximity claims) | small: Merkle openings per step (eprint 2024/1731, 2025/753) | a FRI-style accumulator check | any STARK field | none; no public implementation found | the right primitive on paper for folding RISC-V STARK shards with a hash-based verifier; nothing to adopt | +| Mangrove, Nebula | 390 MB peak at 2^24 gates (Mangrove, eprint 2024/416); pay-per-use steps (Nebula) | curve SNARK | curve cycle | none | research | + +What folding would mean for a shard: the shard prover would hold one transaction's step at a time and the memory +floor would vanish; the price is a curve-based decider at the end of every shard (seconds on a CPU, a different +verifier in the node, pairings on the light-client path), and no production code over our field. Today's small-field +zkVMs get their bounded memory from segmenting and recursion (2.4), not from folding. Folding is a watch item, not a +route. + +### 2.4 Continuations and segment proving at small sizes + +| Prover | The segment knob | What a 2^18 or 2^19 segment costs | Can our shard be cut that way inside the guest? | +|---|---|---|---| +| RISC Zero | `segment_limit_po2`, runtime, 13 to 24 (`env.rs:181-186`); the recursion lifts every segment at 2^18 rows and joins them in a tree | po2 19 is the documented fit for an 8 GB card and po2 20 for a 16 GB card (Boundless); the scheduler's measured tokens put po2 18 at about a third of po2 21 and a lift or join at an eighth (`factory.rs`); the 4.7 M-cycle shard at po2 19 is 9 segments, 9 lifts and 8 joins | yes, with no guest change: the zkVM cuts at the limit on its own; the shard statement is unchanged and one succinct receipt comes out | +| SP1 | `HEIGHT_THRESHOLD` (honoured) and `ELEMENT_THRESHOLD` (overwritten by the server, section 1.2); `SHARD_SIZE` up to 2^24 cycles | the live buffers shrink (22.9 GB against 28.3 GB on the prototype shard at `HEIGHT_THRESHOLD 2^20`, bench-log) and the fixed 13.9 GB does not; the compose tree folds 4 proofs at a time | yes, the same way; but the floor is the server's, so the cut buys nothing until the server is re-sized (route A) | +| Our own planner | `S_p` in pgas, a consensus parameter changed by the fee-switch pattern (`docs/plans/fee-switch-devnet.md`); the cut is at transaction boundaries (`core/src/plan.rs`) | halving `S_p` halves the live trace and doubles the shard count; the aggregator verifies one deferred proof per shard (1.66 M cycles for 4 shards, bench-log 4 October) and the chained aggregation is one per block whatever the count (9.7 s on a mining 5090) | yes, already implemented; a transaction above `S_p` stays one shard and the zkVM's own continuations cover it (spec 7.6 item 1) | +| Jolt | none: monolithic; streaming planned | n/a | no | + +### 2.5 Distributed proving across several small cards + +| System | How one execution is split | Per-card memory | Several cards on one host | What it means for four 12 GB cards | +|---|---|---|---|---| +| SP1 cluster (`sp1-cluster`, BSL 1.1) | by core shard: `ProveShard`, `RecursionReduce`, `RecursionDeferred` and `ShrinkWrap` tasks go to GPU workers, `CoreExecute` and the Groth16 or Plonk wrap to CPU workers (`crates/prover-types/src/lib.rs:31-41`); artifacts through Redis and S3 | "only certain GPUs with >= 24GB RAM are supported" (`infra/charts/sp1-cluster/values-example.yaml`); one task holds one whole card | yes: one GPU node process per card (`gpu{0..7}` services in the docker-compose deployment page); the local server itself supports device 0 only (`task.rs:160`, "only device 0 is supported at the moment"), one server per `CUDA_VISIBLE_DEVICES` | the split is by core shard, and the adopted shard is ONE core shard (section 1.3), so there is nothing to split across cards; the floor per card is unchanged. The cluster is a throughput tool, and its code is BSL | +| RISC Zero Bento (Boundless) | by segment onto Redis; `gpu_prove_agent` spawns one prove agent per card with `CUDA_VISIBLE_DEVICES`; the same `SEGMENT_SIZE` for every card, "the lowest common denominator"; joins form a tree (docs.boundless.network, performance-optimization and bento pages; `compose.yml:62-113`) | by `SEGMENT_SIZE` (2.1): 8 GB po2 19, 16 GB po2 20 | yes, documented: one 16 GB card 264 kHz, two 431 kHz (sub-linear, "bound by bus bandwidth, memory") | **the one documented configuration**: four 12 GB cards at po2 19 or 20 take segments off one queue and the joins fold them; the per-card floor is the segment, and the cost of small segments is the lift and join count (9 lifts and 8 joins for the adopted shard at po2 19) | +| RISC Zero `RISC0_PROVER=actor` | one process, several cards, a token budget per card from measured memory per po2 (`r0vm/src/actors/factory.rs`) | per po2 | yes, experimental since 3.0.1 | the same model without Bento's services | +| Pico Prism 2.0 | a global task queue across two machines, 16 x 5090, 100 Gbps between them (Brevis blog, May 2026) | not published | yes | no figure | +| OpenVM | metered execution on the CPU, segments to GPUs, an aggregation tree, "clusters with hundreds of GPUs" (docs, distributed-proving page) | 24 GB | yes | no 12 GB path | +| ZisK | coordinator and stateless workers; "splits the trace into pieces, proves each in parallel on separate machines, and aggregates"; the first worker aggregates a binary tree (docs, distributed execution page) | not documented | yes, `--gpu` per worker | no figure | +| Ceno | shards round-robin by `shard_id % device_count`; a shard never split across cards; one CUDA context per device (PR #1403) | not stated | yes | the same model | +| Column-split of one trace across cards (FRIttata eprint 2025/1285, HyperFond 2025/1349, deVirgo arXiv 2210.00264, Pianist 2023/1271, Cirrus 2024/1873, SumFold 2025/1653) | the sumcheck or FRI itself is distributed, each worker holding a slice of the columns or rows and exchanging small messages | a slice | research code or CPU clusters only | nothing shipped; the one route that would let four 12 GB cards hold what one 32 GB card holds for a SINGLE core shard, and nobody has it in a zkVM | + +The reading. Every shipping system splits by rows (segments, shards, chunks), proves each on one card, and folds +with recursion. So "four 12 GB cards do what one 32 GB card does" is true for throughput (four shards in flight, or +four segments of one shard, then a join tree) and false for a single unit that exceeds one card: that unit must be cut +smaller, by the zkVM's segment knob (RISC Zero) or by our planner (`S_p`). For Igneum the units are already small and +independent (a shard, assigned by sortition), so the rig's natural mode is one prover process per card, each taking +its own shard. The distributed route therefore costs nothing in protocol and lands as an app change (section 3, route C). + +### 2.6 Proof systems that run on AMD or Apple + +| Target | What exists | Status | Source | +|---|---|---|---| +| Apple, RISC Zero Metal | the full STARK prover (rv32im, keccak, recursion) on Metal, automatic on Apple silicon; the Groth16 wrap x86 only | shipped, maintained (PR #3761's June 2026 matrix lists "metal (Mac M-series): build, run"); the only speed published is a 2023 M2 datasheet (14 to 93 kHz, approximate); nothing for M3, M4 or M5 | `risc0/sys/kernels/zkp/metal/`, dev.risczero.com local-proving page | +| Apple, ICICLE Metal (Ingonyama) | MSM, NTT, sumcheck on Metal since v3.6.0 (Mar 2025); "missing API implementations for Poseidon and Poseidon2 hashes, Merkle tree" at that release; v4.0.0 of 11 Jul 2025 is the latest | a library, closed-source backends under a free research licence (dev.ingonyama.com, install_gpu_backend page); no STARK prover built on it for Metal | ICICLE releases, the Metal blog | +| Apple, Jolt Metal | PR #1938 merged 30 Sep 2026 (the runtime and field kernels); PR #1733, the prover itself, a draft: M5 Max 2^25 cycles in 19.8 s, 3.2x over its CPU | the fastest Apple number anyone has published, in a draft | github.com/a16z/jolt pulls 1733 and 1938 | +| Apple, Stwo | CPU SIMD with NEON; ICICLE-Stwo promises Metal | CPU path shipped; no RISC-V guest of its own | stwo README, Ingonyama blog | +| Apple, Miden | `miden-gpu` on Metal | Cairo-class VM, not RISC-V | hackmd (bobbinth) | +| AMD, sppark | "A limited support for AMD's RDNA and CDNA GPUs" (README); SP1's tree carries no HIP build | a library | github.com/supranational/sppark | +| AMD, SP1 PR #2668 | an external port to RDNA3 and RDNA4 with "a caching memory allocator to work around hipMallocAsync leak bug" | **closed unmerged 20 Mar 2026** | github.com/succinctlabs/sp1/pull/2668 | +| AMD, OpenVM stark-backend HIP fork | `cuda2hip.hpp` so the same `.cu` builds under nvcc and hipcc, native `mont32_t.hip`, tested on gfx1100, targets MI300X and 7900 XTX | merged 15 Sep 2026 in a fork (Okm165/stark-backend PR #2), not upstream | the PR | +| AMD, Goldilocks NTT and STARK on ROCm | 19.19 ms NTT at 2^27 on an RX 7900 XTX; a Goldilocks STARK backend on HIP | research posts | ethresear.ch, qingming-g64-ntt and stark-g64 | +| Vulkan and WebGPU | ICICLE's Vulkan build (Jan 2025) with no installable backend; zkSecurity's WebGPU Stwo (5x on constraint evaluation, 2x end to end, no 64-bit integers in WGSL); ZPrize WebGPU MSM | prototypes; nothing proves a RISC-V shard | the pages named | + +Said plainly, as `docs/analysis/amd-proving.md` said it: on 5 October 2026 no zkVM proves on an AMD GPU, and the only +Apple prover that ships is RISC Zero's. The AMD work that exists is two ports of CUDA STARK kernels through a HIP shim, +one closed, one in a fork; both are days of agent work to revive against a given tree, and PC 1's RX 9070 XT (gfx1201) +is the card to measure on. + +## 3. For each route: the change to our guest, the aggregator and the node's verifier; the cost; the risk; 12 GB under 60 s + +What the node verifies today: SP1 compressed proofs through `igneum-prove-host --mode verify` and `verify-segment` +(`vendor/igneum-node-pv1/igneum/exec/src/proving.rs:41-46, 878-926`), the pinned ids read at start and named in the +native statement (`program_ids`, `IGNEUM_PROOF_PROGRAM_IDS`), the record bound in a BLS-signed `ProofRecord` (version +1) or `SegmentRecord` (version 2) with the proof's SHA-256 (spec 7.7 item 1, 7.8 item 3). A different proof system +means a new `ProofSystem` version (design 5.6), a new pinned id, a second verifier command, and the record's version +field telling the node which. The swap procedure of design 5.6 (test vectors, 90% signalling, a 3-month overlap with +both verifiers, a wrap of the last old proof) is the path for any of the rows below that change the family. + +The 60-s test. The litepaper's minute, the launch target of 20 to 60 s behind the tip, and the mine-and-prove +measurement that a shared card proves 3 to 4x slower (bench-log, `chain-pc2-pv1c`). No 12 GB card has run any +prover in this repository; the 12 GB times below are approximate, scaled from the 5090 by memory bandwidth (an RTX +3060 at 360 GB/s and an RTX 4070 at 504 GB/s against the 5090's 1,792 GB/s, NVIDIA's published figures, approximate), +which is the term a STARK prover is bound by. They are the numbers the first 3060-class run replaces. + +| Route | Guest | Aggregator | Node verifier and record | Cost (agent time) | Risk | Reaches 12 GB with a real shard under 60 s? | +|---|---|---|---|---|---|---| +| **A. Re-size SP1's GPU server** (the prover-floor agent's patch, running tonight): remove the 20 GB panic (`builder.rs:37`), size `max_trace_size` to the shard (honour `ELEMENT_THRESHOLD`, or set the core allocation from the shard's measured cells), one core worker and a buffer of 1, a release threshold so the pool returns memory between stages, `drop_ldes` on; build with `CUDA_ARCHS` for Ampere, Ada and Blackwell | none: the same ELF, the same pinned id (the verifying key hashes the program and its preprocessed tables, not the server's buffer sizes; `HEIGHT_THRESHOLD` only shortens tables below the verifier's 2^22 maximum) | none: the compressed proof format and the aggregator guest are unchanged | none: the same `--mode verify`; the record format unchanged | hours to one day: a fork of `sp1-gpu/crates/prover_components` and `jagged_tracegen` (Apache or MIT), the 11-min cross-build, a per-card profile in `provedefault.rs`, a CI check that the fork's constants match the pinned verifier's | low on the protocol, medium on the build: the fixed recursion stage may hold the floor near 8 to 9 GB (section 1.3, approximate) and the first measurement says whether 11 GB is reached; a fork of `sp1-gpu` to carry forward on every SP1 release; the server rejects nothing it cannot hold, so an out-of-memory shard must fail cleanly and be left (the pool's rule today) | **memory: likely for the adopted shard** (10 to 11 GB on paper, section 1.4), **not** for the prototype shard (28 GB of live trace). **Time: prove-only yes** (4.3 s on the 5090 scales to about 15 to 22 s on a 3060 and 10 to 15 s on a 4070, approximate); **mine-and-prove on a 12 GB card: no at `S_p`** (3 to 4x on a shared card puts a 3060 at 45 to 90 s, approximate, and the miner's 1.7 GB on top of 11 GB does not fit), yes at `S_p/2` on a 4070 if the floor lands under 9 GB (approximate). The measurement decides; this is the route the gate waits on | +| **B. Halve `S_p`** (30,000 to 15,000 pgas, the fee-switch pattern): more and smaller shards | none | none: one deferred proof per shard, so 2x the shards per block; the chained aggregation stays one per block (9.7 s mining, 2.5 s alone) | none | hours: a fee-table change and a rollout plan like `fee-switch-devnet.md` | low: more records per block (the coinbase carries at most 8 shard records, spec 7.7 item 2, so `B_p / S_p` must stay at 8 or under); the assignment window and sortition unchanged | **alone, no**: the floor is the server's (13.9 GB at 0 cycles). **With A, it is the dial** that moves a 12 GB card from prove-only to mine-and-prove, and a 16 GB card to a comfortable fit | +| **C. One prover process per card on a rig** (the distributed route): the app runs one `sp1-gpu-server` per NVIDIA card (`CUDA_VISIBLE_DEVICES`, the per-device socket of `sp1-cuda/src/client.rs:211`, `.cuda().with_device_id(n)`), one host process per card, each taking its own assigned shard; the rig installer already picks cards (`igneum-rig-lib.sh`, `prover_decision`) | none | none: shards are independent units by design (spec 7.2); the aggregator runs on the biggest card | none | one day: the app's prover loop per card (`prover.rs` runs one loop today), the Settings and tile per card, the rig installer's prover unit per card, the socket cleanup per device (the root-socket rule of 5 October) | low; the throughput is per card, the host RAM 6 GB of pinned buffers per server (section 1.2), so a 4-card rig needs 32 GB of RAM or route A's smaller buffers | **it does not move the floor**: each card still needs A. It is the route that makes four 12 GB cards worth four shards a cycle, and it ships with A, not instead of it. Splitting ONE shard across cards is not a route: the adopted shard is one core shard (2.5), and column-split provers are research | +| **D. RISC Zero as proof system version 2** (CUDA and Metal; segments at po2 19 or 20) | a second guest: `core/` is plain Rust and ports as is; the precompile patches differ (SP1's `sha3` and `k256` patches against RISC Zero's `sha2`, `k256` and keccak circuit); the shard statement bytes unchanged; a second pinned ELF and image id in `elf/manifest.json` | a RISC Zero aggregator guest using composition (`env::verify` of the shard receipts, dev.risczero.com composition page); the chain rule (N verifies N-1) inside the family; **a block's shards must be one family**, and a chain cannot cross families inside the proof: a family switch lands at a segment boundary as a fresh chain (spec 7.8 item 6 already allows one after an unproven segment; the rule gains "or at a proof-system version change") | a second verifier mode (`--mode verify-r0`, the `risc0-zkvm` verifier, pure Rust, about 100 ms, 222 KB receipts); the record's `version` selects the family; the native statement names the family's pinned id; both verifiers in the node through the overlap of design 5.6 | 3 to 4 days: guest port and pinning 1, aggregator and chain rule 1, node verifier and record version 1, app profile and host modes 0.5, test vectors and the fast-time harness 0.5; plus the measurement day on PC 2 | medium: two proof systems in consensus for the overlap; RISC Zero's Groth16 wrap is x86 only (the light-client path of ledger P3 stays on SP1 or waits); a 222 KB receipt per shard against 1.27 MB today is a gain; the recursion tree per shard (9 lifts and 8 joins at po2 19) is extra time on small cards; `main` is at 5.0.0 with no release body, so the pin is 3.0.6 | **memory: yes by documentation** (po2 19 for an 8 GB card, po2 20 for 16 GB; 9 to 10 GB per 1 M cycles), the first documented sub-12 GB prover. **Time: approximate**: a 4090 does 808 kHz at po2 21, so the adopted shard is about 6 s on a 4090-class card and about 20 to 30 s on a 3060-class one at po2 19, prove-only; beside the miner over 60 s on a 3060, near it on a 4070. The prover-floor agent's PC 2 run is the first real number | +| **E. Airbender, OpenVM, ZisK, Pico, Ziren** as version 2 | a new guest each (RISC-V, except Ziren's MIPS); OpenVM's and ZisK's toolchains are the most complete | each has its own recursion; OpenVM's aggregation and Halo2 wrap are the most documented | a new verifier each (STARK under 300 KB for OpenVM; PLONK or FFLONK for ZisK and Airbender) | 4 to 6 days each | the same two-family cost as D with no memory gain: 21 GiB (Airbender), 24 GB (OpenVM, Ziren), undocumented (ZisK, Pico); Pico's and Ziren's GPU code is BUSL or closed | **no**: none documents a floor under 21 GiB; the race is tuned for 5090 clusters | +| **F. Jolt (Lattice Jolt) as version 2**: a sumcheck prover with no codeword; CPU and Metal | a new guest (RV64IMAC, Jolt's toolchain; no keccak precompile today, approximate, so the trie hashing costs more cycles than in SP1) | **none exists**: no recursion or continuation shipped, so the aggregator would verify N shard proofs natively and the chain rule would live in the native statement until Jolt's recursion lands | a Dory verifier (BN254 pairings, about 50 KB, sub-second, approximate) or an Akita verifier (lattice, 65 to 80 KB); no on-chain verifier shipped | 5 to 8 days for the guest, the verifier and the record; the aggregator question has no answer in the code | high: alpha software, no audit, no production user, no recursion; the proof system of the miner's CPU, not of its card | **memory: yes by a wide margin** (about 0.9 GB for the adopted shard at 200 bytes a cycle, approximate). **Time on a CPU: about 2 to 3 s** for 4.7 M cycles at over 2 M cycles a second (a16z, Sep 2026, laptop CPU; approximate for our guest), **on Metal under 1 s** (PR #1733's 2^25 in 19.8 s on an M5 Max, approximate). The numbers are the best in this document and the software is not shippable | +| **G. Folding** (Nova family, lattice folding) | a step circuit per transaction or per opcode group | a decider per shard | pairing or lattice verifier | weeks of research, no code over our field | the family that Nexus left | **no** today; the watch item for 2027 | +| **H. AMD through a HIP port of SP1's kernels** (PR #2668 revived against 6.8.1, or the `cuda2hip` shim of the OpenVM fork) | none | none | none: the same SP1 proofs | 3 to 5 days plus PC 1's RX 9070 XT to measure; the `hipMallocAsync` leak needs the caching allocator the PR carried | medium: a kernel port with no upstream; the sppark NTT has a limited HIP path and cuPQC none | memory as route A (the same buffers); **time unmeasured on any AMD card**; the one route that gives AMD miners the 20% pool share | +| **I. Apple through RISC Zero Metal** (route D's Metal half) | as D | as D | as D | inside D's 3 to 4 days | the 2023 M2 figure (14 kHz, approximate) says 5 minutes for the adopted shard; an M5 Max is not measured by anyone | **memory: yes** (unified memory, 64 GB on the M5 Max). **Time: unknown**; the Mac measure lock run is the number | + +## 4. The ranked recommendation + +| Rank | Route | Why | Gate | +|---|---|---|---| +| **1. Soonest to 12 GB with the least change: A, with B as the dial and C for rigs** | re-size SP1's GPU server; keep the guest, the aggregator, the verifier and the pinned ids exactly as they are; set `S_p` from the first 12 GB measurement; one prover per card on rigs | nothing in consensus moves; the work is a fork of two Apache crates and an app profile; it is already running tonight; every other route costs days and adds a second verifier | the prover-floor agent's rows: the adopted shard under 11 GB alone and the time on the first 3060-class or 4070-class card, prove-only and beside the miner. If under 11 GB and under 60 s prove-only: ship 0.3.12 with the 12 GB tier as prove-only and `S_p/2` measured for mine-and-prove. If not under 11 GB: route D | +| **2. The fallback if A misses 11 GB, and the Apple route either way: D, RISC Zero as version 2** | the only shipped prover with a documented sub-12 GB configuration and a shipped Metal path; Apache or MIT including the kernels; 222 KB receipts | the swappable interface was built for this (design 5.6) and the node already names the pinned id in the statement, so a second family is a version, not a redesign; the cost is 3 to 4 days plus the overlap | PC 2's po2 19 and 20 rows (memory, time per segment, lift and join) tonight; the Mac's Metal row | +| **3. Best in five years: the sumcheck family without a codeword (Jolt-class), or the sumcheck-plus-WHIR family SP1 and OpenVM already converge on** | Jolt proves the adopted shard in seconds on a laptop CPU at under 1 GB of memory, which is the only route that gives AMD-only, Apple and 8 GB machines the proving share with their existing hardware; its verifier is small (50 to 80 KB); its licence is MIT or Apache. It is alpha with no recursion, so not before it ships a stable release with continuations and an audit. SP1 Hypercube and OpenVM SWIRL are the same mathematics with a hash-based PCS and a GPU today, which is why staying on SP1 now loses nothing in that direction | do not adopt now; re-read Jolt and the Arc or WARP accumulation line at every 6-month era draw (design 5.6's swap procedure needs 90% signalling and a 3-month overlap, so the lead time is the schedule) | a stable Jolt tag with recursion, an audit, and a CUDA or merged Metal prover | +| **The interface question** | yes: `ProofSystem` is versioned (`VERSION`, `program_id`, `verify_segment`), the record carries `version`, the node reads pinned ids at start and names them in the native statement, and the overlap procedure keeps both verifiers in the node for 3 months with `B_p` from the stricter table. What is missing for two families at once is small and named: the record version selecting the verifier command, the fresh-chain rule at a version change, and the shard plan carrying the family per block so a block's shards are homogeneous (the aggregator folds one family). Those three items are in route D's day of node work | so the answer to "ship one now and move to the other later" is yes, and route A ships nothing that has to be undone | | + +The honest statement of what this ranking does not know: no 12 GB card has run any prover here. Route A's time +figures are bandwidth scaling, labelled approximate; route D's are a 4090 figure scaled the same way. The first 3060 +or 4070 in this repository replaces both columns, and the plan is to borrow or buy one this week (a 4070 is the +common 12 GB card of 2026; a 3060 the common older one; both are the gate's named class, design R2). + +## 5. The tier consequences, and the public line while the change is made + +Every number carries its consequences (CLAUDE.md, 5 October 2026). The table says what each tier has today on SP1 +6.8.1, what route 1 (A plus B plus C) gives it if the gate is met, what route 2 (D) adds, and what only route 3 would +give. "Today" is measured; the rest is the routes' expected outcome, labelled, until the measurement. + +| Tier | Today (measured, bench-log 5 October) | Route 1: re-sized SP1 server, `S_p` as the dial, one server per card | Route 2: RISC Zero version 2 | Only route 3 (sumcheck without a codeword) | +|---|---|---|---|---| +| Home miner, one 8 GB NVIDIA card | mines; proves nothing (the server panics under 20 GB) | proves nothing at `S_p` (the floor's fixed terms, 8 to 9 GB approximate, leave no room); perhaps empty shards | prove-only at po2 19 (Boundless' 8 GB tier), the miner paused per shard; time approximate 30 to 60 s | mines and proves on its CPU | +| Home miner, one 12 GB card (3060, 4070) | mines; proves nothing; the litepaper's gate card | **prove-only at `S_p`** if the floor lands under 11 GB (expected, section 1.4): about 15 to 22 s a shard, approximate; **mine-and-prove at `S_p/2`** on a 4070 if the floor is under 9 GB, approximate; on a 3060 the shared card misses 60 s, approximate, so its default is prove-only with the miner paused per shard (the 16 GB rule of `provedefault.rs` today, moved down a tier) | prove-only at po2 20 (16 GB tier) or po2 19; mine-and-prove not inside 60 s on a 3060, approximate | mines and proves, CPU | +| Home miner, one 16 GB card (5080, 4080, 4060 Ti 16 GB) | an empty shard alone (13.9 GB); nothing beside the miner | **mine-and-prove at `S_p`** (11 GB plus the miner's 1.7 GB), about 7 to 12 s a shard alone and 20 to 40 s beside the miner, approximate | mine-and-prove at po2 20 | the same | +| Home miner, one 24 GB card (4090, 3090) | the adopted shard alone (20.4 GB) and beside the miner (22.2 GB, approximate for the card); the prototype shard never | mine-and-prove at `S_p` with 10 GB to spare; the prototype shard (28 GB live) only if the devnet's fee switch has passed, which it has from DAA 210,000 | the same with Metal irrelevant | the same | +| Home miner, one 32 GB card (5090) | everything, measured | everything, with more shards in flight if the pool releases between stages | the same | the same | +| Rig, several NVIDIA cards | one prover on the biggest card (`prover_decision`) | **one server per card**, each its own shard; the aggregator on the biggest card; host RAM 6 GB pinned per server today, under 2 GB with route A's buffers | the same model (Bento's) | the same | +| Pool user | through the pool; who proves is open (spec 09) | unchanged | unchanged | unchanged | +| AMD-only (RX 9070 XT, 7900 XTX) | mines; proves nothing on the card; the CPU path 282 s a shard at 30 GB | unchanged until route H (a HIP port, 3 to 5 days, measured on PC 1's 9070 XT) | unchanged: RISC Zero is CUDA and Metal only | mines and proves on its CPU | +| Apple silicon (M-series) | mines (26.7 MH/s on the M5 Max); the SP1 CPU prover 41 to 55 s for an empty shard, 272 s for a small one | unchanged | **proves on the GPU through Metal** (64 GB unified memory on an M5 Max holds any segment); the time is the measurement | proves in seconds on Metal (Jolt's draft PR figure, approximate) | +| Windows under 32 GB of RAM | off (the WSL2 prover held 7.9 GB) | the pinned buffers fall with `max_trace_size`, so a 16 GB PC likely qualifies, approximate; measure | RISC Zero's CUDA path also runs in WSL2 | | + +The deadlines these fit (spec 7.2 item 3, the litepaper, `proving-v1.md`): the 10-s exclusive window is the 5090's +alone; a 12 GB card at 15 to 22 s proves its assigned shards in the open phase and is paid when no faster card took +them, which on a chain with few 5090s is most of the time; the minute of the litepaper holds for prove-only 12 GB +cards and for mine-and-prove 16 GB cards; the 600-s unproven deadline holds for every tier above the CPU path. + +### The public line while the change is made + +The litepaper's sentence today ("Target: shard size will be set so a 12 GB card proves one shard in about 20 +seconds") is a target and says so (fud-ledger P1, overclaim 27). What this document adds, for `site/litepaper.html`, +`site/miner.html` and the app's Proving tile, in the copy law: + +> Proving runs on NVIDIA cards with 24 GB or more today. A build for 12 GB and 16 GB cards is being measured: the +> memory is the prover's buffers, not the shard, and the fix is a smaller build of the same prover. AMD and Apple +> cards mine. A second prover with an Apple path exists and is the fallback. + +And the rule for the next status line, whichever way the measurement goes: the number, the card it was taken on, and +the tier it moves, in one sentence, the day it is taken. + +### What this document does about it + +| Consequence | Action | Owner | +|---|---|---| +| The gate card has never run a prover here | get a 4070 or 3060 into the measurement loop this week; until then every 12 GB figure stays approximate | coordinator; the project lead for the card | +| Route A's gate | the prover-floor agent's rows (asked for by message tonight); if under 11 GB, `provedefault.rs` gains the 12 GB prove-only and 16 GB mine-and-prove tiers and the rig installer one server per card | prover-floor agent, then the proving engineer | +| Route D's measurement | RISC Zero 3.0.6 at po2 19 and 20 on PC 2 (CUDA) and on this Mac (Metal), the same shard statement run natively: memory, time per segment, lift and join, receipt size | prover-floor agent (PC 2); a Mac measure job for Metal | +| The two-family node items (record version selects the verifier, fresh chain at a version change, one family per block) | spec 7.8 gains the three rules when route D starts; nothing changes before | execution engineer | +| AMD | route H is a 3-to-5-day job with a measurement on PC 1's 9070 XT; opened as a plan when route A's result is in | execution engineer | +| The public line | the paragraph above to the site and the tile with the next site pass | site-pages owner | + +## Sources + +Our own: `docs/bench-log.md` entries "proving v1: segment records, the chain rule, the unproven rule" (5 October 2026), +"the SP1 CPU prover on PC 1" (5 October), "shard proving on the RTX 5090" (4 October); `docs/plans/proving-v0.md`, +`proving-v1.md`; `docs/analysis/amd-proving.md`; `docs/spec/07-execution.md` 7.2, 7.6, 7.7, 7.8; `docs/design/execution-layer.md` +5.1 to 5.7; `proving/igneum-prove` (`host/src/proof_system.rs`, `program/src/main.rs`, `aggregator/src/main.rs`, +`elf/manifest.json`); `vendor/igneum-node-pv1/igneum/exec/src/proving.rs`; `app/igneum-app/src/provedefault.rs`, `prover.rs`. + +SP1 6.8.1, read from `~/.cargo/registry/src/index.crates.io-*/` and the vendored tree `vendor/sp1-6.8.1` (commit +c84ada1e, 24 Sep 2026) on the `prover-floor` worktree: `sp1-core-executor-6.8.1/src/opts.rs`, `src/utils.rs`, +`src/artifacts/rv64im_costs.json`; `sp1-prover-6.8.1/src/components.rs`, `src/worker/config.rs`, `src/shapes.rs`; +`sp1-primitives-6.8.1/src/fri_params.rs`; `sp1-verifier-6.8.1/src/compressed/config.rs`; `sp1-hypercube-6.8.1/src/verifier/config.rs`; +`sp1-cuda-6.8.1/src/server.rs`, `src/client.rs`; `sp1-gpu/README.md`, `sp1-gpu/crates/prover_components/src/builder.rs`, +`src/components.rs`, `sp1-gpu/crates/jagged_tracegen/src/lib.rs`, `sp1-gpu/crates/shard_prover/src/prover.rs`, +`sp1-gpu/crates/cuda/src/task.rs`, `src/device.rs`, `sp1-gpu/crates/sys/lib/runtime/mem_pool.cu`, `sp1-gpu/crates/zerocheck/src/primitives.rs`. +Web: docs.succinct.xyz (hardware-acceleration, hardware-requirements, proof-types, security-model, provers introduction, +cluster architecture, docker-compose deployment); blog.succinct.xyz (sp1-hypercube, real-time-proving-16-gpus, +sp1-hypercube-is-now-live-on-mainnet); github.com/succinctlabs/sp1 releases v6.0.0 to v6.8.1, issues #2674, #2930, +#2950, #2969, pulls #2631, #2668, #2723, #2917, #2974; github.com/succinctlabs/sp1-cluster (README, LICENSE, +`infra/charts/sp1-cluster/values-example.yaml`, `crates/worker/src/config.rs`); eprint 2025/917 (jagged polynomial commitments). + +RISC Zero: `~/.cargo/registry` crates `risc0-zkp-3.0.4/src/lib.rs`, `risc0-zkvm-3.0.4/src/receipt.rs`, `src/host/recursion/prove/mod.rs`, +`risc0-circuit-rv32im-4.0.4/src/execute/mod.rs`, `src/zirgen/defs.rs.inc`, `src/prove/hal/cuda.rs`; github.com/risc0/risc0 +`risc0/zkvm/src/host/client/env.rs`, `risc0/zkvm/Cargo.toml`, `risc0/zkvm/build.rs`, `risc0/sys/kernels/zkp/{cuda,metal}/`, +`risc0/r0vm/src/actors/factory.rs`, `risc0/circuit/recursion/src/lib.rs`, releases v2.0.0, v3.0.1, v3.0.6, pull #3761; +dev.risczero.com (local-proving, composition); docs.boundless.network (bento, performance-optimization, quick-start); +github.com/boundless-xyz/boundless (`compose.yml`, `bento/README.md`, `bento/LICENSE-BSL`); github.com/ekrembal/gsr-stark-verifier pull 5; l2beat.com/zk-catalog/risc0. + +Others: zksync.io/airbender, docs.zksync.io airbender and proving pages, github.com/matter-labs/zksync-airbender (README, +`docs/gpu.md`, pull #448), veridise.com (the Airbender audit); 0xpolygonhermez.github.io/zisk (introduction, limits, +distributed execution, installation), github.com/0xPolygonHermez/zisk (README, pull #1238, `zisk-contracts`); +blog.openvm.dev (2.0, 2.0-production, 2.1, openvm-gpu, v1), docs.openvm.dev (security-model, distributed-proving, sdk); +pico-docs.brevis.network, github.com/brevis-network/pico and pico-gpu (README, LICENSE), blog.brevis.network (Prism 1.0, +2.0, 2.1); docs.zkm.io (prover, performance), github.com/ProjectZKM/Ziren, zkm.io (the independent evaluation of v1.1.4), +eprint 2026/2330; github.com/starkware-libs/stwo and stwo-cairo (README), ingonyama.com (ICICLE-Stwo, the Starknet +partnership, ICICLE Metal v3.6), dev.ingonyama.com (install_gpu_backend), blog.zksecurity.xyz/posts/webgpu, starkware.co +(S-two 2.0.0, Nexus on S-two), theblock.co (S-two on Starknet); github.com/a16z/jolt (README, book: intro, dory, akita, +streaming, recursion, blindfold; pulls #1733, #1938; tags), a16zcrypto.substack.com ("How to prove software ran +correctly", Sep 2026), a16zcrypto.com (jolt-6x-speedup, 64-bit-proving-jolt, zkvm-jolt-zero-knowledge, faqs-on-jolts-initial-implementation), +eprint 2025/611; github.com/scroll-tech/ceno (README, Cargo.toml, pull #1403), ceno-gpu-mock, scroll.io (Ceno post), +osec.io ("zkVMs' unfaithful claims"); github.com/nexus-xyz/nexus-zkvm (README, LICENSE), blog.nexus.xyz (roadmap); +lita.gitbook.io (Valida architecture, benchmarks); github.com/powdr-labs/powdr; irreducible.com (announcing-binius64, +reinventing-irreducible, irreducible-shutting-down), github.com/binius-zk/binius64, eprint 2026/1656; eprint 2021/1043, +2022/1010, 2024/1609, 2025/1187, 2024/1586, 2024/185 (linear-code commitments, WHIR, Vortex), github.com/Consensys/linea-monorepo; +PolyhedraZK/Expander and blog.polyhedra.network (returned 530 tonight); eprint 2021/370, 2024/2099, 2024/1220, 2024/416, +2024/1605, 2025/247, 2025/294, 2026/242, 2024/1731, 2025/753, 2026/1371 (folding and accumulation), sonobe.pse.dev, +github.com/privacy-scaling-explorations/sonobe, NethermindEth/latticefold, LFDT-Nightstream/Nightstream; eprint 2023/1271, +2024/1208, 2024/1873, 2025/1349, 2025/1653, 2025/1285, 2018/691, arXiv 2210.00264, 2602.16338 (distributed proving); +github.com/supranational/sppark, github.com/Okm165/stark-backend pull 2, ethresear.ch (qingming G64 NTT and STARK on ROCm); +github.com/cysic-labs/venus, erigon.tech (Zilkworm), github.com/DelphinusLab/prover-node-docker, hackmd.io/@bobbinth +(Miden), ethproofs.org/clusters (5 October 2026). From 104be2dbcf631d2084f2d87faf363072fc68ce5e Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:20:16 +0000 Subject: [PATCH 111/158] counter-asic-2-node.md: gate G4 run 3 PASS on the final class (x8 with the era, d5070e7): 182 / 122 blocks, 0 rejected, 0 forks; suite job 4 green (G6) Co-Authored-By: Claude Fable 5.1 --- .../class-v3-20261005-2215Z-era-mx8.json | 367 ++++++++++++++++++ docs/plans/counter-asic-2-node.md | 21 +- 2 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 docs/plans/counter-asic-2-gate/class-v3-20261005-2215Z-era-mx8.json diff --git a/docs/plans/counter-asic-2-gate/class-v3-20261005-2215Z-era-mx8.json b/docs/plans/counter-asic-2-gate/class-v3-20261005-2215Z-era-mx8.json new file mode 100644 index 00000000..3ce77b68 --- /dev/null +++ b/docs/plans/counter-asic-2-gate/class-v3-20261005-2215Z-era-mx8.json @@ -0,0 +1,367 @@ +{ + "pass": true, + "checks": { + "switch_line_on_every_node": true, + "switch_line_names_the_rounded_epoch": true, + "template_switched_at_the_first_v3_epoch": true, + "blocks_before_the_boundary": true, + "blocks_after_the_boundary": true, + "v2_and_v3_programs_seen": true, + "program_ids_differ_across_the_switch": true, + "miners_agree_on_every_program": true, + "zero_rejected_by_miners": true, + "zero_rejected_by_nodes": true, + "sinks_agree": true, + "block_counts_agree": true + }, + "activation": 150, + "epoch_blocks": 60, + "first_v3_epoch": 3, + "boundary_daa": 180, + "secs": 480, + "threads": 1, + "node": "/Users/joshm/Projects/igneum-wt-ca2-v3/vendor/igneum-node-ca2/target-ca2/release/igneumd", + "miner": "/Users/joshm/Projects/igneum-wt-ca2-v3/vendor/igneum-node-ca2/target-ca2/release/igneum-miner", + "genesis_bits": "0x1f010000", + "template_switch": { + "epoch": 3, + "daa": 181, + "at": 127.9 + }, + "run_ended_at_s": 280.2, + "final_daa": 300, + "blocks": { + "total": 304, + "before_boundary": 182, + "after_boundary": 122, + "chain_before": 180, + "chain_after": 120 + }, + "programs": [ + { + "epoch": 0, + "class": "v2", + "program_id": "8f8806638d59850f", + "seed": "234e082d653dc69d", + "miners": 3, + "disagree": false, + "ready_ms": [ + 179, + 178, + 179 + ] + }, + { + "epoch": 1, + "class": "v2", + "program_id": "e145305446a6b4ce", + "seed": "09952ab515cc5a10", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + }, + { + "epoch": 2, + "class": "v2", + "program_id": "743ad2a3cab0518a", + "seed": "1812a8eabb5a452c", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + }, + { + "epoch": 3, + "class": "v3", + "program_id": "a6523b90cff501e3", + "seed": "6296e3d38df15872", + "miners": 3, + "disagree": false, + "ready_ms": [ + 191, + 191, + 191 + ] + }, + { + "epoch": 4, + "class": "v3", + "program_id": "bc811b3c4b8b1ced", + "seed": "a59152f149a517e0", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + }, + { + "epoch": 5, + "class": "v3", + "program_id": "e784541f19cdebe5", + "seed": "87ec7849fce0019f", + "miners": 3, + "disagree": false, + "ready_ms": [ + 2, + 2, + 2 + ] + } + ], + "accepted_per_miner": [ + 101, + 89, + 113 + ], + "rejected_by_miners": [ + 0, + 0, + 0 + ], + "rejected_by_nodes": [ + 0, + 0, + 0 + ], + "rejected_lines": [], + "sinks": [ + "a9ce45df8beeaf13", + "a9ce45df8beeaf13", + "a9ce45df8beeaf13" + ], + "block_counts": [ + 303, + 303, + 303 + ], + "tips_per_node": [ + 1, + 1, + 1 + ], + "switch_lines": [ + "Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)" + ], + "samples": [ + { + "t": 4.6, + "daa": 0, + "epoch": 0, + "class": 2, + "nodes": [ + "0/234e082d", + "0/234e082d", + "0/234e082d" + ] + }, + { + "t": 19.7, + "daa": 48, + "epoch": 0, + "class": 2, + "nodes": [ + "48/78df0834", + "48/78df0834", + "48/78df0834" + ] + }, + { + "t": 34.7, + "daa": 67, + "epoch": 1, + "class": 2, + "nodes": [ + "67/0b934b49", + "67/0b934b49", + "67/0b934b49" + ] + }, + { + "t": 49.7, + "daa": 84, + "epoch": 1, + "class": 2, + "nodes": [ + "84/2ba5b427", + "84/2ba5b427", + "84/2ba5b427" + ] + }, + { + "t": 64.8, + "daa": 101, + "epoch": 1, + "class": 2, + "nodes": [ + "101/6574b577", + "101/6574b577", + "101/6574b577" + ] + }, + { + "t": 79.8, + "daa": 123, + "epoch": 2, + "class": 2, + "nodes": [ + "123/21e2b6ce", + "123/21e2b6ce", + "123/21e2b6ce" + ] + }, + { + "t": 94.8, + "daa": 134, + "epoch": 2, + "class": 2, + "nodes": [ + "134/74b83d55", + "134/74b83d55", + "134/74b83d55" + ] + }, + { + "t": 109.9, + "daa": 153, + "epoch": 2, + "class": 2, + "nodes": [ + "153/71b251a6", + "153/71b251a6", + "153/71b251a6" + ] + }, + { + "t": 124.9, + "daa": 175, + "epoch": 2, + "class": 2, + "nodes": [ + "175/b1500fb5", + "175/b1500fb5", + "175/b1500fb5" + ] + }, + { + "t": 139.9, + "daa": 185, + "epoch": 3, + "class": 3, + "nodes": [ + "185/dd8ff66c", + "185/dd8ff66c", + "185/dd8ff66c" + ] + }, + { + "t": 155, + "daa": 191, + "epoch": 3, + "class": 3, + "nodes": [ + "191/2ef77979", + "191/2ef77979", + "191/2ef77979" + ] + }, + { + "t": 170, + "daa": 194, + "epoch": 3, + "class": 3, + "nodes": [ + "194/1871cd31", + "194/1871cd31", + "194/1871cd31" + ] + }, + { + "t": 185, + "daa": 206, + "epoch": 3, + "class": 3, + "nodes": [ + "206/808070af", + "206/808070af", + "206/808070af" + ] + }, + { + "t": 200.1, + "daa": 221, + "epoch": 3, + "class": 3, + "nodes": [ + "221/1bccc51c", + "221/1bccc51c", + "221/1bccc51c" + ] + }, + { + "t": 215.1, + "daa": 237, + "epoch": 3, + "class": 3, + "nodes": [ + "237/d0bfaef8", + "237/d0bfaef8", + "237/d0bfaef8" + ] + }, + { + "t": 230.1, + "daa": 253, + "epoch": 4, + "class": 3, + "nodes": [ + "253/a4136a32", + "253/a4136a32", + "253/a4136a32" + ] + }, + { + "t": 245.2, + "daa": 268, + "epoch": 4, + "class": 3, + "nodes": [ + "268/51ef3166", + "268/51ef3166", + "268/51ef3166" + ] + }, + { + "t": 260.2, + "daa": 277, + "epoch": 4, + "class": 3, + "nodes": [ + "277/869d0dd3", + "277/869d0dd3", + "277/869d0dd3" + ] + }, + { + "t": 275.2, + "daa": 292, + "epoch": 4, + "class": 3, + "nodes": [ + "292/fe54acb8", + "292/fe54acb8", + "292/fe54acb8" + ] + } + ] +} \ No newline at end of file diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index 24287a9a..7586f224 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -120,13 +120,30 @@ The regression was `memhard::derive_items` at m = 1 (2.2x); the fix dispatches t Epoch 0's v2 id is the same in both runs (`8f8806638d59850f`: a v2 program is untouched by the era code, on the chain as in the packs); the v3 ids differ from run 1 because the era draw is now inside the class. +### Result, run 3 (5 October 2026, 22:15:04Z to 22:19:44Z): the final class, x8 with the era + +Binaries rebuilt on ca2-v3 d233fa1 (mixer x8, the era drawn inside the class, the verifier fix), fork 89dfcb95. Summary: `docs/plans/counter-asic-2-gate/class-v3-20261005-2215Z-era-mx8.json`. PASS: every check true. + +| Check | Measured | +|---|---| +| Switch lines, digest | 3 of 3, the same line; digest `0186df7d0834d054...` | +| Template class per epoch | epochs 0 to 2 class 2, 3 to 5 class 3; the switch seen at DAA 181, 127.9 s wall | +| Blocks before / after the boundary | 182 / 122 (selected chain 180 / 120), 304 in all; 101, 89, 113 accepted per miner | +| Program id per epoch, all three miners agreeing | e0 v2 `8f8806638d59850f`, e1 v2 `e145305446a6b4ce`, e2 v2 `743ad2a3cab0518a`, e3 v3 `a6523b90cff501e3`, e4 v3 `bc811b3c4b8b1ced`, e5 v3 `e784541f19cdebe5` | +| Rejected blocks | miners 0 / 0 / 0, nodes 0 / 0 / 0 | +| Forks | sinks `a9ce45df8beeaf13` on all three nodes, block counts 303 / 303 / 303, one tip each | +| Program and cache ready, one CPU core | v2 epoch 0 179 ms, the first v3 epoch 191 ms (x8 construction: the cache fill is unchanged, the items are derived on demand), an in-day swap 2 ms | + +Epoch 0's v2 id `8f8806638d59850f` is the same in all three runs. Three runs, three v3 classes (mixer x4; x4 with the era; x8 with the era), the same chain behaviour each time: the switch rounds up to epoch 3, no block rejected, one chain. + The PC 2 suite jobs for the same fork (79bd8e10), the G6 evidence (the coordinator's status file carries the SUMMARY lines): | Job | Tree | What | Result | |---|---|---|---| | `build-20261005-215219` | main b105a55 | the Linux node, the six node suites, the app tests | Linux build and app tests passed; `kaspa-consensus` failed on the known flake (`ban_is_decided_by_the_carrying_block`, `UnexpectedDifficulty` in `mine_on_all`, the 0.3.10 cut's section 11 case) | | `build-20261005-215712` | main b105a55 | `kaspa-consensus` alone | 97 passed, 0 failed, 3 ignored, `ban_is_decided ...` ok, 21:59:45Z | -| `build-20261005-220351` | main 8ea6740 | the other five crates (`kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows`) and the app tests | 22:04 to 22:06:55Z: igneum-exec 17, igneum-miner 18, consensus-core and p2p-flows green, the app 78 + 26 + 8; `kaspa-pow` FAILED on `program_class_v3_seeds_hash_their_own_program_over_their_own_cache` (the fork test compared a chain v3 program's class to `V3_CLASS` with `era: None`; on the era-merged crate the class carries the drawn era inside). Fixed on the fork at 89dfcb95 (the class minus its era is `V3_CLASS`, the era is drawn, another era seed keeps the program id and hashes another program over the same day cache). Re-run owed with the mixer fix job | +| `build-20261005-220351` | main 8ea6740 | the other five crates (`kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows`) and the app tests | 22:04 to 22:06:55Z: igneum-exec 17, igneum-miner 18, consensus-core and p2p-flows green, the app 78 + 26 + 8; `kaspa-pow` FAILED on `program_class_v3_seeds_hash_their_own_program_over_their_own_cache` (the fork test compared a chain v3 program's class to `V3_CLASS` with `era: None`; on the era-merged crate the class carries the drawn era inside). Fixed on the fork at 89dfcb95 (the class minus its era is `V3_CLASS`, the era is drawn, another era seed keeps the program id and hashes another program over the same day cache). Re-run: job 4 | +| `build-20261005-221237` | main d233fa1, fork 89dfcb95 | the five crates and the app | 22:12:37 to 22:15:44Z (185 s): every stage ok; kaspa-consensus-core 108 (2 ignored), igneum-exec 17, kaspa-pow 33 + 14 (the v3 engine test, with the igneum-pow feature), igneum-miner 18, kaspa-p2p-flows 7, igneum-app 78 + 26 + 8; 0 failed. With job 2, G6 is green | The PC's test stage builds the fork's test binaries with the plan's feature set, so the v3 engine test DID run on PC 2 (the earlier reading that it was Mac-only is withdrawn): the PC job is the G6 evidence. @@ -137,7 +154,7 @@ The PC's test stage builds the fork's test binaries with the plan's feature set, | igneum-pow `cargo test --release` | 42 unit + 11 pack tests, including `program_classes`, `program_class_and_era_are_checked`, the pinned-pack diffs | pass (Mac, 5 Oct 2026) | | `proto-cuda/nvrtc/emu/packfile-test.sh` | 13 checks: the attempt rule, the generator rule, a v3 pack with its era, the token matcher | pass (Mac) | | `host.c`, `worker.cpp` (emulation), `main.swift` | syntax / compile | pass (Mac) | -| fork `cargo test -p kaspa-pow --features igneum-pow` | 14 engine tests including `program_class_v3_seeds_hash_their_own_program_over_their_own_cache` (rewritten at 89dfcb95 for the era-in-class rule) | pass (Mac, fork 79bd8e10 on the pre-era crate; 89dfcb95 on the era crate: see the final report) | +| fork `cargo test -p kaspa-pow --features igneum-pow` | 14 engine tests including `program_class_v3_seeds_hash_their_own_program_over_their_own_cache` (rewritten at 89dfcb95 for the era-in-class rule) | pass (Mac, 89dfcb95 against d233fa1; PC 2 job 4: 33 + 14) | | fork `cargo test -p kaspa-consensus-core` | 108 + 7: the switch rounding, the era clock, the params, digest and fast-time file tests | pass (Mac, fork 79bd8e10) | | PC 2 `build-job.mjs` suites | `kaspa-consensus-core igneum-exec kaspa-pow kaspa-consensus igneum-miner` + `igneum-app` | the coordinator publishes on its go | From f91dc1368912dfef9513153b4e23fdc5a53e609f Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:20:25 +0000 Subject: [PATCH 112/158] Consequences decisions: D9 a 12 GB card for PC 1 (the coordinator's request, proving-methods.md), checked against the cards evidence.md says are on order Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-decisions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index 17b0e14a..8488b81a 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -12,3 +12,4 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | D6 | C11 | Whether class v3 should favour AMD (fewer, wider loads per hash) at a cost to the 5090's latency-bound share, or accept that AMD cards mine at about a seventh of a 5090 and 4.5x the electricity per hash | Accept it for v3 and say so on the miners page ("NVIDIA first; AMD mines at a lower rate per watt on this class"); open the AMD question as a Counter ASIC 3.0 item with its own measurement | Tonight's rule was the project lead's and the 5090 margin is the anti-chip argument; AMD's position is a public-copy question, not a gate | | D7 | C13 | Same as D3's supply wording | with D3 | | | D8 | C7 | The miner page says "One click: install, start, the card mines and proves" (site/miner.html 7, 13, 21; site/index.html 484). On HiveOS and the rig installer a rig mines only until a Linux prover build is published, and on the app a 12 GB card mines only, a 16 GB card proves with the miner paused, 20 GB and up does both (the sweep of 5 October, before the v1-shard row) | Qualify the sentence on the miner page and the homepage card: "the card mines; 24 GB cards prove too, 32 GB does both at once; rigs mine until the Linux prover ships". The same page's "a visible 1% software fee you can switch off" becomes "solo mining carries a 1% software fee you can switch off; in a pool the pool's own fee is the only one" once pool-v0 ships (the pool agent fixed the rule: no software dev fee in pool mode) | The sentence is the product's first promise and tonight's measurement bounds it by card | +| D9 | C3, C15, C26 | No 12 GB NVIDIA card exists in the fleet, so every 12 GB number tonight is scaled from the 5090 and labelled approximate. The 13.9 GB floor is SP1's GPU server code (`docs/analysis/proving-methods.md`, branch proving-methods e7e0db7: builder.rs refuses cards under 20 GB, the trace is allocated at the maximum shard, the CUDA mempool never releases; the proving plan's read of the same file says 24 GB, 4c82e56: the prover-floor agent's rows settle which). The prover-floor patch and the per-card profiles cannot be measured without the hardware | Buy one 12 GB NVIDIA card this week for PC 1's spare slot (an RTX 3060 12 GB or 4070 12 GB, about £250 to £400, approximate; the coordinator's request). Check first whether the RTX 3060 and the RTX 5060 Ti 16 GB that evidence.md row 16 says are "on order" are real and arriving; if so, no purchase, only the delivery date. No public line says "12 GB proves" before a real 12 GB card runs the rebuilt server on the S_p-curve fixtures and recipe | Money, and the one measurement every 12 GB claim rests on; the prover-floor agent's rows tonight replace the approximate figures when they land | From d3b30f4940b3cfab4cc4dbe7d869dd5791de5946 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:21:27 +0000 Subject: [PATCH 113/158] Consequences: C28 host RAM per server on rigs; D9 wording reconciled; D10 the proving route (A now, RISC Zero as version 2 is the project lead's) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 3 +++ docs/plans/consequences-decisions.md | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 82987e70..9ccfb30d 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -58,3 +58,6 @@ Sweep 7 (21:49 UTC) notes, no new row: the hot table is measured and NOT adopted | C27 | The root-socket fault recurred at 21:25Z from another agent's job (agg-cost-pc2-1) after the class fix and CI check landed; PC 2's prover was dark 37 minutes; a resume at 21:25 answered ok without restarting the miners | bench-log 1d78979; status 21:45, 22:03 | every PC job; the devnet's proving and hash rate tonight | The class check lives in CI, but PC jobs are published from worktrees by `publish-jobs.sh` and never pass through CI before they run, so a job written on a branch without the check runs the old shape. The check must run where the job is published, not only where the repo is tested | `packaging/ota/publish-jobs.sh add` runs `tools/ci/prover-socket-check.sh` and the bash-body check on the script it publishes and refuses on a failure; the sub-agent on the bash-body check wires both; the resume defect is on the next-cut list (stated) | sub-agent bash-body-check (the wiring); coordinator (the rule) | closed on branch bash-body-check 6805125 (publish-jobs.sh add --kind run runs the bash-body check and prover-socket-check.sh before signing, refuses with the output, never skips; test-publish-jobs.sh 32 passed with four new refusals). Merge notes for the integrator: prover-socket-check.sh exists on both proving-v1 c2544be and this branch (add/add, take the superset here); the socket grep flags tools/amd-prove/pc1-cpu-prove.ps1 (CPU-only, -u root, no GPU server) so that job needs the cleanup lines or an allow-list entry before its next publish, told to the coordinator | Sweep 8 (22:09 UTC) notes: mixer x8 DECIDED into v3 on the PC rows (the daily 1 GiB build latency-bound on every card: 5090 23 to 25 ms, 9070 XT 72 to 77 ms at every multiplier; the chip row 0.92x with the 3x factor), so the public claim holds with margin (D5 re-cut); the verifier regression (2.2x) bisected to inlining in the mixer's fetch loop and fixed, so the quiet-core figures of C19 return to about 0.6 / 0.9 / 1.3 ms; G6 job 3 failed on a stale fork test (era inside the class), job 4 on the final tree; the integration merge into master has three known conflicts (bench-log append-only, packfile.h and host.c take the ca2-v3 side). +| C28 | One sp1-gpu-server per card on rigs pins about 6 GB of host RAM per server today (under 2 GB with route A's buffers, approximate) | `docs/analysis/proving-methods.md` section 5, rig row (proving-methods e7e0db7) | rigs with several 24 GB or 32 GB cards on the rig installer | A six-card rig that proves on every card pins about 36 GB of host RAM under the shipped server; the rig installer's preflight says 16 GB to prove (a warning, per card not per rig) and its prover unit runs one server, so the moment it moves to one server per card (the analysis's route C) the RAM check is wrong by the card count | The rig preflight scales its RAM warning by the number of proving cards (6 GB each today, the route A figure when measured) and the README's per-card table gains a host RAM column; the one-server-per-card unit lands only with that check | rig installer a3e7b2b03222f5cff | sent | + +Sweep 8a (22:2x UTC) note: `docs/analysis/proving-methods.md` (branch proving-methods e7e0db7, 411 lines) carries its own per-tier table for today, route A, route D and route 3, and a public paragraph; it is the third draft of the proving public line (with proving-v1 440fd59 and ca2-coord 1c8439f), noted under D2 and D8; the route choice is D10. diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index 8488b81a..415b9184 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -12,4 +12,5 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | D6 | C11 | Whether class v3 should favour AMD (fewer, wider loads per hash) at a cost to the 5090's latency-bound share, or accept that AMD cards mine at about a seventh of a 5090 and 4.5x the electricity per hash | Accept it for v3 and say so on the miners page ("NVIDIA first; AMD mines at a lower rate per watt on this class"); open the AMD question as a Counter ASIC 3.0 item with its own measurement | Tonight's rule was the project lead's and the 5090 margin is the anti-chip argument; AMD's position is a public-copy question, not a gate | | D7 | C13 | Same as D3's supply wording | with D3 | | | D8 | C7 | The miner page says "One click: install, start, the card mines and proves" (site/miner.html 7, 13, 21; site/index.html 484). On HiveOS and the rig installer a rig mines only until a Linux prover build is published, and on the app a 12 GB card mines only, a 16 GB card proves with the miner paused, 20 GB and up does both (the sweep of 5 October, before the v1-shard row) | Qualify the sentence on the miner page and the homepage card: "the card mines; 24 GB cards prove too, 32 GB does both at once; rigs mine until the Linux prover ships". The same page's "a visible 1% software fee you can switch off" becomes "solo mining carries a 1% software fee you can switch off; in a pool the pool's own fee is the only one" once pool-v0 ships (the pool agent fixed the rule: no software dev fee in pool mode) | The sentence is the product's first promise and tonight's measurement bounds it by card | -| D9 | C3, C15, C26 | No 12 GB NVIDIA card exists in the fleet, so every 12 GB number tonight is scaled from the 5090 and labelled approximate. The 13.9 GB floor is SP1's GPU server code (`docs/analysis/proving-methods.md`, branch proving-methods e7e0db7: builder.rs refuses cards under 20 GB, the trace is allocated at the maximum shard, the CUDA mempool never releases; the proving plan's read of the same file says 24 GB, 4c82e56: the prover-floor agent's rows settle which). The prover-floor patch and the per-card profiles cannot be measured without the hardware | Buy one 12 GB NVIDIA card this week for PC 1's spare slot (an RTX 3060 12 GB or 4070 12 GB, about £250 to £400, approximate; the coordinator's request). Check first whether the RTX 3060 and the RTX 5060 Ti 16 GB that evidence.md row 16 says are "on order" are real and arriving; if so, no purchase, only the delivery date. No public line says "12 GB proves" before a real 12 GB card runs the rebuilt server on the S_p-curve fixtures and recipe | Money, and the one measurement every 12 GB claim rests on; the prover-floor agent's rows tonight replace the approximate figures when they land | +| D9 | C3, C15, C26 | No 12 GB NVIDIA card exists in the fleet, so every 12 GB number tonight is scaled from the 5090 and labelled approximate. The 13.9 GB floor is SP1's GPU server code (`docs/analysis/proving-methods.md`, branch proving-methods e7e0db7, section 1.3: builder.rs adds 4 GB to the card's physical memory and panics under 24, so a 16 GB card (16 + 4 = 20) and a 12 GB card never start and 20 GB is the smallest that does; the trace is allocated at the maximum shard; the CUDA mempool never releases; the proving plan's "under 24" (4c82e56) is the same test read before the addition). The prover-floor patch and the per-card profiles cannot be measured without the hardware | Buy one 12 GB NVIDIA card this week for PC 1's spare slot (an RTX 3060 12 GB or 4070 12 GB, about £250 to £400, approximate; the coordinator's request). Check first whether the RTX 3060 and the RTX 5060 Ti 16 GB that evidence.md row 16 says are "on order" are real and arriving; if so, no purchase, only the delivery date. No public line says "12 GB proves" before a real 12 GB card runs the rebuilt server on the S_p-curve fixtures and recipe | Money, and the one measurement every 12 GB claim rests on; the prover-floor agent's rows tonight replace the approximate figures when they land | +| D10 | C3, C15, C26, C28 | The proving route for the 12 GB tier and for Apple: `docs/analysis/proving-methods.md` section 4 ranks (1) route A, a re-sized SP1 GPU server with `S_p` as the dial and one server per card on rigs, nothing in consensus moving; (2) route D, RISC Zero 3.0.x as proof-system version 2 (the only shipped prover with a documented sub-12 GB configuration and a Metal path), the fallback if A misses 11 GB and the Apple route either way, 3 to 4 days plus a 3-month two-verifier overlap and three spec 7.8 rules; (3) a sumcheck family without a codeword (Jolt-class) in years, not now. Route A is already running tonight; route D adds a second proof family to the node, which is a consensus and verifier change outside tonight's delegation | Route A on the prover-floor rows, gated as the document says (the adopted shard under 11 GB alone and under 60 s prove-only on a real 12 GB card, D9); route D's measurement (RISC Zero at po2 19 and 20 on PC 2 and the Mac's Metal row) may run as a measurement, but adopting a second proof family waits for the project lead and for route A's result; the public paragraph of section 5 ("Proving runs on NVIDIA cards with 24 GB or more today. A build for 12 GB and 16 GB cards is being measured ...") is the honest line meanwhile and is the one of the three drafts to prefer, because it names what is being measured instead of a tier | A second verifier in the node is the kind of change the testnet's genesis must carry from day one; measuring it costs nothing, adopting it is the project lead's | From 20a2d198836ab071209c06918a79f0548e8e1ff2 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:21:54 +0000 Subject: [PATCH 114/158] Consequences decisions: the ten in one glance, ordered by when they bite Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-decisions.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index 415b9184..34f92029 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -2,6 +2,20 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number that needs the project lead: money, a public claim, or a consensus parameter outside tonight's delegation. The row number points at `consequences-2026-10-05.md`. Recommendation first, then the options. +## The ten in one glance (what the project lead does, in the order they bite) + +| # | By when | One line | What the project lead does | +|---|---|---|---| +| D1 | 16:00 UTC, 6 October | The fee switch at H = 210,000 (about 19:50 UTC) darkens every 0.3.10 prover; 0.3.11 must be on every prover first, else H moves | Nothing if the morning check passes; the coordinator holds it. Know it exists | +| D9 | this week | No 12 GB card exists here; every 12 GB number is scaled from the 5090 | Confirm whether the 3060 and 5060 Ti 16 GB that evidence.md says are on order are real; if not, buy one 12 GB card (about £250 to £400) | +| D2 | before the next site push | The litepaper's "12 GB proves" is false on this build; three drafts of the replacement exist (proving-v1 440fd59, ca2-coord 1c8439f, proving-methods.md section 5) | Pick one; the proving-methods paragraph is recommended | +| D8 | the same push | "The card mines and proves" and "a visible 1% software fee" are solo-NVIDIA sentences now | Approve the qualified wording | +| D3, D7 | the same push | "Any 4 GB card", "4 GB about four years, 8 GB more than a decade", "4B hard cap" against the lifetime table and the 3.96 billion ever minted | Approve the re-cut sentences | +| D4 | gate 1 (before the testnet genesis) | Dataset growth mapping: (b) power-of-two steps (years 4, 12, 28, 60) keeps the public sentences true; (a) fades cards one year at a time | Choose; (b) recommended with the step calendar published | +| D5 | before the next site push | The chip claim: mixer x8 measured at 0.92x with the 3x factor, "under 2x" holds with margin on the stated convention | Confirm the claim stays, with the convention named | +| D6 | Counter ASIC 3.0 | AMD mines at a seventh of a 5090 and 4.9x the electricity per hash; no read width closes it | Accept for v3; the miners page says so | +| D10 | after route A's rows and D9's card | Proving route: route A (re-sized SP1 server) now; RISC Zero as a second proof family (Apple, and the fallback) is a consensus and verifier change | Measure both; adopt a second family only on your say | + | # | Row | What needs deciding | Recommendation | Why | |---|---|---|---|---| | D1 | C1 | The fee switch lands at H = 210,000 about 19:50Z on 6 October, and the 0.3.10 node's export does not carry the switch, so every app prover's shards are refused or vetoed from H. Move H, or race 0.3.11 onto every prover first | If 0.3.11 (with the proving-v1 fork, whose export carries `daaScore` and `feesV1ActivationDaa`) is not on every prover by 16:00Z on 6 October, republish the override with H = tip + 86,400 rounded to the next thousand, re-read the digest on a scratch node, every node in one sweep (the fee-switch plan's own rule for a later H). The coordinator holds the devnet delegation; this note is so the morning does not find proving dark | A dark proving pool on the devnet costs nothing on chain (the escrow keeps it) but every coverage, latency and fleet number measured after H is void | From 9f6a4ebc65ac314d471045f7a90ba60a1646d8cd Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:22:44 +0000 Subject: [PATCH 115/158] App: every worker gets --prepare-packs in the platform's path form and runs with the app data folder as cwd (program class v3: the Metal worker compiles v3 only from a prepared pack; the Macs would have answered need lines at the first v3 epoch) cargo test --release -p igneum-app under the build lock: 113 + 27 + 8 passed, 0 failed (the prepare_packs_tests, resume_tests and provedefault tests among them). Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/src/engine.rs | 43 ++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/app/igneum-app/src/engine.rs b/app/igneum-app/src/engine.rs index 84d356d5..5278f089 100644 --- a/app/igneum-app/src/engine.rs +++ b/app/igneum-app/src/engine.rs @@ -449,6 +449,12 @@ fn jitter_secs(seed: u64) -> u64 { 5 + (seed.wrapping_mul(2654435761) >> 7) % 56 } +/// The `--prepare-packs` directory the miner gets, relative to the app data folder (its cwd), in the platform's +/// separator: `packs\prepare` on Windows, `packs/prepare` elsewhere. Every worker gets it (program class v3). +pub(crate) fn prepare_packs_arg() -> String { + if cfg!(windows) { "packs\\prepare".to_string() } else { "packs/prepare".to_string() } +} + /// Seconds after a resume before every enabled card must be mining (a worker takes 10 to 60 s to its first STATUS /// line with a hash rate; the pack export before it a few seconds more). pub(crate) const RESUME_CHECK_SECS: u64 = 90; @@ -1464,13 +1470,16 @@ impl Engine { a.push("--network".into()); a.push(r.network.clone()); } + // The miner runs with the app data folder as its cwd: the pack paths stay relative (the miner splits + // --worker-args on spaces, and %LOCALAPPDATA% may carry a space in the user name). Every worker gets + // --prepare-packs (5 October 2026, program class v3: the Metal worker too compiles v3 only from a prepared + // pack, `prepare class=v3 era=`; before this the flag went to the CUDA and OpenCL + // workers only and a Mac would answer `need` lines at the first v3 epoch and stop mining, the 18:23Z class). + a.push("--prepare-packs".into()); + a.push(prepare_packs_arg()); if card.worker != "Metal" { - // The miner runs with the app data folder as its cwd: the pack paths stay relative (the miner splits - // --worker-args on spaces, and %LOCALAPPDATA% may carry a space in the user name). The prebuilt workers - // take the exported pack with --pack and build the next program from --prepare-packs; a worker built - // from source has the program compiled in and exits 42 at the boundary instead. - a.push("--prepare-packs".into()); - a.push("packs\\prepare".into()); + // The prebuilt workers take the exported pack with --pack and build the next program from + // --prepare-packs; a worker built from source has the program compiled in and exits 42 at the boundary. if card.worker == "OpenCL" { a.push("--job-nonces".into()); a.push("2097152".into()); @@ -1505,7 +1514,9 @@ impl Engine { self.miners[i].starts += 1; let seg = if self.miners[i].starts > 1 { format!("-r{}", self.miners[i].starts) } else { String::new() }; let log = self.shared.runtime.log_dir.join(format!("miner-{}-{}{seg}.log", self.miners[i].label, self.stamp)); - let cwd = if card.worker == "Metal" { None } else { Some(self.shared.runtime.app_dir.clone()) }; + // every worker runs with the app data folder as its cwd, so the relative pack paths resolve (the Metal worker + // too, since it takes --prepare-packs now) + let cwd = Some(self.shared.runtime.app_dir.clone()); self.miners[i].prepared = false; // the fleet's per-card kernel tuning (from the signed manifest) reaches the GPU worker through the miner's environment let envs: Vec<(String, String)> = self.ota.tuning_path().map(|p| vec![("IGNEUM_TUNING_FILE".to_string(), p.display().to_string())]).unwrap_or_default(); @@ -3447,6 +3458,24 @@ pub fn parse_race(body: &str) -> Option { Some(r) } +#[cfg(test)] +mod prepare_packs_tests { + use super::*; + + /// Program class v3 (5 October 2026): the Metal worker needs the prepare directory too, in the platform's + /// separator, relative to the app data folder the miner runs in. + #[test] + fn every_worker_gets_the_prepare_directory_in_the_platform_form() { + let arg = prepare_packs_arg(); + if cfg!(windows) { + assert_eq!(arg, "packs\\prepare"); + } else { + assert_eq!(arg, "packs/prepare", "the Mac's Metal worker gets a forward-slash path"); + } + assert!(!arg.contains(' ') && !arg.starts_with('/'), "relative, no space: the miner splits --worker-args on spaces and the data folder may carry one"); + } +} + #[cfg(test)] mod resume_tests { use super::*; From a215abc8095950a1a5da9e7e7407c870c2dca294 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:23:44 +0000 Subject: [PATCH 116/158] Consequences: C28 closed; C29 the litepaper's verifier ms is the ratio; C30 the 5090's 15% app gap; D11 the unfunded chip bounty on the hero Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 4 +++- docs/plans/consequences-decisions.md | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 9ccfb30d..1112a8e4 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -58,6 +58,8 @@ Sweep 7 (21:49 UTC) notes, no new row: the hot table is measured and NOT adopted | C27 | The root-socket fault recurred at 21:25Z from another agent's job (agg-cost-pc2-1) after the class fix and CI check landed; PC 2's prover was dark 37 minutes; a resume at 21:25 answered ok without restarting the miners | bench-log 1d78979; status 21:45, 22:03 | every PC job; the devnet's proving and hash rate tonight | The class check lives in CI, but PC jobs are published from worktrees by `publish-jobs.sh` and never pass through CI before they run, so a job written on a branch without the check runs the old shape. The check must run where the job is published, not only where the repo is tested | `packaging/ota/publish-jobs.sh add` runs `tools/ci/prover-socket-check.sh` and the bash-body check on the script it publishes and refuses on a failure; the sub-agent on the bash-body check wires both; the resume defect is on the next-cut list (stated) | sub-agent bash-body-check (the wiring); coordinator (the rule) | closed on branch bash-body-check 6805125 (publish-jobs.sh add --kind run runs the bash-body check and prover-socket-check.sh before signing, refuses with the output, never skips; test-publish-jobs.sh 32 passed with four new refusals). Merge notes for the integrator: prover-socket-check.sh exists on both proving-v1 c2544be and this branch (add/add, take the superset here); the socket grep flags tools/amd-prove/pc1-cpu-prove.ps1 (CPU-only, -u root, no GPU server) so that job needs the cleanup lines or an allow-list entry before its next publish, told to the coordinator | Sweep 8 (22:09 UTC) notes: mixer x8 DECIDED into v3 on the PC rows (the daily 1 GiB build latency-bound on every card: 5090 23 to 25 ms, 9070 XT 72 to 77 ms at every multiplier; the chip row 0.92x with the 3x factor), so the public claim holds with margin (D5 re-cut); the verifier regression (2.2x) bisected to inlining in the mixer's fetch loop and fixed, so the quiet-core figures of C19 return to about 0.6 / 0.9 / 1.3 ms; G6 job 3 failed on a stale fork test (era inside the class), job 4 on the final tree; the integration merge into master has three known conflicts (bench-log append-only, packfile.h and host.c take the ca2-v3 side). -| C28 | One sp1-gpu-server per card on rigs pins about 6 GB of host RAM per server today (under 2 GB with route A's buffers, approximate) | `docs/analysis/proving-methods.md` section 5, rig row (proving-methods e7e0db7) | rigs with several 24 GB or 32 GB cards on the rig installer | A six-card rig that proves on every card pins about 36 GB of host RAM under the shipped server; the rig installer's preflight says 16 GB to prove (a warning, per card not per rig) and its prover unit runs one server, so the moment it moves to one server per card (the analysis's route C) the RAM check is wrong by the card count | The rig preflight scales its RAM warning by the number of proving cards (6 GB each today, the route A figure when measured) and the README's per-card table gains a host RAM column; the one-server-per-card unit lands only with that check | rig installer a3e7b2b03222f5cff | sent | +| C28 | One sp1-gpu-server per card on rigs pins about 6 GB of host RAM per server today (under 2 GB with route A's buffers, approximate) | `docs/analysis/proving-methods.md` section 5, rig row (proving-methods e7e0db7) | rigs with several 24 GB or 32 GB cards on the rig installer | A six-card rig that proves on every card pins about 36 GB of host RAM under the shipped server; the rig installer's preflight says 16 GB to prove (a warning, per card not per rig) and its prover unit runs one server, so the moment it moves to one server per card (the analysis's route C) the RAM check is wrong by the card count | The rig preflight scales its RAM warning by the number of proving cards (6 GB each today, the route A figure when measured) and the README's per-card table gains a host RAM column; the one-server-per-card unit lands only with that check | rig installer a3e7b2b03222f5cff | closed (rig installer 086008a: the preflight checks host RAM against 8 GB plus about 6 GB per proving card at the 23,552 MB gate, PROVER_RAM_GB_PER_CARD default 6 marked approximate, the README host RAM row per tier; the one-server-per-card unit lands only with that check) | Sweep 8a (22:2x UTC) note: `docs/analysis/proving-methods.md` (branch proving-methods e7e0db7, 411 lines) carries its own per-tier table for today, route A, route D and route 3, and a public paragraph; it is the third draft of the proving public line (with proving-v1 440fd59 and ca2-coord 1c8439f), noted under D2 and D8; the route choice is D10. +| C29 | The litepaper's verifier line now reads "Measured 2.1 ms on one loaded Apple M5 Max core for class v3" and the status says "4.8x inside the gate"; the measurement (ca2-mixer 54bbfcc) is 2.79 ms per warp for x8 on the loaded core (2.1 was the RATIO to v2), worst cold unit 2.94 ms, quiet-core about 1.3 ms by scaling | site/litepaper.html on ca2-coord 8e65696; status 22:20 | the public page; every node operator who reads the gate margin | A ratio printed as milliseconds understates the verifier cost by a third and overstates the gate margin (10 / 2.79 = 3.6x, 3.4x on the worst cold unit, not 4.8x). The number is the one a reviewer will re-run first | The line reads "about 2.8 ms per warp on a loaded M5 Max core (about 1.3 ms quiet, approximate), 2.1x the v2 verifier; worst cold unit 2.9 ms; the 10 ms gate leaves 3.4x" until the quiet-core run lands | coordinator ada8afb62d752b1e2 | sent | +| C30 | The 5090 mines in the app at 115.4 MH/s (the power sweep, 22:09 to 22:15Z, hash from the app's API) against 136 to 137 MH/s at device time in every bench row tonight, with the cap not binding (draw 316 W under a 431 W cap, SM at 3,051 MHz) | bench-log e304458; read-width and mixer PC rows | every 5090 owner on the app (and every big card: the gap is the app's job loop, not the kernel) | About 15% of a 5090's hash is lost between the kernel and the app, and the sweep entry explains it away as API sampling. M11 measured the 9070 XT at the app's 2^21 job size equal to its 2^24 rate, but no 5090 row exists at 2^21; the 5090 finishes a 2^21 job in about 15 ms, so per-job launch, read-back and template work can cost that much. The STATUS line prints "wall" and "inside jobs" rates and would show it | One measurement on PC 1: `igneum-worker-cuda --bench` on the live pack at --batch-log2 21 and 24 on the 5090, and the 5090's STATUS wall-against-inside gap over 10 minutes; if the job size is the cause, the app's job size for cards over 100 MH/s rises (2^22 or 2^23) in the next cut: a 15% gain for every 5090 owner | repro-bench agent a0b9f574775ef1693 (its PC 1 slot); coordinator | sent | diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index 34f92029..d8ebf511 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -14,6 +14,7 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | D4 | gate 1 (before the testnet genesis) | Dataset growth mapping: (b) power-of-two steps (years 4, 12, 28, 60) keeps the public sentences true; (a) fades cards one year at a time | Choose; (b) recommended with the step calendar published | | D5 | before the next site push | The chip claim: mixer x8 measured at 0.92x with the 3x factor, "under 2x" holds with margin on the stated convention | Confirm the claim stays, with the convention named | | D6 | Counter ASIC 3.0 | AMD mines at a seventh of a 5090 and 4.9x the electricity per hash; no read width closes it | Accept for v3; the miners page says so | +| D11 | before the next site push | The hero, the abstract and the level 1 copy now say "the model and the bounty are public" / "bounty standing"; funding.md prices the chip bounty at USD 50,000, marks it NOT FUNDED, and its rule 3 says a bounty is announced only when escrowed | Strike "and the bounty" from the hero and the abstract until the USD 50,000 is escrowed, or escrow it; the litepaper's older "a bounty is attached" (finality, line 686) is the same question | | D10 | after route A's rows and D9's card | Proving route: route A (re-sized SP1 server) now; RISC Zero as a second proof family (Apple, and the fallback) is a consensus and verifier change | Measure both; adopt a second family only on your say | | # | Row | What needs deciding | Recommendation | Why | @@ -28,3 +29,4 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | D8 | C7 | The miner page says "One click: install, start, the card mines and proves" (site/miner.html 7, 13, 21; site/index.html 484). On HiveOS and the rig installer a rig mines only until a Linux prover build is published, and on the app a 12 GB card mines only, a 16 GB card proves with the miner paused, 20 GB and up does both (the sweep of 5 October, before the v1-shard row) | Qualify the sentence on the miner page and the homepage card: "the card mines; 24 GB cards prove too, 32 GB does both at once; rigs mine until the Linux prover ships". The same page's "a visible 1% software fee you can switch off" becomes "solo mining carries a 1% software fee you can switch off; in a pool the pool's own fee is the only one" once pool-v0 ships (the pool agent fixed the rule: no software dev fee in pool mode) | The sentence is the product's first promise and tonight's measurement bounds it by card | | D9 | C3, C15, C26 | No 12 GB NVIDIA card exists in the fleet, so every 12 GB number tonight is scaled from the 5090 and labelled approximate. The 13.9 GB floor is SP1's GPU server code (`docs/analysis/proving-methods.md`, branch proving-methods e7e0db7, section 1.3: builder.rs adds 4 GB to the card's physical memory and panics under 24, so a 16 GB card (16 + 4 = 20) and a 12 GB card never start and 20 GB is the smallest that does; the trace is allocated at the maximum shard; the CUDA mempool never releases; the proving plan's "under 24" (4c82e56) is the same test read before the addition). The prover-floor patch and the per-card profiles cannot be measured without the hardware | Buy one 12 GB NVIDIA card this week for PC 1's spare slot (an RTX 3060 12 GB or 4070 12 GB, about £250 to £400, approximate; the coordinator's request). Check first whether the RTX 3060 and the RTX 5060 Ti 16 GB that evidence.md row 16 says are "on order" are real and arriving; if so, no purchase, only the delivery date. No public line says "12 GB proves" before a real 12 GB card runs the rebuilt server on the S_p-curve fixtures and recipe | Money, and the one measurement every 12 GB claim rests on; the prover-floor agent's rows tonight replace the approximate figures when they land | | D10 | C3, C15, C26, C28 | The proving route for the 12 GB tier and for Apple: `docs/analysis/proving-methods.md` section 4 ranks (1) route A, a re-sized SP1 GPU server with `S_p` as the dial and one server per card on rigs, nothing in consensus moving; (2) route D, RISC Zero 3.0.x as proof-system version 2 (the only shipped prover with a documented sub-12 GB configuration and a Metal path), the fallback if A misses 11 GB and the Apple route either way, 3 to 4 days plus a 3-month two-verifier overlap and three spec 7.8 rules; (3) a sumcheck family without a codeword (Jolt-class) in years, not now. Route A is already running tonight; route D adds a second proof family to the node, which is a consensus and verifier change outside tonight's delegation | Route A on the prover-floor rows, gated as the document says (the adopted shard under 11 GB alone and under 60 s prove-only on a real 12 GB card, D9); route D's measurement (RISC Zero at po2 19 and 20 on PC 2 and the Mac's Metal row) may run as a measurement, but adopting a second proof family waits for the project lead and for route A's result; the public paragraph of section 5 ("Proving runs on NVIDIA cards with 24 GB or more today. A build for 12 GB and 16 GB cards is being measured ...") is the honest line meanwhile and is the one of the three drafts to prefer, because it names what is being measured instead of a tier | A second verifier in the node is the kind of change the testnet's genesis must carry from day one; measuring it costs nothing, adopting it is the project lead's | +| D11 | C29 context; `docs/plans/funding.md` 36 and 63 | The chip bounty on the public pages: "the model and the bounty are public" (hero, abstract, ca2-coord cabec3b), "bounty standing" (level 1 copy); funding.md: USD 50,000 standing, "Not funded", "a bounty is announced only when it is escrowed"; the litepaper already says "a bounty is attached" to the finality review (line 686) | Strike "and the bounty" from the hero and the abstract and "standing" from level 1 until the money is escrowed, and say "a bounty follows the external review" if a sentence is wanted; or escrow USD 50,000 (and the USD 25,000 finality bounty) and keep the words. The coordinator is told to hold the words at the ship pending this | A public promise of money the project has not set aside is the FUD the ledger exists to prevent, by the project's own funding rule | From e805ceab8f51f579cc518e7e3af444689ba17be1 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:24:08 +0000 Subject: [PATCH 117/158] Consequences decisions: the glance table counts eleven Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-decisions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index d8ebf511..2e102837 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -2,7 +2,7 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number that needs the project lead: money, a public claim, or a consensus parameter outside tonight's delegation. The row number points at `consequences-2026-10-05.md`. Recommendation first, then the options. -## The ten in one glance (what the project lead does, in the order they bite) +## The eleven in one glance (what the project lead does, in the order they bite) | # | By when | One line | What the project lead does | |---|---|---|---| From 8e47f84d1742902022fda8775ba2aa3886218a9f Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:25:28 +0000 Subject: [PATCH 118/158] Metal worker: a class v3 day is built from the pack (memhard.metal: the mixer multiplier, the cache size, the era layout), datasets keyed by (day, class, era); class-v3.mjs --metal runs node 0's miner on the Metal worker (gate G4b) and --genesis-bits The Swift DatasetContext is the version 2 item construction; a v3 program over it would hash another dataset than the node's (every found refused by the CPU re-check). servePackDataset compiles the pack's memhard.metal and runs igneum_cache_fill and igneum_build as packbench does, releases the cache, and the v3 job path takes the pack program and the pack day of its class and era only (need + error otherwise). The gate script's --metal mode drives igneum-miner --worker --prepare-packs --exit-on-seed-change (the app's shape) on node 0 and reports the PREPARE and prepared lines, need / mismatch / refusal lines, accepted blocks after the switch, the CPU re-check counter and the swap line. Co-Authored-By: Claude Fable 5.1 --- infra/fast-time/class-v3.mjs | 69 +++++++++++++++++++-- proto-metal/main.swift | 114 ++++++++++++++++++++++++++++++----- 2 files changed, 163 insertions(+), 20 deletions(-) diff --git a/infra/fast-time/class-v3.mjs b/infra/fast-time/class-v3.mjs index ca0af9df..3a82a3ea 100644 --- a/infra/fast-time/class-v3.mjs +++ b/infra/fast-time/class-v3.mjs @@ -14,7 +14,15 @@ // check passes. Never touches the live devnet (26610/26611, 26640/26641, 28640) or the simnet.mjs ports. // // node infra/fast-time/class-v3.mjs [--secs 420] [--activation 150] [--epochs-after 2] [--threads 1] +// [--metal ] [--genesis-bits 0x1f010000] // IGNEUMD and IGNEUM_MINER name the binaries (default: the ca2 fork worktree's target-ca2/release). +// --metal: node 0's miner is a real Metal GPU worker (igneum-miner --worker --prepare-packs +// --exit-on-seed-change, the app's own shape), so the Metal worker's class v3 path (the prepare line with the pack +// directory and the class and era tokens, the pack-built program and day) mines across the boundary (gate G4b); +// the report then adds the miner's PREPARE lines, the worker's prepared lines, any need / mismatch / refusal line, +// the accepted blocks on each side and the swap time. --genesis-bits raises the CPU difficulty for a GPU miner +// (0x1e010000 = 2^24 hashes per block: under a second on an M5 Max; the CPU miners on nodes 1 and 2 then verify +// and rarely find). import { spawn } from 'node:child_process'; import { mkdirSync, rmSync, writeFileSync, readFileSync, openSync, existsSync } from 'node:fs'; @@ -28,9 +36,11 @@ const IGNEUMD = process.env.IGNEUMD || `${BIN}/igneumd`; const CPU_MINER = process.env.IGNEUM_MINER || `${BIN}/igneum-miner`; const TMP = '/tmp/igneum-fast-time-v3'; const BASE = 29600, SUFFIX = 960; -const GENESIS_BITS = 0x1f010000; const args = process.argv.slice(2); const flag = (name, dflt) => { const i = args.indexOf(`--${name}`); return i >= 0 ? +args[i + 1] : dflt; }; +const sflag = (name) => { const i = args.indexOf(`--${name}`); return i >= 0 ? args[i + 1] : null; }; +const GENESIS_BITS = flag('genesis-bits', 0x1f010000); +const METAL = sflag('metal'); const SECS = flag('secs', 420); const ACTIVATION = flag('activation', 150); const EPOCHS_AFTER = flag('epochs-after', 2); @@ -38,7 +48,7 @@ const THREADS = flag('threads', 1); const started = []; const log = (...a) => console.log(new Date().toISOString().slice(11, 23), ...a); const sleep = (ms) => new Promise(r => setTimeout(r, ms)); -for (const b of [IGNEUMD, CPU_MINER]) if (!existsSync(b)) { console.error(`missing ${b}`); process.exit(2); } +for (const b of [IGNEUMD, CPU_MINER, ...(METAL ? [METAL] : [])]) if (!existsSync(b)) { console.error(`missing ${b}`); process.exit(2); } rmSync(TMP, { recursive: true, force: true }); mkdirSync(TMP, { recursive: true }); // The file is merged as TEXT, never through JSON.parse: a `never` height is 18446744073709551615, which JavaScript @@ -95,7 +105,8 @@ async function stopAll() { } process.on('SIGINT', async () => { await stopAll(); process.exit(130); }); process.on('unhandledRejection', async (e) => { log(`FAILED: ${e?.stack || e}`); await stopAll(); process.exit(3); }); // a thrown start leaves no node behind -const minerLog = (i) => { try { return readFileSync(`${TMP}/cpu${i}.log`, 'utf8').split('\n'); } catch { return []; } }; +const minerName = (i) => (METAL && i === 0) ? 'metal0' : `cpu${i}`; +const minerLog = (i) => { try { return readFileSync(`${TMP}/${minerName(i)}.log`, 'utf8').split('\n'); } catch { return []; } }; const t0 = Date.now(); const since = () => ((Date.now() - t0) / 1000).toFixed(1); @@ -110,8 +121,18 @@ for (const n of nodes) { log(`n0 PoW schedule: ${n0.grepLog(/PoW schedule from the override file/).map(l => l.replace(/^.*?(PoW schedule)/, '$1')).join(' | ') || '(no line)'}`); log(`n0 digest: ${n0.grepLog(/Consensus params digest/).map(l => l.replace(/^.*?digest: /, '').slice(0, 16)).join(' ')}`); -// one real CPU miner per node, 1 thread, 2^16 expected hashes per block at genesis -nodes.forEach((n, i) => miner(CPU_MINER, ['mine', n.grpc, String(THREADS), String(SECS), `cpu${i}`, '--engine', 'igneum-pow', '--payout-label', `cpu${i}`, '--status-secs', '30', '--no-vote'], `cpu${i}`, { IGNEUM_POW_DAY_MS: String(DAY_MS) })); +// one real CPU miner per node, 1 thread, 2^16 expected hashes per block at genesis; with --metal, node 0's miner drives +// the Metal worker the way the app does (the pack for every prepared pair under packs/prepare, exit 42/44 on a refusal) +const PACKS = `${TMP}/packs/prepare`; +mkdirSync(PACKS, { recursive: true }); +nodes.forEach((n, i) => { + if (METAL && i === 0) { + miner(CPU_MINER, ['mine', n.grpc, '1', String(SECS), 'metal0', '--engine', 'igneum-pow', '--worker', METAL, '--prepare-packs', PACKS, '--exit-on-seed-change', '--payout-label', 'metal0', '--status-secs', '30', '--no-vote'], 'metal0', { IGNEUM_POW_DAY_MS: String(DAY_MS) }); + log(`n0 miner: Metal worker ${METAL}, packs under ${PACKS}`); + } else { + miner(CPU_MINER, ['mine', n.grpc, String(THREADS), String(SECS), `cpu${i}`, '--engine', 'igneum-pow', '--payout-label', `cpu${i}`, '--status-secs', '30', '--no-vote'], `cpu${i}`, { IGNEUM_POW_DAY_MS: String(DAY_MS) }); + } +}); const pay = devAddress('fast-time-v3'); const epochs = new Map(); // epoch index -> { class, firstSeenDaa, at } @@ -187,6 +208,29 @@ const sinks = dag.map(d => String(d.sink || '?').slice(0, 16)); const counts = dag.map(d => d.blockCount ?? '?'); const tips = dag.map(d => (d.tipHashes || []).length); +// the Metal miner's protocol lines (gate G4b): PREPARE sent (the miner), prepared / need / error (the worker), the swap +let metal = null; +if (METAL) { + const L = minerLog(0); + const t = (l) => { const m = /^(\d+\.\d+) /.exec(l); return m ? +m[1] * 1000 : null; }; + const switchAt = firstV3 ? t0 + firstV3.at * 1000 : null; + const prepares = L.filter(l => /PREPARE sent for epoch seed/.test(l)).map(l => l.replace(/^\S+ /, '')); + const prepared = L.filter(l => /worker: prepared /.test(l)).map(l => l.replace(/^\S+ /, '')); + const preparedV3 = prepared.filter(l => / class v3 /.test(l)); + const need = L.filter(l => /worker: need |^\S+ worker error:.*need /.test(l) || /\bneed [0-9a-f]{64}/.test(l)); + const mismatch = L.filter(l => /program class mismatch|era seed mismatch|PACK OUT OF DATE|pack .*: program pack/.test(l)); + const refused = L.filter(l => /exiting with code 4[24]|refused the program pack|prepare-failed/.test(l)); + const swaps = L.filter(l => /swapped with no pause|compiles inline|worker without prepare support/.test(l)).map(l => l.replace(/^\S+ /, '')); + const accepted = L.filter(l => /ACCEPTED block/.test(l)); + const acceptedAfter = switchAt ? accepted.filter(l => (t(l) || 0) >= switchAt).length : 0; + const found = L.filter(l => /worker: found |^\S+ found /.test(l)).length; + const status = L.filter(l => /miner 'metal0' \[igneum-pow\]/.test(l)).map(l => l.replace(/^\S+ /, '')); + const lastStatus = status.at(-1) || ''; + const mismatched = +(/mismatched=(\d+)/.exec(lastStatus)?.[1] ?? 0); + const rate = /hash=([\d.]+) MH\/s/.exec(lastStatus)?.[1]; + metal = { worker: METAL, prepares, prepared, prepared_v3: preparedV3, need: need.length, mismatch_lines: mismatch, refused, swaps, accepted_total: accepted.length, accepted_after_switch: acceptedAfter, found_lines: found, cpu_recheck_mismatched: mismatched, rate_mh_s: rate, last_status: lastStatus }; +} + const checks = { switch_line_on_every_node: switchLines.every(Boolean), switch_line_names_the_rounded_epoch: switchLines.every(l => l && l.includes(`active from epoch ${FIRST_V3_EPOCH} `)), @@ -200,6 +244,14 @@ const checks = { zero_rejected_by_nodes: rejectedNode.every(r => r.length === 0), sinks_agree: new Set(sinks).size === 1, block_counts_agree: new Set(counts.map(String)).size === 1, + ...(METAL ? { + metal_prepare_sent_for_v3: metal.prepares.some(l => / class v3 /.test(l)), + metal_worker_prepared_v3_pack: metal.prepared_v3.length > 0, + metal_no_need_or_mismatch: metal.need === 0 && metal.mismatch_lines.length === 0 && metal.refused.length === 0, + metal_accepted_blocks_after_switch: metal.accepted_after_switch > 0, + metal_cpu_recheck_clean: metal.cpu_recheck_mismatched === 0, + metal_swapped_without_pause: metal.swaps.some(l => /swapped with no pause/.test(l)), + } : {}), }; const pass = Object.values(checks).every(Boolean); const summary = { @@ -210,10 +262,15 @@ const summary = { programs: programRows, accepted_per_miner: accepted, rejected_by_miners: rejectedMiner.map(r => r.length), rejected_by_nodes: rejectedNode.map(r => r.length), rejected_lines: [...rejectedMiner.flat(), ...rejectedNode.flat()].slice(0, 20), - sinks, block_counts: counts, tips_per_node: tips, switch_lines: switchLines, samples, + sinks, block_counts: counts, tips_per_node: tips, switch_lines: switchLines, samples, metal, }; writeFileSync(`${TMP}/summary.json`, JSON.stringify(summary, null, 2)); log(`SUMMARY ${pass ? 'PASS' : 'FAIL'}: blocks ${before.length} before / ${after.length} after the boundary at DAA ${BOUNDARY} (chain ${chainBefore} / ${chainAfter}); programs ${programRows.map(p => `e${p.epoch}:${p.class}:${p.program_id}:${Math.max(...p.ready_ms)}ms`).join(' ')}; rejected miners ${rejectedMiner.map(r => r.length).join('/')} nodes ${rejectedNode.map(r => r.length).join('/')}; sinks ${sinks.join(' ')} (${checks.sinks_agree ? 'agree' : 'DIFFER'}); block counts ${counts.join('/')}; switch lines ${switchLines.filter(Boolean).length}/3`); +if (metal) { + log(`METAL: ${metal.prepares.length} PREPARE lines (${metal.prepares.filter(l => / class v3 /.test(l)).length} class v3), ${metal.prepared.length} prepared (${metal.prepared_v3.length} v3), need ${metal.need}, mismatch/refusal lines ${metal.mismatch_lines.length + metal.refused.length}, accepted ${metal.accepted_total} (${metal.accepted_after_switch} after the switch), cpu re-check mismatched ${metal.cpu_recheck_mismatched}, rate ${metal.rate_mh_s} MH/s`); + for (const l of [...metal.prepares, ...metal.prepared, ...metal.swaps]) log(` ${l.slice(0, 260)}`); + for (const l of [...metal.mismatch_lines, ...metal.refused].slice(0, 10)) log(` BAD ${l.slice(0, 260)}`); +} for (const [k, v] of Object.entries(checks)) if (!v) log(`FAILED CHECK ${k}`); log(`summary: ${TMP}/summary.json`); await stopAll(); diff --git a/proto-metal/main.swift b/proto-metal/main.swift index 09e52fa4..085dbc74 100644 --- a/proto-metal/main.swift +++ b/proto-metal/main.swift @@ -2964,9 +2964,29 @@ func raceProgram(_ gpu: GPU, _ program: ServeProgram, dataset: MTLBuffer, datase final class ServeDataset { let dayHex: String - let ctx: DatasetContext + /// The Swift-side context of a class v2 day (the version 2 item construction); nil for a day built from a pack + /// (class v3, Counter ASIC 2.0: the pack's memhard.metal carries the mixer multiplier and the era layout) + let ctx: DatasetContext? let buffer: MTLBuffer - init(dayHex: String, ctx: DatasetContext, buffer: MTLBuffer) { self.dayHex = dayHex; self.ctx = ctx; self.buffer = buffer } + /// Counter ASIC 2.0: the class and era this day's dataset was built for; a v3 program hashes over the dataset of + /// its own class and era only (the item construction and the layout differ from v2's) + let programClass: String + let eraHex: String + let cacheFillGPUms: Double + let buildGPUms: Double + init(dayHex: String, ctx: DatasetContext, buffer: MTLBuffer) { + self.dayHex = dayHex; self.ctx = ctx; self.buffer = buffer; self.programClass = "v2"; self.eraHex = "" + self.cacheFillGPUms = ctx.cacheFillGPUms; self.buildGPUms = ctx.lastBuildGPUms + } + init(dayHex: String, packClass: String, eraHex: String, buffer: MTLBuffer, cacheFillGPUms: Double, buildGPUms: Double) { + self.dayHex = dayHex; self.ctx = nil; self.buffer = buffer; self.programClass = packClass; self.eraHex = eraHex + self.cacheFillGPUms = cacheFillGPUms; self.buildGPUms = buildGPUms + } + func matches(cls: String, era: String) -> Bool { + if cls != "" && cls != programClass { return false } + if era != "" && programClass == "v3" && era.lowercased() != eraHex.lowercased() { return false } + return true + } } // The resident programs and datasets, shared by the job loop (main thread) and the prepare queue (background). @@ -2978,7 +2998,7 @@ final class ServeStore { var current: (String, String)? = nil func program(_ seedHex: String, cls: String = "", era: String = "") -> ServeProgram? { lock.lock(); defer { lock.unlock() }; return programs.first { $0.seedHex == seedHex && $0.matches(cls: cls, era: era) } } - func dataset(_ dayHex: String) -> ServeDataset? { lock.lock(); defer { lock.unlock() }; return datasets.first { $0.dayHex == dayHex } } + func dataset(_ dayHex: String, cls: String = "", era: String = "") -> ServeDataset? { lock.lock(); defer { lock.unlock() }; return datasets.first { $0.dayHex == dayHex && $0.matches(cls: cls, era: era) } } func counts() -> (Int, Int) { lock.lock(); defer { lock.unlock() }; return (programs.count, datasets.count) } /// Adds a program; with more than two resident, the oldest one that is not the current job's goes. @@ -2990,7 +3010,7 @@ final class ServeStore { } func add(_ d: ServeDataset) { lock.lock(); defer { lock.unlock() } - if datasets.contains(where: { $0.dayHex == d.dayHex }) { return } + if datasets.contains(where: { $0.dayHex == d.dayHex && $0.programClass == d.programClass && $0.eraHex == d.eraHex }) { return } datasets.append(d) while datasets.count > 2, let i = datasets.firstIndex(where: { $0.dayHex != current?.1 && $0.dayHex != d.dayHex }) { datasets.remove(at: i) } } @@ -3088,6 +3108,66 @@ func serveDataset(_ gpu: GPU, _ store: ServeStore, dayHex: String, day: [UInt8], return (sd, ms(t0, nowNs())) } +// Counter ASIC 2.0: the day's cache and dataset of a class v3 pack, built on the device from the pack's own memhard.metal +// (igneum_cache_fill, igneum_build: the mixer multiplier, the cache size of the day under the growth rule and the era's +// interleaved layout are all in that text), as packbench.swift builds it; the Swift DatasetContext is the version 2 +// construction and must never serve a v3 program. The cache buffer is released after the build. program.h gives the +// sizes and the identity; a pack with a hot table (IGNEUM_HOT_MB > 0) is refused here (the hot table is not adopted). +func servePackDataset(_ gpu: GPU, _ store: ServeStore, dayHex: String, dir: String, wantClass: String, wantEra: String) throws -> (ServeDataset, Double) { + if let d = store.dataset(dayHex, cls: wantClass, era: wantEra) { return (d, 0) } + let t0 = nowNs() + func refuse(_ why: String) -> NSError { NSError(domain: "pack", code: 3, userInfo: [NSLocalizedDescriptionKey: "pack \(dir): \(why)"]) } + guard let programH = try? String(contentsOfFile: dir + "/program.h", encoding: .utf8) else { throw refuse("cannot read program.h") } + func defineU32(_ name: String) -> UInt32? { + guard let re = try? NSRegularExpression(pattern: "#define \(name) ([0-9a-fA-Fx]+)"), let m = re.firstMatch(in: programH, range: NSRange(programH.startIndex..., in: programH)) else { return nil } + let v = String(programH[Range(m.range(at: 1), in: programH)!]).replacingOccurrences(of: "u", with: "") + return v.hasPrefix("0x") ? UInt32(v.dropFirst(2), radix: 16) : UInt32(v) + } + func defineStr(_ name: String) -> String? { + guard let re = try? NSRegularExpression(pattern: "#define \(name) \"([^\"]*)\""), let m = re.firstMatch(in: programH, range: NSRange(programH.startIndex..., in: programH)) else { return nil } + return String(programH[Range(m.range(at: 1), in: programH)!]) + } + let generator = defineU32("IGNEUM_GENERATOR") ?? 1 + let packClass = generator == 3 ? "v3" : "v2" + let eraHex = defineStr("IGNEUM_ERA_SEED_HEX") ?? "" + if let packDay = defineStr("IGNEUM_DAY_BYTES_HEX"), packDay.lowercased() != dayHex.lowercased() { throw refuse("the pack's day is \(packDay), not \(dayHex)") } + if wantClass != "" && wantClass != packClass { throw refuse("program class mismatch: this pack is class \(packClass), the line names class \(wantClass)") } + if wantEra != "" && packClass == "v3" && wantEra.lowercased() != eraHex.lowercased() { throw refuse("era seed mismatch: the pack's era is \(eraHex.prefix(16)), the line names \(wantEra.prefix(16))") } + if (defineU32("IGNEUM_HOT_MB") ?? 0) > 0 { throw refuse("a hot-table pack (IGNEUM_HOT_MB) is not served by this worker") } + guard (defineU32("IGNEUM_DATASET_MODE") ?? 0) == 1 else { throw refuse("not a memory-hard pack (IGNEUM_DATASET_MODE 1)") } + let datasetLog2 = Int(defineU32("IGNEUM_DATASET_LOG2") ?? 28) + let cacheLog2 = Int(defineU32("IGNEUM_CACHE_LOG2_WORDS") ?? 26) + let cacheSegments = Int(defineU32("IGNEUM_CACHE_SEGMENTS") ?? 65536) + guard datasetLog2 >= 20 && datasetLog2 <= 31 && cacheLog2 >= 16 && cacheLog2 <= 30 && cacheSegments % 256 == 0 && cacheSegments > 0 else { throw refuse("program.h sizes out of range") } + guard let mhSrc = try? String(contentsOfFile: dir + "/memhard.metal", encoding: .utf8) else { throw refuse("cannot read memhard.metal") } + let lib: MTLLibrary + do { lib = try gpu.device.makeLibrary(source: mhSrc, options: MTLCompileOptions()) } catch { throw refuse("Metal compile of memhard.metal: \(error)") } + guard let fillFn = lib.makeFunction(name: "igneum_cache_fill"), let buildFn = lib.makeFunction(name: "igneum_build") else { throw refuse("memhard.metal lacks igneum_cache_fill or igneum_build") } + let fillPipe = try gpu.device.makeComputePipelineState(function: fillFn) + let buildPipe = try gpu.device.makeComputePipelineState(function: buildFn) + let words = 1 << datasetLog2 + guard let cache = gpu.device.makeBuffer(length: (1 << cacheLog2) * 4, options: .storageModePrivate) else { throw refuse("cannot allocate the 2^\(cacheLog2) word cache") } + guard let dataset = gpu.device.makeBuffer(length: words * 4, options: .storageModePrivate) else { throw refuse("cannot allocate the 2^\(datasetLog2) word dataset") } + func run(_ body: (MTLComputeCommandEncoder) -> Void) throws -> Double { + let cb = gpu.queue.makeCommandBuffer()! + let enc = cb.makeComputeCommandEncoder()! + body(enc); enc.endEncoding(); cb.commit(); cb.waitUntilCompleted() + if let e = cb.error { throw refuse("command buffer: \(e)") } + return (cb.gpuEndTime - cb.gpuStartTime) * 1000 + } + let fillMs = try run { enc in + enc.setComputePipelineState(fillPipe); enc.setBuffer(cache, offset: 0, index: 0) + enc.dispatchThreadgroups(MTLSize(width: cacheSegments / 256, height: 1, depth: 1), threadsPerThreadgroup: MTLSize(width: 256, height: 1, depth: 1)) + } + let buildMs = try run { enc in + enc.setComputePipelineState(buildPipe); enc.setBuffer(cache, offset: 0, index: 0); enc.setBuffer(dataset, offset: 0, index: 1) + enc.dispatchThreadgroups(MTLSize(width: words / 16 / 256, height: 1, depth: 1), threadsPerThreadgroup: MTLSize(width: 256, height: 1, depth: 1)) + } + let sd = ServeDataset(dayHex: dayHex, packClass: packClass, eraHex: eraHex, buffer: dataset, cacheFillGPUms: fillMs, buildGPUms: buildMs) + store.add(sd) + return (sd, ms(t0, nowNs())) +} + func runServe(_ opts: Options) -> Never { let gpu = GPU() let datasetLog2 = opts.datasetLog2 @@ -3123,7 +3203,7 @@ func runServe(_ opts: Options) -> Never { emit("prepare-failed \(f[1]) \(f[2]) bad field (epoch_seed 64 hex, day_seed hex)"); continue } let (epochHex, dayHex) = (f[1], f[2]) - let have = (store.program(epochHex, cls: wantClass, era: wantEra) != nil, store.dataset(dayHex) != nil) + let have = (store.program(epochHex, cls: wantClass, era: wantEra) != nil, store.dataset(dayHex, cls: wantClass, era: wantEra) != nil) if have.0 && have.1 { emit("prepared \(epochHex) \(dayHex) 0 program 0 dataset 0 (already resident)"); continue } let packDir = f.count > 3 ? f[3...].joined(separator: " ") : "" if wantClass == "v3" && packDir.isEmpty { emit("prepare-failed \(epochHex) \(dayHex) a class v3 program is compiled from a pack: send prepare with a pack directory (igneum-miner --prepare-packs )"); continue } @@ -3133,7 +3213,10 @@ func runServe(_ opts: Options) -> Never { let (sp, progMs) = wantClass == "v3" ? try servePackProgram(gpu, store, seedHex: epochHex, seed: epochSeed, dir: packDir, wantClass: wantClass, wantEra: wantEra) : try serveProgram(gpu, store, seedHex: epochHex, seed: epochSeed, datasetLog2: datasetLog2) - let (sd, dsMs) = serveDataset(gpu, store, dayHex: dayHex, day: daySeed, datasetLog2: datasetLog2) + // a class v3 day is built from the pack (its construction and layout), a v2 day by the Swift context + let (sd, dsMs) = wantClass == "v3" + ? try servePackDataset(gpu, store, dayHex: dayHex, dir: packDir, wantClass: wantClass, wantEra: wantEra) + : serveDataset(gpu, store, dayHex: dayHex, day: daySeed, datasetLog2: datasetLog2) // The race: the prepared program against its own dataset, exclusive windows between jobs var raceMs = 0.0 if sp.raceDue && opts.race != "off" { @@ -3142,7 +3225,7 @@ func runServe(_ opts: Options) -> Never { raceMs = ms(r0, nowNs()) } let (np, nd) = store.counts() - emit("prepared \(epochHex) \(dayHex) \(fmt(ms(t0, nowNs()), 1)) program \(fmt(progMs, 1)) dataset \(fmt(dsMs, 1)) race \(fmt(raceMs, 1)) variant \(sp.compiled.variant) class \(sp.programClass) loads/hash \(sp.loadsPerHash) cache-fill \(fmt(sd.ctx.cacheFillGPUms, 1)) resident \(np) programs \(nd) datasets") + emit("prepared \(epochHex) \(dayHex) \(fmt(ms(t0, nowNs()), 1)) program \(fmt(progMs, 1)) dataset \(fmt(dsMs, 1)) race \(fmt(raceMs, 1)) variant \(sp.compiled.variant) class \(sp.programClass) loads/hash \(sp.loadsPerHash) cache-fill \(fmt(sd.cacheFillGPUms, 1)) build \(fmt(sd.buildGPUms, 1)) resident \(np) programs \(nd) datasets") } catch { emit("prepare-failed \(epochHex) \(dayHex) \(wantClass == "v3" ? "" : "Metal compile failed: ")\(error.localizedDescription)") } } continue @@ -3165,22 +3248,25 @@ func runServe(_ opts: Options) -> Never { let program: ServeProgram let dataset: ServeDataset do { - if store.program(pair.0, cls: wantClass, era: wantEra) == nil || store.dataset(pair.1) == nil { prepareQueue.sync {} } + if store.program(pair.0, cls: wantClass, era: wantEra) == nil || store.dataset(pair.1, cls: wantClass, era: wantEra) == nil { prepareQueue.sync {} } let sp: ServeProgram + let sd: ServeDataset if wantClass == "v3" { - guard let resident = store.program(pair.0, cls: wantClass, era: wantEra) else { + // a v3 job runs on the pack program and the pack-built day of its class and era, both prepared; nothing + // is compiled inline (the Swift generator and context are the version 2 construction) + guard let resident = store.program(pair.0, cls: wantClass, era: wantEra), let day = store.dataset(pair.1, cls: wantClass, era: wantEra) else { emit("need \(pair.0) \(pair.1)") - emit("error \(jobId) pack (metal): program class mismatch: no class v3 program for epoch \(pair.0.prefix(16)) is resident (this worker compiles v3 from a pack); send prepare with a pack directory") + emit("error \(jobId) pack (metal): program class mismatch: no class v3 program and day for epoch \(pair.0.prefix(16)) day \(pair.1) is resident (this worker builds v3 from a pack); send prepare with a pack directory") continue } - sp = resident + sp = resident; sd = day } else { let (p, progMs) = try serveProgram(gpu, store, seedHex: pair.0, seed: epochSeed, datasetLog2: datasetLog2) if progMs > 0 { emit("info program epoch \(pair.0.prefix(16)) loads/hash \(p.loadsPerHash) compiled inline in \(fmt(progMs, 1)) ms (not prepared)") } - sp = p + let (d, dsMs) = serveDataset(gpu, store, dayHex: pair.1, day: daySeed, datasetLog2: datasetLog2) + if dsMs > 0 { emit("info dataset day \(pair.1) built inline in \(fmt(dsMs, 1)) ms (cache fill \(fmt(d.cacheFillGPUms, 1)) ms, build \(fmt(d.buildGPUms, 1)) ms GPU; not prepared)") } + sp = p; sd = d } - let (sd, dsMs) = serveDataset(gpu, store, dayHex: pair.1, day: daySeed, datasetLog2: datasetLog2) - if dsMs > 0 { emit("info dataset day \(pair.1) built inline in \(fmt(dsMs, 1)) ms (cache fill \(fmt(sd.ctx.cacheFillGPUms, 1)) ms, build \(fmt(sd.ctx.lastBuildGPUms, 1)) ms GPU; not prepared)") } program = sp; dataset = sd } catch { emit("error \(jobId) Metal compile failed: \(error)"); continue } if switched, let prev = lastPair { From b7f4cc9f13809d06e270f4931b397579b9f90201 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:27:06 +0000 Subject: [PATCH 119/158] Consequences ledger: sweep 9 notes (H lands 18:50 to 19:35Z by the live rate, G4b), C30 taken; D1 ETA re-cut Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 4 +++- docs/plans/consequences-decisions.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 1112a8e4..9ebe85e0 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -62,4 +62,6 @@ Sweep 8 (22:09 UTC) notes: mixer x8 DECIDED into v3 on the PC rows (the daily 1 Sweep 8a (22:2x UTC) note: `docs/analysis/proving-methods.md` (branch proving-methods e7e0db7, 411 lines) carries its own per-tier table for today, route A, route D and route 3, and a public paragraph; it is the third draft of the proving public line (with proving-v1 440fd59 and ca2-coord 1c8439f), noted under D2 and D8; the route choice is D10. | C29 | The litepaper's verifier line now reads "Measured 2.1 ms on one loaded Apple M5 Max core for class v3" and the status says "4.8x inside the gate"; the measurement (ca2-mixer 54bbfcc) is 2.79 ms per warp for x8 on the loaded core (2.1 was the RATIO to v2), worst cold unit 2.94 ms, quiet-core about 1.3 ms by scaling | site/litepaper.html on ca2-coord 8e65696; status 22:20 | the public page; every node operator who reads the gate margin | A ratio printed as milliseconds understates the verifier cost by a third and overstates the gate margin (10 / 2.79 = 3.6x, 3.4x on the worst cold unit, not 4.8x). The number is the one a reviewer will re-run first | The line reads "about 2.8 ms per warp on a loaded M5 Max core (about 1.3 ms quiet, approximate), 2.1x the v2 verifier; worst cold unit 2.9 ms; the 10 ms gate leaves 3.4x" until the quiet-core run lands | coordinator ada8afb62d752b1e2 | sent | -| C30 | The 5090 mines in the app at 115.4 MH/s (the power sweep, 22:09 to 22:15Z, hash from the app's API) against 136 to 137 MH/s at device time in every bench row tonight, with the cap not binding (draw 316 W under a 431 W cap, SM at 3,051 MHz) | bench-log e304458; read-width and mixer PC rows | every 5090 owner on the app (and every big card: the gap is the app's job loop, not the kernel) | About 15% of a 5090's hash is lost between the kernel and the app, and the sweep entry explains it away as API sampling. M11 measured the 9070 XT at the app's 2^21 job size equal to its 2^24 rate, but no 5090 row exists at 2^21; the 5090 finishes a 2^21 job in about 15 ms, so per-job launch, read-back and template work can cost that much. The STATUS line prints "wall" and "inside jobs" rates and would show it | One measurement on PC 1: `igneum-worker-cuda --bench` on the live pack at --batch-log2 21 and 24 on the 5090, and the 5090's STATUS wall-against-inside gap over 10 minutes; if the job size is the cause, the app's job size for cards over 100 MH/s rises (2^22 or 2^23) in the next cut: a 15% gain for every 5090 owner | repro-bench agent a0b9f574775ef1693 (its PC 1 slot); coordinator | sent | +| C30 | The 5090 mines in the app at 115.4 MH/s (the power sweep, 22:09 to 22:15Z, hash from the app's API) against 136 to 137 MH/s at device time in every bench row tonight, with the cap not binding (draw 316 W under a 431 W cap, SM at 3,051 MHz) | bench-log e304458; read-width and mixer PC rows | every 5090 owner on the app (and every big card: the gap is the app's job loop, not the kernel) | About 15% of a 5090's hash is lost between the kernel and the app, and the sweep entry explains it away as API sampling. M11 measured the 9070 XT at the app's 2^21 job size equal to its 2^24 rate, but no 5090 row exists at 2^21; the 5090 finishes a 2^21 job in about 15 ms, so per-job launch, read-back and template work can cost that much. The STATUS line prints "wall" and "inside jobs" rates and would show it | One measurement on PC 1: `igneum-worker-cuda --bench` on the live pack at --batch-log2 21 and 24 on the 5090, and the 5090's STATUS wall-against-inside gap over 10 minutes; if the job size is the cause, the app's job size for cards over 100 MH/s rises (2^22 or 2^23) in the next cut: a 15% gain for every 5090 owner | repro-bench agent a0b9f574775ef1693 (its PC 1 slot); coordinator | taken (repro-bench agent: --bench at 2^21 and 2^24 on the 5090 on the genesis pack and the live pack in its PC 1 slot, then PC 2; the STATUS wall-against-inside gap from the 10 minutes before and after its window; the consequence written either way) | + +Sweep 9 (22:2x UTC) notes: the devnet at 22:24:31Z reads DAA 136,578, 0.909 blocks/s measured over the stats window and 1.005 DAA/s averaged since the fee-switch plan's 15:40Z read (112,227), so H = 210,000 lands between about 18:50 and 19:35 UTC on 6 October, up to an hour EARLIER than the 19:50Z written in fee-switch-devnet.md, the rollout plan 7a and release-0.3.10.md; the 16:00Z check (D1) keeps about 2.8 hours of margin and stands; the plans' ETA is re-cut in D1 and sent to the coordinator. Gates tonight: G3, G4, G6 green on the final class (x8 + era); G4b added (the Mac app passes --prepare-packs only to non-Metal workers, so every Mac would stop at the first v3 epoch: found by the node agent, the fix with a unit test and a real Metal gate run before the ship); G1 and G2 on the PC 1 job since 22:16. diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index 2e102837..a6a6e5a6 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -6,7 +6,7 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | # | By when | One line | What the project lead does | |---|---|---|---| -| D1 | 16:00 UTC, 6 October | The fee switch at H = 210,000 (about 19:50 UTC) darkens every 0.3.10 prover; 0.3.11 must be on every prover first, else H moves | Nothing if the morning check passes; the coordinator holds it. Know it exists | +| D1 | 16:00 UTC, 6 October | The fee switch at H = 210,000 (about 18:50 to 19:35 UTC by the devnet's rate at 22:24Z, earlier than the plans' 19:50) darkens every 0.3.10 prover; 0.3.11 must be on every prover first, else H moves | Nothing if the morning check passes; the coordinator holds it. Know it exists | | D9 | this week | No 12 GB card exists here; every 12 GB number is scaled from the 5090 | Confirm whether the 3060 and 5060 Ti 16 GB that evidence.md says are on order are real; if not, buy one 12 GB card (about £250 to £400) | | D2 | before the next site push | The litepaper's "12 GB proves" is false on this build; three drafts of the replacement exist (proving-v1 440fd59, ca2-coord 1c8439f, proving-methods.md section 5) | Pick one; the proving-methods paragraph is recommended | | D8 | the same push | "The card mines and proves" and "a visible 1% software fee" are solo-NVIDIA sentences now | Approve the qualified wording | From ffdf28fc473a50ef7f58e81aefd622b9687ca93b Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:27:37 +0000 Subject: [PATCH 120/158] Consequences ledger: C5 the 0.3.10 restart aborted 0 shards (PC 2's prover was dark on the socket across it) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 9ebe85e0..2a70f3b7 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -12,7 +12,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C2 | 16,751 MiB GPU peak during the chain of 8 with the miner resident (`chain-pc2-pv1c`) | bench-log "proving v1", step 2 chain row | 16 GB cards (RTX 5080, 5060 Ti 16 GB, 4060 Ti 16 GB) | The plan's "a 16 GB card sits 0.4 GB under tonight's peak" is the empty-shard row (15,590 MiB). The chained aggregation adds 1.2 GB and lands at 16.4 GB, over a 16 GB card. So a 16 GB card cannot mine and aggregate on this build; it can at most mine and prove shards, with 0.4 GB spare and no full shard measured | The 16 GB chained-aggregation row goes into the sweep; the aggregator step in `prover.rs aggregate_once` gates on card memory (24 GB with the miner running, else pause the miner on that card for the aggregation); the Proving tile says which role the card runs | proving v1 | closed as measured (proving agent, spcurve-miner-pc2-pv1 219517f: the adopted shard beside the miner 22,210 MiB and 13.2 s, so a 24 GB card has 2.3 GB spare from the fee switch, the number approximate for the card itself because it is the 5090's allocation pattern; the prototype shard 30.1 GB, the 32 GB card alone; aggregation_card gates on the same memory rule, 23,552 MB either role). Open: the first real 24 GB card measurement, and the public line says "24 GB" from a 32 GB card's pattern (D2 wording) | | C3 | 13,816 MiB GPU peak, prover alone, empty shards (`prover-cost-pc2-pv1`) | bench-log "proving v1", step 1 | 12 GB cards (RTX 3060 12 GB, 4070, 5070), the default-on rule (`provedefault.rs` `MIN_VRAM_MB` 11,776) | On the only measurement the prover by itself exceeds a 12 GB card by 1.5 GB, so the 12 GB default gate switches proving on for cards that cannot run it on this build unless the SP1 knobs bring the peak down. The litepaper's "12 GB or more proves full shards" (`site/litepaper.html` 560) and evidence row 16 rest on the sweep | 0.3.11 does not ship the default-on until the sweep has a row under 11.5 GB for a full v1 shard; if none, the gate moves to 24 GB and the public claim is qualified (D2) | proving v1 (sweep in work); public claim: decision | closed as measured (proving agent: the sweep moves no floor, 13.9 GB for an empty shard, 28.3 GB for a full prototype shard; the default is 20 GB mining / 16 GB prove-only; the 12 GB sentence is false on this build and goes to the project lead as D2 with the curve); the v1-shard row is C15 | | C4 | Host RAM 25,550 MB used of 63,132 on PC 2 with the prover on; the WSL2 VM working set 7,915 MB | bench-log "proving v1", step 1 host RAM row | Windows home miners with 16 GB RAM (the common gaming PC); Macs with 16 GB switching the CPU prover on | The prover default has no RAM floor. On Windows the WSL2 VM alone holds 7.9 GB beside the app, the node and the game-class desktop; a 16 GB machine with proving on by default swaps or kills the node. The app's own RSS (engine, node, verifier) is not separated in the measurement, so no requirement can be stated yet | The sweep job records the app's and the node's working sets beside the VM's; `provedefault` reads total RAM and stays off under 32 GB on Windows until measured; the Proving tile and the miner page state the RAM requirement | proving v1; miner UI adbf58b058186a18b (the tile line) | closed in code (proving v1 c2544be: MIN_RAM_MB_WINDOWS 31,000 in provedefault.rs, the tile line names the 7.9 GB VM on a 63 GB PC; unknown RAM is not a gate; the miner UI help line next cut) | -| C5 | The app quit for the 0.3.10 update at 20:01:09Z and aborted the chain job at block 3 ("aborted (the app is quitting)"); `prover.rs` kills the child on quit | bench-log "proving v1" chain run 1; `app/igneum-app/src/prover.rs` 266 to 272 | every prover on every update; with proving v1 the aggregator | Tonight's `update-now` to every app aborts whichever shard each prover has in flight (up to 37 s of work each, no payout, re-assigned to nobody until the window passes). Under proving v1 a restart mid-segment loses the aggregator's chain state: the segment goes unproven after T (600 DAA), the aggregator share of 8 blocks is forfeited to the escrow, and the next record must be fresh-chain. With one aggregator on the devnet every app update costs 10 minutes of unproven segments | The update's safe moment waits for the prover's current proof (as it does for the node); the aggregator persists the last segment proof and resumes the chain after a restart; the Updates section says "waits for the proof in flight". For 0.3.10 tonight the aborted shards are an accepted cost, recorded | proving v1; shipper (tonight's rollout note); miner UI (Updates wording) | refused for tonight by the proving agent (persisting the segment proof and holding the update for a proof in flight are 0.3.12; the cost is in the plan as the rule working as written); the shipper carries the aborted-shard count in release-0.3.10.md section 8; the 20:01:09Z abort was NOT 0.3.10 (shipper: nothing published), see C16 | +| C5 | The app quit for the 0.3.10 update at 20:01:09Z and aborted the chain job at block 3 ("aborted (the app is quitting)"); `prover.rs` kills the child on quit | bench-log "proving v1" chain run 1; `app/igneum-app/src/prover.rs` 266 to 272 | every prover on every update; with proving v1 the aggregator | Tonight's `update-now` to every app aborts whichever shard each prover has in flight (up to 37 s of work each, no payout, re-assigned to nobody until the window passes). Under proving v1 a restart mid-segment loses the aggregator's chain state: the segment goes unproven after T (600 DAA), the aggregator share of 8 blocks is forfeited to the escrow, and the next record must be fresh-chain. With one aggregator on the devnet every app update costs 10 minutes of unproven segments | The update's safe moment waits for the prover's current proof (as it does for the node); the aggregator persists the last segment proof and resumes the chain after a restart; the Updates section says "waits for the proof in flight". For 0.3.10 tonight the aborted shards are an accepted cost, recorded | proving v1; shipper (tonight's rollout note); miner UI (Updates wording) | refused for tonight by the proving agent (persisting the segment proof and holding the update for a proof in flight are 0.3.12; the cost is in the plan as the rule working as written); the shipper carries the aborted-shard count in release-0.3.10.md section 8; the 20:01:09Z abort was NOT 0.3.10 (shipper: nothing published), see C16. The count, read from the intake at 22:3xZ: PC 2 quit for the 0.3.10 restart at 21:49:29Z (run win-1ccfe586-20261005-200114) with no proof in flight, because its prover had been dark on the root socket since 21:25Z (last exporter line 21:44:02Z, C17); the Mac proves nothing by default; so the restart aborted 0 shards tonight and the first instance of this class will be the 0.3.11 rollout | | C6 | The prover costs a mining 5090 4.0% (124.7 to 119.7 MH/s); the software dev fee is 1 template in 100 | bench-log "proving v1" step 1; `packaging/hive/README.md` "The dev fee" | pool users; the pool's ledger | A member who proves sends 4% fewer shares, so the pool's vardiff and `stats.hashrate` read a 4% loss while the proving income (90% of the shard credit plus a tenth to an aggregator) is paid to the member's own key and never appears in the pool's ledger: the pool dashboard understates a proving member's earnings. The software dev fee has no mechanism in pool mode (the pool issues the templates), so a pooled miner pays no dev fee today and the pool design must say whether it takes one (1 share in 100 to the dev address) or none | The pool-v0 design states both: the member `stats` carry a `proving` flag and the pool page shows proving income beside shares; the dev fee rule in pool mode is written down before the first pool ships | pool a4781ba117326091f | closed on pool-v0 (pool agent: the member stats line carries a proving flag, /api/miners/
and the pool page show it with the 4% note and that proving income never passes through the pool; the software dev fee is NONE in pool mode, the pool's own fee (default 1%) is the only fee, carried in the welcome message's share_scheme, shown on the Connect card, written in docs/plans/pool.md and packaging/hive/README.md). Merge note: packaging/hive/README.md is now edited on three branches (hive-words, ota-k2, pool-v0). Public wording: the miner page's "a visible 1% software fee you can switch off" is a solo-mining sentence once a pool exists, added to D8 | | C7 | The HiveOS package holds `igneumd`, `igneum-miner` and the two workers; no `igneum-prove-host`, no SP1 GPU server, no per-card rule | `packaging/hive/make-hive-package.sh` 26 to 30; README "What the hooks do" | rigs (HiveOS, Linux), the largest hashrate tier | A rig cannot prove at all, so the 20% proving share is reachable only from the app. The miner page says "the card mines and proves" (`site/miner.html` 7, `site/index.html` 484) and the HiveOS README does not say rigs mine only. A rig that could prove needs the 251 MB SP1 GPU server, CUDA 12.8 and the per-card profile of C2 and C3, and a rig's RAM (4 to 8 GB on most Hive images, approximate) is below C4's floor | The rig installer either carries the prover with the per-card rule and a RAM check, or its README and the miners page say rigs mine only and provers are app machines; `IDENTITIES=8 for a big card, 2 for a small one` gets a threshold in GB | rig installer a3e7b2b03222f5cff | closed (rig installer 88f31d9: gates 23,552 MB for both roles from provedefault.rs 440fd59, the README table per tier; HiveOS hive-words 2d056e8: the same table; open only the miners page sentence, D8) | | C8 | Dataset 2 GiB at genesis plus 0.5 GiB a year; the working-set rule "under 6 GB on an 8 GB card"; the cache 256 MiB doubling at years 4 and 12; scratch up to 128 KiB per resident warp | spec 01 section 1.13.3; coordinator's budget rule; layer 6 option C; `docs/plans/hot-table.md` section 3 | 4 GB and 8 GB cards; the litepaper's claim | `site/index.html` 461 says "Any 4 GB card" and the litepaper (560) says a 4 GB card mines for about four years and an 8 GB card for more than a decade. At 75% of the card the 4 GB card's dataset room is about 2.4 GiB: under one year. The 8 GB card's room is about 5 GiB after cache, hot table, scratch and buffers: about six years, five and a half with the year-4 cache step. Neither public sentence holds under the schedule | A card-lifetime table per tier (sub-agent, `docs/analysis/card-lifetime-2026-10-05.md`); the public wording is a claim for the project lead (D3) | sub-agent (table); decision (wording) | table landed (sub-agent, docs/analysis/card-lifetime-2026-10-05.md, 1fecfe2, merged into ca2-coord at 22:50): 4 GB 1.0 to 1.5 years under (a) and year 4 under (b); 8 GB 6.3 to 7.5 or 12; 12 GB 12 to 13.5 or 12 to 28 depending on whether the cache stays resident; Apple 8 GB 2.6 to 3.4 or 4. The coordinator decided the cache is freed after the daily build and recommends (b) to the project lead; the public sentences hold only under (b): D3 and D4 revised | From a7870be336a0a9f1ca71fa8777080a07aae17242 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:28:15 +0000 Subject: [PATCH 121/158] Consequences ledger: C29 withdrawn in part (2.1 ms is the fixed crate's measurement), C19 superseded, D11 applied pending escrow Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 4 ++-- docs/plans/consequences-decisions.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 2a70f3b7..3594d1d0 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -31,7 +31,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | # | Number | Source | Tier affected | Consequence | Action | Owner | State | |---|---|---|---|---|---|---|---| | C18 | 5090 0.072 against 9070 XT 0.032 MH/s per pound at list prices (2.2x), 4.9x per watt | read-width.md 4.1 | AMD home miners; the public level 3 copy | The coordinator's 22:30 status entry says "near parity per pound"; the table it cites says 2.2x. The level 3 page is written from the status | The status and level 3 carry the table's figure (2.2x per pound, 4.9x per watt, 7.5x in rate); "near parity" is struck | coordinator | closed (coordinator 6b07776: the status, the rollout plan and the public copy carry 2.2x per pound, 4.9x per watt, 7.5x in rate) | -| C19 | Mixer x4 into class v3: CPU verify 1.6 to 4.8 ms per warp against 0.604 today; pruning depth 108,000 DAA s (spec 02) | status 22:00; M16 table; spec 02 line 17 | every node (the three testnet seeds on small Hetzner VMs, the observer, a laptop node); IBD; pools | A new node verifies every header in the pruning window on one core: 108,000 x 4.8 ms = 8.6 min at x4 against 1.1 min today (a 30-s block-time budget at 1 block/s is unaffected: 4.8 ms per block is 0.5% of a core). Every miner's own `cpu re-check` of a found hash and every pool share verification cost the same 8x; the 10 ms gate (ledger M9) keeps 2x of margin at the slow end, which is what Counter ASIC 3.0 has left to spend. On the 2019-class laptop core the evidence table names (rule 3) the figure is unmeasured and may pass 10 ms | The ca2-mixer document carries: ms per warp on the M5 Max core AND a scaled 2019-class figure (marked approximate), the pruning-window IBD minutes per tier, pool shares per core per second, and the gate margin left for 3.0; the seeds' header-verify load is checked in the testnet go checklist | ca2-mixer af345b1e2c541ffbb; coordinator | closed as measured (ca2-mixer 54bbfcc, mixer-x4.md 6.5): one M5 Max core under a load of 5.6, ms per warp v2 1.33, x4 1.94 (1.45x), x8 2.79 (2.1x), worst cold 1.58 / 2.04 / 2.94; quiet-core scaled 0.60 / 0.88 / 1.26 (approximate); 2019-class laptop 1.5 / 2.2 / 3.2 (approximate, unmeasured); shares per core per second quiet 1,660 / 1,140 / 790 (spec 09's 2,270 re-cut to 1,660), a 22,000-member pool needs 1.3 / 1.9 / 2.8 quiet cores; IBD over 108,000 headers quiet 1.1 / 1.6 / 2.3 min (laptop 2.7 / 4.0 / 5.8); gate margin for 3.0 on the loaded core 8.4 / 8.0 / 7.1 ms. The mixer multiplies the ALU part only, so x8 is 2.1x the verifier. Owed before the level 3 page quotes an absolute: one quiet-core run (the ratios are the measurement tonight) | +| C19 | Mixer x4 into class v3: CPU verify 1.6 to 4.8 ms per warp against 0.604 today; pruning depth 108,000 DAA s (spec 02) | status 22:00; M16 table; spec 02 line 17 | every node (the three testnet seeds on small Hetzner VMs, the observer, a laptop node); IBD; pools | A new node verifies every header in the pruning window on one core: 108,000 x 4.8 ms = 8.6 min at x4 against 1.1 min today (a 30-s block-time budget at 1 block/s is unaffected: 4.8 ms per block is 0.5% of a core). Every miner's own `cpu re-check` of a found hash and every pool share verification cost the same 8x; the 10 ms gate (ledger M9) keeps 2x of margin at the slow end, which is what Counter ASIC 3.0 has left to spend. On the 2019-class laptop core the evidence table names (rule 3) the figure is unmeasured and may pass 10 ms | The ca2-mixer document carries: ms per warp on the M5 Max core AND a scaled 2019-class figure (marked approximate), the pruning-window IBD minutes per tier, pool shares per core per second, and the gate margin left for 3.0; the seeds' header-verify load is checked in the testnet go checklist | ca2-mixer af345b1e2c541ffbb; coordinator | closed as measured (ca2-mixer 54bbfcc, mixer-x4.md 6.5): one M5 Max core under a load of 5.6, ms per warp v2 1.33, x4 1.94 (1.45x), x8 2.79 (2.1x), worst cold 1.58 / 2.04 / 2.94; quiet-core scaled 0.60 / 0.88 / 1.26 (approximate); 2019-class laptop 1.5 / 2.2 / 3.2 (approximate, unmeasured); shares per core per second quiet 1,660 / 1,140 / 790 (spec 09's 2,270 re-cut to 1,660), a 22,000-member pool needs 1.3 / 1.9 / 2.8 quiet cores; IBD over 108,000 headers quiet 1.1 / 1.6 / 2.3 min (laptop 2.7 / 4.0 / 5.8); gate margin for 3.0 on the loaded core 8.4 / 8.0 / 7.1 ms. The mixer multiplies the ALU part only, so x8 is 2.1x the verifier. Owed before the level 3 page quotes an absolute: one quiet-core run (the ratios are the measurement tonight). Superseded at 22:07 by the fixed crate: v3 (x8) 2.1 ms per warp near-quiet, 3.4x v2 (see C29) | | C20 | Layer 9: the epoch length as an era parameter, 600 to 7,200 DAA s (10 min to 2 h), base 3,600 | status 22:30 and 23:00 | rigs (HiveOS and the rig installer), Macs, pools, the seed path | Both rig miners run `--exit-on-seed-change` and re-export the pack on exit 42 (h-run.sh 56 to 61, igneum-miner.sh 67 to 78): at a 10-minute epoch every card's miner restarts six times an hour with a pack export each time, and the restart gap is lost hashing; the app's prepare-ahead path does not restart. The Mac fleet's prepare pause (35 s an hour at one epoch an hour, bench-log M11) becomes 3.5 min an hour, 6%. The 10-minute seed VDF (spec 04) equals the shortest epoch, so the seed for epoch n+1 is known only as epoch n starts, which is the compile-ahead window the agent must measure per card (the 5090 compiled in 1,285 ms, the 9070 XT unmeasured). A pool's `job` cadence and the dev-fee counter are unaffected | The epoch-length document carries a per-tier row: rig restart cost per epoch length (and the fix: prepare-ahead in the rig scripts, no exit 42 path), the Mac pause share, the compile-ahead margin per card at 600 DAA s against the VDF; the rig installer removes `--exit-on-seed-change` in favour of the prepare path before layer 9 can draw a short epoch | ca2-epoch a32a3ece66c02417a; rig installer | closed with a correction (ca2-epoch 4300608, epoch-length.md sections 6.3, 7, 9): the rig scripts pass --prepare-packs as well, so a worker with prepare support swaps in place and exit 42 is the fallback on a prepare MISS (the loaded iGPU missed 2 of 10, M11), not a restart every epoch; the risk at 600 s is one restart plus export plus inline compile per missed boundary; the Mac race pause is 6.3% of a 600-s epoch (race default off); the program is known a full epoch ahead at every length (lead and T_epoch fixed, option A); the 9070 XT compile is OWED (no prepared line from gfx1201 in any upload); the rig installer (88f31d9) confirms the rig already takes the prepare path: exit 42 fires only when a worker's ready line lacks "prepare 1", and both shipped workers answer it; documented in its README, no code change | | C21 | OTA K2: apps embed `OTA_PUBLIC_KEYS = [K1, K2]` and honour a signed `revoked_keys` list; the rig installer verifies the manifest with ONE key (`OTA_PUBLIC_KEY_HEX`, install-rig.sh 168, igneum-update.sh 2) and knows no revocation; the HiveOS package verifies nothing (no manifest, the override reaches it only by republish) | ota-k2 c722579; packaging/linux; packaging/hive | rigs (both packages), the seeds (if they take the manifest) | The day K1 is lost or revoked and the manifest is signed with K2, every rig on the installer refuses the manifest, stops taking overrides, and is isolated at the next height switch; a leaked K1 keeps signing for rigs, because they carry no revocation list. HiveOS rigs get neither keys nor revocation: a republished package is their only path, and nothing checks who published it | The rig installer carries both public keys and the `revoked_keys` rule in the same form as the app (keys.md section 4, step 3), installed and read from the manifest; the HiveOS README states that the package is unsigned and names the sha256 the Flight Sheet URL should be checked against; keys.md lists the rig and HiveOS paths in its table of what trusts K1 | OTA key af2bb75a5436324d0 (keys.md, the shared verifier form); rig installer a3e7b2b03222f5cff | closed for the rig and the docs (rig installer 88f31d9: OTA_PUBLIC_KEYS [K1, K2 slot] embedded, manifest_check mirrors the app's manifest::check with the revoked_keys record at /var/lib/igneum/updates/revoked.json, tested on three throwaway keys; OTA agent 00fcbb5: keys.md table of every path that trusts K1, the HiveOS README unsigned-archive note); open: the wallet (wallet-v1) still trusts K1 alone, listed in keys.md for its owner; merge note: packaging/hive/README.md is edited on both ota-k2 and hive-words | @@ -61,7 +61,7 @@ Sweep 8 (22:09 UTC) notes: mixer x8 DECIDED into v3 on the PC rows (the daily 1 | C28 | One sp1-gpu-server per card on rigs pins about 6 GB of host RAM per server today (under 2 GB with route A's buffers, approximate) | `docs/analysis/proving-methods.md` section 5, rig row (proving-methods e7e0db7) | rigs with several 24 GB or 32 GB cards on the rig installer | A six-card rig that proves on every card pins about 36 GB of host RAM under the shipped server; the rig installer's preflight says 16 GB to prove (a warning, per card not per rig) and its prover unit runs one server, so the moment it moves to one server per card (the analysis's route C) the RAM check is wrong by the card count | The rig preflight scales its RAM warning by the number of proving cards (6 GB each today, the route A figure when measured) and the README's per-card table gains a host RAM column; the one-server-per-card unit lands only with that check | rig installer a3e7b2b03222f5cff | closed (rig installer 086008a: the preflight checks host RAM against 8 GB plus about 6 GB per proving card at the 23,552 MB gate, PROVER_RAM_GB_PER_CARD default 6 marked approximate, the README host RAM row per tier; the one-server-per-card unit lands only with that check) | Sweep 8a (22:2x UTC) note: `docs/analysis/proving-methods.md` (branch proving-methods e7e0db7, 411 lines) carries its own per-tier table for today, route A, route D and route 3, and a public paragraph; it is the third draft of the proving public line (with proving-v1 440fd59 and ca2-coord 1c8439f), noted under D2 and D8; the route choice is D10. -| C29 | The litepaper's verifier line now reads "Measured 2.1 ms on one loaded Apple M5 Max core for class v3" and the status says "4.8x inside the gate"; the measurement (ca2-mixer 54bbfcc) is 2.79 ms per warp for x8 on the loaded core (2.1 was the RATIO to v2), worst cold unit 2.94 ms, quiet-core about 1.3 ms by scaling | site/litepaper.html on ca2-coord 8e65696; status 22:20 | the public page; every node operator who reads the gate margin | A ratio printed as milliseconds understates the verifier cost by a third and overstates the gate margin (10 / 2.79 = 3.6x, 3.4x on the worst cold unit, not 4.8x). The number is the one a reviewer will re-run first | The line reads "about 2.8 ms per warp on a loaded M5 Max core (about 1.3 ms quiet, approximate), 2.1x the v2 verifier; worst cold unit 2.9 ms; the 10 ms gate leaves 3.4x" until the quiet-core run lands | coordinator ada8afb62d752b1e2 | sent | +| C29 | The litepaper's verifier line now reads "Measured 2.1 ms on one loaded Apple M5 Max core for class v3" and the status says "4.8x inside the gate"; the measurement (ca2-mixer 54bbfcc) is 2.79 ms per warp for x8 on the loaded core (2.1 was the RATIO to v2), worst cold unit 2.94 ms, quiet-core about 1.3 ms by scaling | site/litepaper.html on ca2-coord 8e65696; status 22:20 | the public page; every node operator who reads the gate margin | A ratio printed as milliseconds understates the verifier cost by a third and overstates the gate margin (10 / 2.79 = 3.6x, 3.4x on the worst cold unit, not 4.8x). The number is the one a reviewer will re-run first | The line reads "about 2.8 ms per warp on a loaded M5 Max core (about 1.3 ms quiet, approximate), 2.1x the v2 verifier; worst cold unit 2.9 ms; the 10 ms gate leaves 3.4x" until the quiet-core run lands | coordinator ada8afb62d752b1e2 | closed, the reviewer's reading WITHDRAWN in part (coordinator 7e6f77c, status 22:25): the fixed crate's session at 22:07 measured 2.1 ms per warp for class v3 as a MEASUREMENT (worst cold 2.15) on a core at load 5.5, and that binary's v2 figure matched readwidth's quiet 0.61 ms within 1%, so the numbers are near-quiet and the litepaper's 2.1 ms was right; the 2.79 ms I cited was the slow binary's 21:40 session (the inlining regression, since fixed); the gate leaves 4.8x (4.6x on the worst cold unit), 3.4x the v2 verifier. The "about 1.3 ms quiet" scaling is struck everywhere. C19's quiet-core figures are superseded by this session | | C30 | The 5090 mines in the app at 115.4 MH/s (the power sweep, 22:09 to 22:15Z, hash from the app's API) against 136 to 137 MH/s at device time in every bench row tonight, with the cap not binding (draw 316 W under a 431 W cap, SM at 3,051 MHz) | bench-log e304458; read-width and mixer PC rows | every 5090 owner on the app (and every big card: the gap is the app's job loop, not the kernel) | About 15% of a 5090's hash is lost between the kernel and the app, and the sweep entry explains it away as API sampling. M11 measured the 9070 XT at the app's 2^21 job size equal to its 2^24 rate, but no 5090 row exists at 2^21; the 5090 finishes a 2^21 job in about 15 ms, so per-job launch, read-back and template work can cost that much. The STATUS line prints "wall" and "inside jobs" rates and would show it | One measurement on PC 1: `igneum-worker-cuda --bench` on the live pack at --batch-log2 21 and 24 on the 5090, and the 5090's STATUS wall-against-inside gap over 10 minutes; if the job size is the cause, the app's job size for cards over 100 MH/s rises (2^22 or 2^23) in the next cut: a 15% gain for every 5090 owner | repro-bench agent a0b9f574775ef1693 (its PC 1 slot); coordinator | taken (repro-bench agent: --bench at 2^21 and 2^24 on the 5090 on the genesis pack and the live pack in its PC 1 slot, then PC 2; the STATUS wall-against-inside gap from the 10 minutes before and after its window; the consequence written either way) | Sweep 9 (22:2x UTC) notes: the devnet at 22:24:31Z reads DAA 136,578, 0.909 blocks/s measured over the stats window and 1.005 DAA/s averaged since the fee-switch plan's 15:40Z read (112,227), so H = 210,000 lands between about 18:50 and 19:35 UTC on 6 October, up to an hour EARLIER than the 19:50Z written in fee-switch-devnet.md, the rollout plan 7a and release-0.3.10.md; the 16:00Z check (D1) keeps about 2.8 hours of margin and stands; the plans' ETA is re-cut in D1 and sent to the coordinator. Gates tonight: G3, G4, G6 green on the final class (x8 + era); G4b added (the Mac app passes --prepare-packs only to non-Metal workers, so every Mac would stop at the first v3 epoch: found by the node agent, the fix with a unit test and a real Metal gate run before the ship); G1 and G2 on the PC 1 job since 22:16. diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index a6a6e5a6..d274ffb2 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -29,4 +29,4 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | D8 | C7 | The miner page says "One click: install, start, the card mines and proves" (site/miner.html 7, 13, 21; site/index.html 484). On HiveOS and the rig installer a rig mines only until a Linux prover build is published, and on the app a 12 GB card mines only, a 16 GB card proves with the miner paused, 20 GB and up does both (the sweep of 5 October, before the v1-shard row) | Qualify the sentence on the miner page and the homepage card: "the card mines; 24 GB cards prove too, 32 GB does both at once; rigs mine until the Linux prover ships". The same page's "a visible 1% software fee you can switch off" becomes "solo mining carries a 1% software fee you can switch off; in a pool the pool's own fee is the only one" once pool-v0 ships (the pool agent fixed the rule: no software dev fee in pool mode) | The sentence is the product's first promise and tonight's measurement bounds it by card | | D9 | C3, C15, C26 | No 12 GB NVIDIA card exists in the fleet, so every 12 GB number tonight is scaled from the 5090 and labelled approximate. The 13.9 GB floor is SP1's GPU server code (`docs/analysis/proving-methods.md`, branch proving-methods e7e0db7, section 1.3: builder.rs adds 4 GB to the card's physical memory and panics under 24, so a 16 GB card (16 + 4 = 20) and a 12 GB card never start and 20 GB is the smallest that does; the trace is allocated at the maximum shard; the CUDA mempool never releases; the proving plan's "under 24" (4c82e56) is the same test read before the addition). The prover-floor patch and the per-card profiles cannot be measured without the hardware | Buy one 12 GB NVIDIA card this week for PC 1's spare slot (an RTX 3060 12 GB or 4070 12 GB, about £250 to £400, approximate; the coordinator's request). Check first whether the RTX 3060 and the RTX 5060 Ti 16 GB that evidence.md row 16 says are "on order" are real and arriving; if so, no purchase, only the delivery date. No public line says "12 GB proves" before a real 12 GB card runs the rebuilt server on the S_p-curve fixtures and recipe | Money, and the one measurement every 12 GB claim rests on; the prover-floor agent's rows tonight replace the approximate figures when they land | | D10 | C3, C15, C26, C28 | The proving route for the 12 GB tier and for Apple: `docs/analysis/proving-methods.md` section 4 ranks (1) route A, a re-sized SP1 GPU server with `S_p` as the dial and one server per card on rigs, nothing in consensus moving; (2) route D, RISC Zero 3.0.x as proof-system version 2 (the only shipped prover with a documented sub-12 GB configuration and a Metal path), the fallback if A misses 11 GB and the Apple route either way, 3 to 4 days plus a 3-month two-verifier overlap and three spec 7.8 rules; (3) a sumcheck family without a codeword (Jolt-class) in years, not now. Route A is already running tonight; route D adds a second proof family to the node, which is a consensus and verifier change outside tonight's delegation | Route A on the prover-floor rows, gated as the document says (the adopted shard under 11 GB alone and under 60 s prove-only on a real 12 GB card, D9); route D's measurement (RISC Zero at po2 19 and 20 on PC 2 and the Mac's Metal row) may run as a measurement, but adopting a second proof family waits for the project lead and for route A's result; the public paragraph of section 5 ("Proving runs on NVIDIA cards with 24 GB or more today. A build for 12 GB and 16 GB cards is being measured ...") is the honest line meanwhile and is the one of the three drafts to prefer, because it names what is being measured instead of a tier | A second verifier in the node is the kind of change the testnet's genesis must carry from day one; measuring it costs nothing, adopting it is the project lead's | -| D11 | C29 context; `docs/plans/funding.md` 36 and 63 | The chip bounty on the public pages: "the model and the bounty are public" (hero, abstract, ca2-coord cabec3b), "bounty standing" (level 1 copy); funding.md: USD 50,000 standing, "Not funded", "a bounty is announced only when it is escrowed"; the litepaper already says "a bounty is attached" to the finality review (line 686) | Strike "and the bounty" from the hero and the abstract and "standing" from level 1 until the money is escrowed, and say "a bounty follows the external review" if a sentence is wanted; or escrow USD 50,000 (and the USD 25,000 finality bounty) and keep the words. The coordinator is told to hold the words at the ship pending this | A public promise of money the project has not set aside is the FUD the ledger exists to prevent, by the project's own funding rule | +| D11 | C29 context; `docs/plans/funding.md` 36 and 63 | The chip bounty on the public pages: "the model and the bounty are public" (hero, abstract, ca2-coord cabec3b), "bounty standing" (level 1 copy); funding.md: USD 50,000 standing, "Not funded", "a bounty is announced only when it is escrowed"; the litepaper already says "a bounty is attached" to the finality review (line 686) | Strike "and the bounty" from the hero and the abstract and "standing" from level 1 until the money is escrowed, and say "a bounty follows the external review" if a sentence is wanted; or escrow USD 50,000 (and the USD 25,000 finality bounty) and keep the words. Applied at 22:25 (ca2-coord 7e6f77c): "and the bounty" struck from the hero and the abstract, "standing" from level 1, the copy says "a bounty follows the external review"; the words return only once escrowed | A public promise of money the project has not set aside is the FUD the ledger exists to prevent, by the project's own funding rule | From 20ed778e5420a716bf6ee7f92884458f64153f38 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:28:36 +0000 Subject: [PATCH 122/158] Consequences ledger: the branch's merge note for the integrator Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 3594d1d0..5e4aff28 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -65,3 +65,5 @@ Sweep 8a (22:2x UTC) note: `docs/analysis/proving-methods.md` (branch proving-me | C30 | The 5090 mines in the app at 115.4 MH/s (the power sweep, 22:09 to 22:15Z, hash from the app's API) against 136 to 137 MH/s at device time in every bench row tonight, with the cap not binding (draw 316 W under a 431 W cap, SM at 3,051 MHz) | bench-log e304458; read-width and mixer PC rows | every 5090 owner on the app (and every big card: the gap is the app's job loop, not the kernel) | About 15% of a 5090's hash is lost between the kernel and the app, and the sweep entry explains it away as API sampling. M11 measured the 9070 XT at the app's 2^21 job size equal to its 2^24 rate, but no 5090 row exists at 2^21; the 5090 finishes a 2^21 job in about 15 ms, so per-job launch, read-back and template work can cost that much. The STATUS line prints "wall" and "inside jobs" rates and would show it | One measurement on PC 1: `igneum-worker-cuda --bench` on the live pack at --batch-log2 21 and 24 on the 5090, and the 5090's STATUS wall-against-inside gap over 10 minutes; if the job size is the cause, the app's job size for cards over 100 MH/s rises (2^22 or 2^23) in the next cut: a 15% gain for every 5090 owner | repro-bench agent a0b9f574775ef1693 (its PC 1 slot); coordinator | taken (repro-bench agent: --bench at 2^21 and 2^24 on the 5090 on the genesis pack and the live pack in its PC 1 slot, then PC 2; the STATUS wall-against-inside gap from the 10 minutes before and after its window; the consequence written either way) | Sweep 9 (22:2x UTC) notes: the devnet at 22:24:31Z reads DAA 136,578, 0.909 blocks/s measured over the stats window and 1.005 DAA/s averaged since the fee-switch plan's 15:40Z read (112,227), so H = 210,000 lands between about 18:50 and 19:35 UTC on 6 October, up to an hour EARLIER than the 19:50Z written in fee-switch-devnet.md, the rollout plan 7a and release-0.3.10.md; the 16:00Z check (D1) keeps about 2.8 hours of margin and stands; the plans' ETA is re-cut in D1 and sent to the coordinator. Gates tonight: G3, G4, G6 green on the final class (x8 + era); G4b added (the Mac app passes --prepare-packs only to non-Metal workers, so every Mac would stop at the first v3 epoch: found by the node agent, the fix with a unit test and a real Metal gate run before the ship); G1 and G2 on the PC 1 job since 22:16. + +Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/consequences-2026-10-05.md and consequences-decisions.md only (base ca8d9f3); a merge-tree against master cde561c and after shows 0 conflicts. From a9455c14227d8034d4e66a91da09dd69389d6666 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:30:17 +0000 Subject: [PATCH 123/158] Consequences ledger: C31 three faults in the copy the ship takes (12 GB beside 24 GB, the stale evidence row 16, mapping (b) stated as fact before D4) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 5e4aff28..a26323c0 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -67,3 +67,4 @@ Sweep 8a (22:2x UTC) note: `docs/analysis/proving-methods.md` (branch proving-me Sweep 9 (22:2x UTC) notes: the devnet at 22:24:31Z reads DAA 136,578, 0.909 blocks/s measured over the stats window and 1.005 DAA/s averaged since the fee-switch plan's 15:40Z read (112,227), so H = 210,000 lands between about 18:50 and 19:35 UTC on 6 October, up to an hour EARLIER than the 19:50Z written in fee-switch-devnet.md, the rollout plan 7a and release-0.3.10.md; the 16:00Z check (D1) keeps about 2.8 hours of margin and stands; the plans' ETA is re-cut in D1 and sent to the coordinator. Gates tonight: G3, G4, G6 green on the final class (x8 + era); G4b added (the Mac app passes --prepare-packs only to non-Metal workers, so every Mac would stop at the first v3 epoch: found by the node agent, the fix with a unit test and a real Metal gate run before the ship); G1 and G2 on the PC 1 job since 22:16. Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/consequences-2026-10-05.md and consequences-decisions.md only (base ca8d9f3); a merge-tree against master cde561c and after shows 0 conflicts. +| C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | sent | From 0d4680c6b007e223e9ae662227789e0ddf4a2f22 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:30:45 +0000 Subject: [PATCH 124/158] Consequences ledger: C31 addendum, the step schedule as fact in five places against miner.html's old sentence Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index a26323c0..f6e2d33d 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -67,4 +67,4 @@ Sweep 8a (22:2x UTC) note: `docs/analysis/proving-methods.md` (branch proving-me Sweep 9 (22:2x UTC) notes: the devnet at 22:24:31Z reads DAA 136,578, 0.909 blocks/s measured over the stats window and 1.005 DAA/s averaged since the fee-switch plan's 15:40Z read (112,227), so H = 210,000 lands between about 18:50 and 19:35 UTC on 6 October, up to an hour EARLIER than the 19:50Z written in fee-switch-devnet.md, the rollout plan 7a and release-0.3.10.md; the 16:00Z check (D1) keeps about 2.8 hours of margin and stands; the plans' ETA is re-cut in D1 and sent to the coordinator. Gates tonight: G3, G4, G6 green on the final class (x8 + era); G4b added (the Mac app passes --prepare-packs only to non-Metal workers, so every Mac would stop at the first v3 epoch: found by the node agent, the fix with a unit test and a real Metal gate run before the ship); G1 and G2 on the PC 1 job since 22:16. Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/consequences-2026-10-05.md and consequences-decisions.md only (base ca8d9f3); a merge-tree against master cde561c and after shows 0 conflicts. -| C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | sent | +| C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | sent. Addendum 22:4x: the step schedule is stated as a genesis fact in five places on ca2-coord (index.html 443 "doubling at years 4, 12 and 28", 461 "Any 4 GB card at launch, 8 GB from year 4"; litepaper.html 436 and 562) while miner.html 472 still says "Any 4 GB card. The dataset starts at 2 GB and grows by half a gigabyte a year": the site contradicts itself on the schedule, and four of the five pre-empt D4 | From e296194215f73e213e490575dd793aa63b285275 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:30:57 +0000 Subject: [PATCH 125/158] Consequences ledger: C31 closed (ca2-coord a7be43f) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index f6e2d33d..02301f81 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -67,4 +67,4 @@ Sweep 8a (22:2x UTC) note: `docs/analysis/proving-methods.md` (branch proving-me Sweep 9 (22:2x UTC) notes: the devnet at 22:24:31Z reads DAA 136,578, 0.909 blocks/s measured over the stats window and 1.005 DAA/s averaged since the fee-switch plan's 15:40Z read (112,227), so H = 210,000 lands between about 18:50 and 19:35 UTC on 6 October, up to an hour EARLIER than the 19:50Z written in fee-switch-devnet.md, the rollout plan 7a and release-0.3.10.md; the 16:00Z check (D1) keeps about 2.8 hours of margin and stands; the plans' ETA is re-cut in D1 and sent to the coordinator. Gates tonight: G3, G4, G6 green on the final class (x8 + era); G4b added (the Mac app passes --prepare-packs only to non-Metal workers, so every Mac would stop at the first v3 epoch: found by the node agent, the fix with a unit test and a real Metal gate run before the ship); G1 and G2 on the PC 1 job since 22:16. Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/consequences-2026-10-05.md and consequences-decisions.md only (base ca8d9f3); a merge-tree against master cde561c and after shows 0 conflicts. -| C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | sent. Addendum 22:4x: the step schedule is stated as a genesis fact in five places on ca2-coord (index.html 443 "doubling at years 4, 12 and 28", 461 "Any 4 GB card at launch, 8 GB from year 4"; litepaper.html 436 and 562) while miner.html 472 still says "Any 4 GB card. The dataset starts at 2 GB and grows by half a gigabyte a year": the site contradicts itself on the schedule, and four of the five pre-empt D4 | +| C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | closed on ca2-coord a7be43f (the 12 GB clause struck; the merge rule "take proving-v1's row 16 and its proving sentences" in the rollout plan; the schedule reads as the gate 1 proposal on the litepaper and the homepage; miner.html 472 checked separately) | From b3a0cac16c8b1f5824c0111bf4d2a35a5506b191 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:31:25 +0000 Subject: [PATCH 126/158] Consequences ledger: C17 closed in part, C9 to decision D4, C1's hour re-cut in the number cell Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 02301f81..8b16b56a 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -8,7 +8,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | # | Number | Source | Tier affected | Consequence | Action | Owner | State | |---|---|---|---|---|---|---|---| -| C1 | Fee switch H = 210,000, reached about 19:50Z on 6 October (0.965 blocks/s) | `docs/plans/fee-switch-devnet.md` sections 3 and 7; `vendor/igneum-node-0310/igneum/exec/src/rpc.rs` 849 (no `daaScore`, no `feesV1ActivationDaa` in `igneum_exportSegments`); the app's exporter call without `--fees-v1-activation-daa` (`app/igneum-app/src/prover.rs` 516 to 519) | every prover on the devnet (PC 2, the Mac, any 0.3.10 machine) | From the first chain block at or above H the 0.3.10 node cuts 30,000-pgas shards and meters with the v1 table, but the app's exporter sees a dump without the switch and cuts at 7.5 M with the prototype table: the host refuses the fixture (wrong `S_p`) or the node vetoes the statement. Proving on the devnet goes dark at H and nothing is paid until every prover runs a node whose export carries the switch (the proving-v1 fork does, `vendor/igneum-node-pv1` rpc.rs 961 and 982). `pc2-chain.ps1` fixtures from the live node fail the same way after H | Either 0.3.11 (proving-v1 fork) on every prover before H, or the switch republished at a later H before 19:50Z tomorrow (a digest flip, every node). Recommendation in `consequences-decisions.md` D1 | proving v1 acd4f36bc2c07a4e2, shipper ae892a8b0f78fe31c, coordinator ada8afb62d752b1e2 | in work (proving agent: fork eb32c645 on 21d4c73c carries daaScore and feesV1ActivationDaa, the exporter needs no flag; 0.3.11 on every prover before 16:00Z on 6 October or H moves to tip + 86,400; the line is in the proving plan, the rollout plan and release-0.3.10.md) | +| C1 | Fee switch H = 210,000, reached about 19:00Z on 6 October (18:50 to 19:35 by the live rate at 22:24Z; the plans first said 19:50 from 0.965 blocks/s) | `docs/plans/fee-switch-devnet.md` sections 3 and 7; `vendor/igneum-node-0310/igneum/exec/src/rpc.rs` 849 (no `daaScore`, no `feesV1ActivationDaa` in `igneum_exportSegments`); the app's exporter call without `--fees-v1-activation-daa` (`app/igneum-app/src/prover.rs` 516 to 519) | every prover on the devnet (PC 2, the Mac, any 0.3.10 machine) | From the first chain block at or above H the 0.3.10 node cuts 30,000-pgas shards and meters with the v1 table, but the app's exporter sees a dump without the switch and cuts at 7.5 M with the prototype table: the host refuses the fixture (wrong `S_p`) or the node vetoes the statement. Proving on the devnet goes dark at H and nothing is paid until every prover runs a node whose export carries the switch (the proving-v1 fork does, `vendor/igneum-node-pv1` rpc.rs 961 and 982). `pc2-chain.ps1` fixtures from the live node fail the same way after H | Either 0.3.11 (proving-v1 fork) on every prover before H, or the switch republished at a later H before 19:50Z tomorrow (a digest flip, every node). Recommendation in `consequences-decisions.md` D1 | proving v1 acd4f36bc2c07a4e2, shipper ae892a8b0f78fe31c, coordinator ada8afb62d752b1e2 | in work (proving agent: fork eb32c645 on 21d4c73c carries daaScore and feesV1ActivationDaa, the exporter needs no flag; 0.3.11 on every prover before 16:00Z on 6 October or H moves to tip + 86,400; the line is in the proving plan, the rollout plan and release-0.3.10.md) | | C2 | 16,751 MiB GPU peak during the chain of 8 with the miner resident (`chain-pc2-pv1c`) | bench-log "proving v1", step 2 chain row | 16 GB cards (RTX 5080, 5060 Ti 16 GB, 4060 Ti 16 GB) | The plan's "a 16 GB card sits 0.4 GB under tonight's peak" is the empty-shard row (15,590 MiB). The chained aggregation adds 1.2 GB and lands at 16.4 GB, over a 16 GB card. So a 16 GB card cannot mine and aggregate on this build; it can at most mine and prove shards, with 0.4 GB spare and no full shard measured | The 16 GB chained-aggregation row goes into the sweep; the aggregator step in `prover.rs aggregate_once` gates on card memory (24 GB with the miner running, else pause the miner on that card for the aggregation); the Proving tile says which role the card runs | proving v1 | closed as measured (proving agent, spcurve-miner-pc2-pv1 219517f: the adopted shard beside the miner 22,210 MiB and 13.2 s, so a 24 GB card has 2.3 GB spare from the fee switch, the number approximate for the card itself because it is the 5090's allocation pattern; the prototype shard 30.1 GB, the 32 GB card alone; aggregation_card gates on the same memory rule, 23,552 MB either role). Open: the first real 24 GB card measurement, and the public line says "24 GB" from a 32 GB card's pattern (D2 wording) | | C3 | 13,816 MiB GPU peak, prover alone, empty shards (`prover-cost-pc2-pv1`) | bench-log "proving v1", step 1 | 12 GB cards (RTX 3060 12 GB, 4070, 5070), the default-on rule (`provedefault.rs` `MIN_VRAM_MB` 11,776) | On the only measurement the prover by itself exceeds a 12 GB card by 1.5 GB, so the 12 GB default gate switches proving on for cards that cannot run it on this build unless the SP1 knobs bring the peak down. The litepaper's "12 GB or more proves full shards" (`site/litepaper.html` 560) and evidence row 16 rest on the sweep | 0.3.11 does not ship the default-on until the sweep has a row under 11.5 GB for a full v1 shard; if none, the gate moves to 24 GB and the public claim is qualified (D2) | proving v1 (sweep in work); public claim: decision | closed as measured (proving agent: the sweep moves no floor, 13.9 GB for an empty shard, 28.3 GB for a full prototype shard; the default is 20 GB mining / 16 GB prove-only; the 12 GB sentence is false on this build and goes to the project lead as D2 with the curve); the v1-shard row is C15 | | C4 | Host RAM 25,550 MB used of 63,132 on PC 2 with the prover on; the WSL2 VM working set 7,915 MB | bench-log "proving v1", step 1 host RAM row | Windows home miners with 16 GB RAM (the common gaming PC); Macs with 16 GB switching the CPU prover on | The prover default has no RAM floor. On Windows the WSL2 VM alone holds 7.9 GB beside the app, the node and the game-class desktop; a 16 GB machine with proving on by default swaps or kills the node. The app's own RSS (engine, node, verifier) is not separated in the measurement, so no requirement can be stated yet | The sweep job records the app's and the node's working sets beside the VM's; `provedefault` reads total RAM and stays off under 32 GB on Windows until measured; the Proving tile and the miner page state the RAM requirement | proving v1; miner UI adbf58b058186a18b (the tile line) | closed in code (proving v1 c2544be: MIN_RAM_MB_WINDOWS 31,000 in provedefault.rs, the tile line names the 7.9 GB VM on a 63 GB PC; unknown RAM is not a gate; the miner UI help line next cut) | @@ -16,7 +16,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C6 | The prover costs a mining 5090 4.0% (124.7 to 119.7 MH/s); the software dev fee is 1 template in 100 | bench-log "proving v1" step 1; `packaging/hive/README.md` "The dev fee" | pool users; the pool's ledger | A member who proves sends 4% fewer shares, so the pool's vardiff and `stats.hashrate` read a 4% loss while the proving income (90% of the shard credit plus a tenth to an aggregator) is paid to the member's own key and never appears in the pool's ledger: the pool dashboard understates a proving member's earnings. The software dev fee has no mechanism in pool mode (the pool issues the templates), so a pooled miner pays no dev fee today and the pool design must say whether it takes one (1 share in 100 to the dev address) or none | The pool-v0 design states both: the member `stats` carry a `proving` flag and the pool page shows proving income beside shares; the dev fee rule in pool mode is written down before the first pool ships | pool a4781ba117326091f | closed on pool-v0 (pool agent: the member stats line carries a proving flag, /api/miners/
and the pool page show it with the 4% note and that proving income never passes through the pool; the software dev fee is NONE in pool mode, the pool's own fee (default 1%) is the only fee, carried in the welcome message's share_scheme, shown on the Connect card, written in docs/plans/pool.md and packaging/hive/README.md). Merge note: packaging/hive/README.md is now edited on three branches (hive-words, ota-k2, pool-v0). Public wording: the miner page's "a visible 1% software fee you can switch off" is a solo-mining sentence once a pool exists, added to D8 | | C7 | The HiveOS package holds `igneumd`, `igneum-miner` and the two workers; no `igneum-prove-host`, no SP1 GPU server, no per-card rule | `packaging/hive/make-hive-package.sh` 26 to 30; README "What the hooks do" | rigs (HiveOS, Linux), the largest hashrate tier | A rig cannot prove at all, so the 20% proving share is reachable only from the app. The miner page says "the card mines and proves" (`site/miner.html` 7, `site/index.html` 484) and the HiveOS README does not say rigs mine only. A rig that could prove needs the 251 MB SP1 GPU server, CUDA 12.8 and the per-card profile of C2 and C3, and a rig's RAM (4 to 8 GB on most Hive images, approximate) is below C4's floor | The rig installer either carries the prover with the per-card rule and a RAM check, or its README and the miners page say rigs mine only and provers are app machines; `IDENTITIES=8 for a big card, 2 for a small one` gets a threshold in GB | rig installer a3e7b2b03222f5cff | closed (rig installer 88f31d9: gates 23,552 MB for both roles from provedefault.rs 440fd59, the README table per tier; HiveOS hive-words 2d056e8: the same table; open only the miners page sentence, D8) | | C8 | Dataset 2 GiB at genesis plus 0.5 GiB a year; the working-set rule "under 6 GB on an 8 GB card"; the cache 256 MiB doubling at years 4 and 12; scratch up to 128 KiB per resident warp | spec 01 section 1.13.3; coordinator's budget rule; layer 6 option C; `docs/plans/hot-table.md` section 3 | 4 GB and 8 GB cards; the litepaper's claim | `site/index.html` 461 says "Any 4 GB card" and the litepaper (560) says a 4 GB card mines for about four years and an 8 GB card for more than a decade. At 75% of the card the 4 GB card's dataset room is about 2.4 GiB: under one year. The 8 GB card's room is about 5 GiB after cache, hot table, scratch and buffers: about six years, five and a half with the year-4 cache step. Neither public sentence holds under the schedule | A card-lifetime table per tier (sub-agent, `docs/analysis/card-lifetime-2026-10-05.md`); the public wording is a claim for the project lead (D3) | sub-agent (table); decision (wording) | table landed (sub-agent, docs/analysis/card-lifetime-2026-10-05.md, 1fecfe2, merged into ca2-coord at 22:50): 4 GB 1.0 to 1.5 years under (a) and year 4 under (b); 8 GB 6.3 to 7.5 or 12; 12 GB 12 to 13.5 or 12 to 28 depending on whether the cache stays resident; Apple 8 GB 2.6 to 3.4 or 4. The coordinator decided the cache is freed after the daily build and recommends (b) to the project lead; the public sentences hold only under (b): D3 and D4 revised | -| C9 | Index mapping option (a) multiply-shift (fades cards) against (b) power-of-two steps 2, 4, 8 GiB | spec 01 section 1.13.3, gate 1 | 8 GB and 12 GB cards | Option (b)'s 8 GiB step (about year 12) ends 8 GB and 12 GB cards on the same day; option (a) fades them one year at a time. The choice is a genesis parameter and a tier consequence nobody has put beside the options | Decision request D4 with the lifetime table of C8 | decision | open | +| C9 | Index mapping option (a) multiply-shift (fades cards) against (b) power-of-two steps 2, 4, 8 GiB | spec 01 section 1.13.3, gate 1 | 8 GB and 12 GB cards | Option (b)'s 8 GiB step (about year 12) ends 8 GB and 12 GB cards on the same day; option (a) fades them one year at a time. The choice is a genesis parameter and a tier consequence nobody has put beside the options | Decision request D4 with the lifetime table of C8 | decision | decision (D4, with the card-lifetime table; the public copy now reads the step schedule as the gate 1 proposal, C31) | | C10 | The on-die-cache recompute chip's gain is 2.4x at every scratch share under the 6 GB cap; the mixer multiplier x2 brings it to 1.2x, x4 to 0.6x, inside the 10 ms verify gate | `docs/analysis/scratch-soundness.md` finding 2 and section 10; M16 analysis table | chip builders; the site's "under 2x" claim; pool share verification | Layer 3 does not deliver the headline and the rollout plan's own rule (6a) says the public claim is qualified when no share gets the chip under 2x. The lever that does is M16's mixer multiplier, which doubles the CPU verify per warp (0.441 ms to about 0.9 ms at x2): a pool core verifies about 11,000 members' shares instead of 22,000, and node block verification doubles. The corrected SRAM cost ($19 to $37 of silicon per mirror die) means the mirror never stops a funded chip; the cache schedule keeps it above GPU L2 only | The coordinator either carries the mixer x2 into class v3 for the devnet (it is a lottery-hash change like the others; the verify cost per warp is measured with it) or marks the site's "under 2x" as qualified in the public copy level 3 and D5 asks the project lead which | coordinator ada8afb62d752b1e2 | closed as a consequence (coordinator: the public claim was qualified at 21:50; at 22:00 the M16 mixer x4 was decided into class v3 behind the same activation; the pool-core and node verification consequences are C14) | | C11 | RX 9070 XT 17.73 MH/s at 198.9 W (0.089 MH/W) against the RTX 5090 122.30 MH/s at 307.6 W (0.398 MH/W); every read width costs the 9070 XT the same 2.4 G line fetches a second | bench-log AMD telemetry entry; readwidth probe ceilings (status 20:35) | AMD home miners; the "three vendors" copy | Under the "widest latency-bound read" rule w16 moves bytes per hash, not loads per hash, so the AMD card keeps about a seventh of the 5090's hash rate and pays 4.5x the electricity per hash; at a UK tariff of 25 p/kWh (approximate) the 9070 XT spends 4.5x the 5090's pence per IGN. The readwidth decision table carries MH/s only; no MH/W or MH per pound per card per class, and the public copy implies vendor parity | The readwidth table adds MH/W (and MH per pound at list prices, approximate) per card per class; the v3 decision names the AMD consequence; whether the class should favour fewer loads per hash for AMD's 64-byte lines is a consensus choice for the project lead (D6) | read-width a451c9935bfb1bc19; coordinator; decision | closed (read-width e752fc7 section 4.1: MH/W and MH per pound per class per card; the AMD gap is the card's random-access rate, no width closes it; the coordinator's 22:30 entry says "near parity per pound" where the table says the 5090 is 2.2x per pound: C18) | | C12 | The v3 working set on Apple: 1,568 to 1,760 MiB today, 2,592 to 2,784 MiB at the 2 GiB genesis dataset, in unified memory shared with macOS; 2,048 launched warps x 128 KiB scratch | `docs/plans/hot-table.md` section 3 M5 Max row; era-layout section 3 | Apple silicon with 8 GB and 16 GB (the base Mac mini, MacBook Air) | An 8 GB Mac holds the miner's 2.6 GB beside macOS's 3 to 4 GB: it mines today and swaps at the first dataset growth step. The app has no gate on Metal's `recommendedMaxWorkingSetSize`; the site's "Any 4 GB card" has no Apple line. The CPU prover's RAM on a 16 GB Mac is unmeasured (the Settings switch lets it on) | The app reads `recommendedMaxWorkingSetSize` and refuses to mine (with the reason on the Mine tile) when the working set exceeds it; the site says "Mac: 8 GB or more"; the Apple scratch row at 128 KiB is measured in the readwidth table, not launched at 2,048 by assumption | miner UI adbf58b058186a18b; read-width (the Apple row) | in work (read-width 30ff674: the Apple scratch footprint by arithmetic is 1.4 GiB at 2,048 x 32 KiB and 1.6 GiB at 2,048 x 128 KiB, 2.4 GiB at 4,096 x 128 KiB; the Metal harness now prints currentAllocatedSize and recommendedMaxWorkingSetSize in its RESULT line, the measured row waits for the Mac measure lock, held by a prover measurement since 20:31Z; the miner UI gates on the arithmetic plus its output buffer until then, mining.gate_reason next cut) | @@ -24,7 +24,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | C14 | The M16 mixer x4 decided into class v3 at 22:00 (coordinator): verifier 1.6 to 4.8 ms per warp against 0.441 ms today (0.87 cold), measurement running on branch ca2-mixer | coordinator's reply 21:5x; `docs/analysis/m16-recompute-attacker-2026-10-05.md` table; spec 09 section 9.8 item 5 | pool operators; every node (the Hetzner seeds, the observer, a 2019-class laptop); the 10 ms verify gate | At x4 one pool core verifies 200 to 600 shares a second instead of 2,270, so one core covers 2,000 to 6,000 members at one share per 10 s instead of 22,000; a block's CPU re-check and the miner's own `cpu re-check` of every found hash cost 4 to 11x; the seeds' small VMs verify every header at that cost; the gate's margin falls from 23x to 2 to 6x, which bounds Counter ASIC 3.0's room | The coordinator is adding the numbers to the ca2-mixer document and the status (said in reply); the reviewer checks at the next sweep that the pool-members-per-core and the seed-VM header-verify rows are there, and that the spec 09 figure 2,270 shares a second per core is re-cut with v3 | coordinator ada8afb62d752b1e2 | closed with C19 (the same measurement: x4 is 1.45x and x8 2.1x the verifier, not 4 to 11x; a pool core verifies 1,140 shares a second at x4 and 790 at x8 quiet) | | C15 | A full prototype shard peaks at 28.3 GB on sp1-gpu-server 6.8.1 (the sweep, proving agent 22:0x); an empty one 13.9 GB; no knob moves either floor | proving agent's reply; sweep job `memsweep-pc2-pv1` | 24 GB cards (4090, 7900-class if it had a path); the 5090 that mines and proves; the fleet table | A 24 GB card cannot prove a prototype full shard at all, mining or not; a 5090 mining (3.4 GB resident) plus a full shard is 31.7 GB against 31.8 GB, the edge. The 20 GB / 16 GB default rests on the empty-shard number. From H tomorrow the fleet proves v1 shards of 30,000 pgas (about 7 M cycles, a ninth of the prototype shard) whose peak is unmeasured; `proving/fixtures/fees-v1-shards2` and `-shards3` are that shape. The fleet table's "proving-only" rows are 32 GB-card rows until then; the litepaper's "12 GB" (D2) and the evidence row 16 fall with it | The sweep's last row is a v1-budget shard with and without the miner, and the provedefault gates are set from it before 0.3.11 ships default-on; the fleet table labels its rows by the card that fits | proving v1 | closed as measured (proving agent, the S_p curve, app default 440fd59): 32 GB mines and proves today (28.3 GB alone, 30.0 beside the miner); 24 GB proves the adopted 30,000-pgas shard (20.4 GB alone, about 22 GB beside the miner) from DAA 210,000 and nothing before it; 16 GB proves only empty shards alone (13.9 GB), nothing beside the miner (15.7 GB); 12 GB proves nothing on SP1 6.8.1; the default is on at 24 GB or more. Relayed to the rig installer and the HiveOS words sub-agent for their tables; until H tomorrow every prover on the devnet is a 32 GB card | | C16 | PC 2's app quit at 20:01:09Z ("aborted (the app is quitting)"), logged by the proving agent as "the app quit for the 0.3.10 update"; the shipper says nothing of 0.3.10 was published and no update-now of its exists (the live manifest is 0.3.9 from 17:59:14Z) | bench-log "proving v1" chain run 1; the shipper's reply 22:0x | every measurement on PC 2 that straddles 20:01Z (the prover-cost phase B ended 19:51Z, the chain re-run began 20:05Z, the readwidth 5090 job queued) | An app that quits for an unknown reason voids any number taken across it (CLAUDE.md: a number taken while another build or simulation ran is not a number; the same for a restart). The bench-log line names a cause that did not happen | The proving agent reads PC 2's app log for the quit reason at 20:01Z and corrects the bench-log line; if the cause is another agent's job or the auto-update, that agent's measurements across it are marked | proving v1 (the log read); the agent the cause names | closed in part (proving agent: PC 2 logged "quit: stopping the miners, then the node" at 20:01:09Z, a plain quit command 20 s after the efficiency sweep's administrator prompt was cancelled at the keyboard and 13 s after the live prover failed on a root-owned /tmp/sp1-cuda-0.sock left by the chain job; the bench-log line corrected; the quit's origin is not in the log, see C17) | -| C17 | The chain job ran igneum-prove-host as root inside WSL2 and left a root-owned `/tmp/sp1-cuda-0.sock`; the live prover (the app's user) then failed with `CudaClientError: Connect(PermissionDenied)` at 20:00:56Z; the app quit at 20:01:09Z on a command whose source the log does not name | proving agent's reply 22:1x; PC 2's app log | every PC 2 measurement that shares the card with the live prover; every operator whose machine takes remote jobs | Two classes, not one bug. (1) Any job script that runs the SP1 server or the host as root in WSL2 breaks the live prover for every later shard until a reboot or a manual unlink; the proving agent fixed its own scripts (kill the server, remove the socket at the end), the class check (CLAUDE.md, 5 October: grep every script with the same shape, add a check that fails when the shape comes back) is not yet written. (2) A quit the log cannot attribute (job, UI, signal, update) voids the measurements around it and nobody can say who stopped a miner; the app logs "quit:" without a source | (1) `tools/ci/` gets a check that fails on any `.ps1` or `.sh` playbook that invokes `igneum-prove-host`, `sp1-gpu-server` or `wsl -u root` without the socket cleanup line, and the proving agent greps tonight's four PC 2 scripts; (2) the engine's quit log line carries its source (job id and playbook name, the UI, a signal, the updater) in the next app cut | proving v1 (1); coordinator for the next-cut list (2) | sent | +| C17 | The chain job ran igneum-prove-host as root inside WSL2 and left a root-owned `/tmp/sp1-cuda-0.sock`; the live prover (the app's user) then failed with `CudaClientError: Connect(PermissionDenied)` at 20:00:56Z; the app quit at 20:01:09Z on a command whose source the log does not name | proving agent's reply 22:1x; PC 2's app log | every PC 2 measurement that shares the card with the live prover; every operator whose machine takes remote jobs | Two classes, not one bug. (1) Any job script that runs the SP1 server or the host as root in WSL2 breaks the live prover for every later shard until a reboot or a manual unlink; the proving agent fixed its own scripts (kill the server, remove the socket at the end), the class check (CLAUDE.md, 5 October: grep every script with the same shape, add a check that fails when the shape comes back) is not yet written. (2) A quit the log cannot attribute (job, UI, signal, update) voids the measurements around it and nobody can say who stopped a miner; the app logs "quit:" without a source | (1) `tools/ci/` gets a check that fails on any `.ps1` or `.sh` playbook that invokes `igneum-prove-host`, `sp1-gpu-server` or `wsl -u root` without the socket cleanup line, and the proving agent greps tonight's four PC 2 scripts; (2) the engine's quit log line carries its source (job id and playbook name, the UI, a signal, the updater) in the next app cut | proving v1 (1); coordinator for the next-cut list (2) | closed in part (proving v1 c2544be: every pv1 playbook kills the server and unlinks the socket at start and end, tools/ci/prover-socket-check.sh in CI; the publish-time gate is C27 on bash-body-check 6805125; the unattributed quit, the engine logging its source, is on the coordinator's next-cut list) | ## Round 2 (22:30 to 23:30) From e6f81106bde2c950f7790becd56ec4a910fa1664 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:31:31 +0000 Subject: [PATCH 127/158] Consequences ledger: C31 the five places on 0d9b23d Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 8b16b56a..ee466e78 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -67,4 +67,4 @@ Sweep 8a (22:2x UTC) note: `docs/analysis/proving-methods.md` (branch proving-me Sweep 9 (22:2x UTC) notes: the devnet at 22:24:31Z reads DAA 136,578, 0.909 blocks/s measured over the stats window and 1.005 DAA/s averaged since the fee-switch plan's 15:40Z read (112,227), so H = 210,000 lands between about 18:50 and 19:35 UTC on 6 October, up to an hour EARLIER than the 19:50Z written in fee-switch-devnet.md, the rollout plan 7a and release-0.3.10.md; the 16:00Z check (D1) keeps about 2.8 hours of margin and stands; the plans' ETA is re-cut in D1 and sent to the coordinator. Gates tonight: G3, G4, G6 green on the final class (x8 + era); G4b added (the Mac app passes --prepare-packs only to non-Metal workers, so every Mac would stop at the first v3 epoch: found by the node agent, the fix with a unit test and a real Metal gate run before the ship); G1 and G2 on the PC 1 job since 22:16. Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/consequences-2026-10-05.md and consequences-decisions.md only (base ca8d9f3); a merge-tree against master cde561c and after shows 0 conflicts. -| C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | closed on ca2-coord a7be43f (the 12 GB clause struck; the merge rule "take proving-v1's row 16 and its proving sentences" in the rollout plan; the schedule reads as the gate 1 proposal on the litepaper and the homepage; miner.html 472 checked separately) | +| C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | closed on ca2-coord a7be43f and 0d9b23d (the 12 GB clause struck; the merge rule "take proving-v1's row 16 and its proving sentences" in the rollout plan; one wording in all five places, "the proposed schedule, fixed at the testnet genesis: 2 GB, doubling at years 4, 12 and 28", the lifetime sentences kept as consequences of the proposal and marked approximate) | From c6c78657d673d1dc81c1613a6eb14f65ecc0aadc Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:32:16 +0000 Subject: [PATCH 128/158] Consequences ledger: C32 the app update wipes fetched job kits (a class), the prover-floor build against the 0.3.11 update-now Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index ee466e78..13831ca2 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -68,3 +68,4 @@ Sweep 9 (22:2x UTC) notes: the devnet at 22:24:31Z reads DAA 136,578, 0.909 bloc Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/consequences-2026-10-05.md and consequences-decisions.md only (base ca8d9f3); a merge-tree against master cde561c and after shows 0 conflicts. | C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | closed on ca2-coord a7be43f and 0d9b23d (the 12 GB clause struck; the merge rule "take proving-v1's row 16 and its proving sentences" in the rollout plan; one wording in all five places, "the proposed schedule, fixed at the testnet genesis: 2 GB, doubling at years 4, 12 and 28", the lifetime sentences kept as consequences of the proposal and marked approximate) | +| C32 | The 0.3.10 install at 21:49Z cleared PC 1's app jobs folder and with it the AMD kit fetched at 21:23:59Z; the amd-card-test playbook now says its fetch must be republished after any app update | bench-log 39f02ff; status 21:05 ("the jobs folder is cleared by fetch jobs" was the earlier, wrong reading) | every PC job tonight and tomorrow; the 0.3.11 rollout | A class, not one playbook: every fetch-then-run pair (era, hot table, mixer, repro, Ember, the AMD sweep, the prover-floor build) loses its kit when an update lands between the fetch and the run, and the run fails in seconds or, worse, runs against a stale copy. The 0.3.11 update-now reaches PC 2 while the prover-floor agent's 60 to 90 minute server build runs there (go at 22:17, to about 23:50): if that build's working directory is under the app's jobs folder, the update wipes it mid-build and the 12 GB rows slip past the morning | (1) The 0.3.11 update-now is sequenced after the prover-floor build closes, or the build's directory is confirmed outside the jobs folder before the ship; (2) every run playbook begins with a presence check of its kit and fails with "kit missing: republish the fetch after the app update" (the class check: the bash-body sub-agent's CI check gains a rule that a run job naming a kit path tests it first, or the coordinator's queue re-fetches after every update as a rule) | coordinator ada8afb62d752b1e2 (the queue and the ship order) | sent | From 0bb6e84af88627633fe9298fe53be30b9d172c1a Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:32:42 +0000 Subject: [PATCH 129/158] Consequences ledger: C32 taken (the update-now sequenced after the prover-floor build; the re-fetch rule) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 13831ca2..302bafba 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -68,4 +68,4 @@ Sweep 9 (22:2x UTC) notes: the devnet at 22:24:31Z reads DAA 136,578, 0.909 bloc Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/consequences-2026-10-05.md and consequences-decisions.md only (base ca8d9f3); a merge-tree against master cde561c and after shows 0 conflicts. | C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | closed on ca2-coord a7be43f and 0d9b23d (the 12 GB clause struck; the merge rule "take proving-v1's row 16 and its proving sentences" in the rollout plan; one wording in all five places, "the proposed schedule, fixed at the testnet genesis: 2 GB, doubling at years 4, 12 and 28", the lifetime sentences kept as consequences of the proposal and marked approximate) | -| C32 | The 0.3.10 install at 21:49Z cleared PC 1's app jobs folder and with it the AMD kit fetched at 21:23:59Z; the amd-card-test playbook now says its fetch must be republished after any app update | bench-log 39f02ff; status 21:05 ("the jobs folder is cleared by fetch jobs" was the earlier, wrong reading) | every PC job tonight and tomorrow; the 0.3.11 rollout | A class, not one playbook: every fetch-then-run pair (era, hot table, mixer, repro, Ember, the AMD sweep, the prover-floor build) loses its kit when an update lands between the fetch and the run, and the run fails in seconds or, worse, runs against a stale copy. The 0.3.11 update-now reaches PC 2 while the prover-floor agent's 60 to 90 minute server build runs there (go at 22:17, to about 23:50): if that build's working directory is under the app's jobs folder, the update wipes it mid-build and the 12 GB rows slip past the morning | (1) The 0.3.11 update-now is sequenced after the prover-floor build closes, or the build's directory is confirmed outside the jobs folder before the ship; (2) every run playbook begins with a presence check of its kit and fails with "kit missing: republish the fetch after the app update" (the class check: the bash-body sub-agent's CI check gains a rule that a run job naming a kit path tests it first, or the coordinator's queue re-fetches after every update as a rule) | coordinator ada8afb62d752b1e2 (the queue and the ship order) | sent | +| C32 | The 0.3.10 install at 21:49Z cleared PC 1's app jobs folder and with it the AMD kit fetched at 21:23:59Z; the amd-card-test playbook now says its fetch must be republished after any app update | bench-log 39f02ff; status 21:05 ("the jobs folder is cleared by fetch jobs" was the earlier, wrong reading) | every PC job tonight and tomorrow; the 0.3.11 rollout | A class, not one playbook: every fetch-then-run pair (era, hot table, mixer, repro, Ember, the AMD sweep, the prover-floor build) loses its kit when an update lands between the fetch and the run, and the run fails in seconds or, worse, runs against a stale copy. The 0.3.11 update-now reaches PC 2 while the prover-floor agent's 60 to 90 minute server build runs there (go at 22:17, to about 23:50): if that build's working directory is under the app's jobs folder, the update wipes it mid-build and the 12 GB rows slip past the morning | (1) The 0.3.11 update-now is sequenced after the prover-floor build closes, or the build's directory is confirmed outside the jobs folder before the ship; (2) every run playbook begins with a presence check of its kit and fails with "kit missing: republish the fetch after the app update" (the class check: the bash-body sub-agent's CI check gains a rule that a run job naming a kit path tests it first, or the coordinator's queue re-fetches after every update as a rule) | coordinator ada8afb62d752b1e2 (the queue and the ship order) | taken (coordinator: the prover-floor build lives under /opt/igneum-floor in WSL2, outside the jobs folder, but it is the app's job process and an app restart ends it, so the 0.3.11 update-now goes to PC 2 only after floor-build-3 closes, the ship's earlier steps not waiting; the re-fetch rule and the presence-check rule are in the rollout plan beside the one-job rule, the playbook owners carry it at their next publish; the CI side is with the bash-body sub-agent as a kit-path check) | From 68af6db901f9bcb2604af76c282da0017a40c9d1 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:32:45 +0000 Subject: [PATCH 130/158] counter-asic-2-node.md: gate run 4, a real Metal miner across the v3 boundary (3 v3 prepares and prepared lines, swap with no pause, 124 blocks on v3, 0 re-check mismatches); class-v3.mjs judges the Metal checks from the first v3 prepare on Co-Authored-By: Claude Fable 5.1 --- .../class-v3-20261005-2225Z-metal-mx8.json | 474 ++++++++++++++++++ docs/plans/counter-asic-2-node.md | 16 +- infra/fast-time/class-v3.mjs | 9 +- 3 files changed, 496 insertions(+), 3 deletions(-) create mode 100644 docs/plans/counter-asic-2-gate/class-v3-20261005-2225Z-metal-mx8.json diff --git a/docs/plans/counter-asic-2-gate/class-v3-20261005-2225Z-metal-mx8.json b/docs/plans/counter-asic-2-gate/class-v3-20261005-2225Z-metal-mx8.json new file mode 100644 index 00000000..cebc94c4 --- /dev/null +++ b/docs/plans/counter-asic-2-gate/class-v3-20261005-2225Z-metal-mx8.json @@ -0,0 +1,474 @@ +{ + "pass": false, + "checks": { + "switch_line_on_every_node": true, + "switch_line_names_the_rounded_epoch": true, + "template_switched_at_the_first_v3_epoch": true, + "blocks_before_the_boundary": true, + "blocks_after_the_boundary": true, + "v2_and_v3_programs_seen": true, + "program_ids_differ_across_the_switch": true, + "miners_agree_on_every_program": true, + "zero_rejected_by_miners": true, + "zero_rejected_by_nodes": true, + "sinks_agree": true, + "block_counts_agree": true, + "metal_prepare_sent_for_v3": true, + "metal_worker_prepared_v3_pack": true, + "metal_no_need_or_mismatch": false, + "metal_accepted_blocks_after_switch": true, + "metal_cpu_recheck_clean": true, + "metal_swapped_without_pause": true + }, + "activation": 150, + "epoch_blocks": 60, + "first_v3_epoch": 3, + "boundary_daa": 180, + "secs": 480, + "threads": 1, + "node": "/Users/joshm/Projects/igneum-wt-ca2-v3/vendor/igneum-node-ca2/target-ca2/release/igneumd", + "miner": "/Users/joshm/Projects/igneum-wt-ca2-v3/vendor/igneum-node-ca2/target-ca2/release/igneum-miner", + "genesis_bits": "0x1e010000", + "template_switch": { + "epoch": 3, + "daa": 180, + "at": 264.5 + }, + "run_ended_at_s": 366.8, + "final_daa": 301, + "blocks": { + "total": 305, + "before_boundary": 182, + "after_boundary": 123, + "chain_before": 91, + "chain_after": 67 + }, + "programs": [ + { + "epoch": 0, + "class": "v2", + "program_id": "3e974094b5ce543f", + "seed": "130e3b7d568db07a", + "miners": 2, + "disagree": false, + "ready_ms": [ + 192, + 192 + ] + }, + { + "epoch": 1, + "class": "v2", + "program_id": "1d5b129e89ec2995", + "seed": "bc056261dafe0ec2", + "miners": 2, + "disagree": false, + "ready_ms": [ + 4, + 4 + ] + }, + { + "epoch": 2, + "class": "v2", + "program_id": "835e953cdc2c11be", + "seed": "1908705502833bdf", + "miners": 2, + "disagree": false, + "ready_ms": [ + 9, + 7 + ] + }, + { + "epoch": 3, + "class": "v3", + "program_id": "082b7ee882aaf418", + "seed": "4ae011065b84f679", + "miners": 2, + "disagree": false, + "ready_ms": [ + 1069, + 1085 + ] + }, + { + "epoch": 4, + "class": "v3", + "program_id": "7ef81e08e4611665", + "seed": "d5964a745dbe2ce2", + "miners": 2, + "disagree": false, + "ready_ms": [ + 6, + 6 + ] + }, + { + "epoch": 5, + "class": "v3", + "program_id": "ecee6312983c15ee", + "seed": "f9d5de9eea7330ae", + "miners": 2, + "disagree": false, + "ready_ms": [ + 5, + 5 + ] + } + ], + "accepted_per_miner": [ + 301, + 2, + 1 + ], + "rejected_by_miners": [ + 0, + 0, + 0 + ], + "rejected_by_nodes": [ + 0, + 0, + 0 + ], + "rejected_lines": [], + "sinks": [ + "66a33eca15d33ba2", + "66a33eca15d33ba2", + "66a33eca15d33ba2" + ], + "block_counts": [ + 304, + 304, + 304 + ], + "tips_per_node": [ + 2, + 2, + 2 + ], + "switch_lines": [ + "Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)", + "Program class v3 from the override file: active from epoch 3 (DAA score 150 rounded up to the epoch boundary at 180, epochs of 60 DAA)" + ], + "samples": [ + { + "t": 4.6, + "daa": 1, + "epoch": 0, + "class": 2, + "nodes": [ + "1/97755438", + "0/130e3b7d", + "0/130e3b7d" + ] + }, + { + "t": 19.7, + "daa": 3, + "epoch": 0, + "class": 2, + "nodes": [ + "3/52e2c440", + "3/52e2c440", + "3/52e2c440" + ] + }, + { + "t": 34.7, + "daa": 3, + "epoch": 0, + "class": 2, + "nodes": [ + "3/52e2c440", + "3/52e2c440", + "3/52e2c440" + ] + }, + { + "t": 49.7, + "daa": 13, + "epoch": 0, + "class": 2, + "nodes": [ + "13/fa702087", + "13/fa702087", + "13/fa702087" + ] + }, + { + "t": 64.8, + "daa": 38, + "epoch": 0, + "class": 2, + "nodes": [ + "38/2a687376", + "38/2a687376", + "38/2a687376" + ] + }, + { + "t": 79.8, + "daa": 55, + "epoch": 0, + "class": 2, + "nodes": [ + "55/2f1f0ea8", + "55/2f1f0ea8", + "55/2f1f0ea8" + ] + }, + { + "t": 94.8, + "daa": 57, + "epoch": 0, + "class": 2, + "nodes": [ + "57/e1b2abc1", + "57/e1b2abc1", + "57/e1b2abc1" + ] + }, + { + "t": 109.9, + "daa": 61, + "epoch": 1, + "class": 2, + "nodes": [ + "61/86fa7300", + "61/86fa7300", + "61/86fa7300" + ] + }, + { + "t": 124.9, + "daa": 61, + "epoch": 1, + "class": 2, + "nodes": [ + "61/86fa7300", + "61/86fa7300", + "61/86fa7300" + ] + }, + { + "t": 140, + "daa": 61, + "epoch": 1, + "class": 2, + "nodes": [ + "61/86fa7300", + "61/86fa7300", + "61/86fa7300" + ] + }, + { + "t": 155, + "daa": 73, + "epoch": 1, + "class": 2, + "nodes": [ + "73/e7f6e171", + "73/e7f6e171", + "73/e7f6e171" + ] + }, + { + "t": 170.1, + "daa": 94, + "epoch": 1, + "class": 2, + "nodes": [ + "94/0b1c93b1", + "94/0b1c93b1", + "94/0b1c93b1" + ] + }, + { + "t": 185.2, + "daa": 114, + "epoch": 1, + "class": 2, + "nodes": [ + "114/16034702", + "114/16034702", + "114/16034702" + ] + }, + { + "t": 200.2, + "daa": 117, + "epoch": 1, + "class": 2, + "nodes": [ + "117/d018a345", + "117/d018a345", + "117/d018a345" + ] + }, + { + "t": 215.4, + "daa": 120, + "epoch": 2, + "class": 2, + "nodes": [ + "120/e022d64e", + "120/e022d64e", + "120/e022d64e" + ] + }, + { + "t": 230.4, + "daa": 134, + "epoch": 2, + "class": 2, + "nodes": [ + "134/e7d07203", + "134/e7d07203", + "134/e7d07203" + ] + }, + { + "t": 245.4, + "daa": 157, + "epoch": 2, + "class": 2, + "nodes": [ + "157/d23b3a1b", + "157/d23b3a1b", + "157/d23b3a1b" + ] + }, + { + "t": 260.5, + "daa": 175, + "epoch": 2, + "class": 2, + "nodes": [ + "175/4bf5acbe", + "175/4bf5acbe", + "175/4bf5acbe" + ] + }, + { + "t": 275.5, + "daa": 198, + "epoch": 3, + "class": 3, + "nodes": [ + "198/f337dbc1", + "198/f337dbc1", + "198/f337dbc1" + ] + }, + { + "t": 290.6, + "daa": 217, + "epoch": 3, + "class": 3, + "nodes": [ + "217/dc28325d", + "217/dc28325d", + "217/dc28325d" + ] + }, + { + "t": 305.6, + "daa": 234, + "epoch": 3, + "class": 3, + "nodes": [ + "234/a0079166", + "234/a0079166", + "234/a0079166" + ] + }, + { + "t": 320.7, + "daa": 249, + "epoch": 4, + "class": 3, + "nodes": [ + "249/eb206cb2", + "249/eb206cb2", + "249/eb206cb2" + ] + }, + { + "t": 335.7, + "daa": 268, + "epoch": 4, + "class": 3, + "nodes": [ + "268/9b9866b1", + "268/9b9866b1", + "268/9b9866b1" + ] + }, + { + "t": 350.7, + "daa": 285, + "epoch": 4, + "class": 3, + "nodes": [ + "285/3263ff7a", + "285/3263ff7a", + "285/3263ff7a" + ] + }, + { + "t": 365.8, + "daa": 299, + "epoch": 4, + "class": 3, + "nodes": [ + "299/31eff7d0", + "299/31eff7d0", + "299/31eff7d0" + ] + } + ], + "metal": { + "worker": "/Users/joshm/Projects/igneum-wt-ca2-v3/proto-metal/igneum-bench-ca2", + "prepares": [ + "PREPARE sent for epoch seed b7eda892cff02b39c043f5fa683a7c1629c96d02fa480522603be595372e371f day 1243916 class v2 (6 DAA blocks before the boundary at 60; CPU side ready in 960 ms)", + "PREPARE sent for epoch seed bc056261dafe0ec266f2e8f508e8fe7349e4d0c064853c6fd37ffa6a5e1fab3c day 1243916 class v2 (the current pair, after seed mismatches; CPU side ready in 1248 ms)", + "PREPARE sent for epoch seed 1908705502833bdf6c5d08b6d6ae3a364e3fe2bf0956504b20bc5071eecbdfc3 day 1243916 class v2 (6 DAA blocks before the boundary at 120; CPU side ready in 1768 ms)", + "PREPARE sent for epoch seed 4ae011065b84f67924d0c42ff63669e6124b6bddc4a7f71a3f1075b5891efd8c day 1243916 class v3 (6 DAA blocks before the boundary at 180; CPU side ready in 1740 ms)", + "PREPARE sent for epoch seed d5964a745dbe2ce2a88d6a4198a244996f1f4aba6cc1bc20841bb532e469fdd6 day 1243916 class v3 (6 DAA blocks before the boundary at 240; CPU side ready in 1289 ms)", + "PREPARE sent for epoch seed f9d5de9eea7330aeec83c0994b8929e61a2ac2f13dbae6462cceace3b1d6f24e day 1243916 class v3 (7 DAA blocks before the boundary at 300; CPU side ready in 1166 ms)" + ], + "prepared": [ + "worker: prepared b7eda892cff02b39c043f5fa683a7c1629c96d02fa480522603be595372e371f 69676e65756d2d6461792f0cfb120000000000 35663.4 program 64.5 dataset 0.0 race 35598.9 variant base class v2 loads/hash 128 cache-fill 2.0 build 30.5 resident 2 programs 1 datasets (prepare answered 1.3 s after it was sent)", + "worker: prepared bc056261dafe0ec266f2e8f508e8fe7349e4d0c064853c6fd37ffa6a5e1fab3c 69676e65756d2d6461792f0cfb120000000000 33632.6 program 108.1 dataset 0.0 race 33524.5 variant base class v2 loads/hash 128 cache-fill 2.0 build 30.5 resident 2 programs 1 datasets (prepare answered 35.2 s after it was sent)", + "worker: prepared 1908705502833bdf6c5d08b6d6ae3a364e3fe2bf0956504b20bc5071eecbdfc3 69676e65756d2d6461792f0cfb120000000000 34728.4 program 77.3 dataset 0.0 race 34651.0 variant base class v2 loads/hash 128 cache-fill 2.0 build 30.5 resident 2 programs 1 datasets (prepare answered 0.0 s after it was sent)", + "worker: prepared 4ae011065b84f67924d0c42ff63669e6124b6bddc4a7f71a3f1075b5891efd8c 69676e65756d2d6461792f0cfb120000000000 252.5 program 55.5 dataset 196.9 race 0.0 variant base class v3 loads/hash 128 cache-fill 0.9 build 41.1 resident 2 programs 2 datasets (prepare answered 0.3 s after it was sent)", + "worker: prepared d5964a745dbe2ce2a88d6a4198a244996f1f4aba6cc1bc20841bb532e469fdd6 69676e65756d2d6461792f0cfb120000000000 51.1 program 51.1 dataset 0.0 race 0.0 variant base class v3 loads/hash 128 cache-fill 0.9 build 41.1 resident 2 programs 2 datasets (prepare answered 0.1 s after it was sent)", + "worker: prepared f9d5de9eea7330aeec83c0994b8929e61a2ac2f13dbae6462cceace3b1d6f24e 69676e65756d2d6461792f0cfb120000000000 46.0 program 46.0 dataset 0.0 race 0.0 variant base class v3 loads/hash 128 cache-fill 0.9 build 41.1 resident 2 programs 2 datasets (prepare answered 0.0 s after it was sent)" + ], + "prepared_v3": [ + "worker: prepared 4ae011065b84f67924d0c42ff63669e6124b6bddc4a7f71a3f1075b5891efd8c 69676e65756d2d6461792f0cfb120000000000 252.5 program 55.5 dataset 196.9 race 0.0 variant base class v3 loads/hash 128 cache-fill 0.9 build 41.1 resident 2 programs 2 datasets (prepare answered 0.3 s after it was sent)", + "worker: prepared d5964a745dbe2ce2a88d6a4198a244996f1f4aba6cc1bc20841bb532e469fdd6 69676e65756d2d6461792f0cfb120000000000 51.1 program 51.1 dataset 0.0 race 0.0 variant base class v3 loads/hash 128 cache-fill 0.9 build 41.1 resident 2 programs 2 datasets (prepare answered 0.1 s after it was sent)", + "worker: prepared f9d5de9eea7330aeec83c0994b8929e61a2ac2f13dbae6462cceace3b1d6f24e 69676e65756d2d6461792f0cfb120000000000 46.0 program 46.0 dataset 0.0 race 0.0 variant base class v3 loads/hash 128 cache-fill 0.9 build 41.1 resident 2 programs 2 datasets (prepare answered 0.0 s after it was sent)" + ], + "need": 0, + "mismatch_lines": [ + "1791239226.967 PACK OUT OF DATE: the prepared pair b7eda892cff02b39 is not the node's epoch bc056261dafe0ec2; rebuilding the program pack for the worker" + ], + "refused": [], + "swaps": [ + "SEED CHANGE at daa 60: epoch seed 130e3b7d568db07aad638f67b2ac26e7cf93c51e4f564d45c6724fc4b2d3da65 -> bc056261dafe0ec266f2e8f508e8fe7349e4d0c064853c6fd37ffa6a5e1fab3c, day 1243916 -> 1243916 (epoch 1): the pair was not prepared (unexpected seeds); the worker compiles inline", + "SEED CHANGE at daa 120: epoch seed bc056261dafe0ec266f2e8f508e8fe7349e4d0c064853c6fd37ffa6a5e1fab3c -> 1908705502833bdf6c5d08b6d6ae3a364e3fe2bf0956504b20bc5071eecbdfc3, day 1243916 -> 1243916 (epoch 2): prepare was sent but the worker has not answered prepared yet; it compiles inline", + "SEED CHANGE at daa 180: epoch seed 1908705502833bdf6c5d08b6d6ae3a364e3fe2bf0956504b20bc5071eecbdfc3 -> 4ae011065b84f67924d0c42ff63669e6124b6bddc4a7f71a3f1075b5891efd8c, day 1243916 -> 1243916 (epoch 3): swapped with no pause (prepared 4 s ago, prepare took 252 ms)", + "SEED CHANGE at daa 240: epoch seed 4ae011065b84f67924d0c42ff63669e6124b6bddc4a7f71a3f1075b5891efd8c -> d5964a745dbe2ce2a88d6a4198a244996f1f4aba6cc1bc20841bb532e469fdd6, day 1243916 -> 1243916 (epoch 4): swapped with no pause (prepared 4 s ago, prepare took 51 ms)", + "SEED CHANGE at daa 300: epoch seed d5964a745dbe2ce2a88d6a4198a244996f1f4aba6cc1bc20841bb532e469fdd6 -> f9d5de9eea7330aeec83c0994b8929e61a2ac2f13dbae6462cceace3b1d6f24e, day 1243916 -> 1243916 (epoch 5): swapped with no pause (prepared 6 s ago, prepare took 46 ms)" + ], + "accepted_total": 301, + "accepted_after_switch": 124, + "found_lines": 0, + "cpu_recheck_mismatched": 0, + "last_status": "" + } +} \ No newline at end of file diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index 7586f224..445d0302 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -136,6 +136,20 @@ Binaries rebuilt on ca2-v3 d233fa1 (mixer x8, the era drawn inside the class, th Epoch 0's v2 id `8f8806638d59850f` is the same in all three runs. Three runs, three v3 classes (mixer x4; x4 with the era; x8 with the era), the same chain behaviour each time: the switch rounds up to epoch 3, no block rejected, one chain. +### Result, run 4 (5 October 2026, 22:25:22Z to 22:31:28Z): a real Metal miner across the boundary (gate G4b) + +The fleet-outage case: the three runs above used CPU miners, so the Metal worker's class v3 path had not mined. Run 4 puts node 0's miner on the Metal worker the way the app does (`igneum-miner --worker igneum-bench --prepare-packs --exit-on-seed-change`, `igneum-bench` built from ca2-v3 00c55aa: a class v3 program comes from the pack's `program_bound.metal`, its day from the pack's `memhard.metal` (the x8 construction, the era layout), keyed by (day, class, era)), genesis bits 0x1e010000 (2^24 hashes per block), CPU miners on nodes 1 and 2. Summary: `docs/plans/counter-asic-2-gate/class-v3-20261005-2225Z-metal-mx8.json`. + +| Check | Measured | +|---|---| +| The chain | 182 / 123 blocks across DAA 180, 0 rejected (miners and nodes), sinks `66a33eca15d33ba2` on all three, 304 / 304 / 304, 3 of 3 switch lines, the switch at DAA 180, 264.5 s wall | +| PREPARE lines | 6 (three class v2, three class v3), each 6 to 7 DAA before its boundary, the v3 ones with the pack directory and `class=v3 era=` | +| The worker's `prepared` for the v3 packs | three: `prepared 252.5 program 55.5 dataset 196.9 race 0.0 variant base class v3 loads/hash 128 cache-fill 0.9 build 41.1` (the first, with the day built from the pack: 0.9 ms cache fill, 41.1 ms build on the GPU), then 51.1 and 46.0 ms (the day resident) | +| The swap at the boundary | `SEED CHANGE at daa 180 ... (epoch 3): swapped with no pause (prepared 4 s ago, prepare took 252 ms)`; the same at 240 and 300 | +| Blocks on v3 | 124 accepted after the switch (301 in the run), every one re-checked on the CPU: `mismatched 0`; `need` 0; no class or era mismatch line; no refusal, no exit 42 or 44 | + +One line before the switch, on the v2 path: `PACK OUT OF DATE: the prepared pair b7eda892... is not the node's epoch bc056261...; rebuilding the program pack` at DAA 60: the epoch-1 seed reported at `boundary - lead` flipped (the quarter-lead confirm is 3 DAA on the 60x profile, 150 on the devnet) and the miner's rebuild path of 05ef0fa3 wrote the right pack. The v2 prepares answered 33 to 35 s after they were sent (the Metal variant race runs inside the prepare, longer than the 10-DAA lead of the profile; 600 s on the devnet), so epochs 1 and 2 compiled inline; a pack program races nothing and answered in 252 ms. The v3 path is clean; the script now judges the Metal checks from the first v3 prepare on (the run's own report flagged the v2 line and read FAIL on that one check). + The PC 2 suite jobs for the same fork (79bd8e10), the G6 evidence (the coordinator's status file carries the SUMMARY lines): | Job | Tree | What | Result | @@ -161,7 +175,7 @@ The PC's test stage builds the fork's test binaries with the plan's feature set, ## 7. Unverified, and what is owed - The era walk for era >= 1 has never run (the devnet is 180 days from era 1); a pruning-proof sync past era 0 fails with `MissingEraSeed` until an era witness exists in the proof format. -- The Metal worker has no class v3 generator (it regenerates from the seed in Swift): the integration branch adds generator 3 there, or the Mac mines v3 with the OpenCL worker from a pack. +- The Metal worker takes class v3 from the pack (program and day) and mined across the boundary in run 4; the app must pass `--prepare-packs` to its Metal worker (the app branch fixes `engine.rs`, which passed it to the non-Metal workers only). - The GPU workers' class refusal was checked by the C test of `pf_pack_class_ok` and the syntax of both hosts, not by a live worker on a v3 pack: the integration's bit-exact gate (G1) is where a real worker first builds a v3 pack. - `next_pair` keeps the current era seed for the next epoch; an epoch boundary that is also an era boundary (once per 180 days) would prepare the wrong era, and the job line then names the right one, so the worker refuses the prepared pair and the miner prepares again (one wasted compile, no wrong block). The VDF era seed replaces the stand-in before this matters. - Done 22:05Z: ca2-cache rebased as 1950661 fast-forwarded (the first attempt, 2de19e5 on 464d6e1, conflicted in 8 files and was aborted); igneum-pow 53 + 19 tests and the packfile test pass; the fork's kaspa-pow, miner and kaspad check clean against the merged crate (seam unchanged). `V3_CLASS = { era: None, hot: None, ..LoadClass::MX4 }`. diff --git a/infra/fast-time/class-v3.mjs b/infra/fast-time/class-v3.mjs index 3a82a3ea..b20647ae 100644 --- a/infra/fast-time/class-v3.mjs +++ b/infra/fast-time/class-v3.mjs @@ -218,7 +218,12 @@ if (METAL) { const prepared = L.filter(l => /worker: prepared /.test(l)).map(l => l.replace(/^\S+ /, '')); const preparedV3 = prepared.filter(l => / class v3 /.test(l)); const need = L.filter(l => /worker: need |^\S+ worker error:.*need /.test(l) || /\bneed [0-9a-f]{64}/.test(l)); - const mismatch = L.filter(l => /program class mismatch|era seed mismatch|PACK OUT OF DATE|pack .*: program pack/.test(l)); + // a PACK OUT OF DATE before the first class v3 prepare is the v2 rebuild path at work (a seed that flipped inside the + // quarter-lead confirm window, 3 DAA on the 60x profile); the gate judges the v3 path from its first prepare on + const firstV3Prepare = L.findIndex(l => /PREPARE sent for epoch seed .* class v3/.test(l)); + const sinceV3 = (l, i) => firstV3Prepare < 0 || i >= firstV3Prepare; + const mismatch = L.filter((l, i) => sinceV3(l, i) && /program class mismatch|era seed mismatch|PACK OUT OF DATE|pack .*: program pack/.test(l)); + const mismatchBefore = L.filter((l, i) => !sinceV3(l, i) && /program class mismatch|era seed mismatch|PACK OUT OF DATE/.test(l)); const refused = L.filter(l => /exiting with code 4[24]|refused the program pack|prepare-failed/.test(l)); const swaps = L.filter(l => /swapped with no pause|compiles inline|worker without prepare support/.test(l)).map(l => l.replace(/^\S+ /, '')); const accepted = L.filter(l => /ACCEPTED block/.test(l)); @@ -228,7 +233,7 @@ if (METAL) { const lastStatus = status.at(-1) || ''; const mismatched = +(/mismatched=(\d+)/.exec(lastStatus)?.[1] ?? 0); const rate = /hash=([\d.]+) MH\/s/.exec(lastStatus)?.[1]; - metal = { worker: METAL, prepares, prepared, prepared_v3: preparedV3, need: need.length, mismatch_lines: mismatch, refused, swaps, accepted_total: accepted.length, accepted_after_switch: acceptedAfter, found_lines: found, cpu_recheck_mismatched: mismatched, rate_mh_s: rate, last_status: lastStatus }; + metal = { worker: METAL, prepares, prepared, prepared_v3: preparedV3, need: need.length, mismatch_lines: mismatch, v2_rebuild_lines_before_v3: mismatchBefore, refused, swaps, accepted_total: accepted.length, accepted_after_switch: acceptedAfter, found_lines: found, cpu_recheck_mismatched: mismatched, rate_mh_s: rate, last_status: lastStatus }; } const checks = { From 1c90ca9c6f99e17e17f9dea044b43e815d5a7119 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:33:19 +0000 Subject: [PATCH 131/158] Consequences: H lands about 18:45Z by the DAA rate at 22:33Z (C1, D1) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- docs/plans/consequences-decisions.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 302bafba..6897c45e 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -8,7 +8,7 @@ Rows already handled before this ledger opened, for the shape: 15.6 GB mine-and- | # | Number | Source | Tier affected | Consequence | Action | Owner | State | |---|---|---|---|---|---|---|---| -| C1 | Fee switch H = 210,000, reached about 19:00Z on 6 October (18:50 to 19:35 by the live rate at 22:24Z; the plans first said 19:50 from 0.965 blocks/s) | `docs/plans/fee-switch-devnet.md` sections 3 and 7; `vendor/igneum-node-0310/igneum/exec/src/rpc.rs` 849 (no `daaScore`, no `feesV1ActivationDaa` in `igneum_exportSegments`); the app's exporter call without `--fees-v1-activation-daa` (`app/igneum-app/src/prover.rs` 516 to 519) | every prover on the devnet (PC 2, the Mac, any 0.3.10 machine) | From the first chain block at or above H the 0.3.10 node cuts 30,000-pgas shards and meters with the v1 table, but the app's exporter sees a dump without the switch and cuts at 7.5 M with the prototype table: the host refuses the fixture (wrong `S_p`) or the node vetoes the statement. Proving on the devnet goes dark at H and nothing is paid until every prover runs a node whose export carries the switch (the proving-v1 fork does, `vendor/igneum-node-pv1` rpc.rs 961 and 982). `pc2-chain.ps1` fixtures from the live node fail the same way after H | Either 0.3.11 (proving-v1 fork) on every prover before H, or the switch republished at a later H before 19:50Z tomorrow (a digest flip, every node). Recommendation in `consequences-decisions.md` D1 | proving v1 acd4f36bc2c07a4e2, shipper ae892a8b0f78fe31c, coordinator ada8afb62d752b1e2 | in work (proving agent: fork eb32c645 on 21d4c73c carries daaScore and feesV1ActivationDaa, the exporter needs no flag; 0.3.11 on every prover before 16:00Z on 6 October or H moves to tip + 86,400; the line is in the proving plan, the rollout plan and release-0.3.10.md) | +| C1 | Fee switch H = 210,000, reached about 18:45Z on 6 October (DAA 137,041 at 22:32:54Z, 1.002 DAA/s averaged since the 15:40Z read; the plans first said 19:50 from 0.965 blocks/s) | `docs/plans/fee-switch-devnet.md` sections 3 and 7; `vendor/igneum-node-0310/igneum/exec/src/rpc.rs` 849 (no `daaScore`, no `feesV1ActivationDaa` in `igneum_exportSegments`); the app's exporter call without `--fees-v1-activation-daa` (`app/igneum-app/src/prover.rs` 516 to 519) | every prover on the devnet (PC 2, the Mac, any 0.3.10 machine) | From the first chain block at or above H the 0.3.10 node cuts 30,000-pgas shards and meters with the v1 table, but the app's exporter sees a dump without the switch and cuts at 7.5 M with the prototype table: the host refuses the fixture (wrong `S_p`) or the node vetoes the statement. Proving on the devnet goes dark at H and nothing is paid until every prover runs a node whose export carries the switch (the proving-v1 fork does, `vendor/igneum-node-pv1` rpc.rs 961 and 982). `pc2-chain.ps1` fixtures from the live node fail the same way after H | Either 0.3.11 (proving-v1 fork) on every prover before H, or the switch republished at a later H before 19:50Z tomorrow (a digest flip, every node). Recommendation in `consequences-decisions.md` D1 | proving v1 acd4f36bc2c07a4e2, shipper ae892a8b0f78fe31c, coordinator ada8afb62d752b1e2 | in work (proving agent: fork eb32c645 on 21d4c73c carries daaScore and feesV1ActivationDaa, the exporter needs no flag; 0.3.11 on every prover before 16:00Z on 6 October or H moves to tip + 86,400; the line is in the proving plan, the rollout plan and release-0.3.10.md) | | C2 | 16,751 MiB GPU peak during the chain of 8 with the miner resident (`chain-pc2-pv1c`) | bench-log "proving v1", step 2 chain row | 16 GB cards (RTX 5080, 5060 Ti 16 GB, 4060 Ti 16 GB) | The plan's "a 16 GB card sits 0.4 GB under tonight's peak" is the empty-shard row (15,590 MiB). The chained aggregation adds 1.2 GB and lands at 16.4 GB, over a 16 GB card. So a 16 GB card cannot mine and aggregate on this build; it can at most mine and prove shards, with 0.4 GB spare and no full shard measured | The 16 GB chained-aggregation row goes into the sweep; the aggregator step in `prover.rs aggregate_once` gates on card memory (24 GB with the miner running, else pause the miner on that card for the aggregation); the Proving tile says which role the card runs | proving v1 | closed as measured (proving agent, spcurve-miner-pc2-pv1 219517f: the adopted shard beside the miner 22,210 MiB and 13.2 s, so a 24 GB card has 2.3 GB spare from the fee switch, the number approximate for the card itself because it is the 5090's allocation pattern; the prototype shard 30.1 GB, the 32 GB card alone; aggregation_card gates on the same memory rule, 23,552 MB either role). Open: the first real 24 GB card measurement, and the public line says "24 GB" from a 32 GB card's pattern (D2 wording) | | C3 | 13,816 MiB GPU peak, prover alone, empty shards (`prover-cost-pc2-pv1`) | bench-log "proving v1", step 1 | 12 GB cards (RTX 3060 12 GB, 4070, 5070), the default-on rule (`provedefault.rs` `MIN_VRAM_MB` 11,776) | On the only measurement the prover by itself exceeds a 12 GB card by 1.5 GB, so the 12 GB default gate switches proving on for cards that cannot run it on this build unless the SP1 knobs bring the peak down. The litepaper's "12 GB or more proves full shards" (`site/litepaper.html` 560) and evidence row 16 rest on the sweep | 0.3.11 does not ship the default-on until the sweep has a row under 11.5 GB for a full v1 shard; if none, the gate moves to 24 GB and the public claim is qualified (D2) | proving v1 (sweep in work); public claim: decision | closed as measured (proving agent: the sweep moves no floor, 13.9 GB for an empty shard, 28.3 GB for a full prototype shard; the default is 20 GB mining / 16 GB prove-only; the 12 GB sentence is false on this build and goes to the project lead as D2 with the curve); the v1-shard row is C15 | | C4 | Host RAM 25,550 MB used of 63,132 on PC 2 with the prover on; the WSL2 VM working set 7,915 MB | bench-log "proving v1", step 1 host RAM row | Windows home miners with 16 GB RAM (the common gaming PC); Macs with 16 GB switching the CPU prover on | The prover default has no RAM floor. On Windows the WSL2 VM alone holds 7.9 GB beside the app, the node and the game-class desktop; a 16 GB machine with proving on by default swaps or kills the node. The app's own RSS (engine, node, verifier) is not separated in the measurement, so no requirement can be stated yet | The sweep job records the app's and the node's working sets beside the VM's; `provedefault` reads total RAM and stays off under 32 GB on Windows until measured; the Proving tile and the miner page state the RAM requirement | proving v1; miner UI adbf58b058186a18b (the tile line) | closed in code (proving v1 c2544be: MIN_RAM_MB_WINDOWS 31,000 in provedefault.rs, the tile line names the 7.9 GB VM on a 63 GB PC; unknown RAM is not a gate; the miner UI help line next cut) | diff --git a/docs/plans/consequences-decisions.md b/docs/plans/consequences-decisions.md index d274ffb2..2aadbfd2 100644 --- a/docs/plans/consequences-decisions.md +++ b/docs/plans/consequences-decisions.md @@ -6,7 +6,7 @@ Sibling of `ledger-decisions.md`. Each is a consequence of a measured number tha | # | By when | One line | What the project lead does | |---|---|---|---| -| D1 | 16:00 UTC, 6 October | The fee switch at H = 210,000 (about 18:50 to 19:35 UTC by the devnet's rate at 22:24Z, earlier than the plans' 19:50) darkens every 0.3.10 prover; 0.3.11 must be on every prover first, else H moves | Nothing if the morning check passes; the coordinator holds it. Know it exists | +| D1 | 16:00 UTC, 6 October | The fee switch at H = 210,000 (about 18:45 UTC by the devnet's DAA rate at 22:33Z, 1.002 DAA/s since 15:40Z; an hour earlier than the plans' 19:50) darkens every 0.3.10 prover; 0.3.11 must be on every prover first, else H moves | Nothing if the morning check passes; the coordinator holds it. Know it exists | | D9 | this week | No 12 GB card exists here; every 12 GB number is scaled from the 5090 | Confirm whether the 3060 and 5060 Ti 16 GB that evidence.md says are on order are real; if not, buy one 12 GB card (about £250 to £400) | | D2 | before the next site push | The litepaper's "12 GB proves" is false on this build; three drafts of the replacement exist (proving-v1 440fd59, ca2-coord 1c8439f, proving-methods.md section 5) | Pick one; the proving-methods paragraph is recommended | | D8 | the same push | "The card mines and proves" and "a visible 1% software fee" are solo-NVIDIA sentences now | Approve the qualified wording | From 492da2e09565035eda77860038bfa377421dbc74 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:34:07 +0000 Subject: [PATCH 132/158] Consequences ledger: C33 the public stats API carries no proving coverage (2.7%, one prover, 46 s median on /api/live) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 6897c45e..fd6c4a4a 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -69,3 +69,4 @@ Sweep 9 (22:2x UTC) notes: the devnet at 22:24:31Z reads DAA 136,578, 0.909 bloc Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/consequences-2026-10-05.md and consequences-decisions.md only (base ca8d9f3); a merge-tree against master cde561c and after shows 0 conflicts. | C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | closed on ca2-coord a7be43f and 0d9b23d (the 12 GB clause struck; the merge rule "take proving-v1's row 16 and its proving sentences" in the rollout plan; one wording in all five places, "the proposed schedule, fixed at the testnet genesis: 2 GB, doubling at years 4, 12 and 28", the lifetime sentences kept as consequences of the proposal and marked approximate) | | C32 | The 0.3.10 install at 21:49Z cleared PC 1's app jobs folder and with it the AMD kit fetched at 21:23:59Z; the amd-card-test playbook now says its fetch must be republished after any app update | bench-log 39f02ff; status 21:05 ("the jobs folder is cleared by fetch jobs" was the earlier, wrong reading) | every PC job tonight and tomorrow; the 0.3.11 rollout | A class, not one playbook: every fetch-then-run pair (era, hot table, mixer, repro, Ember, the AMD sweep, the prover-floor build) loses its kit when an update lands between the fetch and the run, and the run fails in seconds or, worse, runs against a stale copy. The 0.3.11 update-now reaches PC 2 while the prover-floor agent's 60 to 90 minute server build runs there (go at 22:17, to about 23:50): if that build's working directory is under the app's jobs folder, the update wipes it mid-build and the 12 GB rows slip past the morning | (1) The 0.3.11 update-now is sequenced after the prover-floor build closes, or the build's directory is confirmed outside the jobs folder before the ship; (2) every run playbook begins with a presence check of its kit and fails with "kit missing: republish the fetch after the app update" (the class check: the bash-body sub-agent's CI check gains a rule that a run job naming a kit path tests it first, or the coordinator's queue re-fetches after every update as a rule) | coordinator ada8afb62d752b1e2 (the queue and the ship order) | taken (coordinator: the prover-floor build lives under /opt/igneum-floor in WSL2, outside the jobs folder, but it is the app's job process and an app restart ends it, so the 0.3.11 update-now goes to PC 2 only after floor-build-3 closes, the ship's earlier steps not waiting; the re-fetch rule and the presence-check rule are in the rollout plan beside the one-job rule, the playbook owners carry it at their next publish; the CI side is with the bash-body sub-agent as a kit-path check) | +| C33 | `/api/live` at 22:33Z: 13 of 482 blocks fully proven in 10 minutes (2.7%), 1 prover, median proof lag 46 s, the live node's verifier "Off" with 42 pool entries pending and 0 verified; `/api/stats` (the documented public API) carries no proving field at all | live and stats handlers (`site/api/stats.mjs` FIELDS; the explorer branch 3e01212); the homepage "~60 s to a proof"; evidence row 15 | everyone who reads the public API or the homepage tile; the testnet's first external reader | The public stats API hides the one number that qualifies the tile and row 15: coverage is 2.7% with one prover, and the proof lag is 46 s. A reader can find it only on /api/live. The live node's verifier "Off" (42 pending, 0 verified) is the Mac app node in trust mode or without a host, so the page says "verifier Off" while the chain pays provers: a public-page oddity the morning reader will ask about | `/api/stats` gains a `proving` object from the same live_state (`blocks_10m`, `blocks_fully_proven_10m`, `shards_paid_10m`, `provers_10m`, `median_proof_lag_s`, `active`), documented in docs/api/public-stats.md and in its contract test; the live page's verifier line names which node it reads and why it is off; the homepage tile's "~60 s" caption cites the measured 46 s median and the 2.7% coverage ("the target; today one prover covers 2.7% of blocks at a 46 s median") | explorer a76f60b415859b7b5 (the API); the tile caption: D2 wording for the project lead | sent | From 8970afc6008ec81665b0b2509ddfce29a28f2f89 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:34:28 +0000 Subject: [PATCH 133/158] counter-asic-2-node.md: the integration merges (readwidth 06dcb31, origin/master ae699a4) and how each conflict was kept Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-2-node.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index 445d0302..2011dd88 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -172,6 +172,15 @@ The PC's test stage builds the fork's test binaries with the plan's feature set, | fork `cargo test -p kaspa-consensus-core` | 108 + 7: the switch rounding, the era clock, the params, digest and fast-time file tests | pass (Mac, fork 79bd8e10) | | PC 2 `build-job.mjs` suites | `kaspa-consensus-core igneum-exec kaspa-pow kaspa-consensus igneum-miner` + `igneum-app` | the coordinator publishes on its go | +## 6a. The integration merges for the ship (5 October 2026, 22:35Z to 22:45Z) + +| Merge | Commit | Conflicts, and how they were kept | +|---|---|---| +| readwidth 30ff674 (the OpenCL `__local` declaration rule, the read-width decision record, the per-watt rows) | 3566afd | `emit.rs` (2 hunks: the hot-table kernel arguments, HEAD's superset), `packbench.swift` (readwidth's resident-footprint lines added to HEAD's hot-aware RESULT line), `bench-log.md` (both entries), `read-width.md` add/add (readwidth's version, a pure superset of HEAD's) | +| origin/master 1f0d62c (the 0.3.10 merge) | 49c7e78 | `host.c` (one struct hunk: both fields, `dupOf` and `pci[32]`; master's topology, duplicate-platform and read-back code and ca2-v3's class/era tokens, `mh_word`, hot and mixer fields all auto-merged; `cc -fsyntax-only` clean), `bench-log.md` (both entries) | + +Checks after the merges: the igneum-pow suite, the packfile test, the CUDA emulation `emu/test.sh`, `proto-opencl/test-generic.sh` on Apple OpenCL, the fork's check: the result is in the final report and the status file. + ## 7. Unverified, and what is owed - The era walk for era >= 1 has never run (the devnet is 180 days from era 1); a pruning-proof sync past era 0 fails with `MissingEraSeed` until an era witness exists in the proof format. From d449ff486de16fcd0bcc883a1cce50e186731368 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:35:56 +0000 Subject: [PATCH 134/158] Consequences ledger: C33 closed (explorer 303038a) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index fd6c4a4a..e9501cd2 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -69,4 +69,4 @@ Sweep 9 (22:2x UTC) notes: the devnet at 22:24:31Z reads DAA 136,578, 0.909 bloc Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/consequences-2026-10-05.md and consequences-decisions.md only (base ca8d9f3); a merge-tree against master cde561c and after shows 0 conflicts. | C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | closed on ca2-coord a7be43f and 0d9b23d (the 12 GB clause struck; the merge rule "take proving-v1's row 16 and its proving sentences" in the rollout plan; one wording in all five places, "the proposed schedule, fixed at the testnet genesis: 2 GB, doubling at years 4, 12 and 28", the lifetime sentences kept as consequences of the proposal and marked approximate) | | C32 | The 0.3.10 install at 21:49Z cleared PC 1's app jobs folder and with it the AMD kit fetched at 21:23:59Z; the amd-card-test playbook now says its fetch must be republished after any app update | bench-log 39f02ff; status 21:05 ("the jobs folder is cleared by fetch jobs" was the earlier, wrong reading) | every PC job tonight and tomorrow; the 0.3.11 rollout | A class, not one playbook: every fetch-then-run pair (era, hot table, mixer, repro, Ember, the AMD sweep, the prover-floor build) loses its kit when an update lands between the fetch and the run, and the run fails in seconds or, worse, runs against a stale copy. The 0.3.11 update-now reaches PC 2 while the prover-floor agent's 60 to 90 minute server build runs there (go at 22:17, to about 23:50): if that build's working directory is under the app's jobs folder, the update wipes it mid-build and the 12 GB rows slip past the morning | (1) The 0.3.11 update-now is sequenced after the prover-floor build closes, or the build's directory is confirmed outside the jobs folder before the ship; (2) every run playbook begins with a presence check of its kit and fails with "kit missing: republish the fetch after the app update" (the class check: the bash-body sub-agent's CI check gains a rule that a run job naming a kit path tests it first, or the coordinator's queue re-fetches after every update as a rule) | coordinator ada8afb62d752b1e2 (the queue and the ship order) | taken (coordinator: the prover-floor build lives under /opt/igneum-floor in WSL2, outside the jobs folder, but it is the app's job process and an app restart ends it, so the 0.3.11 update-now goes to PC 2 only after floor-build-3 closes, the ship's earlier steps not waiting; the re-fetch rule and the presence-check rule are in the rollout plan beside the one-job rule, the playbook owners carry it at their next publish; the CI side is with the bash-body sub-agent as a kit-path check) | -| C33 | `/api/live` at 22:33Z: 13 of 482 blocks fully proven in 10 minutes (2.7%), 1 prover, median proof lag 46 s, the live node's verifier "Off" with 42 pool entries pending and 0 verified; `/api/stats` (the documented public API) carries no proving field at all | live and stats handlers (`site/api/stats.mjs` FIELDS; the explorer branch 3e01212); the homepage "~60 s to a proof"; evidence row 15 | everyone who reads the public API or the homepage tile; the testnet's first external reader | The public stats API hides the one number that qualifies the tile and row 15: coverage is 2.7% with one prover, and the proof lag is 46 s. A reader can find it only on /api/live. The live node's verifier "Off" (42 pending, 0 verified) is the Mac app node in trust mode or without a host, so the page says "verifier Off" while the chain pays provers: a public-page oddity the morning reader will ask about | `/api/stats` gains a `proving` object from the same live_state (`blocks_10m`, `blocks_fully_proven_10m`, `shards_paid_10m`, `provers_10m`, `median_proof_lag_s`, `active`), documented in docs/api/public-stats.md and in its contract test; the live page's verifier line names which node it reads and why it is off; the homepage tile's "~60 s" caption cites the measured 46 s median and the 2.7% coverage ("the target; today one prover covers 2.7% of blocks at a 46 s median") | explorer a76f60b415859b7b5 (the API); the tile caption: D2 wording for the project lead | sent | +| C33 | `/api/live` at 22:33Z: 13 of 482 blocks fully proven in 10 minutes (2.7%), 1 prover, median proof lag 46 s, the live node's verifier "Off" with 42 pool entries pending and 0 verified; `/api/stats` (the documented public API) carries no proving field at all | live and stats handlers (`site/api/stats.mjs` FIELDS; the explorer branch 3e01212); the homepage "~60 s to a proof"; evidence row 15 | everyone who reads the public API or the homepage tile; the testnet's first external reader | The public stats API hides the one number that qualifies the tile and row 15: coverage is 2.7% with one prover, and the proof lag is 46 s. A reader can find it only on /api/live. The live node's verifier "Off" (42 pending, 0 verified) is the Mac app node in trust mode or without a host, so the page says "verifier Off" while the chain pays provers: a public-page oddity the morning reader will ask about | `/api/stats` gains a `proving` object from the same live_state (`blocks_10m`, `blocks_fully_proven_10m`, `shards_paid_10m`, `provers_10m`, `median_proof_lag_s`, `active`), documented in docs/api/public-stats.md and in its contract test; the live page's verifier line names which node it reads and why it is off; the homepage tile's "~60 s" caption cites the measured 46 s median and the 2.7% coverage ("the target; today one prover covers 2.7% of blocks at a 46 s median") | explorer a76f60b415859b7b5 (the API); the tile caption: D2 wording for the project lead | closed on explorer d7e797c (/api/stats carries the proving object with coverage_10m, 0.0273 at 22:33Z, in the contract test, the live check and docs/api/public-stats.md with the sentence that coverage is what a third party reads before "every block is proven"; the live page's proving legend and the API note say the observer's node runs its own verifier off and reads paid shards from the chain). The homepage tile caption stays with the project lead (D2) | From 21127216e7f95525743f43c241079e7670a370b0 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:36:56 +0000 Subject: [PATCH 135/158] counter-asic-2-node.md: the integration checks on 19b30e3 (igneum-pow, packfile, the CUDA emulation, test-generic on Apple OpenCL, the fork check) all green Co-Authored-By: Claude Fable 5.1 --- docs/plans/counter-asic-2-node.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/counter-asic-2-node.md b/docs/plans/counter-asic-2-node.md index 2011dd88..7aace6e6 100644 --- a/docs/plans/counter-asic-2-node.md +++ b/docs/plans/counter-asic-2-node.md @@ -179,7 +179,7 @@ The PC's test stage builds the fork's test binaries with the plan's feature set, | readwidth 30ff674 (the OpenCL `__local` declaration rule, the read-width decision record, the per-watt rows) | 3566afd | `emit.rs` (2 hunks: the hot-table kernel arguments, HEAD's superset), `packbench.swift` (readwidth's resident-footprint lines added to HEAD's hot-aware RESULT line), `bench-log.md` (both entries), `read-width.md` add/add (readwidth's version, a pure superset of HEAD's) | | origin/master 1f0d62c (the 0.3.10 merge) | 49c7e78 | `host.c` (one struct hunk: both fields, `dupOf` and `pci[32]`; master's topology, duplicate-platform and read-back code and ca2-v3's class/era tokens, `mh_word`, hot and mixer fields all auto-merged; `cc -fsyntax-only` clean), `bench-log.md` (both entries) | -Checks after the merges: the igneum-pow suite, the packfile test, the CUDA emulation `emu/test.sh`, `proto-opencl/test-generic.sh` on Apple OpenCL, the fork's check: the result is in the final report and the status file. +Checks after the merges (49c7e78, 22:50Z): the igneum-pow suite 53 + 4 + 19 + 7 pass; the packfile test 0 failures; the CUDA emulation `emu/test.sh` PASS (ready + prepare 1, 64 + 64 + 32 found on pack A, pack B prepared with its self-test, the swap, the self-heal rebuild of pack A, 17 sampled hashes equal to `igneum-pow hash-bound`, the NVRTC source check PASS for 6 files); `proto-opencl/test-generic.sh` on Apple OpenCL PASS (the same protocol, job 5 refused after the swap, 15 sampled hashes equal); the fork's `kaspa-pow`, `igneum-miner`, `kaspad` check clean. The emulation needs `IGNEUM_CUDA_INC` pointed at a checkout's `proto-cuda/nvrtc/redist/include` (the worktree has none). ## 7. Unverified, and what is owed From b2e6d6159e458c4257ead09c93f348d323791d5a Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:37:29 +0000 Subject: [PATCH 136/158] CI: run jobs test their fetched kit before use (the wiped-jobs-folder class) The app's install clears the jobs folder on a PC, so a run job whose kit was fetched by an earlier fetch job finds nothing after an update and fails in seconds (5 October 2026, 21:49Z, the AMD kit; bench-log 4df339f). Rule: a run playbook that reaches a path under the jobs folder other than its own tests the kit is there before its first use, and the fetch is republished under a new id after any app update. tools/ci/kit-path-check.sh reads every *.ps1 under relay/playbooks/ and tools/. A kit root is a path derived from the jobs folder (`$jobs = Split-Path $env:IGNEUM_JOB_DIR` then `Join-Path $jobs ''`, the race-5090.ps1 shape) or one carrying a literal `jobs\` (the amd-card-test.ps1 shape); every path built from it belongs to that kit. A presence check (Test-Path, [IO.File]::Exists, [IO.Directory]::Exists, Get-Item or Get-ChildItem with -ErrorAction) on the root or anything under it covers the whole kit. A use before that line fails with "kit path used before a presence check: republish the fetch after any app update", as does a literal jobs\ path in a command with no check. The job's own folder ($env:IGNEUM_JOB_DIR) is not a kit path. Fixtures: kit-path-ok.ps1 (both shapes, checked; a sibling pack file covered by the worker's check) and kit-path-unchecked.ps1 (the worker run before its check, a literal never checked); --self-test asserts the lines. Wired into ci.yml after the bash-body step, and into publish-jobs.sh add --kind run beside the other two checks; test-publish-jobs.sh gains the refusal (34 passed, 0 failed). The current tree: race-5090.ps1 is the one playbook with a kit, checked before use. README-ship.md: the rule. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 2 + packaging/README-ship.md | 4 + packaging/ota/publish-jobs.sh | 7 +- packaging/ota/test-publish-jobs.sh | 6 +- tools/ci/fixtures/kit-path-ok.ps1 | 25 ++++++ tools/ci/fixtures/kit-path-unchecked.ps1 | 14 +++ tools/ci/kit-path-check.sh | 110 +++++++++++++++++++++++ 7 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 tools/ci/fixtures/kit-path-ok.ps1 create mode 100644 tools/ci/fixtures/kit-path-unchecked.ps1 create mode 100755 tools/ci/kit-path-check.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1da84358..95d0c966 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,8 @@ jobs: run: bash tools/ci/copied-sources-check.sh - name: bash bodies in PowerShell job scripts pass bash -n, the lost-quote class (self-test first, then the tree) run: bash tools/ci/bash-body-check.sh --self-test && bash tools/ci/bash-body-check.sh + - name: run jobs test their fetched kit before use, the wiped-jobs-folder class (self-test first, then the tree) + run: bash tools/ci/kit-path-check.sh --self-test && bash tools/ci/kit-path-check.sh - name: pinned guest programs match their manifest and are built only by pin-guests.sh run: bash tools/ci/pinned-guests-check.sh - name: no secret file names and no 64-hex secrets in the tree (self-test first, then the tree) diff --git a/packaging/README-ship.md b/packaging/README-ship.md index 0d62e74a..ceb69dae 100644 --- a/packaging/README-ship.md +++ b/packaging/README-ship.md @@ -121,3 +121,7 @@ file and run), unescapes it the way PowerShell would, and runs `bash -n` on it. never passes CI before it runs, so `packaging/ota/publish-jobs.sh add --kind run` runs the same check (and `tools/ci/prover-socket-check.sh`, the root-socket class; `bash -n` for a `.sh` script) on the script before anything is signed, and refuses the publish with the check's output. `packaging/ota/test-publish-jobs.sh` proves the refusals. +The wiped-jobs-folder class (5 October 2026, 21:49Z): an app install clears the jobs folder, so a run job that uses a kit +fetched by an earlier fetch job tests the kit is there (Test-Path on the kit root or any file under it) before its first +use, and the fetch is republished under a new id after any app update. `tools/ci/kit-path-check.sh` fails CI and the +publish on a kit path used before that check. diff --git a/packaging/ota/publish-jobs.sh b/packaging/ota/publish-jobs.sh index 8d444d5f..1d3c9d7d 100755 --- a/packaging/ota/publish-jobs.sh +++ b/packaging/ota/publish-jobs.sh @@ -261,14 +261,17 @@ if [ "$CMD" = add ]; then # anything is signed. A failure refuses the publish with the check's output; a missing check refuses it too. # PowerShell: every inline bash body must parse (tools/ci/bash-body-check.sh, the lost-quote class; a body it # cannot read fails, never skips). Bash: the script itself must parse. Both: a root prover run kills the GPU - # server and unlinks its socket (tools/ci/prover-socket-check.sh, the root-socket class). A check file that is - # missing from this tree refuses too (bash exits 127 with the reason), so no job goes out unchecked. + # server and unlinks its socket (tools/ci/prover-socket-check.sh, the root-socket class), and a fetched kit under + # the jobs folder is tested before its first use (tools/ci/kit-path-check.sh, the wiped-jobs-folder class). A + # check file that is missing from this tree refuses too (bash exits 127 with the reason), so no job goes out + # unchecked. if [ "$SHELL_KIND" = powershell ]; then out="$(bash "$ROOT/tools/ci/bash-body-check.sh" "$SCRIPT" 2>&1)" || { echo "run: bash-body-check.sh refuses $SCRIPT:" >&2; printf '%s\n' "$out" >&2; exit 1; } else out="$(bash -n "$SCRIPT" 2>&1)" || { echo "run: bash -n refuses $SCRIPT (the lost-quote class):" >&2; printf '%s\n' "$out" >&2; exit 1; } fi out="$(bash "$ROOT/tools/ci/prover-socket-check.sh" "$SCRIPT" 2>&1)" || { echo "run: prover-socket-check.sh refuses $SCRIPT:" >&2; printf '%s\n' "$out" >&2; exit 1; } + out="$(bash "$ROOT/tools/ci/kit-path-check.sh" "$SCRIPT" 2>&1)" || { echo "run: kit-path-check.sh refuses $SCRIPT:" >&2; printf '%s\n' "$out" >&2; exit 1; } PARAMS="$(python3 -c 'import json,sys; print(json.dumps({"script": open(sys.argv[1]).read(), "shell": sys.argv[2], "elevated": sys.argv[3]=="1", "stop_miners_first": sys.argv[4]=="1", **({"timeout_minutes": int(sys.argv[5])} if sys.argv[5] else {})}))' "$SCRIPT" "$SHELL_KIND" "$ELEVATED" "$STOP_MINERS" "$TIMEOUT_MIN")" [ -n "$TITLE" ] || TITLE="run $(basename "$SCRIPT")" ;; diff --git a/packaging/ota/test-publish-jobs.sh b/packaging/ota/test-publish-jobs.sh index c5f8b545..3f149f8f 100755 --- a/packaging/ota/test-publish-jobs.sh +++ b/packaging/ota/test-publish-jobs.sh @@ -9,7 +9,8 @@ # a new envelope; an envelope made by hand from the FIRST file and the SECOND signature (the stale pair an edge can # serve across a deploy) is REFUSED by the signer with the words the app logs; a tampered inner byte is refused; # `sign` rewrites all three consistently; a `run` script that fails a class check (a lost quote in an inline bash -# body, a body the check cannot read, a bash script that does not parse, a root prover run without the socket cleanup) +# body, a body the check cannot read, a bash script that does not parse, a root prover run without the socket cleanup, +# a fetched kit used before a presence check) # is REFUSED before anything is signed and the envelope is left as it was; and the real OTA key is the key the app # embeds. # @@ -112,6 +113,9 @@ expect_fail "a bash script with a lost quote" "$PUBLISH" add --kind run --target printf '& wsl.exe -d Ubuntu-24.04 -u root -- bash /mnt/c/prove.sh\n# igneum-prove-host --mode shard --shard 0\n' > "$T/root-prover.ps1" expect_fail "a root prover script without the socket cleanup" "$PUBLISH" add --kind run --target 1ccfe586 --script "$T/root-prover.ps1" --id test-root-socket --dest "$D" --base-url https://example.invalid/dl/t grep -q "prover-socket" "$T/out" && ok "refused by the socket check" || bad "another reason: $(tail -1 "$T/out")" +printf '$jobs = Split-Path $env:IGNEUM_JOB_DIR\n$exe = Join-Path (Join-Path $jobs "fetch-kit-1") "worker.exe"\n& $exe --list\n' > "$T/kit-unchecked.ps1" +expect_fail "a run script that uses a fetched kit before a presence check" "$PUBLISH" add --kind run --target 1ccfe586 --script "$T/kit-unchecked.ps1" --id test-kit-unchecked --dest "$D" --base-url https://example.invalid/dl/t +grep -q "kit path used before a presence check" "$T/out" && ok "refused by the kit-path check" || bad "another reason: $(tail -1 "$T/out")" cmp -s "$T/before.signed" "$D/igneum-jobs.signed.json" && ok "a refused publish leaves the envelope untouched" || bad "a refused publish changed the envelope" echo "== the key" diff --git a/tools/ci/fixtures/kit-path-ok.ps1 b/tools/ci/fixtures/kit-path-ok.ps1 new file mode 100644 index 00000000..14904f36 --- /dev/null +++ b/tools/ci/fixtures/kit-path-ok.ps1 @@ -0,0 +1,25 @@ +# Fixture for tools/ci/kit-path-check.sh --self-test: both ways a run job reaches a fetched kit under the app's jobs +# folder, each checked for presence before its first use. Must pass (2 kit paths). Never run; a stand-in for a job. +$ErrorActionPreference = 'Continue' +# the job's own folder always exists (the app made it for this run): not a kit path +$job = $env:IGNEUM_JOB_DIR +$jobFile = Join-Path $env:IGNEUM_JOB_DIR 'jobs.txt' +# 1. the race-5090.ps1 shape: the jobs folder is the parent of this job's folder, the kit is a sibling job's folder +$jobs = Split-Path $env:IGNEUM_JOB_DIR +$fetched = Join-Path $jobs 'fetch-race-worker-20261004' +$exe = Join-Path $fetched 'igneum-worker-cuda.exe' +if (-not (Test-Path $exe)) { Write-Output "RESULT race worker missing at $exe (the fetch job runs first)"; exit 2 } +& $exe --race --race-rounds 3 2>&1 | ForEach-Object { "RESULT $_" } +# 2. the amd-card-test.ps1 shape: jobs\\kit under the app folder, a wait loop then the check; a sibling file +# under the same kit (pack-a) is covered by the check on the worker +$KitJob = 'amd-kit-1' +$kit = Join-Path $env:IGNEUM_APP_DIR ("jobs\" + $KitJob + "\kit") +$worker = Join-Path $kit 'igneum-worker-opencl.exe' +$tele = Join-Path $kit 'igneum-gpu-telemetry.exe' +$pack = Join-Path $kit 'pack-a' +$deadline = (Get-Date).AddMinutes(2) +while (-not ((Test-Path $worker) -and (Test-Path $tele)) -and (Get-Date) -lt $deadline) { Start-Sleep -Seconds 5 } +if (-not (Test-Path $worker)) { "RESULT no worker at $worker"; exit 1 } +$list = & $worker --list 2>&1 +$serve = Start-Process -FilePath $worker -ArgumentList '--serve','--pack',"`"$pack`"" -RedirectStandardInput $jobFile -NoNewWindow -PassThru +exit 0 diff --git a/tools/ci/fixtures/kit-path-unchecked.ps1 b/tools/ci/fixtures/kit-path-unchecked.ps1 new file mode 100644 index 00000000..41023537 --- /dev/null +++ b/tools/ci/fixtures/kit-path-unchecked.ps1 @@ -0,0 +1,14 @@ +# Fixture for tools/ci/kit-path-check.sh --self-test: the wiped-jobs-folder class (5 October 2026, 21:49Z: the 0.3.10 +# install cleared the jobs folder and the AMD kit job failed in seconds). Line 9 runs the worker before its check at +# line 10; line 13 runs a literal jobs\ path that is never checked. Must fail twice. Never run; a stand-in for a job. +$ErrorActionPreference = 'Continue' +# 1. the worker is run first and tested after +$jobs = Split-Path $env:IGNEUM_JOB_DIR +$fetched = Join-Path $jobs 'fetch-race-worker-20261004' +$exe = Join-Path $fetched 'igneum-worker-cuda.exe' +& $exe --race 2>&1 | ForEach-Object { "RESULT $_" } +if (-not (Test-Path $exe)) { Write-Output "RESULT race worker missing at $exe"; exit 2 } +# 2. an inline literal under the jobs folder, never checked +$KitJob = 'amd-kit-1' +& "$env:IGNEUM_APP_DIR\jobs\$KitJob\kit\igneum-worker-opencl.exe" --list 2>&1 +exit 0 diff --git a/tools/ci/kit-path-check.sh b/tools/ci/kit-path-check.sh new file mode 100755 index 00000000..693791f8 --- /dev/null +++ b/tools/ci/kit-path-check.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash +# The wiped-jobs-folder class (5 October 2026, 21:49Z, bench-log 39f02ff): the app's install clears the jobs folder on +# a PC, so a run job whose kit was fetched by an earlier fetch job finds nothing after an update and fails in seconds. +# Rule: a run playbook that reaches a path under the app's jobs folder other than its own +# (`$jobs = Split-Path $env:IGNEUM_JOB_DIR` then `Join-Path $jobs ''`, the race-5090.ps1 shape; or a literal +# `jobs\\...`, the amd-card-test.ps1 shape) tests that the kit is there (Test-Path, [IO.File]::Exists, +# [IO.Directory]::Exists, Get-Item or Get-ChildItem with -ErrorAction) before its first use, and republishes the fetch +# after any app update. A check on the kit's root or on anything under it covers the whole kit. This check reads every +# *.ps1 under relay/playbooks/ and tools/ and fails on a kit path used before a presence check. The job's own folder +# ($env:IGNEUM_JOB_DIR, made by the app for the run) is not a kit path. +# +# Usage: tools/ci/kit-path-check.sh # the tree (tools/ci/fixtures/ left out) +# tools/ci/kit-path-check.sh ... # named files (the jobs publisher runs it on the script it publishes) +# tools/ci/kit-path-check.sh --self-test # must fire on the unchecked fixture and stay quiet on the correct one +set -uo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)" +REPO="$(cd "$HERE/../.." && pwd)" + +check_files() { + python3 - "$@" <<'PY' +import re, sys + +SEED_LIT = re.compile(r'jobs\\') # a literal path under the jobs folder +SEED_FOLDER = re.compile(r'Split-Path\s+\$env:IGNEUM_JOB_DIR') # the jobs folder itself: the parent of this job's folder +CHECK = re.compile(r'Test-Path|\[IO\.(File|Directory)\]::Exists|Get-(Item|ChildItem)\b[^|]*-ErrorAction') +ASSIGN = re.compile(r'^\s*\$([A-Za-z_][A-Za-z0-9_]*)\s*=(?!=)\s*(.*)$') +# a right-hand side that only builds a path (the kit var it names is then a path, not a use of one) +DERIV = re.compile(r'^(Join-Path\b|Split-Path\b|\(|"|\'|\[IO\.Path\]::Combine|\$env:|\$[A-Za-z_][A-Za-z0-9_]*\s*(\+|\.(TrimEnd|Replace)|$))') +TEXT_ONLY = re.compile(r'^\s*(Write-Output|Write-Host|Write-Verbose|Say|Log)\b|^\s*"') +VAR = re.compile(r'\$([A-Za-z_][A-Za-z0-9_]*)\b(?!:)') +MSG = 'kit path used before a presence check: republish the fetch after any app update' + +rc = 0 +files = sys.argv[1:] +with_kits = 0 +for path in files: + try: + lines = open(path, encoding='utf-8', errors='replace').read().split('\n') + except OSError as e: + print('FAIL %s: %s' % (path, e)); rc = 1; continue + folder = set() # vars that are the jobs folder (always there) + root_of = {} # kit var -> its root var + root_line = {} # root var -> the line it is defined on + checked = set() # roots with a presence check so far + reported = set() + inline_checked = False + fails = [] + for ln, raw in enumerate(lines, 1): + s = raw.strip() + if not s or s.startswith('#'): continue + refs = set(VAR.findall(raw)) + kit_refs = refs & set(root_of) + m = ASSIGN.match(raw) + if m: + name, rhs = m.group(1), m.group(2).strip() + if SEED_FOLDER.search(rhs) and not SEED_LIT.search(rhs): + folder.add(name); continue + deriv = bool(DERIV.match(rhs)) + if deriv and (SEED_LIT.search(rhs) or (refs & folder)): + root_of[name] = name; root_line[name] = ln; continue + if deriv and kit_refs: + root_of[name] = root_of[sorted(kit_refs)[0]]; continue + if CHECK.search(raw): + for v in kit_refs: checked.add(root_of[v]) + if SEED_LIT.search(raw): inline_checked = True + continue + for v in sorted(kit_refs): + r = root_of[v] + if r in checked or r in reported: continue + reported.add(r) + fails.append('FAIL %s:%d %s ($%s, kit path $%s defined at line %d)' % (path, ln, MSG, v, r, root_line[r])) + if SEED_LIT.search(raw) and not inline_checked and not TEXT_ONLY.match(raw): + inline_checked = True + fails.append('FAIL %s:%d %s (a literal jobs\\ path in a command, never checked)' % (path, ln, MSG)) + if fails: + rc = 1 + for f in fails: print(f) + elif root_of or inline_checked: + with_kits += 1 + print('ok %s (%d kit path%s, checked before use)' % (path, len(root_line), '' if len(root_line) == 1 else 's')) +print('kit-path-check: %d files, %d with a fetched kit, %s' % (len(files), with_kits, 'all checked before use' if rc == 0 else 'FAILURES above')) +sys.exit(rc) +PY +} + +if [ "${1:-}" = "--self-test" ]; then + F="$HERE/fixtures" + ok_out="$(check_files "$F/kit-path-ok.ps1" 2>&1)"; ok_rc=$? + bad_out="$(check_files "$F/kit-path-unchecked.ps1" 2>&1)"; bad_rc=$? + echo "self-test correct fixture: rc $ok_rc"; printf '%s\n' "$ok_out" | sed 's/^/ /' + echo "self-test unchecked fixture: rc $bad_rc"; printf '%s\n' "$bad_out" | sed 's/^/ /' + [ $ok_rc -eq 0 ] || { echo "SELF-TEST FAILED: the correct fixture was flagged"; exit 1; } + printf '%s\n' "$ok_out" | grep -q '^ok .*2 kit paths' || { echo "SELF-TEST FAILED: the correct fixture holds 2 kit paths, a different number was found"; exit 1; } + [ $bad_rc -ne 0 ] || { echo "SELF-TEST FAILED: the unchecked fixture passed"; exit 1; } + for want in 'kit-path-unchecked.ps1:9 ' 'kit-path-unchecked.ps1:13 '; do + printf '%s\n' "$bad_out" | grep -q "^FAIL .*$want.*republish the fetch after any app update" || { echo "SELF-TEST FAILED: the unchecked use at $want was not reported"; exit 1; } + done + [ "$(printf '%s\n' "$bad_out" | grep -c '^FAIL ')" -eq 2 ] || { echo "SELF-TEST FAILED: 2 failures expected"; exit 1; } + echo "self-test passed: the unchecked kit paths fail, the checked ones pass" + exit 0 +fi + +if [ $# -gt 0 ]; then + check_files "$@"; exit $? +fi +cd "$REPO" +files=$(git ls-files 'relay/playbooks/**' 'tools/**' | grep -E '\.ps1$' | grep -v '^tools/ci/fixtures/' || true) +if [ -z "$files" ]; then echo "kit-path-check: no .ps1 files under relay/playbooks/ or tools/"; exit 0; fi +# shellcheck disable=SC2086 +check_files $files From c3aef6fcae7315628c2d86656713f10a92ee8170 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:37:52 +0000 Subject: [PATCH 137/158] Consequences ledger: C32 CI side closed (kit-path-check e3bd761), the bash-body-check branch in the merge note Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index e9501cd2..5b118a14 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -66,7 +66,7 @@ Sweep 8a (22:2x UTC) note: `docs/analysis/proving-methods.md` (branch proving-me Sweep 9 (22:2x UTC) notes: the devnet at 22:24:31Z reads DAA 136,578, 0.909 blocks/s measured over the stats window and 1.005 DAA/s averaged since the fee-switch plan's 15:40Z read (112,227), so H = 210,000 lands between about 18:50 and 19:35 UTC on 6 October, up to an hour EARLIER than the 19:50Z written in fee-switch-devnet.md, the rollout plan 7a and release-0.3.10.md; the 16:00Z check (D1) keeps about 2.8 hours of margin and stands; the plans' ETA is re-cut in D1 and sent to the coordinator. Gates tonight: G3, G4, G6 green on the final class (x8 + era); G4b added (the Mac app passes --prepare-packs only to non-Metal workers, so every Mac would stop at the first v3 epoch: found by the node agent, the fix with a unit test and a real Metal gate run before the ship); G1 and G2 on the PC 1 job since 22:16. -Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/consequences-2026-10-05.md and consequences-decisions.md only (base ca8d9f3); a merge-tree against master cde561c and after shows 0 conflicts. +Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/consequences-2026-10-05.md and consequences-decisions.md only (base ca8d9f3); a merge-tree against master 1f0d62c shows 0 conflicts. Branch `bash-body-check` (7adb1ca, 6805125, e3bd761) carries tools/ci/bash-body-check.sh, kit-path-check.sh, the copied prover-socket-check.sh (add/add with proving-v1's: take bash-body-check's), ci.yml steps, the publish-jobs.sh gate and packaging/README-ship.md; it is on the coordinator's ship order after ca2-coord. | C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | closed on ca2-coord a7be43f and 0d9b23d (the 12 GB clause struck; the merge rule "take proving-v1's row 16 and its proving sentences" in the rollout plan; one wording in all five places, "the proposed schedule, fixed at the testnet genesis: 2 GB, doubling at years 4, 12 and 28", the lifetime sentences kept as consequences of the proposal and marked approximate) | -| C32 | The 0.3.10 install at 21:49Z cleared PC 1's app jobs folder and with it the AMD kit fetched at 21:23:59Z; the amd-card-test playbook now says its fetch must be republished after any app update | bench-log 39f02ff; status 21:05 ("the jobs folder is cleared by fetch jobs" was the earlier, wrong reading) | every PC job tonight and tomorrow; the 0.3.11 rollout | A class, not one playbook: every fetch-then-run pair (era, hot table, mixer, repro, Ember, the AMD sweep, the prover-floor build) loses its kit when an update lands between the fetch and the run, and the run fails in seconds or, worse, runs against a stale copy. The 0.3.11 update-now reaches PC 2 while the prover-floor agent's 60 to 90 minute server build runs there (go at 22:17, to about 23:50): if that build's working directory is under the app's jobs folder, the update wipes it mid-build and the 12 GB rows slip past the morning | (1) The 0.3.11 update-now is sequenced after the prover-floor build closes, or the build's directory is confirmed outside the jobs folder before the ship; (2) every run playbook begins with a presence check of its kit and fails with "kit missing: republish the fetch after the app update" (the class check: the bash-body sub-agent's CI check gains a rule that a run job naming a kit path tests it first, or the coordinator's queue re-fetches after every update as a rule) | coordinator ada8afb62d752b1e2 (the queue and the ship order) | taken (coordinator: the prover-floor build lives under /opt/igneum-floor in WSL2, outside the jobs folder, but it is the app's job process and an app restart ends it, so the 0.3.11 update-now goes to PC 2 only after floor-build-3 closes, the ship's earlier steps not waiting; the re-fetch rule and the presence-check rule are in the rollout plan beside the one-job rule, the playbook owners carry it at their next publish; the CI side is with the bash-body sub-agent as a kit-path check) | +| C32 | The 0.3.10 install at 21:49Z cleared PC 1's app jobs folder and with it the AMD kit fetched at 21:23:59Z; the amd-card-test playbook now says its fetch must be republished after any app update | bench-log 39f02ff; status 21:05 ("the jobs folder is cleared by fetch jobs" was the earlier, wrong reading) | every PC job tonight and tomorrow; the 0.3.11 rollout | A class, not one playbook: every fetch-then-run pair (era, hot table, mixer, repro, Ember, the AMD sweep, the prover-floor build) loses its kit when an update lands between the fetch and the run, and the run fails in seconds or, worse, runs against a stale copy. The 0.3.11 update-now reaches PC 2 while the prover-floor agent's 60 to 90 minute server build runs there (go at 22:17, to about 23:50): if that build's working directory is under the app's jobs folder, the update wipes it mid-build and the 12 GB rows slip past the morning | (1) The 0.3.11 update-now is sequenced after the prover-floor build closes, or the build's directory is confirmed outside the jobs folder before the ship; (2) every run playbook begins with a presence check of its kit and fails with "kit missing: republish the fetch after the app update" (the class check: the bash-body sub-agent's CI check gains a rule that a run job naming a kit path tests it first, or the coordinator's queue re-fetches after every update as a rule) | coordinator ada8afb62d752b1e2 (the queue and the ship order) | taken (coordinator: the prover-floor build lives under /opt/igneum-floor in WSL2, outside the jobs folder, but it is the app's job process and an app restart ends it, so the 0.3.11 update-now goes to PC 2 only after floor-build-3 closes, the ship's earlier steps not waiting; the re-fetch rule and the presence-check rule are in the rollout plan beside the one-job rule, the playbook owners carry it at their next publish; the CI side is with the bash-body sub-agent as a kit-path check). CI side closed on bash-body-check e3bd761: tools/ci/kit-path-check.sh in ci.yml and in publish-jobs.sh add --kind run (34 tests pass); every existing kit-using playbook (13 across master, ca2-v3, ca2-analysis, rdna4-telemetry) already checks before use, so the gate guards the shape without a backlog | | C33 | `/api/live` at 22:33Z: 13 of 482 blocks fully proven in 10 minutes (2.7%), 1 prover, median proof lag 46 s, the live node's verifier "Off" with 42 pool entries pending and 0 verified; `/api/stats` (the documented public API) carries no proving field at all | live and stats handlers (`site/api/stats.mjs` FIELDS; the explorer branch 3e01212); the homepage "~60 s to a proof"; evidence row 15 | everyone who reads the public API or the homepage tile; the testnet's first external reader | The public stats API hides the one number that qualifies the tile and row 15: coverage is 2.7% with one prover, and the proof lag is 46 s. A reader can find it only on /api/live. The live node's verifier "Off" (42 pending, 0 verified) is the Mac app node in trust mode or without a host, so the page says "verifier Off" while the chain pays provers: a public-page oddity the morning reader will ask about | `/api/stats` gains a `proving` object from the same live_state (`blocks_10m`, `blocks_fully_proven_10m`, `shards_paid_10m`, `provers_10m`, `median_proof_lag_s`, `active`), documented in docs/api/public-stats.md and in its contract test; the live page's verifier line names which node it reads and why it is off; the homepage tile's "~60 s" caption cites the measured 46 s median and the 2.7% coverage ("the target; today one prover covers 2.7% of blocks at a 46 s median") | explorer a76f60b415859b7b5 (the API); the tile caption: D2 wording for the project lead | closed on explorer d7e797c (/api/stats carries the proving object with coverage_10m, 0.0273 at 22:33Z, in the contract test, the live check and docs/api/public-stats.md with the sentence that coverage is what a third party reads before "every block is proven"; the live page's proving legend and the API note say the observer's node runs its own verifier off and reads paid shards from the chain). The homepage tile caption stays with the project lead (D2) | From 399b1ca427a6b5d78a44319be4150bf6d40a1737 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:38:10 +0000 Subject: [PATCH 138/158] docs/analysis/asic-resistance-history.md: the 2011 to 2026 history of ASIC resistance (31 rows, chip, gain per joule, months held, response), the papers, the chip economics and the market-cap table, ten lessons, the audit of class v2 and the Counter ASIC 2.0 layers, seven ranked additions (partial-store chip row first), nine candidates placed nowhere Co-Authored-By: Claude Fable 5.1 --- docs/analysis/asic-resistance-history.md | 418 +++++++++++++++++++++++ 1 file changed, 418 insertions(+) create mode 100644 docs/analysis/asic-resistance-history.md diff --git a/docs/analysis/asic-resistance-history.md b/docs/analysis/asic-resistance-history.md new file mode 100644 index 00000000..47b3a20c --- /dev/null +++ b/docs/analysis/asic-resistance-history.md @@ -0,0 +1,418 @@ +# ASIC resistance, 2011 to 2026: the history, the papers, the lessons, and the audit of Igneum against them + +5 October 2026 (night), branch `asic-history`. Asked by the project lead at 20:05 UTC: "do a full on deep dive into the full history of 'asic resistance' and see if we can add or upgrade anything." Baseline for the audit: the Counter ASIC 2.0 final class decided tonight (`docs/plans/counter-asic-2-status.md` on `ca2-coord`, entries 20:16 to 22:25 UTC; `docs/analysis/chip-model-v3.md` on `ca2-mixer` 1ab8b21). Every figure about another chain cites a repo file, a paper or a dated article, or is labelled approximate. Hash-per-joule gains are computed from the cited hashrate and watt figures of the chip and of the best consumer GPU of the same year, and are approximate by construction (GPU figures vary by tuning). Research gathered by four sub-agents between 20:10 and 20:45 UTC; the fetch failures they reported are listed in section 6. + +## 0. One page for the project lead + +**What the history says Igneum is doing right.** + +| # | What | The evidence | +|---|---|---| +| 1 | Binding the hash to random reads over a dataset larger than any on-chip cache, with the dataset growing on a schedule, and measuring the latency-bound share per card | Every compute-bound hash fell to a chip at 20x to 1,200x per joule within 16 to 37 months (rows Scrypt, X11, Blake, kHeavyHash, Blake3). The memory-bound hashes capped the chip at 1.1x to 4.8x (Ethash rows) or saw no chip at all (KawPow, Verthash, Autolykos, FishHash). RandomX's own design chose a 2 GiB dataset because a 2 GiB SRAM die was "questionable" at 7 nm in 2019 (RandomX `doc/design.md`) | +| 2 | A random program per epoch from a VDF seed, a weak-program filter, era draws from chain state, instruction families unlocked by height, and no scheduled human fork | Monero forked four times in 20 months and lost 85% of its hashrate at each fork to chips that returned within months (CryptoNight rows). Vertcoin forked three times and was 51%-attacked after two of them. Ravencoin's X16Rv2 fork was followed by FPGA bitstreams within weeks. Grin's six-monthly tweaks worked only because the lane was scheduled to die. The only random-program hashes with no chip after five years are the ones that never needed a fork (KawPow, FiroPoW, ProgPowZ) | +| 3 | Pricing the on-die-cache recompute chip and spending the mixer budget against it (x8: 0.92x with the 3x factor), with the model public | This is the "light-evaluation attack" Least Authority flagged on ProgPoW in 2019 and ProgPoW never fixed; Bob Rao's hardware audit put an on-die-DAG ProgPoW chip at "<< 0.1x" the energy per hash of a GPU. Kik's 2020 exploit was the same attack through a 64-bit seed. Igneum has a number against it; ProgPoW had a suggestion | + +**What the history says Igneum is missing or under-weighting.** + +| # | What | The evidence | +|---|---|---| +| 1 | The partial-store chip with a custom memory system (HBM or many narrow DRAM channels) is not in the chip model. The model prices only the f = 0 endpoint (all SRAM, recompute everything) | The only chip class that ever beat a memory-bound GPU hash did it this way: Ethash chips reached 2.1x (Linzhi, 2020), 2.9x (E9, 2022) and 4.8x (Jasminer X4, 2021) per joule through custom memory controllers and on-package memory, with no on-die dataset at all. The time-memory curve between f = 0 and f = 1 is open item O-1.6 and has never been drawn (`proto-metal/MEMHARD.md` section 3 item 2). Cuckoo Cycle's "tmto-hard" claim fell to a 50x memory cut for 2x time within two months of publication (Andersen, 2014) | +| 2 | The item-derivation mixer has a fixed shape. That fixed shape is exactly what hands the recompute chip its 3x fixed-function factor (bare 0.31x becomes 0.92x) | RandomX made the item derivation itself a random program (SuperscalarHash: about 450 instructions generated per seed, scheduled for a superscalar core, 170-cycle latency to match DRAM), so a chip cannot hard-wire it. Igneum draws the mixer's constants per day and keeps the shape; a chip hard-wires the shape. CryptoNight-R's random math per block raised chip latency only 2.5x; the lever is in the memory path, which for the recompute chip is the mixer | +| 3 | No clock and no detector. Chips have appeared at market caps from $18M (Radiant) and $23M (Grin) upward, and Vorick's 2018 rule was "any coin with over $20M of block reward in a year has a secret ASIC on it". Monero's secret chips held 85% of the hashrate before anyone saw them. Igneum's bounty is unfunded (D11) and the public benchmark is January 2027 | Rows Monero, Radiant, Grin, Handshake, Kadena; section 2.5 (the market-cap table); MoneroCrusher's nonce analysis (February 2019) found the chips by their share pattern, which is the detector Igneum can run from day one on the observer | + +**The one change I would make first.** Add the partial-store HBM chip to the chip model and draw the time-memory curve before genesis (ranked addition 1, section 4.3). It is an analysis, it costs the GPU nothing, and it is the one chip class that history shows beating memory-bound GPU work. If that row comes out under 2x the x8 decision stands as measured; if it does not, the next lever is known before the vectors are frozen. + +Everything else in this document is the evidence behind that page. + +## 1. The history, one row per attempt + +Columns: what the hash relied on; when it went live; the first chip that beat it (vendor, model, date, rated hashrate and watts); the gain in hash per joule against the best consumer GPU of the time (approximate, derived); how long it held (months from live to first chip); what the chain did. "None" in the chip column means no shipped chip was found in any source as of October 2026. + +### 1.1 The rows + +| # | Hash (chain) | Live | Relied on | First chip (vendor, model, date, rate, watts) | Gain per joule vs GPU (approximate) | Held (months) | Response | Sources | +|---|---|---|---|---|---|---|---|---| +| 1 | Scrypt (Tenebrix, Litecoin, Dogecoin) | Sep and Oct 2011 | 128 KB scratchpad, meant to fit a CPU cache and not a 2011 GPU; latency at SRAM scale | Gridseed GC3355, early 2014, 360 kH/s at 7 to 8 W; Innosilicon A2 Terminator, Apr 2014, 28 nm; KnC Titan, 2014, 300 MH/s at 850 W | Gridseed 19x, A2 68x, Titan 140x (vs Radeon 7970 at 700 kH/s, 285 W); Antminer L7 (2021) 1,100x | 27 | Embraced. Dogecoin merge-mined with Litecoin from Sep 2014 | [S1] [S2] [S3] [S4] | +| 2 | X11 (Dash) and the chain family X13, X15, X17, Quark, Nist5, C11 | Jan 2014 | A chain of 11 SHA-3 candidates; compute only. Duffield said it was meant to replay Bitcoin's CPU to GPU to ASIC path, not to prevent it | iBeLink DM384M, Mar 2016, 384 MH/s at 715 W; Baikal Giant A900 (2016); Antminer D3, Sep 2017, 19.3 GH/s at 1,200 W | DM384M 33x, D3 1,000x (vs R9 280X at 4 MH/s, 250 W) | 26 | Embraced. Baikal's multi-algo units covered the whole family by 2017 | [S5] [S6] [S7] | +| 3 | Ethash (Ethereum) | Jul 2015 | DAG of 1 GB growing per epoch, 128-byte random reads; memory bandwidth | Antminer E3, announced Apr 2018, 180 MH/s at 800 W, 4 GB DDR3; Innosilicon A10 Pro (2020) 500 MH/s at 950 W; Linzhi Phoenix (Dec 2020) 2,733 MH/s at about 3,000 W; Jasminer X4 (Oct 2021) 2.5 GH/s at 1,200 W; Antminer E9 (Jun 2022) 2.4 GH/s at 1,920 W | E3 1.1x to 1.6x (a tuned 1080 Ti beat it per watt); A10 Pro 1.2x vs RTX 3080; Phoenix 2.1x; X4 4.8x; E9 2.9x | 32 to the first chip; about 65 to a chip over 2x | ProgPoW (EIP-1057) debated 2018 to 2020 and shelved; PoS at the Merge, 15 Sep 2022. ASIC share of hashrate stayed small (one 2018 estimate: 3%) | [S8] [S9] [S10] [S11] [S12] [S13] | +| 4 | Etchash (Ethereum Classic) | Nov 2020 (Thanos, ECIP-1099) | Ethash with the DAG cut to 2.5 GB to keep 3 to 4 GB cards mining; not an anti-chip change | Post-Merge Ethash chips moved over: Antminer E9 Pro (Feb 2023) 3.68 GH/s at 2,200 W; Jasminer X16-P (2023) 5.8 GH/s at 1,900 W | E9 Pro about 4x, X16-P about 7x vs RTX 3090 (approximate) | n/a | Embraced | [S14] [S15] | +| 5 | Equihash 200,9 (Zcash, Horizen, Pirate) | Oct 2016 | Generalised birthday problem (Wagner); 144 MB in practice; memory size, with a claimed 1,000x compute penalty for halving memory | Antminer Z9 mini, announced 3 May 2018, 10 kSol/s at 300 W; Innosilicon A9 ZMaster (Jun 2018) 50 kSol/s at 620 W; Z11 (2019) 135 kSol/s at 1,418 W; Z15 (2020) 420 kSol/s at 1,510 W | Z9 mini 12x, A9 29x, Z11 34x, Z15 100x (vs GTX 1080 Ti at 700 Sol/s, 250 W) | 18 | Zcash: no fork (Zcon0 vote 45 to 19 against prioritising resistance, Jun 2018; ECC chose Sapling over resistance); PoS plan announced Nov 2021. Horizen: stayed after a 51% attack (Jun 2018). Pirate: stayed | [S16] [S17] [S18] [S19] [S20] | +| 6 | Equihash parameter forks: Zhash 144,5 (Bitcoin Gold), ZelHash 125,4 (Flux), BeamHash I to III 150,5 (Beam), 210,9 (Aion), 192,7 (Zero) | Jul 2018 (BTG), Jun 2019 (Flux), Jan 2019 (Beam) | Larger memory per solver than 200,9 (Beam and Flux also changed the datapath against FPGA bitstreams) | None found for 144,5, 125,4 or 150,5. Vorick (May 2018) wrote that an Equihash chip able to follow any parameter fork had been designed | n/a | BTG 7 years, Flux 7 years, Beam 7 years, with small prizes (section 2.5) | BTG: forked after the May 2018 51% attack, attacked again Jan 2020. Beam: planned "one or two hard forks" then BeamHash III (Jun 2020) as the last. Flux: stayed on ZelHash | [S21] [S22] [S23] [S24] | +| 7 | Scrypt-N, Lyra2RE, Lyra2REv2 (Vertcoin) | Jan 2014, Dec 2014, Aug 2015 | Memory-hard sponge (Lyra2) inside a hash chain; Scrypt-N grew N over time | Dayun Zig Z1, Sep 2018, 6.8 GH/s at 1,200 W (FPGA bitstreams of about 216 MH/s per board preceded it in 2018) | Z1 20x (vs GTX 1080 Ti at 59 MH/s, 207 W) | 37 (Lyra2REv2) | Lyra2REv3, Feb 2019, "to rid the network of the current generation of ASICs and FPGAs"; 51% attacks via rented hash in Oct to Dec 2018 (22 reorgs) and Dec 2019 | [S25] [S26] [S27] [S28] | +| 8 | Verthash (Vertcoin) | Jan 2021 | A 1.2 GB file generated from the chain's own block headers; random reads; bandwidth, like Ethash | None found | n/a | 69 and counting, small prize | No fork since | [S29] [S30] | +| 9 | X16R (Ravencoin) | Jan 2018 | 16 hashes in an order set by the previous block hash; compute, with order randomised | OW Miner OW1, Sep 2019, about 182 MH/s at 1,400 W; SKC Turing R1 claimed. Widely thought FPGA-based | OW1 1.3x (vs GTX 1080 Ti at 18 to 31 MH/s, 190 to 284 W) | 20 | X16Rv2, 1 Oct 2019 (hashrate fell 70%); FPGA bitstreams for X16Rv2 within weeks (BittWare CVP-13 at 240 MH/s) | [S31] [S32] [S33] | +| 10 | KawPow (Ravencoin; Neoxa, Clore, Meowcoin, Neurai) | 6 May 2020 | ProgPoW 0.9.4 variant: random math per block, DAG, 16 KB cache reads; targets the GPU datapath | None found as of 2026 (no vendor lists one; one retailer listing naming an "Antminer X9" for KawPow is an error) | n/a | 77 and counting | Roadmap: "No additional future algorithm forks are envisaged" | [S34] [S35] [S36] | +| 11 | MTP (Zcoin, now Firo) | Dec 2018 | Argon2d memory array with a Merkle tree (Biryukov and Khovratovich, "Egalitarian computing"); 4 GB; memory size | None | n/a | 34, then replaced | Dinur and Nadler broke the 2 GB instance to under 1 MB at a 170x compute penalty before launch (2017); MTP 1.2 patched it. FiroPoW (ProgPoW variant) Oct 2021 for block size and GPU fairness, not for a chip | [S37] [S38] [S39] [S40] | +| 12 | FiroPoW (Firo) | 26 Oct 2021 | ProgPoW 0.9.4 with a per-block program | None found | n/a | 59 and counting | Nov 2025 fork cut the maximum DAG to 6.76 GB to keep 8 GB cards | [S40] [S41] | +| 13 | Blake-256 14r (Decred) | Feb 2016 | Compute; chosen to be ASIC-friendly ("easy and fast implementation of hardware is the main design goal") | Innosilicon D9, about Apr 2018, 2.4 TH/s at 1,000 W; Obelisk DCR1 (Jun 2018); Antminer DR5 (Dec 2018) 35 TH/s at 1,610 W | D9 130x, DR5 1,200x (vs GTX 1080 Ti at 4.6 GH/s, 250 W) | 26 | Embraced by design | [S42] [S43] [S44] | +| 14 | Blake2b (Sia) | Jun 2015 | Compute | Antminer A3, Jan 2018, 815 GH/s at 1,186 W; Obelisk SC1 (Jul 2018) 550 GH/s at 500 W; Innosilicon S11 (2018) 3.83 TH/s at 1,380 W | A3 58x, S11 230x (vs GTX 1080 Ti at 2.96 GH/s, 250 W) | 31 | Fork at block 179,000 (Oct 2018) to brick Bitmain and Innosilicon units and keep Obelisk's; Innosilicon then held about 37% of hashrate | [S45] [S46] [S47] | +| 15 | Blake2s (Kadena) | Nov 2019 | Compute; chosen to be "GPU mineable and not immediately ASIC mineable (but for which an ASIC can be made)" | Goldshell KD2 and KD5, Mar 2021, 18 TH/s at 2,250 W; Antminer KA3 (Sep 2022) 166 TH/s at 3,154 W | KD5 195x, KA3 1,280x (vs RTX 3080 at 8.9 GH/s, 217 W) | 16 | Embraced by design | [S48] [S49] [S50] | +| 16 | CryptoNight (Bytecoin, Monero) | Jul 2012, Apr 2014 | 2 MB scratchpad sized to a per-core L3, AES rounds, random reads; latency at SRAM scale | Secret chips from about late 2017 (85% of the hashrate vanished at the April 2018 fork); Antminer X3, announced Mar 2018, 220 kH/s at 550 W; Baikal Giant-N | X3 40x to 50x (vs Vega 64 at about 2 kH/s, 200 to 250 W, approximate) | 43 to the secret chips, 47 to the announced one | Forks: CryptoNight v7 (6 Apr 2018), v8 (18 Oct 2018), CryptoNight-R (9 Mar 2019, random math per block seeded by height, chip latency up 2.5x), RandomX (30 Nov 2019). MoneroCrusher's nonce analysis (Feb 2019) found chips at over 85% of the hashrate again, four months after v8 | [S51] [S52] [S53] [S54] [S55] | +| 17 | RandomX (Monero; Wownero, ArQmA, Zephyr, Tari) | 30 Nov 2019 | A VM running 8 chained random programs per hash on a superscalar CPU with floating point; 2 GiB dataset derived from a 256 MiB cache by a random superscalar program (SuperscalarHash); 2 MiB scratchpad in L1, L2, L3 tiers | Antminer X5, Sep 2023, 212 kH/s at 1,350 W (RISC-V cores); Antminer X9, Jul 2026 delivery, 1 MH/s at 2,472 W; Pinecone INIBOX R1X, Mar 2026, 1.2 MH/s at 2,055 W | X5 at parity with a Ryzen 9 7950X (157 against about 200 H/J, approximate); X9 and R1X 2x to 3x over the best CPU (approximate). GPUs are 25x worse per joule than CPUs on it | 46 to parity hardware, about 75 to a 2x to 3x chip | No fork as of Oct 2026. Four audits in 2019 (Trail of Bits, X41, Kudelski, QuarksLab) found nothing critical | [S56] [S57] [S58] [S59] [S60] | +| 18 | Cuckoo Cycle (Grin Cuckaroo lane, Aeternity, Cortex) | Jan 2019 (Grin) | Find a 42-cycle in a random graph; lean solver one bit per edge; memory latency, or bandwidth in the mean solver | None on the Cuckaroo lane (Cuckaroo29 tweaked every 6 months: Cuckarood Jul 2019, Cuckaroom Jan 2020, Cuckarooz Jul 2020) | n/a | 24, retired on schedule | The lane was built to die: 90% of reward at launch falling to 0% in Jan 2021 (HF4) | [S61] [S62] [S63] | +| 19 | Cuckatoo31+ (Grin's chip lane) | Jan 2019 | Same, with plain bits in place of ternary counters to simplify chips; "Proof of SRAM" per Tromp | Obelisk GRN1 announced Jan 2019 and cancelled Jul 2019; Innosilicon G32 announced 2019 and never shipped; iPollo G1, Dec 2020, 36 GPS Cuckatoo32 at 2,800 W | G1 about 4x (vs RTX 3090 at about 1 GPS, 300 W, approximate) | 23, by design | Surrender by schedule. Tromp's $10,000 linear TMTO bounty was claimed in Apr 2025 (N/k bits at about k + 1,000 hashes per edge) | [S61] [S64] [S65] [S66] | +| 20 | ProgPoW (Ethereum proposal; Bitcoin Interest, Sero, Zano as ProgPowZ, Quai) | EIP May 2018; Bitcoin Interest 2018; Quai Jan 2025 | Random math per period on a 32-register file, 16 KB cache reads, 256-byte DAG loads, keccak-f800; "saturate the GPU" | None | n/a | 8 years across its adopters | Ethereum: tentatively approved Jan 2019 and Feb 2020, petition 27 Feb 2020, left "approved" and unscheduled on 6 Mar 2020, dead. Audits: Least Authority (Sep 2019) and Bob Rao (Sep 2019). Kik's 64-bit-seed exploit (Mar 2020) patched in 0.9.4 | [S67] [S68] [S69] [S70] [S71] [S72] | +| 21 | Autolykos v1 and v2 (Ergo) | Jul 2019; v2 Feb 2021 | v1: memory-hard with a per-miner secret key, so puzzles could not be outsourced to pools. v2: the secret removed (contract pools bypassed it); a 2 GB table that grows 5% per 51,200 blocks from block 614,400 | None | n/a | 87 and counting, small prize | v2 by EIP-0009 at block 417,792 | [S73] [S74] | +| 22 | Octopus (Conflux) | Oct 2020 | Ethash-style DAG; the "dense matrix step" could not be verified in `conflux-rust` tonight (unverified) | None | n/a | 72 and counting | CIP-102 (Aug 2022) proposed switching to Ethash to attract post-Merge miners; dormant | [S75] [S76] | +| 23 | kHeavyHash (Kaspa; Bugna kept it) | Nov 2021 | cSHAKE256, a 64x64 4-bit matrix multiply from the pre-PoW hash, cSHAKE256; compute, designed for optical and specialised hardware | IceRiver KS0, Jul 2023, 100 GH/s at 65 W; KS1, KS2 (Sep 2023); Antminer KS3, Aug 2023, 8.3 TH/s at 3,188 W; KS5 Pro (Mar 2024) 21 TH/s at 3,150 W | KS0 250x, KS5 Pro 1,100x (vs RTX 3090 at 910 MH/s, 150 W) | 17 to 20 | Embraced (Sompolinsky, May 2023: "an overall positive"). Hashrate went from under 100 PH/s to over 700 PH/s in months; the GPU share was negligible by late 2023 (approximate). Forks that left: Karlsen (FishHashPlus, Sep 2024), Pyrin (PyrinHash v2, Sep 2024), Spectre (CPU AstroBWTv3), Nexellia, Waglayla, Cryptix, Hoosat | [S77] [S78] [S79] [S80] [S81] | +| 24 | NexaPow (Nexa) | 2023 | SHA-256 plus a secp256k1 Schnorr signature per attempt; framed as "useful ASICs" | DragonBall A21, Jan 2025, 3.4 GH/s at 1,800 W | 3x to 4x (vs RTX 3090 at 123 to 137 MH/s, 230 W, approximate) | 24 | None | [S82] [S83] | +| 25 | Blake3 (Alephium; Iron Fish until 2024) | Nov 2021 | Double Blake3; compute; chosen as ASIC-friendly | Goldshell AL-BOX, 2023, 360 GH/s at 180 W; IceRiver AL0; Antminer AL1 (2024) 15.6 TH/s at 3,510 W; AL3 | AL-BOX 156x, AL1 350x (vs RTX 3090 at 2.3 GH/s, 180 W) | 22 to 24 | Alephium embraced. Iron Fish forked to FishHash (Apr 2024, FIP-3: Ethash-derived, fixed 4.6 GB dataset, 512 iterations, 128-byte mix); Karlsen adopted FishHashPlus (Sep 2024) | [S84] [S85] [S86] [S87] | +| 26 | FishHash (Iron Fish, Karlsen) | Apr 2024 | Ethash-derived, 4.6 GB fixed dataset; bandwidth | None found | n/a | 30 and counting, small prize | None | [S87] | +| 27 | Eaglesong (Nervos) | Nov 2019 | Compute (a new sponge) | Toddminer C1 (Feb 2020); Antminer K5, Mar 2020, 1.13 TH/s at 1,580 W; Goldshell CK5 (Mar 2021) 12 TH/s at 2,400 W | K5 70x, CK5 500x (vs RTX 3090 at about 2.1 GH/s, approximate) | 4 | Embraced | [S88] [S89] | +| 28 | Blake2b + SHA3 (Handshake) | Feb 2020 | Compute | Goldshell HS1, Jun 2020; HS3 (Jul 2020) 2 TH/s at 2,000 W; HS5 | Over 100x (approximate) | 5 | Embraced | [S90] [S91] | +| 29 | SHA512/256d (Radiant) | 2022 | Compute | DragonBall A11; IceRiver RX0, Sep 2024, 260 GH/s at 100 W | About 550x (vs RTX 3090 at 1.3 to 1.5 GH/s, approximate) | About 24 | None | [S92] [S93] | +| 30 | ProgPowZ (Zano), DynexSolve (Dynex), Janushash (Warthog), XelisHash v1 and v2 (Xelis), VerusHash 2.2 (Verus) | 2019 to 2024 | ProgPoW variant; GPU "neuromorphic" useful work; a product of VerusHash and SHA256t to balance CPU and GPU; CPU and GPU balanced; AES-based CPU hash | None found for any of them | n/a | Small prizes throughout | Xelis forked to v2 (Jul 2024) for FPGA resistance | [S94] [S95] [S96] [S97] [S98] | +| 31 | Ethash on EthereumPoW (ETHW) after the Merge | Sep 2022 | As Ethash | The Ethash chips above | About 4x (E9 Pro, X16-P vs RTX 3090, approximate) | n/a | Embraced | [S15] | + +### 1.2 What the rows say when sorted + +| Class of hash | Rows | Months to first chip | First-chip gain per joule | Best gain reached | +|---|---|---|---|---| +| Compute only (chains of hashes, Blake family, SHA-3 family, matrix multiply) | 2, 13, 14, 15, 23, 25, 27, 28, 29 | 4 to 31 (median about 24) | 33x to 250x | 500x to 1,280x | +| Memory at SRAM scale (128 KB scrypt, 2 MB CryptoNight) | 1, 16 | 27, 43 | 19x, 40x to 50x | 1,100x (Scrypt, 2021) | +| Memory size without a bandwidth bound (Equihash, Lyra2REv2) | 5, 7 | 18, 37 | 12x, 20x | 100x | +| Memory bandwidth at DRAM scale (Ethash, Verthash, FishHash, Etchash) | 3, 4, 8, 26 | 32 (Ethash); none for the others | 1.1x to 1.6x | 2.9x to 4.8x | +| Random program on a commodity datapath (RandomX, ProgPoW family, X16R's order randomisation) | 9, 10, 12, 17, 20, 30 | X16R 20 (FPGA-class, 1.3x); RandomX 46 to parity; none for ProgPoW's adopters in 8 years | 1.3x (X16R), 1x (RandomX 2023) | 2x to 3x (RandomX 2026, approximate) | +| Graph search (Cuckoo) | 18, 19 | 23 on the chip lane; never on the tweaked lane | 4x | 4x | + +Two caveats on the random-program rows. The prizes were small: Ravencoin, Firo and Zano never reached the market caps at which the 2018 chips appeared (section 2.5), so "no chip" is partly an economic fact. And RandomX's chips arrived once Monero's reward justified them: parity hardware at 46 months, a 2x to 3x chip at about 75 months (approximate), on a hash whose whole purpose was to make the CPU the chip. + +## 2. The academic side + +### 2.1 Memory-hard functions + +| Paper | Result | What it means for Igneum | +|---|---|---| +| Abadi, Burrows, Manasse, Wobber, "Moderately hard, memory-bound functions", NDSS 2003 and ACM TOIT 2005 [P1]; Dwork, Goldberg, Naor, "On memory-bound functions for fighting spam", CRYPTO 2003 [P2] | The origin of the idea: CPU speed varies 100x across machines, memory latency does not, so a cost function bound by cache misses is fairer than one bound by cycles | Igneum's latency-bound rule is this argument from 2003 applied to GPUs and DRAM: the DRAM row cycle is the same physics for a chip and a card (section 2.6) | +| Percival, "Stronger key derivation via sequential memory-hard functions", BSDCan 2009 [P3] | Defines sequential memory-hardness; ROMix is sequential memory-hard in the random-oracle model; cost measured in area-time (dollar-seconds) | The area-time measure is the one the chip model uses (equal silicon); scrypt's 2011 deployment at 128 KB ignored the paper's own scale | +| Alwen and Serbinenko, "High parallel complexity graphs and memory-hard functions", STOC 2015 [P4] | Cumulative memory complexity (CMC) in the parallel random-oracle model; earlier sequential measures fail against parallel, amortising adversaries | A chip is a parallel, amortising adversary; any Igneum claim about the dataset must be made in a parallel model | +| Alwen and Blocki, "Efficiently computing data-independent memory-hard functions", CRYPTO 2016 [P5]; "Towards practical attacks on Argon2i and Balloon hashing", EuroS&P 2017 [P6] | Any data-independent MHF can be computed in less than n^2 cumulative memory; Argon2i at O(n^1.75 log n), Catena and Balloon at O(n^1.67); the attacks are practical at real parameters | Igneum's addresses are data-dependent (register state), which is the right side of this result; the price is cache-timing leakage, which does not matter for a PoW | +| Alwen, Chen, Pietrzak, Reyzin, Tessaro, "Scrypt is maximally memory-hard", EUROCRYPT 2017 [P7] | scrypt's CMC is Omega(n^2 w) in the parallel ROM, optimal, against parallel amortising adversaries | Data-dependent chains of reads are the construction with the proof; Igneum's item derivation (8 dependent cache reads) is a short chain of this kind, with no proof | +| Biryukov, Dinu, Khovratovich, "Argon2", EuroS&P 2016 [P8]; Boneh, Corrigan-Gibbs, Schechter, "Balloon hashing", ASIACRYPT 2016 [P9] | Argon2d: a one-pass adversary can cut memory at most 3x at equal area-time; Argon2i needs over 10 passes to resist the Alwen-Blocki attack. Balloon: provable in the sequential model only; the paper says parallel ASIC attacks are outside its model | A "memory-hard" label without a stated adversary model has been wrong three times in this list (Argon2i, Balloon, Catena) | +| Biryukov and Khovratovich, "Tradeoff cryptanalysis of memory-hard functions", ASIACRYPT 2015 [P10]; Forler, Lucks, Wenzel, "Catena", 2013 [P11]; Simplicio et al., "Lyra2", IEEE TC 2016 [P12] | The ranking trade-off attack on Lyra2, yescrypt and Argon2; Catena's proofs flawed, 25x area-time cut; designers changed their algorithms | Lyra2REv2 (row 7) carried this construction into a PoW and still fell to a chip at 20x; the cryptanalysis found the shortcut before the chip did | + +### 2.2 Bandwidth-hard functions + +| Paper | Result | What it means for Igneum | +|---|---|---| +| Ren and Devadas, "Bandwidth hard functions for ASIC resistance", TCC 2017 [P13] | Memory-hardness (CMC) bounds a chip's area advantage and says nothing about energy; energy spent on off-chip memory traffic is comparable for a chip and a CPU, so bandwidth-hardness is the lever; scrypt, Catena-BRG and Balloon are bandwidth-hard with suitable parameters; the stacked double butterfly is capacity-hard and not bandwidth-hard | The chip model's "equal silicon" row is an area argument. The energy argument is the one the Ethash chips answered: they moved the same bytes at lower energy per byte with custom memory controllers (rows 3 and 4). Igneum's hash moves 128 x 64 B = 8 KB of DRAM lines per hash on AMD and 128 x 32 B on NVIDIA; a chip with 4-byte access granularity moves 512 B for the same work. That is the bandwidth-per-watt gain the plan's last section warns about, stated in Ren-Devadas's units | +| Blocki, Ren, Zhou, "Bandwidth-hard functions: reductions and lower bounds", CCS 2018 [P14] | Bandwidth cost in the parallel ROM equals the red-blue pebbling cost of the graph; high CMC implies high bandwidth cost; Argon2i and DRSample are maximally bandwidth-hard; a tight lower bound on scrypt's energy | The right formal target for a future proof about the item derivation, if one is ever attempted; none exists today | +| Alwen, Blocki, Harsha, "Practical graphs for optimal side-channel resistant MHFs", CCS 2017 [P15] | DRSample: a practical graph with maximal depth-robustness | Not applicable: Igneum does not need side-channel resistance | + +### 2.3 Asymmetric, egalitarian and graph proofs of work + +| Paper | Result | What it means for Igneum | +|---|---|---| +| Biryukov and Khovratovich, "Equihash", NDSS 2016 [P16] | Wagner's generalised birthday with algorithm binding; claimed 1,000x compute for halving memory | The claim did not survive contact with a chip design: 144 MB in practice fitted the Z9's memory system (row 5) | +| Biryukov and Khovratovich, "Egalitarian computing", USENIX Security 2016 [P17]; Dinur and Nadler, "Time-memory tradeoff attacks on the MTP proof-of-work scheme", CRYPTO 2017 [P18] | MTP: Argon2d plus a Merkle tree. Dinur-Nadler: malicious proofs with under 1 MB in place of 2 GB at a 170x compute penalty, by injecting blocks that steer Argon2d's data-dependent addressing | The attacker who controls the memory's contents controls the addresses. In Igneum the day key comes from a VDF of chain state and the cache fill is a chained block function, so no miner chooses the contents. The analogy still holds for the unreviewed mixer: a structural weakness in M_r is the shortcut this paper found in MTP | +| Tromp, "Cuckoo Cycle", BITCOIN 2015 [P19]; Andersen, "A public review of Cuckoo Cycle", 31 Mar 2014, and "Exploiting time-memory tradeoffs in Cuckoo Cycle", 1 Aug 2014 [P20]; the linear TMTO bounty, claimed Apr 2025 [S66] | Edge trimming cut memory about 50x for about 2x time, two months after publication; Tromp adopted it. The 2025 bounty result: an N/k-bit chip must hash each edge about k + 1,000 times | A time-memory claim is a curve, and the curve was wrong by 50x until someone drew it. Igneum's curve between "store everything" and "recompute everything" has not been drawn (O-1.6) | +| Georghiades, Flolid, Vishwanath, "HashCore", 2019 [P21] | "Inverted benchmarking": random widgets modelled on SPEC CPU workloads so the CPU is already the chip | The same idea as RandomX and ProgPoW stated generally: the hash is a benchmark of the target hardware | + +### 2.4 Program-based proofs of work and their audits + +| Document | What it says | What it means for Igneum | +|---|---|---| +| RandomX `doc/design.md` and `doc/specs.md` (tevador) [S56] [S57] | A VM so that the work is "data and code"; 8 chained programs per hash so a miner cannot filter (filtering 25% of programs at a 50% speedup yields 0.44x honest speed); SuperscalarHash of about 450 instructions with 155 multiplies, scheduled for a superscalar core at a 170-cycle latency to match DRAM, so a light-mode chip with the 256 MiB cache on die pays 760 cycles and 1,240 multiplies per item, "energy comparable to loading 64 bytes from DRAM"; a 2,080 MiB dataset because a 2 GiB SRAM die was "questionable" at 7 nm in 2019; cache-to-dataset ratio capped at 8 to keep the area-time product constant; double-precision floating point to force the whole CPU; "DRAM cannot do more than about 25 million random accesses per second per bank group" | Igneum rebuilt the idea for a GPU. The parts that carried over: the dataset above on-chip cache, the dependent item derivation, the cache-to-dataset ratio (4 at genesis, 8 at year 4 under option C). The parts that did not: per-hash programs (a GPU cannot JIT per hash and stay a GPU), floating point (vendor rounding), a random item-derivation program (Igneum's mixer has a fixed shape with drawn constants). Section 4.3 ranks the last of these | +| Trail of Bits audit of RandomX, 2 Jul 2019 [S60]; Kudelski, X41, QuarksLab (2019) | Two low findings and 47 brittle parameters; the design affirmed | Four paid external reviews before launch, for a hash whose whole value was the resistance claim. Igneum has had none (ledger M7) | +| EIP-1057 ProgPoW [S67]; Least Authority audit, 9 Sep 2019 [S69]; Bob Rao hardware audit, Sep 2019 [S70] | Claimed chip gain 1.1x to 1.2x. Least Authority: no issues, five suggestions, one of them the light-evaluation attack (on-the-fly DAG generation with the 16 MB cache in on-die SRAM) "may become possible within a few years" once about 100 MB of fast on-die SRAM is feasible. Rao: energy per hash is the only meaningful metric; shipping Ethash chips show about 1.6x hashrate per watt; conventional compute chips gain little on ProgPoW; integrating the DAG on die cuts data-movement energy by over 10x, so "ProgPOW ASICs with << 0.1X E/H over GPUs can be built"; an advanced-node chip is "$20M+" and "1+ year"; a 16-die split holding a 2.78 GB DAG was about $172 per board in 2019 against about $240 for a GPU board, and a monolithic die "viable around 2025" | The light-evaluation attack is Igneum's M16 recompute chip. ProgPoW left it as a suggestion; Igneum priced it and spent the mixer against it (x8). Rao's "$172 per board for a 16-die split" is the HBM-class partial-store chip in a different form, and it is the row the Igneum model lacks | +| Kik, "ProgPoW exploit", 4 Mar 2020 [S71] | A 64-bit seed lets a chip skip memory access with a cooperating node; patched in 0.9.4 | Igneum's seed is 256 bits and the program is the epoch's; the nearest analogue is header grinding for cache locality, unmeasured (section 4.3, check 4) | + +### 2.5 The economics of a chip + +**What a chip costs to make** (design plus masks, by node; all figures from the cited articles, which disagree with each other by 2x and say so): + +| Node | Mask set | Full design, IBS as quoted by Semiengineering (2018 and 2021) | Full design, other estimates | Sources | +|---|---|---|---|---| +| 65 nm MPW shuttle | n/a | n/a | Europractice 2025: about €51,000 minimum (9 mm^2 at €5,720 per mm^2) | [E1] | +| 28 nm | "beyond $1M" (SemiAnalysis 2022); $1M to $3M (Silicon Analysts 2026) | $51.3M (2018); $40M (2021) | $5M to $30M total NRE for a small chip (Silicon Analysts) | [E2] [E3] [E4] | +| 16/12 nm | n/a | $106M (2018 revision of a 2014 $310M figure) | Europractice MPW 16 nm: about €125,000 minimum | [E1] [E4] | +| 7 nm | "beyond $10M" (SemiAnalysis); $5M to $10M (Silicon Analysts) | $297.8M (2018); $217M "mainstream" (2021); Semiengineering's own 2023 discount: about $160M | Startups shipped 7 nm chips for "$50M to $75M" all-in (SemiAnalysis); a 10 nm-class mining chip "$20M+" (Rao 2019) | [E2] [E3] [E4] [S70] | +| 5 nm | $10M to $20M | $542.2M (2018); $416M (2021); about $280M discounted (2023) | Marvell 2023: $449M (secondary source, approximate) | [E2] [E3] [E5] | +| 3 nm | "$40M range" | $500M to $1.5B (2018); $590M (2021) | Marvell 2023: $581M (secondary, approximate) | [E2] [E3] [E5] | + +Miner-makers' own numbers: Bitmain's 2018 filing shows R&D of $73M in 2017 and $86M in the first half of 2018 and three failed chips at a reported combined cost of about $500M [E6]; Canaan's 2019 prospectus shows R&D of $26.5M in 2018 and "seven tape-outs" at a 100% success rate [E7]; Vorick wrote that Bitmain brought the Sia A3 to market for "less than $10 million" and took over $20M of orders within eight minutes [S47]; Obelisk's DCR1 was a 28 nm part [S43]; Taylor's 2013 survey gives $150,000 for a 130 nm and $500,000 for a 65 nm Bitcoin chip NRE in 2012 [E8]. + +**Where the 256 MiB cache lands a chip.** The Counter ASIC 2.0 analysis priced the 256 MiB SRAM mirror at 128 mm^2 and $46 per good die at N5 on the shipped-product density (`sram-mirror.md` revision 2, from AMD V-Cache 64 MB on 41 mm^2 at N7 [E9], TSMC N5 HD macro 31.8 Mib/mm^2 [E10]). The history adds the node question: a cheap chip is a 28 nm chip ($1M to $3M of masks, a $5M to $30M project), and a 28 nm bit cell is about 6x an N7 cell (approximate, from memory: TSMC 28 nm HD about 0.127 um^2 against N7's 0.027 [E10]), so 256 MiB at 28 nm is about 1,000 mm^2 of SRAM on the V-Cache density: more than a reticle. The cache forces the recompute chip onto a 7 nm or better node, which moves its project from the $5M class to the $50M class (SemiAnalysis's 7 nm startup figure). That is a stronger statement than the $46 per die, and it is the reason the cache size matters more than its per-die cost. Option C (the cache doubles with the dataset) keeps it true as nodes shrink: at the 6% per year density trend the status file cites, a 512 MiB mirror in year 4 costs more mm^2 than 256 MiB today. + +**When chips appeared** (CoinMarketCap historical snapshots pulled by the research agent; daily issuance is arithmetic from each chain's schedule; all approximate): + +| Chain | First public chip | Market cap then | Daily issuance then (USD) | +|---|---|---|---| +| Litecoin | Gridseed, Dec 2013 | $817M | $1.0M | +| Dash | PinIdea DR-100, Aug 2017 (iBeLink 2016 widely cited, date unverified) | $2.2B | $0.6M | +| Siacoin | Obelisk SC1 announced Jun 2017; Antminer A3 Jan 2018 | $430M; $1.5B | $0.43M; $1.1M | +| Decred | Obelisk DCR1 Jun 2017; Innosilicon D9 Apr 2018 | $216M; $353M | $0.18M | +| Monero | Antminer X3, Mar 2018 (secret chips from early 2017 per Vorick, unverified) | $3.3B | $0.75M | +| Ethereum | Antminer E3, Apr 2018 | $37.4B | $7.6M | +| Zcash | Z9 mini, May 2018 | $1.1B | $2.1M | +| Bitcoin Gold | the same chips, May 2018 | $1.3B | $0.14M | +| Grin | GRN1 announced Jan 2019 (cancelled); G32 Apr 2019 (never shipped); iPollo G1 Dec 2020 | $23M (Apr 2019); $23M (Dec 2020) | $0.24M; $33K | +| Nervos | Toddminer C1, Feb 2020 | $75M | $65K to $85K | +| Handshake | Goldshell HS1, Jun 2020 | $30M | $31K | +| Kadena | Goldshell KD5, Mar 2021 | $42M | $21K | +| Kaspa | IceRiver KS0, Jul 2023 | $480M | $0.42M | +| Alephium | Goldshell AL-BOX, May 2024 | $179M | $0.1M | +| Radiant | IceRiver RX0, Sep 2024 | $18M | $22K | + +Sources: [E11] (the research agent's CoinMarketCap pulls, dates in the table) and the chip rows above. Reading: a compute-bound hash gets a chip at $20K to $30K of daily issuance (Radiant, Kadena, Handshake); the 2018 cluster sat at $0.15M to $2M a day. Vorick's rule from May 2018: any coin with over $20M of block reward in a year (about $55K a day) should assume a secret chip [S47]. For Igneum the clock is the day its issuance in dollars crosses about $50K; a memory-bound hash buys time against that clock (Ethash: 32 months at the largest prize in the table), and the random program buys more (section 1.2), but nothing in the table says it buys forever. + +### 2.6 Latency as the resource + +| Source | What it says | What it means for Igneum | +|---|---|---| +| Li, Reddy, Jacob, "A performance and power comparison of modern high-speed DRAM architectures", MEMSYS 2018 [L1] | Row timings from datasheets: DDR4 tRCD 14, tRAS 33, tRP 14 ns; GDDR5 tRCD 14, tRAS 28, tRP 12; HBM and HBM2 tRCD 14, tRAS 34, tRP 14. Row cycle tRC about 40 ns (GDDR5) to 48 ns (DDR4, HBM2). "The memory-latency problem does still remain" | The row cycle is the floor under every dependent random read whatever the controller; HBM does not shorten it. What HBM and a custom controller change is the number of rows that can be opened per second per watt (channels, banks, pseudo-channels), which is the throughput of random reads in flight, which is what the 9070 XT probe measured as the card's ceiling (2.4 G reads/s against the 5090's 17.5 G) | +| Chang, CMU thesis, Dec 2017 [L2] | Over two decades DRAM capacity improved 128x, bandwidth 20x, latency 1.3x | The latency-bound rule has a long half-life; the bandwidth-per-watt lever (the Ethash chips) does not stand still | +| NVIDIA profiling guide and the Ampere tuning deck [L3] | L1 and L2 lines are 128 bytes in four 32-byte sectors; DRAM-to-L2 transactions default to 64 bytes since Volta, configurable 32, 64 or 128 on A100 | The 5090's 32-byte sector per 4-byte read is where a custom controller gains bandwidth efficiency (8x fewer bytes), the Ren-Devadas energy lever; the 9070 XT's 64-byte line is 16x. Neither changes the row cycle | +| RandomX `doc/design.md` [S56] | About 25 million random accesses per second per DRAM bank group; "all Dataset accesses read one CPU cache line (64 bytes) and are fully prefetched"; one program iteration tuned to "typical DRAM access latency (50-100 ns)" | The same arithmetic Igneum uses (128 dependent reads per hash against the card's random-read ceiling), from the design that held longest | +| Condrey, "PoSME", arXiv Apr 2026 (single author, not peer reviewed) [L4] | Latency-bound pointer chasing with hash compute under 3.5% of the step cost; GPUs 14x to 19x slower than a consumer CPU | The only dedicated latency-bound PoW paper found; its GPU-vs-CPU gap is the cost RandomX pays, and the cost Igneum avoids by keeping thousands of loads in flight per card | + +The paper that does not exist: nothing found treats cache timing as a feature; Catena and Argon2i treat it as a leak. No peer-reviewed survey of ASIC resistance as such surfaced; the nearest are Cho's 2018 multi-hash evaluation [P22] (X11-style resistance "is not strong enough"), Feng and Luo's 2020 three-processor study [P23] (GPUs dominate CryptoNight, Ethash and Cuckoo on CPU, GPU and Xeon Phi) and Yaish and Zohar's 2023 pricing of mining hardware as a bundle of options [P24]. + +## 3. The lessons + +Each lesson is stated once, with the rows it comes from. + +1. **Compute-bound work loses by 30x to 1,000x within two years, whatever its shape.** Chains of eleven hashes (row 2), sixteen hashes in a random order (row 9), a 64x64 matrix multiply (row 23), a new sponge (row 27), a signature per attempt (row 24): every one got a chip, the random-order chain at 1.3x by an FPGA within 20 months and the rest at 33x to 1,100x. Multiplying the number of fixed functions multiplies the chip's die, not its difficulty. For Igneum: nothing in the program's ALU work is a defence and the design already says so (ledger M1); the defence is the memory path. + +2. **Memory at SRAM scale is compute-bound with extra steps.** Scrypt's 128 KB (row 1) and CryptoNight's 2 MB (row 16) were sized to a 2011 and a 2014 CPU cache; a chip put the same memory on die and won 19x and 40x. Igneum's answer is the 256 MiB cache growing with the dataset (option C) and the 1 GiB to 2 GiB dataset; section 2.5 shows the cache size also sets the chip's node and therefore its project cost. The hot table (layer 5) was a step back toward SRAM scale, and the measurement agreed (the honest card paid 7% to 16%, the chip paid $0.23 per MB). + +3. **Bandwidth-bound work gets a memory chip at 2x to 5x.** Ethash held 32 months and then got chips whose whole design was the memory system: DDR3 (E3, no gain), GDDR6 (A10 Pro, 1.2x), custom controllers (Linzhi, 2.1x), on-package memory (Jasminer X4, 4.8x) (rows 3, 4). Rao's audit explains why in energy terms: the chip moves the same bytes at lower energy per byte, and a split-die design holding the DAG was already cheaper than a GPU board in 2019. Ren and Devadas give the bound: a chip's energy advantage on a bandwidth-hard function is the ratio of its memory energy per bit to the GPU's. Igneum's rule "avoid leaning on bandwidth" is right; its model has no row for this chip (section 4.3, addition 1). + +4. **Latency-bound and random-program work held longest, and the prize was usually small.** CryptoNight's latency bound at SRAM scale held 43 months, then fell to secret chips (row 16). RandomX's at DRAM scale held 46 months to parity hardware and about 75 to a 2x to 3x chip (row 17, approximate), on the largest prize any resistant hash has carried. ProgPoW's adopters have had no chip in 8 years on small prizes (rows 10, 12, 20). Verthash, Autolykos, Octopus and FishHash have none on small prizes (rows 8, 21, 22, 26). The honest reading: the random program on a DRAM-latency bound is the strongest construction the history has, and nobody has tested it at Ethereum's prize. + +5. **Periodic human forks fail as a defence.** Monero: four forks in 20 months; chips were back at 85% of the hashrate within four months of the v8 fork (row 16), and Vorick wrote that a chip able to survive forks at under a 5x hit had been designed. Vertcoin: three forks, two followed by rented-hash 51% attacks within weeks, because each fork reset the hashrate to a rentable size (row 7). Ravencoin: FPGA bitstreams for the new order within weeks (row 9). Sia: the fork bricked competitors' chips and left one vendor at 37% (row 14). Grin: the tweaks worked because the lane was scheduled to die (row 18). The chip's design cycle is 5 months for Bitmain (Vorick) and 13 for a startup; a fork every 6 months is a race the chip wins on the second lap, and each fork is a governance event. Igneum's draws are automatic and scheduled at genesis; that is the right side of this lesson, and section 4.3 asks whether the epoch can also be shorter than a bitstream (addition 5). + +6. **RandomX got the target right and the derivation right, and it costs GPUs 25x.** Right: the work is "code and data" so a fixed circuit cannot serve it; chained programs defeat filtering (0.44x); the dataset is above any SRAM die; the item derivation is a random superscalar program tuned to DRAM latency so the light-mode chip pays as much energy per item as a DRAM read (section 2.4). The cost: a GPU runs the VM at 25x worse per joule than a CPU (row 17), which is the cost Igneum refuses, and the reason the program is per hour and compiled. What Igneum did not take: the random item derivation (addition 2) and four external audits before launch (addition 3). + +7. **ProgPoW got the datapath right and lost on governance and one unpriced attack.** Right: target the commodity hardware's whole datapath (random math, register file, cache reads, DAG loads) so a chip has to be a GPU; the hardware audit agreed for compute-only chips (1.1x to 1.2x, Rao). Unpriced: the DAG on die (Least Authority suggestion 2, Rao's "<< 0.1x"), the same attack Igneum calls M16. Not adopted: two tentative approvals, a petition, bugs found late (Kik), authorship disputes and a PoS roadmap; the change needed a contentious fork on a live chain (row 20). Igneum's lesson is the one it already follows: every layer goes in before the public testnet as a genesis rule or a reserve, so no adoption vote is ever needed. + +8. **What a "GPU-friendly" chain lost when its GPU miner fell behind: the miners, then the chain's shape.** Kaspa's hashrate rose 7x in months and its GPU share went to nothing; seven forks left to re-resist (row 23). Alephium, Nervos, Handshake, Kadena and Radiant went the same way without the forks (rows 25, 27, 28, 15, 29). Iron Fish forked away from its own Blake3 within a year of the first box (row 25). The chains kept their security budget and lost the fleet that had launched them; the fleet's hardware went to the next GPU chain. For Igneum the metric is the share of hashrate on consumer cards by model, which is what the January 2027 benchmark should report and what the observer can estimate earlier (addition 4). + +9. **An unreviewed memory-hard construction has a shortcut until someone looks.** MTP fell from 2 GB to under 1 MB before launch (row 11); Catena's proofs were flawed; Argon2i's parameters were attackable at the IRTF's "paranoid" setting; Cuckoo's memory claim was off by 50x within two months (section 2.3). Igneum's M_r and chained cache have had no cryptanalysis (`MEMHARD.md` section 3, ledger M7); the acceptance rule is a statistical filter, not a proof. The x8 decision multiplies the mixer's weight in the chip model, which multiplies the cost of a structural weakness in it (addition 3). + +10. **The secret chip is found by its share, and it is on the chain before the announcement.** Monero's chips held 85% before anyone saw them; a nonce-pattern analysis found them (row 16). Zcash's Z9 was "5x to 10x below" what Obelisk's own study said the hash allowed, which is Vorick's evidence that better secret chips existed (section 1.1 row 5). A detector costs an observer query; a bounty costs escrow (addition 4). + +## 4. The audit of Igneum against the history + +### 4.1 The first-generation layers (live in class v2 and carried into v3) + +| Layer | Answers which failure | Does not answer | Evidence | +|---|---|---|---| +| Random program per epoch from a VDF seed, 12 integer families, nonce-dependent select | Fixed-function chips (lesson 1); program filtering and seed grinding (RandomX's 0.44x, spec 04's 130-to-1) | A "GPU without graphics": a programmable sequencer over 12 ops and 8 registers (ledger M1); an FPGA overlay or bitstream compiled within the hour (rows 7, 9: FPGAs were the first adversary of Lyra2REv2 and X16R); the 7.5x AMD gap is a one-vendor fleet | Rows 9, 10, 16, 17, 20 | +| Weak-program acceptance, exact 16 loads, fresh-source rule | Per-program hash-rate spread (1.10x residual) that a chip could pick | Nothing it claims to; a chip's advantage cannot come from the program (status 20:16) | Census [I1] | +| 1 GiB to 2 GiB dataset of 4-byte random reads, latency-bound, cache 256 MiB | SRAM-scale memory (lesson 2); the bandwidth lever at the honest card (lesson 3: 128 x 4 B keeps the 5090 at 9% of its stream bandwidth) | The partial-store chip with a custom memory system (lesson 3); the time-memory curve (O-1.6) | Rows 1, 3, 16; [P13] | +| 8 dependent cache reads per item, fixed-shape mixer with drawn constants | The on-die recompute chip (Least Authority's light-evaluation attack), priced at 2.45x bare under v2 | The fixed shape gives the chip its 3x factor (lesson 6); no cryptanalysis (lesson 9) | [S69] [S70]; M16 | +| Era draws from chain state (op weights, fold rotations), reserve families by height, dataset growth, no human release | Fork fatigue and fork-reset attacks (lesson 5); the chip that "survives forks at under 5x" (Vorick) is the chip that the draws are meant to outlast | The draws touch the program, not the item derivation, so they cost the recompute chip nothing (`chip-model-v3.md` section 2) | Rows 7, 16, 18 | +| Warp-unit CPU verification without the dataset (2.1 ms per warp under x8) | Keeps the verifier light, the Equihash and Cuckoo goal | Caps every lever: the mixer budget stops at the 10 ms gate | [P16] [P19] | + +### 4.2 The Counter ASIC 2.0 layers as decided tonight + +| Layer | Decision (status file) | Answers | Does not answer | History's verdict | +|---|---|---|---|---| +| 1 Load width 4, 16, 64 B | Keep 4 B (w16 closes nothing) | Keeps the 5090 latency-bound (9% of stream) | The AMD 7.5x gap (2.4 G reads/s at every width) | Right by lesson 3; the vendor gap is a 3.0 question and a soft form of lesson 8 | +| 2 Per-program width mix | Out (spread over 5% on every card) | n/a | n/a | Right: a per-program spread is what a chip picks (lesson 1's X16R: randomised order gave 1.3x, the shape still fixed) | +| 3 Per-warp scratch with RMW | Out (does not move the recompute chip; 2.4x at every share; costs GPUs 12% to 48%) | n/a | n/a | Right: SRAM-tier work favours the chip (lesson 2; Rao: SRAM is the chip's weapon) | +| 4 + 8 Era layout (stride, interleave) and per-site windows | In (era inside the class) | A hard-wired layout tuned to one era | A programmable address decoder (era-layout.md section 8 says so); costs the recompute chip nothing | Small by itself; its value is in lesson 5 (automatic change without a fork) | +| 5 Hot table sized to GPU cache | Measured, not adopted (honest card pays g = 0.84 to 0.93; chip pays SRAM) | n/a | n/a | Right by lesson 2 | +| 6 Cache growth | Option C: doubles with the dataset (256 MiB, 512 MiB year 4, 1 GiB year 12) | Keeps the mirror on a leading node (section 2.5) | n/a | Right; RandomX's cache-to-dataset ratio of 8 is reached at year 4 | +| 7 INT8 matrix family | Reserve R1 = mm8, W_new 4, unlock era 4 or 90% signal | A family that a 12-op chip lacks | Matrix hardware is the most abundant custom silicon on earth; Least Authority's suggestion 5 was "watch ML hardware"; Apple's emulation costs 1.6x to 4.7x per op | Keep in reserve, order it last (addition 6) | +| 9 Epoch length as an era parameter (10 min to 2 h) | Reserve only, design on `ca2-epoch` | The bitstream-per-epoch FPGA (rows 7, 9) | The FPGA overlay (a soft GPU) and the HBM FPGA | Rank it up (addition 5) | +| Mixer x8 (M16's lever) | In: 0.31x bare, 0.92x with the 3x factor, verifier 2.1 ms per warp, daily build 23 to 77 ms | The on-die recompute chip (lesson 6, Least Authority's attack) | Its own fixed shape (the 3x factor stays) and its lack of review (lesson 9) | The right lever; additions 2 and 3 are what the history says to do to it next | + +### 4.3 Ranked additions and upgrades + +Ranked by how much the history says each would change the outcome, with the cost to GPUs and the risk. "Genesis" means a rule fixed before the public testnet; "reserve" means a named family or parameter in the genesis reserve, unlockable by height or 90% signal; "nowhere" means do not add. + +| Rank | Addition | What it does | Evidence | Cost to GPUs | Risk | Where | +|---|---|---|---|---|---|---| +| 1 | **Price the partial-store chip and draw the time-memory curve.** A chip that stores a fraction f of the dataset in HBM or on many narrow DRAM channels, recomputes the rest from a 256 MiB on-die cache under x8, and reads with 4-byte granularity. Rows for f = 0.25, 0.5, 1 at HBM3 and at GDDR7 random-read rates, priced in energy per hash (Rao's metric) and in reads in flight per watt | The only chip class that beat a memory-bound GPU hash: Ethash's 2.1x to 4.8x came from the memory system with no on-die dataset (rows 3, 4); Rao priced a 16-die DAG holder under a GPU board in 2019; Cuckoo's curve was wrong by 50x until drawn [P20]; O-1.6 is open and `MEMHARD.md` section 3 item 2 says the curve was never drawn | None (analysis) | The row may come out over 2x, which would qualify the public claim before anyone else does | Genesis (before the vectors freeze) | +| 2 | **A random item-derivation program per day** in place of the fixed-shape mixer: a SuperscalarHash-style generator, integer only, drawn from the day key, with its own acceptance test, compiled once a day by miners and verifiers | RandomX's reason for SuperscalarHash: a fixed derivation is hard-wired by a chip; a random one makes the light-mode chip a CPU (section 2.4). In Igneum's model the fixed shape is the 3x factor that turns 0.31x into 0.92x; removing the factor is worth more than x8 to x16 would be (x16: 0.46x with the factor by M16's table) | None per hash (the daily build is 23 to 77 ms at x8 and would roughly double); the verifier needs a per-day compiled derivation (a JIT, or a round schedule drawn from a fixed set of reviewed rounds), measured against the 10 ms gate | Cryptanalysis of random ARX programs; weak draws; a JIT in the verifier is new attack surface; the vendors must agree bit-exactly on a program they compile | Reserve (named family, unlock by height or signal) now; genesis if the verifier cost is measured under the gate before the freeze | +| 3 | **External cryptanalysis of M_r, the chained cache and the acceptance rule before genesis**, with the x8 shape as the target | Lesson 9 (MTP, Catena, Argon2i, Cuckoo); RandomX bought four audits for $141,000 before launch [S60]; the x8 decision multiplies the mixer's weight in the chip model, so a shortcut inside the mixer is now worth 8x more to a chip | None | Finding something late moves the vectors; not finding it in time moves nothing | Genesis gate (ledger M7, raised in priority) | +| 4 | **The clock and the detector.** (a) A share-pattern detector on the observer: per-program hash-rate spread, nonce-group patterns and per-card-model rate bands, with an alert when a population behaves like one fixed design (MoneroCrusher's method); (b) a stated trigger: the bounty escrowed and the benchmark live before daily issuance crosses about $50K (Vorick's rule), not on a calendar date | Lesson 10 (85% secret share); section 2.5's table (chips at $20K to $30K a day on compute-bound hashes); D11 (the bounty is unfunded) | None | A detector with false positives; a trigger the project lead has to fund | Not a layer; genesis-independent; do it before the public testnet | +| 5 | **Rank layer 9 (the epoch length) up, and measure the FPGA lane**: the compile-ahead cost per card at a 10-minute epoch (the `ca2-epoch` work), plus an estimate of a soft-overlay FPGA miner with HBM (reads in flight per watt against the 5090's 17.5 G/s) | FPGAs were the first adversary of Lyra2REv2 and X16R and came back within weeks of X16Rv2 (rows 7, 9); Xelis forked for FPGA resistance (row 30); a per-hour program is a bitstream target in a way a per-hash program is not | At 10-minute epochs: 6x the compile work per card (measured on `ca2-epoch`); the VDF lead shrinks | A short epoch moves the difficulty window (spec 1.12) and the seed path | Reserve (as decided), with the measurement before the public testnet | +| 6 | **Order the reserve by chip-unfriendliness**: families that force a full 32-bit datapath per lane first (byte permute, bit-field extract, variable shifts, popcount, select, the second shuffle form), mm8 last | Least Authority's "watch ML hardware"; int8 matrix blocks are licensable IP at every node; Apple pays 1.6x to 4.7x per emulated dot4 (status 20:38) | None at launch | None | Reserve ordering, genesis | +| 7 | **A vendor-share metric and a 3.0 target for the AMD gap**: the share of hashrate by vendor published with the benchmark, and the line-width question kept open as the plan says | Lesson 8: a one-vendor fleet is a softer version of chip capture; Equihash's NVIDIA tilt and Ethash's balance were part of each chain's miner politics (rows 3, 5) | n/a | A width that closes the gap makes the 5090 bandwidth-bound (status 20:27) | Counter ASIC 3.0 | + +Checks the history suggests that are not layers: + +| Check | Why | Source | +|---|---|---| +| 1. Header grinding for cache locality: can a miner search the pre-PoW header hash H for 32-lane groups whose 128 loads cluster into fewer DRAM rows or cache lines, at a search cost below the gain? | Kik's ProgPoW exploit and Dinur-Nadler's MTP attack were both "the attacker steers the addresses" | [S71] [P18] | +| 2. The chip detector's baseline: the per-program spread per card model, from the first week of the public testnet | Needed before addition 4(a) can alert | [S54] | +| 3. The 28 nm SRAM density figure in section 2.5 (approximate, from memory) and the node-cost consequence, cited properly | It is the argument that the cache size sets the chip's project cost | [E10] | + +Evaluated and placed nowhere, with the reason: + +| Candidate | Verdict | Reason | +|---|---|---| +| Program entropy per hash (RandomX) instead of per hour | Nowhere | Per-hash programs need an interpreter or JIT on the GPU, which is the 25x GPU penalty RandomX pays (row 17) and the reason Igneum compiles per epoch. The filtering attack per-hash chaining prevents is already closed by the VDF seed and the acceptance rule. Per-hour's residual exposure is the FPGA lane, which addition 5 addresses with a shorter epoch, not with per-hash programs | +| Superscalar dependency-chain design for the program itself | Nowhere, beyond what exists | The program's ALU work is not the defence (lesson 1); the dependency chain that matters is the 8 dependent cache reads per item and the 128 dependent loads per hash, both in place. The superscalar idea belongs in the item derivation (addition 2) | +| Verthash's table from the blockchain; a dataset derived from chain history | Nowhere | Against the recompute chip and the partial-store chip it changes nothing: both build the table from the same public inputs the GPU does. The day key already comes from a VDF of chain state, which gives the unpredictability without a history dependency; a history dependency costs the verifier the history (Verthash needs the headers) and ties the hash to pruning (spec 10) | +| Grin's dual PoW with a shifting split | Nowhere | It is a scheduled surrender (row 19). Igneum's automatic schedules (dataset growth, reserve unlocks, cache doubling) are the shifting split applied to one hash; a second lane would hand a chip a lane | +| Autolykos v1's non-outsourceability | Nowhere | It stops pools, not chips, and Ergo removed it after 19 months because contract pools bypassed it (row 21); Igneum needs pools (spec 09) | +| A per-hash VRF against nonce grinding | Nowhere | A signature per attempt is what NexaPow did and it got a 3x to 4x chip (row 24): EC arithmetic is fixed-function work. The grinding Igneum must guard is the header-locality search (check 1), which a VRF does not touch | +| Ternary or variable-precision integer ops | Nowhere, beyond the reserve | Every family must be bit-exact on three vendors; dot4 is native on NVIDIA and AMD and emulated on Apple at 1.6x to 4.7x (status 20:38), so each precision added is paid by the weakest vendor. The reserve already holds the integer-exact candidates; adding more does not change lesson 1 | +| Cache-timing-bound reads (ProgPoW's 16 KB cache, RandomX's L1 tier) | Nowhere | Measured out tonight at the L2 tier (layer 5) and the per-warp tier (layer 3): the honest card pays and the chip buys SRAM at $0.23 per MB. Rao's audit says the same about ProgPoW's cache reads | +| Divergent data-dependent branches | Nowhere (already excluded) | Branches cost a GPU divergence and a chip nothing; RandomX's single predictable branch targets speculative CPUs, which Igneum does not have | +| Floating point | Nowhere (already excluded) | Vendor rounding splits the chain (spec 1.14); RandomX could afford it because its target is one ISA family with IEEE semantics | + +## 5. Decisions this raises for the project lead + +| # | Decision | Recommendation | +|---|---|---| +| 1 | Add the partial-store chip rows to `chip-model-v3.md` and draw the time-memory curve before the public testnet | Yes, before the vectors freeze (addition 1) | +| 2 | Name a random item-derivation program as a reserve family, and fund the verifier measurement that would move it to genesis | Reserve now; genesis if the verifier lands under the gate (addition 2) | +| 3 | Commission the external cryptanalysis of M_r and the chained cache before genesis, with the x8 shape as the target | Yes (addition 3; ledger M7) | +| 4 | Escrow the bounty and set its trigger to daily issuance, not to a date; build the share-pattern detector on the observer | Yes to the detector now; the escrow is the project lead's (D11) | +| 5 | Rank the epoch-length reserve above the mm8 reserve, and measure the FPGA lane | Yes (additions 5 and 6) | + +## 6. Sources and limits of this research + +Research was gathered by four sub-agents between 20:10 and 20:45 UTC on 5 October 2026 and checked against the citations below. Fetch failures they reported: eprint.iacr.org PDFs sit behind a challenge page (abstract pages worked), so the Ren-Devadas energy figures, the Alwen-Blocki EuroS&P tables and the Lyra2 exponent come from abstracts; medium.com and bitcointalk.org returned 403 (Vorick's post was read through archive.sia.tech and secondary coverage; the IfDefElse posts through the Veil interview); Bitmain's prospectus PDF was blocked; the Dash iBeLink date and Octopus's "matrix step" are unverified; the 28 nm SRAM bit cell is from memory. Every hash-per-joule gain is derived from the cited rate and watt figures and is approximate. + +Igneum sources: [I1] `docs/analysis/weak-program-census-2026-10-03.md`; `docs/plans/counter-asic-2.md` (be4b295); `docs/plans/counter-asic-2-status.md` and `docs/plans/counter-asic-2-rollout.md` (`ca2-coord`); `docs/analysis/chip-model-v3.md` (`ca2-mixer` 1ab8b21); `docs/analysis/m16-recompute-attacker-2026-10-05.md`; `docs/analysis/sram-mirror.md` revision 2 (`ca2-analysis`); `docs/plans/era-layout.md` (`ca2-era`); `docs/plans/hot-table.md` (`ca2-cache`); `docs/plans/read-width.md` (`readwidth`); `docs/spec/01-lottery-hash.md`, `04-seeds-and-vdf.md`; `docs/bench-log.md` ("the 9070 XT on the eGPU", `opencl-rdna4`); `proto-metal/MEMHARD.md`; `docs/fud-ledger.md` M1, M3, M7, M16, C2, D11. + +History rows: +- [S1] https://medium.com/@Linzhi/what-is-memory-hard-45a363b59dfe (Tenebrix's 2011 claim); https://en.wikipedia.org/wiki/Litecoin +- [S2] https://jamesachambers.com/early-bitcoin-asic-miner-pictures-history/ (Gridseed); https://www.mikewesson.com/2013/04/29/mining-litecoin-on-ati-radeon-7970s/ (7970 at 700 kH/s) +- [S3] https://www.design-reuse.com/news/34403/innosilicon-28nm-litecoin-asic-reference-miner.html (A2, Apr 2014); https://www.coindesk.com/markets/2014/05/14/kncminer-reveals-additional-titan-scrypt-asic-specs (Titan) +- [S4] https://www.asicminervalue.com/miners/bitmain/antminer-l3-504mh ; https://cryptoage.com/en/2550-bitmain-antminer-l7-is-a-new-asic-miner-for-litecoin-and-dogecoin.html ; https://www.coindesk.com/markets/2014/09/11/dogecoin-community-celebrates-as-merge-mining-with-litecoin-begins +- [S5] https://docs.dash.org/en/stable/docs/user/introduction/features.html ; https://www.dash.org/news/happy-birthday-darkcoin/ +- [S6] https://cryptomining-blog.com/7117-the-first-x11-mining-asic-ibelink-dm384m-asic-dash-miner/ ; https://cryptomining-blog.com/7493-power-usage-and-noise-of-the-ibelink-dm384m-x11-asic-miner/ ; https://bitcointalk.org/index.php?topic=854257.320 (R9 280X at 4 MH/s) +- [S7] https://99bitcoins.com/guides-and-tutorials/dash-mining/antminer-d3-review/ ; https://www.cryptocompare.com/mining/asic-miner-market/baikal-giant-x10-x11-10ghs/ +- [S8] https://ethereum.org/developers/docs/consensus-mechanisms/pow/mining/mining-algorithms/dagger-hashimoto/ +- [S9] https://cryptoslate.com/bitmain-e3-asic-ethereum-miner/ (4 Apr 2018: E3 4.44 W/MH against a tuned 1080 Ti and RX 570); https://hothardware.com/news/bitmain-launches-ethereum-asic-miner-hashrate-comparable-8-gtx-1080-gpus +- [S10] https://innosilicon.global/product/innosilicon-a10-pro-6gb-ethereum-miner-500-mh-s/ ; https://www.notebookcheck.net/The-NVIDIA-GeForce-RTX-3080-is-an-Ethereum-mining-monster-overclocked-cards-deliver-nearly-100-MH-s-double-the-Radeon-RX-5700-XT.494246.0.html +- [S11] https://www.coindesk.com/tech/2020/12/21/linzhi-begins-rollout-of-long-awaited-ethereum-miner-phoenix ; https://www.theblock.co/post/88622/questions-new-ethash-asic-ethereum (F2Pool: 2,733 MH/s at about 3,000 W) +- [S12] https://miningnow.com/asic-miner/jasminer-x4-2500mh-s/ ; https://www.asicminervalue.com/miners/bitmain/antminer-e9-2-4gh ; https://2miners.com/blog/asic-miners-for-ethereum-antminer-e3-vs-innosilicon-a10-eth-master-comparison/ (the 3% estimate) +- [S13] https://eips.ethereum.org/EIPS/eip-1057 ; https://www.theblock.co/news/ecosystems/2020-02-26-ethereum-community-members-submit-dissenting-progpow-petition-57061 ; https://ethereum.org/roadmap/merge/ ; https://cointelegraph.com/news/bitmains-antminer-e3-to-continue-mining-ether-with-new-update (the E3's 4 GB limit) +- [S14] https://ethereumclassic.org/blog/2020-11-27-thanos-hard-fork-upgrade/ +- [S15] https://www.asicminervalue.com/miners/bitmain/antminer-e9-pro-3-68gh ; https://pool.kryptex.com/device/asic/jasminer/x16-p ; https://whattomine.com/coins/151-eth-ethash/asics +- [S16] https://eprint.iacr.org/2015/946 (Equihash); https://en.wikipedia.org/wiki/Zcash +- [S17] https://variance.hu/2017/05/08/748-solsec-zcash-equihash-teljesitmeny-egy-gtx-1080-ti-kartyabol/ (1080 Ti at 748 Sol/s) +- [S18] https://www.coindesk.com/markets/2018/05/03/bitmains-latest-crypto-asic-can-mine-zcash ; https://coinguides.org/innosilicon-a9-zmaster-50k-sols-equihash-asic/ ; https://support.bitmain.com/hc/en-us/articles/360012223994-Z9-Specifications ; https://www.asicminervalue.com/miners/bitmain/antminer-z11 ; https://d-central.tech/miners/antminer-z15/ +- [S19] https://github.com/ZcashFoundation/zfnd/blob/master/_posts/blog/2018-05-08-statement-on-asics.md ; https://www.coindesk.com/tech/2018/06/28/zcash-votes-against-asic-resistance-in-boon-for-big-miners ; https://electriccoin.co/blog/ecc-roadmap-calls-for-focus-on-wallet-proof-of-stake-and-interoperability/ +- [S20] https://blog.horizen.io/zencash-statement-on-double-spend-attack/ ; https://blog.horizen.io/horizen-zen-statement-on-mining-algorithm/ ; https://forum.zcashcommunity.com/t/list-of-all-coins-projects-on-equihash-asic-resistant-not-resistant/29085 +- [S21] https://en.wikipedia.org/wiki/Bitcoin_Gold ; https://gist.github.com/metalicjames/71321570a105940529e709651d0a9765 +- [S22] https://fluxofficial.medium.com/zels-custom-pow-algorithm-zelhash-activation-in-mid-june-ad3d14d72135 ; https://uploads-ssl.webflow.com/60c73eaed3399e074029d643/60fd7d883200fcde5ceb7049_ZelHash_v1.0.pdf +- [S23] https://github.com/BeamMW/beam/wiki/BEAM-Mining ; https://docs.beam.mw/BeamHashII.pdf ; https://medium.com/minerstat/beamhashiii-beam-forks-to-a-new-algorithm-at-block-777777-dd2aeacc9e5 +- [S24] https://aion.theoan.com/blog/aion-mainnet-launch-kilimanjaro/ ; https://miningpoolstats.stream/zero +- [S25] https://vertcoin.io/history/ ; https://www.newsbtc.com/2014/12/01/vertcoin-introduces-new-pow-algorithm-promises-asic-free-features/ +- [S26] https://cryptoage.com/en/1231-first-asic-miner-lyra2rev2-dayun-zig-z1.html ; https://www.asicminervalue.com/miners/dayun/zig-z1 ; https://whattomine.com/gpus/36-nvidia-geforce-gtx-1080-ti ; https://arxiv.org/pdf/1905.08792 (FPGA bitstreams) +- [S27] https://cryptobriefing.com/vertcoin-vtc-51-percent-attack/ ; https://en.wikipedia.org/wiki/Vertcoin ; https://www.fxstreet.com/cryptocurrencies/news/vertcoin-cryptocurrency-network-fell-victim-to-attack-51-201912030704 +- [S28] https://github.com/vertcoin-project/vertcoin-core/releases/tag/0.14.0 (Lyra2REv3) +- [S29] https://soundcloud.com/vertcoin-talk/vertcoin-talk-episode-24-verthash-fork-happens-january-30th-2021 ; https://crazy-mining.org/en/software/wallets/vertcoin-vtc-instructions-for-mining-on-verthash/ +- [S30] https://coincub.com/mining/how-to-mine-vertcoin-vtc/ +- [S31] https://ravencoin.org/assets/documents/X16R-Whitepaper.pdf ; https://tronblack.medium.com/ravencoin-asic-thoughts-e6c0079609e6 +- [S32] https://cryptoage.com/en/1782-asics-ow-miner-ow1-and-skc-miner-turing-r1-for-the-x16r-algorithm-exist.html ; https://en.cryptonomist.ch/2019/09/17/mining-ravencoin-hashrate/ +- [S33] https://en.cryptonomist.ch/2019/10/02/ravencoin-rvn-hard-fork/ ; https://cryptomining-blog.com/11320-ravencoin-rvn-getting-fpga-mining-support-for-the-x16rv2-algorithm/ +- [S34] https://medium.com/minerstat/kawpow-ravencoin-forks-to-a-new-algorithm-2e730cd09fb3 ; https://tronblack.medium.com/ravencoin-kawpow-expectations-a6a063df58f2 +- [S35] https://github.com/RavenProject/Ravencoin/blob/master/roadmap/README.md ; https://whattomine.com/coins/234-rvn-kawpow/gpus ; https://miningreturns.com/learn/ravencoin-mining-guide +- [S36] https://www.neoxa.net/whitepaper/ ; https://woolypooly.com/en/blog/ravencoin-algorithm +- [S37] https://arxiv.org/pdf/1606.03588 (Egalitarian computing, MTP) +- [S38] https://eprint.iacr.org/2017/497 (Dinur and Nadler); http://blog.zorinaq.com/attacks-on-mtp/ ; https://firo.org/2017/07/21/mtp-audit-and-implementation-bounty.html +- [S39] https://firo.org/2018/12/05/mtp-faq-all-you-need-to-know.html +- [S40] https://firo.org/2021/10/01/firopow-and-instantsend-release.html +- [S41] https://firo.org/2025/11/19/hardfork-successful-nov-2025.html +- [S42] https://docs.decred.org/research/blake-256-hash-function/ +- [S43] https://www.asicminervalue.com/miners/innosilicon/d9-decredmaster ; https://www.asicminervalue.com/miners/obelisk/dcr1 ; https://crypto.news/hardware-companies-are-launching-dedicated-asic-miners-for-decred/ (DCR1 at 28 nm) +- [S44] https://cryptoage.com/en/1254-bitmain-antminer-dr3-7,8-th-s-on-the-algorithm-blake-14r-decred.html ; https://medium.com/luxor/bitmain-antminer-dr5-decred-setup-guide-1c417f5f61fc +- [S45] https://1stminingrig.com/antminer-a3-review-bitmain-surprises-everyone-with-this-new-siacoin-miner/ ; https://medium.com/obelisk-blog/obelisk-update-may-june-2018-260fce12a825 ; https://www.eastshoremining.com/tutorial-innosilicon-s11-siamaster-3-83th-siacoin-miner/ +- [S46] https://www.coindesk.com/markets/2018/10/19/sia-network-releases-hard-fork-code-to-block-crypto-mining-giants ; https://siasetup.info/learn/forks +- [S47] Vorick, "The state of cryptocurrency mining", 13 May 2018: https://archive.sia.tech/the-state-of-cryptocurrency-mining-538004a37f9b (read through https://davidgerard.co.uk/blockchain/2018/05/14/from-sia-an-incendiary-post-on-the-state-of-cryptocurrency-mining-in-2018/ and https://zycrypto.com/asic-manufacturer-shares-important-information-for-token-creators-and-miners/); Bitmain's reply https://blog.bitmain.com/en/bitmain-sia-state-cryptocurrency-mining/ +- [S48] https://medium.com/kadena-io/kadena-public-blockchain-releases-fully-public-testnet-v3-hashing-algorithm-and-mining-api-e230a51c7b26 ; https://www.coindesk.com/markets/2019/11/04/kadena-goes-live-announces-new-token-sale-aiming-for-20-million +- [S49] https://www.asicminervalue.com/miners/goldshell/kd5 ; https://asicmarketplace.com/product/goldshell-kd2-kadena-miner-6-4-th-s/ ; https://asicmarketplace.com/product/bitmain-antminer-ka3-kadena-miner-166th/ +- [S50] https://minerstat.com/hardware/nvidia-rtx-3080-lhr +- [S51] https://bytecoin.org/old/whitepaper.pdf ; https://docs.getmonero.org/proof-of-work/cryptonight/ ; https://en.wikipedia.org/wiki/CryptoNote +- [S52] https://news.8btc.com/bitmain-to-release-antminer-x3-cryptonight-asic-miner-with-220-khs-hashrate ; https://bitcointalk.org/index.php?topic=3127974.0 ; https://www.asicminervalue.com/miners/baikal/bk-n ; https://cointelegraph.com/news/bitmain-announces-new-monero-mining-antminer-x3-cryptos-devs-say-will-not-work +- [S53] https://github.com/monero-project/monero/pull/3253 (v7); https://coinguides.org/monero-network-upgrade-v8-cnv2-beryllium-bullet/ ; https://github.com/SChernykh/CryptonightR (CN-R: chip latency up 2.5x) +- [S54] https://medium.com/@MoneroCrusher/analysis-more-than-85-of-the-current-monero-hashrate-is-asics-and-each-machine-is-doing-128-kh-s-f39e3dca7d78 ; https://beincrypto.com/hashrate-analysis-reveals-asics-account-for-85-of-monero-mining/ +- [S55] https://github.com/tevador/randomx (30 Nov 2019) +- [S56] https://github.com/tevador/RandomX/blob/master/doc/design.md +- [S57] https://github.com/tevador/RandomX/blob/master/doc/specs.md +- [S58] https://xmrig.com/benchmark/5kFcJv (3950X); https://whattomine.com/coins/101-xmr-randomx/gpus (RTX 3090 at 2.0 kH/s, 290 W) +- [S59] https://bt-miners.com/products/bitmain-antminer-x5-monero-miner-212k-bt-miners/ ; https://bitmain.com.vc/news/bitmain-launches-antminer-x9 ; https://pineconeinibox.shop/product/pinecone-matches-inibox-r1x-xmr-edition/ ; https://github.com/xmrig/xmrig/blob/master/doc/ALGORITHMS.md ; https://rfc.tari.com/RFC-0131_Mining ; https://www.theblock.co/post/353240/tari-privacy-network-merged-monero-mining-launch-mainnet +- [S60] https://github.com/tevador/RandomX/blob/master/README.md (the four audits and their cost); https://blog.trailofbits.com/2019/07/02/state/ +- [S61] https://github.com/mimblewimble/docs/blob/master/docs/about-grin/proof-of-work.md ; https://github.com/tromp/cuckoo/blob/master/README.md ; https://github.com/tromp/cuckoo/blob/master/doc/cuckoo.pdf +- [S62] https://forum.grin.mw/t/mid-july-pow-hardfork-cuckaroo29-cuckarood29/5082 ; https://www.cudominer.com/grin-network-update-hard-fork-16th-january-2020/ ; https://forum.grin.mw/t/grin-v5-0-0-network-upgrade-hard-fork-4-january-2021/7895 +- [S63] https://docs.aeternity.com/aeternity-core-concepts/protocol/consensus-mechanisms/cuckoo-cycle-proof-of-work ; https://medium.com/cortexlabs/miners-can-now-test-mine-on-testnet-dolores-in-preparation-for-the-mainnet-launch-cf851d7b0146 +- [S64] https://forum.grin.mw/t/introducing-the-grn1-a-cuckatoo31-asic-from-obelisk/2519 ; https://medium.com/obelisk-blog/grn1-cancellation-announcement-54782c6e3e83 +- [S65] https://bitcointalk.org/index.php?topic=5219851.0 ; https://forum.grin.mw/t/innosilicons-grin-asics-canceled/6932 ; https://ipollo-miners.com/product/ipollo-g1/ +- [S66] https://forum.grin.mw/t/another-cuckatoo-bounty-succesfully-claimed/11739 (Apr 2025) +- [S67] https://eips.ethereum.org/EIPS/eip-1057 ; https://github.com/ifdefelse/ProgPOW +- [S68] https://github.com/ethereum/pm/blob/master/AllCoreDevs-EL-Meetings/Meeting%2052.md ; https://www.coindesk.com/markets/2019/01/04/ethereum-developers-give-tentative-greenlight-to-asic-blocking-code ; https://souptacular.github.io/2020-03-02-progpow-the-ethereum-community-speaks/ ; https://www.coindesk.com/tech/2020/03/06/ethereums-progpow-call-features-frustration-but-little-progress +- [S69] https://leastauthority.com/static/publications/LeastAuthority-ProgPow-Algorithm-Final-Audit-Report.pdf (9 Sep 2019) +- [S70] https://github.com/ethcatherders/progpow-audit ("Bob Rao - ProgPOW Hardware Audit Report Final.pdf", Sep 2019) +- [S71] https://github.com/kik/progpow-exploit (4 Mar 2020); https://github.com/Souptacular/linzhi (Linzhi's 3x to 8x claim) +- [S72] https://cryptoage.com/en/1238-bitcoin-interest-bci-and-new-mining-algorithm-progpow.html ; https://en.wikipedia.org/wiki/Zano_(blockchain_platform) ; https://github.com/sero-cash/serominer ; https://x.com/QuaiNetwork/status/1880037240149057759 ; https://veil-project.com/blog/2020-OhGodAGirl/ +- [S73] https://docs.ergoplatform.com/mining/autolykos/ ; https://ergoplatform.org/en/blog/2019_07_09_after_launch/ ; https://bytwork.com/en/news/khardfork-ergo-07 +- [S74] https://www.hashrate.no/gpus/3090/ERG +- [S75] https://mining.confluxnetwork.org/ ; https://github.com/Conflux-Chain/conflux-rust +- [S76] https://github.com/Conflux-Chain/CIPs/blob/master/CIPs/cip-102.md +- [S77] https://www.kaspafaq.com/sp_accordion_faqs/what-is-kheavyhash/ ; https://github.com/Dagmbisrat/Kaspa-FPGA-Miner +- [S78] https://whattomine.com/coins/352-kas-kheavyhash/gpus/49-nvidia-geforce-rtx-3090 +- [S79] https://www.cryptominerbros.com/product/iceriver-ks0-100gh-s-kas-miner/ ; https://www.asicminervalue.com/miners/iceriver/ks1 ; https://apextomining.com/product/new-bitmain-antminer-ks3-8-3t-3188w-kas-miner-asic-mining-machine-profitable-comining-soon/ ; https://www.asicminervalue.com/miners/bitmain/antminer-ks5-pro-21th +- [S80] https://hashdag.medium.com/kaspa-where-to-part-iv-last-c68717a8d309 (May 2023); https://miningreturns.com/news/kaspa-asic-mining-era-what-you-need-to-know +- [S81] https://x.com/karlsennetwork/status/1829148683104870534 ; https://www.hashrate.no/c/Algorithm_change_for_Karlsen_and_Pyrin ; https://github.com/spectre-project/rusty-spectre ; https://cryptix-network.org/whitepaper ; https://network.hoosat.fi/public/htn-whitepaper-2.pdf ; https://bugna.org/ +- [S82] https://spec.nexa.org/mining/NexaPOW/ +- [S83] https://www.cryptominerbros.com/product/dragonball-miner-a21-nexa-miner/ ; https://whattomine.com/coins/357-nexa-nexapow +- [S84] https://docs.alephium.org/frequently-asked-questions/ ; https://medium.com/@alephium/one-year-of-mainnet-b7ed5d3024ee +- [S85] https://hashrate.no/gpus/3090/ALPH +- [S86] https://www.asicminervalue.com/miners/goldshell/al-box ; https://mineshop.eu/bitmain-antminer-al1 ; https://www.zeusbtc.com/Asic-Miner/Asic-Miner-Details.asp?ID=3719 +- [S87] https://fips.ironfish.network/fips/fip-3-memory-hard-mining-algorithm ; https://fips.ironfish.network/fips/fip-10-hardfork-1 ; https://github.com/iron-fish/fish-hash ; https://github.com/karlsen-network/fish-hash-plus +- [S88] https://medium.com/nervosnetwork/a-decentralized-mainnet-launch-for-nervos-ckb-9cb119d15540 +- [S89] https://www.asicminervalue.com/miners/bitmain/antminer-k5-1130gh ; https://www.asicminervalue.com/miners/goldshell/ck5 ; https://2miners.com/blog/nervos-ckb-network-hashrate-increased-asics-are-the-cause/ +- [S90] https://www.coindesk.com/markets/2020/02/04/handshakes-uncensorable-web-domains-go-live-on-mainnet +- [S91] https://www.goldshell.com/news/goldshell-announces-best-handshakehns-miner-hs1-coming-soon/ ; https://www.asicminervalue.com/miners/goldshell/hs3 +- [S92] https://radiantblockchain.org/ ; https://d-central.tech/miners/rxd-rx0/ +- [S93] https://cryptoage.com/en/2929-video-card-hashrate-based-on-the-sha512-256d-algorithm-cryptocurrency-mining-radiant-rxd.html +- [S94] https://cryptomining-blog.com/11865-mining-zano-using-the-progpowz-proof-of-work-algorithm/ +- [S95] https://github.com/dynexcoin/DynexSolve ; https://minerstat.com/coin/DNX/faq +- [S96] https://docs.warthog.network/janushash/ ; https://github.com/CoinFuMasterShifu/Janushash +- [S97] https://docs.xelis.io/network-upgrades +- [S98] https://docs.verus.io/overview/verus-proof-of-power.html + +Papers: +- [P1] https://www.microsoft.com/en-us/research/publication/moderately-hard-memory-bound-functions/ +- [P2] https://www.wisdom.weizmann.ac.il/~naor/PAPERS/mem.pdf +- [P3] https://www.tarsnap.com/scrypt/scrypt.pdf +- [P4] https://eprint.iacr.org/2014/238 +- [P5] https://eprint.iacr.org/2016/115 +- [P6] https://eprint.iacr.org/2016/759 +- [P7] https://eprint.iacr.org/2016/989 +- [P8] https://www.cryptolux.org/images/d/d0/Argon2ESP.pdf +- [P9] https://eprint.iacr.org/2016/027 +- [P10] https://eprint.iacr.org/2015/227 +- [P11] https://eprint.iacr.org/2013/525 +- [P12] https://eprint.iacr.org/2015/136 +- [P13] https://eprint.iacr.org/2017/225 +- [P14] https://eprint.iacr.org/2018/221 +- [P15] https://eprint.iacr.org/2017/443 +- [P16] https://eprint.iacr.org/2015/946 +- [P17] https://arxiv.org/abs/1606.03588 +- [P18] https://eprint.iacr.org/2017/497 +- [P19] https://eprint.iacr.org/2014/059 +- [P20] https://da-data.blogspot.com/2014/03/a-public-review-of-cuckoo-cycle.html ; http://www.cs.cmu.edu/~dga/crypto/cuckoo/analysis.pdf +- [P21] https://arxiv.org/abs/1902.00112 +- [P22] https://ieeexplore.ieee.org/document/8516911/ +- [P23] http://www.vldb.org/pvldb/vol13/p898-feng.pdf +- [P24] https://arxiv.org/abs/2002.11064 + +Economics and silicon: +- [E1] https://europractice-ic.com/schedules-prices-2025/ +- [E2] https://newsletter.semianalysis.com/p/the-dark-side-of-the-semiconductor (24 Jul 2022) +- [E3] https://semiengineering.com/big-trouble-at-3nm/ (21 Jun 2018); https://semiengineering.com/the-increasingly-uneven-race-to-3nm-2nm/ (24 May 2021); https://semiengineering.com/what-will-that-chip-cost/ (30 Oct 2023) +- [E4] https://siliconanalysts.com/analysis/fabless-startup-tapeout-cost-guide (1 Mar 2026, secondary) +- [E5] https://patentpc.com/blog/chip-manufacturing-costs-in-2025-2030-how-much-does-it-cost-to-make-a-3nm-chip (secondary, approximate) +- [E6] https://techcrunch.com/2018/09/26/bitmain-hong-kong-ipo/ ; https://bitcoinmagazine.com/markets/bitmain-ipo-prospectus-reveals-offering-may-be-gamble-investors ; https://www.chaincatcher.com/en/article/2057998 +- [E7] https://www.sec.gov/Archives/edgar/data/1780652/000119312519297270/d773846d424b4.htm +- [E8] https://michaeltaylor.org/papers/bitcoin_taylor_cases_2013.pdf ; https://michaeltaylor.org/papers/Taylor_Bitcoin_IEEE_Computer_2017.pdf +- [E9] https://www.tomshardware.com/news/amd-unveils-more-ryzen-3d-packaging-and-v-cache-details-at-hot-chips (Aug 2021); https://www.graphcore.ai/posts/introducing-second-generation-ipu-systems-for-ai-at-scale ; https://www.theregister.com/software/2020/09/29/groq-is-hard-to-grok-but-reckons-its-ai-chips-roq-ex-googlers-unorthodox-design-now-shipping-to-customers/1170931 +- [E10] https://newsletter.semianalysis.com/p/tsmcs-3nm-conundrum-does-it-even (21 Dec 2022); https://fuse.wikichip.org/news/7343/iedm-2022-did-we-just-witness-the-death-of-sram/ ; https://www.tomshardware.com/news/no-sram-scaling-implies-on-more-expensive-cpus-and-gpus ; https://images.nvidia.com/aem-dam/Solutions/geforce/blackwell/nvidia-rtx-blackwell-gpu-architecture.pdf (GB202: 128 MB L2 on the full die, 96 MB on the RTX 5090, 750 mm^2) +- [E11] CoinMarketCap historical snapshots, https://coinmarketcap.com/historical/YYYYMMDD/ for the dates in the section 2.5 table (pulled 5 Oct 2026); HBM pricing https://www.trendforce.com/presscenter/news/20240506-12125.html and https://www.nextplatform.com/2024/02/27/he-who-can-pay-top-dollar-for-hbm-memory-controls-ai-training/ ; the E3's DDR3 and the A10's presumed GDDR6 from the ProgPoW FAQ https://medium.com/@ifdefelse/progpow-faq-6d2dce8b5c8b (Jan 2019, read through secondary coverage) and https://coingeek.com/memory-limitations-prompt-bitmain-antminer-e3-to-halt-etc-support/ + +Latency: +- [L1] https://terpconnect.umd.edu/~blj/papers/memsys2018-dramsim.pdf +- [L2] https://arxiv.org/abs/1712.08304 +- [L3] https://docs.nvidia.com/nsight-compute/ProfilingGuide/index.html ; https://developer.download.nvidia.com/video/gputechconf/gtc/2020/presentations/s21819-optimizing-applications-for-nvidia-ampere-gpu-architecture.pdf +- [L4] https://arxiv.org/abs/2604.15751 From a2f08e3f70177fc71a5e5868191b113d0566f4b5 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:38:22 +0000 Subject: [PATCH 139/158] prover loop: a PermissionDenied from the host names the root-owned GPU-server socket and the fix (the root-socket class); the plan's two times Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/src/prover.rs | 6 +++++- docs/plans/proving-v1.md | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/igneum-app/src/prover.rs b/app/igneum-app/src/prover.rs index 7799e7df..8f3e548e 100644 --- a/app/igneum-app/src/prover.rs +++ b/app/igneum-app/src/prover.rs @@ -539,7 +539,11 @@ fn loop_forever(shared: Arc, bin_dir: PathBuf) { let prover_env = if t.cuda { "cuda" } else { "cpu" }; let (ok, out) = run_tool(&shared, t, &t.host, &[fix_p, "--mode".into(), "compressed".into(), "--shard".into(), w.shard.to_string(), "--prover".into(), payout.clone(), "--out".into(), res_p], &[("SP1_PROVER", prover_env), ("RUST_LOG", "off")], Duration::from_secs(3 * 3600), &dir.join(format!("prove-{}-{}.log", w.number, w.shard))); if !ok || !results.exists() { - return Err(format!("prover: {}", out.lines().rev().find(|l| l.contains("RESULT") || l.contains("rror")).unwrap_or("failed"))); + let last = out.lines().rev().find(|l| l.contains("RESULT") || l.contains("rror")).unwrap_or("failed").to_string(); + // the root-socket class (5 October 2026, PC 2 at 20:00Z and 21:25Z): a job that ran the host as root + // inside WSL2 left /tmp/sp1-cuda-0.sock owned by root, and this user's client cannot open it + let hint = if last.contains("PermissionDenied") { " (a GPU-server socket /tmp/sp1-cuda-*.sock owned by another user, left by a job that ran the prover as root: remove it as that user, or run the socket-fix job)" } else { "" }; + return Err(format!("prover: {last}{hint}")); } let res: Value = serde_json::from_str(&std::fs::read_to_string(&results).map_err(|e| e.to_string())?).map_err(|e| e.to_string())?; let statement = res["statement"].as_str().ok_or("no statement in the results")?.to_string(); diff --git a/docs/plans/proving-v1.md b/docs/plans/proving-v1.md index 8e8d8c4e..b7ac3175 100644 --- a/docs/plans/proving-v1.md +++ b/docs/plans/proving-v1.md @@ -118,6 +118,8 @@ The resume path (5 October 2026, the 0.3.11 app): `POST /api/resume` on 0.3.9 re If the prover-floor agent's rebuilt `sp1-gpu-server` (the Setup sizes cut, built on PC 2 under WSL2) proves a shard under 11 GB, it becomes a shipped artefact and needs its own row of rules before 0.3.12: it is built from a pinned SP1 source tag with `CUDA_ARCHS` covering sm_86, sm_89 and sm_120 (the 12 and 16 GB tiers are Ampere and Ada, not only the 5090's Blackwell; one card family per measured row), by the packaging path that builds the Windows payload (PC 1's build job for the Linux binary, the Mac signs the manifest as it does the DMG), lands in the DMG and the WSL2 package beside the host as `wsl2/bin/sp1-gpu-server` with its sha256 in `payload-inputs.json`, is named in `evidence.md` beside the prover rows ("prover built from SP1 at "), is rebuilt and re-measured at every SP1 upgrade, and ships only after `--mode verify-segment` and `--mode verify` on proofs it made show the pinned verifying keys unchanged (the server changes allocation, not the circuit; the ids `0x2b1a81cb...` and `0x474678f3...` must still verify them). The 12 GB claim itself waits for the on-order RTX 3060 to run that server on the same fixtures and recipe as the curve; until then the public line stays at 24 GB. +The root-socket class on PC 2, the two times: 20:00:56Z (my chain job's root run; the live prover failed with Connect(PermissionDenied) until the socket was gone) and 21:25:24Z (the aggregation-cost job's root run; the prover stayed dark through the 0.3.10 restart at 21:49:41Z until `socketfix-pc2-pv1` removed the root-owned `/tmp/sp1-cuda-0.sock` at 22:01:16Z; the next shard, block 89011, was proven at 22:02:13Z and paid, and every shard since). The permanent fix in the 0.3.11 app tree: every committed playbook that runs a prove mode as root carries `pkill -f sp1-gpu-server; rm -f /tmp/sp1-cuda-*.sock` at its start and end, `tools/ci/prover-socket-check.sh` (in `ci.yml`) fails a playbook without them, and the app's prover names the cause in its log line when the host reports PermissionDenied. The app itself cannot remove a socket another user owns, so a job written outside the tree must still follow the rule. + A prover job on a shared card runs as the app's user or cleans its socket (`pkill -f sp1-gpu-server; rm -f /tmp/sp1-cuda-*.sock` at the start and the end; `tools/ci/prover-socket-check.sh`): the root-socket fault of 20:00Z, bench-log. ### The prover profiles: the tiers from the S_p curve (bench-log, "proving v1", the sweep, the miner-on pair and the curve) From 563edf1f4a3ea1c87f3c700281c437f44e3c5cc1 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:38:37 +0000 Subject: [PATCH 140/158] release-0.3.10 plan: PC 2's prover restored at 22:01Z (a socket fix), the open item closed Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.10.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/release-0.3.10.md b/docs/plans/release-0.3.10.md index b825916e..7e3df4e5 100644 --- a/docs/plans/release-0.3.10.md +++ b/docs/plans/release-0.3.10.md @@ -481,7 +481,7 @@ The next cut (0.3.11), decided by the coordinator on 5 October 2026 night: | C1 (the consequences reviewer, 20:0xZ): the 0.3.10 node's `igneum_exportSegments` (fork `igneum/exec/src/rpc.rs`) writes no `daaScore` and no `feesV1ActivationDaa` per segment, which the 0.3.9 exporter needs to replay both sides of the fee switch (`export/src/main.rs`: "a dump without `daaScore` is accepted only when the switch is never or 0"). Measured here: the handler (90 lines from `rpc.rs` 849 in `vendor/igneum-node-0310`) carries neither key (`daaScore` appears in the fork only in other RPCs, lines 239, 423, 819); the reviewer names `vendor/igneum-node-pv1` at eb32c645 (on 21d4c73c) as the carrier: its `rpc.rs` writes `daaScore` per segment (line 961) and `fees` and `feesV1ActivationDaa` at the top level (line 982); confirmed here at 20:4xZ (`git -C vendor/igneum-node-pv1 grep -n feesV1ActivationDaa -- igneum/exec/src/rpc.rs`: line 982); my first read of that path at 20:1xZ was wrong. So at H = 210,000 (about 19:50Z on 6 October) every 0.3.10 prover's export of a post-H block fails or meters with the wrong table and the fleet's provers go dark, unless the next node cut carries that RPC onto every prover before H, or H is republished later. The mechanism (the proving agent, 20:1xZ): the app's prover calls the exporter with no fee flags, so from H every app prover on 0.3.10 cuts with the prototype table and every statement is vetoed. The two closes: (a) the proving-v1 fork (eb32c645, on 21d4c73c) on every prover before H through 0.3.11; (b) republish H = tip + 86,400 by the fee-switch plan's rule. Decision due 16:00Z on 6 October; the coordinator, the proving agent and the 0.3.11 shipper hold the same line | open, dated: (a) or (b) by 16:00Z on 6 October | | C5 (the same reviewer): the app kills its prover child on quit (`prover.rs` 266 to 272), so the `update-now` of this rollout aborts whichever shard each prover has in flight (up to 37 s each, no payout). Accepted as the cost of the restart; the count is read after the rollout from the provers' "stopped after" lines (section 8) so the coverage numbers measured across the restart are read with it | recorded at the rollout | | `/api/resume` answered ok on the 0.3.9 app and never restarted the miners (PC 2's 5090 worker "off" with the card holding 1.7 GB from a job's resume at 21:25:11Z until its 0.3.10 restart, the iGPU miner too; the Counter ASIC coordinator, 21:4xZ). The class: a resume that reports success without a miner restart. The app should re-check the miner processes after a resume and report a failure | open: next cut | -| PC 2's prover after the 0.3.10 restart: every assigned shard fails at once with `CudaClientError: Connect(PermissionDenied)` (section 8), where 0.3.9's PC 2 proved shards from 17:36Z. The host, the pinned ids and the CUDA backend are detected as before; the connect that fails is the SP1 CUDA prover's client socket. The aggregation-cost agent's job `agg-cost-pc2-2` ran as root in WSL minutes before and its lines show no CUDA or socket change; the 0.3.10 app's prover path changed only to read the program ids (`--mode id`). Unexplained; handed to the coordinator and the Counter ASIC coordinator (PC 2's measurement agents) | open: PC 2 proves nothing until it is found; a restart of the WSL distro or of the CUDA prover service is the first thing to try | +| PC 2's prover after the 0.3.10 restart: every assigned shard failed at once with `CudaClientError: Connect(PermissionDenied)` (section 8) from 21:49:56Z. RESOLVED at 22:01Z by a socket fix on PC 2 (the Counter ASIC coordinator's agents; the SP1 CUDA prover's client socket permission), after which PC 2 proved and was paid every 40 s. The proving agent is adding a log line that names the cause on the app branch for the next cut | closed 22:01Z | | PC 37ba0461 (the US laptop) started the 0.3.10 install at 21:40:53Z, stopped its miners and node at 21:41:16Z and had not come back by 21:56Z: the per-user installer on the owner's machine, nothing to drive remotely | open: the console shows when it returns; its 0.3.10 line and worker start are read then | | `dl/public/igneum-downloads.json` (the unsigned index the site's download page reads) alternates at the edge between the new bytes and the previous ones for over 20 minutes after the deploy (one fetch byte-identical at 21:52Z, the next three not): different edge nodes behind one hostname. The two signed manifests were byte-identical and verified from the first check. The ship's verify step counts it as a failure and refuses to post the console item, so the item was posted with `--from console` | open: the verify should accept the index after the signed manifests pass, or retry it for longer; the site serves the previous version's buttons from a stale edge until it settles | | The console's machine card keeps a machine's LAST non-empty node commit string: PC 1's card read `node 2.1.0-a24ab01a` for 17 minutes after its node had restarted as the PC build (`igneumd/2.1.0`, no commit), while PC 2's card read `2.1.0` at once; the Mac's card kept node 1's a24ab01a after node 1 moved to 21d4c73c. A card's commit string is therefore not a fact about the running node until the app re-reads it; the node log is | open: the card should show the string the app last READ, with its time, or nothing | From 9b2cd505c10d54787c649a12f69af3affbb4806c Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:38:48 +0000 Subject: [PATCH 141/158] Consequences ledger: sweep 11 notes (G4b green, the PCs' workers key by class and era, the patched server built, the merges) Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index 5b118a14..b366062f 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -70,3 +70,5 @@ Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/c | C31 | The copy the ship takes (ca2-coord at 22:3x): litepaper line 562 "12 GB or more proves full shards" beside line 452 "Proving needs an NVIDIA card with 24 GB or more"; evidence row 16 still "A 12 GB card proves one shard in about 20 s, designed" while proving-v1 440fd59 withdrew it; line 562 states the dataset "doubles on a step schedule fixed at genesis (years 4, 12 and 28)" | ca2-coord site/litepaper.html 452 and 562, docs/evidence.md 43; proving-v1 440fd59; D4 | every reader of the litepaper; the integrator; the project lead's D4 | One page says 12 GB and 24 GB for the same thing; the evidence table on the ship branch contradicts the measurement, and the two branches will conflict on evidence.md and litepaper.html at the merge (ship order: ca2-coord before proving-v1), so the stale row can win by accident; and the step schedule is written as a genesis fact while the coordinator's own 22:50 entry calls mapping (b) a recommendation for the project lead (gate 1, D4): a public page should not decide a genesis parameter before he does | On ca2-coord: strike "12 GB or more proves full shards" from line 562 (line 452 is the sentence); take proving-v1's evidence row 16 (WITHDRAWN, 24 GB measured) at the merge and say so in the merge plan; write the growth sentence as the recommendation it is ("the plan is a step schedule ... decided at gate 1") until D4 is taken | coordinator ada8afb62d752b1e2 | closed on ca2-coord a7be43f and 0d9b23d (the 12 GB clause struck; the merge rule "take proving-v1's row 16 and its proving sentences" in the rollout plan; one wording in all five places, "the proposed schedule, fixed at the testnet genesis: 2 GB, doubling at years 4, 12 and 28", the lifetime sentences kept as consequences of the proposal and marked approximate) | | C32 | The 0.3.10 install at 21:49Z cleared PC 1's app jobs folder and with it the AMD kit fetched at 21:23:59Z; the amd-card-test playbook now says its fetch must be republished after any app update | bench-log 39f02ff; status 21:05 ("the jobs folder is cleared by fetch jobs" was the earlier, wrong reading) | every PC job tonight and tomorrow; the 0.3.11 rollout | A class, not one playbook: every fetch-then-run pair (era, hot table, mixer, repro, Ember, the AMD sweep, the prover-floor build) loses its kit when an update lands between the fetch and the run, and the run fails in seconds or, worse, runs against a stale copy. The 0.3.11 update-now reaches PC 2 while the prover-floor agent's 60 to 90 minute server build runs there (go at 22:17, to about 23:50): if that build's working directory is under the app's jobs folder, the update wipes it mid-build and the 12 GB rows slip past the morning | (1) The 0.3.11 update-now is sequenced after the prover-floor build closes, or the build's directory is confirmed outside the jobs folder before the ship; (2) every run playbook begins with a presence check of its kit and fails with "kit missing: republish the fetch after the app update" (the class check: the bash-body sub-agent's CI check gains a rule that a run job naming a kit path tests it first, or the coordinator's queue re-fetches after every update as a rule) | coordinator ada8afb62d752b1e2 (the queue and the ship order) | taken (coordinator: the prover-floor build lives under /opt/igneum-floor in WSL2, outside the jobs folder, but it is the app's job process and an app restart ends it, so the 0.3.11 update-now goes to PC 2 only after floor-build-3 closes, the ship's earlier steps not waiting; the re-fetch rule and the presence-check rule are in the rollout plan beside the one-job rule, the playbook owners carry it at their next publish; the CI side is with the bash-body sub-agent as a kit-path check). CI side closed on bash-body-check e3bd761: tools/ci/kit-path-check.sh in ci.yml and in publish-jobs.sh add --kind run (34 tests pass); every existing kit-using playbook (13 across master, ca2-v3, ca2-analysis, rdna4-telemetry) already checks before use, so the gate guards the shape without a backlog | | C33 | `/api/live` at 22:33Z: 13 of 482 blocks fully proven in 10 minutes (2.7%), 1 prover, median proof lag 46 s, the live node's verifier "Off" with 42 pool entries pending and 0 verified; `/api/stats` (the documented public API) carries no proving field at all | live and stats handlers (`site/api/stats.mjs` FIELDS; the explorer branch 3e01212); the homepage "~60 s to a proof"; evidence row 15 | everyone who reads the public API or the homepage tile; the testnet's first external reader | The public stats API hides the one number that qualifies the tile and row 15: coverage is 2.7% with one prover, and the proof lag is 46 s. A reader can find it only on /api/live. The live node's verifier "Off" (42 pending, 0 verified) is the Mac app node in trust mode or without a host, so the page says "verifier Off" while the chain pays provers: a public-page oddity the morning reader will ask about | `/api/stats` gains a `proving` object from the same live_state (`blocks_10m`, `blocks_fully_proven_10m`, `shards_paid_10m`, `provers_10m`, `median_proof_lag_s`, `active`), documented in docs/api/public-stats.md and in its contract test; the live page's verifier line names which node it reads and why it is off; the homepage tile's "~60 s" caption cites the measured 46 s median and the 2.7% coverage ("the target; today one prover covers 2.7% of blocks at a 46 s median") | explorer a76f60b415859b7b5 (the API); the tile caption: D2 wording for the project lead | closed on explorer d7e797c (/api/stats carries the proving object with coverage_10m, 0.0273 at 22:33Z, in the contract test, the live check and docs/api/public-stats.md with the sentence that coverage is what a third party reads before "every block is proven"; the live page's proving legend and the API note say the observer's node runs its own verifier off and reads paid shards from the chain). The homepage tile caption stays with the project lead (D2) | + +Sweep 11 (22:38 UTC) notes, no new row: gate G4b GREEN (a real Metal miner across a v3 boundary; a second Metal-only fault found and fixed first, 00c55aa: serveDataset keyed the day dataset by day alone and would have hashed v3 over an x1 dataset; the coordinator's next-cut rule: every worker path mines across a boundary in the gate network before a class change ships). The reviewer checked the PCs' side of that class: the CUDA worker and the OpenCL host build each resident pair (program, cache, dataset) from the pack's own memhard.h and key it by (epoch, day, class, era) through pairIsClass (proto-cuda/nvrtc/worker.cpp 451, proto-opencl/host.c 1106 on ca2-v3 fa3c932), so the fault does not reach the PCs at N4. The patched sp1-gpu-server built green on PC 2 at 22:32:29Z with sm_86, sm_89, sm_120 (C26's arch list), the floor sweep (9 points) running; the integration merges (readwidth 30ff674, origin/master 1f0d62c) on ca2-v3 49c7e78 with every check green. All gates but G5 (the ship's build) are green; the ship waits on the merged tip. From 2c0dc01e4950bf5acfba160e427dda5bd25aeb8f Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:39:25 +0000 Subject: [PATCH 142/158] Consequences ledger: C34 the packaged override line lacks proving v1's four fields Co-Authored-By: Claude Fable 5.1 --- docs/plans/consequences-2026-10-05.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/plans/consequences-2026-10-05.md b/docs/plans/consequences-2026-10-05.md index b366062f..25dd516d 100644 --- a/docs/plans/consequences-2026-10-05.md +++ b/docs/plans/consequences-2026-10-05.md @@ -72,3 +72,4 @@ Merge note for the integrator (22:3x UTC): branch `consequences` is docs/plans/c | C33 | `/api/live` at 22:33Z: 13 of 482 blocks fully proven in 10 minutes (2.7%), 1 prover, median proof lag 46 s, the live node's verifier "Off" with 42 pool entries pending and 0 verified; `/api/stats` (the documented public API) carries no proving field at all | live and stats handlers (`site/api/stats.mjs` FIELDS; the explorer branch 3e01212); the homepage "~60 s to a proof"; evidence row 15 | everyone who reads the public API or the homepage tile; the testnet's first external reader | The public stats API hides the one number that qualifies the tile and row 15: coverage is 2.7% with one prover, and the proof lag is 46 s. A reader can find it only on /api/live. The live node's verifier "Off" (42 pending, 0 verified) is the Mac app node in trust mode or without a host, so the page says "verifier Off" while the chain pays provers: a public-page oddity the morning reader will ask about | `/api/stats` gains a `proving` object from the same live_state (`blocks_10m`, `blocks_fully_proven_10m`, `shards_paid_10m`, `provers_10m`, `median_proof_lag_s`, `active`), documented in docs/api/public-stats.md and in its contract test; the live page's verifier line names which node it reads and why it is off; the homepage tile's "~60 s" caption cites the measured 46 s median and the 2.7% coverage ("the target; today one prover covers 2.7% of blocks at a 46 s median") | explorer a76f60b415859b7b5 (the API); the tile caption: D2 wording for the project lead | closed on explorer d7e797c (/api/stats carries the proving object with coverage_10m, 0.0273 at 22:33Z, in the contract test, the live check and docs/api/public-stats.md with the sentence that coverage is what a third party reads before "every block is proven"; the live page's proving legend and the API note say the observer's node runs its own verifier off and reads paid shards from the chain). The homepage tile caption stays with the project lead (D2) | Sweep 11 (22:38 UTC) notes, no new row: gate G4b GREEN (a real Metal miner across a v3 boundary; a second Metal-only fault found and fixed first, 00c55aa: serveDataset keyed the day dataset by day alone and would have hashed v3 over an x1 dataset; the coordinator's next-cut rule: every worker path mines across a boundary in the gate network before a class change ships). The reviewer checked the PCs' side of that class: the CUDA worker and the OpenCL host build each resident pair (program, cache, dataset) from the pack's own memhard.h and key it by (epoch, day, class, era) through pairIsClass (proto-cuda/nvrtc/worker.cpp 451, proto-opencl/host.c 1106 on ca2-v3 fa3c932), so the fault does not reach the PCs at N4. The patched sp1-gpu-server built green on PC 2 at 22:32:29Z with sm_86, sm_89, sm_120 (C26's arch list), the floor sweep (9 points) running; the integration merges (readwidth 30ff674, origin/master 1f0d62c) on ca2-v3 49c7e78 with every check green. All gates but G5 (the ship's build) are green; the ship waits on the merged tip. +| C34 | The rollout plan's packaged override line (counter-asic-2-rollout.md 26) carries five switches: difficulty_v2 33000, proving_v0 84100, fees_v1 210000, finality_v3 135200, program_class_v3 N4; section 8a says 0.3.11 publishes ONE object with both activations set | counter-asic-2-rollout.md 26 and 8a; proving-v1.md (the four v1 fields enter the digest only once proving_v1_activation_daa is set) | every node and every prover on the devnet at the 0.3.11 publish | If the publisher copies line 26, proving v1 ships in the binary and never activates: proving_v1_activation_daa stays at never on every node, the digest is the five-field one, the segment records are never carried, and C1's fix (the export fields) still works but the aggregator, the chain rule and the unproven rule stay off while the plan and the public copy say they are live. The four fields (activation_daa H1 = tip + 14,400 at publish, segment_blocks 8, unproven_daa 600, aggregator_share_bps 1000) must be in the packaged line, the manifest object, the hand nodes' and the seed's files, verbatim, and the expected digest read on a scratch node with all nine fields | Line 26 and the ship step name the nine-field object with N4 and H1 both set at publish and the scratch-node digest read over that object (the 22:xx "expected 0.3.11 digest with the two new fields at never" is the rolling-upgrade digest, not the activation one; both are recorded) | coordinator ada8afb62d752b1e2 (the ship runbook) | sent | From 123838199ffa0bb89fbd177f12f02ea13842b6c7 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:40:42 +0000 Subject: [PATCH 143/158] Igneum Miner 0.3.11: the six version files Co-Authored-By: Claude Fable 5.1 --- app/igneum-app/Cargo.lock | 2 +- app/igneum-app/Cargo.toml | 2 +- app/igneum-app/resources/igneum-app.rc | 8 ++++---- app/windows/version.h | 4 ++-- packaging/mac/app/Info.plist | 2 +- packaging/windows/Igneum-Miner.iss | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/igneum-app/Cargo.lock b/app/igneum-app/Cargo.lock index f3a5ca19..f2265f7c 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.10" +version = "0.3.11" dependencies = [ "ed25519-dalek", "getrandom", diff --git a/app/igneum-app/Cargo.toml b/app/igneum-app/Cargo.toml index bf0f5163..55f3afc0 100644 --- a/app/igneum-app/Cargo.toml +++ b/app/igneum-app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "igneum-app" -version = "0.3.10" +version = "0.3.11" 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 0aadc835..b0ba9a75 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,10,0 -PRODUCTVERSION 0,3,10,0 +FILEVERSION 0,3,11,0 +PRODUCTVERSION 0,3,11,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.10" + VALUE "FileVersion", "0.3.11" VALUE "InternalName", "igneum-app" VALUE "LegalCopyright", "Igneum contributors" VALUE "OriginalFilename", "igneum-app.exe" VALUE "ProductName", "Igneum Miner" - VALUE "ProductVersion", "0.3.10" + VALUE "ProductVersion", "0.3.11" END END BLOCK "VarFileInfo" diff --git a/app/windows/version.h b/app/windows/version.h index 4c841424..5de91594 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.10" -#define IGNEUM_HOST_VERSION_RC 0,3,10,0 +#define IGNEUM_HOST_VERSION_STR "0.3.11" +#define IGNEUM_HOST_VERSION_RC 0,3,11,0 #endif diff --git a/packaging/mac/app/Info.plist b/packaging/mac/app/Info.plist index 30760a88..53182e89 100644 --- a/packaging/mac/app/Info.plist +++ b/packaging/mac/app/Info.plist @@ -11,7 +11,7 @@ CFBundleVersion VERSION_STAMP CFBundleShortVersionString - 0.3.10 + 0.3.11 CFBundlePackageType APPL CFBundleExecutable diff --git a/packaging/windows/Igneum-Miner.iss b/packaging/windows/Igneum-Miner.iss index 66d906a9..817a4aca 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.10" + #define AppVersion "0.3.11" #endif #define AppName "Igneum Miner" #define Publisher "Igneum" From b584e41e310ae4712b295b1f67d00685d733a9e7 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:43:14 +0000 Subject: [PATCH 144/158] CI on the merged 0.3.11 tree: MacBook reworded in the analysis prose (the identity check's hostname pattern), a presence check before the kit's first use in the three proving-v1 PC 2 scripts (C32), pc1-cpu-prove.ps1 on the socket check's allow list (the CPU path starts no GPU server) Co-Authored-By: Claude Fable 5.1 --- docs/analysis/card-lifetime-2026-10-05.md | 2 +- docs/analysis/proving-methods.md | 2 +- tools/ci/prover-socket-check.sh | 2 +- tools/proving-v1/pc2-memory-miner-on.ps1 | 4 +++- tools/proving-v1/pc2-memory-sweep.ps1 | 4 +++- tools/proving-v1/pc2-sp-curve.ps1 | 4 +++- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/analysis/card-lifetime-2026-10-05.md b/docs/analysis/card-lifetime-2026-10-05.md index 4a6d565a..5d80ca55 100644 --- a/docs/analysis/card-lifetime-2026-10-05.md +++ b/docs/analysis/card-lifetime-2026-10-05.md @@ -61,7 +61,7 @@ What the table says per tier: | 16 GB | 17 to 19.5 years under (a), year 28 under (b) | | 24 GB | Under the resident reading the year-28 cache doubling (2 GiB) ends it the same day under both options. Freed: 31 years or year 60 | | 32 GB | 38 to 43 years under (a), year 60 under (b). Not a constraint for any plan | -| Apple 8 GB | 2.6 to 3.4 years under (a), year 4 under (b). The base MacBook Air is a short-lived miner | +| Apple 8 GB | 2.6 to 3.4 years under (a), year 4 under (b). The base 8 GB Apple laptop is a short-lived miner | | Apple 16 GB | 10 to 11.4 years under (a), year 12 under (b): the same shape as an 8 GB card | | Apple 32 / 64 GB | 25 years and 55 years or more. No constraint | diff --git a/docs/analysis/proving-methods.md b/docs/analysis/proving-methods.md index 59157332..cefa6708 100644 --- a/docs/analysis/proving-methods.md +++ b/docs/analysis/proving-methods.md @@ -171,7 +171,7 @@ Read 5 October 2026, 22:10 to 23:00 UTC, by four research agents and this one; e | **Pico** (Brevis) | Plonky3 STARK, KoalaBear default; chunk size a parameter with no documented default | CUDA via `pico-gpu`; **no VRAM figure published**; every run on 32 GB 5090s | Prism 2.1: 16 x 5090 over two machines, 4.87 s average on Ethereum blocks | Groth16 via gnark | core MIT or Apache; **`pico-gpu` is BUSL-1.1** and "not recommended for production" (its README) | v2.1.2, Aug 2026; Sherlock audit | | **Ziren** (ZKM, MIPS) | Plonky3-class, KoalaBear, LogUp GKR, WHIR | CUDA 12, compute capability 8.6+, "24 GB VRAM or higher"; the GPU prover is a Docker image pinned by digest, source "planned H1 2026" (docs.zkm.io prover page; an independent evaluation of v1.1.4 says the GPU path is not open) | one 5090: 5.9 MHz on a 288 M-cycle block; 4 GPUs 3.1 to 3.3x | compressed proof 603 KiB; Groth16 or PLONK | core MIT or Apache; GPU image licence unspecified | v1.2.7; no public audit cited | | **Stwo / S-two** (StarkWare) | Circle STARK over Mersenne31; blowup 1 (rate 1/2), 70 queries, 26 bits of grinding | **CPU SIMD first** (AVX2, AVX-512, NEON, WASM); GPU through ICICLE-Stwo (Ingonyama): about 3 GB of trace in GPU memory, out of memory from 2^23 rows; a WebGPU port of the constraint evaluation (zkSecurity blog, April 2025); client-side proving under 1 GB after a spill allocator (third-party PR) | 620 k Poseidon2 a second on an M3 laptop | via a Cairo verifier (proofs of proofs); sizes not published here | Apache-2.0 | live on Starknet mainnet since 3 Nov 2025; no RISC-V guest of its own (Nexus 3.0 is the RISC-V zkVM on it, BUSL-1.1 until 2029) | -| **Jolt** (a16z) | sumcheck and lookups (Lasso lineage, Twist and Shout memory checking); PCS Dory over BN254 by default, or **Akita**, a lattice commitment over a 128-bit prime field (Sep 2026, "Lattice Jolt"); RV64IMAC; no continuations (the book's recursion page is "under construction") | **No CUDA in the public repository** (LayerZero's "Jolt Pro" CUDA port is private); **Metal**: PR #1938 merged 30 Sep 2026 (the `jolt-metal` runtime crate), PR #1733 (the full prover on Metal) still a draft. Memory: "about 200 bytes per cycle" with Akita (a16z substack, Sep 2026); the book: "under 2 GB of memory per million cycles"; a streaming prover bounded to "a few GBs" is planned, not shipped | over 2 M cycles a second on a laptop CPU with Akita, over 10 M with Metal on a MacBook (a16z substack, Sep 2026); PR #1733: M5 Max, 2^25 cycles in 19.8 s, 2^27 in 77 s at an 89.4 GiB footprint | proof about 50 KB (Dory) or 65 to 80 KB (Akita); verify sub-second, approximate; on-chain 1.3 to 2 M gas estimated in 2024; no Groth16 wrapper shipped | MIT or Apache-2.0 | `v0.3.0-alpha` is the last tag (1 Oct 2025); README: "not suitable for production use"; no audit | +| **Jolt** (a16z) | sumcheck and lookups (Lasso lineage, Twist and Shout memory checking); PCS Dory over BN254 by default, or **Akita**, a lattice commitment over a 128-bit prime field (Sep 2026, "Lattice Jolt"); RV64IMAC; no continuations (the book's recursion page is "under construction") | **No CUDA in the public repository** (LayerZero's "Jolt Pro" CUDA port is private); **Metal**: PR #1938 merged 30 Sep 2026 (the `jolt-metal` runtime crate), PR #1733 (the full prover on Metal) still a draft. Memory: "about 200 bytes per cycle" with Akita (a16z substack, Sep 2026); the book: "under 2 GB of memory per million cycles"; a streaming prover bounded to "a few GBs" is planned, not shipped | over 2 M cycles a second on a laptop CPU with Akita, over 10 M with Metal on a Apple laptop (a16z substack, Sep 2026); PR #1733: M5 Max, 2^25 cycles in 19.8 s, 2^27 in 77 s at an 89.4 GiB footprint | proof about 50 KB (Dory) or 65 to 80 KB (Akita); verify sub-second, approximate; on-chain 1.3 to 2 M gas estimated in 2024; no Groth16 wrapper shipped | MIT or Apache-2.0 | `v0.3.0-alpha` is the last tag (1 Oct 2025); README: "not suitable for production use"; no audit | | **Ceno** (Scroll) | GKR tower prover, BabyBear, WHIR or BaseFold PCS; RV32IM | CUDA, but the real HAL is in a **private** `ceno-gpu` repository (the public one is a mock); no memory numbers | 2 GPUs 1.6x over one (PR #1403, Sep 2026) | via OpenVM recursion to Halo2 | Apache-2.0 | README: "under construction and not suitable for use in production" | | **Nexus 3.0** | on Stwo (Circle STARK, M31) | no GPU path documented | none published | not published | **BUSL-1.1** until 10 Feb 2029 | last push 6 Jan 2026; folding (Nova family) abandoned June 2025 for the STARK | | **Valida** (Lita) | Plonky3 STARK | no GPU; a CUDA port "underway" in July 2025 | none current | not published | Apache or MIT | dormant since Sep 2025; documented soundness issues in its own benchmarks page | diff --git a/tools/ci/prover-socket-check.sh b/tools/ci/prover-socket-check.sh index ddd10e56..c67ad3e9 100755 --- a/tools/ci/prover-socket-check.sh +++ b/tools/ci/prover-socket-check.sh @@ -10,7 +10,7 @@ set -euo pipefail cd "$(dirname "$0")/../.." fail=0 # the publisher's test writes a known-bad root prover script on purpose, to prove the publish refuses it -ALLOW='^packaging/ota/test-publish-jobs\.sh$' +ALLOW='^(packaging/ota/test-publish-jobs\.sh|tools/amd-prove/pc1-cpu-prove(-sp)?\.ps1)$' # pc1-cpu-prove: the CPU path starts no GPU server (its author's allow entry, 5 October 2026) list_files() { if [ $# -gt 0 ]; then printf '%s\n' "$@"; else git ls-files 'tools/**' 'relay/playbooks/**' 'proving/**' 'packaging/**' | grep -E '\.(sh|ps1|mjs)$'; fi; } while IFS= read -r f; do [[ "$f" =~ $ALLOW ]] && continue diff --git a/tools/proving-v1/pc2-memory-miner-on.ps1 b/tools/proving-v1/pc2-memory-miner-on.ps1 index 0376a92f..b560394c 100644 --- a/tools/proving-v1/pc2-memory-miner-on.ps1 +++ b/tools/proving-v1/pc2-memory-miner-on.ps1 @@ -14,7 +14,9 @@ Start-Sleep -Seconds 45 $job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-pv1-mem2' }; New-Item -ItemType Directory -Force -Path $job | Out-Null function WslPath($p) { $w = (& wsl.exe -d Ubuntu-24.04 -u root -- wslpath -a ($p -replace '\\', '/') 2>$null); if ($w) { ($w -replace "`0", '').Trim() } else { '/mnt/c' + ($p.Substring(2) -replace '\\', '/') } } $jobW = WslPath $job -$emptyW = WslPath (Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json') +$kitFile = Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json' +if (-not (Test-Path $kitFile)) { Write-Output "RESULT kit missing: $kitFile; republish the fetch after any app update (C32)"; exit 2 } +$emptyW = WslPath $kitFile $bash = @" set -uo pipefail export PATH="`$HOME/.cargo/bin:`$HOME/.sp1/bin:`$PATH" diff --git a/tools/proving-v1/pc2-memory-sweep.ps1 b/tools/proving-v1/pc2-memory-sweep.ps1 index 9f265c17..ab4741fc 100644 --- a/tools/proving-v1/pc2-memory-sweep.ps1 +++ b/tools/proving-v1/pc2-memory-sweep.ps1 @@ -17,7 +17,9 @@ Start-Sleep -Seconds 45 $job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-pv1-mem' }; New-Item -ItemType Directory -Force -Path $job | Out-Null function WslPath($p) { $w = (& wsl.exe -d Ubuntu-24.04 -u root -- wslpath -a ($p -replace '\\', '/') 2>$null); if ($w) { ($w -replace "`0", '').Trim() } else { '/mnt/c' + ($p.Substring(2) -replace '\\', '/') } } $jobW = WslPath $job -$emptyW = WslPath (Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json') +$kitFile = Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json' +if (-not (Test-Path $kitFile)) { Write-Output "RESULT kit missing: $kitFile; republish the fetch after any app update (C32)"; exit 2 } +$emptyW = WslPath $kitFile $bash = @" set -uo pipefail export PATH="`$HOME/.cargo/bin:`$HOME/.sp1/bin:`$PATH" diff --git a/tools/proving-v1/pc2-sp-curve.ps1 b/tools/proving-v1/pc2-sp-curve.ps1 index 66f0b939..9c5643f1 100644 --- a/tools/proving-v1/pc2-sp-curve.ps1 +++ b/tools/proving-v1/pc2-sp-curve.ps1 @@ -17,7 +17,9 @@ Start-Sleep -Seconds 45 $job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-pv1-sp' }; New-Item -ItemType Directory -Force -Path $job | Out-Null function WslPath($p) { $w = (& wsl.exe -d Ubuntu-24.04 -u root -- wslpath -a ($p -replace '\\', '/') 2>$null); if ($w) { ($w -replace "`0", '').Trim() } else { '/mnt/c' + ($p.Substring(2) -replace '\\', '/') } } $jobW = WslPath $job -$emptyW = WslPath (Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json') +$kitFile = Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json' +if (-not (Test-Path $kitFile)) { Write-Output "RESULT kit missing: $kitFile; republish the fetch after any app update (C32)"; exit 2 } +$emptyW = WslPath $kitFile $pkg = Join-Path $env:LOCALAPPDATA 'igneum\prove\igneum-prove-wsl2-pv1b\igneum-prove-wsl2' if (-not (Test-Path $pkg)) { $pkg = Join-Path $env:LOCALAPPDATA 'igneum\prove\igneum-prove-wsl2-pv1b' } $pkgW = WslPath $pkg From b9acc5733ff9dd48af3bb9c525f3ed258a53a157 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:45:59 +0000 Subject: [PATCH 145/158] packaged-config: the nine-field devnet override object (program class v3 and proving v1 at DAA 154,800, the first multiple of 3,600 at or above the forecast publish tip + 14,400; proving v1 segment 8, unproven 600, aggregator share 1000 bps) in the packaged line Co-Authored-By: Claude Fable 5.1 --- packaging/mac/packaged-config.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/mac/packaged-config.sh b/packaging/mac/packaged-config.sh index 6731e2fa..93d1021f 100644 --- a/packaging/mac/packaged-config.sh +++ b/packaging/mac/packaged-config.sh @@ -28,7 +28,7 @@ DL_HOST="https://dl.igneum.network" # carry the devnet's activation height here, the same N as every other devnet node, before it is cut (Mac and CI alike: # make-payload.sh sources this file). Rule and order: docs/plans/difficulty-v2-rollout-devnet.md. # Example: NODE_OVERRIDE_PARAMS='{"difficulty_v2_activation_daa": 123456}' -NODE_OVERRIDE_PARAMS='{"difficulty_v2_activation_daa":33000,"proving_v0_activation_daa":84100,"fees_v1_activation_daa":210000,"finality_v3_activation_daa":135200}' +NODE_OVERRIDE_PARAMS='{"difficulty_v2_activation_daa":33000,"proving_v0_activation_daa":84100,"fees_v1_activation_daa":210000,"finality_v3_activation_daa":135200,"program_class_v3_activation_daa":154800,"proving_v1_activation_daa":154800,"proving_v1_segment_blocks":8,"proving_v1_unproven_daa":600,"proving_v1_aggregator_share_bps":1000}' # igneum_secret_file -> the file to read: the variable when set, else .next when it # exists, else ; IGNEUM_CONFIG_DIR (tests) replaces ~/.config/igneum From 142b5ef254a934957bceb0ca55855d009b0995f8 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 23:02:07 +0000 Subject: [PATCH 146/158] release-0.3.11 plan: the tree, the builds and tests so far, the override object and both digests, PC 1's hung engine and the relaunch, the next cut Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.11.md | 110 +++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 docs/plans/release-0.3.11.md diff --git a/docs/plans/release-0.3.11.md b/docs/plans/release-0.3.11.md new file mode 100644 index 00000000..3f24cc69 --- /dev/null +++ b/docs/plans/release-0.3.11.md @@ -0,0 +1,110 @@ +# Igneum Miner 0.3.11: program class v3 (Counter ASIC 2.0) and proving v1 on the devnet, 5 October 2026 + +Release engineer, from 22:39 UTC, on the coordinator's instruction under the project lead's delegation ("Counter ASIC 2.0 fully deployed", +"deploy what is absolute best" for proving v1). Worktree `/Users/joshm/Projects/igneum-wt-ship0311`, branch `release-0.3.11`, +assembled by the Counter ASIC coordinator from master b38f3de (the 0.3.10 merge) and taken over at its merge tip b968ee0 so there +is one ship, not two. Fork worktree `vendor/igneum-node-0311` UNDER the release tree (the node links `../../../../igneum-pow`, the +release tree's crate), branch `release-0.3.11-node` at 89dfcb95 (on 21d4c73c = 0.3.10's node, with proving-v1 ece42979 and the +digest re-pin). The 0.3.10 recipe (`release-0.3.10.md`) throughout; every Mac build under the main checkout's lock; every PC +job and publish from this worktree's tools (the signed envelope, the per-job zip names). Times are UTC. + +## 1. What 0.3.11 carries + +| Change | Where | State | +|---|---|---| +| Program class v3 (Counter ASIC 2.0): the era draw, the cache growth rule, the mixer x8, behind `program_class_v3_activation_daa` (keys on the epoch: `program_class_v3_first_epoch`); the workers carry the class, era and attempt rules on the serve protocol and refuse a pack of the wrong class or era | main `ca2-v3` fa3c932 (code 49c7e78; `igneum-pow`, the workers, the fast-time scripts, the plans); fork `ca2-v3-node` 89dfcb95 | merged (c9b0b2c) | +| Counter ASIC 2.0 docs, spec, site, evidence, the rollout plan and its gates (G1, G2, G3, G4, G4b, G6 green; G5 = the one-commit workers of this cut) | main `ca2-coord` 076c0ab then 57844e9 (C34) | merged (18605f4, 5cedcd4) | +| Proving v1 (spec 7.8): the aggregated segment record, the chain rule and the unproven rule behind `proving_v1_activation_daa`, with `proving_v1_segment_blocks` 8, `proving_v1_unproven_daa` 600, `proving_v1_aggregator_share_bps` 1000; the app's prover loop (the CPU path refused under 32 GB with the reason, the root-socket cleanup, the PermissionDenied line naming the cause); the resume fix (every stopped card re-armed, its pack re-exported, checked 90 s later); every worker gets `--prepare-packs` in the platform's path form (the Mac's Metal worker takes a class v3 day from the prepared pack) | main `proving-v1` 22c2363 (its agent: the final code tip; c36dfea after it is docs only and waits); fork proving-v1 ece42979 inside 89dfcb95 | merged (5cbb796) | +| CI: `bash-body-check.sh` (inline bash bodies in PowerShell job scripts parse), `kit-path-check.sh` (a run job tests its fetched kit before use, C32), `prover-socket-check.sh` (every root prover playbook unlinks the GPU server's socket) | main `bash-body-check` e3bd761 | merged (fe1ecdb; ci.yml keeps the signer-pipe step, both new steps and proving-v1's socket step) | +| The consequences ledger, the proving-methods analysis, the ASIC-resistance history | `consequences` 99fd988, `proving-methods` e7e0db7, `asic-history` 9e4af7f | merged (5dffb1b, 0f5bfc3, b968ee0) | +| The pinned proving guests | unchanged (no prover drain) | | + +Changelog line (the coordinator's words): "Igneum Miner 0.3.11: program class v3 (the era draw, the cache growth rule, the mixer x8) from epoch +N4/3600 and proving v1 from DAA N5; the resume fix; the Metal worker takes v3 from a prepared pack". + +## 2. The branch + +| Commit | What | +|---|---| +| b968ee0 | the Counter ASIC coordinator's merge tip (above), taken over at 22:40Z; its checks on the Mac: igneum-pow 53 + 4 + 19 + 7, the app 113 + 27 + 8, 0 failed | +| 21173c4 | `Igneum Miner 0.3.11: the six version files` (`--check`: 0.3.11 in all 6) | +| cc72f4a | CI on the merged tree, three findings fixed: the identity check's hostname pattern `MacBook` matched prose in `card-lifetime-2026-10-05.md` and `proving-methods.md` (reworded "Apple laptop"); the kit-path check flagged `tools/proving-v1/pc2-{memory-miner-on,memory-sweep,sp-curve}.ps1` for a bare `jobs\` literal in `WslPath (Join-Path ...)` (now `Test-Path` on the kit root, then `WslPath $kitFile`); the socket check flagged `tools/amd-prove/pc1-cpu-prove.ps1` and its `-sp` sibling, which the addendum said were allow-listed and were not (allowed, the CPU path starts no GPU server). The other agent's resolution had kept both ci.yml sides and bash-body-check's 24-line socket check; one slip of mine (a `git show :3:` redirect after that resolution had already committed) truncated that script to 0 lines in the working tree for a minute and was restored from HEAD | +| 23bc2b2 | `packaging/mac/packaged-config.sh`: the nine-field object (section 4) in the packaged line, `--test` passes (C34: a fresh install must start on the fleet's digest) | + +Every check at cc72f4a: identity 0 hits over 220 files, copied-sources, pinned-guests, signer-pipe, bash-body 15 bodies in 28 files, kit-path 14 of +14 kits checked, prover-socket, no-conflict-markers, workflow shell 0 findings over 35 .ps1, relay tests 17, UI tests 23. + +## 3. Builds and tests (the 0.3.10 recipe) + +| What | Command | Result | +|---|---|---| +| The fork's Mac node, 89dfcb95 | `CARGO_TARGET_DIR=vendor/igneum-node/target-0311 cargo build --release -j 4 -p kaspad -p igneum-miner --features kaspad/igneum-pow` from `vendor/igneum-node-0311` (under the release tree), under the lock; the target dir cloned by APFS from `target-0310` | 22:46:16 to 22:49:5xZ (3 min 19 s, incremental): igneumd bd7f043c453f4b3e9575e678912b71115227b09789d5ec0f367b8278f031d043 (41,386,016, `89dfcb95` in its strings); copied into the fork worktree's `target-integration/release/` | +| The seed's Linux node (glibc 2.36 target, zig) | `NODE_SRC= TARGET_DIR= OUT_DIR= infra/cross/build-linux.sh` (the 0.3.10 fix: absolute paths; `cargo clean -p kaspa-build-info` first), under the lock | 22:46:35 to 22:49:59Z (3 min 20 s): igneumd 63cf490d42483d3aa4e525eba9b4e4b68cae9090c32f409e745858defc381c52 (47,913,832, `GLIBC_2.34` at most, `89dfcb95` in its strings), igneum-miner a136d622... (9,861,280) | +| The two Windows workers (G5: one commit) | `proto-cuda/nvrtc/build-windows.sh` under the lock, tree 23bc2b2 | 22:46:43 to 22:46:50Z: igneum-worker-cuda.exe 2b3b8c92885442179f6bf2907c6f3eb453dc4a19908d90fd05981a09b7c2674c (1,536,512), igneum-worker-opencl.exe edc4a75da3b93d814caa69fd635010780d63d5b622ec24c3741d433c584f91e3 (478,208); both carry the resource block; both differ from 0.3.10's pair (85cc357b..., afa73a32...): the class, era and attempt rules are in them | +| The app | `cargo build --release` in `app/igneum-app` (target cloned from the 0.3.10 worktree), then `cargo test --release -p igneum-app`, under the lock | 22:47Z: `igneum-app 0.3.11`; tests ok 113 (lib) + 27 (ota-sign) + 8 (prove-verify), 0 failed | +| `igneum-pow` | `cargo test --release` in `igneum-pow`, under the lock | 22:47:33Z: ok 53 + 4 + 19 + 7, 0 failed (the class v3 vectors, the era draw, the mixer x8, the scratch soundness) | +| The prover host and export (the pin unchanged) | `cargo build --release -p igneum-prove-export -p igneum-prove-host` in `proving/igneum-prove` (the worktree needed the vendor links: `vendor/igneum-node-exec` and 45 others symlinked to the main checkout's, beside the real `igneum-node-0311` worktree) | 22:48Z: `--mode id` shard `0x2b1a81cb...`, aggregator `0x474678f3...` (the 0.3.9 pin: no prover drain); the nine real fixtures `--mode native` all ok | +| PC 1 build job (the node and the app, Linux and Windows) | `IGNEUM_WIN_RELEASE=/target-integration/x86_64-pc-windows-gnu/release node tools/build-job.mjs run --node vendor/igneum-node-0311 --target ae432dc7 --targets linux,windows --no-tests` from this worktree, its own zip `build-inputs-20261005224625-29789.zip` | job `build-20261005-224654`, published 22:46:54Z (PC 1 given by the Counter ASIC coordinator at 22:4xZ: Ember's collect job closed, the AMD sweep off tonight). (pending) | +| PC 2 suites | waits for the Counter ASIC coordinator's "PC 2 suites go" (agg-cost-pc2-3 on PC 2 until about 22:59Z) | (pending) | + +### 3a. PC 1's app down (22:31 to 22:5xZ) + +PC 1's installed 0.3.10 app quit at 22:31:06Z (its last upload 22:31:08Z, run `win-ae432dc7-20261005-214041`; Ember's tune job on it +reported "aborted (the app is quitting)"; the quit's sender is C35 for the consequences reviewer: the tray Quit, stdin EOF in wrapper mode, +or `POST /api/quit`, which Ember's playbook sends when its budget is spent) and did not come back, so PC 1 mined nothing, the build job +`build-20261005-224654` could not start (no app to fetch it) and no update-now could reach it. The relay agent on PC 1 was alive (it runs +as a logon scheduled task at highest privileges in the user's session), so the relaunch went through it at 22:52:31Z as relay item #241 +(`node tools/relay.mjs run PC1 ... relaunch-pc1.ps1`): the per-user install's `igneum-app.exe` started through `explorer.exe`, which hands +the app the user's own medium-integrity token as a double-click does (a plain `Start-Process` from the elevated agent would have made the +app elevated, which its updater never does), with a one-shot scheduled task at limited run level as the fallback; the script prints the +process, its session, `app.url` and `/api/state`. Its result (22:5xZ): `igneum-app.exe` was ALIVE, pid 26696 since 21:49:40Z in session 1 +(`app.url` written 21:49:40Z), and answered nothing on `/api/state` in 60 s: the engine had logged its quit at 22:31:06Z, stopped the miners +and the node, and then hung instead of exiting (the C35 fact: a quit that never ends; the sender is still the reviewer's question). The +script launched nothing because a process existed. A second task (#243, 22:55:10Z) ends that engine by pid, as the app's own updater ends the +old engine, and launches as above. Its result (#244, 22:57:18Z) changed the picture: the old engine was `responding=True` and had a node, two +miners and three workers ALIVE under it, so PC 1 had been mining with an engine whose uploads and HTTP answers had stopped (my probe's "no +answer" on `/api/state` may be its own fault: no token); the kill left those children running as orphans on their ports; the new engine +(pid 30964, 22:55:28Z, session 1) wrote its `app.url` at once and then put nothing into the intake for minutes. The next step, if no run id +appears by 23:01:30Z, is the updater's own order as one task: every igneum process ended (miners, workers, node, engine), then the launch. + +The reading (Ember's agent through the Counter ASIC coordinator, 23:00Z): the alive children were Ember's SECOND engine's orphans (its +tune job starts a second `igneum-app` with its own two miners on the 5090 and the 9070 XT), left mining when the job was aborted, holding +both GPUs; the installed engine's quit at 22:31:06Z then stuck in the jobs runner's abort, waiting for EOF on the script's stdout pipe, whose +write end the second engine and its miners had inherited (PowerShell's `Process.Start` inherits every inheritable handle), so EOF never +came. The class rule, for every job script that starts a second engine (`relay/playbooks/sweep-5090.ps1` has the same shape): no pipe into +it, and its whole tree killed at the end. The installer-build fallback script of 0.3.10 starts no second engine. +(pending: the reset's result, the new run id, the first STATUS line) +| The DMG | `NODE=/target-integration/release/igneumd MINER=... packaging/mac/build-dmg.sh` under the lock, 22:50:0x to 22:50:51Z | `Igneum-Miner-0.3.11.dmg` b7e81d4f6f3af9f9179e29faa72c844b795df757dfb2e4cd1d56cd454e78e1e7 (41,592,041): engine 0.3.11, node 89dfcb95 Mac arm64, the 0.3.9 prover host and export, `igneum-bench` (the Metal worker) rebuilt from this tree (667,145 to 555,808 bytes, signed ad hoc), fingerprints 477bb0ef and ed9c4d2e; read back from the mounted image: `Contents/Resources/igneum-app.json` carries the nine-field `node_override_params` with N4 = N5 = 154,800 (C34) | + +## 4. The override object, N4 and N5, the digests + +The object every node runs after the publish (the four live fields plus the five new ones): + +``` +{"difficulty_v2_activation_daa":33000,"proving_v0_activation_daa":84100,"fees_v1_activation_daa":210000,"finality_v3_activation_daa":135200,"program_class_v3_activation_daa":154800,"proving_v1_activation_daa":154800,"proving_v1_segment_blocks":8,"proving_v1_unproven_daa":600,"proving_v1_aggregator_share_bps":1000} +``` + +N4 and N5 are fixed BEFORE the DMG and the installer are built, because the packaged line (C34) must equal the manifest's object: DAA 136,967 at +22:45Z (PC 1's card) at 0.965 blocks/s puts the publish (about 23:40Z) near 140,200; tip + 14,400 is near 154,600; the first multiple of 3,600 at or +above it is 154,800 (N4's rule), which N5 takes too. At the publish the floor N - DAA >= 10,800 is checked; it holds until DAA 144,000 (about 00:45Z); +past that the line is re-pinned and the DMG and installer rebuilt. + +The two digest readings on the 0.3.11 Mac node (bd7f043c..., ports 60975/60976, 22 s each, under `run`): + +| Override file | Lines | Digest | +|---|---|---| +| none (the rolling-upgrade value: both new activations at never) | `igneumd/2.1.0-89dfcb95` | c562d70e1428c9789823cc40067623b4767f7c555ce7ff4ea11c1498f013ef6c, EQUAL to the node agent's pinned test on 79bd8e10 (22:49:59Z) | +| the nine-field object above | `Program class v3 from the override file: active from epoch 43 (DAA score 154800 rounded up to the epoch boundary at 154800, epochs of 3600 DAA)`, `Proving v1 from the override file: segment records paid from DAA score 154800, 8 blocks a segment, unproven after 600 DAA, aggregator share 1000 bps`, `Calibrated v1 fees ... 210000`, `Finality rule v3 ... 135200` | **0139ab9dc2992d449ec787d8f021974933631eb55740ab4b6ce9d5c226e72888** (22:50:23Z): the value every node must print after the publish; the sweep in section 9 reads it on every node | + +A node on the 0.3.10 override (1f4b4425...) and a node on this object never handshake, so the rollout moves every node in one sweep (the fee-switch +and N3 shape): the manifest, update-now, the hand nodes, the seed. + +## 10. The next cut + +| Branch | What | Why not 0.3.11 | +|---|---|---| +| `ember-tune` b671c8b (and the tune behind it) | the C35 fix: `Cmd::Quit(&'static str)` so every "quit:" line names its sender (the window host's stdin, the host gone, `POST /api/quit`, the sweep's end), `elevation_allowed()` = Power control alone (the unattended sweep on PC 1 raised one UAC prompt at 22:30Z under the old rule), no power cap at start under `--sweep`; the quit-source hunk is separable (main.rs 2 lines, server.rs 1 line, engine.rs the Quit arm, `elevation_allowed` and its test) | arrived after the tree closed at 23bc2b2 (the app, the DMG and PC 1's job carry it); not among the branches named for this cut | +| `fud-close` (the ledger closer's main branch, a22ba27a60a6f1c64; its ready tip was due about 23:05Z) | 45 public-text fixes on the site and litepaper, spec 8.3 and 8.8, two CI checks, relay fixes; touches `packfile.h` and `host.c`, so taking it means the two Windows workers, the Mac worker and the DMG rebuilt from the merged tip (G5) | offered by the Counter ASIC coordinator at 22:5xZ after the tree closed; not among the branches named for this cut; the fork-side `ledger-fixes` is not in 0.3.11 either | +| `explorer` d7e797c (and 3e01212) | `/api/stats` gains `proving`; `tools/ci/public-api-check.mjs` then FAILS when the live API lacks it, and ci.yml runs that check against the live site on every master push, which reads the OLD API until Vercel redeploys after the push (the reviewer's C36) | not in 23bc2b2 (only on the explorer branch); its merge needs the check to retry for a few minutes or to require `proving` only when `observer_updated_at` is newer than the commit | +| `proving-v1` c36dfea | docs only, after the code tip 22c2363 | its agent's choice: docs follow | +| the 0.3.10 list (`release-0.3.10.md` section 11): fork `pack-loop` 05ef0fa3, `job-console`, the rest of `opencl-rdna4`, `opencl-rdna4-telemetry` | | unchanged | From 310e5f0ee55a5d50f834189ae2b69eb8410dc6c7 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 23:04:38 +0000 Subject: [PATCH 147/158] ca2-soundness: docs/analysis/scratch-soundness.md alone (its code is already in the tree through the mixer merge; the branch merge conflicted there); release-0.3.11 plan: C37 and C38 Co-Authored-By: Claude Fable 5.1 --- docs/analysis/scratch-soundness.md | 408 +++++++++++++++++++++++++++++ docs/plans/release-0.3.11.md | 3 + 2 files changed, 411 insertions(+) create mode 100644 docs/analysis/scratch-soundness.md diff --git a/docs/analysis/scratch-soundness.md b/docs/analysis/scratch-soundness.md new file mode 100644 index 00000000..43f45615 --- /dev/null +++ b/docs/analysis/scratch-soundness.md @@ -0,0 +1,408 @@ +# Layer 3 soundness: the per-warp scratch with read-modify-writes + +5 October 2026 (night), cryptographer role, Counter ASIC 2.0 plan step 4 (`docs/plans/counter-asic-2.md`). Branch +`ca2-soundness` on top of `readwidth` b970dda (the scratch as a class parameter, 32 or 128 KiB per warp). Tests: +`igneum-pow/tests/scratch.rs`; Metal runs through `proto-metal/packbench` on the M5 Max; commands and counts in +`docs/bench-log.md` (entry of the same date). Nothing here touches the lottery hash as shipped: variant 5 is behind +`LoadClass::scratch(k, kb)` and is never emitted by generator version 2. + +Every figure below is measured (machine, date, command named) or cited; "approximate" marks a figure from memory. + +## 0. The five findings + +| # | Question | Finding | Status | +|---|---|---|---| +| 1 | Is what is written uniform and beyond a chip's precomputation? | The fill is a bijection of the lane nonce, the rewrite a bijection of the fold value in each word; written words show no bit bias over 3 to 12 million rewrites per class (worst 3.63 sigma of 6). The fill IS precomputable, by design, and at 64 slots 78.5 percent of reads are fill reads. | sound as a function; see 2 for what that means | +| 2 | Does any short cut avoid the writes? | No short cut inside a unit: a slot after d read-modify-writes needs all d fold values (replay test). But the live state is bounded by the read-modify-write count, not by the scratch size, because CPU verification resets the scratch per unit: 64 to 320 bytes per lane at scr2 to scr8, whatever the nominal 32 KiB, 128 KiB or 1 MiB. The named chip (cache mirror plus recompute) keeps that in SRAM at under 5 percent of its mirror and its gain does not move at any share under the 6 GB cap. | NOT sound as an anti-chip layer | +| 3 | Is the verifier's one-warp simulation exact? | Exact when the GPU's lazy per-unit tag is unique over the arena's life and the arena holds no stale tag. The kernels rely on this and neither host guarantees it (no clear at allocation, no clear at the 32-bit wrap of the tag counter, 16.4 minutes on a 5090). With the host contract of section 4.3 the simulation is exact: 14 edge packs twice, 200 fuzz packs, consecutive units on one warp and the wrap inside a launch all match the CPU on Metal (228 of 228); a broken tag and a broken fill are caught (3 of 3). | sound with a host contract; today it is luck | +| 4 | The attack surface of the writes | Out of bounds: impossible by the mask, 42 of 42 emitted kernels pass the static check, which catches six deliberate breaks. Aliasing: none, lane-major arenas disjoint by (warp, lane), two logical units of a wave64 get two arenas. Ordering: one lane, one slot, program order; no cross-lane sharing, no atomics needed. Alignment: 16-byte slots at 16-byte offsets from a 256-byte-aligned base. Wrap: identical to the CPU, tested at the launch level. | sound | +| 5 | What a conformance vector must carry | The class and geometry, the fill and rewrite, the host contract (tags, clearing, groups a multiple of warps), two consecutive units on one warp with a forced slot collision, a unit in the top 256 nonces with the wrap inside the launch, and the fingerprint declared independent of the warp count. The standard three-unit vectors catch a broken tag only through base 1,000,000 and would miss it at a 1 MiB scratch. | defined in section 6 | + +Recommendation (section 10): do not adopt layer 3 as the plan states it (read-modify-writes taken from the 16 +dataset loads). It replaces latency-bound dataset reads with cache-bound ones for the GPU, costs the named chip +nothing it cannot keep in a few megabytes of SRAM, and leaves that chip's gain at 2.4x at every share. The lever +that moves that chip is the mixer multiplier of the M16 analysis (x2 brings it to 1.2x, x4 to 0.6x, under the +verifier's 10 ms gate). If a scratch is kept for another reason, add the read-modify-writes beside the 128 loads, +never in their place, and ship the host contract and the vector of section 6 with it. + +## 1. What the branch implements + +| Piece | Where | What | +|---|---|---| +| Class | `igneum-pow/src/generator.rs:170-230` | `LoadClass { scratch: Some(k), scratch_kb }`: `k` of the 16 memory slots are `Op::Scratch`; `scratch_kb` KiB per warp of 16-byte slots, lane-major, `slots = kb x 2` per lane (32 KiB: 64, 128 KiB: 256); `scratch_slot_mask() = slots - 1` | +| Draw | `generator.rs:488-491` | the first `k` of the 16 drawn load slots become scratch ops (a uniform k-subset); the source register follows the fresh-source rule like a load | +| Fill | `igneum-pow/src/verify.rs:30` | `scratch_fill(seed, base, lane, slot, j) = splitmix32(((base + lane) ^ seed[j]) + slot x 0x9e3779b1 + (j + 1) x 0x85ebca77)`, j in 0..2 | +| Fold | `verify.rs:18` | `x = dst ^ w0; x = (rotl(x, 11) x 0x9e3779b1) ^ w1; x = (rotl(x, 11) x 0x9e3779b1) ^ w2; dst = x` (the read-width fold over the three data words) | +| Rewrite | `verify.rs:41` | the slot becomes `(x ^ w1, rotl(x, 7) ^ w2, x + w0)` | +| CPU model | `verify.rs:48-100`, `:305-312` | `ScratchModel`: per (lane, slot) a written bit and three words; an unwritten slot reads as its fill; one model per unit, so a unit starts from the fill | +| Acceptance | `igneum-pow/src/accept.rs:202-215, 374` | a scratch site that reads one slot in all 32 lanes rejects the program (lane-constant site); scratch slots carry bit 31 in the address list and are left out of the distinct-address bound, which now covers the dataset loads only | +| GPU statement | `igneum-pow/src/emit.rs:143-157` | `s_ = rN & mask; v_ = 16-byte load of slot s_; m_ = (v_.x == tag) ? ~0 : 0; w = (v_.yzw & m_) \| (fill & ~m_); fold; dst = x_; 16-byte store of (tag, x_ ^ w1_, rotl(x_, 7) ^ w2_, x_ + w0_)` in Metal, CUDA and OpenCL | +| Persistent prologue | `emit.rs:159-175` | `lane = tid & 31; warp_ = tid >> 5; arena = scratch + (warp_ x 32 + lane) x words_per_lane; for (g_ = warp_; g_ < groups; g_ += nwarps_) { gbase = baseNonce + g_ x 32; tag = salt + g_; ... }` | +| Hosts | `proto-metal/packbench.swift:144-164`, `proto-opencl/host.c:1025-1033, 1268` | the arena is allocated and never written by the host; `salt` starts at 1 and advances by the launch's unit count; no clear at allocation, none at the wrap | + +The constraint of the night (coordinator, 5 October 2026): the whole working set on an 8 GB card stays under 6 GB +(1 GiB table, the layer 5 hot table, the scratch of every resident warp, buffers), which caps the scratch at tens of +KiB per warp. On an RTX 5090 at full occupancy (170 SMs x 64 warps = 10,880 warps, approximate hardware maximum; +the measured version 2 kernel ran 24 warps per SM, 4,080 warps, `docs/bench-log.md` M11, 4 October 2026): + +| Scratch per warp | 10,880 warps | 4,080 warps (measured occupancy) | Table + scratch at 10,880 | Under 6 GB with a 1 GiB table | +|---|---|---|---|---| +| 32 KiB | 340 MiB | 128 MiB | 1,364 MiB | yes | +| 128 KiB | 1,360 MiB | 510 MiB | 2,384 MiB | yes | +| 1 MiB (the first experiment) | 10,880 MiB | 4,080 MiB | 11,904 MiB | no | + +## 2. Question 1: uniformity of what is written + +### 2.1 As functions + +The fill of word j of slot s for lane nonce n is `splitmix32(((n ^ seed[j]) + s x 0x9e3779b1 + (j + 1) x 0x85ebca77))`. +`splitmix32` is a bijection of its 32-bit input; for fixed (seed, s, j) the input is a bijection of n. So over any +2^32 consecutive nonces every 32-bit value appears once as the fill of (s, j): uniform. Test +`fill_is_a_bijection_of_the_nonce`: 2^16 consecutive nonces give 2^16 distinct words for 7 slots x 3 word +positions; the fill of lane l at base b equals the fill of lane 0 at base b + l; it wraps with the nonce +(base 0xffffffe0, lane 32 equals nonce 0). + +The rewrite `(x ^ w1, rotl(x, 7) ^ w2, x + w0)` is, for fixed old content w, a bijection of the fold value x in +EACH word. Test `rewrite_is_a_bijection_of_the_fold_value`: 2^16 consecutive x give 2^16 distinct words in each +position for 16 random w. Consequence: a uniform x gives a uniform word in every position, and the three words +are three images of the same x, so a rewritten slot carries exactly 32 bits of new state behind 96 bits of +storage (from w and any one written word, x is recovered; the test checks all three inversions). + +The fold value x is `fold(dst, w)`, a bijection of `dst` for fixed w (xor, then rotate-multiply-xor twice; the +multiplier is odd). So the written words are uniform whenever `dst` is, and `dst` is a register of the running +program. + +### 2.2 The attack: what a chip can precompute + +The fill is a pure function of (seed, nonce, slot): precomputable, and meant to be (the verifier computes it too). +A chip never stores a fill; it computes it in about 10 integer operations when a slot is first touched. The written +words depend on `dst`, the register state at that instruction, which depends on every earlier instruction of the +hash, including the dataset loads. Nothing about them is precomputable before the hash runs. This is the whole of +what question 1 can give: the writes are as unpredictable as the registers. What that is worth is question 2. + +### 2.3 The stats run (the `TESTS.md` section 3 shape) + +Test `written_words_unbiased_and_rehit_rates`, M5 Max, 5 October 2026, `cargo test --test scratch`: for each +class, programs of `igneum-genesis`, `igneum-genesis/stats1`, `igneum-genesis/stats2`, 2^11 units each (196,608 +hashes per class), closed-form dataset, every read-modify-write traced (`verify::interpret_warp_scratch`). Ones +count per bit of every written word and of the change each rewrite makes (written XOR read), sigma = sqrt(N)/2, +limit 6 sigma like the acceptance rule's output check. + +| Class | Slots per lane | RMW per hash per lane | Rewrites traced | Max bias, written words (sigma) | Max bias, written XOR read (sigma) | +|---|---|---|---|---|---| +| scr2k32 | 64 | 16 | 3,145,728 | 2.61 | 3.40 | +| scr4k32 | 64 | 32 | 6,291,456 | 2.18 | 3.81 | +| scr8k32 | 64 | 64 | 12,582,912 | 3.63 | 2.25 | +| scr2k128 | 256 | 16 | 3,145,728 | 3.36 | 2.19 | +| scr4k128 | 256 | 32 | 6,291,456 | 2.71 | 3.68 | +| scr8k128 | 256 | 64 | 12,582,912 | 2.73 | 2.60 | + +576 bit positions (6 classes x 3 words x 32 bits) at under 4 sigma is what fair coins give. Verdict: no structural +bias in what is written. Like `TESTS.md` section 3 this is a sanity check, not a proof of strength. + +## 3. Question 2: no short cut avoids the writes + +### 3.1 Inside a unit: the chain is dependent + +Slot s of lane l, touched d times in a unit, holds `w_d = rewrite(x_d, w_{d-1})`, `w_0 = fill`, with +`x_i = fold(dst_i, w_{i-1})`. `x_i` depends on the slot content before it, which depends on every earlier fold +value of that slot; and `dst_i` is the register state, which the earlier fold values entered. Test +`slot_is_replayable_from_its_fold_values`: a slot after 64 read-modify-writes is reproduced from the fill and the +64 fold values; dropping one diverges. So a chip cannot skip a write and still read the slot later. It has three +ways to hold a slot, all exact: + +| Store | Bytes per lane | Cost on a re-hit | +|---|---|---| +| Dense: every slot, 12 data bytes plus a valid bit | 12 x slots: 776 (64 slots), 3,104 (256), 24,832 (2,048) | one SRAM read | +| Sparse: only touched slots, 12 bytes plus a slot index | about 13 x distinct: 185 to 820 (table below) | one lookup | +| Implicit: only the fold values, 4 bytes plus a slot index per read-modify-write, replay on a re-hit | 5 x 8k: 80 (scr2), 160 (scr4), 320 (scr8) | d rewrites of 5 integer ops | + +The implicit store is smaller than the dense one whenever `slots > 8k / 3`: at scr4 above 10.7 slots, at scr8 +above 21.3. So "the smallest scratch at which keeping it implicitly is dearer than storing it" is 8k/3 slots per +lane, 2.7 to 5.3 KiB per warp at scr4 to scr8. Every size on the table, 32 KiB and above, is past it: a chip +keeps the scratch implicitly in 80 to 320 bytes per lane at any nominal size, and the replay cost is bounded by +the re-hit depth, which the next table measures. + +### 3.2 The re-hit rate at 64 and 256 slots (and at 2,048) + +Measured in the same test run (every read-modify-write of 196,608 hashes per class traced; a re-hit is a read of a +slot the same unit wrote earlier). Birthday: `distinct = S (1 - (1 - 1/S)^n)` for n uniform draws from S slots. + +| Class | S | n = RMW per hash | Distinct slots, birthday | Re-hits, birthday | Re-hit %, birthday | Re-hit %, measured | Max chain depth seen | Slot histogram against uniform | +|---|---|---|---|---|---|---|---|---| +| scr2k32 | 64 | 16 | 14.26 | 1.74 | 10.9 | 12.58 | 7 | chi2 z 22,023; hottest slot 2.74x, coldest 0.83x | +| scr4k32 | 64 | 32 | 25.33 | 6.67 | 20.8 | 21.47 | 8 | z 10,880; 1.87x, 0.92x | +| scr8k32 | 64 | 64 | 40.64 | 23.36 | 36.5 | 36.99 | 9 | z 7,587; 1.39x, 0.91x | +| scr2k128 | 256 | 16 | 15.54 | 0.46 | 2.9 | 3.84 | 5 | z 19,146; 5.10x, 0.82x | +| scr4k128 | 256 | 32 | 30.14 | 1.86 | 5.8 | 6.25 | 6 | z 9,632; 3.06x, 0.90x | +| scr8k128 | 256 | 64 | 56.72 | 7.28 | 11.4 | 11.89 | 6 | z 5,873; 1.98x, 0.90x | +| 1 MiB (not run) | 2,048 | 32 | 31.76 | 0.24 | 0.8 | | | | + +Two readings. First, the slot a read-modify-write addresses is the low 6 or 8 bits of a program register, and +those bits are not uniform: `or` sets them, `mul` clears them, so one slot of 256 is addressed 5.1 times as often +as the mean and the re-hit rate runs 2 to 33 percent above the birthday rate. For the dataset the same bias on the +low bits of a 28-bit address is harmless (it moves the read inside an item); for a 64-slot scratch it concentrates +the chain. Second, the chain depth is small: at scr4k32 the deepest slot in 196,608 hashes saw 8 earlier +read-modify-writes; a replay costs at most 8 x 5 integer operations, against about 1,170 for one dataset item. + +### 3.3 The live state is bounded by the read-modify-write count, not by the size + +The verifier evaluates one unit from nothing but (program, day, nonce group): `ScratchModel::new` per unit, +`verify.rs:296`. Every conforming GPU must therefore start every unit from the fill, which the tag does +(section 4). So no state crosses a unit boundary, and the state a unit can ever read back is what it wrote itself: +at most 8k slots per lane. The nominal size only sets how often those 8k writes land on the same slot (the table +above). The scratch's "memory" is 8k x 16 bytes per lane of touched slots, 256 bytes to 1 KiB at scr2 to scr8, +and a chip holds it implicitly in 80 to 320 bytes. + +The attack of rolling back or sharing scratch between units has nothing to take: a unit starts from the fill +whatever ran before it, so a chip that clears 64 valid bits per unit has rolled back, and nothing one unit wrote +is readable by another. The CPU verifier is that chip. + +### 3.4 The named chip, and what the scratch costs it + +The strongest chip the plan has priced (coordinator, 5 October 2026): the whole 256 MiB cache on the die, computing +every dataset item on the fly. Its cache SRAM, from `docs/analysis/sram-mirror.md` revision 2 (`ca2-analysis` +e6085c6), headline at shipped-product density / bit-cell lower bound, dollars per good die approximate: 164 / 83 +mm^2 and $30 / $13 at N7 (shipped density from AMD 3D V-Cache, 64 MB on 41 mm^2, Hot Chips 2021); 128 / 64 mm^2 and +$46 / $21 at N5, N3E and Intel 18A (TSMC N5 HD macro 31.8 Mib/mm^2 after assist overhead, SemiAnalysis, December +2022); 106 / 54 mm^2 and $56 / $26 at N2; with a 96 MB hot table 226 / 114 at N7, 175 / 89 at N5, 146 / 74 at N2. +The chip's cache cost in the table below is the N5 headline, 128 mm^2 and $46 per good die. It computes every item +through the mixer (`docs/analysis/m16-recompute-attacker-2026-10-05.md`: 128 items per hash, about 1,170 integer +operations per item, 150,000 per hash; at a 50 T op/s integer budget equal to a 5090's, approximate, 0.33 Ghash/s). +Against the measured version 2 rate of the RTX 5090, 139.7 MH/s (`docs/bench-log.md` M11, 4 October 2026), that is +2.4x before any fixed-function factor, 7x with the 3x the M16 analysis allows (approximate). + +Units in flight on that chip. It has no DRAM latency to cover: every one of its 1,024 cache reads per hash is an +on-die SRAM read. Its hash latency is the dependent chain: 128 items x (8 dependent SRAM reads plus 9 mixer +applications). At about 10 ns per on-die read and about 40 ns per 130-operation mixer on a 16-wide integer +pipeline at 2 GHz (both approximate), an item is about 0.4 us and a hash about 50 us; at 0.33 Ghash/s that is +about 17,000 hashes in flight, 530 units of 32 lanes. A tighter pipeline halves it. The GPU covers DRAM latency (40 to 48 ns row +cycle, MEMSYS 2018, more under load) with 130,560 lanes in flight at the measured occupancy (4,080 warps x 32), 348,160 at +full occupancy, that is 8 to 20 times more lanes than the chip needs. + +What the scratch costs that chip, per variant, with the arithmetic: + +Chip cache mirror: 128 mm^2, $46 per good die (N5 headline; 64 mm^2, $21 bit-cell lower bound). Chip scratch SRAM at +the same two densities (2.1 MB/mm^2 headline, 4.2 MB/mm^2 lower bound at N5): + +| Variant | Dataset loads per hash | Chip ops per hash | Chip rate at 50 T op/s | 5090 rate | Chip gain | Chip scratch SRAM at 17,000 lanes, implicit store | Same, dense 64-slot store | Dense store as mm^2, headline / lower bound (N5) | Share of the 256 MiB mirror (any density) | +|---|---|---|---|---|---|---|---|---|---| +| scr0 (control), 128 loads | 128 | 150,000 | 333 MH/s | 139.7 measured | 2.4x | 0 | 0 | 0 | 0 | +| 12.5% replaced (scr2) | 112 | 131,400 | 381 | 160 projected (128/112 x 139.7) | 2.4x | 1.4 MB | 13 MB | 6.2 / 3.1 mm^2 | 4.9% | +| 25% replaced (scr4) | 96 | 112,800 | 443 | 186 projected | 2.4x | 2.7 MB | 13 MB | 6.2 / 3.1 | 4.9% | +| 50% replaced (scr8) | 64 | 75,600 | 661 | 279 projected | 2.4x | 5.4 MB | 13 MB | 6.2 / 3.1 | 4.9% | +| 12.5% added (16 RMW beside 128 loads) | 128 | 150,200 | 333 | 139.7 or below | 2.4x or more | 1.4 MB | 13 MB | 6.2 / 3.1 | 4.9% | +| 25% added | 128 | 150,400 | 332 | 139.7 or below | 2.4x or more | 2.7 MB | 13 MB | 6.2 / 3.1 | 4.9% | +| 50% added | 128 | 150,800 | 332 | 139.7 or below | 2.4x or more | 5.4 MB | 13 MB | 6.2 / 3.1 | 4.9% | +| 256-slot dense store (128 KiB class), any share | | | | | | | 53 MB | 25 / 12.6 | 20% | + +How the rows are computed: a read-modify-write costs the chip about 12 integer operations (fold and rewrite) and +one SRAM access; replacing a load removes an item derivation (1,170 operations); the 5090's rate for a replaced +load is projected from the measured distinct-load bound (the card's rate tracks distinct dataset loads per hash, +`docs/bench-log.md` 3 October, 23.7 G loads/s at 1 GiB; the readwidth agent's M5 Max measurement of the night, +relayed by the coordinator, shows the same: 27.7 MH/s at v2 to 29.4-31.7 at 25 percent replaced and 44.4-49.1 at 50 +percent, 32 KiB per warp). The scratch SRAM is 17,000 lanes x 80 to 320 bytes (implicit) or x 776 bytes (dense at +64 slots) or x 3,104 bytes (dense at 256 slots); its share of the mirror is a ratio of bytes, 4.9 or 20 percent, +whichever density is used for both; the implicit store (the chip's cheaper choice at every size, section 3.1) is +0.5 to 2 percent. The chip's gain is set by operations per dataset item and the GPU's distinct-load bound, and the +scratch touches neither. + +Plain answer to the coordinator's question: no read-modify-write share under the 6 GB cap, replaced or added, +brings the named chip under 2x. The share would be chosen as the smallest at which the chip falls under 1.5x, and +there is none: the gain is 2.4x at 0, 12.5, 25 and 50 percent, 32 or 128 KiB. This changes nothing about the public +claim that layer 3 would have changed: the claim must rest on the mixer, not on the scratch. + +The lever that does move that chip, from the M16 table, beside it: + +| Mixer cost multiplier | Chip ops per hash | Chip rate | Gain against 139.7 MH/s, no fixed-function factor | With a 3x factor (approximate) | CPU verify per warp (M16 table, scaled from 0.41 to 1.2 ms) | 5090 daily dataset build | +|---|---|---|---|---|---|---| +| x1 (today) | 150,000 | 333 MH/s | 2.4x | 7.2x | 0.4 to 1.2 ms | 13.4 ms | +| x2 | 300,000 | 167 | 1.2x | 3.6x | 0.8 to 2.4 ms | 27 ms | +| x4 | 600,000 | 83 | 0.6x | 1.8x | 1.6 to 4.8 ms | 54 ms | +| x8 | 1,200,000 | 42 | 0.3x | 0.9x | 3.3 to 9.6 ms | 107 ms | + +The mixer multiplier leaves the honest hash rate untouched (the miner pays the mixer once a day), costs the chip +linearly, and is bounded by the 10 ms verification gate (x8 is at the gate's edge on this core, and the 2019-class +core of O-1.14 is unmeasured). The scratch costs the honest GPU a measured share of its rate when it spills the +cache and nothing when it does not, and costs the chip a few megabytes. The comparison is not close. + +### 3.5 Where the GPU's writes would cost DRAM latency, and why that does not help + +The GPU's hot scratch footprint is not the nominal size either: it is the slots in-flight units have touched, +about `warps x 32 lanes x distinct slots x 16 bytes` (x 2 at a 32-byte sector, approximate): on the 5090 at 4,080 +resident warps and scr4, 25.3 slots at 64 or 30.1 at 256, 53 to 63 MB of slots, 100 to 125 MB in sectors, around +the card's 96 MiB L2 (`docs/bench-log.md`, 3 October). The readwidth agent's M5 Max rows (coordinator's message: +the rate rises with the share at 32 and 128 KiB) show the scratch sitting in that chip's caches at 4,096 warps. +To push the writes to DRAM latency the hot footprint must pass the last-level cache at the resident count: +`96 MiB / 4,080 warps = 24 KiB per warp`, which at 512 bytes of touched slots per lane per read-modify-write slot +means `8k x 512 B > 24 KiB`, k above 6 (above 48 read-modify-writes per hash) at ANY nominal size on the table, or +a higher resident count. That fits the 6 GB cap (it is the hot set, not the arena, that matters), and it costs +the honest miner a DRAM-latency read-modify-write per slot (a DRAM row cycle is 40 to 48 ns across DDR4, GDDR5 and +HBM2, Li, Reddy and Jacob, MEMSYS 2018; the loaded latency a GPU kernel sees is higher, approximate; DRAM latency +improved 1.3x in two decades while bandwidth improved 20x, Chang 2017, so no memory technology an attacker could +buy removes it, and no shipped mining chip has used HBM or stacked memory) while the named chip still keeps the same +hot set in a few megabytes of SRAM at 8 to 20 times fewer lanes in flight. The write path cannot be made to cost the +chip more than the GPU, because the GPU must keep 8 to 20 times more of it live. + +## 4. Question 3: the verifier's one-warp simulation is exact + +### 4.1 Lazy fill on both sides + +The CPU initialises lazily with a written bit per (lane, slot), one model per unit. The GPU initialises lazily with +a 32-bit tag in word 0 of each 16-byte slot: a slot whose tag equals the unit's tag reads as written, any other +reads as the fill (`emit.rs:143-157`). There is no explicit fill and no reset between units of a persistent warp +(`emit.rs:159-175`: the loop over `g_` keeps the arena). The two agree if and only if, when a unit first touches a +slot, that slot does not already carry the unit's tag. That is: + +1. Tags are unique over the life of the arena's contents (`tag = salt + g_`, `salt` the host's running counter). +2. The arena holds no word equal to a live tag in a slot's tag position before the unit writes it. + +### 4.2 The attacks (the bug classes) + +| Case | What happens | Today | +|---|---|---| +| Recycled allocation | A fresh process starts `salt` at 1 (`packbench.swift:144`, `host.c:1027`). If the driver hands back the previous process's arena with its contents (Metal, CUDA and OpenCL do not promise zeroed memory, approximate), slots tagged 1..N from the old run match the new run's first units exactly, and those units read stale words instead of the fill: a CPU mismatch on every colliding slot. | not guarded; passes on this Mac because fresh allocations read as zero in practice and tag 0 is never issued (luck, not contract) | +| Tag counter wrap | `salt` is 32 bits and advances by units per launch. A 5090 at 139.7 MH/s runs 4.37 M units/s, 2^32 units in 984 s: the counter wraps every 16.4 minutes on one card (81.8 minutes on the M5 Max at 28 MH/s). After the wrap a slot whose LAST writer carried the repeated tag reads as written. With 10,880 arenas each slot is rewritten about 395,000 times between two uses of one tag (at 64 slots a unit leaves a slot untouched with probability 0.60; 0.60^395,000 is 0), so on a full card the wrap is harmless in practice; on a one-warp launch repeated 2^32 times it is not. | not guarded | +| Tag 0 on zeroed memory | A host that starts `salt` at 0 gives unit 0 the tag 0, which a zeroed arena carries in every slot: unit 0 reads zeros for every first touch. | both hosts start at 1; nothing in the pack says they must | +| `groups` not a multiple of the warp count | Warps run different trip counts; the OpenCL local-memory exchange path carries a barrier inside the loop (spec 1.9), so a short warp hangs or desynchronises. | `packbench` refuses it; `host.c` rounds the batch | + +### 4.3 The host contract that makes the simulation exact + +A host of a scratch class MUST: allocate the arena as `warps x 32 x words_per_lane` words and zero it; issue tags +from a 32-bit counter that starts at 1 and advances by the unit count of every launch; zero the arena again before +any launch whose tags would pass 2^32 - 1 (tag 0 is never issued); launch `groups` as a multiple of the warp count. +The zeroing costs one memset of the arena (340 MiB at 32 KiB x 10,880 warps) every 2^32 units, 16 minutes on a +5090. This is the class fix for all four rows: with it the GPU's tag test and the CPU's written bit are the same +predicate. + +### 4.4 The tests (Metal, M5 Max, 5 October 2026) + +Two consecutive units on one persistent warp and the wrap inside a launch (`packbench --warps 1`, +`--batch-base 4294967040`, the option added on this branch); the hand-built edge programs that force every +read-modify-write of a hash onto one slot (so two consecutive units on one arena collide on every slot); the +deliberate breaks. Results in section 7.2. On the CPU, the same edge programs against an independent hand model +(a second interpreter with its own slot store, `tests/scratch.rs`): 56 of 56 cases match, and the hand model with +its rewrite words swapped mismatches on every case (the comparison has teeth). + +## 5. Question 4: the attack surface of the writes + +| Surface | Argument | Test | +|---|---|---| +| Out of bounds | `s_ = rN & (slots - 1)`, so `s_ < slots`; the lane's arena is `(warp_ x 32 + lane) x 4 x slots` words from the base, the access is `arena + 4 x s_ + 0..3`, the largest index is `warps x 32 x 4 x slots - 1`, the host's allocation. The emitter has one scratch template (`emit.rs:143`) and it masks. | `scr_packs_regenerate_and_pass_the_static_scratch_check`: 42 of 42 emitted kernels (7 scr packs x 6 files, the OpenCL bound file carrying two kernels) regenerate byte for byte from program.json and pass the text check: k masked slot definitions with the class mask, k tagged stores, 3k fill calls, one arena definition with the class stride, one tag definition, no `scratch[`; six deliberate breaks caught (section 8) | +| Aliasing between lanes | Lane-major: lane l of warp w owns words `[(32w + l) x 4S, (32w + l + 1) x 4S)`; two (w, l) pairs give disjoint ranges. Inside the range a slot is 4 words at `4 x s_`, so two slots of one lane are disjoint too. | the `lanevar` edge program: one init-dependent slot per lane, 32 lanes at 64 slots share slots in pairs by the birthday bound; any cross-lane aliasing would change the fold; 128 of 128 lanes on Metal (section 7.2) | +| Wave64 (two logical units in one hardware wave) | `warp_ = tid >> 5`, so the two halves get `warp_ = 2w` and `2w + 1`, two arenas; `gbase` and `tag` are per `g_`, per half. | not run on wave64 hardware (the OpenCL emulator's persistent launch is on the readwidth commit; unverified here) | +| Determinism: alignment | A slot is 16 bytes at byte offset `16 x (lane_base + s_)`; the arena base is the buffer base: Metal, CUDA and OpenCL allocations are at least 128-byte aligned (CUDA 256, OpenCL `CL_DEVICE_MEM_BASE_ADDR_ALIGN` at least the largest built-in type, approximate from memory), so every 16-byte vector access is aligned. | Metal: every run of section 7 | +| Determinism: ordering | A lane's two read-modify-writes of the same slot in one hash are a load and a store, then a load and a store, from one thread to one address: program order within a thread holds in every model. No other thread touches the slot (aliasing row), so no atomics, fences or barriers are needed and none are emitted. | `slot0` and `sixteen` edge programs: 64 and 128 dependent read-modify-writes on one slot per lane per hash, standalone and as the second unit on a warp | +| Determinism: vendors | The statement is integer only: xor, rotate by immediate, multiply, add, a 16-byte load and store. Bit-exact across Metal, CUDA and OpenCL by construction; measured only on Metal here. | Metal; CUDA and OpenCL runs are PC jobs (not mine tonight) | +| 32-bit nonce wrap | `gbase = baseNonce + g_ x 32` and `nonce = baseNonce + gid` wrap in 32-bit arithmetic; `scr_fill(gbase + lane)` wraps like the CPU's `base.wrapping_add(lane)`; `out[gid]` indexes by launch position, not by nonce. An aligned unit never straddles 2^32 (spec 1.9), so the wrap case is a launch whose unit SEQUENCE crosses it. | `packbench --batch-base 4294967040 --batch-log2 9`: 16 units from 0xffffff00, the ninth at gbase 0; fingerprint identical at 1 and 4 warps (section 7.2); every fuzz pack runs that launch | + +## 6. Question 5: what a vector for the scratch class must carry + +Before a scratch pack can be a conformance vector (plan step 4, "only then a vector"), it must carry, beyond what +`igneum-program-pack-3` carries today: + +1. The class in the program id and the pack (`scrk`: it is, `program_id_class`, `generator.rs:400-412`) + and the geometry (slots per lane, words per lane, bytes per warp: it is, `program.h`). +2. The fill and the rewrite as text (it is, `program.json` "scratch"). +3. The host contract of section 4.3 as text in `program.h` and `program.json`: tag counter from 1, zero at + allocation and at the wrap, `groups` a multiple of the warp count. Not there today. +4. Vectors that exercise the tag path, which the three standard units do not reliably: two consecutive units on + one warp (bases 0 and 32 in one one-warp launch) for a program whose consecutive units collide on a slot. At + 64 slots any generated program collides (25 touched of 64 per unit; the broken-tag run of section 8 was caught by + base 1,000,000, a warp's 16th unit, and NOT by a two-unit launch whose vectors lack base 32). At 2,048 slots two + consecutive units share a touched slot with probability about 0.4 (32 x 32 / 2,048 expected overlaps = 0.5), so + the standard vectors would miss a broken tag at the 1 MiB size with probability about 0.6 per unit pair. The + edge programs `slot0` and `sixteen` collide on every slot at every size: a vector set should carry one. +5. A unit in the top 256 nonces with the launch crossing 2^32 (`--batch-base` near the top, at least two warps). +6. The batch fingerprint declared independent of the warp count (`8c07620f4d9adefd` for scr4k32 at 2^12 nonces + from base 0 at 1, 2 and 128 warps, section 7.2): unit independence is the property the per-unit reset gives, and + a fingerprint that moved with the warp count would mean a unit read another unit's slot. + +## 7. Tests and results + +### 7.1 CPU (`igneum-pow/tests/scratch.rs`, `cargo test -j4 --test scratch`, M5 Max, 5 October 2026, 3.6 s) + +| Test | What | Result | +|---|---|---| +| `rewrite_is_a_bijection_of_the_fold_value` | 16 random slot contents x 2^16 consecutive fold values, each written word distinct; the three inversions | pass | +| `fill_is_a_bijection_of_the_nonce` | 7 slots x 3 words x 2^16 nonces distinct; lane and base interchange; wrap | pass | +| `written_words_unbiased_and_rehit_rates` | 6 classes x 3 seeds x 2^11 units, every rewrite traced: bias within 6 sigma (worst 3.63), re-hit rate within 0.9x to 2x of birthday, slot histogram, depth histogram | pass (tables of sections 2.3 and 3.2) | +| `edge_programs_match_the_hand_model` | 7 edge programs x 2 geometries x 4 bases (0, 32, 0x7ffffff0, 0xffffffe0) against an independent hand model; the slots driven and the re-hit counts as built; the mutated hand model mismatches | 56 of 56 pass, 56 of 56 teeth | +| `scr_packs_regenerate_and_pass_the_static_scratch_check` | 7 scr packs: program and program id from program.json, 6 kernel texts byte for byte, static scratch check on all 42, the pack's vectors from the CPU; six deliberate breaks caught | pass | +| `fuzz_scr_programs_cpu` | 200 generated programs over the six classes, generator contract and acceptance on every one, 4 units each (one in 0..224, one around 2^31, one in the top 256 nonces, one uniform), traced run equal to the untraced run, every slot inside the lane; writes the 214 packs for Metal with `IGNEUM_SCRATCH_PACKS_OUT` | pass; 200 of 200 have a unit in the top 256 | +| `slot_is_replayable_from_its_fold_values` | 64 dependent read-modify-writes replayed from the fill and the fold values; one dropped diverges | pass | + +The rest of the crate: 33 of 34 lib tests and all pack tests pass; `verify::tests::fold_and_wide_fetch` fails on the +readwidth tip itself (`verify.rs:508`, `k as u32 * 0x9E37_79B1` overflows under the test profile's overflow +checks; the readwidth agent's test, reported to its owner, not touched here). + +### 7.2 Metal (`proto-metal/packbench` built from this branch, M5 Max, 5 October 2026, under `with-lock.sh run`) + +| Run | Launch | Expected | Result | +|---|---|---|---| +| scr4k32, standard pack | 2,048 warps, 2^24 nonces, 1 batch | 3 of 3 standalone, 3 of 3 in batch | PASS, fingerprint `3d1af881bd978fb9`; 1.8 s wall for the whole run (compile, cache, 1 GiB build, vectors, batch) | +| scr4k32, warp-count independence | 2^12 nonces (128 units) at 1, 2 and 128 warps | one fingerprint | `8c07620f4d9adefd` at all three, PASS | +| scr4k32, wrap inside the launch | 512 nonces from 0xffffff00 at 1 and 4 warps | one fingerprint, the base-0 vector inside the window after the wrap | `8e9e233234d3a297` at both, in-batch 1 of 1, PASS | +| scr4k32, broken tag (`tag = salt`), standard vectors | 2,048 warps, 2^24 | the base-1,000,000 vector (warp 530's 16th unit) fails | standalone 3 of 3, in batch 2 of 3, overall FAIL (caught) | +| scr4k32, broken tag, two units on one warp | 1 warp, 2^6 | nothing to catch it: the standard vectors have no base 32 | standalone 3 of 3, in batch 1 of 1, PASS (missed: the point of section 6 item 4) | +| 14 edge packs (7 programs x 32 and 128 KiB), run A | 1 warp, 2^6 (units at bases 0 and 32 on one arena) | 4 of 4 standalone, 2 of 2 in batch each | 14 of 14 PASS (56 of 56 standalone units, 28 of 28 in batch) | +| 14 edge packs, run B | 1 warp, 2^9 from 0xffffff00 (16 units on one arena, the wrap inside) | 4 of 4 standalone, 3 of 3 in batch each | 14 of 14 PASS (56 of 56, 42 of 42) | +| edge `slot0` at 32 and 128 KiB, broken tag (`tag = salt`) | 1 warp, 2^6 | standalone 4 of 4, in batch 1 of 2, FAIL | as expected at both geometries: the second unit read the first's slot 0 and FAILED; the standalone units passed | +| edge `slot0` at 32 KiB, broken lazy fill (`m_` forced to all ones: a first touch reads the stale words) | 1 warp, 2^6 | standalone fails | 0 of 4 standalone, 0 of 2 in batch, FAIL (lane 0 of base 0: GPU `64b49aeb987dae69`, expected `9ff3a2021f66b5be`) | +| 200 fuzz packs (scr2k32 29, scr4k32 26, scr8k32 42, scr2k128 32, scr4k128 26, scr8k128 45; datasets 64 MiB, 256 MiB, 1 GiB) | 2 warps, 2^9 from 0xffffff00 (8 units per warp, the wrap inside) | 4 of 4 standalone, 2 of 2 in the window, 200 of 200 PASS | 200 of 200 PASS: 800 of 800 standalone units (25,600 hashes), 400 of 400 in batch; 91 s for the 200 runs | + +Totals on Metal: 228 of 228 runs PASS where a pass was expected, 3 of 3 FAIL where a failure was built in. + +## 8. Deliberate breaks (the watcher rule) + +| Break | Where | Caught by | Evidence | +|---|---|---|---| +| One slot mask dropped (Metal) | copy of scr4k32 `program.metal` | static check: "masked slot followed by the load: 3, expected 4" | test output | +| Mask 63 changed to 127 on every RMW (Metal) | same | "masked slot followed by the load: 0, expected 4" | test output | +| Arena stride 256 changed to 128 words (Metal) | same | "arena definition: 0, expected 1" | test output | +| A stray `arena[0]` and `scratch[1]` access (Metal) | same | "arena mentions: 10, expected 9; direct scratch indexing: 1, expected 0" | test output | +| One slot mask dropped (OpenCL, CUDA) | copies of scr4k32 `kernel.cl`, `kernel.cu` | "masked slot followed by the load: 3, expected 4" | test output | +| Wrong class geometry or RMW count or kernel count passed against a right text | the same text | the check fails | test output | +| `tag = salt` (every unit of a launch shares the tag) | copy of scr4k32 `program.metal`, on the GPU | the base-1,000,000 vector in a 2,048-warp batch | `vectors standalone 3/3, in batch 2/3`, overall FAIL | +| the same on the `slot0` edge pack, two units on one warp | on the GPU | in-batch 1 of 2 | bench-log entry | +| lazy fill broken (`m_` all ones) | copy of the `slot0` edge pack, on the GPU | standalone vectors | bench-log entry | +| The hand model's rewrite words swapped | `tests/scratch.rs` | every edge case mismatches | 56 of 56 | + +The out-of-bounds break (mask dropped) was not run on the GPU on purpose: Metal does not bounds-check device +buffers (`TESTS.md` section 5), so a run would read another lane's or another buffer's words and "did not crash" would +prove nothing. The static check is the guard, as it is for the dataset mask. + +## 9. What is unverified + +1. CUDA and OpenCL runs of the scratch packs on NVIDIA and AMD (PC jobs, reserved for the readwidth agent tonight); + the 5090's rate per variant, so the "projected" column of section 3.4 is the distinct-load bound, not a + measurement. Wave64 hardware for the two-arena argument. +2. The chip-side latency figures of section 3.4 (10 ns SRAM read, 40 ns mixer) are approximate; the conclusion + does not depend on them: at ten times the in-flight count the scratch is still under a sixth of the mirror. +3. The recycled-allocation case was not reproduced (it needs a driver that hands back live contents); the argument + is that nothing forbids it and the contract of 4.3 removes it. +4. The slot-bias finding (section 3.2) was measured on three seeds per class; the hottest-slot ratio will vary by + program. +5. `verify::tests::fold_and_wide_fetch` on the readwidth tip (section 7.1). + +## 10. Recommendation + +1. Layer 3 is sound as a construct: the written words are uniform, the chain inside a unit has no short cut, the + kernels cannot write out of bounds, and with the host contract of section 4.3 the CPU's one-warp simulation is + exact (14 edge packs, 200 fuzz packs, the wrap, consecutive units on one arena, on Metal). +2. Layer 3 is not sound as a chip-resistance layer, at the capped size or at any size: CPU verification resets the + scratch per unit, so its live state is 8k slots per lane whatever the arena, a chip keeps it implicitly in 80 to + 320 bytes per lane, and the named chip (on-die cache mirror plus recompute) keeps its whole scratch in 1.4 to + 13 MB of SRAM at 530 units in flight, 3 to 5 percent of its mirror. Its gain stays at 2.4x (7x with a 3x + fixed-function factor, approximate) at 0, 12.5, 25 and 50 percent, replaced or added, 32 or 128 KiB. No share + under the 6 GB cap brings it under 2x. +3. Taking the read-modify-writes from the 16 dataset loads makes the hash less memory-hard for everyone: the GPU + measured faster at every share on the M5 Max (readwidth rows), and the chip's operations per hash fall with the + loads. If a scratch is kept at all, add it beside the 128 loads. There is no reason found here to keep one. +4. The lever that moves the named chip is the M16 mixer multiplier: x2 to 1.2x, x4 to 0.6x against the measured + 5090 rate, at 0.8 to 4.8 ms of verification per warp against the 10 ms gate. Decision 2 should price that + against the gate on the 2019-class core (O-1.14) rather than layer 3. +5. If the project lead keeps layer 3 for a reason outside this analysis: ship the host contract in the pack, add the four + vector items of section 6 (consecutive units with a forced collision, the wrap launch, the warp-count-independent + fingerprint, the contract text), and run the CUDA and OpenCL twins of section 7.2 on the PCs before the class + becomes a genesis rule. diff --git a/docs/plans/release-0.3.11.md b/docs/plans/release-0.3.11.md index 3f24cc69..316bef16 100644 --- a/docs/plans/release-0.3.11.md +++ b/docs/plans/release-0.3.11.md @@ -31,6 +31,9 @@ N4/3600 and proving v1 from DAA N5; the resume fix; the Metal worker takes v3 fr | cc72f4a | CI on the merged tree, three findings fixed: the identity check's hostname pattern `MacBook` matched prose in `card-lifetime-2026-10-05.md` and `proving-methods.md` (reworded "Apple laptop"); the kit-path check flagged `tools/proving-v1/pc2-{memory-miner-on,memory-sweep,sp-curve}.ps1` for a bare `jobs\` literal in `WslPath (Join-Path ...)` (now `Test-Path` on the kit root, then `WslPath $kitFile`); the socket check flagged `tools/amd-prove/pc1-cpu-prove.ps1` and its `-sp` sibling, which the addendum said were allow-listed and were not (allowed, the CPU path starts no GPU server). The other agent's resolution had kept both ci.yml sides and bash-body-check's 24-line socket check; one slip of mine (a `git show :3:` redirect after that resolution had already committed) truncated that script to 0 lines in the working tree for a minute and was restored from HEAD | | 23bc2b2 | `packaging/mac/packaged-config.sh`: the nine-field object (section 4) in the packaged line, `--test` passes (C34: a fresh install must start on the fleet's digest) | +| a94416e | the plan draft committed to the tree before the push (the reviewer's C37) | +| after a94416e | C38 (the reviewer through the Counter ASIC coordinator): the evidence rows, the litepaper's chip bullet, `chip-model-v3.md` and the rollout plan cite files on four Counter ASIC branches that never reached the tree. Taken as docs plus standalone sources under one gate: the diff against 23bc2b2 over every input a built artefact reads (`igneum-pow/src`, `app/`, `proto-cuda/nvrtc/{packfile.h,worker.cpp,cuda_api.h,build-windows.sh}`, `proto-cuda/{host.cu,build.bat,windows-app}`, `proto-opencl/{host.c,cl_dynamic.h,build.*}`, `proto-metal`, `packaging`, `proving`, `vendor`, `.github`) must stay empty apart from files no script compiles or copies. Merged: `ca2-analysis` ee42d7c (`sram-mirror.md`, `int8-matrix-family.md`, the dot4 probe sources: `proto-metal/dot4-probe.swift` is standalone, the DMG script compiles `main.swift` alone), `ca2-epoch` e95e8b5 (`epoch-length.md`), `prover-floor` cfe3d80 (`prover-floor.md`, `tools/prover-floor/` scripts, a playbook, `proving/prover-floor/sp1-gpu-6.8.1-floor.patch`, which nothing reads: the next cut's packaging row); `docs/bench-log.md` conflicted each time (append-only: both sides kept). `ca2-soundness` a465881 conflicted in `igneum-pow/tests/scratch.rs` (add/add) and `proto-metal/packbench.swift`, code the mixer merge already carries, so its merge was aborted and `docs/analysis/scratch-soundness.md` taken alone. So G5 holds: the workers, the Metal worker and the DMG built at 23bc2b2 correspond to the tip's built inputs | + Every check at cc72f4a: identity 0 hits over 220 files, copied-sources, pinned-guests, signer-pipe, bash-body 15 bodies in 28 files, kit-path 14 of 14 kits checked, prover-socket, no-conflict-markers, workflow shell 0 findings over 35 .ps1, relay tests 17, UI tests 23. From 34b2fa0ff61087b1e711ea030a44151217666452 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 23:04:52 +0000 Subject: [PATCH 148/158] prover-floor: pc2-floor-sweep3-miner.ps1 tests its kit file before the first use (the kit-path check, C32) Co-Authored-By: Claude Fable 5.1 --- tools/prover-floor/pc2-floor-sweep3-miner.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/prover-floor/pc2-floor-sweep3-miner.ps1 b/tools/prover-floor/pc2-floor-sweep3-miner.ps1 index 8282cc4e..6a29c5a1 100644 --- a/tools/prover-floor/pc2-floor-sweep3-miner.ps1 +++ b/tools/prover-floor/pc2-floor-sweep3-miner.ps1 @@ -19,7 +19,9 @@ Start-Sleep -Seconds 45 $job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-floor-measure' }; New-Item -ItemType Directory -Force -Path $job | Out-Null function WslPath($p) { $w = (& wsl.exe -d Ubuntu-24.04 -u root -- wslpath -a ($p -replace '\\', '/') 2>$null); if ($w) { ($w -replace "`0", '').Trim() } else { '/mnt/c' + ($p.Substring(2) -replace '\\', '/') } } $jobW = WslPath $job -$emptyW = WslPath (Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json') +$kitFile = Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json' +if (-not (Test-Path $kitFile)) { Write-Output "RESULT kit missing: $kitFile; republish the fetch after any app update (C32)"; exit 2 } +$emptyW = WslPath $kitFile $points = if ($env:FLOOR_POINTS) { $env:FLOOR_POINTS } else { 'run m12 "$EMPTY" SP1_GPU_MEMORY_BUDGET_GB=12;run m12 "$V1" SP1_GPU_MEMORY_BUDGET_GB=12;run me26 "$V1" SP1_GPU_ELEMENT_THRESHOLD=67108864;run me26 "$EMPTY" SP1_GPU_ELEMENT_THRESHOLD=67108864' } $bash = @' set -uo pipefail From d3b44141d1299902917cce5d0fe21089fcd9841c Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 23:06:38 +0000 Subject: [PATCH 149/158] prover-floor: the three other PC 2 scripts test their kit file before the first use (C32) Co-Authored-By: Claude Fable 5.1 --- tools/prover-floor/pc2-floor-measure.ps1 | 4 +++- tools/prover-floor/pc2-floor-sweep1.ps1 | 4 +++- tools/prover-floor/pc2-floor-sweep2.ps1 | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/prover-floor/pc2-floor-measure.ps1 b/tools/prover-floor/pc2-floor-measure.ps1 index d72e9771..367012c6 100644 --- a/tools/prover-floor/pc2-floor-measure.ps1 +++ b/tools/prover-floor/pc2-floor-measure.ps1 @@ -18,7 +18,9 @@ Start-Sleep -Seconds 45 $job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-floor-measure' }; New-Item -ItemType Directory -Force -Path $job | Out-Null function WslPath($p) { $w = (& wsl.exe -d Ubuntu-24.04 -u root -- wslpath -a ($p -replace '\\', '/') 2>$null); if ($w) { ($w -replace "`0", '').Trim() } else { '/mnt/c' + ($p.Substring(2) -replace '\\', '/') } } $jobW = WslPath $job -$emptyW = WslPath (Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json') +$kitFile = Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json' +if (-not (Test-Path $kitFile)) { Write-Output "RESULT kit missing: $kitFile; republish the fetch after any app update (C32)"; exit 2 } +$emptyW = WslPath $kitFile $points = if ($env:FLOOR_POINTS) { $env:FLOOR_POINTS } else { 'POINTS_PLACEHOLDER' } $bash = @' set -uo pipefail diff --git a/tools/prover-floor/pc2-floor-sweep1.ps1 b/tools/prover-floor/pc2-floor-sweep1.ps1 index f7c5160b..7a38e1cb 100644 --- a/tools/prover-floor/pc2-floor-sweep1.ps1 +++ b/tools/prover-floor/pc2-floor-sweep1.ps1 @@ -18,7 +18,9 @@ Start-Sleep -Seconds 45 $job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-floor-measure' }; New-Item -ItemType Directory -Force -Path $job | Out-Null function WslPath($p) { $w = (& wsl.exe -d Ubuntu-24.04 -u root -- wslpath -a ($p -replace '\\', '/') 2>$null); if ($w) { ($w -replace "`0", '').Trim() } else { '/mnt/c' + ($p.Substring(2) -replace '\\', '/') } } $jobW = WslPath $job -$emptyW = WslPath (Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json') +$kitFile = Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json' +if (-not (Test-Path $kitFile)) { Write-Output "RESULT kit missing: $kitFile; republish the fetch after any app update (C32)"; exit 2 } +$emptyW = WslPath $kitFile $points = if ($env:FLOOR_POINTS) { $env:FLOOR_POINTS } else { 'run ctrl32 "$EMPTY" SP1_GPU_MEMORY_BUDGET_GB=32;run ctrl32 "$V1" SP1_GPU_MEMORY_BUDGET_GB=32;run b12 "$EMPTY" SP1_GPU_MEMORY_BUDGET_GB=12;run b12 "$V1" SP1_GPU_MEMORY_BUDGET_GB=12;run b16 "$V1" SP1_GPU_MEMORY_BUDGET_GB=16;run e26 "$EMPTY" SP1_GPU_ELEMENT_THRESHOLD=67108864;run e26 "$V1" SP1_GPU_ELEMENT_THRESHOLD=67108864;run e25 "$V1" SP1_GPU_ELEMENT_THRESHOLD=33554432;run b12c1 "$V1" SP1_GPU_MEMORY_BUDGET_GB=12 SP1_WORKER_NORMALIZE_PROGRAM_CACHE_SIZE=1' } $bash = @' set -uo pipefail diff --git a/tools/prover-floor/pc2-floor-sweep2.ps1 b/tools/prover-floor/pc2-floor-sweep2.ps1 index 485e0f5c..5f9058f7 100644 --- a/tools/prover-floor/pc2-floor-sweep2.ps1 +++ b/tools/prover-floor/pc2-floor-sweep2.ps1 @@ -18,7 +18,9 @@ Start-Sleep -Seconds 45 $job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-floor-measure' }; New-Item -ItemType Directory -Force -Path $job | Out-Null function WslPath($p) { $w = (& wsl.exe -d Ubuntu-24.04 -u root -- wslpath -a ($p -replace '\\', '/') 2>$null); if ($w) { ($w -replace "`0", '').Trim() } else { '/mnt/c' + ($p.Substring(2) -replace '\\', '/') } } $jobW = WslPath $job -$emptyW = WslPath (Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json') +$kitFile = Join-Path $env:LOCALAPPDATA 'igneum\app\jobs\chain-pc2-pv1c\block-83616.json' +if (-not (Test-Path $kitFile)) { Write-Output "RESULT kit missing: $kitFile; republish the fetch after any app update (C32)"; exit 2 } +$emptyW = WslPath $kitFile $points = if ($env:FLOOR_POINTS) { $env:FLOOR_POINTS } else { 'run x12 "$EMPTY" SP1_GPU_MEMORY_BUDGET_GB=12;run x12 "$V1" SP1_GPU_MEMORY_BUDGET_GB=12;run x12 "$ONE" SP1_GPU_MEMORY_BUDGET_GB=12;run x12 "$FULL" SP1_GPU_MEMORY_BUDGET_GB=12;run xe26 "$V1" SP1_GPU_ELEMENT_THRESHOLD=67108864;run xe26 "$EMPTY" SP1_GPU_ELEMENT_THRESHOLD=67108864;run x16 "$V1" SP1_GPU_MEMORY_BUDGET_GB=16;run x32 "$V1" SP1_GPU_MEMORY_BUDGET_GB=32;run x12r "$V1" SP1_GPU_MEMORY_BUDGET_GB=12 SP1_GPU_RECURSION_TRACE_ALLOCATION=100663296' } $bash = @' set -uo pipefail From c785bd3113f8589204db41fbc498162b1c9080fe Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 23:08:52 +0000 Subject: [PATCH 150/158] release-0.3.11 plan: the relay's PC1 is the 1ccfe586 box (PC 2 restarted twice by my tasks, PC 1 unreachable until the morning), the combined PC 2 job, C38 complete Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.11.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/plans/release-0.3.11.md b/docs/plans/release-0.3.11.md index 316bef16..90024780 100644 --- a/docs/plans/release-0.3.11.md +++ b/docs/plans/release-0.3.11.md @@ -50,6 +50,33 @@ Every check at cc72f4a: identity 0 hits over 220 files, copied-sources, pinned-g | PC 1 build job (the node and the app, Linux and Windows) | `IGNEUM_WIN_RELEASE=/target-integration/x86_64-pc-windows-gnu/release node tools/build-job.mjs run --node vendor/igneum-node-0311 --target ae432dc7 --targets linux,windows --no-tests` from this worktree, its own zip `build-inputs-20261005224625-29789.zip` | job `build-20261005-224654`, published 22:46:54Z (PC 1 given by the Counter ASIC coordinator at 22:4xZ: Ember's collect job closed, the AMD sweep off tonight). (pending) | | PC 2 suites | waits for the Counter ASIC coordinator's "PC 2 suites go" (agg-cost-pc2-3 on PC 2 until about 22:59Z) | (pending) | +### 3a. PC 1's app down, and the relay task that hit the wrong machine (22:31 to 23:05Z) + +PC 1's installed 0.3.10 app quit at 22:31:06Z (its last upload 22:31:08Z, run `win-ae432dc7-20261005-214041`; Ember's tune job on it +reported "aborted (the app is quitting)"; the quit's sender is C35 for the consequences reviewer) and did not come back, so PC 1 mined +nothing, its build job `build-20261005-224654` could not start and no update-now could reach it. Three relay tasks (#241 at 22:52:31Z, #243 at +22:55:10Z, #245 at 23:03:18Z) went to the relay machine named "PC1" to relaunch the app, the second ending an engine that answered nothing on +`/api/state` and the third ending every igneum process before the launch, as the app's own updater does. + +They hit the wrong machine. The relay's "PC1" is the 1ccfe586 box, the console's PC 2: both PCs carry the hostname DESKTOP-KMCV30N, the only +relay agent runs on the 1ccfe586 box and was named PC1 when the clients were set up, and the relay's PC2 entry reads "never seen". The +intake proves it: PC 2 got two new engine runs, `win-1ccfe586-20261005-225528` and `-230330`, at the exact times of #243 and #245, while PC 1 +has no run after 21:40:41Z; #243's "hung" engine, pid 26696 from 21:49:40Z, was PC 2's healthy 0.3.10 engine (my probe's "no answer" on +`/api/state` was its own fault, no token), and the node, three miners and three workers it found were PC 2's own (a 5090 and the iGPU; PC 1 +would have shown the 9070 XT too). So PC 2, the box the night's measurements run on, was force-restarted at 22:55:28Z and 23:03:30Z, which +killed the aggregation-cost agent's job 3 (re-run owed, 20 min) and ended whatever followed; its app came back each time (after #245: pid 30484, +responding, node, three miners and both workers up, the card climbing at 23:04Z). PC 1 is exactly as it was: engine down since 22:31:06Z, +no relay agent on that box, unreachable tonight; it waits for the project lead in the morning and takes 0.3.11 through the manifest at its relaunch; the +fleet runs short its 141 MH/s until then. Told the Counter ASIC coordinator at 23:05Z; it re-set PC 2's schedule (this cut's combined build +and suite job first, then the prover-floor pair, the aggregation-cost re-run, "PC 2 clear", the M16 job) and ruled that no relay task goes to +"PC1" from anyone without its word. Every relay "PC1" reading tonight was PC 2 (the AMD agent's "9070 XT absent" probes read a box that has +no 9070 XT; the hardware events are being corrected). The relay machine should be renamed PC2 (`node tools/relay.mjs name `) +and the PC 1 box get its own agent (section 11). The Mac cross-build fallback for the Windows exes was announced and not started: the +combined PC 2 job replaced it within the minute. + +| PC 2 combined job | `IGNEUM_WIN_RELEASE=/target-integration/x86_64-pc-windows-gnu/release node tools/build-job.mjs run --node vendor/igneum-node-0311 --target 1ccfe586 --targets linux,windows --node-tests "kaspa-consensus kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows" --app-tests igneum-app` from this worktree (its own zip `build-inputs-20261005230716-50058.zip`); PC 1's job removed from the jobs file so nothing double-places the exes | (pending: the id, the stages, the suites, the exes) | +| PC 2 suites | waits for the Counter ASIC coordinator's "PC 2 suites go" (agg-cost-pc2-3 on PC 2 until about 22:59Z) | (pending) | + ### 3a. PC 1's app down (22:31 to 22:5xZ) PC 1's installed 0.3.10 app quit at 22:31:06Z (its last upload 22:31:08Z, run `win-ae432dc7-20261005-214041`; Ember's tune job on it From 87dcbd804a82c8c2fe4e1470db23f6e2bc64e424 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 23:10:03 +0000 Subject: [PATCH 151/158] release-0.3.11 plan: the PC 2 job id, the rollout order as agreed Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.11.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/plans/release-0.3.11.md b/docs/plans/release-0.3.11.md index 90024780..4ed5d9d1 100644 --- a/docs/plans/release-0.3.11.md +++ b/docs/plans/release-0.3.11.md @@ -74,7 +74,7 @@ no 9070 XT; the hardware events are being corrected). The relay machine should b and the PC 1 box get its own agent (section 11). The Mac cross-build fallback for the Windows exes was announced and not started: the combined PC 2 job replaced it within the minute. -| PC 2 combined job | `IGNEUM_WIN_RELEASE=/target-integration/x86_64-pc-windows-gnu/release node tools/build-job.mjs run --node vendor/igneum-node-0311 --target 1ccfe586 --targets linux,windows --node-tests "kaspa-consensus kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows" --app-tests igneum-app` from this worktree (its own zip `build-inputs-20261005230716-50058.zip`); PC 1's job removed from the jobs file so nothing double-places the exes | (pending: the id, the stages, the suites, the exes) | +| PC 2 combined job | `IGNEUM_WIN_RELEASE=/target-integration/x86_64-pc-windows-gnu/release node tools/build-job.mjs run --node vendor/igneum-node-0311 --target 1ccfe586 --targets linux,windows --node-tests "kaspa-consensus kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows" --app-tests igneum-app` from this worktree (its own zip `build-inputs-20261005230716-50058.zip`); PC 1's job removed from the jobs file so nothing double-places the exes | job `build-20261005-230745`, published 23:07:45Z (PC 2 woken), the first job on PC 2's re-set schedule. (pending: the stages, the suites, the exes) | | PC 2 suites | waits for the Counter ASIC coordinator's "PC 2 suites go" (agg-cost-pc2-3 on PC 2 until about 22:59Z) | (pending) | ### 3a. PC 1's app down (22:31 to 22:5xZ) @@ -138,3 +138,18 @@ and N3 shape): the manifest, update-now, the hand nodes, the seed. | `explorer` d7e797c (and 3e01212) | `/api/stats` gains `proving`; `tools/ci/public-api-check.mjs` then FAILS when the live API lacks it, and ci.yml runs that check against the live site on every master push, which reads the OLD API until Vercel redeploys after the push (the reviewer's C36) | not in 23bc2b2 (only on the explorer branch); its merge needs the check to retry for a few minutes or to require `proving` only when `observer_updated_at` is newer than the commit | | `proving-v1` c36dfea | docs only, after the code tip 22c2363 | its agent's choice: docs follow | | the 0.3.10 list (`release-0.3.10.md` section 11): fork `pack-loop` 05ef0fa3, `job-console`, the rest of `opencl-rdna4`, `opencl-rdna4-telemetry` | | unchanged | + +## 5. The rollout order, as agreed with the Counter ASIC coordinator (PC 2's scheduler tonight) + +The fee-switch and N3 shape (every node in one sweep, because the digest flips to 0139ab9d...), with PC 1 out of reach: + +| Step | What | Gate | +|---|---|---| +| the floor | 154,800 minus the tip's DAA at least 10,800 at the publish (holds until DAA 144,000, about 00:45Z); past it the line is re-pinned and the DMG and installer rebuilt | read on the observer before the manifest step | +| the manifest | `--public`, `--override '' --activation-height 154800 --deadline-note "program class v3 + proving v1"`, the changelog line of section 1 | CI green on the release tip | +| update-now | the Mac (attached to node 1) and the laptop (PC 37ba0461, if it is back) first; PC 2 only on the coordinator's "PC 2 clear" (after this cut's build job, the prover-floor pair and the aggregation-cost re-run); PC 1 is unreachable (section 3a) and takes 0.3.11 through the manifest at its morning relaunch | | +| the watch | every worker starts clean on the first try with the class-aware pair; the Mac's Metal worker takes the class v3 day from the prepared pack (`--prepare-packs`); the re-fetch rule C32: an update clears the jobs folder, so the agents whose kits sit on PC 2 republish their fetches after it | | +| the hand nodes, the seed | the two scripts with the same object (`IGNEUMD` the 0.3.11 Mac node, `IGNEUMD_COMMIT=89dfcb95`; `IGNEUMD_LINUX` the zig build 63cf490d... and its sha) | after the app nodes | +| the sweep | every restarted node prints 0139ab9dc2992d449ec787d8f021974933631eb55740ab4b6ce9d5c226e72888; PC 1 joins at its relaunch | | +| the HiveOS package | republished with the new override through the `--public` step | | +| the plan, the master merge, the push | the live observer must not read stale (`public-api-check`'s other arm); the explorer branch's strict check is not in this tree | | From 9d1c494a9f182ac73103502b3370480b0394324c Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 23:15:20 +0000 Subject: [PATCH 152/158] release-0.3.11 plan: the three digests on the 0.3.11 binary (4d8f8bb6 with the live file, measured), the two-publish order (C39, C40), the HiveOS package's missing override Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.11.md | 45 +++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/docs/plans/release-0.3.11.md b/docs/plans/release-0.3.11.md index 4ed5d9d1..ac673664 100644 --- a/docs/plans/release-0.3.11.md +++ b/docs/plans/release-0.3.11.md @@ -104,6 +104,8 @@ write end the second engine and its miners had inherited (PowerShell's `Process. came. The class rule, for every job script that starts a second engine (`relay/playbooks/sweep-5090.ps1` has the same shape): no pipe into it, and its whole tree killed at the end. The installer-build fallback script of 0.3.10 starts no second engine. (pending: the reset's result, the new run id, the first STATUS line) +| The Linux workers for HiveOS | `infra/cross/build-workers-linux.sh` (zig, glibc 2.36 target) under the lock, 23:10Z | igneum-worker-cuda 4aaff27fcb26bc5fe98d2b311b9f95f099c59414a556af32080b82b41e8360db (6,755,568), igneum-worker-opencl 82d90890be36f9b795b218794062e388bfd9c6f7524fe671074cdec84c906259 (306,000), ELF x86-64 dynamic; untested on a GPU host, as the script says | +| The HiveOS package | `NODE_OUT= WORKERS_OUT= VERSION=0.3.11 packaging/hive/make-hive-package.sh`, 23:11:30Z, then `packaging/ota/publish-public.sh --hive` into `dl/public/` (the ship's deploy carries it) | `igneum-hive-0.3.11.tar.gz` c606a17043c013c411086b4abd0f38a227aa8a7db9d5934157760a3da5a5edc9 (24,496,653); no override inside (section 5) | | The DMG | `NODE=/target-integration/release/igneumd MINER=... packaging/mac/build-dmg.sh` under the lock, 22:50:0x to 22:50:51Z | `Igneum-Miner-0.3.11.dmg` b7e81d4f6f3af9f9179e29faa72c844b795df757dfb2e4cd1d56cd454e78e1e7 (41,592,041): engine 0.3.11, node 89dfcb95 Mac arm64, the 0.3.9 prover host and export, `igneum-bench` (the Metal worker) rebuilt from this tree (667,145 to 555,808 bytes, signed ad hoc), fingerprints 477bb0ef and ed9c4d2e; read back from the mounted image: `Contents/Resources/igneum-app.json` carries the nine-field `node_override_params` with N4 = N5 = 154,800 (C34) | ## 4. The override object, N4 and N5, the digests @@ -126,8 +128,45 @@ The two digest readings on the 0.3.11 Mac node (bd7f043c..., ports 60975/60976, | none (the rolling-upgrade value: both new activations at never) | `igneumd/2.1.0-89dfcb95` | c562d70e1428c9789823cc40067623b4767f7c555ce7ff4ea11c1498f013ef6c, EQUAL to the node agent's pinned test on 79bd8e10 (22:49:59Z) | | the nine-field object above | `Program class v3 from the override file: active from epoch 43 (DAA score 154800 rounded up to the epoch boundary at 154800, epochs of 3600 DAA)`, `Proving v1 from the override file: segment records paid from DAA score 154800, 8 blocks a segment, unproven after 600 DAA, aggregator share 1000 bps`, `Calibrated v1 fees ... 210000`, `Finality rule v3 ... 135200` | **0139ab9dc2992d449ec787d8f021974933631eb55740ab4b6ce9d5c226e72888** (22:50:23Z): the value every node must print after the publish; the sweep in section 9 reads it on every node | -A node on the 0.3.10 override (1f4b4425...) and a node on this object never handshake, so the rollout moves every node in one sweep (the fee-switch -and N3 shape): the manifest, update-now, the hand nodes, the seed. +| the fleet's live four-field file (what every node runs today) | `Calibrated v1 fees ... 210000`, `Finality rule v3 ... 135200`, no class v3 or proving v1 line (both at never) | **4d8f8bb668828a3dcf7b783b995f3d3ebfde32a092dd1dbd5bf4373c5c65a62c** (23:13:54Z): the 0.3.11 BINARY alone flips the digest (the 0.3.10 node prints 1f4b4425... with the same file, `release-0.3.10.md` 9a): the new fields enter the digest even at never. This is the digest of step 1 (the reviewer's C40, the Counter ASIC rollout plan's section 4 step 1); both had assumed c562d70e..., which is the no-file case | + +Three digests on the 0.3.11 binary, so two sweeps: step 1 moves every node to the binary (4d8f8bb6...) and step 2 moves every node to the +nine-field object (0139ab9d...). A node on either side of a sweep is refused by the other side (the handshake), so each sweep is one +window, as the fee switch's 8 min 47 s was. + +## 10. The next cut + +| Branch | What | Why not 0.3.11 | +|---|---|---| +| `ember-tune` b671c8b (and the tune behind it) | the C35 fix: `Cmd::Quit(&'static str)` so every "quit:" line names its sender (the window host's stdin, the host gone, `POST /api/quit`, the sweep's end), `elevation_allowed()` = Power control alone (the unattended sweep on PC 1 raised one UAC prompt at 22:30Z under the old rule), no power cap at start under `--sweep`; the quit-source hunk is separable (main.rs 2 lines, server.rs 1 line, engine.rs the Quit arm, `elevation_allowed` and its test) | arrived after the tree closed at 23bc2b2 (the app, the DMG and PC 1's job carry it); not among the branches named for this cut | +| `fud-close` (the ledger closer's main branch, a22ba27a60a6f1c64; its ready tip was due about 23:05Z) | 45 public-text fixes on the site and litepaper, spec 8.3 and 8.8, two CI checks, relay fixes; touches `packfile.h` and `host.c`, so taking it means the two Windows workers, the Mac worker and the DMG rebuilt from the merged tip (G5) | offered by the Counter ASIC coordinator at 22:5xZ after the tree closed; not among the branches named for this cut; the fork-side `ledger-fixes` is not in 0.3.11 either | +| `explorer` d7e797c (and 3e01212) | `/api/stats` gains `proving`; `tools/ci/public-api-check.mjs` then FAILS when the live API lacks it, and ci.yml runs that check against the live site on every master push, which reads the OLD API until Vercel redeploys after the push (the reviewer's C36) | not in 23bc2b2 (only on the explorer branch); its merge needs the check to retry for a few minutes or to require `proving` only when `observer_updated_at` is newer than the commit | +| `proving-v1` c36dfea | docs only, after the code tip 22c2363 | its agent's choice: docs follow | +| the 0.3.10 list (`release-0.3.10.md` section 11): fork `pack-loop` 05ef0fa3, `job-console`, the rest of `opencl-rdna4`, `opencl-rdna4-telemetry` | | unchanged | + +## 5. The rollout order: two publishes, two sweeps (the reviewer's C39 and C40, the Counter ASIC coordinator's rule, the fee-switch shape) + +Why two: the app writes the manifest's `consensus.override` at the manifest TAKE (`ota.rs` 661, `write_override`) and restarts its node with +it at the next safe window, whatever binary is installed; a 0.3.10 `igneumd` refuses a file with `program_class_v3_activation_daa` or the +proving v1 fields (`OverrideParams` is `deny_unknown_fields`) and dies at start, and the update then waits for a synced node (`engine.rs` +2211) until the slot minute or the 1,800-block rule forces it. One manifest with 0.3.11 AND the nine fields would take every 0.3.10 node +down at its next safe window: the 0.3.5 class the fee-switch plan named. And the 0.3.11 binary alone flips the digest (section 4), so the +binary move is itself a sweep. + +| Step | What | Digest after | +|---|---|---| +| 1a | the observer, node 1 and the seed on the 0.3.11 binaries with the four-field file: `IGNEUMD=/target-integration/release/igneumd IGNEUMD_COMMIT=89dfcb95 infra/devnet/restart-hand-nodes.sh ''`, then `IGNEUMD_LINUX= IGNEUMD_LINUX_SHA256=63cf490d... infra/devnet/restart-seed.sh ''`; the apps still on 0.3.10 are refused by them from this moment until each updates | 4d8f8bb6... on the three | +| 1b | the manifest: 0.3.11 with `consensus` carried over UNCHANGED (`--activation-height 135200 --deadline-note "finality v3"`, the four-field object, exactly as 0.3.10 shipped), `--public` (the HiveOS package rides along) | | +| 1c | update-now: the Mac (its card follows node 1) and the laptop first; PC 2 only on the Counter ASIC coordinator's "PC 2 clear"; PC 1 is down and unreachable (section 3a) and takes 0.3.11 through the manifest at its morning relaunch, refused until then. The watch: every app logs 0.3.11 and its node a DAA score at 4d8f8bb6...; every worker starts clean on the first try with the class-aware pair; the Mac's Metal worker takes the class v3 day from the prepared pack; C32: the agents whose kits sit on PC 2 republish their fetches after its update | 4d8f8bb6... on every reporting node | +| 2a | the floor: 154,800 minus the tip's DAA at least 10,800 (holds until DAA 144,000, about 00:45Z); past it N4 = N5 re-pinned to the first multiple of 3,600 at or above tip + 14,400, the packaged line, the DMG and the installer rebuilt; the DAA read sent to the Counter ASIC coordinator before 2b | | +| 2b | the hand nodes' and the seed's files switched to the nine-field object and restarted (the same two scripts), the manifest republished with `--override '' --activation-height 154800 --deadline-note "program class v3 + proving v1"`, update-now (the same order; PC 2 on "clear" again), the sweep | 0139ab9d... on every node | +| 3 | the plan's final sections, the merge to master (the live observer must not read stale: `public-api-check`'s other arm), the push, the report with per-machine times | | + +The HiveOS package carries NO override: `packaging/hive/h-run.sh` line 31 starts the rig's node with `--devnet --appdir --rpclisten --listen` and +the peers, no `--override-params-file`, and no HiveOS package has ever carried one, so a rig's bundled node runs on genesis params and is refused +by every devnet peer (the HiveOS path is untested on a GPU host since 4 October). "Republish with the new override" therefore needs an +`h-run.sh` change (the file written from the Flight Sheet's extra config, as `PEERS=` is), which is the next cut's; tonight's package carries +the class-aware binaries only (section 11). ## 10. The next cut @@ -151,5 +190,5 @@ The fee-switch and N3 shape (every node in one sweep, because the digest flips t | the watch | every worker starts clean on the first try with the class-aware pair; the Mac's Metal worker takes the class v3 day from the prepared pack (`--prepare-packs`); the re-fetch rule C32: an update clears the jobs folder, so the agents whose kits sit on PC 2 republish their fetches after it | | | the hand nodes, the seed | the two scripts with the same object (`IGNEUMD` the 0.3.11 Mac node, `IGNEUMD_COMMIT=89dfcb95`; `IGNEUMD_LINUX` the zig build 63cf490d... and its sha) | after the app nodes | | the sweep | every restarted node prints 0139ab9dc2992d449ec787d8f021974933631eb55740ab4b6ce9d5c226e72888; PC 1 joins at its relaunch | | -| the HiveOS package | republished with the new override through the `--public` step | | +| the HiveOS package | rebuilt from this tree's Linux node (63cf490d...) and the two Linux workers (`infra/cross/build-workers-linux.sh`, zig) with `packaging/hive/make-hive-package.sh VERSION=0.3.11`, and published through the `--public` step. It carries NO override: `packaging/hive/h-run.sh` line 31 starts the rig's node with `--devnet --appdir --rpclisten --listen` and the peers, no `--override-params-file`, and no HiveOS package has ever carried one, so a rig's bundled node runs on genesis params and is refused by every devnet peer (the HiveOS path is untested on a GPU host since 4 October). "Republish with the new override" therefore needs an `h-run.sh` change (the file written from the Flight Sheet's extra config, as `PEERS=` is), which is the next cut's; tonight's package carries the class-aware binaries only (section 11) | the coordinator told | | the plan, the master merge, the push | the live observer must not read stale (`public-api-check`'s other arm); the explorer branch's strict check is not in this tree | | From 6f95751e06ebdb40c404a653b6ba1cc6508b691f Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 23:18:28 +0000 Subject: [PATCH 153/158] Windows payload inputs: node 89dfcb95 (push-inputs.sh, the PC 2 build, the class-aware workers, signed); release-0.3.11 plan: the PC 2 job Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.11.md | 2 +- packaging/windows/node-source.pin | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plans/release-0.3.11.md b/docs/plans/release-0.3.11.md index ac673664..ad54c5c7 100644 --- a/docs/plans/release-0.3.11.md +++ b/docs/plans/release-0.3.11.md @@ -74,7 +74,7 @@ no 9070 XT; the hardware events are being corrected). The relay machine should b and the PC 1 box get its own agent (section 11). The Mac cross-build fallback for the Windows exes was announced and not started: the combined PC 2 job replaced it within the minute. -| PC 2 combined job | `IGNEUM_WIN_RELEASE=/target-integration/x86_64-pc-windows-gnu/release node tools/build-job.mjs run --node vendor/igneum-node-0311 --target 1ccfe586 --targets linux,windows --node-tests "kaspa-consensus kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows" --app-tests igneum-app` from this worktree (its own zip `build-inputs-20261005230716-50058.zip`); PC 1's job removed from the jobs file so nothing double-places the exes | job `build-20261005-230745`, published 23:07:45Z (PC 2 woken), the first job on PC 2's re-set schedule. (pending: the stages, the suites, the exes) | +| PC 2 combined job | `IGNEUM_WIN_RELEASE=/target-integration/x86_64-pc-windows-gnu/release node tools/build-job.mjs run --node vendor/igneum-node-0311 --target 1ccfe586 --targets linux,windows --node-tests "kaspa-consensus kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows" --app-tests igneum-app` from this worktree (its own zip `build-inputs-20261005230716-50058.zip`); PC 1's job removed from the jobs file so nothing double-places the exes | job `build-20261005-230745`, published 23:07:45Z (PC 2 woken), the first job on PC 2's re-set schedule; started 23:09Z, done 23:16:49Z (469 s): the Linux stage, the Windows stage 264 s, the test stage `RESULT test node [kaspa-consensus kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows] exit 0 54 s` and `RESULT test app/igneum-app [igneum-app] exit 0 6 s`; 9 outputs verified and placed: igneumd.exe be8e83c07aeae5eb6842768071735289f3ef149ed9a7088592d8c54a4c252c08 (51,321,856), igneum-miner.exe 1ba1a249e2a21d52087a81f61f37cd2e1e3273c7ec8809ef9cfaa98f015895ce (10,987,520), igneum-app.exe ab104cc0... (3,065,344, the PC's; the installer's engine is the runner's), Linux igneumd d7a2715e... (49,193,960, glibc 2.39, HiveOS), igneum-miner 09d05ff6..., igneum-app 222f30c0... | | PC 2 suites | waits for the Counter ASIC coordinator's "PC 2 suites go" (agg-cost-pc2-3 on PC 2 until about 22:59Z) | (pending) | ### 3a. PC 1's app down (22:31 to 22:5xZ) diff --git a/packaging/windows/node-source.pin b/packaging/windows/node-source.pin index 6b34aaf1..846ac7cb 100644 --- a/packaging/windows/node-source.pin +++ b/packaging/windows/node-source.pin @@ -1 +1 @@ -21d4c73c6ce32fcbd68391968e85827339a511c0 +89dfcb95be5ace1a2b7a4fb18d88aeb22023cab4 From d85f4dec2b3c30570ebef57ceceb72ac175f821a Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 23:25:52 +0000 Subject: [PATCH 154/158] igneum-census: build against class v3 (Instr width/win/off, Program class/era_bytes, fetch layout, Scratch/Hot arms) The ci run on 6f95751 (37387751432) failed in the census release build only; windows-ci was green. The census generates the lottery hash's class, so every new field takes its v2 value and the two experiment ops are unreachable. Co-Authored-By: Claude Fable 5.1 --- igneum-census/src/main.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/igneum-census/src/main.rs b/igneum-census/src/main.rs index 70f32678..bd7955ef 100644 --- a/igneum-census/src/main.rs +++ b/igneum-census/src/main.rs @@ -20,7 +20,8 @@ use std::sync::Mutex; use std::time::Instant; use igneum_pow::accept::{check as accept_check, Reject}; -use igneum_pow::generator::{candidate_from_words, generate_v1_from_words, GeneratorConfig, Instr, Op, Program, GENERATOR_VERSION, INSTR_COUNT, ITERATIONS, LANES, OP_WEIGHTS}; +use igneum_pow::generator::{candidate_from_words, generate_v1_from_words, GeneratorConfig, Instr, LoadClass, Op, Program, GENERATOR_VERSION, INSTR_COUNT, ITERATIONS, LANES, OP_WEIGHTS}; +use igneum_pow::memhard::Layout; use igneum_pow::seed::{fnv1a64, program_rng, seed_words, SplitMix64}; use igneum_pow::verify::{dataset_elem, hash_warp, splitmix32, DatasetMode, DatasetSource}; @@ -180,9 +181,9 @@ fn generate_fixed16(seed_string: &str, seed: [u32; 8], fresh: Fresh) -> Program fresh_reg[src as usize] = false; } fresh_reg[dst as usize] = true; - instrs.push(Instr { op, dst: dst as u8, src: src as u8, src2: b as u8, imm, imm2, rot, bit: bit as u8, mask }); + instrs.push(Instr { op, dst: dst as u8, src: src as u8, src2: b as u8, imm, imm2, rot, bit: bit as u8, mask, width: 1, win: 0, off: 0 }); } - Program { seed_string: seed_string.to_string(), seed_bytes: seed_string.as_bytes().to_vec(), seed, generator: GENERATOR_VERSION, attempt: 0, instrs } + Program { seed_string: seed_string.to_string(), seed_bytes: seed_string.as_bytes().to_vec(), seed, generator: GENERATOR_VERSION, attempt: 0, class: LoadClass::V2, era_bytes: None, instrs } } // --------------------------------------------------------------------------------------------------------- @@ -478,6 +479,9 @@ fn run_warp(p: &Program, base: u32, ds: &DatasetSource, acc: &mut Acc, lane_addr r[d][lane] = r[d][lane].rotate_right(src[lane] & 31); } } + // Experiment-only ops of the read-width and scratch variants; the census generates the lottery + // hash's class only, so neither op ever appears here. + Op::Scratch | Op::Hot => unreachable!("the census never generates a scratch or hot op"), Op::Mad => { let src = r[a]; let src2 = r[ins.src2 as usize]; @@ -505,7 +509,7 @@ fn run_warp(p: &Program, base: u32, ds: &DatasetSource, acc: &mut Acc, lane_addr } match memhard { Some(m) => { - m.fetch(&idx, &mut val); + m.fetch(&idx, &mut val, Layout::LINEAR); } None => { for lane in 0..LANES { From f7613e5f96e3d255dbcae6d15f9fa0bec189dc56 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Mon, 5 Oct 2026 23:56:46 +0000 Subject: [PATCH 155/158] release-0.3.11 plan: the push and both CI runs, step 1 (the hand nodes and the seed at 4d8f8bb6, the manifest with the override carried over, the Mac, PC 2 and its epoch-boundary recovery, the stall and the Mac's resume, the laptop), the next-cut list Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.11.md | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/plans/release-0.3.11.md b/docs/plans/release-0.3.11.md index ad54c5c7..9c8ee51e 100644 --- a/docs/plans/release-0.3.11.md +++ b/docs/plans/release-0.3.11.md @@ -134,6 +134,44 @@ Three digests on the 0.3.11 binary, so two sweeps: step 1 moves every node to th nine-field object (0139ab9d...). A node on either side of a sweep is refused by the other side (the handshake), so each sweep is one window, as the fee switch's 8 min 47 s was. +## 6. The push and CI + +`git push -u origin release-0.3.11` at 3b0262f (23:18:29Z, the credential helper; the pre-push hook's site flip restored), `gh workflow run +windows.yml --ref release-0.3.11` -> run 37387737179, acquired at once (GitHub operational again), green 23:23:11Z (the parse job 23:18:39 to +23:19:25Z; engine, window host, payload, installer, smoke run 23:19:31 to 23:23:11Z, the G13 step against the 89dfcb95 inputs). The main +`ci.yml` run on 3b0262f (37387751432) failed in one step, the igneum-census release build (the class v3 fields missing from the census's own +initialisers, `fetch`'s new Layout argument, no Scratch/Hot match arms; pow tests, simulators and site jobs passed); the coordinator fixed it +in this worktree as 2a62735 (`igneum-census/src/main.rs` only; `git diff --stat 3b0262f 2a62735 -- app packaging igneum-pow proto-cuda +proto-opencl proto-metal` is empty, so the Windows artefacts of 37387737179 stand) and its run 37388453875 is green (pow tests and census +build, simulators, site). The 0.3.11 CI verdict is therefore run 37388453875 on 2a62735; the Windows build is run 37387737179 on 3b0262f, +the same app sources; the merge to master goes from 2a62735. + +| File | sha256 | Size | +|---|---|---| +| Igneum-Miner-0.3.11.dmg | b7e81d4f6f3af9f9179e29faa72c844b795df757dfb2e4cd1d56cd454e78e1e7 | 41,592,041 | +| Igneum-Miner-Setup-0.3.11.exe | 84a21443f78598597f33cef307fa162e53c680dd90d29f02ceaf79ac5e231ee4 | 50,065,009 | +| igneum-windows-app.zip | ed0cf2a75a1de8897de33ddcdcdb4ea844eca6b38627d6b91c562700c9fbe5eb | 72,070,333 | +| igneum-hive-0.3.11.tar.gz (dl/public) | c606a17043c013c411086b4abd0f38a227aa8a7db9d5934157760a3da5a5edc9 | 24,496,653 | + +## 7. The rollout, step 1 (the binary sweep to 4d8f8bb6...) + +Baseline 23:19:40Z: tip DAA 139,642; the observer and node 1 on 21d4c73c at 1f4b4425...; the Mac app 0.3.10 (attached to node 1); PC 2 +0.3.10 at 120.8 MH/s; PC 37ba0461 back on 0.3.10 at 2.3 MH/s; PC 1 down since 22:31:06Z (section 3a); Sam's Mac quit since 20:47Z. + +| Step | Time | Result | +|---|---|---| +| 1a the observer | 23:23:54Z (pid 61754) | `igneumd/2.1.0-89dfcb95`, the four-field file, digest 4d8f8bb668828a3dcf7b783b995f3d3ebfde32a092dd1dbd5bf4373c5c65a62c | +| 1a node 1 | 23:24:06Z (pid 61864, caffeinate 61866) | the same | +| 1a the seed | 23:24:25Z (MainPID 125853) | `igneumd/2.1.0-89dfcb95`, the same lines; the a24ab01a... no: the 21d4c73c binary kept as `igneumd.prev-035` (the script's name) | +| 1b the ship (`--from ci`) | 23:24:44Z | preflight ok (tree 2a62735 clean, 0.3.11 in all 6, fork 89dfcb95, gh igneum-labs, live inputs 89dfcb95 built 23:17:51Z); ci, fetch, dmg, copy already; `consensus.override: carried over from the current manifest` (the four-field object), `activation_height` 135200, `deadline_note` "finality v3"; manifest 0.3.11 mac+windows signed (key 8f186e37...) and in `dl/public/`; one deploy; verify: the token folder's manifest 0.3.11, signature ok, mac b7e81d4f..., windows 84a21443...; the public folder's `igneum-downloads.json` not yet the local bytes at the edge (the 0.3.10 class), resumed from `verify` for the console item | +| 1c update-now, the Mac and the laptop | 23:28:06Z (`update-now-0311-d937c69d-37ba0461`, apps woken) | the Mac: the job ran 23:28:48Z, `Igneum Miner 0.3.11 is available: downloading (41 MB)` 23:28:49Z, engine restart 23:29:05Z (run `mac-d937c69d-20261005-232905`), 59 s after the job; `[ok] updated to Igneum Miner 0.3.11 from 0.3.10`; `a node already answers on 127.0.0.1:26610; using it`: the app attaches to node 1 (89dfcb95, 4d8f8bb6...), so its card follows node 1; `cards: Apple M5 Max [apple, off]`: its Metal miner was off before and after, so the prepared-pack check has no subject on the Mac tonight. The laptop (PC 37ba0461): (pending) | +| the console | 23:30Z | item #366 "Igneum Miner 0.3.11 shipped (mac+windows)" (`--from console` after the public index settled at the edge; the ship's own verify had refused it as 0.3.10's did) | +| the old side during the window | from 23:24Z | PC 2 and the laptop at 0 peers (refused by the new side) until each updates. The new side (node 1, the observer, the seed, the Mac app attached to node 1 with its miner off) has NO miner on it, so its chain STALLED at DAA 139,751 from about 23:29Z (the observer's `/api/stats` at 23:31:42Z: 139,751, age 1.8 s) until a miner joins it; the old side's fork grows only while a miner mines there, and PC 2's 0.0 MH/s from 23:29Z is the prover-floor sweep stopping its miners for its run (`floor-sweep-2`, started 23:21:17Z), not the refusal. Put to the Counter ASIC coordinator at 23:31Z with the numbers; its call: "PC 2 clear" the minute the sweep closes (about 23:36Z) and at 23:45Z at the latest, the restore job after the update, because an app restart under the sweep kills its job tree and its restore never runs; the laptop's 2 MH/s restarts the new side's chain when its install ends. The lesson for the next cut's plan: step 1a (the hand nodes and the seed first) moves the hub to a side with no hash until the first miner updates; the first update-now should go to a miner within the same minute | +| the Mac's miner (C42) | 23:39:10Z | the new side had NO miner: the Mac app's mining was a persisted `settings.paused` (cleared only by Resume; its STATUS lines read "0.00 MH/s, paused" since the update), node 1 runs no miner, the fleet's hash was on the refused old side. `POST /api/resume` (the token path) answered ok at 23:39:10Z; the Metal worker compiled the program inline at 23:39:11Z ("not prepared": the prepared-pack path did not engage on this start, a G4b finding, section 11), raced 14 variants, 3.15 MH/s wall at 23:39:48Z; the new side's first blocks accepted 23:39:49, 23:39:51, 23:40:00Z. The stall: about 23:29Z to 23:39:49Z, 11 minutes of stopped DAA clock on the side every node ends up on. New check for step 1 of every digest-flipping cut: the new side has at least one miner before the first update-now | +| 1c update-now, PC 2 | 23:39:44Z (`update-now-0311-1ccfe586`, on the Counter ASIC coordinator's "PC 2 clear": `floor-sweep-2`'s first point had hung 18 min, nothing in flight to protect; its restore-and-diagnose job is the first PC 2 job after the update). PC 2 fetched the woken file at 23:40:13Z and logged `1 new for this machine, 1 queued`: update-now is itself a job and the app runs jobs one after another, so it waits behind the hung `floor-sweep-2` (started 23:21:17Z, cap 30 min, freed about 23:51:17Z); PC 2 reads `0.00 MH/s, waiting | node 139753 blocks, 0 peers, syncing` every 30 s meanwhile (refused by the new side, its miners stopped by the sweep). The class, for the next cut's plan: an update-now cannot pre-empt a running job; a hung job's cap sets the update's time | the queued job ran at 23:51:19Z, two seconds after the sweep's cap; installer verified 23:51:22Z; `quit: stopping the miners, then the node` 23:51:24Z; engine restart 23:51:30Z (run `win-1ccfe586-20261005-235130`), 11 min 46 s after the job and 3 min 35 s after PC 2 had received it; `igneumd started` 23:51:32Z; the prover's host and the pinned ids as before; `miner nvidia-1ccfe586-1 started` 23:51:43Z. Check 1 PASS at the first start: `epoch seed f4d9d3d8... (daa 140361): CPU program and cache ready in 166 ms`, `worker: ready cuda NVIDIA_GeForce_RTX_5090 ... first pack ... self-test PASS` at 23:52:21Z, no refusal. Then the hourly epoch boundary fell at DAA 140,401 (`SEED CHANGE ... f4d9d3d8... -> cb5b51cc...`, 23:52:21Z, 40 s after the first pack): the worker refused the new epoch's jobs (`epoch seed mismatch: this worker holds epoch f4d9d3d8..., the job is for epoch cb5b51cc...`, 80 lines over 80 s) while the app's attempt-aware path prepared the new epoch (`program pack checked for epoch seed cb5b51cc... day 20732: attempt 0`, the line the pack-loop fix added; `PREPARE sent ... class v2 (3559 DAA blocks before the boundary at 144000)`), the worker switched at 23:53:42Z, STATUS 113.4 MH/s wall at 23:54:14Z, the card 112.4 MH/s at 23:56:04Z. The Mac's worker crossed the same boundary in 241 ms (`prepared cb5b51cc... class v2`). PC 2's node joined the new side at once (its DAA tracks the observer's, 1 peer: node 1 at 192.168.68.64). The sweep's restore-and-diagnose job is the Counter ASIC coordinator's, after this | +| the laptop (PC 37ba0461) | silent since 23:28:28Z | its last upload, "2.25 MH/s, mining \| node 139757 blocks, 0 peers" at 23:28:28Z, is 22 s after `update-now-0311-d937c69d-37ba0461` was published; no job line reached the intake before the silence. Its 0.3.10 install kept it silent 55 minutes (21:41 to 22:36Z), so this is its install in progress until shown otherwise; publish 2 does not wait on it (a 2 MH/s machine whose 0.3.11 reads the nine fields when it returns; on 0.3.10 its node would die on the file until the forced apply, the C39 case for one machine) | +| PC 1 | unreachable tonight (section 3a); refused by every peer on 1f4b4425 until its morning relaunch takes 0.3.11 through the manifest | | + ## 10. The next cut | Branch | What | Why not 0.3.11 | @@ -141,6 +179,7 @@ window, as the fee switch's 8 min 47 s was. | `ember-tune` b671c8b (and the tune behind it) | the C35 fix: `Cmd::Quit(&'static str)` so every "quit:" line names its sender (the window host's stdin, the host gone, `POST /api/quit`, the sweep's end), `elevation_allowed()` = Power control alone (the unattended sweep on PC 1 raised one UAC prompt at 22:30Z under the old rule), no power cap at start under `--sweep`; the quit-source hunk is separable (main.rs 2 lines, server.rs 1 line, engine.rs the Quit arm, `elevation_allowed` and its test) | arrived after the tree closed at 23bc2b2 (the app, the DMG and PC 1's job carry it); not among the branches named for this cut | | `fud-close` (the ledger closer's main branch, a22ba27a60a6f1c64; its ready tip was due about 23:05Z) | 45 public-text fixes on the site and litepaper, spec 8.3 and 8.8, two CI checks, relay fixes; touches `packfile.h` and `host.c`, so taking it means the two Windows workers, the Mac worker and the DMG rebuilt from the merged tip (G5) | offered by the Counter ASIC coordinator at 22:5xZ after the tree closed; not among the branches named for this cut; the fork-side `ledger-fixes` is not in 0.3.11 either | | `explorer` d7e797c (and 3e01212) | `/api/stats` gains `proving`; `tools/ci/public-api-check.mjs` then FAILS when the live API lacks it, and ci.yml runs that check against the live site on every master push, which reads the OLD API until Vercel redeploys after the push (the reviewer's C36) | not in 23bc2b2 (only on the explorer branch); its merge needs the check to retry for a few minutes or to require `proving` only when `observer_updated_at` is newer than the commit | +| the app's job queue | an update-now job pre-empts a running job instead of queuing behind it, or the queue reports its wait in the STATUS line (tonight PC 2's update waited 11 minutes behind a hung sweep's cap, section 7; the Counter ASIC coordinator's ask) | app change | | `proving-v1` c36dfea | docs only, after the code tip 22c2363 | its agent's choice: docs follow | | the 0.3.10 list (`release-0.3.10.md` section 11): fork `pack-loop` 05ef0fa3, `job-console`, the rest of `opencl-rdna4`, `opencl-rdna4-telemetry` | | unchanged | From d61ce0ccee1ed6a0c75cb8b1b1fe7839cd6efe3c Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Tue, 6 Oct 2026 00:12:23 +0000 Subject: [PATCH 156/158] release 0.3.11: the plan's step 2, the digest sweep and the open items; sections in order Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.11.md | 161 +++++++++++++++++------------------ 1 file changed, 80 insertions(+), 81 deletions(-) diff --git a/docs/plans/release-0.3.11.md b/docs/plans/release-0.3.11.md index 9c8ee51e..793c71f5 100644 --- a/docs/plans/release-0.3.11.md +++ b/docs/plans/release-0.3.11.md @@ -48,7 +48,11 @@ Every check at cc72f4a: identity 0 hits over 220 files, copied-sources, pinned-g | `igneum-pow` | `cargo test --release` in `igneum-pow`, under the lock | 22:47:33Z: ok 53 + 4 + 19 + 7, 0 failed (the class v3 vectors, the era draw, the mixer x8, the scratch soundness) | | The prover host and export (the pin unchanged) | `cargo build --release -p igneum-prove-export -p igneum-prove-host` in `proving/igneum-prove` (the worktree needed the vendor links: `vendor/igneum-node-exec` and 45 others symlinked to the main checkout's, beside the real `igneum-node-0311` worktree) | 22:48Z: `--mode id` shard `0x2b1a81cb...`, aggregator `0x474678f3...` (the 0.3.9 pin: no prover drain); the nine real fixtures `--mode native` all ok | | PC 1 build job (the node and the app, Linux and Windows) | `IGNEUM_WIN_RELEASE=/target-integration/x86_64-pc-windows-gnu/release node tools/build-job.mjs run --node vendor/igneum-node-0311 --target ae432dc7 --targets linux,windows --no-tests` from this worktree, its own zip `build-inputs-20261005224625-29789.zip` | job `build-20261005-224654`, published 22:46:54Z (PC 1 given by the Counter ASIC coordinator at 22:4xZ: Ember's collect job closed, the AMD sweep off tonight). (pending) | -| PC 2 suites | waits for the Counter ASIC coordinator's "PC 2 suites go" (agg-cost-pc2-3 on PC 2 until about 22:59Z) | (pending) | +| PC 2 combined job | `IGNEUM_WIN_RELEASE=/target-integration/x86_64-pc-windows-gnu/release node tools/build-job.mjs run --node vendor/igneum-node-0311 --target 1ccfe586 --targets linux,windows --node-tests "kaspa-consensus kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows" --app-tests igneum-app` from this worktree (its own zip `build-inputs-20261005230716-50058.zip`); PC 1's job removed from the jobs file so nothing double-places the exes | job `build-20261005-230745`, published 23:07:45Z (PC 2 woken), the first job on PC 2's re-set schedule; started 23:09Z, done 23:16:49Z (469 s): the Linux stage, the Windows stage 264 s, the test stage `RESULT test node [kaspa-consensus kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows] exit 0 54 s` and `RESULT test app/igneum-app [igneum-app] exit 0 6 s`; 9 outputs verified and placed: igneumd.exe be8e83c07aeae5eb6842768071735289f3ef149ed9a7088592d8c54a4c252c08 (51,321,856), igneum-miner.exe 1ba1a249e2a21d52087a81f61f37cd2e1e3273c7ec8809ef9cfaa98f015895ce (10,987,520), igneum-app.exe ab104cc0... (3,065,344, the PC's; the installer's engine is the runner's), Linux igneumd d7a2715e... (49,193,960, glibc 2.39, HiveOS), igneum-miner 09d05ff6..., igneum-app 222f30c0... | +| PC 2 suites | the combined job above (the PC 1 job of the row above never started: PC 1's app is down, section 3a) | six node suites exit 0 in 54 s, the app suite exit 0 in 6 s (23:16:49Z) | +| The Linux workers for HiveOS | `infra/cross/build-workers-linux.sh` (zig, glibc 2.36 target) under the lock, 23:10Z | igneum-worker-cuda 4aaff27fcb26bc5fe98d2b311b9f95f099c59414a556af32080b82b41e8360db (6,755,568), igneum-worker-opencl 82d90890be36f9b795b218794062e388bfd9c6f7524fe671074cdec84c906259 (306,000), ELF x86-64 dynamic; untested on a GPU host, as the script says | +| The HiveOS package | `NODE_OUT= WORKERS_OUT= VERSION=0.3.11 packaging/hive/make-hive-package.sh`, 23:11:30Z, then `packaging/ota/publish-public.sh --hive` into `dl/public/` (the ship's deploy carries it) | `igneum-hive-0.3.11.tar.gz` c606a17043c013c411086b4abd0f38a227aa8a7db9d5934157760a3da5a5edc9 (24,496,653); no override inside (section 5) | +| The DMG | `NODE=/target-integration/release/igneumd MINER=... packaging/mac/build-dmg.sh` under the lock, 22:50:0x to 22:50:51Z | `Igneum-Miner-0.3.11.dmg` b7e81d4f6f3af9f9179e29faa72c844b795df757dfb2e4cd1d56cd454e78e1e7 (41,592,041): engine 0.3.11, node 89dfcb95 Mac arm64, the 0.3.9 prover host and export, `igneum-bench` (the Metal worker) rebuilt from this tree (667,145 to 555,808 bytes, signed ad hoc), fingerprints 477bb0ef and ed9c4d2e; read back from the mounted image: `Contents/Resources/igneum-app.json` carries the nine-field `node_override_params` with N4 = N5 = 154,800 (C34) | ### 3a. PC 1's app down, and the relay task that hit the wrong machine (22:31 to 23:05Z) @@ -74,40 +78,6 @@ no 9070 XT; the hardware events are being corrected). The relay machine should b and the PC 1 box get its own agent (section 11). The Mac cross-build fallback for the Windows exes was announced and not started: the combined PC 2 job replaced it within the minute. -| PC 2 combined job | `IGNEUM_WIN_RELEASE=/target-integration/x86_64-pc-windows-gnu/release node tools/build-job.mjs run --node vendor/igneum-node-0311 --target 1ccfe586 --targets linux,windows --node-tests "kaspa-consensus kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows" --app-tests igneum-app` from this worktree (its own zip `build-inputs-20261005230716-50058.zip`); PC 1's job removed from the jobs file so nothing double-places the exes | job `build-20261005-230745`, published 23:07:45Z (PC 2 woken), the first job on PC 2's re-set schedule; started 23:09Z, done 23:16:49Z (469 s): the Linux stage, the Windows stage 264 s, the test stage `RESULT test node [kaspa-consensus kaspa-consensus-core igneum-exec kaspa-pow igneum-miner kaspa-p2p-flows] exit 0 54 s` and `RESULT test app/igneum-app [igneum-app] exit 0 6 s`; 9 outputs verified and placed: igneumd.exe be8e83c07aeae5eb6842768071735289f3ef149ed9a7088592d8c54a4c252c08 (51,321,856), igneum-miner.exe 1ba1a249e2a21d52087a81f61f37cd2e1e3273c7ec8809ef9cfaa98f015895ce (10,987,520), igneum-app.exe ab104cc0... (3,065,344, the PC's; the installer's engine is the runner's), Linux igneumd d7a2715e... (49,193,960, glibc 2.39, HiveOS), igneum-miner 09d05ff6..., igneum-app 222f30c0... | -| PC 2 suites | waits for the Counter ASIC coordinator's "PC 2 suites go" (agg-cost-pc2-3 on PC 2 until about 22:59Z) | (pending) | - -### 3a. PC 1's app down (22:31 to 22:5xZ) - -PC 1's installed 0.3.10 app quit at 22:31:06Z (its last upload 22:31:08Z, run `win-ae432dc7-20261005-214041`; Ember's tune job on it -reported "aborted (the app is quitting)"; the quit's sender is C35 for the consequences reviewer: the tray Quit, stdin EOF in wrapper mode, -or `POST /api/quit`, which Ember's playbook sends when its budget is spent) and did not come back, so PC 1 mined nothing, the build job -`build-20261005-224654` could not start (no app to fetch it) and no update-now could reach it. The relay agent on PC 1 was alive (it runs -as a logon scheduled task at highest privileges in the user's session), so the relaunch went through it at 22:52:31Z as relay item #241 -(`node tools/relay.mjs run PC1 ... relaunch-pc1.ps1`): the per-user install's `igneum-app.exe` started through `explorer.exe`, which hands -the app the user's own medium-integrity token as a double-click does (a plain `Start-Process` from the elevated agent would have made the -app elevated, which its updater never does), with a one-shot scheduled task at limited run level as the fallback; the script prints the -process, its session, `app.url` and `/api/state`. Its result (22:5xZ): `igneum-app.exe` was ALIVE, pid 26696 since 21:49:40Z in session 1 -(`app.url` written 21:49:40Z), and answered nothing on `/api/state` in 60 s: the engine had logged its quit at 22:31:06Z, stopped the miners -and the node, and then hung instead of exiting (the C35 fact: a quit that never ends; the sender is still the reviewer's question). The -script launched nothing because a process existed. A second task (#243, 22:55:10Z) ends that engine by pid, as the app's own updater ends the -old engine, and launches as above. Its result (#244, 22:57:18Z) changed the picture: the old engine was `responding=True` and had a node, two -miners and three workers ALIVE under it, so PC 1 had been mining with an engine whose uploads and HTTP answers had stopped (my probe's "no -answer" on `/api/state` may be its own fault: no token); the kill left those children running as orphans on their ports; the new engine -(pid 30964, 22:55:28Z, session 1) wrote its `app.url` at once and then put nothing into the intake for minutes. The next step, if no run id -appears by 23:01:30Z, is the updater's own order as one task: every igneum process ended (miners, workers, node, engine), then the launch. - -The reading (Ember's agent through the Counter ASIC coordinator, 23:00Z): the alive children were Ember's SECOND engine's orphans (its -tune job starts a second `igneum-app` with its own two miners on the 5090 and the 9070 XT), left mining when the job was aborted, holding -both GPUs; the installed engine's quit at 22:31:06Z then stuck in the jobs runner's abort, waiting for EOF on the script's stdout pipe, whose -write end the second engine and its miners had inherited (PowerShell's `Process.Start` inherits every inheritable handle), so EOF never -came. The class rule, for every job script that starts a second engine (`relay/playbooks/sweep-5090.ps1` has the same shape): no pipe into -it, and its whole tree killed at the end. The installer-build fallback script of 0.3.10 starts no second engine. -(pending: the reset's result, the new run id, the first STATUS line) -| The Linux workers for HiveOS | `infra/cross/build-workers-linux.sh` (zig, glibc 2.36 target) under the lock, 23:10Z | igneum-worker-cuda 4aaff27fcb26bc5fe98d2b311b9f95f099c59414a556af32080b82b41e8360db (6,755,568), igneum-worker-opencl 82d90890be36f9b795b218794062e388bfd9c6f7524fe671074cdec84c906259 (306,000), ELF x86-64 dynamic; untested on a GPU host, as the script says | -| The HiveOS package | `NODE_OUT= WORKERS_OUT= VERSION=0.3.11 packaging/hive/make-hive-package.sh`, 23:11:30Z, then `packaging/ota/publish-public.sh --hive` into `dl/public/` (the ship's deploy carries it) | `igneum-hive-0.3.11.tar.gz` c606a17043c013c411086b4abd0f38a227aa8a7db9d5934157760a3da5a5edc9 (24,496,653); no override inside (section 5) | -| The DMG | `NODE=/target-integration/release/igneumd MINER=... packaging/mac/build-dmg.sh` under the lock, 22:50:0x to 22:50:51Z | `Igneum-Miner-0.3.11.dmg` b7e81d4f6f3af9f9179e29faa72c844b795df757dfb2e4cd1d56cd454e78e1e7 (41,592,041): engine 0.3.11, node 89dfcb95 Mac arm64, the 0.3.9 prover host and export, `igneum-bench` (the Metal worker) rebuilt from this tree (667,145 to 555,808 bytes, signed ad hoc), fingerprints 477bb0ef and ed9c4d2e; read back from the mounted image: `Contents/Resources/igneum-app.json` carries the nine-field `node_override_params` with N4 = N5 = 154,800 (C34) | - ## 4. The override object, N4 and N5, the digests The object every node runs after the publish (the four live fields plus the five new ones): @@ -134,6 +104,30 @@ Three digests on the 0.3.11 binary, so two sweeps: step 1 moves every node to th nine-field object (0139ab9d...). A node on either side of a sweep is refused by the other side (the handshake), so each sweep is one window, as the fee switch's 8 min 47 s was. +## 5. The rollout order: two publishes, two sweeps (the reviewer's C39 and C40, the Counter ASIC coordinator's rule, the fee-switch shape) + +Why two: the app writes the manifest's `consensus.override` at the manifest TAKE (`ota.rs` 661, `write_override`) and restarts its node with +it at the next safe window, whatever binary is installed; a 0.3.10 `igneumd` refuses a file with `program_class_v3_activation_daa` or the +proving v1 fields (`OverrideParams` is `deny_unknown_fields`) and dies at start, and the update then waits for a synced node (`engine.rs` +2211) until the slot minute or the 1,800-block rule forces it. One manifest with 0.3.11 AND the nine fields would take every 0.3.10 node +down at its next safe window: the 0.3.5 class the fee-switch plan named. And the 0.3.11 binary alone flips the digest (section 4), so the +binary move is itself a sweep. + +| Step | What | Digest after | +|---|---|---| +| 1a | the observer, node 1 and the seed on the 0.3.11 binaries with the four-field file: `IGNEUMD=/target-integration/release/igneumd IGNEUMD_COMMIT=89dfcb95 infra/devnet/restart-hand-nodes.sh ''`, then `IGNEUMD_LINUX= IGNEUMD_LINUX_SHA256=63cf490d... infra/devnet/restart-seed.sh ''`; the apps still on 0.3.10 are refused by them from this moment until each updates | 4d8f8bb6... on the three | +| 1b | the manifest: 0.3.11 with `consensus` carried over UNCHANGED (`--activation-height 135200 --deadline-note "finality v3"`, the four-field object, exactly as 0.3.10 shipped), `--public` (the HiveOS package rides along) | | +| 1c | update-now: the Mac (its card follows node 1) and the laptop first; PC 2 only on the Counter ASIC coordinator's "PC 2 clear"; PC 1 is down and unreachable (section 3a) and takes 0.3.11 through the manifest at its morning relaunch, refused until then. The watch: every app logs 0.3.11 and its node a DAA score at 4d8f8bb6...; every worker starts clean on the first try with the class-aware pair; the Mac's Metal worker takes the class v3 day from the prepared pack; C32: the agents whose kits sit on PC 2 republish their fetches after its update | 4d8f8bb6... on every reporting node | +| 2a | the floor: 154,800 minus the tip's DAA at least 10,800 (holds until DAA 144,000, about 00:45Z); past it N4 = N5 re-pinned to the first multiple of 3,600 at or above tip + 14,400, the packaged line, the DMG and the installer rebuilt; the DAA read sent to the Counter ASIC coordinator before 2b | | +| 2b | the hand nodes' and the seed's files switched to the nine-field object and restarted (the same two scripts), the manifest republished with `--override '' --activation-height 154800 --deadline-note "program class v3 + proving v1"`, update-now (the same order; PC 2 on "clear" again), the sweep | 0139ab9d... on every node | +| 3 | the plan's final sections, the merge to master (the live observer must not read stale: `public-api-check`'s other arm), the push, the report with per-machine times | | + +The HiveOS package carries NO override: `packaging/hive/h-run.sh` line 31 starts the rig's node with `--devnet --appdir --rpclisten --listen` and +the peers, no `--override-params-file`, and no HiveOS package has ever carried one, so a rig's bundled node runs on genesis params and is refused +by every devnet peer (the HiveOS path is untested on a GPU host since 4 October). "Republish with the new override" therefore needs an +`h-run.sh` change (the file written from the Flight Sheet's extra config, as `PEERS=` is), which is the next cut's; tonight's package carries +the class-aware binaries only (section 11). + ## 6. The push and CI `git push -u origin release-0.3.11` at 3b0262f (23:18:29Z, the credential helper; the pre-push hook's site flip restored), `gh workflow run @@ -172,6 +166,40 @@ Baseline 23:19:40Z: tip DAA 139,642; the observer and node 1 on 21d4c73c at 1f4b | the laptop (PC 37ba0461) | silent since 23:28:28Z | its last upload, "2.25 MH/s, mining \| node 139757 blocks, 0 peers" at 23:28:28Z, is 22 s after `update-now-0311-d937c69d-37ba0461` was published; no job line reached the intake before the silence. Its 0.3.10 install kept it silent 55 minutes (21:41 to 22:36Z), so this is its install in progress until shown otherwise; publish 2 does not wait on it (a 2 MH/s machine whose 0.3.11 reads the nine fields when it returns; on 0.3.10 its node would die on the file until the forced apply, the C39 case for one machine) | | PC 1 | unreachable tonight (section 3a); refused by every peer on 1f4b4425 until its morning relaunch takes 0.3.11 through the manifest | | +## 8. The rollout, step 2 (the object sweep to 0139ab9d...) + +| Step | Time | Result | +|---|---|---| +| 2a the floor | 23:55:42Z | tip DAA 140,706; 154,800 - 140,706 = 14,094 >= 10,800, so N4 = N5 = 154,800 stand (the floor holds until DAA 144,000); no re-pin, no rebuild | +| 2b the observer | 23:56:59Z (pid 97249) | `/tmp/igneum-devnet/override-v3.json` switched to the nine-field object; `igneumd/2.1.0-89dfcb95`, `Program class v3 from the override file: active from epoch 43 (DAA score 154800 ...)`, `Proving v1 from the override file: ... 154800, 8 blocks a segment, unproven after 600 DAA, aggregator share 1000 bps`, digest 0139ab9dc2992d449ec787d8f021974933631eb55740ab4b6ce9d5c226e72888 | +| 2b node 1 | 23:57:12Z (pid 97417) | the same lines and digest; it refused the seed (still 4d8f8bb6...) at 23:57:13Z and registered it at 23:57:42Z (protocol version 15), 29 s after the seed's own restart | +| 2b the seed | 23:57:30Z (MainPID 126124) | `/etc/igneum/override-v3.json` the nine-field object; the same binary 63cf490d..., the same lines, digest 0139ab9d... | +| 2b the manifest | 23:57:49Z | `publish-manifest.sh --version 0.3.11 --override '' --activation-height 154800 --deadline-note "program class v3 + proving v1" --notes '
' --public --deploy`; the live manifest's `consensus` read back byte-identical at the edge (the nine fields, `activation_height` 154800) | +| 2b update-now, the Mac and the laptop | 00:00:30Z (`update-now-0311-switch-d937c69d-37ba0461`, apps woken) | the Mac ran it 00:01:06Z: `0.3.11 is current`, `consensus parameters from the signed manifest: {... nine fields ...}`, `consensus override changed (.../Igneum/app/override.json); the node restarts with it at a safe moment`; the Mac's node card is node 1 (external: pid 0, starts 0, `consensus_digest` empty), already restarted by hand at 23:57:12Z, so there was nothing for the app to restart and its digest is node 1's. The laptop (PC 37ba0461) was not on the air (below) | +| 2b update-now, PC 2 | 00:01:10Z (`update-now-0311-switch-1ccfe586`, on the Counter ASIC coordinator's "PC 2 clear": `floor-restore-1` closed 23:57:29Z) | PC 2 fetched the woken file 00:01:41Z, ran the job the same second (`0.3.11 is current`, `consensus override changed (C:\Users\Admin\AppData\Local\igneum\app\override.json); the node restarts with it at a safe moment`), its node restarted at 00:01:42Z (`Consensus params digest: 0139ab9d...`, `igneumd/2.1.0`), the first STATUS 00:02:01Z "0.00 MH/s, waiting, node 141065 blocks, 1 peers, synced", mining at 00:02:31Z, 106.48 MH/s with 830 accepted this run and 0 faults at 00:06:31Z; the run id stays `win-1ccfe586-20261005-235130` (the node restarted, not the engine) | +| the window | 23:57:42 to 00:01:42Z | PC 2 (on 4d8f8bb6...) refused the seed (on 0139ab9d...) at 23:57:42Z and 23:58:12Z and mined on the old side alone; node 1 refused the seed once (23:57:13Z). The new side (the observer, node 1, the seed) had no miner on it either: the Mac's miner was off node 1 from 23:57:14Z (the next row), so the new side only relayed PC 2's old-side blocks it had already accepted (`PoW accepted ... daa 140757` at 23:57:32Z was the last) and waited; the two sides rejoined when PC 2's node restarted at 00:01:42Z and PC 2's hash carried the chain. The observer's tip read 140,757 at 00:02:22Z and 141,645 at 00:10:39Z (about 1.7 blocks/s, the catch-up after the rejoin), no stall as in step 1 | +| the Mac's miner (a miner finding) | 23:57:14Z to 00:08:34Z | the miner (`igneum-miner mine grpc://127.0.0.1:26610`) lost node 1 at node 1's 23:57:12Z restart and NEVER reconnected: 5,317 `submit error ... Not connected to server` and `template error: Not connected to server` lines, its TEMPLATES line frozen at `templates=2350` with `subscribed=true` while `fetch_errors` climbed (143 at 23:59:34Z, 595 at 00:05:07Z), the card's line "the node is not answering; the miner retries", the STATUS line still "26 MH/s, mining" with the accepted count frozen at 18,117 (773 this run). The retries are template and submit calls on a dead gRPC channel; nothing re-subscribes. Recovery through a job: `publish-jobs.sh add --kind restart --target d937c69d --what miners` (`restart-miners-0311-d937c69d`, 00:08:01Z); the Mac ran it 00:08:31Z (`miners restarted`), the new miner (pid 8567) started 00:08:33Z, its first block was accepted 00:08:34Z, the kernel race settled at 26.9 MH/s 00:09:09Z. Lost: 11 min 20 s of 26 MH/s. In step 1 this was masked: node 1's 23:24:06Z restart was followed by the Mac's own engine restart (the update) at 23:29:05Z, and the Mac was paused anyway | +| PC 2 at the epoch boundary | 23:52:22Z | 40 s after PC 2's first 0.3.11 pack the hour turned (`f4d9d3d8...` to `cb5b51cc...`): `hourly program changed: the pair was not prepared (unexpected seeds); the worker compiles inline`, 80 s of `worker fault: seed mismatch: the worker holds another program; preparing the current pair epoch cb5b51cc... day 2`, the worker on the new epoch at 23:53:42Z (113 MH/s), one `did not come back within 180 s` line for the old worker instance at 23:56:01Z. The restart landed inside the minute before the boundary, before the next epoch's prepare had run; the attempt-aware path (`program pack checked ... attempt 0`) recovered it without a job. Also at 23:52:14Z: `prover: block 74094 shard 0: ... Failed to create the CUDA prover impl: ... PermissionDenied (a GPU-server socket ...)`, the 0.3.10 socket class again on the first shard after the update; shards after it proved (`block 93665 shard 0 paid 0.9446373 IGN` at 00:06:09Z) | +| the laptop (PC 37ba0461) | absent | no upload since 23:28:28Z (section 7) and no card on the console at 00:08Z; both update-now jobs wait for it in the jobs file; its 0.3.10 app takes 0.3.11 and the nine-field object through the manifest when it is next on the air | +| PC 1 (ae432dc7), Sam's Mac (3a9bf309) | pending their relaunch | PC 1 down since 22:31:06Z (section 3a), Sam's Mac quit since 20:47Z on 0.3.9; each takes the manifest at its relaunch: the OLD app writes the nine-field object at the manifest take (`ota.rs` 661) and its old node, if restarted with that file before the 0.3.11 install lands, dies on the unknown fields (`deny_unknown_fields`, the reviewer's C39); the install then starts the 0.3.11 node on the file, so the worst case is one node death inside the update, to be read in the morning's intake | + +## 9. The digest sweep + +| Node | Binary | Digest | Since | +|---|---|---|---| +| the observer (`/tmp/igneum-devnet/observer-v4`, rpc 26640, json 28640) | `igneumd/2.1.0-89dfcb95` (bd7f043c...) | 0139ab9dc2992d449ec787d8f021974933631eb55740ab4b6ce9d5c226e72888 | 23:56:59Z | +| node 1 (`/tmp/igneum-devnet/node1`, rpc 26610) | the same | 0139ab9d... | 23:57:12Z | +| the seed (188.245.5.161, `igneumd.service`) | `igneumd/2.1.0-89dfcb95` (63cf490d..., glibc 2.36 target) | 0139ab9d... | 23:57:30Z | +| PC 2 (1ccfe586, `devnet-v4`, rpc 26610) | the 0.3.11 installer's `igneumd.exe` be8e83c0... (PC 2's own build job) | 0139ab9d... | 00:01:42Z | +| the Mac (d937c69d) | attached to node 1 (no node of its own) | node 1's | 23:57:12Z | +| the laptop (37ba0461) | 0.3.10 node, 1f4b4425... at its last upload | pending | silent since 23:28:28Z | +| PC 1 (ae432dc7) | 0.3.10 node, 1f4b4425... | pending | down since 22:31:06Z | +| Sam's Mac (3a9bf309) | 0.3.9 node a24ab01a | pending | quit since 20:47Z | + +The chain at 00:10:39Z: tip DAA 141,645 on the observer (age 1.4 s), node 1 with 2 peers (the seed and PC 2's side), PC 2 with 1 peer at +106 to 109 MH/s, the Mac at 26 MH/s, every live node on 0139ab9d... Epoch 43 (DAA 154,800) is about 3 h 45 min out at 0.965 blocks/s +(about 03:55Z); the class v3 and proving v1 lines of every node name it. + ## 10. The next cut | Branch | What | Why not 0.3.11 | @@ -183,51 +211,22 @@ Baseline 23:19:40Z: tip DAA 139,642; the observer and node 1 on 21d4c73c at 1f4b | `proving-v1` c36dfea | docs only, after the code tip 22c2363 | its agent's choice: docs follow | | the 0.3.10 list (`release-0.3.10.md` section 11): fork `pack-loop` 05ef0fa3, `job-console`, the rest of `opencl-rdna4`, `opencl-rdna4-telemetry` | | unchanged | -## 5. The rollout order: two publishes, two sweeps (the reviewer's C39 and C40, the Counter ASIC coordinator's rule, the fee-switch shape) +## 11. Open after the cut -Why two: the app writes the manifest's `consensus.override` at the manifest TAKE (`ota.rs` 661, `write_override`) and restarts its node with -it at the next safe window, whatever binary is installed; a 0.3.10 `igneumd` refuses a file with `program_class_v3_activation_daa` or the -proving v1 fields (`OverrideParams` is `deny_unknown_fields`) and dies at start, and the update then waits for a synced node (`engine.rs` -2211) until the slot minute or the 1,800-block rule forces it. One manifest with 0.3.11 AND the nine fields would take every 0.3.10 node -down at its next safe window: the 0.3.5 class the fee-switch plan named. And the 0.3.11 binary alone flips the digest (section 4), so the -binary move is itself a sweep. - -| Step | What | Digest after | +| Item | What | Owner | |---|---|---| -| 1a | the observer, node 1 and the seed on the 0.3.11 binaries with the four-field file: `IGNEUMD=/target-integration/release/igneumd IGNEUMD_COMMIT=89dfcb95 infra/devnet/restart-hand-nodes.sh ''`, then `IGNEUMD_LINUX= IGNEUMD_LINUX_SHA256=63cf490d... infra/devnet/restart-seed.sh ''`; the apps still on 0.3.10 are refused by them from this moment until each updates | 4d8f8bb6... on the three | -| 1b | the manifest: 0.3.11 with `consensus` carried over UNCHANGED (`--activation-height 135200 --deadline-note "finality v3"`, the four-field object, exactly as 0.3.10 shipped), `--public` (the HiveOS package rides along) | | -| 1c | update-now: the Mac (its card follows node 1) and the laptop first; PC 2 only on the Counter ASIC coordinator's "PC 2 clear"; PC 1 is down and unreachable (section 3a) and takes 0.3.11 through the manifest at its morning relaunch, refused until then. The watch: every app logs 0.3.11 and its node a DAA score at 4d8f8bb6...; every worker starts clean on the first try with the class-aware pair; the Mac's Metal worker takes the class v3 day from the prepared pack; C32: the agents whose kits sit on PC 2 republish their fetches after its update | 4d8f8bb6... on every reporting node | -| 2a | the floor: 154,800 minus the tip's DAA at least 10,800 (holds until DAA 144,000, about 00:45Z); past it N4 = N5 re-pinned to the first multiple of 3,600 at or above tip + 14,400, the packaged line, the DMG and the installer rebuilt; the DAA read sent to the Counter ASIC coordinator before 2b | | -| 2b | the hand nodes' and the seed's files switched to the nine-field object and restarted (the same two scripts), the manifest republished with `--override '' --activation-height 154800 --deadline-note "program class v3 + proving v1"`, update-now (the same order; PC 2 on "clear" again), the sweep | 0139ab9d... on every node | -| 3 | the plan's final sections, the merge to master (the live observer must not read stale: `public-api-check`'s other arm), the push, the report with per-machine times | | - -The HiveOS package carries NO override: `packaging/hive/h-run.sh` line 31 starts the rig's node with `--devnet --appdir --rpclisten --listen` and -the peers, no `--override-params-file`, and no HiveOS package has ever carried one, so a rig's bundled node runs on genesis params and is refused -by every devnet peer (the HiveOS path is untested on a GPU host since 4 October). "Republish with the new override" therefore needs an -`h-run.sh` change (the file written from the Flight Sheet's extra config, as `PEERS=` is), which is the next cut's; tonight's package carries -the class-aware binaries only (section 11). - -## 10. The next cut - -| Branch | What | Why not 0.3.11 | -|---|---|---| -| `ember-tune` b671c8b (and the tune behind it) | the C35 fix: `Cmd::Quit(&'static str)` so every "quit:" line names its sender (the window host's stdin, the host gone, `POST /api/quit`, the sweep's end), `elevation_allowed()` = Power control alone (the unattended sweep on PC 1 raised one UAC prompt at 22:30Z under the old rule), no power cap at start under `--sweep`; the quit-source hunk is separable (main.rs 2 lines, server.rs 1 line, engine.rs the Quit arm, `elevation_allowed` and its test) | arrived after the tree closed at 23bc2b2 (the app, the DMG and PC 1's job carry it); not among the branches named for this cut | -| `fud-close` (the ledger closer's main branch, a22ba27a60a6f1c64; its ready tip was due about 23:05Z) | 45 public-text fixes on the site and litepaper, spec 8.3 and 8.8, two CI checks, relay fixes; touches `packfile.h` and `host.c`, so taking it means the two Windows workers, the Mac worker and the DMG rebuilt from the merged tip (G5) | offered by the Counter ASIC coordinator at 22:5xZ after the tree closed; not among the branches named for this cut; the fork-side `ledger-fixes` is not in 0.3.11 either | -| `explorer` d7e797c (and 3e01212) | `/api/stats` gains `proving`; `tools/ci/public-api-check.mjs` then FAILS when the live API lacks it, and ci.yml runs that check against the live site on every master push, which reads the OLD API until Vercel redeploys after the push (the reviewer's C36) | not in 23bc2b2 (only on the explorer branch); its merge needs the check to retry for a few minutes or to require `proving` only when `observer_updated_at` is newer than the commit | -| `proving-v1` c36dfea | docs only, after the code tip 22c2363 | its agent's choice: docs follow | -| the 0.3.10 list (`release-0.3.10.md` section 11): fork `pack-loop` 05ef0fa3, `job-console`, the rest of `opencl-rdna4`, `opencl-rdna4-telemetry` | | unchanged | - -## 5. The rollout order, as agreed with the Counter ASIC coordinator (PC 2's scheduler tonight) - -The fee-switch and N3 shape (every node in one sweep, because the digest flips to 0139ab9d...), with PC 1 out of reach: - -| Step | What | Gate | -|---|---|---| -| the floor | 154,800 minus the tip's DAA at least 10,800 at the publish (holds until DAA 144,000, about 00:45Z); past it the line is re-pinned and the DMG and installer rebuilt | read on the observer before the manifest step | -| the manifest | `--public`, `--override '' --activation-height 154800 --deadline-note "program class v3 + proving v1"`, the changelog line of section 1 | CI green on the release tip | -| update-now | the Mac (attached to node 1) and the laptop (PC 37ba0461, if it is back) first; PC 2 only on the coordinator's "PC 2 clear" (after this cut's build job, the prover-floor pair and the aggregation-cost re-run); PC 1 is unreachable (section 3a) and takes 0.3.11 through the manifest at its morning relaunch | | -| the watch | every worker starts clean on the first try with the class-aware pair; the Mac's Metal worker takes the class v3 day from the prepared pack (`--prepare-packs`); the re-fetch rule C32: an update clears the jobs folder, so the agents whose kits sit on PC 2 republish their fetches after it | | -| the hand nodes, the seed | the two scripts with the same object (`IGNEUMD` the 0.3.11 Mac node, `IGNEUMD_COMMIT=89dfcb95`; `IGNEUMD_LINUX` the zig build 63cf490d... and its sha) | after the app nodes | -| the sweep | every restarted node prints 0139ab9dc2992d449ec787d8f021974933631eb55740ab4b6ce9d5c226e72888; PC 1 joins at its relaunch | | -| the HiveOS package | rebuilt from this tree's Linux node (63cf490d...) and the two Linux workers (`infra/cross/build-workers-linux.sh`, zig) with `packaging/hive/make-hive-package.sh VERSION=0.3.11`, and published through the `--public` step. It carries NO override: `packaging/hive/h-run.sh` line 31 starts the rig's node with `--devnet --appdir --rpclisten --listen` and the peers, no `--override-params-file`, and no HiveOS package has ever carried one, so a rig's bundled node runs on genesis params and is refused by every devnet peer (the HiveOS path is untested on a GPU host since 4 October). "Republish with the new override" therefore needs an `h-run.sh` change (the file written from the Flight Sheet's extra config, as `PEERS=` is), which is the next cut's; tonight's package carries the class-aware binaries only (section 11) | the coordinator told | -| the plan, the master merge, the push | the live observer must not read stale (`public-api-check`'s other arm); the explorer branch's strict check is not in this tree | | +| the miner's dead channel (section 8) | `igneum-miner mine grpc://...` keeps `subscribed=true` after the node restarts and retries template and submit calls on the closed channel for ever (11 min 20 s tonight, 5,317 errors, hash burned at 26 MH/s with 0 accepted); it must re-subscribe on the first `Not connected to server` and the app's card must read "the miner lost the node" instead of "mining, 26 MH/s". Until the fix: every hand restart of node 1 is followed by `publish-jobs.sh add --kind restart --target d937c69d --what miners` (the runbook's step), and the Mac's card is read by its accepted count, not its MH/s | miner (next cut) | +| the HiveOS package carries no override | `packaging/hive/h-run.sh` starts the rig's node without `--override-params-file`; a rig on `igneum-hive-0.3.11.tar.gz` runs on genesis params and is refused by every peer; the file must come from the Flight Sheet's extra config as `PEERS=` does (section 5) | packaging (next cut) | +| the prepared pack and a restart near the hour (section 8) | an engine restarted in the last minute before an epoch boundary has no prepared pair for the next hour (80 s of seed-mismatch faults on PC 2 at 23:52:22Z, recovered by the attempt-aware path); the prepare should run at engine start for the next epoch too, or the update-now path should wait out the boundary when it is under 2 min away | app | +| PC 2's first shard after an update | `Failed to create the CUDA prover impl: ... PermissionDenied (a GPU-server socket ...)` on block 74094 at 23:52:14Z, the 0.3.10 socket class (fixed 22:01Z for the running engine; the root socket outlives the engine restart); the later shards proved. The socket unlink belongs in the engine's prover start, not only in the playbooks (`prover-socket-check.sh` covers the playbooks) | app / proving | +| the relay machine named PC1 (section 3a) | it is the 1ccfe586 box: rename it (`node tools/relay.mjs name DESKTOP-KMCV30N PC2`), give the ae432dc7 box its own agent, and no relay task to "PC1" without the Counter ASIC coordinator's word until then | relay (the project lead in the morning for the PC 1 box) | +| PC 1 down since 22:31:06Z | relaunch in the morning (the installed app, double-clicked or through the Start menu, never elevated); it takes 0.3.11 and the nine-field object through the manifest, one node death inside the update possible (section 8); the intake's first lines of `win-ae432dc7-...` are the check; `ember-tune` b671c8b carries the C35 fix for the quit's sender | the project lead, then the release engineer | +| the laptop (37ba0461) | silent since 23:28:28Z, off the console; its 0.3.10 install kept it silent 55 min once already; when it uploads again its first STATUS line must show 0.3.11, the nine-field object in the manifest log line and 0139ab9d... | watch | +| Sam's Mac (3a9bf309) | 0.3.9, quit since 20:47Z; `min_supported` is 0.3.0 so it updates straight to 0.3.11 at its relaunch | watch | +| the app's job queue | an update-now queues behind a running job (PC 2's step-1 update waited 11 min behind a hung sweep's 30 min cap, section 7); pre-empt, or report the wait in the STATUS line | app (section 10) | +| the public index at the edge | `dl/public/igneum-downloads.json` alternates between the unsigned and the signed copy at the edge for minutes after a deploy, so the ship's verify refuses it once per cut (0.3.10 and 0.3.11 both); `--from console` after it settles is the workaround; the verify should accept either copy while both carry the cut's hashes, or the deploy should purge the edge | ship-app | +| the pre-push site flip | the pre-push hook rebuilds `site/` and leaves the tree modified (`git checkout -- site/` after every push); the site build is not idempotent | site | +| the explorer branch's strict check (C36) | `public-api-check.mjs` on `explorer` d7e797c fails against the live API until Vercel redeploys; merge it right after a master push, not before | explorer (section 10) | +| C1 (the reviewer) | the 0.3.10 `exportSegments` lacks `daaScore` and `feesV1ActivationDaa`; proving-v1's `rpc.rs:982` (eb32c645) carries them; the decision on which the aggregator reads is due 16:00Z 6 October | the Counter ASIC coordinator | +| the Metal worker at resume (G4b) | "the pair was not prepared" at the Mac's 23:39:10Z resume (section 7), compiled inline; same class as the PC 2 boundary row above | app | +| the 0.3.10 list | `release-0.3.10.md` section 11, unchanged: fork `pack-loop` 05ef0fa3, `job-console`, the rest of `opencl-rdna4`, `opencl-rdna4-telemetry`, the per-job zip pin check | | From 18de4376052a24ffd911bdfe2c869e624340d8d6 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Tue, 6 Oct 2026 00:14:19 +0000 Subject: [PATCH 157/158] release 0.3.11: the plan's merge and push section Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.11.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/plans/release-0.3.11.md b/docs/plans/release-0.3.11.md index 793c71f5..e84e626e 100644 --- a/docs/plans/release-0.3.11.md +++ b/docs/plans/release-0.3.11.md @@ -230,3 +230,13 @@ The chain at 00:10:39Z: tip DAA 141,645 on the observer (age 1.4 s), node 1 with | C1 (the reviewer) | the 0.3.10 `exportSegments` lacks `daaScore` and `feesV1ActivationDaa`; proving-v1's `rpc.rs:982` (eb32c645) carries them; the decision on which the aggregator reads is due 16:00Z 6 October | the Counter ASIC coordinator | | the Metal worker at resume (G4b) | "the pair was not prepared" at the Mac's 23:39:10Z resume (section 7), compiled inline; same class as the PC 2 boundary row above | app | | the 0.3.10 list | `release-0.3.10.md` section 11, unchanged: fork `pack-loop` 05ef0fa3, `job-console`, the rest of `opencl-rdna4`, `opencl-rdna4-telemetry`, the per-job zip pin check | | + +## 12. The merge and the push + +The plan's last branch commit 7ab72f3 (sections 8, 9, 11); `git merge --no-ff release-0.3.11` onto the local master 063baf6 (the CLAUDE.md +standing rule of 22:52Z, unpushed until now) = master 30cd292 (parents 063baf6, 7ab72f3), no conflict (the site files merged clean this time); +pushed 00:13:04Z (`b38f3de..30cd292`); the pre-push hook's site flip (bench.html, downloads.json, index.html, journey.json, miner.html) +restored with `git checkout -- site/`. CI on 30cd292: `ci` run 37392831312, `windows-ci` run 37392831184 (both started 00:13:13Z). No +release tag: 0.3.9 and 0.3.10 carry none (the repo's tags are backups and the CA2 history marks), so none is cut here without the word. +The fleet at the push: the Mac 25.7 MH/s with 55 accepted since its miner restart, PC 2 105 to 116 MH/s, every live node on 0139ab9d..., +tip DAA 141,715 at 00:12:36Z. From f8088c49c31fb092c51718eae02e64a9a639809f Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Tue, 6 Oct 2026 07:03:10 +0000 Subject: [PATCH 158/158] release 0.3.11: C35's cause on PC 1 and the ember-tune guard e600e63 in the next-cut list Co-Authored-By: Claude Fable 5.1 --- docs/plans/release-0.3.11.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/plans/release-0.3.11.md b/docs/plans/release-0.3.11.md index e84e626e..542428aa 100644 --- a/docs/plans/release-0.3.11.md +++ b/docs/plans/release-0.3.11.md @@ -78,6 +78,13 @@ no 9070 XT; the hardware events are being corrected). The relay machine should b and the PC 1 box get its own agent (section 11). The Mac cross-build fallback for the Windows exes was announced and not started: the combined PC 2 job replaced it within the minute. +C35's cause (Ember Tune, 00:2xZ): the second engine its measurement job starts on PC 1 reports 0.3.9 (the `ember-tune` branch's Cargo +version), sat under the manifest's `min_supported_version`, took 0.3.10 as urgent (over `auto_update = false` in its copied settings), ran +`ota-apply.ps1` at 22:31:05Z, and the per-user installer's PrepareToInstall quit the INSTALLED app at 22:31:06Z, which then hung on the pipe +its orphaned grandchildren held. For this plan's bookkeeping: PC 1's 0.3.10 came through my update-now at 21:40:41Z (`release-0.3.10.md` +section 8); the 22:31:05Z run was a second install of 0.3.10 over 0.3.10 by that engine, outside the rollout order, and it is what took +PC 1 down. The guard is `ember-tune` e600e63 (section 10). + ## 4. The override object, N4 and N5, the digests The object every node runs after the publish (the four live fields plus the five new ones): @@ -204,6 +211,7 @@ The chain at 00:10:39Z: tip DAA 141,645 on the observer (age 1.4 s), node 1 with | Branch | What | Why not 0.3.11 | |---|---|---| +| `ember-tune` e600e63 | the C35 guard: `Engine.no_ota` (`IGNEUM_APP_NO_OTA=1` or `--sweep` skips the OTA tick and refuses Check now, logged at start), 6 lines in `engine.rs`, separable like the quit-source hunk; the playbooks and `tools/ci/second-engine-check.sh` (file not pipe, tree ended, NO_OTA set) ride with it. Without it any test engine on an old Cargo version can install over the fleet's app (section 3a) | arrived after the merge | | `ember-tune` b671c8b (and the tune behind it) | the C35 fix: `Cmd::Quit(&'static str)` so every "quit:" line names its sender (the window host's stdin, the host gone, `POST /api/quit`, the sweep's end), `elevation_allowed()` = Power control alone (the unattended sweep on PC 1 raised one UAC prompt at 22:30Z under the old rule), no power cap at start under `--sweep`; the quit-source hunk is separable (main.rs 2 lines, server.rs 1 line, engine.rs the Quit arm, `elevation_allowed` and its test) | arrived after the tree closed at 23bc2b2 (the app, the DMG and PC 1's job carry it); not among the branches named for this cut | | `fud-close` (the ledger closer's main branch, a22ba27a60a6f1c64; its ready tip was due about 23:05Z) | 45 public-text fixes on the site and litepaper, spec 8.3 and 8.8, two CI checks, relay fixes; touches `packfile.h` and `host.c`, so taking it means the two Windows workers, the Mac worker and the DMG rebuilt from the merged tip (G5) | offered by the Counter ASIC coordinator at 22:5xZ after the tree closed; not among the branches named for this cut; the fork-side `ledger-fixes` is not in 0.3.11 either | | `explorer` d7e797c (and 3e01212) | `/api/stats` gains `proving`; `tools/ci/public-api-check.mjs` then FAILS when the live API lacks it, and ci.yml runs that check against the live site on every master push, which reads the OLD API until Vercel redeploys after the push (the reviewer's C36) | not in 23bc2b2 (only on the explorer branch); its merge needs the check to retry for a few minutes or to require `proving` only when `observer_updated_at` is newer than the commit |