Merge build/master cde05e1f into spec-accept-23 (the hash lane's derivation recipe and test taken; the rewritten 1.4.6 keeps its program-id paragraph, which carries the recipe's text; AP-F8-4 ordered before AP-F8-5; the public ledger and the ledger page regenerated)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
commit
dc2ecacc50
74 changed files with 2111 additions and 234 deletions
|
|
@ -23,8 +23,15 @@ pub const POWER_STEPS_PCT: [u32; 6] = [100, 90, 80, 70, 60, 50];
|
|||
/// 6 October 2026, run 6: the 5090's best MH/W sat on the 60% floor (1,854 MHz: 0.563 MH/W, the rate within 0.15%),
|
||||
/// so the ladder and the floor go to 45% of the maximum; the 1% rate tolerance is the guard below that
|
||||
pub const CLOCK_STEPS_PCT: [u32; 7] = [100, 90, 80, 70, 60, 50, 45];
|
||||
/// A card's clock floor when the vendor reports none: this share of its maximum core clock.
|
||||
pub const CLOCK_FLOOR_PCT: u32 = 45;
|
||||
/// A card's clock floor when the vendor reports none: this share of its maximum core clock. 7 October 2026, the PC 1
|
||||
/// efficiency passes (docs/bench-log.md): the 5090's best MH per watt sat at 1,200 to 1,300 MHz (39 to 42 percent of
|
||||
/// 3,090) and the rate fell past 5 percent only at 1,200 (class v3) and 1,100 (class v4), under the old 45 percent
|
||||
/// floor; so the ladder continues below 45 percent in [`CLOCK_FINE_STEP_MHZ`] steps down to 20 percent of the maximum
|
||||
/// (618 MHz on the 5090) and the stop rule, not the floor, ends the search.
|
||||
pub const CLOCK_FLOOR_PCT: u32 = 20;
|
||||
/// Below the percent ladder's last rung (45 percent) the clock ladder descends in steps of this many MHz until the
|
||||
/// rate falls more than the tolerance under the cap point's rate (the knee), a step faults, or the floor is reached.
|
||||
pub const CLOCK_FINE_STEP_MHZ: u32 = 100;
|
||||
/// A point may lose this much rate against the fastest point and still win on MH per watt (the manifest can change it).
|
||||
pub const RATE_TOLERANCE_PCT: f64 = 1.0;
|
||||
/// A step whose hottest GPU reading reaches this is marked hot and cannot win (the engine aborts at 90).
|
||||
|
|
@ -193,6 +200,9 @@ pub struct Plan {
|
|||
pub tolerance_pct: f64,
|
||||
power: Vec<Step>,
|
||||
clock_pcts: Vec<u32>,
|
||||
/// the clock ladder below the percent rungs: MHz values from the last rung minus one fine step down to the floor
|
||||
/// (the core-clock knob of 7 October 2026; empty when the card has no readable maximum clock)
|
||||
clock_fine: Vec<u32>,
|
||||
fixed: Vec<Step>,
|
||||
/// Ember 2 (Climb): the start point, the step sizes and the step budget
|
||||
climb: Option<Climb>,
|
||||
|
|
@ -218,7 +228,7 @@ impl Plan {
|
|||
let mem_step = if limits.mem_max_mhz > limits.mem_default_mhz { ((limits.mem_max_mhz - limits.mem_default_mhz) / 20).max(25) } else { 0 };
|
||||
let core_step = if limits.clock_max_mhz > 0 { (limits.clock_max_mhz / 20).max(25) } else { 0 };
|
||||
let start = Point { clock_mhz: limits.clamp_clock(start.clock_mhz), power_pct: start.power_pct.clamp(50, 100), mem_mhz: limits.clamp_mem(start.mem_mhz) };
|
||||
Plan { kind: PlanKind::Climb, limits: limits.clone(), before: start, tolerance_pct: goal.tolerance_pct(tolerance_pct), power: Vec::new(), clock_pcts: Vec::new(), fixed: Vec::new(), climb: Some(Climb { start, mem_step, core_step, budget: 5, goal }) }
|
||||
Plan { kind: PlanKind::Climb, limits: limits.clone(), before: start, tolerance_pct: goal.tolerance_pct(tolerance_pct), power: Vec::new(), clock_pcts: Vec::new(), clock_fine: Vec::new(), fixed: Vec::new(), climb: Some(Climb { start, mem_step, core_step, budget: 5, goal }) }
|
||||
}
|
||||
|
||||
/// The goal's score of a row: MH per watt for efficiency and balanced, the rate for maximum rate.
|
||||
|
|
@ -285,7 +295,22 @@ impl Plan {
|
|||
}
|
||||
}
|
||||
let clock_pcts = if limits.clock_max_mhz > 0 { CLOCK_STEPS_PCT[1..].to_vec() } else { Vec::new() };
|
||||
Plan { kind: PlanKind::Full, limits: limits.clone(), before, tolerance_pct, power, clock_pcts, fixed: Vec::new(), climb: None }
|
||||
// the fine ladder: from the last percent rung down to the floor in CLOCK_FINE_STEP_MHZ steps (the knob of
|
||||
// 7 October 2026; the stop rule in `next` ends it at the knee)
|
||||
let mut clock_fine = Vec::new();
|
||||
if limits.clock_max_mhz > 0 {
|
||||
let last_pct = limits.clamp_clock(limits.clock_max_mhz * CLOCK_STEPS_PCT[CLOCK_STEPS_PCT.len() - 1] / 100);
|
||||
let floor = limits.clock_floor();
|
||||
let mut m = (last_pct / CLOCK_FINE_STEP_MHZ) * CLOCK_FINE_STEP_MHZ;
|
||||
if m >= last_pct {
|
||||
m = m.saturating_sub(CLOCK_FINE_STEP_MHZ);
|
||||
}
|
||||
while m >= floor && m > 0 {
|
||||
clock_fine.push(m);
|
||||
m = m.saturating_sub(CLOCK_FINE_STEP_MHZ);
|
||||
}
|
||||
}
|
||||
Plan { kind: PlanKind::Full, limits: limits.clone(), before, tolerance_pct, power, clock_pcts, clock_fine, fixed: Vec::new(), climb: None }
|
||||
}
|
||||
|
||||
/// The prior's point, then one neighbour: the next clock step up when the prior caps the clock (is the cap
|
||||
|
|
@ -304,14 +329,14 @@ impl Plan {
|
|||
if neighbour != p {
|
||||
fixed.push(Step { point: neighbour, watts: limits.watts_for(neighbour.power_pct), kind: Kind::Confirm });
|
||||
}
|
||||
Plan { kind: PlanKind::Confirm, limits: limits.clone(), before, tolerance_pct, power: Vec::new(), clock_pcts: Vec::new(), fixed, climb: None }
|
||||
Plan { kind: PlanKind::Confirm, limits: limits.clone(), before, tolerance_pct, power: Vec::new(), clock_pcts: Vec::new(), clock_fine: Vec::new(), fixed, climb: None }
|
||||
}
|
||||
|
||||
/// One step at the card's current point: the before number, and all a measure-only card (Apple, or NVIDIA
|
||||
/// with Power control off) reports.
|
||||
pub fn baseline(limits: &Limits, before: Point, tolerance_pct: f64) -> Plan {
|
||||
let fixed = vec![Step { point: before, watts: limits.watts_for(before.power_pct), kind: Kind::Baseline }];
|
||||
Plan { kind: PlanKind::Baseline, limits: limits.clone(), before, tolerance_pct, power: Vec::new(), clock_pcts: Vec::new(), fixed, climb: None }
|
||||
Plan { kind: PlanKind::Baseline, limits: limits.clone(), before, tolerance_pct, power: Vec::new(), clock_pcts: Vec::new(), clock_fine: Vec::new(), fixed, climb: None }
|
||||
}
|
||||
|
||||
/// How many steps the plan has at most (the clock ladder counts whether or not it runs).
|
||||
|
|
@ -319,7 +344,42 @@ impl Plan {
|
|||
if let Some(c) = &self.climb {
|
||||
return c.budget;
|
||||
}
|
||||
self.fixed.len() + self.power.len() + self.clock_pcts.len()
|
||||
self.fixed.len() + self.power.len() + self.clock_pcts.len() + self.clock_fine.len()
|
||||
}
|
||||
|
||||
/// The cap point's row: the power ladder's choice (the row the clock search is read against), else the first row.
|
||||
pub fn cap_row(&self, rows: &[Row]) -> Option<Row> {
|
||||
if self.power.is_empty() {
|
||||
rows.first().cloned()
|
||||
} else {
|
||||
choose(&rows[..self.power.len().min(rows.len())], self.tolerance_pct).or_else(|| rows.first().cloned())
|
||||
}
|
||||
}
|
||||
|
||||
/// Why the clock search ended after `rows`, in words, or None while it runs: a faulted clock row (a rejected or
|
||||
/// mismatched hash during the hold: the fingerprint check), the knee (the rate under the cap point's by more than
|
||||
/// the tolerance), or the floor.
|
||||
pub fn clock_stop_reason(&self, rows: &[Row]) -> Option<String> {
|
||||
let last = rows.last()?;
|
||||
if last.point.clock_mhz == 0 || !matches!(self.kind, PlanKind::Full) {
|
||||
return None;
|
||||
}
|
||||
let clock_rows = rows.len().saturating_sub(self.power.len());
|
||||
if clock_rows == 0 {
|
||||
return None;
|
||||
}
|
||||
if last.mark == Some(Mark::Faulted) {
|
||||
return Some(format!("fingerprint mismatch at {} MHz, clocks reset", last.point.clock_mhz));
|
||||
}
|
||||
if let Some(cap) = self.cap_row(rows) {
|
||||
if last.usable() && cap.usable() && cap.mhs > 0.0 && last.mhs < cap.mhs * (1.0 - self.tolerance_pct.max(0.0) / 100.0) {
|
||||
return Some(format!("rate fell {:.1} percent at {} MHz", 100.0 * (cap.mhs - last.mhs) / cap.mhs, last.point.clock_mhz));
|
||||
}
|
||||
}
|
||||
if clock_rows >= self.clock_pcts.len() + self.clock_fine.len() {
|
||||
return Some(format!("the floor at {} MHz", last.point.clock_mhz));
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
|
|
@ -338,10 +398,22 @@ impl Plan {
|
|||
return Some(self.power[i].clone());
|
||||
}
|
||||
let k = i - self.power.len();
|
||||
let pct = *self.clock_pcts.get(k)?;
|
||||
// the stop rule (7 October 2026): a faulted clock row or the knee ends the search; the choice is made among
|
||||
// the rows so far
|
||||
if k > 0 {
|
||||
if let Some(reason) = self.clock_stop_reason(rows) {
|
||||
if !reason.starts_with("the floor") {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
let clock = if k < self.clock_pcts.len() {
|
||||
self.limits.clamp_clock(self.limits.clock_max_mhz * self.clock_pcts[k] / 100)
|
||||
} else {
|
||||
*self.clock_fine.get(k - self.clock_pcts.len())?
|
||||
};
|
||||
// the clock ladder rides the power point the power ladder chose (the before point when nothing won)
|
||||
let power_pct = if self.power.is_empty() { self.before.power_pct } else { choose(&rows[..self.power.len()], self.tolerance_pct).map(|r| r.point.power_pct).unwrap_or(self.before.power_pct) };
|
||||
let clock = self.limits.clamp_clock(self.limits.clock_max_mhz * pct / 100);
|
||||
// a step whose clamp lands on the previous step's clock is dropped (the floor was reached)
|
||||
if rows.last().map(|r| r.point.clock_mhz == clock).unwrap_or(false) {
|
||||
return None;
|
||||
|
|
@ -921,6 +993,38 @@ pub fn result_line(kind: PlanKind, mhs: f64, watts: f64, eff: f64) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// The core-clock knob's result on a card (7 October 2026; the UI lane's field shape): the chosen lock against the cap
|
||||
/// point's unlocked row, and the stop reason in words.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct LockResult {
|
||||
/// the chosen core clock cap (0 = unlocked)
|
||||
pub lock_mhz: u32,
|
||||
pub lock_mhs: f64,
|
||||
pub lock_w: f64,
|
||||
pub lock_mhw: f64,
|
||||
/// the cap point's row (clock 0): the rate and draw the lock is read against
|
||||
pub unlocked_mhs: f64,
|
||||
pub unlocked_w: f64,
|
||||
/// "rate fell 5.1 percent at 1,200 MHz", "fingerprint mismatch at 1,400 MHz, clocks reset", "the floor at 618 MHz",
|
||||
/// "no lever" (a card without a clock cap), "" while nothing ran
|
||||
pub lock_note: String,
|
||||
}
|
||||
|
||||
/// The knob's result from a finished plan's rows and its chosen row.
|
||||
pub fn lock_result(plan: &Plan, rows: &[Row], chosen: &Row) -> LockResult {
|
||||
let cap = plan.cap_row(rows);
|
||||
let (unlocked_mhs, unlocked_w) = cap.as_ref().map(|c| (c.mhs, c.watts)).unwrap_or((0.0, 0.0));
|
||||
let ran_clocks = rows.iter().any(|r| r.point.clock_mhz > 0);
|
||||
let note = if plan.limits.clock_max_mhz == 0 {
|
||||
"no lever".to_string()
|
||||
} else if !ran_clocks {
|
||||
String::new()
|
||||
} else {
|
||||
plan.clock_stop_reason(rows).unwrap_or_else(|| format!("stopped at {} MHz", rows.last().map(|r| r.point.clock_mhz).unwrap_or(0)))
|
||||
};
|
||||
LockResult { lock_mhz: chosen.point.clock_mhz, lock_mhs: chosen.mhs, lock_w: chosen.watts, lock_mhw: chosen.eff, unlocked_mhs, unlocked_w, lock_note: note }
|
||||
}
|
||||
|
||||
/// Why a card cannot be tuned beyond measuring, or None when both knobs are available.
|
||||
pub fn control_reason(vendor: &str, limits: &Limits, device: &str, power_control: bool, amd_helper: bool) -> Option<String> {
|
||||
match vendor {
|
||||
|
|
@ -952,7 +1056,9 @@ mod tests {
|
|||
#[test]
|
||||
fn the_full_plan_is_the_power_ladder_then_the_clock_ladder_at_the_chosen_power() {
|
||||
let plan = Plan::full(&l5090(), Point { clock_mhz: 0, power_pct: 80, mem_mhz: 0 }, 1.0);
|
||||
assert_eq!(plan.len(), 5 + 6, "five power steps (60% and 50% clamp to 400 W; one kept) and six clock steps (90% down to 45%)");
|
||||
// five power steps (60% and 50% clamp to 400 W; one kept), six percent rungs (90% down to 45% = 1,390) and the
|
||||
// fine ladder 1,300 down to the 20% floor (618): 1,300, 1,200, ..., 700 = 7 steps
|
||||
assert_eq!(plan.len(), 5 + 6 + 7);
|
||||
let first = plan.next(&[]).unwrap();
|
||||
assert_eq!((first.point, first.watts, first.kind), (Point { clock_mhz: 0, power_pct: 100, mem_mhz: 0 }, 575.0, Kind::Power));
|
||||
// the power ladder: 575, 518, 460, 403, 400
|
||||
|
|
@ -974,24 +1080,21 @@ mod tests {
|
|||
let s = plan.next(&rows).unwrap();
|
||||
assert_eq!(s.point.clock_mhz, 2472);
|
||||
rows.push(row_at(s.point, 220.0, 123.5));
|
||||
rows.push(row_at(plan.next(&rows).unwrap().point, 200.0, 118.0));
|
||||
let s = plan.next(&rows).unwrap();
|
||||
assert_eq!(s.point.clock_mhz, 1854, "60% of 3,090");
|
||||
rows.push(row_at(s.point, 180.0, 100.0));
|
||||
let s = plan.next(&rows).unwrap();
|
||||
assert_eq!(s.point.clock_mhz, 1545, "50%");
|
||||
rows.push(row_at(s.point, 170.0, 90.0));
|
||||
let s = plan.next(&rows).unwrap();
|
||||
assert_eq!(s.point.clock_mhz, 1390, "45% of 3,090 is the floor (6 October 2026)");
|
||||
rows.push(row_at(s.point, 160.0, 80.0));
|
||||
assert_eq!(s.point.clock_mhz, 2163, "70%");
|
||||
rows.push(row_at(s.point, 200.0, 118.0));
|
||||
// the stop rule (7 October 2026): 118 is 4.8% under the cap point's 124, past the 1% tolerance, so the search
|
||||
// ends here (the 6 October ladder went on to 1,854, 1,545 and 1,390)
|
||||
assert_eq!(plan.next(&rows), None);
|
||||
assert_eq!(plan.clock_stop_reason(&rows).as_deref(), Some("rate fell 4.8 percent at 2163 MHz"));
|
||||
// the choice: 2,472 MHz keeps 99.6% of the top rate at 220 W = 0.561 MH/W; 2,163 MHz (118 MH/s) is outside the 1% tolerance
|
||||
let best = choose(&rows, 1.0).unwrap();
|
||||
assert_eq!(best.point, Point { clock_mhz: 2472, power_pct: 100, mem_mhz: 0 });
|
||||
// a wider tolerance lets the 2,163 MHz step (0.590 MH/W, 4.8% slower) win
|
||||
assert_eq!(choose(&rows, 5.0).unwrap().point.clock_mhz, 2163);
|
||||
// no power limits, clocks only; no clocks, power only; nothing, empty
|
||||
assert_eq!(Plan::full(&Limits { clock_max_mhz: 2000, ..Default::default() }, Point::default(), 1.0).len(), 6);
|
||||
// clocks only: six percent rungs (1,800 .. 900) then the fine ladder 800 .. 400 (the 20% floor) = 5 more
|
||||
assert_eq!(Plan::full(&Limits { clock_max_mhz: 2000, ..Default::default() }, Point::default(), 1.0).len(), 6 + 5);
|
||||
assert_eq!(Plan::full(&Limits { power_default_w: 300.0, ..Default::default() }, Point::default(), 1.0).len(), 6);
|
||||
assert!(Plan::full(&Limits::default(), Point::default(), 1.0).is_empty());
|
||||
}
|
||||
|
|
@ -1017,11 +1120,12 @@ mod tests {
|
|||
#[test]
|
||||
fn limits_never_exceed_the_vendor_or_undercut_the_floor() {
|
||||
let l = l5090();
|
||||
assert_eq!(l.clock_floor(), 1390);
|
||||
assert_eq!(l.clamp_clock(1000), 1390);
|
||||
assert_eq!(l.clock_floor(), 618, "20% of 3,090 (7 October 2026; the 45% floor of 6 October sat on the 5090's knee)");
|
||||
assert_eq!(l.clamp_clock(1000), 1000);
|
||||
assert_eq!(l.clamp_clock(500), 618);
|
||||
assert_eq!(l.clamp_clock(5000), 3090);
|
||||
assert_eq!(l.clamp_clock(0), 0, "unlocked stays unlocked");
|
||||
assert_eq!(Limits { clock_max_mhz: 3000, clock_min_mhz: 2100, ..Default::default() }.clamp_clock(1500), 2100, "the vendor's floor wins over the 45% rule");
|
||||
assert_eq!(Limits { clock_max_mhz: 3000, clock_min_mhz: 2100, ..Default::default() }.clamp_clock(1500), 2100, "the vendor's floor wins over the 20% rule");
|
||||
assert_eq!(l.watts_for(100), 575.0);
|
||||
assert_eq!(l.watts_for(50), 400.0);
|
||||
assert_eq!(Limits { power_default_w: 300.0, power_max_w: 250.0, ..Default::default() }.watts_for(100), 250.0);
|
||||
|
|
@ -1321,4 +1425,138 @@ mod tests {
|
|||
assert!(control_reason("amd", &Limits::default(), "1", false, true).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
/// The core-clock knob (7 October 2026, the PC 1 efficiency passes): a flat ladder walks below the old 45 percent
|
||||
/// floor in 100 MHz steps to the 20 percent floor, and the result names the floor.
|
||||
#[test]
|
||||
fn the_clock_ladder_continues_below_45_percent_in_100_mhz_steps_to_the_floor() {
|
||||
let plan = Plan::full(&l5090(), Point { clock_mhz: 0, power_pct: 100, mem_mhz: 0 }, 1.0);
|
||||
let mut rows = Vec::new();
|
||||
for _ in 0..5 {
|
||||
let s = plan.next(&rows).unwrap();
|
||||
rows.push(row_at(s.point, 300.0, 136.8));
|
||||
}
|
||||
let mut clocks = Vec::new();
|
||||
while let Some(s) = plan.next(&rows) {
|
||||
assert_eq!(s.kind, Kind::Clock);
|
||||
clocks.push(s.point.clock_mhz);
|
||||
// the rate holds (memory-bound): the draw falls with the clock
|
||||
rows.push(row_at(s.point, 300.0 - clocks.len() as f64 * 10.0, 136.0));
|
||||
}
|
||||
assert_eq!(clocks, vec![2781, 2472, 2163, 1854, 1545, 1390, 1300, 1200, 1100, 1000, 900, 800, 700]);
|
||||
assert_eq!(plan.clock_stop_reason(&rows).as_deref(), Some("the floor at 700 MHz"));
|
||||
let chosen = choose(&rows, 1.0).unwrap();
|
||||
assert_eq!(chosen.point.clock_mhz, 700, "flat rate: the lowest draw wins");
|
||||
let r = lock_result(&plan, &rows, &chosen);
|
||||
assert_eq!((r.lock_mhz, r.lock_w, r.unlocked_mhs, r.unlocked_w), (700, 170.0, 136.8, 300.0));
|
||||
assert_eq!(r.lock_note, "the floor at 700 MHz");
|
||||
}
|
||||
|
||||
/// The stop rule on PC 1's RTX 5090 rows of 7 October 2026 (class v3, the card alone): the first clock row more
|
||||
/// than the tolerance under the cap point's rate ends the search and the best MH per watt among the rows within
|
||||
/// tolerance is chosen. At the 1 percent tolerance the 5090's rate (136.6 at 2,781) is 1.24 percent down at
|
||||
/// 1,545 MHz, so the search ends there and 1,854 MHz (135.6 MH/s at 239.6 W) is the point; at 1.5 percent it runs
|
||||
/// on to 1,200 (5.2 percent down) and 1,300 MHz is the point. The tolerance is the manifest's.
|
||||
#[test]
|
||||
fn the_clock_search_stops_at_the_knee_and_names_it() {
|
||||
let measured: Vec<(u32, f64, f64)> = vec![(2781, 317.9, 136.6), (2472, 276.0, 136.4), (2163, 252.4, 136.1), (1854, 239.6, 135.6), (1545, 232.1, 134.9), (1390, 229.0, 134.85), (1300, 223.3, 134.6), (1200, 215.7, 129.5)];
|
||||
let walk = |tolerance: f64| -> (Plan, Vec<Row>) {
|
||||
let plan = Plan::full(&Limits { clock_max_mhz: 3090, ..Default::default() }, Point { clock_mhz: 0, power_pct: 100, mem_mhz: 0 }, tolerance);
|
||||
let mut rows = vec![];
|
||||
for (mhz, w, mhs) in &measured {
|
||||
let Some(s) = plan.next(&rows) else { break };
|
||||
assert_eq!(s.point.clock_mhz, *mhz);
|
||||
rows.push(row_at(s.point, *w, *mhs));
|
||||
}
|
||||
(plan, rows)
|
||||
};
|
||||
let (plan, rows) = walk(1.0);
|
||||
assert_eq!(rows.last().unwrap().point.clock_mhz, 1545, "the search ends on the first row over 1 percent under the cap row");
|
||||
assert_eq!(plan.next(&rows), None);
|
||||
let reason = plan.clock_stop_reason(&rows).unwrap();
|
||||
assert!(reason.starts_with("rate fell 1.2 percent at 1545 MHz"), "{reason}");
|
||||
let chosen = choose(&rows, 1.0).unwrap();
|
||||
assert_eq!(chosen.point.clock_mhz, 1854, "the best MH per watt within 1 percent of the fastest row");
|
||||
let r = lock_result(&plan, &rows, &chosen);
|
||||
assert_eq!((r.lock_mhz, r.unlocked_mhs), (1854, 136.6));
|
||||
assert!((r.lock_mhw - 135.6 / 239.6).abs() < 1e-6);
|
||||
let (plan, rows) = walk(1.5);
|
||||
assert_eq!(rows.last().unwrap().point.clock_mhz, 1200);
|
||||
assert_eq!(plan.next(&rows), None);
|
||||
assert!(plan.clock_stop_reason(&rows).unwrap().starts_with("rate fell 5.2 percent at 1200 MHz"));
|
||||
assert_eq!(choose(&rows, 1.5).unwrap().point.clock_mhz, 1300);
|
||||
}
|
||||
|
||||
/// The fingerprint rule: a clock row marked Faulted (a rejected or mismatched hash during the hold) ends the search
|
||||
/// at once; the choice is made among the usable rows and the note says why.
|
||||
#[test]
|
||||
fn a_faulted_clock_row_ends_the_search_and_the_note_says_so() {
|
||||
let plan = Plan::full(&Limits { clock_max_mhz: 3090, ..Default::default() }, Point { clock_mhz: 0, power_pct: 100, mem_mhz: 0 }, 1.0);
|
||||
let mut rows = vec![];
|
||||
for (mhz, w, mhs) in [(2781, 317.9, 136.6), (2472, 276.0, 136.4)] {
|
||||
let s = plan.next(&rows).unwrap();
|
||||
assert_eq!(s.point.clock_mhz, mhz);
|
||||
rows.push(row_at(s.point, w, mhs));
|
||||
}
|
||||
let s = plan.next(&rows).unwrap();
|
||||
assert_eq!(s.point.clock_mhz, 2163);
|
||||
let mut bad = row_at(s.point, 252.4, 136.1);
|
||||
bad.faults = 1;
|
||||
bad.mark = Some(Mark::Faulted);
|
||||
rows.push(bad);
|
||||
assert_eq!(plan.next(&rows), None, "the search ends on the faulted row");
|
||||
assert_eq!(plan.clock_stop_reason(&rows).as_deref(), Some("fingerprint mismatch at 2163 MHz, clocks reset"));
|
||||
let chosen = choose(&rows, 1.0).unwrap();
|
||||
assert_eq!(chosen.point.clock_mhz, 2472, "the faulted row never wins");
|
||||
assert_eq!(lock_result(&plan, &rows, &chosen).lock_note, "fingerprint mismatch at 2163 MHz, clocks reset");
|
||||
}
|
||||
|
||||
/// The same through the state machine with a fake helper (the known-failed case first: a mismatch mid-search must
|
||||
/// reset and abort): the run applies 2,781 and 2,472, a fault lands during 2,163's hold, the row comes out Faulted,
|
||||
/// the next step is none, and the run's final Apply is the chosen 2,472 point (the reset), then Finished.
|
||||
#[test]
|
||||
fn a_mismatch_mid_search_resets_to_the_chosen_point_and_finishes() {
|
||||
let plan = Plan::full(&Limits { clock_max_mhz: 3090, ..Default::default() }, Point { clock_mhz: 0, power_pct: 100, mem_mhz: 0 }, 1.0);
|
||||
let timing = Timing { settle: Duration::from_secs(1), hold: Duration::from_secs(2), apply: Duration::from_secs(3) };
|
||||
let t0 = Instant::now();
|
||||
let mut run = Run::new(0, "0", "card-0", plan, 300.0, false, timing, t0);
|
||||
let mut t = t0;
|
||||
let mut applied: Vec<Step> = Vec::new();
|
||||
let mut finished: Option<Row> = None;
|
||||
let watts_for = |mhz: u32| -> f64 { match mhz { 2781 => 317.9, 2472 => 276.0, _ => 252.4 } };
|
||||
for _ in 0..200 {
|
||||
t += Duration::from_millis(500);
|
||||
let acked = true;
|
||||
let limit = run.current.as_ref().map(|s| s.watts).unwrap_or(0.0);
|
||||
// the fake helper: every setting takes; during 2,163's hold the worker reports a mismatched hash
|
||||
if let Some(cur) = run.current.clone() {
|
||||
if matches!(run.phase, Phase::Holding { .. }) {
|
||||
run.sample_rate(136.4);
|
||||
run.sample_telemetry(watts_for(cur.point.clock_mhz), cur.point.clock_mhz as f64, 13801.0, 60.0);
|
||||
if cur.point.clock_mhz == 2163 {
|
||||
run.sample_fault();
|
||||
}
|
||||
}
|
||||
}
|
||||
for o in run.tick(t, Readback { limit_w: limit, acked }) {
|
||||
match o {
|
||||
Out::Apply(s) => applied.push(s),
|
||||
Out::Finished(r) => finished = Some(r),
|
||||
Out::Failed(e) => panic!("the run failed: {e}"),
|
||||
Out::Row(_) => {}
|
||||
}
|
||||
}
|
||||
if finished.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let clocks: Vec<u32> = applied.iter().map(|s| s.point.clock_mhz).collect();
|
||||
assert_eq!(clocks, vec![2781, 2472, 2163, 2472], "2,781, 2,472, the faulted 2,163, then the reset to the chosen 2,472");
|
||||
assert_eq!(applied.last().unwrap().kind, Kind::Confirm);
|
||||
let f = finished.expect("finished");
|
||||
assert_eq!(f.point.clock_mhz, 2472);
|
||||
assert_eq!(run.rows.len(), 3);
|
||||
assert_eq!(run.rows[2].mark, Some(Mark::Faulted));
|
||||
assert_eq!(run.plan.clock_stop_reason(&run.rows).as_deref(), Some("fingerprint mismatch at 2163 MHz, clocks reset"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2727,6 +2727,13 @@ impl Engine {
|
|||
cc.tune_steps = of;
|
||||
cc.tune_eta_s = eta;
|
||||
cc.tune_plan = plan.into();
|
||||
// the clock search's own step count while a clock step runs (the UI's "locking clocks: step 4 of 9")
|
||||
if let Some(r) = self.sweep.as_ref() {
|
||||
let power_steps = r.rows.iter().filter(|x| x.point.clock_mhz == 0 && x.mark.is_some()).count() as u32;
|
||||
let on_clock = r.current.as_ref().map(|s| s.kind == crate::ember::Kind::Clock).unwrap_or(false);
|
||||
cc.lock_step = if on_clock { step.saturating_sub(power_steps) } else { 0 };
|
||||
cc.lock_steps = if on_clock { of.saturating_sub(power_steps) } else { 0 };
|
||||
}
|
||||
}
|
||||
if self.shared.runtime.sweep_only {
|
||||
// the job playbook forwards this to the installed app's /api/tune-progress
|
||||
|
|
@ -2765,6 +2772,22 @@ impl Engine {
|
|||
c.tune_source = kind.name().into();
|
||||
c.tune_line = crate::ember::result_line(kind, row.mhs, row.watts, row.eff);
|
||||
c.tune_curve = run.rows.iter().map(|r| r.json()).collect();
|
||||
// the core-clock knob's result (7 October 2026): the chosen lock against the cap point, and why it stopped
|
||||
if kind == crate::ember::PlanKind::Baseline {
|
||||
c.lock_note = if c.vendor == "apple" || run.plan.limits.clock_max_mhz == 0 { "no lever".into() } else { c.sweep_note.clone() };
|
||||
} else {
|
||||
let lr = crate::ember::lock_result(&run.plan, &run.rows, &row);
|
||||
c.lock_mhz = lr.lock_mhz;
|
||||
c.lock_mhs = lr.lock_mhs;
|
||||
c.lock_w = lr.lock_w;
|
||||
c.lock_mhw = lr.lock_mhw;
|
||||
c.unlocked_mhs = lr.unlocked_mhs;
|
||||
c.unlocked_w = lr.unlocked_w;
|
||||
c.lock_at = unix as f64;
|
||||
c.lock_note = lr.lock_note;
|
||||
}
|
||||
c.lock_step = 0;
|
||||
c.lock_steps = 0;
|
||||
let control = c.tune_control;
|
||||
if kind == crate::ember::PlanKind::Baseline {
|
||||
c.sweep_note = if control { String::new() } else { c.sweep_note.clone() };
|
||||
|
|
|
|||
|
|
@ -135,6 +135,18 @@ pub struct CardState {
|
|||
// Ember 2: the memory clock the last tune chose and the measured curve (every row of the last plan)
|
||||
pub tune_mem_mhz: u32,
|
||||
pub tune_curve: Vec<serde_json::Value>,
|
||||
// the core-clock knob (7 October 2026, src/ember.rs lock_result; the UI lane's field shape): the chosen lock
|
||||
// against the cap point's unlocked row, the step while the clock search runs, the moment and the stop reason
|
||||
pub lock_mhz: u32, // the chosen core clock cap (0 = unlocked)
|
||||
pub lock_mhs: f64,
|
||||
pub lock_w: f64,
|
||||
pub lock_mhw: f64,
|
||||
pub unlocked_mhs: f64, // the cap point's row the lock is read against
|
||||
pub unlocked_w: f64,
|
||||
pub lock_step: u32, // the clock search's step while it runs (0 otherwise)
|
||||
pub lock_steps: u32,
|
||||
pub lock_at: f64, // unix s the lock point was taken (0 = never)
|
||||
pub lock_note: String, // "rate fell 5.1 percent at 1200 MHz", "fingerprint mismatch at 1400 MHz, clocks reset", "the floor at 700 MHz", "no lever"
|
||||
// the kernel variant race (docs/design/miner-tuning.md): what the worker's last race chose
|
||||
pub variant: String,
|
||||
pub race_mhs: f64,
|
||||
|
|
|
|||
|
|
@ -357,6 +357,12 @@ Reading: NO. Class v5 with the shadow at zero leaves the strongest chip at 5.1x
|
|||
|
||||
Per tier: a miner on class v4 pays the premium and gets the 2.1x to 3.9x chip ceiling in exchange; on class v5 with the shadow kept the ceiling stays and the dataset is the chain's; on class v5 with the shadow dropped the premium goes and the ceiling returns to 5x to 9x. The decision is the founder's; this section gives the number.
|
||||
|
||||
### 5.11 Two columns from the counter-asic-4 research file (7 October 2026, 22:3x UK; `docs/analysis/counter-asic-4-research.md` sections 15 to 19 at bca23f96, the research lane's reading, carried here as the chip model's own columns)
|
||||
|
||||
The k column. k is the chip core's energy per op over the GPU's at the same operating point, and the model's 3.4x row takes k about 0.33 for an ALU-shaped core. On the public figures the int8 tensor tile is the one GPU block whose energy per op a chip at the same node cannot undercut with certainty: the 4090 measures 0.056 pJ per MAC; NVIDIA's 5 nm INT4 test chip reads 0.021 pJ per MAC at 0.46 V and about 0.1 at nominal (JSSC 2023, via Dally's NASEM slides; claimed), so INT8 at 2x to 4x that gives k 0.7 to 3 with the centre near 1; every ALU-shaped block reads k 0.3 to 0.8 on the same sources. A shadow built of tensor tiles at the ALU shadow's premium (about 11,400 u8 tiles per hash) therefore gives 2.1x at k = 1 and 1.6x at k = 1.5 and removes the k 0.3 column from the table; it needs a SIMD byte-dot verifier (the scalar one at 12.4 ms fails the 10 ms gate). This is a design candidate, not the shipped stream: the shipped shadow is ALU-shaped and its row stays 2.1x at k = 1 and 3.4x at k about 0.33.
|
||||
|
||||
The capex column. The `f = 1` GDDR7 chip of 5.5 is USD 2.8 per MH/s of silicon and memory, which is USD 0.00016 per MH/s-hour of capex over two years against USD 0.000023 of electricity: capex-dominated 7x, as the 5090 is (USD 14.7 per MH/s at MSRP, 10x). A 64 MiB hot table adds about USD 15 of N5 die, the shadow core USD 25 to 40, an interposer USD 200, so the chip's capex reaches at most about USD 4.3 per MH/s: the per-unit capex wall is unreachable by 3x to 7x, and the break-even market cap moves only through the project cost (the mission lane's model: about USD 100 M with the N5 shadow core, about 200 M if the shadow runs per load and forces one die or an interposer). Every figure here is modelled on cited or claimed parts; the research lane's microbench (20 probes, the mma_u8 and l2 rows the ones this model would take) is on PC 1's queue after the hot-table job.
|
||||
|
||||
## 6. The per-day derivation (item 2)
|
||||
|
||||
6 October 2026, Counter ASIC 3.0 item 2, worker `derive` (`docs/plans/counter-asic-3-derivation.md`; everything
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ Versions in the table: `igneum-pow` is the Rust crate at `igneum-pow/Cargo.toml`
|
|||
| 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 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 claim: at launch the strongest chip in the public model reaches 2.1x (a core as good as a GPU lane, k = 1) to 3.4x (a core three times better, k about 0.33) per joule against an RTX 5090 under class v4, live from genesis on the testnet and the mainnet; the ladder's second rung brings it to about 2.8x; class v5 makes the dataset the chain's state so a stateless or stale chip is wrong on every item; the hot-set cache is bounded at 1.067x at the ceiling and the weak-day FPGA at 12 percent on 15 days a century, both routed to the next class; datacentre silicon does not change the question; a stored-dataset chip pays for itself only at about USD 100 M of market cap in two years; without class v4 the same chip would reach 5x to 9x (the class v3 baseline, the devnet's starting state, never the launch state) | the home page's chip line, the litepaper's chip section (/litepaper#chip-model), the miner page's line | tested by the team (every card, the verifier, the two attack-pass bounds, the H100), the chip itself modelled, class v5 and the ladder designed, the X9 figure claimed against a CPU core and never measured | `docs/analysis/chip-model-v3.md` 5 and 6; `docs/analysis/latency-shadow-2026-10-06.md`; `docs/plans/counter-asic-3-status.md`; `docs/analysis/attack-pass/f8-uniform.md`, `f4-weakday.md`, `docs/analysis/ca3-v4-uniform.md`; `docs/design/class-v5-stored-state.md`; the H100 and market-cap rows of 7 October; `docs/plans/cryptanalysis/in-house-pass.md` (the internal adversarial pass) | the chip model's arithmetic in its file; the card rows by the benchmark package; the attack-pass harnesses `tools/attack/f8-uniform` and the F4 census; the verifier by `igneum-pow bench` | 136 MH/s at 350 W (5090, bench) and 290 W (app); 27 MH/s at 21 W (M5 Max); 249 MH/s (H100 SXM) at 98 percent of its read ceiling, 1.78x hash, 1.15x MH/W, a third per rented dollar; 2.33 ms per warp; 2.1x, 3.4x, 2.8x at launch; the shadow's premium on a 5090 81.8 W at its knee (class v4 at the 1,200 MHz lock 133.80 MH/s at 305.1 W; class v3 at 1,300 MHz 134.62 at 223.3 W; 7 October 2026); 1.067x at the ceiling; 12 percent on 15 days a century; 10.85 ms at rung 3; USD 100 M; 5.1x to 9.2x the class v3 baseline; 6 and 7 October 2026, the M5 Max, PC 2's RTX 5090, PC 1's RX 9070 XT and RTX 4070, a rented H100 SXM, igneum-build-1 The k about 0.33 bound is the implied core of Bitmain's Antminer X9 (RandomX; 1,000 KH/s, 2,472 W, 2.47 J per KH, USD 5,600; pre-orders 26 December 2025), withdrawn in mid-May 2026 with buyers refunded before any unit shipped, no independent benchmark, commodity Sophgo SG2044 server SoCs with an AES accelerator, no tapeout: a claimed, unmeasured figure carried as the pessimistic bound, not a calibration point (attack pass AP-F5-1, 7 October 2026). | none yet; the next test is the internal adversarial pass (three lanes new to the hash code, outsider inputs only, reports published whole), and the one outside check is staged and waits on its escrow and the publish word |
|
||||
| 17 | The chip resistance claim: at launch the strongest chip in the public model reaches 2.1x (a core as good as a GPU lane, k = 1) to 3.4x (a core three times better, k about 0.33) per joule against an RTX 5090 under class v4, live from genesis on the testnet and the mainnet; the ladder's second rung brings it to about 2.8x; class v5 makes the dataset the chain's state so a stateless or stale chip is wrong on every item; the hot-set cache is bounded at 1.067x at the ceiling and the weak-day FPGA at 12 percent on 15 days a century, both routed to the next class; datacentre silicon does not change the question; a stored-dataset chip pays for itself only at about USD 100 M of market cap in two years; without class v4 the same chip would reach 5x to 9x (the class v3 baseline, the devnet's starting state, never the launch state) | the home page's chip line, the litepaper's chip section (/litepaper#chip-model), the miner page's line | tested by the team (every card, the verifier, the two attack-pass bounds, the H100), the chip itself modelled, class v5 and the ladder designed, the X9 figure claimed against a CPU core and never measured | `docs/analysis/chip-model-v3.md` 5 and 6; `docs/analysis/latency-shadow-2026-10-06.md`; `docs/plans/counter-asic-3-status.md`; `docs/analysis/attack-pass/f8-uniform.md`, `f4-weakday.md`, `docs/analysis/ca3-v4-uniform.md`; `docs/design/class-v5-stored-state.md`; the H100 and market-cap rows of 7 October; `docs/plans/cryptanalysis/in-house-pass.md` (the internal adversarial pass) | the chip model's arithmetic in its file; the card rows by the benchmark package; the attack-pass harnesses `tools/attack/f8-uniform` and the F4 census; the verifier by `igneum-pow bench` | 136 MH/s at 350 W (5090, bench) and 290 W (app); 27 MH/s at 21 W (M5 Max); 249 MH/s (H100 SXM) at 98 percent of its read ceiling, 1.78x hash, 1.15x MH/W, a third per rented dollar; 2.33 ms per warp; 2.1x, 3.4x, 2.8x at launch; the shadow's premium on a 5090 81.8 W at its knee (class v4 at the 1,200 MHz lock 133.80 MH/s at 305.1 W; class v3 at 1,300 MHz 134.62 at 223.3 W; 7 October 2026); 1.067x at the ceiling; 12 percent on 15 days a century; 10.85 ms at rung 3; USD 100 M; 5.1x to 9.2x the class v3 baseline; 6 and 7 October 2026, the M5 Max, PC 2's RTX 5090, PC 1's RX 9070 XT and RTX 4070, a rented H100 SXM, igneum-build-1 The k about 0.33 bound is the implied core of Bitmain's Antminer X9 (RandomX; 1,000 KH/s, 2,472 W, 2.47 J per KH, USD 5,600; pre-orders 26 December 2025), withdrawn in mid-May 2026 with buyers refunded before any unit shipped, no independent benchmark, commodity Sophgo SG2044 server SoCs with an AES accelerator, no tapeout: a claimed, unmeasured figure carried as the pessimistic bound, not a calibration point (attack pass AP-F5-1, 7 October 2026). | none yet; the next test is the internal adversarial pass (three lanes new to the hash code, outsider inputs only, reports published whole), and no outside review has run yet |
|
||||
| 18 | The chip resistance measurements: the program is latency-bound (random reads), not bandwidth-bound, on every card we own, and sits beyond a card's on-chip cache | Litepaper Mining ("waits on memory latency, not on maths or bandwidth"), vs RandomX; the numbers page | tested by the team | readwidth e752fc7 (`docs/plans/read-width.md`), ca2-era 78c0ee4, ca2-cache 2de19e5 (`docs/plans/hot-table.md`) | The dependent-read probes at 32 to 1,024 MiB and the hash rate per class on the three cards; the latency-bound share = rate over the probe ceiling per load | Latency-bound share at the 1 GiB dataset: RTX 5090 0.96 (v2) and 1.01 (v3), RX 9070 XT 0.87 and 0.95, M5 Max 1.01 and 1.06; wider reads do not close the AMD gap (the 9070 XT does 2.4 G dependent reads per second at every width; the 5090 goes bandwidth-bound at 64 B, share 0.58); a 32 to 96 MiB hot table is not kept resident by any card while the dataset streams (g 0.80 to 0.87 in the added form). 5 October 2026 | none yet |
|
||||
| 19 | The lottery hash is sound as a hash: uniform output, deterministic, no out-of-bounds read, fuzzed; class v3 bit-exact on the three vendors | Litepaper vs RandomX ("Every number above is measured and logged"), the numbers page | tested by the team | ca2-mixer 1ab8b21 (`tests/mixer.rs`, `tests/scratch.rs`), ca2-era 78c0ee4, ca2-soundness a465881 (`docs/analysis/scratch-soundness.md`), `igneum-pow/tests/packs.rs` | The crate suite (53 + 4 + 19 + 7), the Metal fuzz, edge, stats and determinism runs on the v3 construction, the pack vectors and 2^24 fingerprints on Metal, Apple OpenCL, the RTX 5090 and the RX 9070 XT, the 1,024-hash CPU re-check per card | Class v3 (mixer x8 + era): 200-program fuzz 200 of 200 on Metal, every tenth on Apple OpenCL; the pinned v3 packs 3/3 + 3/3 and 96 of 96 lanes on Metal and Apple OpenCL; the six era packs' fingerprints equal on the three vendors (PC 1 job run-ca2-era-pc1-20261005, 5 October 2026); the v2 exports byte-identical on the v3 crate; the final-class PC rows and the G2 re-check: job run-ca2-era-pc1b-20261005 (pending at the time of writing) | none yet |
|
||||
| 20 | No premine, no pre-sale, no allocation: every coin is minted by the schedule and every coin goes to the block producer (80%) and the proving pool (20%) | Homepage stats and Economics tiles; litepaper Supply, Economics | implemented | repo `6ac80a3`; fork "igneum-node devnet v0"; `consensus/core/src/igneum.rs`, `coinbase.rs` | `cargo test -p kaspa-consensus-core igneum` (8 pass: subsidy table, ramp, split, cap) and `cargo test -p kaspa-consensus coinbase` (8 pass); `igneum-miner inspect 40`; bench-log "igneum-node devnet v0" | Coinbases on the devnet: 80/20 exact on 39 of 39 single-payee blocks, the 20% to the `igneum-proving-pool-v0` output; the per-second schedule sums to under the 4,000,000,000 cap by less than 100 coins; 3,168,808,781 units per DAA second in years 0 to 2, halving at 63,115,200 DAA s. 3 October 2026, Apple M5 Max. The devnet genesis carries no allocation; the mainnet genesis does not exist yet, so the claim is about the code and the stated rule, not a launch that has happened | none yet |
|
||||
|
|
|
|||
|
|
@ -2497,10 +2497,22 @@ The second sub-version 3 commit is 017e70376489251e18564c0abce7e466e606c8b3 (pus
|
|||
|
||||
F8's 64-seed gate on 017e7037 (the attack-pass lane, box 2, last seed 16:00:20 UTC): 60 of 64 under 1.2x; the four over are the named tail and nothing else (p10 1.5036x, p8 1.3776x, p34 1.2505x, p4 1.2167x; hottest items 355 to 541 reads of 2^31, unattributed, inside the ratio's clean spread); p23, p19, p15, p18 and p56 under the line; the clean spread 0.9915x to 1.144x. Exhaustion on 017e7037: 0 in the attack-pass lane's 20,532 chain-shaped seeds (max attempt 29) plus this lane's 4,099 (max 17); the 10^6 count continues as a strengthening line. Cost line for the node: the chain's epoch draw now pays the 2^20 ratio pass on every candidate that reaches it (the accepted one, and the about 4 percent that fail there), 2.2 s per pass on one box-2 core, so about 2.3 s per epoch draw on that core and, by the attack-pass lane's reading, about 4 to 5 s per epoch per node on slower cores; the earlier rejections cost milliseconds. From the attack-pass lane sub-version 3 at 017e7037 reads green on both gates with the named tail; its close line went to the plan, main and the Counter ASIC lane. This row stays open on the tail (p4, p8, p10, p34) until it is attributed or ruled accepted.
|
||||
|
||||
Two in-house pass findings against sub-version 3 (adv-accept-3, 7 October 2026, evening), neither a chain consequence. (1) Documentation: program.json's `program_id_derivation` and spec 01 section 1.4.6 stated the plain form while generator 4 appends `'sub/' || sub_version_le16`, so a client written from the text derives another id (the devnet v4 pack: 8aa9f185d63f269e from the text against a785001687d8688a; Devnet 3's epoch 0: 30956569d8f3d8d7 against fce15bf61030be57). Fixed at 15008aca and 0f45c8be: one byte recipe (`generator::IdRecipe`) builds the id and the text, the emitter prints that text, the spec states the suffix, the class v5 form (generator 5, no suffix) and the rung form, `tests/derivation.rs` re-derives every pinned pack's id from its own text with the pack's fields only (the plain text as the known-failed case), 38 packs' program.json corrected one line each with every other file and every id and fingerprint byte-identical on re-export; the twelve readwidth mix packs of 5 October keep their text (a class name the parser no longer accepts). (2) Correctness, unreachable: the last-resort program after 256 failed attempts (`last_resort_v4`, the xor rewrite) fails the acceptance rule in 223 of 2,500 seeds (209 by part (a)) and is handed to the chain unchecked; at r = 0.67 to 0.68 the path is reached with probability about 4.6e-44 per epoch, so no chain reaches it and sub-version 3 is not reopened for it. Sub-version 3's last resort is therefore UNREACHABLE AND UNVERIFIED against the rule; class v5's generator (the v5 lane) carries the verified construction with the known-failed test on one of the 223 seeds, and its entry states it. From the same pass: the three lowest-ratio steering seeds of 4,975 at a 256-unit sample read 1.0002x worst live, so the 256-unit ratio is noise as a selector and the selector works only at the 2^20-unit read, which is what rule (c'') uses.
|
||||
|
||||
Owed (recorded, not run, by the founder's word): G2 (the CPU verifier on 1,024 hashes per card) on the amended stream; G3 (the Metal fuzz, edge, stats and determinism runs) on the amended stream; the hash-rate ladder re-measure on the M5 Max and the RTX 5090 (the amendment changes the base program's source draws, not the op mix or the load count, so the latency-bound rows of `docs/analysis/latency-shadow-2026-10-06.md` are expected to hold within their spread; unmeasured); AMD (the RX 9070 XT, PC 1); the 2019-class verifier core (O-1.14); F8's phase E (the 64-seed dynamic census) on the amended stream, which is the attack-pass lane's and the test of the per-op table. The row reads FIXED-AND-PASSED only after phase E passes against the amended class.
|
||||
|
||||
Owed (recorded, not run, by the founder's word): G2 (the CPU verifier on 1,024 hashes per card) on the amended stream; G3 (the Metal fuzz, edge, stats and determinism runs) on the amended stream; the hash-rate ladder re-measure on the M5 Max and the RTX 5090 (the amendment changes the base program's source draws, not the op mix or the load count, so the latency-bound rows of `docs/analysis/latency-shadow-2026-10-06.md` are expected to hold within their spread; unmeasured); AMD (the RX 9070 XT, PC 1); the 2019-class verifier core (O-1.14); F8's phase E (the 64-seed dynamic census) on the amended stream, which is the attack-pass lane's and the test of the per-op table. The row reads FIXED-AND-PASSED only after phase E passes against the amended class.
|
||||
|
||||
Status: Fixed in part, finding bounded, stated (7 October 2026, night, the Counter ASIC lane's words): class v4 sub-version 3 (igneum-pow 017e7037, the audit-freeze tag) is frozen with the dataflow rule, the shared-operand rule, the 0.98 ratio and the total draw; the in-house pass's F8 re-gate reads 60 of 64 seeds under 1.2x with the four-seed tail accepted by the coordinator as the window model's unattributed residue (no chip consequence); the pass then attributed the class by value (eight live hot sets at 1.54x to 2.24x in the lowest 30 of 29,032 accepted programs, each about 1 MB of items at 0.3 percent of reads, 1.002x to a chip); class v5 (1c420786, frozen 21:53 UK) carries the fix as rule (c'''), the per-site distinct-index floor at 0.995 on the state flag (its census refuses 2.435 percent of accepted programs; seven of seven live hot sets refused at 0.9821 to 0.9919; the eighth's ratio owed tonight), with a named residual (three mild shadow-block-written concentrations at 0.9992 to 0.9997, about 1.0004x, a value-level test in the next class); the record is `docs/plans/counter-asic-3-status.md` section 7c and the class v5 design's section 14. The eighth live hot set (seed 122960, id 4be7393ab6c84802, the deepest found: X_f +0.111 percent, 1.54x the window model, its hottest item at 475,616 reads from an all-ones source) reads minimum site 12 at 0.9824 at the acceptance's own 2^20 sample (live 0.9822), refused by class v5's (c''') floor at 0.995; so the floor refuses eight of eight live hot sets by X_f at or above f found in the tail of 88,051 accepted programs (minimum sites 0.9821 to 0.9919) against 0 hot sets in 20 random programs; what it misses stays the three mild shadow-block-written concentrations at 0.9992 to 0.9997 (Devnet 3's first program among them), about 1.0004x to a chip, the value-level test in the next class (22:41 BST; the logs under `docs/analysis/cryptanalysis/logs/adv-accept/` on branch adv-accept; the v5 design's section 14). The RTX 5080 grid's knee is not in tonight; X37 keeps the 5080's stock premium only.
|
||||
|
||||
### AP-F8-4. The program id's derivation text omitted generator 4's sub-version suffix (interoperability, documentation; no object change)
|
||||
|
||||
Found by the in-house pass adv-accept-3 (report-acceptance-rule-3.md at 0c150e3c, finding 2, 7 October 2026, evening): program.json's `program_id_derivation` and spec 01 section 1.4.6 stated `FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words || attempt_le32` while the code (generator.rs `program_id`) appends `'sub/' || sub_version_le16` under generator 4, so an implementation written from the text derives another id (the devnet v4 pack: 8aa9f185d63f269e from the text against the pack's and the chain's a785001687d8688a; Devnet 3's epoch 0: 30956569d8f3d8d7 against fce15bf61030be57); a second client written from the text would disagree with the chain on every program id. The code is the consensus; the text was wrong.
|
||||
|
||||
Fix (15008aca and 0f45c8be on ca3-v4-amend, landed on master through the gate on 7 October 2026 night): the id and its printed derivation come from one byte recipe (`generator::IdRecipe`, `program_id_recipe`, `program_id_class_recipe`, `Program::program_id_derivation`), so the two cannot drift; the emitter prints that text; spec 1.4.6 states the suffix, the class v5 form (generator 5, no suffix) and the class-rung form (`'igneum-program-rw/'` with the class fields); `igneum-pow/tests/derivation.rs` re-derives each of the 18 pinned packs' ids from its own derivation string with the pack's fields only (`every_pinned_pack_id_rederives_from_its_own_derivation_text`; the plain text as the known-failed case in `the_class_v4_derivation_states_the_suffix_and_the_plain_text_derives_another_id`), and the record maker `print_every_pack_derivation` (ignored) rebuilds every pack to set its line. 38 packs' program.json corrected by one line each; every kernel, header, vector and id byte-identical on re-export (the twelve 5 October readwidth mix packs keep their text, their class name no longer parses). igneum-pow against the frozen 017e7037 differs in emit.rs (the one print), generator.rs (the recipe refactor; the hashed bytes unchanged, every pinned id and the recheck pins hold) and the new test only; no acceptance or draw code moved. program.json is OUTSIDE the digested pack bytes: the miner's `kernel_sha256` covers kernel.cu, kernel_bound.cu, kernel.cl, kernel_bound.cl, program.h and memhard.h, and program.json carries that digest as a field; the chain pairs on the program id and the kernel text, never on program.json, so the correction rides any line. The spec read-back check (an implementation from the text reproduces the shipped constants and ids) is the site audit lane's tools/ci/spec-constants-check.mjs on spec-accept-23, written to the hash lane's file-and-line map of 017e7037; finding 3 (the spec does not describe the shipped acceptance rule) is that lane's text in the same landing.
|
||||
|
||||
Status: Fixed (7 October 2026, night): the id and its derivation text come from one byte recipe, every pinned pack's id re-derives from its own text in the suite (the igneum-pow suite on box 2 green at 0f45c8be: 64 + 2 + 7 + 4 + 19 + 2 + 7), the spec states the suffix; no object moved.
|
||||
|
||||
### AP-F8-5. The public specification did not describe the shipped acceptance rule (documentary; no object change)
|
||||
"An implementation written from docs/spec/01-lottery-hash.md section 1.4.6 at 017e7037 mines a different program from the node on 264 of 400 epochs." Found by the in-house pass adv-accept-3 (report-acceptance-rule-3.md at 0c150e3c, finding 3, section 6.2, 7 October 2026, night): the text described (a), (b) and (c) with a 32-attempt cap and an id without a suffix, while the code adds (a') with the shared-operand rule, (c'), (c'') at 2^20 evaluations, the 256-attempt cap keyed on the class v4 shape, the total draw with the last resort, generator 4 and the sub-version suffix, and executes the 256-instruction shadow block 27 times per iteration inside the acceptance interpreter. Measured (sweep 97, 400 seeds, 1,317 attempt verdicts): 759 verdicts differ (758 the code rejects and the text accepts: (a') 733, (c'') 15, (c) 10; 1 the other way, the shadow block changing a (c) statistic); 264 of 400 seeds choose another attempt; the parts the text did carry, (a) and (b), agree on every row.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Generated by `tools/ledger/export-public.mjs` from `docs/fud-ledger.md`; a gate check fails when the two drift. One row per item: the claim or criticism, its status, what was done, and the evidence. Internal identifiers, times of day and team-member names are left out on purpose; the full ledger is published with the repository.
|
||||
|
||||
192 items. By status: Conceded, stated 52; Fixed 30; Decided 22; Fixed on a branch, pending merge 14; Answered by design 9; Answered with evidence 6; Fixed, stated 4; Closed by rule 3; Open 3; Spec fixed 2; Answered by design, with a correction to our own text 1; Answered with evidence, stated 1; Answered by design for finality, Conceded for the lottery 1; Conceded, implemented, stated 1; Rule implemented and measured; launch month simulated 1; Answered by design, with the concession stated 1; Conceded, stated in the litepaper and the design doc 1; Answered with evidence at 1 block/s 1; Conceded, stated in the simulation report 1; Answered by design, with the dependency conceded. Update 7… 1; Conceded, stated in the litepaper, with the dial explained 1; Closed by spec 1; Conceded by decision, stated in the design doc 1; Answered by design, with the founder's edge conceded 1; Answered by design, with a metrics caveat 1; Closed by removal, 3 October 2026 1; Conceded, stated in the litepaper 1; Conceded, stated in the design doc 1; Measured on the live node line, and the overlay does NOT… 1; Conceded, stated in the simulation 1; Conceded in part, labelled, stated 1; Fixed in the node 1; Answered with evidence for the largest body the rules allow 1; Fixed in the proving code 1; Fixed in the spec 1; Rule fixed 1; Rule written 1; Fixed, logged 1; Answered with evidence for the test half 1; Fixed in the node and shipped, rule not yet activated on… 1; Simulation half run 1; Answered with evidence for all four 1; Written 1; Designed 1; Fixed and confirmed 1; Rolled out 1; Conceded by decision 1; Conceded, scheduled, stated 1; Conceded, contained by rule, stated 1; Fixed on a branch and verified locally 1; Answered with evidence and stated 1; Answered with evidence for PC 2 1; Answered by design and with evidence 1; Fixed, stated; restated 1; Fixed, stated; restated further 1; Fixed in part, finding bounded, stated 1; Fixed as a genesis lever, measurement owed 1.
|
||||
193 items. By status: Conceded, stated 52; Fixed 31; Decided 22; Fixed on a branch, pending merge 14; Answered by design 9; Answered with evidence 6; Fixed, stated 4; Closed by rule 3; Open 3; Spec fixed 2; Answered by design, with a correction to our own text 1; Answered with evidence, stated 1; Answered by design for finality, Conceded for the lottery 1; Conceded, implemented, stated 1; Rule implemented and measured; launch month simulated 1; Answered by design, with the concession stated 1; Conceded, stated in the litepaper and the design doc 1; Answered with evidence at 1 block/s 1; Conceded, stated in the simulation report 1; Answered by design, with the dependency conceded. Update 7… 1; Conceded, stated in the litepaper, with the dial explained 1; Closed by spec 1; Conceded by decision, stated in the design doc 1; Answered by design, with the founder's edge conceded 1; Answered by design, with a metrics caveat 1; Closed by removal, 3 October 2026 1; Conceded, stated in the litepaper 1; Conceded, stated in the design doc 1; Measured on the live node line, and the overlay does NOT… 1; Conceded, stated in the simulation 1; Conceded in part, labelled, stated 1; Fixed in the node 1; Answered with evidence for the largest body the rules allow 1; Fixed in the proving code 1; Fixed in the spec 1; Rule fixed 1; Rule written 1; Fixed, logged 1; Answered with evidence for the test half 1; Fixed in the node and shipped, rule not yet activated on… 1; Simulation half run 1; Answered with evidence for all four 1; Written 1; Designed 1; Fixed and confirmed 1; Rolled out 1; Conceded by decision 1; Conceded, scheduled, stated 1; Conceded, contained by rule, stated 1; Fixed on a branch and verified locally 1; Answered with evidence and stated 1; Answered with evidence for PC 2 1; Answered by design and with evidence 1; Fixed, stated; restated 1; Fixed, stated; restated further 1; Fixed in part, finding bounded, stated 1; Fixed as a genesis lever, measurement owed 1.
|
||||
|
||||
| Id | Claim or criticism | Status | What was done | Evidence |
|
||||
|---|---|---|---|---|
|
||||
|
|
@ -193,6 +193,7 @@ Generated by `tools/ledger/export-public.mjs` from `docs/fud-ledger.md`; a gate
|
|||
| N2 | Any peer could crash any pruned node with a sync request below its retention | Fixed | `SyncManager::antipast_hashes_between` (the IBD headers path, `RequestHeaders`) unwrapped the GHOSTDAG reads of the requested low block and of every chain block of the walk; a pruned node holds no GHOSTDAG data below… | unit test `a_sync_request_below_retention_is_an_error_not_a_panic` (a chain of six headers, the genesis's GHOSTDAG… |
|
||||
| P23 | An unwound transaction leaves the node's view until its sender resends it | Fixed on a branch, pending merge | a fork a commit (the P23 commit, on the merge of `ledger-fixes` and `ledger-fixes-2` onto the 0.3.11 fork tip a commit); `EvmPool::on_chain_removed` (igneum/exec/src/pool.rs) and `ExecService::requeue_unwound`… | [igneum/exec/src/pool.rs](../igneum/exec/src/pool.rs) |
|
||||
| AP-F8-1 | A load whose source was last written by `or`, `mul` or `mulhi` makes a cross-hash hot set | Fixed in part, finding bounded, stated | Class v4 sub-version 3 (igneum-pow a commit, the audit-freeze tag) is frozen with the dataflow rule, the shared-operand rule, the 0.98 ratio and the total draw; the in-house pass's F8 re-gate reads 60 of 64 seeds under… | [docs/analysis/ca3-v4-uniform.md](../docs/analysis/ca3-v4-uniform.md) |
|
||||
| AP-F8-4 | The program id's derivation text omitted generator 4's sub-version suffix (interoperability, documentation; no object change) | Fixed | The id and its printed derivation come from one byte recipe (`generator::IdRecipe`, `program_id_recipe`, `program_id_class_recipe`, `Program::program_id_derivation`), so the two cannot drift; the emitter prints that… | [igneum-pow/tests/derivation.rs](../igneum-pow/tests/derivation.rs) |
|
||||
| AP-F8-5 | The public specification did not describe the shipped acceptance rule (documentary; no object change) | Spec fixed | Sections 1.4.3 and 1.4.6 of `docs/spec/01-lottery-hash.md` rewritten to the shipped rule at igneum-pow a commit with every constant named and every order of operations stated (1.4.3: the two per-register states of the… | [docs/analysis/cryptanalysis/report-acceptance-rule-3.md](../docs/analysis/cryptanalysis/report-acceptance-rule-3.md) |
|
||||
| GF1 | A post-quantum signature scheme would need a hard fork, and every vote key is a public BLS12-381 point | Fixed | The byte costs nothing now and a fork later. | none named |
|
||||
| GF2 | A vote key cannot move: a miner who changes keys re-earns 30 days of weight, and so does the post-quantum migration | Fixed | The successor inherits the window, not a fresh one, so a key rotation costs no weight and the migration of GF1 is one item per key. | none named |
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ The chip model. We price the strongest chip we can design against an RTX 5090 an
|
|||
| When a stored-dataset chip pays for itself | at about USD 100 M of market cap in the first two years, not before | modelled, 7 October 2026 |
|
||||
| The baseline the work started from: the same chip under class v3, without the shadow (the Ethash class) | 5x to 9x (5.1x on GDDR7, 9.2x on eight HBM3 stacks; the Ethash chips of this class reached 2.1x to 4.8x) | modelled, 6 October 2026; the precedent measured by others, 2020 to 2022; never the launch state |
|
||||
|
||||
What a miner sees from this. Class v4 costs a 5090 about 80 W more for 0.2 percent of rate, an M5 Max 16 W more for 1.5 percent, an RX 9070 XT and an RTX 4070 nothing (all measured, 6 October 2026). The ladder that sets how much work rides in the shadow starts at rung 0 at genesis and climbs by miner signal; its third rung is inadmissible today because a server core verifies it in 10.85 ms, over the gate (measured, 7 October 2026). On the devnet, which started on class v3, class v4 arrives by miner signal at a published height (a devnet fact, not a launch one). The next test of the model is an internal adversarial pass, not an independent review: three lanes that have never worked on the hash code attack the mixer, the chained cache and the acceptance rule with only what an outsider has (the public kit, the frozen object, the spec, the harnesses) and publish the break or the bound they reach. The one outside check is staged and waits on its escrow and the publish word.
|
||||
What a miner sees from this. Class v4 costs a 5090 about 80 W more for 0.2 percent of rate, an M5 Max 16 W more for 1.5 percent, an RX 9070 XT and an RTX 4070 nothing (all measured, 6 October 2026). The ladder that sets how much work rides in the shadow starts at rung 0 at genesis and climbs by miner signal; its third rung is inadmissible today because a server core verifies it in 10.85 ms, over the gate (measured, 7 October 2026). On the devnet, which started on class v3, class v4 arrives by miner signal at a published height (a devnet fact, not a launch one). The next test of the model is an internal adversarial pass, not an independent review: three lanes that have never worked on the hash code attack the mixer, the chained cache and the acceptance rule with only what an outsider has (the public kit, the frozen object, the spec, the harnesses) and publish the break or the bound they reach. No outside review has run yet.
|
||||
|
||||
## 3. The miner page's line
|
||||
|
||||
|
|
@ -30,4 +30,4 @@ Your card against the strongest chip we can price: an RTX 5090 at 136 MH/s on 35
|
|||
|
||||
| # | Claim | Where it is made | Status | Version or commit | Reproducible test | Result, date, machine | Independent verification |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 17 | The chip resistance claim: at launch the strongest chip in the public model reaches 2.1x (a core as good as a GPU lane, k = 1) to 3.4x (a core three times better, k about 0.33) per joule against an RTX 5090 under class v4, live from genesis on the testnet and the mainnet; the ladder's second rung brings it to about 2.8x; class v5 makes the dataset the chain's state so a stateless or stale chip is wrong on every item; the hot-set cache is bounded at 1.067x at the ceiling and the weak-day FPGA at 12 percent on 15 days a century, both routed to the next class; datacentre silicon does not change the question; a stored-dataset chip pays for itself only at about USD 100 M of market cap in two years; without class v4 the same chip would reach 5x to 9x (the class v3 baseline, the devnet's starting state, never the launch state) | the home page's chip line, the litepaper's chip section (/litepaper#chip-model), the miner page's line | tested by the team (every card, the verifier, the two attack-pass bounds, the H100), the chip itself modelled, class v5 and the ladder designed, the X9 figure claimed against a CPU core and never measured | `docs/analysis/chip-model-v3.md` 5 and 6; `docs/analysis/latency-shadow-2026-10-06.md`; `docs/plans/counter-asic-3-status.md`; `docs/analysis/attack-pass/f8-uniform.md`, `f4-weakday.md`, `docs/analysis/ca3-v4-uniform.md`; `docs/design/class-v5-stored-state.md`; the H100 and market-cap rows of 7 October; `docs/plans/cryptanalysis/in-house-pass.md` (the internal adversarial pass) | the chip model's arithmetic in its file; the card rows by the benchmark package; the attack-pass harnesses `tools/attack/f8-uniform` and the F4 census; the verifier by `igneum-pow bench` | 136 MH/s at 350 W (5090, bench) and 290 W (app); 27 MH/s at 21 W (M5 Max); 249 MH/s (H100 SXM) at 98 percent of its read ceiling, 1.78x hash, 1.15x MH/W, a third per rented dollar; 2.33 ms per warp; 2.1x, 3.4x, 2.8x at launch; the shadow's premium on a 5090 81.8 W at its knee (class v4 at the 1,200 MHz lock 133.80 MH/s at 305.1 W; class v3 at 1,300 MHz 134.62 at 223.3 W; 7 October 2026); 1.067x at the ceiling; 12 percent on 15 days a century; 10.85 ms at rung 3; USD 100 M; 5.1x to 9.2x the class v3 baseline; 6 and 7 October 2026, the M5 Max, PC 2's RTX 5090, PC 1's RX 9070 XT and RTX 4070, a rented H100 SXM, igneum-build-1 | none yet; the next test is the internal adversarial pass (three lanes new to the hash code, outsider inputs only, reports published whole), and the one outside check is staged and waits on its escrow and the publish word |
|
||||
| 17 | The chip resistance claim: at launch the strongest chip in the public model reaches 2.1x (a core as good as a GPU lane, k = 1) to 3.4x (a core three times better, k about 0.33) per joule against an RTX 5090 under class v4, live from genesis on the testnet and the mainnet; the ladder's second rung brings it to about 2.8x; class v5 makes the dataset the chain's state so a stateless or stale chip is wrong on every item; the hot-set cache is bounded at 1.067x at the ceiling and the weak-day FPGA at 12 percent on 15 days a century, both routed to the next class; datacentre silicon does not change the question; a stored-dataset chip pays for itself only at about USD 100 M of market cap in two years; without class v4 the same chip would reach 5x to 9x (the class v3 baseline, the devnet's starting state, never the launch state) | the home page's chip line, the litepaper's chip section (/litepaper#chip-model), the miner page's line | tested by the team (every card, the verifier, the two attack-pass bounds, the H100), the chip itself modelled, class v5 and the ladder designed, the X9 figure claimed against a CPU core and never measured | `docs/analysis/chip-model-v3.md` 5 and 6; `docs/analysis/latency-shadow-2026-10-06.md`; `docs/plans/counter-asic-3-status.md`; `docs/analysis/attack-pass/f8-uniform.md`, `f4-weakday.md`, `docs/analysis/ca3-v4-uniform.md`; `docs/design/class-v5-stored-state.md`; the H100 and market-cap rows of 7 October; `docs/plans/cryptanalysis/in-house-pass.md` (the internal adversarial pass) | the chip model's arithmetic in its file; the card rows by the benchmark package; the attack-pass harnesses `tools/attack/f8-uniform` and the F4 census; the verifier by `igneum-pow bench` | 136 MH/s at 350 W (5090, bench) and 290 W (app); 27 MH/s at 21 W (M5 Max); 249 MH/s (H100 SXM) at 98 percent of its read ceiling, 1.78x hash, 1.15x MH/W, a third per rented dollar; 2.33 ms per warp; 2.1x, 3.4x, 2.8x at launch; the shadow's premium on a 5090 81.8 W at its knee (class v4 at the 1,200 MHz lock 133.80 MH/s at 305.1 W; class v3 at 1,300 MHz 134.62 at 223.3 W; 7 October 2026); 1.067x at the ceiling; 12 percent on 15 days a century; 10.85 ms at rung 3; USD 100 M; 5.1x to 9.2x the class v3 baseline; 6 and 7 October 2026, the M5 Max, PC 2's RTX 5090, PC 1's RX 9070 XT and RTX 4070, a rented H100 SXM, igneum-build-1 | none yet; the next test is the internal adversarial pass (three lanes new to the hash code, outsider inputs only, reports published whole), and no outside review has run yet |
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -154,3 +154,11 @@ The 0.3.23 node's heights move Devnet 3's digest to ba75bf6f, and the one-box-at
|
|||
**The node pin: release-0.3.24-node = 47b9b229** (774f16c9's v5 object plus the testnet re-cut 34892a36), fast-forwarded on both mirrors at 23:01:05 BST after the Devnet 3 canary set read clean on its own binary (igneumd 6bc18ac2, igneum-miner b55b60c7, /srv/artefacts/0324-tn-47b9b229/node-lane; digest 4a284b1d with no file, object version 6 stamped into the headers, the override refused, shutdown 725 ms, two empty nodes handshaking, the shared-devnet dialler and a 2720d8d2 node refused both ways); the pairing igneum-pow class-v5 1c420786. **The hash-side board (the Counter lane, 23:0x BST):** F8 on 1c420786 PASS at 23:03 BST (64 seeds at 2^24 on the chain path with the v5 dataset, the pairing bit for bit, 61 of 64 under 1.2x, the three over inside the named residue p4/p8/p10, p10 1.5047x the worst), F4 PASS (byte-identical to v4's census), the (c''') floor measured, the kit e6c088bb byte-identical to the tip, the fingerprint 82b19cbde8557ea5 on three platforms (AMD on PC 1's queue, Intel deferred). **Main's ruling on the clock:** the cut's gates are F4, F8 and the consensus rerun, all green, so the cut runs now (the pin, the pairs, the hive, the staging, no gap); F9 (10^5 exhaustion) and F1 (10^5 redundancy), running on build-1 as strengthening lines, gate the PUBLISH and the one-minute move: the move file publishes at at_epoch 0 for the fetches, and its minute is named only after both read PASS; anything but PASS and the staging stays staged, main hears first. The 28,800 floor holds for a publish by 23:52 BST (the node lane re-reads dn3-g1 at 23:30 BST and on the minute); past it the constant re-cuts to 32,400 and the pairs with it.
|
||||
|
||||
**The app tree: release-0.3.24 = 1a58f384 on the mirror** (the version bump first, rule 15 green at six places; dash-24 19ab2a87; pool-finish-22 05f86a7f with dn3-split-read.mjs as the pool lane's; igneum-pow and proto-cuda/packs-ca3-v5 from 1c420786, which the Mac node pair build on 774f16c9 needed (StateStream, StateLeaves, ProgramClass::V5) and the build-server lane's --ship seed class the same, its overlay re-pointed; publish-public.sh's interface fix 80a4cfb1; power-helper-24 f1395775 and b9a72b9b (the engine never runs an elevated helper of its own on Windows, the task registered S4U with the right power-helper-task@unattended, the helper's start facts and exit reasons); the Windows pin to 47b9b229); app gate GREEN on build-1 (289 + 35 + 8), UI 87, pre-push 60. The Mac node pair builds on 47b9b229 under the lock from 23:06 BST; the 0.3.24 host is a fresh PC 1 build (version.h 0.3.24) in a slot after the hash lane's family run; the pairs, the hive with three kit zips (the two sub-version 3 and the v5 kit) and the Windows chain on the build-server lane. The PC 1 helper reading (the update-return lane, 23:01 BST): the elevated helper on PC 1 is alive and blind, not dead: the 0.3.20 helper's pre-0.3.21 skip rule skipped every 2-line rewrite as present at start; Stop-ScheduledTask writes no exit line; 0x800710E0 is the scheduler's record of a Start meeting a running instance; no crash; the 0.3.23 kit carries the effective_skip fix and PC 1 answers again once that kit replaces its exe.
|
||||
|
||||
## 16. The floor re-cut to 32,400 and the move minute's gate (23:3x BST)
|
||||
|
||||
**Main's ruling (23:31 BST):** the 28,800 floor is lost to the clock (the pairs from 47b9b229, the hive kits, the fleet's fetches and the ten minutes after the last FETCHED cannot land before 23:52 BST), so the constant re-cuts now rather than at 23:52: **release-0.3.24-node = c9e385eb** (23:32:09 BST; 47b9b229 with program_class_v5_activation_daa 32,400, epoch 9, everything else unchanged: byte 6, the window 86,400, the heights, the pool split at never, chain id 4463 below and 4464 from the floor, the testnet re-cut, pairing 1c420786); its gate set runs from 23:32:12 BST (the build at gate priority on build-1, consensus at gate priority and the five other suites on build-2, the Devnet 3 canary with the mixed-version step, the testnet canary, the fast-time pair); 774f16c9 and 47b9b229 are void as pins and their pairs with them. From dn3-g1's read at 23:30:17 BST (DAA 20,268 at 1.0 DAA/s): the floor lands about 02:52 BST on 8 October and holds for a move minute up to a publish at DAA 25,200, about 00:52 BST. After the digest move the node lane's DAA reads come from a 0.3.24 node the fleet names (build-1's 0.3.22 seed falls off at the move). The app side: release-0.3.24 = 25528e4b (the Windows pin to c9e385eb on 9854030b's crate); the Mac node pair and DMG rebuilt under the lock on c9e385eb from 23:34 BST.
|
||||
|
||||
**The move minute's gate (main, 23:36 BST):** F9's full 10^5 completes about 00:40 BST, too close to the 00:52 ceiling, so the minute is named on the new pin, the last FETCHED plus ten, and an F9/F1 interim line from the attack-pass lane read inside the five minutes before the minute showing 0 exhausted, 0 panics and 0 redundancy failures over everything drawn so far (16,003 seeds at 23:35 BST, 0 exhausted, max attempt 25; geometric at 0.68, the same shape as sub-version 3's 10^6; exhaustion unreachable by construction with the 256 cap and the deterministic last resort); any non-zero before the minute holds the move and main hears first; the full 10^5 lands as the record line after.
|
||||
|
||||
**The Devnet 3 row for the site and this record (main's wording through the Counter lane):** a 0.3.23 node that has not updated falls off at the digest move minute, not at the crossing; the crossing is the class change on nodes already past the digest. Served as: "update before <the fleet's move minute> or the node stops following Devnet 3; class v5 begins at DAA 32,400, about 02:52 BST", the minute filled when the fleet names it.
|
||||
|
|
|
|||
|
|
@ -1812,7 +1812,7 @@ pub fn program_json(p: &Program, day: &str, ds: &DatasetSource) -> String {
|
|||
s.push_str(&format!(" \"generator\": {},\n", p.generator));
|
||||
s.push_str(&format!(" \"attempt\": {},\n", p.attempt));
|
||||
s.push_str(&format!(" \"program_id\": {},\n", jhex64(p.program_id())));
|
||||
s.push_str(" \"program_id_derivation\": \"FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32\",\n");
|
||||
s.push_str(&format!(" \"program_id_derivation\": {},\n", jstr(&p.program_id_derivation())));
|
||||
s.push_str(&format!(
|
||||
" \"dataset_mode\": {},\n",
|
||||
jstr(if memhard.is_some() { "memory-hard" } else { "closed-form" })
|
||||
|
|
|
|||
|
|
@ -1077,6 +1077,17 @@ impl Program {
|
|||
}
|
||||
}
|
||||
|
||||
/// The derivation of [`Program::program_id`] as text, from the same byte recipe (program.json's
|
||||
/// "program_id_derivation"; spec 01 section 1.4.6).
|
||||
pub fn program_id_derivation(&self) -> String {
|
||||
let v4_rung_0 = self.generator == GENERATOR_VERSION_V4 && LoadClass { era: None, ..self.class } == V4_CLASS;
|
||||
if self.class.is_v2() || self.generator == GENERATOR_VERSION_V3 || v4_rung_0 {
|
||||
program_id_recipe(self.generator, &self.seed, self.attempt).text()
|
||||
} else {
|
||||
program_id_class_recipe(self.generator, &self.seed, self.attempt, &self.class).text()
|
||||
}
|
||||
}
|
||||
|
||||
/// The program class of this program, from its generator version (3 = v3, 4 = v4, everything else v2).
|
||||
pub fn program_class(&self) -> ProgramClass {
|
||||
match self.generator {
|
||||
|
|
@ -1087,24 +1098,61 @@ impl Program {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn program_id(generator: u32, seed: &[u32; 8], attempt: u32) -> u64 {
|
||||
let mut b = Vec::with_capacity(PROGRAM_ID_TAG.len() + 4 + 32 + 4 + 6);
|
||||
b.extend_from_slice(PROGRAM_ID_TAG);
|
||||
b.extend_from_slice(&generator.to_le_bytes());
|
||||
for w in seed {
|
||||
b.extend_from_slice(&w.to_le_bytes());
|
||||
/// The byte recipe of a program id and the text that states it: the bytes FNV-1a 64 hashes and, part by part, the label
|
||||
/// of each part (`'literal'` or `field_le32`), so the derivation printed in program.json is built from the same list the id
|
||||
/// is hashed from and the two cannot drift (the documentation finding of 7 October 2026: program.json and spec 1.4.6 said
|
||||
/// the plain form while generator 4 appended `'sub/' || sub_version_le16`, so a client written from the text derived another
|
||||
/// id). `tests/derivation.rs` re-derives every pinned pack's id from its own derivation string.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct IdRecipe {
|
||||
pub bytes: Vec<u8>,
|
||||
parts: Vec<String>,
|
||||
}
|
||||
|
||||
impl IdRecipe {
|
||||
fn lit(&mut self, s: &[u8]) {
|
||||
self.bytes.extend_from_slice(s);
|
||||
self.parts.push(format!("'{}'", String::from_utf8_lossy(s)));
|
||||
}
|
||||
b.extend_from_slice(&attempt.to_le_bytes());
|
||||
fn field(&mut self, bytes: &[u8], label: &str) {
|
||||
self.bytes.extend_from_slice(bytes);
|
||||
self.parts.push(label.to_string());
|
||||
}
|
||||
/// The derivation as program.json prints it: `FNV-1a 64 over <part> || <part> || ...`.
|
||||
pub fn text(&self) -> String {
|
||||
format!("FNV-1a 64 over {}", self.parts.join(" || "))
|
||||
}
|
||||
/// The id: FNV-1a 64 over the bytes.
|
||||
pub fn id(&self) -> u64 {
|
||||
fnv1a64(&self.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// The recipe of [`program_id`].
|
||||
pub fn program_id_recipe(generator: u32, seed: &[u32; 8], attempt: u32) -> IdRecipe {
|
||||
let mut r = IdRecipe::default();
|
||||
r.lit(PROGRAM_ID_TAG);
|
||||
r.field(&generator.to_le_bytes(), "generator_le32");
|
||||
let mut sw = Vec::with_capacity(32);
|
||||
for w in seed {
|
||||
sw.extend_from_slice(&w.to_le_bytes());
|
||||
}
|
||||
r.field(&sw, "seed_words as little-endian bytes");
|
||||
r.field(&attempt.to_le_bytes(), "attempt_le32");
|
||||
if generator == GENERATOR_VERSION_V4 {
|
||||
// The class v4 sub-version (AP-F8-1 amendment, 7 October 2026): `"sub/" || sub_version as little-endian u16`
|
||||
// appended for generator 4 only, so a binary from before the load-source rule (sub-version 0, no suffix) and
|
||||
// one after it never share a program id for one seed; the node's id check then catches a split. v2 and v3
|
||||
// ids are byte-identical. The node reads the sub-version from [`PROGRAM_SUBVERSION_V4`]; packs carry it as
|
||||
// IGNEUM_PROGRAM_SUBVERSION and program.json "sub_version".
|
||||
b.extend_from_slice(b"sub/");
|
||||
b.extend_from_slice(&PROGRAM_SUBVERSION_V4.to_le_bytes());
|
||||
r.lit(b"sub/");
|
||||
r.field(&PROGRAM_SUBVERSION_V4.to_le_bytes(), "sub_version_le16");
|
||||
}
|
||||
fnv1a64(&b)
|
||||
r
|
||||
}
|
||||
|
||||
pub fn program_id(generator: u32, seed: &[u32; 8], attempt: u32) -> u64 {
|
||||
program_id_recipe(generator, seed, attempt).id()
|
||||
}
|
||||
|
||||
/// The sub-version of class v4's program stream, in every generator-4 program id and pack (AP-F8-3: 3 = the
|
||||
|
|
@ -1118,53 +1166,60 @@ pub const PROGRAM_ID_TAG_RW: &[u8] = b"igneum-program-rw/";
|
|||
|
||||
/// The program id of a non-default class: the tag, then the same fields as [`program_id`], then the three mix
|
||||
/// percentages and the slot count as bytes.
|
||||
pub fn program_id_class(generator: u32, seed: &[u32; 8], attempt: u32, class: &LoadClass) -> u64 {
|
||||
let mut b = Vec::with_capacity(PROGRAM_ID_TAG_RW.len() + 4 + 32 + 4 + 4);
|
||||
b.extend_from_slice(PROGRAM_ID_TAG_RW);
|
||||
b.extend_from_slice(&generator.to_le_bytes());
|
||||
/// The recipe of [`program_id_class`].
|
||||
pub fn program_id_class_recipe(generator: u32, seed: &[u32; 8], attempt: u32, class: &LoadClass) -> IdRecipe {
|
||||
let mut r = IdRecipe::default();
|
||||
r.lit(PROGRAM_ID_TAG_RW);
|
||||
r.field(&generator.to_le_bytes(), "generator_le32");
|
||||
let mut sw = Vec::with_capacity(32);
|
||||
for w in seed {
|
||||
b.extend_from_slice(&w.to_le_bytes());
|
||||
sw.extend_from_slice(&w.to_le_bytes());
|
||||
}
|
||||
b.extend_from_slice(&attempt.to_le_bytes());
|
||||
b.extend_from_slice(&class.mix);
|
||||
b.push(class.load_slots);
|
||||
r.field(&sw, "seed_words as little-endian bytes");
|
||||
r.field(&attempt.to_le_bytes(), "attempt_le32");
|
||||
r.field(&class.mix, "mix[3]");
|
||||
r.field(&[class.load_slots], "load_slots_u8");
|
||||
if let Some(k) = class.scratch {
|
||||
b.extend_from_slice(b"scratch/");
|
||||
b.push(k);
|
||||
b.push(class.scratch_kb);
|
||||
r.lit(b"scratch/");
|
||||
r.field(&[k], "scratch_k_u8");
|
||||
r.field(&[class.scratch_kb], "scratch_kb_u8");
|
||||
}
|
||||
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);
|
||||
r.lit(b"mixer/");
|
||||
r.field(&[class.mixer_mult], "mixer_mult_u8");
|
||||
r.field(&[class.growth as u8], "growth_u8");
|
||||
}
|
||||
if class.derive_len != 0 {
|
||||
// Counter ASIC 3.0 item 2: the derivation program's length is part of the construction
|
||||
b.extend_from_slice(b"derive/");
|
||||
b.extend_from_slice(&class.derive_len.to_le_bytes());
|
||||
r.lit(b"derive/");
|
||||
r.field(&class.derive_len.to_le_bytes(), "derive_len_le16");
|
||||
}
|
||||
if let Some(e) = class.era {
|
||||
b.extend_from_slice(b"era/");
|
||||
b.extend_from_slice(&e.id_bytes());
|
||||
r.lit(b"era/");
|
||||
r.field(&e.id_bytes(), "era_id_bytes (allowed[3] || width_words_u8 || stride_mul_le32 || stride_rot_le32 || interleave[4])");
|
||||
}
|
||||
if let Some(sh) = class.shadow {
|
||||
// Counter ASIC 3.0 item 8: the shadow block's size and repeat count are part of the construction
|
||||
b.extend_from_slice(b"shadow/");
|
||||
b.extend_from_slice(&sh.instrs.to_le_bytes());
|
||||
b.extend_from_slice(&sh.reps.to_le_bytes());
|
||||
r.lit(b"shadow/");
|
||||
r.field(&sh.instrs.to_le_bytes(), "shadow_instrs_le16");
|
||||
r.field(&sh.reps.to_le_bytes(), "shadow_reps_le16");
|
||||
}
|
||||
if let Some(h) = class.hot {
|
||||
b.extend_from_slice(b"hot/");
|
||||
b.push(h.mb);
|
||||
b.push(h.k);
|
||||
r.lit(b"hot/");
|
||||
r.field(&[h.mb], "hot_mb_u8");
|
||||
r.field(&[h.k], "hot_k_u8");
|
||||
if h.added {
|
||||
b.extend_from_slice(b"added");
|
||||
r.lit(b"added");
|
||||
}
|
||||
}
|
||||
fnv1a64(&b)
|
||||
r
|
||||
}
|
||||
|
||||
pub fn program_id_class(generator: u32, seed: &[u32; 8], attempt: u32, class: &LoadClass) -> u64 {
|
||||
program_id_class_recipe(generator, seed, attempt, class).id()
|
||||
}
|
||||
|
||||
/// Weights of the ten non-load families under version 2, in draw order. Sum 75. The load family has no
|
||||
|
|
|
|||
142
igneum-pow/tests/derivation.rs
Normal file
142
igneum-pow/tests/derivation.rs
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
//! Every pinned pack's program id re-derived from its own `program_id_derivation` text (the documentation finding of
|
||||
//! 7 October 2026, the in-house pass adv-accept-3: program.json and spec 1.4.6 stated the plain form while generator 4
|
||||
//! appends `'sub/' || sub_version_le16`, so a client written from the text derived 30956569d8f3d8d7 for Devnet 3's epoch
|
||||
//! 0 where the chain says fce15bf61030be57). The text is parsed part by part and the bytes come from the pack's own fields
|
||||
//! (generator, attempt, seed_words, sub_version, load_class), never from the code's id function, so the test fails the
|
||||
//! moment the printed derivation and the hashed bytes disagree.
|
||||
|
||||
use igneum_pow::generator::LoadClass;
|
||||
use serde_json::Value;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn fnv1a64(b: &[u8]) -> u64 {
|
||||
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
|
||||
for &x in b {
|
||||
h ^= x as u64;
|
||||
h = h.wrapping_mul(0x0000_0100_0000_01b3);
|
||||
}
|
||||
h
|
||||
}
|
||||
|
||||
fn hex64(v: &Value) -> u64 {
|
||||
u64::from_str_radix(v.as_str().unwrap().trim_start_matches("0x"), 16).unwrap()
|
||||
}
|
||||
|
||||
/// The bytes a derivation part names, from the pack's own fields.
|
||||
fn part_bytes(part: &str, j: &Value, class: Option<&LoadClass>) -> Vec<u8> {
|
||||
if let Some(lit) = part.strip_prefix('\'').and_then(|p| p.strip_suffix('\'')) {
|
||||
return lit.as_bytes().to_vec();
|
||||
}
|
||||
let c = || class.unwrap_or_else(|| panic!("part {part} needs the pack's load_class"));
|
||||
match part {
|
||||
"generator_le32" => (j["generator"].as_u64().unwrap() as u32).to_le_bytes().to_vec(),
|
||||
"attempt_le32" => (j["attempt"].as_u64().unwrap() as u32).to_le_bytes().to_vec(),
|
||||
"seed_words as little-endian bytes" => j["seed_words"].as_array().unwrap().iter().flat_map(|w| (hex64(w) as u32).to_le_bytes()).collect(),
|
||||
"sub_version_le16" => (j["sub_version"].as_u64().expect("a generator-4 pack carries sub_version") as u16).to_le_bytes().to_vec(),
|
||||
"mix[3]" => c().mix.to_vec(),
|
||||
"load_slots_u8" => vec![c().load_slots],
|
||||
"scratch_k_u8" => vec![c().scratch.unwrap()],
|
||||
"scratch_kb_u8" => vec![c().scratch_kb],
|
||||
"mixer_mult_u8" => vec![c().mixer_mult],
|
||||
"growth_u8" => vec![c().growth as u8],
|
||||
"derive_len_le16" => c().derive_len.to_le_bytes().to_vec(),
|
||||
"shadow_instrs_le16" => c().shadow.unwrap().instrs.to_le_bytes().to_vec(),
|
||||
"shadow_reps_le16" => c().shadow.unwrap().reps.to_le_bytes().to_vec(),
|
||||
"hot_mb_u8" => vec![c().hot.unwrap().mb],
|
||||
"hot_k_u8" => vec![c().hot.unwrap().k],
|
||||
other => panic!("derivation part {other:?} is not one this test knows; teach it the field before pinning such a pack"),
|
||||
}
|
||||
}
|
||||
|
||||
fn rederive(dir: &Path) -> (u64, u64, String) {
|
||||
let j: Value = serde_json::from_str(&std::fs::read_to_string(dir.join("program.json")).unwrap()).unwrap();
|
||||
let text = j["program_id_derivation"].as_str().unwrap().to_string();
|
||||
let parts = text.strip_prefix("FNV-1a 64 over ").unwrap_or_else(|| panic!("{}: the derivation does not start with 'FNV-1a 64 over '", dir.display()));
|
||||
let class = j["load_class"].as_str().and_then(LoadClass::parse);
|
||||
let mut b = Vec::new();
|
||||
for part in parts.split(" || ") {
|
||||
b.extend(part_bytes(part.trim(), &j, class.as_ref()));
|
||||
}
|
||||
(fnv1a64(&b), hex64(&j["program_id"]), text)
|
||||
}
|
||||
|
||||
fn pinned_packs() -> Vec<PathBuf> {
|
||||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda");
|
||||
let mut out = Vec::new();
|
||||
for sub in ["packs-ca3-v4", "packs-ca3-shadow"] {
|
||||
let mut v: Vec<_> = std::fs::read_dir(root.join(sub)).unwrap().map(|e| e.unwrap().path()).filter(|p| p.join("program.json").is_file()).collect();
|
||||
v.sort();
|
||||
out.extend(v);
|
||||
}
|
||||
out.push(root.join("packs/igneum-devnet-v4-epoch0"));
|
||||
assert!(out.len() >= 18, "the pinned packs are missing; this test never skips ({} found)", out.len());
|
||||
out
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_pinned_pack_id_rederives_from_its_own_derivation_text() {
|
||||
let mut seen_sub = false;
|
||||
for dir in pinned_packs() {
|
||||
let (derived, pinned, text) = rederive(&dir);
|
||||
assert_eq!(derived, pinned, "{}: the id re-derived from \"{text}\" is {derived:016x}, the pack says {pinned:016x}", dir.display());
|
||||
if text.contains("'sub/'") {
|
||||
seen_sub = true;
|
||||
}
|
||||
}
|
||||
assert!(seen_sub, "at least one generator-4 pack states the 'sub/' || sub_version_le16 suffix");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_class_v4_derivation_states_the_suffix_and_the_plain_text_derives_another_id() {
|
||||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda/packs-ca3-v4/v4-devnet-epoch0");
|
||||
let j: Value = serde_json::from_str(&std::fs::read_to_string(root.join("program.json")).unwrap()).unwrap();
|
||||
let text = j["program_id_derivation"].as_str().unwrap();
|
||||
assert!(text.ends_with("attempt_le32 || 'sub/' || sub_version_le16"), "{text}");
|
||||
// the known-failed case: the text as printed before 7 October 2026 (no suffix) derives a different id
|
||||
let plain = "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32";
|
||||
let mut b = Vec::new();
|
||||
for part in plain.strip_prefix("FNV-1a 64 over ").unwrap().split(" || ") {
|
||||
b.extend(part_bytes(part, &j, None));
|
||||
}
|
||||
assert_ne!(fnv1a64(&b), hex64(&j["program_id"]), "the plain text must not derive the pinned id (it did before the fix)");
|
||||
assert_eq!(hex64(&j["program_id"]), 0xa785_0016_87d8_688a);
|
||||
}
|
||||
|
||||
/// The record maker (ignored in the suite): prints `DERIVATION <dir> <id> <text>` for every pack directory under proto-cuda,
|
||||
/// the program rebuilt as the pack tests rebuild it (generator 3 with era bytes under class v2's era layout for packs-ca2-era,
|
||||
/// generator 3 through the v3 seam otherwise, generator 2 with its load class, plain generator 2), so program.json's line can
|
||||
/// be set from the code's own recipe after a derivation change. `cargo test --release --test derivation -- --ignored --nocapture`.
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn print_every_pack_derivation() {
|
||||
use igneum_pow::generator::{generate_era, generate_from_seed_bytes, generate_from_seed_bytes_class, generate_from_seed_bytes_program_class, ProgramClass, GENERATOR_VERSION_V3};
|
||||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto-cuda");
|
||||
let mut dirs: Vec<PathBuf> = std::fs::read_dir(&root).unwrap().map(|e| e.unwrap().path()).filter(|p| p.file_name().unwrap().to_str().unwrap().starts_with("packs")).flat_map(|g| std::fs::read_dir(g).unwrap().map(|e| e.unwrap().path())).filter(|p| p.join("program.json").is_file()).collect();
|
||||
dirs.sort();
|
||||
for dir in dirs {
|
||||
let j: Value = serde_json::from_str(&std::fs::read_to_string(dir.join("program.json")).unwrap()).unwrap();
|
||||
let seed = j["seed"].as_str().unwrap();
|
||||
let seed_bytes = igneum_pow::bind::unhex(j["seed_bytes"].as_str().unwrap()).unwrap();
|
||||
let gen = j["generator"].as_u64().unwrap() as u32;
|
||||
let era = j.get("era_seed_bytes").and_then(|v| v.as_str()).map(|h| igneum_pow::bind::unhex(h).unwrap());
|
||||
let lc = j.get("load_class").and_then(|c| c.as_str());
|
||||
let program = if gen == GENERATOR_VERSION_V3 && dir.parent().unwrap().file_name().unwrap() == "packs-ca2-era" {
|
||||
let allowed: Vec<u8> = j["era"]["allowed_widths"].as_array().unwrap().iter().map(|v| v.as_u64().unwrap() as u8).collect();
|
||||
generate_era(seed, &seed_bytes, LoadClass::V2, era.as_deref().unwrap(), &allowed)
|
||||
} else if gen == GENERATOR_VERSION_V3 {
|
||||
generate_from_seed_bytes_program_class(seed, &seed_bytes, ProgramClass::V3, era.as_deref())
|
||||
} else if gen == 4 {
|
||||
generate_from_seed_bytes_program_class(seed, &seed_bytes, ProgramClass::V4, era.as_deref())
|
||||
} else if let Some(c) = lc {
|
||||
let Some(class) = LoadClass::parse(c) else {
|
||||
println!("SKIP {} load_class {c:?} is a name the parser no longer accepts (an old experiment's pack); its derivation line stands as written", dir.strip_prefix(&root).unwrap().display());
|
||||
continue;
|
||||
};
|
||||
generate_from_seed_bytes_class(seed, &seed_bytes, class)
|
||||
} else {
|
||||
generate_from_seed_bytes(seed, &seed_bytes)
|
||||
};
|
||||
println!("DERIVATION {} {:016x} {}", dir.strip_prefix(&root).unwrap().display(), program.program_id(), program.program_id_derivation());
|
||||
assert_eq!(program.program_id(), hex64(&j["program_id"]), "{}: the rebuilt program's id is the pack's", dir.display());
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
# public key compiled into src/manifest.rs, runs each job that targets it ONCE per id, and reports to the log intake
|
||||
# as run_id job-<id>-<machine id8> (read back with tools/jobs.mjs).
|
||||
#
|
||||
# packaging/ota/publish-jobs.sh add --kind run --target 1ccfe586 --script path.ps1 [--elevated] [--stop-miners] \
|
||||
# packaging/ota/publish-jobs.sh add --kind run --target 1ccfe586 --script path.ps1 [--stop-miners] \ (--elevated is REFUSED: the 7 Oct 2026 no-prompt rule)
|
||||
# [--cards-off key,key] [--timeout-minutes 60] [--shell powershell|bash] --title "..." [--expires-hours 48] [--deploy]
|
||||
# (--cards-off: the RUNNER switches these cards off through the app's own card path before the script and puts them
|
||||
# back exactly on any exit, done, failed, timeout, aborted or the app quitting; keys with or without the device
|
||||
|
|
@ -72,7 +72,15 @@ while [ $# -gt 0 ]; do
|
|||
--expires-hours) EXPIRES_H="$2"; shift 2 ;;
|
||||
--script) SCRIPT="$2"; shift 2 ;;
|
||||
--shell) SHELL_KIND="$2"; shift 2 ;;
|
||||
--elevated) ELEVATED=1; shift ;;
|
||||
--elevated)
|
||||
# STANDING RULE (the founder through main, 7 October 2026, 18:5x UTC): no PC job may raise a UAC prompt or need a click, ever. A run
|
||||
# job published --elevated launches the script through an administrator prompt on the PC (the 18:27Z job
|
||||
# run-ca3-pc1-v4-eff-5090-20261007 waited 2 minutes for a click and died with exit 251, the card switched off for nothing).
|
||||
# Anything needing rights goes through the installed app's Igneum Power Helper task (app/igneum-app/src/powertask.rs,
|
||||
# present when Power control is on); a job reads the task's presence first and, without it, reports
|
||||
# "no elevation path: Power control off on <machine>" with the measured-only rows it can still take. The flag is refused here.
|
||||
echo "publish-jobs: --elevated is refused (standing rule of 7 October 2026: no PC job raises a UAC prompt; use the Igneum Power Helper task from the script, see app/igneum-app/src/powertask.rs and tools/ca3-v4-amend/pc1-v4-efficiency.ps1)" >&2
|
||||
exit 3 ;;
|
||||
--stop-miners) STOP_MINERS=1; shift ;;
|
||||
--cards-off) CARDS_OFF="$2"; shift 2 ;;
|
||||
--timeout-minutes) TIMEOUT_MIN="$2"; shift 2 ;;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"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",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'hot/' || hot_mb_u8 || hot_k_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"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",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'hot/' || hot_mb_u8 || hot_k_u8 || 'added'",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"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",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'hot/' || hot_mb_u8 || hot_k_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"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",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'hot/' || hot_mb_u8 || hot_k_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"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",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'hot/' || hot_mb_u8 || hot_k_u8 || 'added'",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"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",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'hot/' || hot_mb_u8 || hot_k_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"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",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'hot/' || hot_mb_u8 || hot_k_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"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",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'hot/' || hot_mb_u8 || hot_k_u8 || 'added'",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0xa6892c64d9ddd7f7",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000",
|
||||
"seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x951b89584750bd75",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x7f4a5ca0a3637820",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8 || 'derive/' || derive_len_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000",
|
||||
"seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x72c1d8048aef9542",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8 || 'derive/' || derive_len_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0xb42f8c8549967581",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8 || 'shadow/' || shadow_instrs_le16 || shadow_reps_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x9a5be3853b011f6c",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8 || 'shadow/' || shadow_instrs_le16 || shadow_reps_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x9a3d4d853ae722fb",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8 || 'shadow/' || shadow_instrs_le16 || shadow_reps_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x9a8b77853b298baa",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8 || 'shadow/' || shadow_instrs_le16 || shadow_reps_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x9abe71853b54db11",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8 || 'shadow/' || shadow_instrs_le16 || shadow_reps_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x9ae3d3853b749dd4",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8 || 'shadow/' || shadow_instrs_le16 || shadow_reps_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x9a477f853aefcc76",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8 || 'shadow/' || shadow_instrs_le16 || shadow_reps_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x9b6191853bdf72c1",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8 || 'shadow/' || shadow_instrs_le16 || shadow_reps_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x90cd909ac3e78d4a",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'mixer/' || mixer_mult_u8 || growth_u8 || 'shadow/' || shadow_instrs_le16 || shadow_reps_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 4,
|
||||
"attempt": 1,
|
||||
"program_id": "0xa785001687d8688a",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || 'sub/' || sub_version_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000",
|
||||
"seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 4,
|
||||
"attempt": 1,
|
||||
"program_id": "0xa785001687d8688a",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || 'sub/' || sub_version_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000",
|
||||
"seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 4,
|
||||
"attempt": 1,
|
||||
"program_id": "0xa785001687d8688a",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || 'sub/' || sub_version_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000",
|
||||
"seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 4,
|
||||
"attempt": 1,
|
||||
"program_id": "0xa785001687d8688a",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || 'sub/' || sub_version_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000",
|
||||
"seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 4,
|
||||
"attempt": 1,
|
||||
"program_id": "0xa785001687d8688a",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || 'sub/' || sub_version_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000",
|
||||
"seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 4,
|
||||
"attempt": 1,
|
||||
"program_id": "0xa785001687d8688a",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || 'sub/' || sub_version_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000",
|
||||
"seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 4,
|
||||
"attempt": 1,
|
||||
"program_id": "0xa785001687d8688a",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || 'sub/' || sub_version_le16",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-epoch/edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07/day/69676e65756d2d6461792ffa50000000000000",
|
||||
"seed_bytes": "edc4fa844da9dc98d37e965176f6558a31560e40502ab3ae5491b21aaaabfb07",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0xe0b70cd155c28f4b",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'scratch/' || scratch_k_u8 || scratch_kb_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0xe0afe0d155bc25d9",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'scratch/' || scratch_k_u8 || scratch_kb_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0xe0b080d155bd35b9",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'scratch/' || scratch_k_u8 || scratch_kb_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0xe0c444d155cd78cf",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'scratch/' || scratch_k_u8 || scratch_kb_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0xe0c4a4d155ce1bef",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'scratch/' || scratch_k_u8 || scratch_kb_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0xe0d1dcd155d90573",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'scratch/' || scratch_k_u8 || scratch_kb_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0xe0d27cd155da1553",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8 || 'scratch/' || scratch_k_u8 || scratch_kb_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x6a183165e23fd2bc",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x5c427d666b4ed4f4",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"generator": 2,
|
||||
"attempt": 0,
|
||||
"program_id": "0x5c4269666b4eb2f8",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
|
||||
"program_id_derivation": "FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed_words as little-endian bytes || attempt_le32 || mix[3] || load_slots_u8",
|
||||
"dataset_mode": "memory-hard",
|
||||
"seed": "igneum-genesis",
|
||||
"seed_bytes": "69676e65756d2d67656e65736973",
|
||||
|
|
|
|||
|
|
@ -419,8 +419,10 @@ for (const [file, active] of PAGES) {
|
|||
{
|
||||
const bj = JSON.parse(readFileSync(join(here, 'miner-bench.json'), 'utf8'));
|
||||
const isCurrent = (r) => r.generator === 'v2' && r.date >= '2026-10-06';
|
||||
const byMhW = (a, b) => ((b.mh_per_w ?? -1) - (a.mh_per_w ?? -1)) || (b.mh_s - a.mh_s) || (a.card < b.card ? -1 : 1);
|
||||
const cur = bj.rows.filter(isCurrent).sort(byMhW);
|
||||
const byRate = (a, b) => (b.mh_s - a.mh_s) || (a.card < b.card ? -1 : 1);
|
||||
const curAll = bj.rows.filter(isCurrent).sort(byRate);
|
||||
const cur = curAll.filter(r => r.group !== 'datacentre');
|
||||
const dcRows = curAll.filter(r => r.group === 'datacentre');
|
||||
const earlier = bj.rows.filter(r => !isCurrent(r)).sort((a, b) => (a.card < b.card ? -1 : a.card > b.card ? 1 : a.generator < b.generator ? -1 : a.generator > b.generator ? 1 : b.mh_s - a.mh_s));
|
||||
const rows = bj.rows;
|
||||
const fmt = (n) => Number(n).toLocaleString('en-GB', { maximumFractionDigits: 1 });
|
||||
|
|
@ -430,16 +432,17 @@ for (const [file, active] of PAGES) {
|
|||
// capture showed eleven columns clipped at five, every row inflated by off-screen wrapped text). The detail row moves
|
||||
// with its data row on a sort.
|
||||
const heads = [
|
||||
['Card', 'card', 'text'], ['MH/s', 'mh_s', 'num'], ['W', 'watts', 'num'], ['MH/W', 'mh_per_w', 'num'],
|
||||
['Card', 'card', 'text'], ['MH/s', 'mh_s', 'num'], ['W', 'watts', 'num'], ['MH per wall watt', 'mh_per_w', 'num'],
|
||||
['v4 cost', 'v4_cost_short', 'text'], ['Tuned', 'tuned_short', 'text'], ['Hive core / mem / PL', 'hive_short', 'text'], ['Date', 'date', 'text'], ['Who', 'by', 'text'],
|
||||
];
|
||||
// the short forms are one clause: the first watts or percent figure of the cost ("+16 W", "+145.3 W unlocked"), the
|
||||
// tune state's first words; the full sentences live in the detail row
|
||||
const shortV4 = (r) => { const v = r.v4_cost || 'not measured'; if (/^not measured/.test(v)) return 'not measured'; const m = v.match(/^([+-]?[\d.,]+\s*(?:W|percent)(?:\s+(?:unlocked|of rate))?)/); return m ? m[1].replace(' of rate', ' rate') : v.split(/[,;(]| for | at /)[0].trim(); };
|
||||
const shortV4 = (r) => { if (r.v4_short) return r.v4_short; const v = r.v4_cost || 'not measured'; if (/^not measured/.test(v)) return 'not measured'; const m = v.match(/^([+-]?[\d.,]+\s*(?:W|percent)(?:\s+(?:unlocked|of rate))?)/); return m ? m[1].replace(' of rate', ' rate') : v.split(/[,;(]| for | at /)[0].trim(); };
|
||||
const shortTuned = (r) => { const v = r.tuned || 'stock, mining'; return v.split(/[:;(]/)[0].replace('full Ember Tune', 'Ember Tune').replace('stock, bench only', 'stock, bench').replace('no lever on Apple silicon', 'no lever').trim(); };
|
||||
const shortHive = (r) => { const h = r.hive; return (h && h.core_mhz != null) ? fmt(h.core_mhz) + ' / ' + fmt(h.mem_mhz) + ' / ' + fmt(h.pl_w) + ' W' : 'stock'; };
|
||||
const cells = (r) => [
|
||||
[r.card, r.card], [fmt(r.mh_s), r.mh_s], [r.watts == null ? 'not read' : fmt(r.watts), r.watts ?? -1], [r.mh_per_w == null ? 'not measured' : fmt3(r.mh_per_w), r.mh_per_w ?? -1],
|
||||
[r.card, r.card], [r.mh_s_display || fmt(r.mh_s), r.mh_s], [r.watts_display || (r.watts == null ? 'not read' : fmt(r.watts) + (r.watt_basis === 'chip' ? ' (chip watts, not wall)' : '')), r.watts ?? -1],
|
||||
[r.mh_per_w == null ? 'not measured' : (r.watt_basis === 'chip' ? '\u25CB ' + fmt3(r.mh_per_w) + ' (chip watts, not ranked)' : fmt3(r.mh_per_w)), r.watt_basis === 'chip' ? -1 : (r.mh_per_w ?? -1)],
|
||||
[shortV4(r), shortV4(r)], [shortTuned(r), shortTuned(r)], [shortHive(r), r.hive && r.hive.core_mhz != null ? 'a ' + r.hive.core_mhz : 'z stock'], [r.date, r.date], [r.by.replace('measured by the ', '').replace('reported by the ', 'reported, '), r.by],
|
||||
];
|
||||
const detail = (r) => {
|
||||
|
|
@ -455,7 +458,7 @@ for (const [file, active] of PAGES) {
|
|||
return parts.join(' · ');
|
||||
};
|
||||
const render = (list, id) => '<div class="tbl"><table class="sortable bench" id="' + id + '"><thead><tr>' +
|
||||
heads.map(([h, k, t]) => `<th data-key="${k}" data-type="${t}" aria-sort="${k === 'mh_per_w' ? 'descending' : 'none'}"><button type="button" class="sort">${h}</button></th>`).join('') +
|
||||
heads.map(([h, k, t]) => `<th data-key="${k}" data-type="${t}" aria-sort="${k === 'mh_s' ? 'descending' : 'none'}"><button type="button" class="sort">${h}</button></th>`).join('') +
|
||||
'</tr></thead><tbody>' + list.map(r => '<tr class="row">' + cells(r).map(([c, v]) => `<td data-v="${esc(String(v))}">${esc(String(c))}</td>`).join('') + '</tr>' +
|
||||
`<tr class="detail"><td colspan="${heads.length}">${detail(r)}</td></tr>`).join('') + '</tbody></table></div>';
|
||||
const sortScript = `<script>
|
||||
|
|
@ -475,8 +478,11 @@ for (const [file, active] of PAGES) {
|
|||
});
|
||||
})();
|
||||
</script>`;
|
||||
const sortStyle = '<style>th .sort{all:unset;cursor:pointer;font:inherit;color:inherit;white-space:nowrap}th .sort::after{content:" \\2195";opacity:.45}th[aria-sort="descending"] .sort::after{content:" \\2193";opacity:1}th[aria-sort="ascending"] .sort::after{content:" \\2191";opacity:1}table.bench{min-width:0;width:100%;table-layout:auto}table.bench tr.row td{border-bottom:0;white-space:normal;overflow-wrap:anywhere}table.bench tr.row td:nth-child(2),table.bench tr.row td:nth-child(3),table.bench tr.row td:nth-child(4),table.bench tr.row td:nth-child(8){white-space:nowrap}table.bench tr.row td:first-child{min-width:150px}table.bench tr.row td:nth-child(5),table.bench tr.row td:nth-child(6){white-space:nowrap}table.bench tr.row td:nth-child(7){white-space:nowrap}table.bench tr.detail td{font-size:13px;color:var(--ash);padding-top:0;overflow-wrap:anywhere;white-space:normal}table.bench tr.detail td b{color:var(--ink-2);font-weight:600}details.earlier{margin:var(--s-4) 0}details.earlier summary{cursor:pointer;color:var(--bone)}</style>';
|
||||
const sortStyle = '<style>th .sort{all:unset;cursor:pointer;font:inherit;color:inherit;white-space:nowrap}th .sort::after{content:" \\2195";opacity:.45}th[aria-sort="descending"] .sort::after{content:" \\2193";opacity:1}th[aria-sort="ascending"] .sort::after{content:" \\2191";opacity:1}table.bench{min-width:0;width:100%;table-layout:auto}table.bench tr.row td{border-bottom:0;white-space:normal;overflow-wrap:anywhere}table.bench tr.row td:nth-child(2),table.bench tr.row td:nth-child(3),table.bench tr.row td:nth-child(4),table.bench tr.row td:nth-child(8){white-space:nowrap}table.bench tr.row td:first-child{min-width:150px}table.bench tr.row td:nth-child(5),table.bench tr.row td:nth-child(6){white-space:nowrap}table.bench tr.row td:nth-child(7){white-space:nowrap}table.bench tr.detail td{font-size:13px;color:var(--ash);padding-top:0;overflow-wrap:anywhere;white-space:normal}table.bench tr.detail td b{color:var(--ink-2);font-weight:600}details.earlier{margin:var(--s-4) 0}p.best{font-size:18px;margin:0 0 14px}details.earlier summary{cursor:pointer;color:var(--bone)}</style>';
|
||||
const table = render(cur, 'bench-current');
|
||||
const dcTable = render(dcRows, 'bench-datacentre');
|
||||
const best = cur.slice().sort(byRate)[0];
|
||||
const bestLine = best ? `<p class="best"><strong>Best desktop card:</strong> ${esc(best.card.replace(/ \(.*$/, ''))}, ${esc(fmt(best.mh_s))} MH/s, ${esc(fmt3(best.mh_per_w))} MH per wall watt tuned (measured, ${esc(best.date)}).</p>` : '';
|
||||
const earlierTable = render(earlier, 'bench-earlier');
|
||||
// Ember Tune's fleet priors (site/miner-priors.json, tools/tuning.mjs --priors --site): one row per card model,
|
||||
// driver major and program class; a row under the sample floor shows its count and no point
|
||||
|
|
@ -496,10 +502,15 @@ for (const [file, active] of PAGES) {
|
|||
const body = scrubBench([
|
||||
sortStyle,
|
||||
'<h2 id="table">The table</h2>',
|
||||
'<p>One row per card on the current class: the class v4 program (the latency-shadow block over the class v3 hash), or a class v3 row re-measured with its class v4 cost on 6 October 2026 or later. Click a column header to sort; the table opens by MH per watt. Integrated GPUs are not listed. The earlier classes sit below, collapsed.</p>',
|
||||
bestLine,
|
||||
'<p>One row per card on the current class: the class v4 program (the latency-shadow block over the class v3 hash), or a class v3 row re-measured with its class v4 cost on 6 October 2026 or later. Cards you can buy first, sorted by hash rate; click a column header to sort. MH per wall watt uses board or wall power; a row whose watts are the chip\'s (Apple silicon: GPU plus DRAM from IOReport) says so and is not ranked on that column. Integrated GPUs are not listed. Datacentre cards and the earlier classes sit below, collapsed.</p>',
|
||||
'<p><strong>Why the rate fell from the first bench to today.</strong> The genesis program did 104 dependent random 4-byte loads per hash over a 1 GiB dataset; the hourly program and class v3 do 128, with the mixer between them; class v4 adds about 100,000 integer operations per hash that ride in the memory wait. So the hash is bound by random-read bandwidth by design, and a card\'s MH/s is a relative number: the difficulty follows it, and the same card earns the same share of blocks at 136 MH/s on class v3 as it did at 228 MH/s on the genesis program. What a miner compares is hash per watt, and what the chain cares about is the chip edge, which the shadow work is there to cut.</p>',
|
||||
table,
|
||||
`<p>Rows on the current class: ${cur.length}. Each row names the engineering log entry or the job it came from.</p>`,
|
||||
`<p>Cards you can buy on the current class: ${cur.length}. Each row names the engineering log entry or the job it came from.</p>`,
|
||||
'<details class="earlier"><summary>Datacentre cards (' + dcRows.length + ' rows, rented for the measurement; about three times the rented dollars per hash of a desktop card)</summary>',
|
||||
'<p>Rented cards measured on the class v4 program by the fleet, stock clocks. They mine; they are not what a home miner buys.</p>',
|
||||
dcTable,
|
||||
'</details>',
|
||||
'<p><strong>The Hive flight sheet column.</strong> Where a card has a measured tune point, the column gives the core clock lock, the memory clock and the power limit to copy into a HiveOS flight sheet (core / mem / PL); the line under each row carries the label with the date, the class v4 cost in full, the miner and driver, the source and the note. Stock means no tune point has been measured yet. The Hive package mines at these settings through Hive\'s own overclock controls; the desktop app\'s Ember Tune lands on them by itself.</p>',
|
||||
'<details class="earlier"><summary>Earlier classes (the genesis program, the hourly program, class v3 before the shadow): ' + earlier.length + ' rows, not comparable with the table above</summary>',
|
||||
'<p>These rows are the bench numbers of 3 and 4 October 2026: the genesis program (104 loads per hash), the hourly program and the first class v3 miner. A higher MH/s here is a different hash, not a faster card.</p>',
|
||||
|
|
@ -520,7 +531,7 @@ for (const [file, active] of PAGES) {
|
|||
const toc = [{ lvl: 2, t: 'The table', id: 'table' }, { lvl: 2, t: 'How a row gets here', id: 'how' }, { lvl: 2, t: 'Fleet tuning priors', id: 'priors' }];
|
||||
writeFileSync(join(here, 'miners.html'), page('Igneum GPU bench table', 'Measured Igneum hash rates per GPU: card, generator version, best MH/s, MH per watt where measured, miner version, date and the log entry each number came from.', body, toc,
|
||||
'Measured hash rates per card on the Igneum lottery hash, with the generator version, the miner version, the date and the log entry behind each number.',
|
||||
{ path: '/miners', heading: 'GPU bench table', eyebrow: `${rows.length} measured rows, ${prows.length} fleet tuning models`, crumb: 'GPU bench table' }));
|
||||
{ path: '/miners', heading: 'GPU bench table', eyebrow: `${cur.length} cards you can buy, ${dcRows.length} datacentre, ${prows.length} fleet tuning models`, crumb: 'GPU bench table' }));
|
||||
built.push('miners.html (' + rows.length + ' rows)');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -229,6 +229,10 @@
|
|||
<li><strong>A cryptography team.</strong> Not yet. One founder working with AI systems wrote the design and the code; external reviewers are named and paid before gate 3, and every security claim here is a design claim until then.</li>
|
||||
<li><strong>Finality that no amount of hardware can break.</strong> No. A miner holding a third of the last 30 days of blocks can split finality during a network partition, and two thirds can lock a bad checkpoint for a double-spend bounded by the 12-hour finality depth. Reaching a third takes at least ten days of producing every block on the chain, in public; an attacker matching the honest network needs twenty days for a third and never reaches two thirds. That is harder than attacking Bitcoin, where a majority can reorganise at once, and it is the limit of proof of work without stake or an outside chain. Igneum chose those limits on purpose. The floor is also bounded in time: an honest partition that lasts long enough for each side's own new blocks to reach two thirds of its window locks on both sides, about ten days of a 30-day window at an even split, and an operator must then resolve it (measured on a test network, 4 October 2026).</li>
|
||||
<li><strong>Finality that never pauses.</strong> No. A lock needs two thirds of all 30-day mining weight. Whenever less than two thirds of that weight is connected and signing, finality pauses until it returns or ages out of the window, up to 30 days. The chain keeps running on proof of work and the node reports the pause.</li>
|
||||
<li><strong>A label that costs nothing.</strong> No. Some investors and exchanges read "GPU-mined" as 2021 whatever the proofs do, and nothing here measures that cost. The only evidence will be whether the first miner apps and verifiable-compute apps sign despite the label.</li>
|
||||
<li><strong>A chain you can debug today.</strong> Not yet. The node does not serve debug_traceTransaction, eth_subscribe or eth_getProof, and there is no public RPC, faucet or explorer for the devnet. They come in a fixed order (docs and templates, then the tracing and subscription RPCs, then a public RPC, listing and faucet, then the explorer) and no outside team is invited to build before the second step is done.</li>
|
||||
<li><strong>A veto on job results.</strong> No. A segment proof is checked against every node's own execution; a proving job for another chain is not, because no full node can re-run an arbitrary program, so a soundness bug in the proof system in force reaches the requesting contract. A job output can mint nothing and touch no system contract, and an app that acts irreversibly on a job result keeps its own fallback.</li>
|
||||
<li><strong>A delay function that outlives a quantum computer.</strong> No. The class-group delay between a locked checkpoint and the next program seed falls to the same machine that would forge the vote keys; it is flagged in the specification, not yet sized, and the fallback is a hash-chain delay behind the same version byte that moves the signature scheme, so both flip in one class change. A grindable hourly seed is a liveness nuisance against the lottery, not a break of finality.</li>
|
||||
<li><strong>A finished protocol.</strong> The sustained-mining finality rule is the newest piece and the one that external review will try hardest to break. The specification, the review and the benchmarks are published as they happen.</li>
|
||||
</ul>
|
||||
<p>Everything in this document is subject to the gates on the roadmap. Nothing in it is an offer to sell anything. Found an error, or a criticism this document does not answer? Email <a href="mailto:hello@igneum.network">hello@igneum.network</a>, or open an issue on the public specification repository: <a href="https://git.igneum.network/igneum-network/spec/issues" rel="noopener">git.igneum.network/igneum-network/spec/issues</a>. Post reaches Igneum Labs LTD, Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre.</p></div>
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ td.mono{font-family:var(--f-mono);font-size:12.5px;min-width:180px}td.iv{color:v
|
|||
<tr data-status="tested by the team"><td class="n">14</td><td class="claim">Ethereum bytecode runs unchanged, with the documented differences of spec 7.1<div class="where">Homepage Build card; litepaper Building</div></td><td><span class="st st-2">tested by the team</span></td><td class="mono">as row 13; fixes <code>F-exec-A</code>, <code>F-exec-B</code> (spec 7.5)</td><td><code>tools/evm-smoke/smoke.mjs</code>: deploy via viem, <code>increment</code>, <code>hashLoop</code>, <code>eth_estimateGas</code>, <code>eth_getLogs</code>; <code>tools/exec-attacks</code> scenarios 1 and 3; bench-log "execution layer attack fixes"</td><td>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 <code>Prover</code> precompile, proof records and the shard planner are not in the node</td><td class="iv">none yet</td></tr>
|
||||
<tr data-status="implemented"><td class="n">15</td><td class="claim">Every block is proven, with the proof landing within about a minute at launch<div class="where">Homepage stats ("~60 s to a proof"); litepaper Proving; roadmap phase 3 gate</div></td><td><span class="st st-1">implemented</span></td><td class="mono">repo <code>d7e1f89</code> (GPU proof), <code>e01a3cc</code>, <code>292e800</code>, <code>eedd136</code> (<code>proving/igneum-prove</code>: shard cutter, MPT witnesses, shard and aggregator guests); SP1 6.8.1; spec 7.2, 7.6</td><td><code>proving/windows-wsl2</code> (SETUP-PROVER, PROVE-BLOCK) on the RTX 5090; <code>igneum-prove-host --mode block</code> on <code>proving/fixtures/</code>; bench-log "proving v0 on the RTX 5090" and "proving: devnet v4 shards"</td><td>First GPU proof of an Igneum block, 4 October 2026, RTX 5090 (WSL2, SP1 cuda, mining paused): fixture <code>block-78-increment</code> (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 <code>docs/benchmarks/proving-e2e.md</code>. 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 the RTX 5090 Windows rig in 34 s, verified on the Apple M5 Max 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 <code>proving_v1_activation_daa</code> (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</td><td class="iv">none yet</td></tr>
|
||||
<tr data-status="designed"><td class="n">16</td><td class="claim">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)<div class="where">Litepaper Proving ("The proving budget"); roadmap gate 2</div></td><td><span class="st st-0">designed</span></td><td class="mono">spec 5.1 (Target), 7.6 (<code>S_p</code> provisional, 7,500,000 pgas = <code>B_p</code> / 4)</td><td><code>PROVE-SHARD.bat</code> on the RTX 5090 (pending); the end-to-end standard in <code>docs/benchmarks/proving-e2e.md</code>; bench-log "proving: devnet v4 shards"</td><td>Measured on a 32 GB card, not yet on a 12 GB card. A shard at the provisional <code>S_p</code> 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</td><td class="iv">none yet</td></tr>
|
||||
<tr data-status="designed"><td class="n">17</td><td class="claim">The chip resistance claim: at launch the strongest chip in the public model reaches 2.1x (a core as good as a GPU lane, k = 1) to 3.4x (a core three times better, k about 0.33) per joule against an RTX 5090 under class v4, live from genesis on the testnet and the mainnet; the ladder's second rung brings it to about 2.8x; class v5 makes the dataset the chain's state so a stateless or stale chip is wrong on every item; the hot-set cache is bounded at 1.067x at the ceiling and the weak-day FPGA at 12 percent on 15 days a century, both routed to the next class; datacentre silicon does not change the question; a stored-dataset chip pays for itself only at about USD 100 M of market cap in two years; without class v4 the same chip would reach 5x to 9x (the class v3 baseline, the devnet's starting state, never the launch state)<div class="where">the home page's chip line, the litepaper's chip section (/litepaper#chip-model), the miner page's line</div></td><td><span class="st st-0">tested by the team (every card, the verifier, the two attack-pass bounds, the H100), the chip itself modelled, class v5 and the ladder designed, the X9 figure claimed against a CPU core and never measured</span></td><td class="mono"><code>docs/analysis/chip-model-v3.md</code> 5 and 6; <code>docs/analysis/latency-shadow-2026-10-06.md</code>; <code>docs/plans/counter-asic-3-status.md</code>; <code>docs/analysis/attack-pass/f8-uniform.md</code>, <code>f4-weakday.md</code>, <code>docs/analysis/ca3-v4-uniform.md</code>; <code>docs/design/class-v5-stored-state.md</code>; the H100 and market-cap rows of 7 October; <code>docs/plans/cryptanalysis/in-house-pass.md</code> (the internal adversarial pass)</td><td>the chip model's arithmetic in its file; the card rows by the benchmark package; the attack-pass harnesses <code>tools/attack/f8-uniform</code> and the F4 census; the verifier by <code>igneum-pow bench</code></td><td>136 MH/s at 350 W (5090, bench) and 290 W (app); 27 MH/s at 21 W (M5 Max); 249 MH/s (H100 SXM) at 98 percent of its read ceiling, 1.78x hash, 1.15x MH/W, a third per rented dollar; 2.33 ms per warp; 2.1x, 3.4x, 2.8x at launch; the shadow's premium on a 5090 81.8 W at its knee (class v4 at the 1,200 MHz lock 133.80 MH/s at 305.1 W; class v3 at 1,300 MHz 134.62 at 223.3 W; 7 October 2026); 1.067x at the ceiling; 12 percent on 15 days a century; 10.85 ms at rung 3; USD 100 M; 5.1x to 9.2x the class v3 baseline; 6 and 7 October 2026, the M5 Max, the RTX 5090 Windows rig's RTX 5090, the three-card Windows rig's RX 9070 XT and RTX 4070, a rented H100 SXM, igneum-build-1 The k about 0.33 bound is the implied core of Bitmain's Antminer X9 (RandomX; 1,000 KH/s, 2,472 W, 2.47 J per KH, USD 5,600; pre-orders 26 December 2025), withdrawn in mid-May 2026 with buyers refunded before any unit shipped, no independent benchmark, commodity Sophgo SG2044 server SoCs with an AES accelerator, no tapeout: a claimed, unmeasured figure carried as the pessimistic bound, not a calibration point (attack pass AP-F5-1, 7 October 2026).</td><td class="iv">none yet; the next test is the internal adversarial pass (three lanes new to the hash code, outsider inputs only, reports published whole), and the one outside check is staged and waits on its escrow and the publish word</td></tr>
|
||||
<tr data-status="designed"><td class="n">17</td><td class="claim">The chip resistance claim: at launch the strongest chip in the public model reaches 2.1x (a core as good as a GPU lane, k = 1) to 3.4x (a core three times better, k about 0.33) per joule against an RTX 5090 under class v4, live from genesis on the testnet and the mainnet; the ladder's second rung brings it to about 2.8x; class v5 makes the dataset the chain's state so a stateless or stale chip is wrong on every item; the hot-set cache is bounded at 1.067x at the ceiling and the weak-day FPGA at 12 percent on 15 days a century, both routed to the next class; datacentre silicon does not change the question; a stored-dataset chip pays for itself only at about USD 100 M of market cap in two years; without class v4 the same chip would reach 5x to 9x (the class v3 baseline, the devnet's starting state, never the launch state)<div class="where">the home page's chip line, the litepaper's chip section (/litepaper#chip-model), the miner page's line</div></td><td><span class="st st-0">tested by the team (every card, the verifier, the two attack-pass bounds, the H100), the chip itself modelled, class v5 and the ladder designed, the X9 figure claimed against a CPU core and never measured</span></td><td class="mono"><code>docs/analysis/chip-model-v3.md</code> 5 and 6; <code>docs/analysis/latency-shadow-2026-10-06.md</code>; <code>docs/plans/counter-asic-3-status.md</code>; <code>docs/analysis/attack-pass/f8-uniform.md</code>, <code>f4-weakday.md</code>, <code>docs/analysis/ca3-v4-uniform.md</code>; <code>docs/design/class-v5-stored-state.md</code>; the H100 and market-cap rows of 7 October; <code>docs/plans/cryptanalysis/in-house-pass.md</code> (the internal adversarial pass)</td><td>the chip model's arithmetic in its file; the card rows by the benchmark package; the attack-pass harnesses <code>tools/attack/f8-uniform</code> and the F4 census; the verifier by <code>igneum-pow bench</code></td><td>136 MH/s at 350 W (5090, bench) and 290 W (app); 27 MH/s at 21 W (M5 Max); 249 MH/s (H100 SXM) at 98 percent of its read ceiling, 1.78x hash, 1.15x MH/W, a third per rented dollar; 2.33 ms per warp; 2.1x, 3.4x, 2.8x at launch; the shadow's premium on a 5090 81.8 W at its knee (class v4 at the 1,200 MHz lock 133.80 MH/s at 305.1 W; class v3 at 1,300 MHz 134.62 at 223.3 W; 7 October 2026); 1.067x at the ceiling; 12 percent on 15 days a century; 10.85 ms at rung 3; USD 100 M; 5.1x to 9.2x the class v3 baseline; 6 and 7 October 2026, the M5 Max, the RTX 5090 Windows rig's RTX 5090, the three-card Windows rig's RX 9070 XT and RTX 4070, a rented H100 SXM, igneum-build-1 The k about 0.33 bound is the implied core of Bitmain's Antminer X9 (RandomX; 1,000 KH/s, 2,472 W, 2.47 J per KH, USD 5,600; pre-orders 26 December 2025), withdrawn in mid-May 2026 with buyers refunded before any unit shipped, no independent benchmark, commodity Sophgo SG2044 server SoCs with an AES accelerator, no tapeout: a claimed, unmeasured figure carried as the pessimistic bound, not a calibration point (attack pass AP-F5-1, 7 October 2026).</td><td class="iv">none yet; the next test is the internal adversarial pass (three lanes new to the hash code, outsider inputs only, reports published whole), and no outside review has run yet</td></tr>
|
||||
<tr data-status="tested by the team"><td class="n">18</td><td class="claim">The chip resistance measurements: the program is latency-bound (random reads), not bandwidth-bound, on every card we own, and sits beyond a card's on-chip cache<div class="where">Litepaper Mining ("waits on memory latency, not on maths or bandwidth"), vs RandomX; the numbers page</div></td><td><span class="st st-2">tested by the team</span></td><td class="mono">readwidth e752fc7 (<code>docs/plans/read-width.md</code>), ca2-era 78c0ee4, ca2-cache 2de19e5 (<code>docs/plans/hot-table.md</code>)</td><td>The dependent-read probes at 32 to 1,024 MiB and the hash rate per class on the three cards; the latency-bound share = rate over the probe ceiling per load</td><td>Latency-bound share at the 1 GiB dataset: RTX 5090 0.96 (v2) and 1.01 (v3), RX 9070 XT 0.87 and 0.95, M5 Max 1.01 and 1.06; wider reads do not close the AMD gap (the 9070 XT does 2.4 G dependent reads per second at every width; the 5090 goes bandwidth-bound at 64 B, share 0.58); a 32 to 96 MiB hot table is not kept resident by any card while the dataset streams (g 0.80 to 0.87 in the added form). 5 October 2026</td><td class="iv">none yet</td></tr>
|
||||
<tr data-status="tested by the team"><td class="n">19</td><td class="claim">The lottery hash is sound as a hash: uniform output, deterministic, no out-of-bounds read, fuzzed; class v3 bit-exact on the three vendors<div class="where">Litepaper vs RandomX ("Every number above is measured and logged"), the numbers page</div></td><td><span class="st st-2">tested by the team</span></td><td class="mono">ca2-mixer 1ab8b21 (<code>tests/mixer.rs</code>, <code>tests/scratch.rs</code>), ca2-era 78c0ee4, ca2-soundness a465881 (<code>docs/analysis/scratch-soundness.md</code>), <code>igneum-pow/tests/packs.rs</code></td><td>The crate suite (53 + 4 + 19 + 7), the Metal fuzz, edge, stats and determinism runs on the v3 construction, the pack vectors and 2^24 fingerprints on Metal, Apple OpenCL, the RTX 5090 and the RX 9070 XT, the 1,024-hash CPU re-check per card</td><td>Class v3 (mixer x8 + era): 200-program fuzz 200 of 200 on Metal, every tenth on Apple OpenCL; the pinned v3 packs 3/3 + 3/3 and 96 of 96 lanes on Metal and Apple OpenCL; the six era packs' fingerprints equal on the three vendors (the three-card Windows rig (RTX 5090, RTX 4070, RX 9070 XT) job run-ca2-era-pc1-20261005, 5 October 2026); the v2 exports byte-identical on the v3 crate; the final-class PC rows and the G2 re-check: job run-ca2-era-pc1b-20261005 (pending at the time of writing)</td><td class="iv">none yet</td></tr>
|
||||
<tr data-status="implemented"><td class="n">20</td><td class="claim">No premine, no pre-sale, no allocation: every coin is minted by the schedule and every coin goes to the block producer (80%) and the proving pool (20%)<div class="where">Homepage stats and Economics tiles; litepaper Supply, Economics</div></td><td><span class="st st-1">implemented</span></td><td class="mono">repo <code>6ac80a3</code>; fork "igneum-node devnet v0"; <code>consensus/core/src/igneum.rs</code>, <code>coinbase.rs</code></td><td><code>cargo test -p kaspa-consensus-core igneum</code> (8 pass: subsidy table, ramp, split, cap) and <code>cargo test -p kaspa-consensus coinbase</code> (8 pass); <code>igneum-miner inspect 40</code>; bench-log "igneum-node devnet v0"</td><td>Coinbases on the devnet: 80/20 exact on 39 of 39 single-payee blocks, the 20% to the <code>igneum-proving-pool-v0</code> output; the per-second schedule sums to under the 4,000,000,000 cap by less than 100 coins; 3,168,808,781 units per DAA second in years 0 to 2, halving at 63,115,200 DAA s. 3 October 2026, Apple M5 Max. The devnet genesis carries no allocation; the mainnet genesis does not exist yet, so the claim is about the code and the stated rule, not a launch that has happened</td><td class="iv">none yet</td></tr>
|
||||
|
|
|
|||
|
|
@ -38,3 +38,8 @@ disclosure prize
|
|||
# the rig names never reach a served page (verification lane, 7 October 2026: a substring grep read "PC 2" inside gRPC port numbers; this is the word-bounded check)
|
||||
\bPC [12]\b
|
||||
counsel is engaged
|
||||
# 7 October 2026 (main's rule): the one outside check is never hinted at before its time; the phrase class, not the bare words (the proving pool's escrow and a staged build are ordinary)
|
||||
outside check
|
||||
waits on its escrow
|
||||
staged and waits
|
||||
the publish word
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Igneum ledger: every criticism, answered</title>
|
||||
<meta name="description" content="Every criticism Igneum expects, in the critic's words, with what was done, the status and the date. 188 entries. Nothing deleted, nothing softened.">
|
||||
<meta name="description" content="Every criticism Igneum expects, in the critic's words, with what was done, the status and the date. 189 entries. Nothing deleted, nothing softened.">
|
||||
<link rel="canonical" href="https://igneum.network/ledger">
|
||||
<meta name="theme-color" content="#0C0C0E">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:site_name" content="Igneum">
|
||||
<meta property="og:title" content="Igneum ledger: every criticism, answered">
|
||||
<meta property="og:description" content="188 criticisms in the critic's words, with what was done, the status and the date.">
|
||||
<meta property="og:description" content="189 criticisms in the critic's words, with what was done, the status and the date.">
|
||||
<meta property="og:url" content="https://igneum.network/ledger">
|
||||
<meta property="og:image" content="https://igneum.network/og-small.png?v=3">
|
||||
<meta property="og:image:width" content="256">
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
<meta property="og:image:alt" content="Igneum. Mined by GPUs. Proven by fire.">
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:title" content="Igneum ledger: every criticism, answered">
|
||||
<meta name="twitter:description" content="188 criticisms in the critic's words, with what was done, the status and the date.">
|
||||
<meta name="twitter:description" content="189 criticisms in the critic's words, with what was done, the status and the date.">
|
||||
<meta name="twitter:image" content="https://igneum.network/og-small.png?v=3">
|
||||
<meta name="twitter:image:alt" content="Igneum. Mined by GPUs. Proven by fire.">
|
||||
<link rel="icon" href="/favicon.ico" sizes="48x48">
|
||||
|
|
@ -216,9 +216,9 @@ blockquote{margin:10px 0;padding:10px 14px;border-left:3px solid var(--line-2);c
|
|||
<section class="page-hero">
|
||||
<div class="container">
|
||||
<div class="breadcrumb"><a href="/">Igneum</a><span>/</span><span>The ledger</span></div>
|
||||
<div class="page-heading"><div><div class="eyebrow"><span class="line"></span>Ledger · 188 entries · regenerated from the repository</div>
|
||||
<div class="page-heading"><div><div class="eyebrow"><span class="line"></span>Ledger · 189 entries · regenerated from the repository</div>
|
||||
<h1>Every criticism, answered<br><span class="accent">or conceded.</span></h1>
|
||||
<p class="lead">This is every criticism the project expects, in the critic's words, with what was done about it and the date. 188 entries since 3 October 2026. Entries are never deleted; a status that changes keeps its history on the line. Where the critic was right the entry says Conceded. Where nothing has been done it says Open and names what settles it. The founder mined through the GPU years. Ethereum's move to proof of stake in September 2022 ended that income and the miners' place in that chain. This is one person building, with AI systems doing the engineering, the coin he wanted to exist for miners: GPU-mined, the miners are the provers, no founder allocation, every cost stated. Help is welcome and a team is wanted: cryptographers, node engineers, miners who will test. This ledger is the application form: pick an open row and write to <a href="mailto:hello@igneum.network">hello@igneum.network</a> with its id.</p></div></div>
|
||||
<p class="lead">This is every criticism the project expects, in the critic's words, with what was done about it and the date. 189 entries since 3 October 2026. Entries are never deleted; a status that changes keeps its history on the line. Where the critic was right the entry says Conceded. Where nothing has been done it says Open and names what settles it. The founder mined through the GPU years. Ethereum's move to proof of stake in September 2022 ended that income and the miners' place in that chain. This is one person building, with AI systems doing the engineering, the coin he wanted to exist for miners: GPU-mined, the miners are the provers, no founder allocation, every cost stated. Help is welcome and a team is wanted: cryptographers, node engineers, miners who will test. This ledger is the application form: pick an open row and write to <a href="mailto:hello@igneum.network">hello@igneum.network</a> with its id.</p></div></div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section compact"><div class="container">
|
||||
|
|
@ -227,11 +227,11 @@ blockquote{margin:10px 0;padding:10px 14px;border-left:3px solid var(--line-2);c
|
|||
<tbody>
|
||||
<tr><td class="num">3</td><td><button type="button" class="chip" data-filter="Open">Open</button></td><td>Nothing has settled it yet. The entry names what will</td></tr>
|
||||
<tr><td class="num">64</td><td><button type="button" class="chip" data-filter="Conceded">Conceded</button></td><td>The critic is right. "Stated" means the public text says so; "not yet stated" means it does not yet</td></tr>
|
||||
<tr><td class="num">63</td><td><button type="button" class="chip" data-filter="Fixed or built">Fixed or built</button></td><td>A code, spec or text change answers it, with the commit or the page named</td></tr>
|
||||
<tr><td class="num">64</td><td><button type="button" class="chip" data-filter="Fixed or built">Fixed or built</button></td><td>A code, spec or text change answers it, with the commit or the page named</td></tr>
|
||||
<tr><td class="num">27</td><td><button type="button" class="chip" data-filter="Closed by rule or decided">Closed by rule or decided</button></td><td>A consensus rule or a decision by the owner answers it, dated</td></tr>
|
||||
<tr><td class="num">15</td><td><button type="button" class="chip" data-filter="Answered with evidence">Answered with evidence</button></td><td>A measurement or a simulation exists and is named</td></tr>
|
||||
<tr><td class="num">16</td><td><button type="button" class="chip" data-filter="Answered by design">Answered by design</button></td><td>A design rule answers it; no measurement is possible yet</td></tr>
|
||||
<tr><td class="num">188</td><td><button type="button" class="chip" data-filter="">All</button></td><td>Every entry. The sections: <a href="#ap">The in-house adversarial pass</a>, <a href="#m">Mining and chips</a>, <a href="#f">Finality and attacks</a>, <a href="#p">Proving and the zkEVM</a>, <a href="#e">Economics and the coin</a>, <a href="#g">Governance and the founders</a>, <a href="#c">Comparisons</a>, <a href="#l">Legal and regulatory</a>, <a href="#x">Launch and operations</a>, <a href="#d">Builders</a></td></tr>
|
||||
<tr><td class="num">189</td><td><button type="button" class="chip" data-filter="">All</button></td><td>Every entry. The sections: <a href="#ap">The in-house adversarial pass</a>, <a href="#m">Mining and chips</a>, <a href="#f">Finality and attacks</a>, <a href="#p">Proving and the zkEVM</a>, <a href="#e">Economics and the coin</a>, <a href="#g">Governance and the founders</a>, <a href="#c">Comparisons</a>, <a href="#l">Legal and regulatory</a>, <a href="#x">Launch and operations</a>, <a href="#d">Builders</a></td></tr>
|
||||
</tbody></table></div>
|
||||
<div class="toolbar"><input type="search" id="q" placeholder="Search the ledger" aria-label="Search the ledger"><span id="shown"></span></div>
|
||||
<h2 id="m">Mining and chips</h2>
|
||||
|
|
@ -1397,6 +1397,12 @@ blockquote{margin:10px 0;padding:10px 14px;border-left:3px solid var(--line-2);c
|
|||
<blockquote>The item histogram of class v4 over 2^26 nonces is not uniform: the top 0.1 percent of items take 0.520 percent of reads against 0.115 for a uniform control (4.05x), one item takes 78,479 reads (153x the mean), and site 15 feeds 6.37 percent of its reads into that top 0.1 percent in every iteration." (attack-pass row F8, <code>a repository file</code>, 7 October 2026)</blockquote>
|
||||
<div class="status"><span class="badge b-fixed-or-built">Fixed in part, finding bounded, stated</span> <span class="did">7 October 2026, night, the Counter ASIC lane's words): class v4 sub-version 3 (igneum-pow 017e7037, the audit-freeze tag) is frozen with the dataflow rule, the shared-operand rule, the 0.98 ratio and the total draw; the in-house pass's F8 re-gate reads 60 of 64 seeds under 1.2x with the four-seed tail accepted by the coordinator as the window model's unattributed residue (no chip consequence); the pass then attributed the class by value (eight live hot sets at 1.54x to 2.24x in the lowest 30 of 29,032 accepted programs, each about 1 MB of items at 0.3 percent of reads, 1.002x to a chip); class v5 (1c420786, frozen 21:53 UK) carries the fix as rule (c'''), the per-site distinct-index floor at 0.995 on the state flag (its census refuses 2.435 percent of accepted programs; seven of seven live hot sets refused at 0.9821 to 0.9919; the eighth's ratio owed tonight), with a named residual (three mild shadow-block-written concentrations at 0.9992 to 0.9997, about 1.0004x, a value-level test in the next class); the record is <code>a repository file</code> section 7c and the class v5 design's section 14. The eighth live hot set (seed 122960, id 4be7393ab6c84802, the deepest found: X_f +0.111 percent, 1.54x the window model, its hottest item at 475,616 reads from an all-ones source) reads minimum site 12 at 0.9824 at the acceptance's own 2^20 sample (live 0.9822), refused by class v5's (c''') floor at 0.995; so the floor refuses eight of eight live hot sets by X_f at or above f found in the tail of 88,051 accepted programs (minimum sites 0.9821 to 0.9919) against 0 hot sets in 20 random programs; what it misses stays the three mild shadow-block-written concentrations at 0.9992 to 0.9997 (Devnet 3's first program among them), about 1.0004x to a chip, the value-level test in the next class (22:41 local time; the logs under <code>a repository file/</code> on branch adv-accept; the v5 design's section 14). The RTX 5080 grid's knee is not in tonight; X37 keeps the 5080's stock premium only.</span></div>
|
||||
<details><summary>The answer as first written</summary><p>Correct as a fault, wrong as a null. The window layer (spec 01 1.13.1 as proposed, <code>a repository file</code> 1.4) moves the uniform null from 0.115 to 0.160 percent at the top 0.1 percent (1.39x, not 4.05x) and explains every per-site row of F8's attribution except site 15. Site 15's source r6 was last written by <code>or r6, r4</code> (instruction 61, the load at 63), a non-injective op whose output bits are 1 with probability 3/4, so the all-ones source recurs with probability (3/4)^32 per read; the era map sends it to item 0xca5b92, F8's hottest item exactly, and F8's next seven items are exactly the seven one-zero-bit sources whose zero survives the window mask. The popcount model at the measured bias (p = 0.7585) predicts 77,348 all-ones reads against 78,479, and the program's top-0.1-percent share at 0.58 against 0.52. The acceptance rule's part (a) takes any write as a fresh source and part (c) counts saturation on final register values only, so the class of fault passes it: of 1,024 chain-shaped class v4 programs 96.6 percent carry a load whose source's last writer is <code>or</code>, <code>mul</code> or <code>mulhi</code>, 48.5 percent an <code>or</code>-sourced one (0.30 percent of all reads per site), 4.9 percent an <code>or</code>-of-<code>or</code> chain (p3's class: 72 percent of that site's reads, 4.6 percent of all reads, on 0.1 percent of items). The ceiling under rule (c)'s 120-of-128 floor is one site repeating its item in all 8 iterations, 6.25 percent of reads, a chip edge of at most 1.067x in 64 bytes of SRAM; the public claim's 2x margin stands, and the public line says "bounded", not "uniform" (<code>a repository file</code> sections 1 to 4).</p></details>
|
||||
</article>
|
||||
<article class="entry" id="AP-F8-4" data-bucket="Fixed or built">
|
||||
<div class="head"><span class="id">AP-F8-4</span><h3>The program id's derivation text omitted generator 4's sub-version suffix (interoperability, documentation; no object change)</h3><span class="date">7 October 2026</span></div>
|
||||
<blockquote></blockquote>
|
||||
<div class="status"><span class="badge b-fixed-or-built">Fixed</span> <span class="did">7 October 2026, night): the id and its derivation text come from one byte recipe, every pinned pack's id re-derives from its own text in the suite (the igneum-pow suite on box 2 green at 0f45c8be: 64 + 2 + 7 + 4 + 19 + 2 + 7), the spec states the suffix; no object moved.</span></div>
|
||||
|
||||
</article>
|
||||
<article class="entry" id="AP-F8-5" data-bucket="Conceded">
|
||||
<div class="head"><span class="id">AP-F8-5</span><h3>The public specification did not describe the shipped acceptance rule (documentary; no object change)</h3><span class="date">7 October 2026</span></div>
|
||||
|
|
|
|||
|
|
@ -449,7 +449,7 @@ body.all .pager{display:none}
|
|||
<tr><td>When a stored-dataset chip pays for itself</td><td>at about USD 100 M of market cap in the first two years, not before</td><td>modelled, 7 October 2026</td></tr>
|
||||
<tr><td>The baseline the work started from: the same chip under class v3, without the shadow (the Ethash class)</td><td>5x to 9x (5.1x on GDDR7, 9.2x on eight HBM3 stacks; the Ethash chips of this class reached 2.1x to 4.8x)</td><td>modelled, 6 October 2026; the precedent measured by others, 2020 to 2022; never the launch state</td></tr>
|
||||
</tbody></table></div>
|
||||
<p>What a miner sees from this. Class v4 costs a 5090 145 W more unlocked, 88 W more at a 1,400 MHz core lock and 82 W at the best operating points (class v4 at 1,200 MHz, class v3 at 1,300; the knee is 1,300 MHz on both), for 0.2 percent more rate (measured, 7 October 2026; the 80 W read on 6 October was at the app's tuned cap); an M5 Max 16 W more for 1.5 percent, an RX 9070 XT and an RTX 4070 nothing (measured, 6 October 2026). The ladder that sets how much work rides in the shadow starts at rung 0 at genesis and climbs by miner signal; its third rung is inadmissible today because a server core verifies it in 10.85 ms, over the gate (measured, 7 October 2026). Devnet 3 runs class v4 from its first block (7 October 2026); the first devnet started on class v3 and reaches class v4 by miner signal at a published height. The next test of the model is an internal adversarial pass, not an independent review: three lanes that have never worked on the hash code attack the mixer, the chained cache and the acceptance rule with only what an outsider has (the public kit, the frozen object, the spec, the harnesses) and publish the break or the bound they reach. The one outside check is staged and waits on its escrow and the publish word.</p>
|
||||
<p>What a miner sees from this. Class v4 costs a 5090 145 W more unlocked, 88 W more at a 1,400 MHz core lock and 82 W at the best operating points (class v4 at 1,200 MHz, class v3 at 1,300; the knee is 1,300 MHz on both), for 0.2 percent more rate (measured, 7 October 2026; the 80 W read on 6 October was at the app's tuned cap); an M5 Max 16 W more for 1.5 percent, an RX 9070 XT and an RTX 4070 nothing (measured, 6 October 2026). The ladder that sets how much work rides in the shadow starts at rung 0 at genesis and climbs by miner signal; its third rung is inadmissible today because a server core verifies it in 10.85 ms, over the gate (measured, 7 October 2026). Devnet 3 runs class v4 from its first block (7 October 2026); the first devnet started on class v3 and reaches class v4 by miner signal at a published height. The next test of the model is an internal adversarial pass, not an independent review: three lanes that have never worked on the hash code attack the mixer, the chained cache and the acceptance rule with only what an outsider has (the public kit, the frozen object, the spec, the harnesses) and publish the break or the bound they reach. No outside review has run yet.</p>
|
||||
<p>No hash has stayed free of chips forever. Igneum does not claim to. It states the gain its own model finds, the response takes a week, and both are measured. The model is public: <a href="/bench#counter-asic-2-0-the-numbers">the numbers</a>; the claim is tested by the in-house adversarial pass and the public benchmark. Monero has run on RandomX since 2019 (approximate) with no chip shipped. Bitmain opened Antminer X9 pre-orders on 26 December 2025 for July 2026 delivery, then withdrew the product in mid-May 2026 and refunded buyers before any unit shipped; none has been independently benchmarked. A box with about a 2x per joule edge over the best CPUs, and about 3x over a desktop, was withdrawn rather than face a RandomX re-tune of 1.5x or more. That is the band Igneum’s class v4 model sits in (2.1x to 3.4x over an RTX 5090), and the defence that held was a maintained algorithm with a credible upgrade path, which is what the ladder is.</p>
|
||||
<p>One thing takes a person, here and on every chain that exists: writing new code. A chain cannot safely write its own generator, and it cannot safely tell a chip from a wave of honest new cards by hashrate alone. If the design above ever failed, anyone could publish a new generator and miners would switch it on by signalling, as Monero's community can fork. Igneum is built to make that day unlikely, and does not depend on avoiding it.</p>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"_about": "Rows of the public bench table at /miners (site/build.mjs). One row per card, generator version and miner version: the best measured rate. Later jobs append rows. Fields: card, generator (v1 | v2), mh_s (best measured MH/s), mh_per_w (MH per watt, null when the power was not measured), miner (the miner or package version), date (YYYY-MM-DD), source (the docs/bench-log.md heading, or the job id), by ('measured by the team' | 'reported by the fleet'), note (short, optional), watts (board power at the rate, null when not read), v4_cost (what the class v4 shadow costs this card against the class v3 control: watts and rate, measured; 'not measured' when not), tuned (the card's tune state at the row: 'full Ember Tune: <point>' | 'clock lock <MHz>, cap <W>' | 'stock, bench only' | 'stock, mining'), driver_os (driver and OS where known). No machine names, no addresses, no owner names: the build scrubs the page and fails on any that survive. hive (the copy-in HiveOS flight-sheet values at the row's tune point: core MHz (a lock), mem MHz, pl W; each labelled measured with the date, or 'stock' where no tune point exists).",
|
||||
"_about": "Rows of the public bench table at /miners (site/build.mjs). One row per card, generator version and miner version: the best measured rate. Later jobs append rows. Fields: card, generator (v1 | v2), mh_s (best measured MH/s), mh_per_w (MH per watt, null when the power was not measured), miner (the miner or package version), date (YYYY-MM-DD), source (the docs/bench-log.md heading, or the job id), by ('measured by the team' | 'reported by the fleet'), note (short, optional), watts (board power at the rate, null when not read), v4_cost (what the class v4 shadow costs this card against the class v3 control: watts and rate, measured; 'not measured' when not), tuned (the card's tune state at the row: 'full Ember Tune: <point>' | 'clock lock <MHz>, cap <W>' | 'stock, bench only' | 'stock, mining'), driver_os (driver and OS where known). No machine names, no addresses, no owner names: the build scrubs the page and fails on any that survive. hive (the copy-in HiveOS flight-sheet values at the row's tune point: core MHz (a lock), mem MHz, pl W; each labelled measured with the date, or 'stock' where no tune point exists). group ('buy' | 'datacentre'); watt_basis ('chip' when the watts are the chip's, not the wall's); v4_short (the one-clause class v4 figure for the column).",
|
||||
"rows": [
|
||||
{
|
||||
"card": "NVIDIA RTX 5090 (32 GB)",
|
||||
|
|
@ -20,7 +20,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 5090 (32 GB)",
|
||||
|
|
@ -41,7 +42,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 5090 (32 GB)",
|
||||
|
|
@ -62,7 +64,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "Apple M5 Max (40 GPU cores, Metal)",
|
||||
|
|
@ -83,7 +86,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "Apple M5 Max (40 GPU cores, Metal)",
|
||||
|
|
@ -104,7 +108,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "Apple silicon laptop (model not reported)",
|
||||
|
|
@ -125,7 +130,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 5060 Ti (16 GB)",
|
||||
|
|
@ -146,41 +152,21 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 5090 (32 GB)",
|
||||
"generator": "v2",
|
||||
"mh_s": 136.1,
|
||||
"watts": 350,
|
||||
"mh_per_w": 0.389,
|
||||
"miner": "igneum-worker-cuda bench, class v3 program (mx8-devnet-epoch0)",
|
||||
"date": "2026-10-06",
|
||||
"source": "bench log: 6 October 2026, Counter ASIC 3.0 item 8, the 5090 rows (the control row)",
|
||||
"by": "measured by the team",
|
||||
"note": "the control, unlocked; the 6 October +80 W reading was at the app's tuned cap; the rate is memory-bound from 2,850 to 1,400 MHz (136.8 to 135.0 MH/s), the best MH per watt at the lowest lock on the grid, so the knee is below 1,400 MHz (the second pass runs to the driver's floor)",
|
||||
"v4_cost": "+145.3 W at the unlocked core (475.5 against 330.2 W) for +0.18 percent of rate; +88.3 W at the 1,400 MHz lock (316.3 against 228.0 W) for +0.23 percent; measured 7 October 2026 (the class v4 efficiency pass on the team's Windows desk machine, 60 s steps, every fingerprint matched)",
|
||||
"tuned": "stock, bench only (unlocked core)",
|
||||
"driver_os": "NVIDIA driver, Windows 11",
|
||||
"hive": {
|
||||
"core_mhz": null,
|
||||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 5090 (32 GB)",
|
||||
"generator": "v2",
|
||||
"mh_s": 127.71,
|
||||
"watts": 226.8,
|
||||
"mh_per_w": 0.563,
|
||||
"miner": "Igneum Miner 0.3.13 + Ember Tune kit 6 (mining, class v3 program)",
|
||||
"date": "2026-10-06",
|
||||
"source": "bench log: 6 October 2026, 16:01Z, Ember run 6 (ember-tune-pc1-6)",
|
||||
"source": "bench log: 6 October 2026, Ember run 6; Counter ASIC 3.0 status: item 8's 5090 rows and the class v4 efficiency pass (7 October 2026)",
|
||||
"by": "measured by the team",
|
||||
"note": "against 127.9 MH/s at 311.0 W untuned (0.411 MH/W): 84 W saved for 0.15 percent of rate; the ladder's floor, not yet its optimum",
|
||||
"v4_cost": "not measured at this tune point (the Ember run was on the class v3 program); the unlocked and locked rows above carry the measured premium",
|
||||
"note": "one row for the card: best rate 136.1 MH/s at 350 W stock unlocked on the class v3 control (bench, 6 October 2026); best MH per wall watt 127.71 MH/s at 226.8 W at the full Ember Tune point, 1,854 MHz (Ember run 6, 6 October 2026); the class v4 efficiency pass (7 October 2026, 60 s steps, every fingerprint matched): unlocked 136.84 MH/s at 475.5 W (0.288), the 1,400 MHz lock 134.98 at 316.3 W (0.427), the knee 1,300 MHz, the best point 1,200 MHz 133.80 at 305.1 W (0.439); the fleet's standing 5090 reads 100.6 MH/s at 308 W beside its prover (reported by the fleet)",
|
||||
"v4_cost": "class v4 at the 1,200 MHz knee: 133.80 MH/s at 305.1 W (0.439 MH/W); the premium over class v3 81.8 W at the best points and 145.3 W unlocked, measured 7 October 2026",
|
||||
"tuned": "full Ember Tune: 1,854 MHz core lock at the 100 percent cap",
|
||||
"driver_os": "NVIDIA driver, Windows 11",
|
||||
"hive": {
|
||||
|
|
@ -188,7 +174,11 @@
|
|||
"mem_mhz": 13801,
|
||||
"pl_w": 460,
|
||||
"label": "measured 6 October 2026 (Ember run 6: the clock lock 1,854 MHz, the memory clock as read, the limit 460 W of 575 as the cap did not bind)"
|
||||
}
|
||||
},
|
||||
"v4_short": "133.8 MH/s, 305 W, 0.439",
|
||||
"group": "buy",
|
||||
"mh_s_display": "136.1 stock (127.7 tuned)",
|
||||
"watts_display": "226.8 tuned (350 stock)"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 4070 (12 GB)",
|
||||
|
|
@ -209,7 +199,8 @@
|
|||
"mem_mhz": 10251,
|
||||
"pl_w": 100,
|
||||
"label": "measured 6 October 2026 (Ember run 6: 1,863 MHz at the 50 percent cap of 200 W, 75.6 W drawn; the item 8 rows at 1,860 MHz and 160 W)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "AMD Radeon RX 9070 XT (16 GB)",
|
||||
|
|
@ -230,7 +221,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "Apple M5 Max (40 GPU cores, Metal)",
|
||||
|
|
@ -242,7 +234,7 @@
|
|||
"date": "2026-10-06",
|
||||
"source": "Counter ASIC 3.0 status: item 8, the Mac rows (IOReport GPU and DRAM watts)",
|
||||
"by": "measured by the team",
|
||||
"note": "GPU plus DRAM watts, not wall",
|
||||
"note": "GPU plus DRAM watts from IOReport, not wall power, so its MH per watt is not ranked against the cards' wall figures",
|
||||
"v4_cost": "+16 W for 1.5 percent of rate at 102,100 ops per hash, measured 6 October 2026",
|
||||
"tuned": "no lever on Apple silicon (no clock or power control exposed); stock",
|
||||
"driver_os": "macOS, Metal",
|
||||
|
|
@ -251,7 +243,9 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"watt_basis": "chip",
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "Intel Arc B580 (12 GB)",
|
||||
|
|
@ -272,7 +266,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA H100 SXM (80 GB)",
|
||||
|
|
@ -293,7 +288,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "datacentre"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 5080 (16 GB)",
|
||||
|
|
@ -314,7 +310,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 3070 Ti (8 GB)",
|
||||
|
|
@ -335,7 +332,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 3070 (8 GB)",
|
||||
|
|
@ -356,7 +354,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 3080 (10 GB)",
|
||||
|
|
@ -377,7 +376,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 3080 Ti (12 GB)",
|
||||
|
|
@ -398,7 +398,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 3090 (24 GB)",
|
||||
|
|
@ -419,7 +420,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 3090 Ti (24 GB)",
|
||||
|
|
@ -440,7 +442,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 4090 (24 GB)",
|
||||
|
|
@ -461,28 +464,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 5090 (32 GB), fleet",
|
||||
"generator": "v2",
|
||||
"mh_s": 100.6,
|
||||
"watts": 308,
|
||||
"mh_per_w": 0.327,
|
||||
"miner": "hive package 0.3.20 igneum-miner, class v4 program",
|
||||
"date": "2026-10-07",
|
||||
"source": "the fleet's standing voter (status line, 7 October 2026)",
|
||||
"by": "reported by the fleet",
|
||||
"note": "a standing voter's status beside its prover, 18:00Z; 122 MH/s at 308 W earlier in the day (0.396 MH/W); the team's own 5090 rows above",
|
||||
"v4_cost": "not measured (class v4 program only)",
|
||||
"tuned": "stock, mining",
|
||||
"driver_os": "NVIDIA driver, Ubuntu 24.04",
|
||||
"hive": {
|
||||
"core_mhz": null,
|
||||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 3060 (12 GB)",
|
||||
|
|
@ -503,7 +486,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 3060 Ti (8 GB)",
|
||||
|
|
@ -524,7 +508,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 4060 Ti (8 GB)",
|
||||
|
|
@ -545,7 +530,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 4070 Ti (12 GB)",
|
||||
|
|
@ -566,7 +552,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 5060 (8 GB)",
|
||||
|
|
@ -587,7 +574,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 5070 (12 GB)",
|
||||
|
|
@ -608,7 +596,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 5070 Ti (16 GB)",
|
||||
|
|
@ -629,7 +618,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "buy"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX A5000 (24 GB)",
|
||||
|
|
@ -650,7 +640,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "datacentre"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA L40S (48 GB)",
|
||||
|
|
@ -671,7 +662,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "datacentre"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA A100 PCIe (80 GB)",
|
||||
|
|
@ -692,7 +684,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "datacentre"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA A100 SXM (80 GB)",
|
||||
|
|
@ -713,7 +706,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "datacentre"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA H200 SXM (141 GB)",
|
||||
|
|
@ -734,7 +728,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "datacentre"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA B200 (180 GB)",
|
||||
|
|
@ -755,7 +750,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
"group": "datacentre"
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX PRO 6000 Blackwell (96 GB)",
|
||||
|
|
@ -776,28 +772,8 @@
|
|||
"mem_mhz": null,
|
||||
"pl_w": null,
|
||||
"label": "stock (no measured tune point; the 5080 and 9070 XT passes and the class v4 efficiency pass land theirs when read)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"card": "NVIDIA RTX 5090 (32 GB)",
|
||||
"generator": "v2",
|
||||
"mh_s": 134.98,
|
||||
"watts": 316.3,
|
||||
"mh_per_w": 0.427,
|
||||
"miner": "igneum-worker-cuda bench (installed worker 0.3.20), class v4 program v4-devnet-epoch0",
|
||||
"date": "2026-10-07",
|
||||
"source": "Counter ASIC 3.0 status: the class v4 efficiency pass (the efficiency pass job of 7 October 2026, 18:40 to 19:16 UTC, on the team's Windows desk machine, the core locked through the installed app's Power Helper task, no prompt)",
|
||||
"by": "measured by the team",
|
||||
"note": "the v3 control at the same lock 134.68 MH/s at 228.0 W (0.591 MH/W); recovered 159.2 W for 1.36 percent of rate against the unlocked class v4 point; the best MH per watt on the grid, so the knee is below 1,400 MHz",
|
||||
"v4_cost": "+88.3 W at this lock for +0.23 percent of rate, measured 7 October 2026",
|
||||
"tuned": "core lock 1,400 MHz (the efficiency pass's grid floor), memory 13,801 MHz, the driver's power limit untouched",
|
||||
"driver_os": "NVIDIA driver 617.14, Windows 11",
|
||||
"hive": {
|
||||
"core_mhz": 1400,
|
||||
"mem_mhz": 13801,
|
||||
"pl_w": 575,
|
||||
"label": "measured 7 October 2026 (the class v4 efficiency pass: the 1,400 MHz lock, the memory clock as read, the limit as the driver's default since the lock alone set the draw; the knee below 1,400 is the second pass's)"
|
||||
}
|
||||
},
|
||||
"group": "datacentre"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
149
tools/ca3-v4-amend/pc1-amd-family-20261007.ps1
Normal file
149
tools/ca3-v4-amend/pc1-amd-family-20261007.ps1
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
# 7 October 2026 copy for the PC 1 queue: tools/ca3-pc1-amd/pc1-amd-family.ps1 (run e) unchanged except the kit id, since a machine
|
||||
# runs a fetch id once and the 0.3.21 host build wipes the jobs folder: fetch-ca3-pc1-amd-family-20261007 (the probe rebuilt on
|
||||
# build-1 from this tree's proto-opencl/family-probe.c).
|
||||
# Counter ASIC 3.0, PC 1 AMD job 2 (6 October 2026, run e: the 9070 XT ALONE): item 6's step cost of every reserve candidate family of spec
|
||||
# 1.13.2 on PC 1's RX 9070 XT (machine ae432dc7, gfx1201), docs/plans/counter-asic-3-reserve.md, status file section
|
||||
# 3 "Item 6" (the OWED AMD column). Run a (run-ca3-pc1-amd-family-20261006, 150 s, exit 0) measured ordinal 0, PC 1's
|
||||
# integrated gfx1036 (one CU: alu 40.59 G steps/s against the 9070 XT's expected 1,000 to 2,000): the device list parse
|
||||
# ran a second -match after the capturing one, which overwrote $Matches, so every device read idx 0 and an empty name,
|
||||
# and the probe took a bare ordinal. Run b: the probe picks the card BY NAME (`--device-name gfx1201`, the match on the
|
||||
# newest AMD platform by driver version, the kit worker's dedup rule; `RESULT device_choice name= index= platform= driver=
|
||||
# cus=`), prints the device's name, CUs and platform on every RESULT line, and refuses with `RESULT error` when no
|
||||
# gfx1201 is listed; the older-platform duplicate and the gfx1036 run after it by explicit ordinal, as their own labelled
|
||||
# columns. Run d (17:17Z, the card by name, every variant built) showed a dependent-chain ratio does NOT survive the
|
||||
# card's own miner: the alu chain read 226 then 195 G steps/s and the ratios swung from 5.9 to 11.8x to 0.17 to 1.3x
|
||||
# between runs (the loaded card's scheduler, not the ops), while the Mac and 5090 columns were taken with the card alone.
|
||||
# Run e: the 9070 XT is switched OFF by the RUNNER (`--cards-off amd:gfx1201` at publish, the app's own card path, put
|
||||
# back on ANY exit with its own enabled flag, identities and cap: rule of 6 October 2026, a script never posts to
|
||||
# /api/cards) for its three runs; this script only confirms by the process list that the card's opencl worker is gone;
|
||||
# the 5090 and the 4070 keep mining (NOT --stop-miners). The gfx1036 and the old-platform columns run
|
||||
# AFTER the restore, beside the miners, as they are not the card under test. mm8 stays exact=unverified: the gfx12 WMMA
|
||||
# iu8 16x16x16 fragment layout is in no source at hand, and a guessed CPU layout would turn a wrong guess into exact=no. The probe is the family kit's
|
||||
# family-probe-cl.exe (proto-opencl/family-probe.c of branch ca3-pc1-amd, cross-compiled with mingw, OpenCL.dll at run
|
||||
# time; the Mac ran the same source bit-exact on Apple OpenCL), three runs per device, each best of 3 with a fresh seed
|
||||
# per repetition, device event time, bit-exact against the CPU reference on two whole 32-lane groups. Every family is
|
||||
# tried through every form AMD's OpenCL C offers; a form that does not compile prints build=failed with the first line
|
||||
# of the build log and the run goes on (run a's finding: AMD's OpenCL C compiles no amd_perm, no sudot4, no WMMA
|
||||
# builtin; ds_bpermute does compile). Never quits, pauses, resumes or updates the installed app; never writes
|
||||
# settings.json. Lines: the probe's own `RESULT FAMILY name= ms= gsteps= ratio= exact= path= variant= ... device= cus=
|
||||
# platform=` and `RESULT FAMILYBEST ...`, each prefixed `RESULT run=<n> dev=<label>`; a `SUMMARY {json}` line at the end.
|
||||
# Read back with `node tools/jobs.mjs <job id>`.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-amd-family'
|
||||
$kitId = 'fetch-ca3-pc1-amd-family-20261007'
|
||||
$started = Get-Date
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) {
|
||||
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
|
||||
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||
}
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
|
||||
$jobs = Split-Path $env:IGNEUM_JOB_DIR
|
||||
$kit = Join-Path $jobs $kitId
|
||||
if (-not (Test-Path $kit)) { "RESULT error kit missing at $kit (the fetch job $kitId runs first; republish it after any app update)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
|
||||
$probe = Join-Path $kit 'bin\family-probe-cl.exe'
|
||||
$src = Join-Path $kit 'src\family-probe.c'
|
||||
if (-not (Test-Path $probe)) { "RESULT error probe missing at $probe"; Summary 'failed' @{ error = 'probe missing' }; exit 2 }
|
||||
"RESULT probe $probe sha256 $((Get-FileHash -Algorithm SHA256 $probe).Hash.ToLower()) bytes $((Get-Item $probe).Length)"
|
||||
if (Test-Path $src) { "RESULT source family-probe.c sha256 $((Get-FileHash -Algorithm SHA256 $src).Hash.ToLower()) bytes $((Get-Item $src).Length)" }
|
||||
|
||||
# what the card carries during the run (read only; nothing is switched)
|
||||
function Workers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe' OR Name = 'igneum-worker-cuda.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Name):$($_.ProcessId):[$($_.CommandLine -replace '\s+', ' ')]" }) }
|
||||
$w = @(Workers)
|
||||
$amdLoaded = ($w | Where-Object { $_ -match 'igneum-worker-opencl' }).Count -gt 0
|
||||
$cardState = if ($amdLoaded) { 'loaded' } else { 'quiet' }
|
||||
"RESULT workers_before $(Stamp) $($w -join ' ')"
|
||||
"RESULT context card_state=$(if ($amdLoaded) { 'LOADED (an igneum-worker-opencl process mines on an AMD card: absolutes are shared-card figures, ratios stand)' } else { 'quiet (no igneum-worker-opencl process)' })"
|
||||
|
||||
# ---- the installed app and the card's settings.json entries (READ ONLY), for the card-alone runs ----
|
||||
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
|
||||
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
|
||||
$urlFile = Join-Path $appDir 'app.url'
|
||||
$base = $null
|
||||
if (Test-Path $urlFile) { $base = (Get-Content -LiteralPath $urlFile -Raw).Trim().TrimEnd('/') }
|
||||
"RESULT app install=$inst app_dir=$appDir url_present=$([bool]$base)"
|
||||
# the 9070 XT's OpenCL device index as the APP numbers it (the installed worker's --list; the probe's own list below
|
||||
# numbers devices differently), for the process-list check of its worker
|
||||
$dev = $null
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) {
|
||||
$wl = @(& (Join-Path $inst 'igneum-worker-opencl.exe') --list 2>&1 | ForEach-Object { "$_" })
|
||||
foreach ($l in $wl) { if ($l -match '^\s*\[(\d+)\].*gfx1201' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } }
|
||||
}
|
||||
"RESULT app_device $(if ($null -ne $dev) { $dev } else { 'unknown (no installed worker list: the quiet check falls back to any opencl worker)' })"
|
||||
# the card is switched off by the RUNNER (`--cards-off amd:gfx1201` at publish), never by this script (rule of 6 October
|
||||
# 2026, tools/ci/playbook-quit-check.sh rule 2); below only the read-only confirmation that its worker is gone
|
||||
function CardWorkers {
|
||||
$procs = @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue)
|
||||
if ($null -ne $dev) { $procs = @($procs | Where-Object { $_.CommandLine -match "--device\s+$dev(\s|$)" }) }
|
||||
@($procs | ForEach-Object { $_.ProcessId })
|
||||
}
|
||||
|
||||
# the probe's own device list: the 9070 XT by name first (the probe picks the newest platform itself), then the
|
||||
# older-platform duplicate and the gfx1036 by explicit ordinal as their own columns
|
||||
$list = @(& $probe --list 2>&1 | ForEach-Object { "$_" })
|
||||
$list | ForEach-Object { "RESULT list $_" }
|
||||
$extra = @()
|
||||
$gfx1201Count = 0; $newest = ''
|
||||
foreach ($l in $list) {
|
||||
if ($l -match '^\[(\d+)\] (.*?) \| (.*?) \| driver (\S+)') {
|
||||
$idx = [int] $Matches[1]; $name = $Matches[2]; $plat = $Matches[3]; $drv = $Matches[4]
|
||||
if ($name -match 'gfx1201') { $gfx1201Count++; if ($drv -gt $newest) { $newest = $drv } }
|
||||
if ($name -match 'gfx1201|gfx1036|Radeon' -and $name -notmatch 'NVIDIA') { $extra += [pscustomobject]@{ idx = $idx; name = $name; platform = $plat; driver = $drv } }
|
||||
}
|
||||
}
|
||||
if ($gfx1201Count -eq 0) { "RESULT error no gfx1201 in the probe's list (the eGPU is off the bus: the 9070 XT column stays OWED); listed AMD devices: $(($extra | ForEach-Object { "[$($_.idx)] $($_.name) on $($_.platform) driver $($_.driver)" }) -join ' ; ')"; Summary 'failed' @{ error = 'no gfx1201' }; exit 2 }
|
||||
# the by-ordinal runs after the by-name one: the gfx1201 entries NOT on the newest driver (the older platform), then the rest
|
||||
$byOrdinal = @($extra | Where-Object { -not ($_.name -match 'gfx1201' -and $_.driver -eq $newest) })
|
||||
"RESULT devices by_name=gfx1201 (newest driver $newest, $gfx1201Count gfx1201 entries) then by_ordinal=[$(($byOrdinal | ForEach-Object { "[$($_.idx)] $($_.name) on $($_.platform) driver $($_.driver)" }) -join ' ; ')]"
|
||||
|
||||
$runs = 0; $exactRows = 0; $rowsTotal = 0; $built = 0; $failedBuilds = 0; $probeErrors = 0; $mainRows = 0
|
||||
function RunProbe([string] $label, [string[]] $probeArgs, [int] $r) {
|
||||
# never name a parameter after the automatic variable for unbound arguments: a splat of it is then empty (run b, 17:10Z)
|
||||
"RESULT run=$r dev=$label start $(Stamp) args=[$($probeArgs -join ' ')] card_state=$cardState"
|
||||
$t0 = Get-Date
|
||||
$lines = @(& $probe @probeArgs --reps 3 2>&1 | ForEach-Object { "$_" })
|
||||
$rc = $LASTEXITCODE
|
||||
foreach ($l in $lines) {
|
||||
if ($l -match '^RESULT ') { "RESULT run=$r dev=$label $($l.Substring(7))" } else { "RESULT run=$r dev=$label text $l" }
|
||||
if ($l -match '^RESULT FAMILY name=\S+ ms=') { $script:rowsTotal++; if ($l -match ' exact=yes ') { $script:exactRows++ }; if ($label -eq '9070xt') { $script:mainRows++ } }
|
||||
if ($l -match '^RESULT FAMILY .*build=failed') { $script:failedBuilds++ }
|
||||
if ($l -match '^RESULT FAMILYBEST name=\S+ ms=') { $script:built++ }
|
||||
if ($l -match '^RESULT error') { $script:probeErrors++ }
|
||||
}
|
||||
"RESULT run=$r dev=$label end $(Stamp) exit=$rc wall_s=$([int]((Get-Date) - $t0).TotalSeconds)"
|
||||
if ($rc -ne 0) { "RESULT run=$r dev=$label error=probe exit $rc" }
|
||||
$script:runs++
|
||||
}
|
||||
# ---- the 9070 XT alone: off in the app, confirmed quiet, three runs, restored whatever happens ----
|
||||
$cardAlone = $false
|
||||
try {
|
||||
$before = @(CardWorkers)
|
||||
"RESULT card_workers_before pids=[$($before -join ' ')]"
|
||||
if ($before.Count -eq 0) { "RESULT card already quiet: no opencl worker for the card (the runner's --cards-off took)"; $cardAlone = $true }
|
||||
else {
|
||||
$t = 0
|
||||
while ($t -lt 150) { Start-Sleep -Seconds 5; $t += 5; if ((@(CardWorkers)).Count -eq 0) { break } }
|
||||
$left = @(CardWorkers)
|
||||
if ($left.Count -eq 0) { "RESULT card $(Stamp) quiet after $t s: the card's igneum-worker-opencl process is gone (the card to itself)"; $cardAlone = $true }
|
||||
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: opencl worker pids [$($left -join ' ')] still run (was the job published with --cards-off amd:gfx1201?); the 9070 XT rows below are LOADED-card figures (run d's class)" }
|
||||
Start-Sleep -Seconds 8
|
||||
}
|
||||
$cardState = if ($cardAlone) { 'alone' } else { 'loaded' }
|
||||
"RESULT workers_during $(Stamp) $((Workers) -join ' ')"
|
||||
for ($r = 1; $r -le 3; $r++) { RunProbe '9070xt' @('--device-name', 'gfx1201') $r; Start-Sleep -Seconds 2 }
|
||||
} finally {
|
||||
"RESULT runs_9070xt_done $(Stamp) (the card comes back through the RUNNER's restore on any exit, not here)"
|
||||
}
|
||||
# ---- the other AMD columns beside the miners (not the card under test) ----
|
||||
$cardState = if ((@(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue)).Count -gt 0) { 'loaded' } else { 'quiet' }
|
||||
foreach ($d in $byOrdinal) {
|
||||
$label = if ($d.name -match 'gfx1201') { 'gfx1201-old-platform' } elseif ($d.name -match 'gfx1036') { 'gfx1036' } else { 'amd-' + $d.idx }
|
||||
for ($r = 1; $r -le 3; $r++) { RunProbe $label @('--device', "$($d.idx)") $r; Start-Sleep -Seconds 2 }
|
||||
}
|
||||
"RESULT workers_after $(Stamp) $((Workers) -join ' ')"
|
||||
"RESULT end $(Stamp)"
|
||||
$status = if ($mainRows -gt 0) { 'done' } elseif ($rowsTotal -gt 0) { 'partial' } else { 'failed' }
|
||||
Summary $status @{ runs = $runs; family_rows = $rowsTotal; rows_9070xt = $mainRows; card_alone_9070xt = $cardAlone; exact_rows = $exactRows; failed_builds = $failedBuilds; familybest_rows = $built; probe_errors = $probeErrors }
|
||||
if ($status -eq 'failed') { exit 1 }
|
||||
exit 0
|
||||
79
tools/ca3-v4-amend/pc1-ca4-microbench.ps1
Normal file
79
tools/ca3-v4-amend/pc1-ca4-microbench.ps1
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# Counter ASIC 4 research (7 October 2026): the per-block micro-benchmark of the research lane (ad6a2bd47d4a46105) on PC 1's RTX 5090,
|
||||
# the card alone through the RUNNER's --cards-off, no prompt. The exe is the research lane's scratch worker with --microbench
|
||||
# (igneum-worker-cuda-ca4mb.exe, sha256 49aa60c4b28ed77a54655476d0f07c06a54aa22e4f6f6ccadd4019b84cbf0268; its own fetch job
|
||||
# fetch-ca4-microbench-exe-20261007). Twice: at the unlocked clock, and at the 1,300 MHz knee lock when the Power Helper task
|
||||
# answers (the lock path of pc1-v4-efficiency.ps1: the task started by the owning user, `<seq> dev <idx>` + `<seq> lgc 1300` through
|
||||
# cmd.txt, the base sequence from cmd.txt and helper.log, rgc in finally; a silent helper skips the locked state and says so). Per
|
||||
# state: `--microbench --mb-seconds 60` (20 probes, about 22 min) with nvidia-smi at 1 Hz; every `RESULT microbench probe=...` line
|
||||
# of the worker is kept verbatim and a `RESULT ca4-research probe=<name> state=<unlocked|lock1300> watts=<mean> watts_instant= watts_average=
|
||||
# sm_mhz= mem_mhz= G_ops_s= checksum= status=` row is derived from the probe's start_utc..end_utc window (the mean from 8 s in).
|
||||
# Compile failures and skips print as the worker prints them. Never quits, pauses, resumes or updates the installed app.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-ca4-microbench'
|
||||
$started = Get-Date
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) { $o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }; foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }; 'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4) }
|
||||
$jobDir = $env:IGNEUM_JOB_DIR; if (-not $jobDir) { $jobDir = Join-Path $env:TEMP 'igneum-ca4-mb' }
|
||||
New-Item -ItemType Directory -Force -Path $jobDir | Out-Null
|
||||
$jobs = Split-Path $jobDir
|
||||
$exe = Join-Path (Join-Path $jobs 'fetch-ca4-microbench-exe-20261007') 'igneum-worker-cuda-ca4mb.exe'
|
||||
if (-not (Test-Path $exe)) { "RESULT error exe missing at $exe (the fetch job fetch-ca4-microbench-exe-20261007 runs first)"; Summary 'failed' @{ error = 'exe missing' }; exit 2 }
|
||||
"RESULT start $(Stamp) job=$jobName exe=$exe sha256=$((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) label=ca4-research"
|
||||
$smi = (Get-Command nvidia-smi -ErrorAction SilentlyContinue).Source
|
||||
if (-not $smi) { foreach ($p in @((Join-Path $env:ProgramFiles 'NVIDIA Corporation\NVSMI\nvidia-smi.exe'), (Join-Path $env:SystemRoot 'System32\nvidia-smi.exe'))) { if (Test-Path $p) { $smi = $p; break } } }
|
||||
$row = @(& $smi --query-gpu=index,name,uuid,pci.bus_id --format=csv,noheader 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match 'RTX 5090' } | Select-Object -First 1
|
||||
if (-not $row) { 'RESULT error no RTX 5090'; Summary 'failed' @{ error = 'no 5090' }; exit 2 }
|
||||
$fld = $row -split ',\s*'; $idx = $fld[0].Trim(); $uuid = $fld[2].Trim(); $bus = $fld[3].Trim()
|
||||
"RESULT card index=$idx uuid=$uuid bus=$bus"
|
||||
$apps = @(& $smi --query-compute-apps=gpu_bus_id,pid,process_name --format=csv,noheader 2>$null | ForEach-Object { "$_" } | Where-Object { $_ -match [regex]::Escape($bus) -and $_ -match 'igneum' })
|
||||
$t = 0; while ($t -lt 150 -and $apps.Count -gt 0) { Start-Sleep -Seconds 5; $t += 5; $apps = @(& $smi --query-compute-apps=gpu_bus_id,pid,process_name --format=csv,noheader 2>$null | ForEach-Object { "$_" } | Where-Object { $_ -match [regex]::Escape($bus) -and $_ -match 'igneum' }) }
|
||||
"RESULT card $(Stamp) $(if ($apps.Count -eq 0) { "quiet after $t s (the runner's --cards-off took)" } else { "UNCONFIRMED: $($apps -join '; ')" })"
|
||||
# ---- the helper (the lock path), as pc1-v4-efficiency.ps1 ----
|
||||
$helperDir = Join-Path $env:LOCALAPPDATA 'igneum\app\sweep'; $cmdFile = Join-Path $helperDir 'cmd.txt'; $helperLog = Join-Path $helperDir 'helper.log'
|
||||
function HelperSeqBase() { $m = 0; foreach ($f in @($cmdFile, $helperLog)) { if (Test-Path $f) { foreach ($l in (Get-Content -LiteralPath $f -ErrorAction SilentlyContinue)) { if ($l -match '(?:^|\s)(\d{1,6}) (?:dev|pl|lgc|rgc|lmc|rmc|nvidia-smi|reregister)\b') { $v = [int]$Matches[1]; if ($v -gt $m) { $m = $v } } } } }; return $m + 100 }
|
||||
function HelperSend([string] $verb) {
|
||||
$s0 = HelperSeqBase; $s1 = $s0 + 1
|
||||
$mark = 0; if (Test-Path $helperLog) { $mark = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue).Count }
|
||||
$pad = ((0..3) | ForEach-Object { "$($s0 - 8 + 2 * $_) dev $idx" }) -join "`n"; Set-Content -LiteralPath $cmdFile -Value "$pad`n$s0 dev $idx`n$s1 $verb`n" -NoNewline -Encoding ascii
|
||||
$t = 0; while ($t -lt 30) { Start-Sleep -Seconds 1; $t++; $new = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue); if ($new.Count -gt $mark) { $hit = $new[$mark..($new.Count - 1)] | Where-Object { $_ -match "\b$s1 nvidia-smi " } | Select-Object -First 1; if ($hit) { return ($hit -replace '^\d+\s+', '') } } }
|
||||
return "no helper.log line for seq $s1 within 30 s"
|
||||
}
|
||||
$viaHelper = $false
|
||||
if (Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue) { try { Start-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction Stop; Start-Sleep -Seconds 3; $viaHelper = $true; 'RESULT helper task started' } catch { "RESULT helper start failed: $_" } } else { 'RESULT helper task not registered: the locked state is skipped' }
|
||||
# ---- the sampler ----
|
||||
$samples = Join-Path $jobDir 'nv-samples.csv'; if (Test-Path $samples) { Remove-Item -LiteralPath $samples -Force }
|
||||
$sampler = Start-Process -FilePath $smi -ArgumentList @('-i', $idx, '--query-gpu=timestamp,power.draw,clocks.sm,clocks.mem,temperature.gpu,utilization.gpu,power.draw.instant,power.draw.average', '--format=csv,noheader,nounits', '-lms', '1000') -RedirectStandardOutput $samples -WindowStyle Hidden -PassThru
|
||||
Start-Sleep -Seconds 5
|
||||
"RESULT sampler pid=$($sampler.Id) first=[$((Get-Content -LiteralPath $samples -ErrorAction SilentlyContinue | Select-Object -First 1))]"
|
||||
function Mean($vals) { $v = @($vals | ForEach-Object { [double]$_ }); if ($v.Count -eq 0) { return $null }; return [math]::Round((($v | Measure-Object -Sum).Sum / $v.Count), 1) }
|
||||
function Window([datetime] $t0, [datetime] $t1) { $sel = @(); if (Test-Path $samples) { foreach ($l in (Get-Content -LiteralPath $samples -ErrorAction SilentlyContinue)) { $p = $l -split ',\s*'; if ($p.Count -lt 6) { continue }; try { $ts = [datetime]::ParseExact($p[0].Trim(), 'yyyy/MM/dd HH:mm:ss.fff', [cultureinfo]::InvariantCulture) } catch { continue }; if ($ts -ge $t0 -and $ts -le $t1) { $sel += ,$p } } }; return ,$sel }
|
||||
function ParseUtc([string] $s) { try { return [datetime]::Parse($s, [cultureinfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::AdjustToUniversal).ToLocalTime() } catch { return $null } }
|
||||
function RunState([string] $state) {
|
||||
$env:CUDA_VISIBLE_DEVICES = $uuid
|
||||
"RESULT microbench-state $state start $(Stamp) $((& $smi -i $idx --query-gpu=clocks.sm,clocks.applications.graphics,power.draw --format=csv,noheader 2>&1 | Out-String).Trim())"
|
||||
$lines = @(& $exe --microbench --mb-seconds 60 --device 0 2>&1 | ForEach-Object { "$_" })
|
||||
$rc = $LASTEXITCODE
|
||||
foreach ($l in $lines) { if ($l -match '^RESULT microbench') { "$l state=$state" } elseif ($l -match 'error|fail|compile|skipped') { "RESULT microbench-note state=$state $l" } }
|
||||
foreach ($l in ($lines | Where-Object { $_ -match '^RESULT microbench probe=' })) {
|
||||
$name = ''; $g = ''; $ck = ''; $st = ''; $su = ''; $eu = ''
|
||||
if ($l -match 'probe=(\S+)') { $name = $Matches[1] }; if ($l -match 'G_ops_s=(\S+)') { $g = $Matches[1] }; if ($l -match 'checksum=(\S+)') { $ck = $Matches[1] }; if ($l -match 'status=(\S+)') { $st = $Matches[1] }
|
||||
if ($l -match 'start_utc=(\S+)') { $su = $Matches[1] }; if ($l -match 'end_utc=(\S+)') { $eu = $Matches[1] }
|
||||
$t0 = ParseUtc $su; $t1 = ParseUtc $eu
|
||||
if ($t0 -and $t1) { $w = Window $t0.AddSeconds(8) $t1; $watts = Mean ($w | ForEach-Object { $_[1] }); $sm = Mean ($w | ForEach-Object { $_[2] }); $mem = Mean ($w | ForEach-Object { $_[3] }); $util = Mean ($w | ForEach-Object { $_[5] }); $wi = Mean ($w | Where-Object { $_.Count -ge 8 -and $_[6] -match '^\s*[\d.]+\s*$' } | ForEach-Object { $_[6] }); $wa = Mean ($w | Where-Object { $_.Count -ge 8 -and $_[7] -match '^\s*[\d.]+\s*$' } | ForEach-Object { $_[7] }); "RESULT ca4-research probe=$name state=$state watts=$watts watts_instant=$wi watts_average=$wa sm_mhz=$sm mem_mhz=$mem util=$util G_ops_s=$g checksum=$ck status=$st samples=$($w.Count)" }
|
||||
else { "RESULT ca4-research probe=$name state=$state watts= (no start_utc/end_utc on the line) G_ops_s=$g checksum=$ck status=$st" }
|
||||
}
|
||||
"RESULT microbench-state $state end $(Stamp) exit=$rc lines=$($lines.Count)"
|
||||
}
|
||||
$locked = $false
|
||||
try {
|
||||
RunState 'unlocked'
|
||||
if ($viaHelper) {
|
||||
$r = HelperSend 'lgc 1300'
|
||||
if ($r -match 'no helper.log') { "RESULT lock1300 skipped: the helper gave no answer ($r)" } else { "RESULT lock1300 set: $r"; $locked = $true; Start-Sleep -Seconds 3; RunState 'lock1300' }
|
||||
} else { 'RESULT lock1300 skipped: no helper path' }
|
||||
Summary 'done' @{ locked_state_ran = $locked }
|
||||
exit 0
|
||||
} finally {
|
||||
if ($locked) { $r = HelperSend 'rgc'; "RESULT restore rgc: $r ; applications clock: $((& $smi -i $idx --query-gpu=clocks.applications.graphics --format=csv,noheader 2>&1 | Out-String).Trim())" }
|
||||
if ($sampler -and -not $sampler.HasExited) { Stop-Process -Id $sampler.Id -Force -ErrorAction SilentlyContinue; 'RESULT restore sampler stopped' }
|
||||
}
|
||||
96
tools/ca3-v4-amend/pc1-ca4-packs.ps1
Normal file
96
tools/ca3-v4-amend/pc1-ca4-packs.ps1
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# Counter ASIC 4 research (7 October 2026): the two prototype classes of the research lane (ad6a2bd47d4a46105) on PC 1's RTX 5090,
|
||||
# the card alone through the RUNNER's --cards-off, no prompt. The kit (fetch job fetch-ca4-packs-20261007): mx8-genesis (the class v3
|
||||
# control from packs-ca2-mixer), mx8_sh256x27 (the class v4-shaped shadow control), mx8_shl256x27 (the per-load shadow, 16 blocks of
|
||||
# 16 after each load), mx8_mm128, mx8_mm512, mx8_mm1430 (the int8 mma.m8n8k16 tile block, inline PTX in the kernel text); every pack
|
||||
# generator 2 over seed igneum-genesis, day 2026-10-03, 1 GiB. The INSTALLED igneum-worker-cuda.exe --bench takes each pack as it took
|
||||
# the 6 October ladder packs (NVRTC compiles the pack's own kernel text; an NVRTC refusal of a tile pack is the row). Per pack, 250
|
||||
# batches of 2^24 at --block-warps 1 with nvidia-smi at 1 Hz (the mean from 8 s in): MH/s, watts (all three power fields), SM MHz,
|
||||
# the fingerprint and the self-test verdict (check=PASS: the 96 vector lanes through the bound kernel, the CUDA-against-CPU gate for
|
||||
# both prototypes; a FAIL row is the finding). Once unlocked and once at the 1,300 MHz knee lock through the Power Helper task when it
|
||||
# answers (the sequence rule; rgc in finally; a silent helper skips the locked state and says so). Rows: `RESULT ca4-research pack=
|
||||
# state= mhs= watts= ... fingerprint= check=`, the worker's own RESULT line verbatim. Never quits, pauses, resumes or updates the app.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-ca4-packs'
|
||||
$started = Get-Date
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) { $o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }; foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }; 'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4) }
|
||||
$jobDir = $env:IGNEUM_JOB_DIR; if (-not $jobDir) { $jobDir = Join-Path $env:TEMP 'igneum-ca4-mb' }
|
||||
New-Item -ItemType Directory -Force -Path $jobDir | Out-Null
|
||||
$jobs = Split-Path $jobDir
|
||||
$packs = Join-Path (Join-Path $jobs 'fetch-ca4-packs-20261007') 'packs'
|
||||
if (-not (Test-Path $packs)) { "RESULT error packs missing at $packs (the fetch job fetch-ca4-packs-20261007 runs first)"; Summary 'failed' @{ error = 'packs 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) { 'RESULT error no installed igneum-worker-cuda.exe'; Summary 'failed' @{ error = 'no worker' }; exit 2 }
|
||||
$exe = Join-Path $inst 'igneum-worker-cuda.exe'
|
||||
# mx8_shl256x27 is the research lane's first per-load export (id 854050a4293f0615, the known-failed record: 11 percent duplicate reads from a lossy
|
||||
# base writer under the sub-blocks) and mx8_shl256x27_v2 its fix (counter-asic-4 2f718001, attempt 3, id bd64b207a30413fb); both run and every row
|
||||
# names its pack directory and the program id read from its program.json
|
||||
$packOrder = @('mx8-genesis', 'mx8_sh256x27', 'mx8_shl256x27', 'mx8_shl256x27_v2', 'mx8_mm128', 'mx8_mm512', 'mx8_mm1430')
|
||||
foreach ($pk in $packOrder) { $d = Join-Path $packs $pk; if (-not (Test-Path $d)) { "RESULT error pack missing at $d"; Summary 'failed' @{ error = "pack $pk missing" }; exit 2 }; foreach ($f in (Get-ChildItem $d -File | Sort-Object Name)) { "RESULT kitfile $pk/$($f.Name) sha256 $((Get-FileHash -Algorithm SHA256 $f.FullName).Hash.ToLower())" } }
|
||||
"RESULT start $(Stamp) job=$jobName exe=$exe sha256=$((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) label=ca4-research"
|
||||
$smi = (Get-Command nvidia-smi -ErrorAction SilentlyContinue).Source
|
||||
if (-not $smi) { foreach ($p in @((Join-Path $env:ProgramFiles 'NVIDIA Corporation\NVSMI\nvidia-smi.exe'), (Join-Path $env:SystemRoot 'System32\nvidia-smi.exe'))) { if (Test-Path $p) { $smi = $p; break } } }
|
||||
$row = @(& $smi --query-gpu=index,name,uuid,pci.bus_id --format=csv,noheader 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match 'RTX 5090' } | Select-Object -First 1
|
||||
if (-not $row) { 'RESULT error no RTX 5090'; Summary 'failed' @{ error = 'no 5090' }; exit 2 }
|
||||
$fld = $row -split ',\s*'; $idx = $fld[0].Trim(); $uuid = $fld[2].Trim(); $bus = $fld[3].Trim()
|
||||
"RESULT card index=$idx uuid=$uuid bus=$bus"
|
||||
$apps = @(& $smi --query-compute-apps=gpu_bus_id,pid,process_name --format=csv,noheader 2>$null | ForEach-Object { "$_" } | Where-Object { $_ -match [regex]::Escape($bus) -and $_ -match 'igneum' })
|
||||
$t = 0; while ($t -lt 150 -and $apps.Count -gt 0) { Start-Sleep -Seconds 5; $t += 5; $apps = @(& $smi --query-compute-apps=gpu_bus_id,pid,process_name --format=csv,noheader 2>$null | ForEach-Object { "$_" } | Where-Object { $_ -match [regex]::Escape($bus) -and $_ -match 'igneum' }) }
|
||||
"RESULT card $(Stamp) $(if ($apps.Count -eq 0) { "quiet after $t s (the runner's --cards-off took)" } else { "UNCONFIRMED: $($apps -join '; ')" })"
|
||||
# ---- the helper (the lock path), as pc1-v4-efficiency.ps1 ----
|
||||
$helperDir = Join-Path $env:LOCALAPPDATA 'igneum\app\sweep'; $cmdFile = Join-Path $helperDir 'cmd.txt'; $helperLog = Join-Path $helperDir 'helper.log'
|
||||
function HelperSeqBase() { $m = 0; foreach ($f in @($cmdFile, $helperLog)) { if (Test-Path $f) { foreach ($l in (Get-Content -LiteralPath $f -ErrorAction SilentlyContinue)) { if ($l -match '(?:^|\s)(\d{1,6}) (?:dev|pl|lgc|rgc|lmc|rmc|nvidia-smi|reregister)\b') { $v = [int]$Matches[1]; if ($v -gt $m) { $m = $v } } } } }; return $m + 100 }
|
||||
function HelperSend([string] $verb) {
|
||||
$s0 = HelperSeqBase; $s1 = $s0 + 1
|
||||
$mark = 0; if (Test-Path $helperLog) { $mark = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue).Count }
|
||||
$pad = ((0..3) | ForEach-Object { "$($s0 - 8 + 2 * $_) dev $idx" }) -join "`n"; Set-Content -LiteralPath $cmdFile -Value "$pad`n$s0 dev $idx`n$s1 $verb`n" -NoNewline -Encoding ascii
|
||||
$t = 0; while ($t -lt 30) { Start-Sleep -Seconds 1; $t++; $new = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue); if ($new.Count -gt $mark) { $hit = $new[$mark..($new.Count - 1)] | Where-Object { $_ -match "\b$s1 nvidia-smi " } | Select-Object -First 1; if ($hit) { return ($hit -replace '^\d+\s+', '') } } }
|
||||
return "no helper.log line for seq $s1 within 30 s"
|
||||
}
|
||||
$viaHelper = $false
|
||||
if (Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue) { try { Start-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction Stop; Start-Sleep -Seconds 3; $viaHelper = $true; 'RESULT helper task started' } catch { "RESULT helper start failed: $_" } } else { 'RESULT helper task not registered: the locked state is skipped' }
|
||||
# ---- the sampler ----
|
||||
$samples = Join-Path $jobDir 'nv-samples.csv'; if (Test-Path $samples) { Remove-Item -LiteralPath $samples -Force }
|
||||
$sampler = Start-Process -FilePath $smi -ArgumentList @('-i', $idx, '--query-gpu=timestamp,power.draw,clocks.sm,clocks.mem,temperature.gpu,utilization.gpu,power.draw.instant,power.draw.average', '--format=csv,noheader,nounits', '-lms', '1000') -RedirectStandardOutput $samples -WindowStyle Hidden -PassThru
|
||||
Start-Sleep -Seconds 5
|
||||
"RESULT sampler pid=$($sampler.Id) first=[$((Get-Content -LiteralPath $samples -ErrorAction SilentlyContinue | Select-Object -First 1))]"
|
||||
function Mean($vals) { $v = @($vals | ForEach-Object { [double]$_ }); if ($v.Count -eq 0) { return $null }; return [math]::Round((($v | Measure-Object -Sum).Sum / $v.Count), 1) }
|
||||
function Window([datetime] $t0, [datetime] $t1) { $sel = @(); if (Test-Path $samples) { foreach ($l in (Get-Content -LiteralPath $samples -ErrorAction SilentlyContinue)) { $p = $l -split ',\s*'; if ($p.Count -lt 6) { continue }; try { $ts = [datetime]::ParseExact($p[0].Trim(), 'yyyy/MM/dd HH:mm:ss.fff', [cultureinfo]::InvariantCulture) } catch { continue }; if ($ts -ge $t0 -and $ts -le $t1) { $sel += ,$p } } }; return ,$sel }
|
||||
function ParseUtc([string] $s) { try { return [datetime]::Parse($s, [cultureinfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::AdjustToUniversal).ToLocalTime() } catch { return $null } }
|
||||
function RunState([string] $state) {
|
||||
$env:CUDA_VISIBLE_DEVICES = $uuid
|
||||
"RESULT packs-state $state start $(Stamp) $((& $smi -i $idx --query-gpu=clocks.sm,clocks.applications.graphics,power.draw --format=csv,noheader 2>&1 | Out-String).Trim())"
|
||||
foreach ($pk in $packOrder) {
|
||||
$d = Join-Path $packs $pk
|
||||
$cls = ''; $pid = ''; try { $pj = Get-Content -LiteralPath (Join-Path $d 'program.json') -Raw | ConvertFrom-Json; $cls = [string] $pj.load_class; $pid = [string] $pj.program_id } catch { }
|
||||
$t0 = Get-Date
|
||||
$lines = @(& $exe --bench --pack $d --batches 250 --batch-log2 24 --block-warps 1 --device 0 2>&1 | ForEach-Object { "$_" })
|
||||
$rc = $LASTEXITCODE
|
||||
$t1 = Get-Date
|
||||
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
|
||||
foreach ($l in $lines) { if ($l -match '^RESULT pack=|error|FAIL|MISMATCH|compile|nvrtc|vector|lane|self-test|checksum' ) { "RESULT worker pack=$pk state=$state $l" } }
|
||||
$fp = ''; $mhs = ''; $check = ''
|
||||
if ($res -and $res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }; if ($res -and $res -match 'mhs=([\d.]+)') { $mhs = $Matches[1] }; if ($res -and $res -match 'check=(\S+)') { $check = $Matches[1] }
|
||||
$w = Window $t0.AddSeconds(8) $t1
|
||||
$watts = Mean ($w | ForEach-Object { $_[1] }); $sm = Mean ($w | ForEach-Object { $_[2] }); $mem = Mean ($w | ForEach-Object { $_[3] }); $util = Mean ($w | ForEach-Object { $_[5] })
|
||||
$wi = Mean ($w | Where-Object { $_.Count -ge 8 -and $_[6] -match '^\s*[\d.]+\s*$' } | ForEach-Object { $_[6] }); $wa = Mean ($w | Where-Object { $_.Count -ge 8 -and $_[7] -match '^\s*[\d.]+\s*$' } | ForEach-Object { $_[7] })
|
||||
$eff = if ($watts -and $mhs) { [math]::Round([double]$mhs / $watts, 4) } else { '' }
|
||||
$err = ''; if (-not $res -or $rc -ne 0) { $e = ($lines | Where-Object { $_ -match 'FAIL|error|MISMATCH|compile' } | Select-Object -First 1); $err = if ($e) { ($e -replace '\s+', ' ') } else { "exit $rc, no RESULT line" } }
|
||||
"RESULT ca4-research pack=$pk dir=$d program_id=$pid class=$cls state=$state mhs=$mhs watts=$watts watts_instant=$wi watts_average=$wa mh_per_w=$eff sm_mhz=$sm mem_mhz=$mem util=$util fingerprint=$fp check=$check exit=$rc wall_s=$([int]($t1 - $t0).TotalSeconds) samples=$($w.Count) error=$err"
|
||||
Start-Sleep -Seconds 3
|
||||
}
|
||||
"RESULT packs-state $state end $(Stamp)"
|
||||
}
|
||||
$locked = $false
|
||||
try {
|
||||
RunState 'unlocked'
|
||||
if ($viaHelper) {
|
||||
$r = HelperSend 'lgc 1300'
|
||||
if ($r -match 'no helper.log') { "RESULT lock1300 skipped: the helper gave no answer ($r)" } else { "RESULT lock1300 set: $r"; $locked = $true; Start-Sleep -Seconds 3; RunState 'lock1300' }
|
||||
} else { 'RESULT lock1300 skipped: no helper path' }
|
||||
Summary 'done' @{ locked_state_ran = $locked }
|
||||
exit 0
|
||||
} finally {
|
||||
if ($locked) { $r = HelperSend 'rgc'; "RESULT restore rgc: $r ; applications clock: $((& $smi -i $idx --query-gpu=clocks.applications.graphics --format=csv,noheader 2>&1 | Out-String).Trim())" }
|
||||
if ($sampler -and -not $sampler.HasExited) { Stop-Process -Id $sampler.Id -Force -ErrorAction SilentlyContinue; 'RESULT restore sampler stopped' }
|
||||
}
|
||||
40
tools/ca3-v4-amend/pc1-clocks-restore.ps1
Normal file
40
tools/ca3-v4-amend/pc1-clocks-restore.ps1
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# PC 1: put the RTX 5090's clocks back to the driver default through the Igneum Power Helper task (7 October 2026, 20:4x UTC).
|
||||
# The second efficiency pass (run-ca3-pc1-v4-eff-5090-floor-20261007) lost the helper at its 1,000 MHz step (no helper.log line
|
||||
# within 30 s for that and every later command, the `nvidia-smi -lgc 0,1000` most likely hanging inside the helper), so its
|
||||
# closing `rgc` never ran and the card stayed at the 1,100 MHz lock (sm 1,087 on the unlocked-end rows; the live miner at 118
|
||||
# to 122 MH/s instead of 136). This script stops the task (the owning user may stop their own task), starts it again, sends
|
||||
# `<seq> dev <idx>` then `<seq> rgc` through cmd.txt, waits for helper.log's answer and prints nvidia-smi's clocks before and
|
||||
# after. No prompt, no card switch, nothing posted to the app, settings.json untouched; runs beside the miners.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
$smi = $null; $c = Get-Command nvidia-smi -ErrorAction SilentlyContinue; if ($c) { $smi = $c.Source }
|
||||
if (-not $smi) { foreach ($p in @((Join-Path $env:ProgramFiles 'NVIDIA Corporation\NVSMI\nvidia-smi.exe'), (Join-Path $env:SystemRoot 'System32\nvidia-smi.exe'))) { if (Test-Path $p) { $smi = $p; break } } }
|
||||
if (-not $smi) { 'RESULT error nvidia-smi not found'; exit 2 }
|
||||
"RESULT before $(Stamp) $((& $smi --query-gpu=index,name,clocks.sm,clocks.applications.graphics,clocks_throttle_reasons.active,power.draw --format=csv,noheader 2>&1 | Out-String).Trim() -replace "`r?`n", ' | ')"
|
||||
$row = @(& $smi --query-gpu=index,name --format=csv,noheader 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match 'RTX 5090' } | Select-Object -First 1
|
||||
if (-not $row) { 'RESULT error no RTX 5090 in the list'; exit 2 }
|
||||
$idx = ($row -split ',')[0].Trim()
|
||||
$helperDir = Join-Path $env:LOCALAPPDATA 'igneum\app\sweep'
|
||||
$cmdFile = Join-Path $helperDir 'cmd.txt'; $helperLog = Join-Path $helperDir 'helper.log'
|
||||
if (Test-Path $helperLog) { "RESULT helper.log tail: $((Get-Content -LiteralPath $helperLog -Tail 4) -join ' || ')" }
|
||||
$task = Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue
|
||||
if (-not $task) { 'RESULT error the Igneum Power Helper task is not registered: no reset path without a prompt (Power control off)'; exit 3 }
|
||||
"RESULT task state before: $($task.State)"
|
||||
try { Stop-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction Stop; 'RESULT task stopped' } catch { "RESULT task stop failed: $_" }
|
||||
Start-Sleep -Seconds 3
|
||||
try { Start-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction Stop; 'RESULT task started' } catch { "RESULT task start failed: $_"; exit 3 }
|
||||
Start-Sleep -Seconds 3
|
||||
"RESULT task state now: $((Get-ScheduledTask -TaskName 'Igneum Power Helper').State)"
|
||||
$seq = 0
|
||||
if (Test-Path $cmdFile) { $seq = @(Get-Content -LiteralPath $cmdFile -ErrorAction SilentlyContinue | ForEach-Object { if ($_ -match '^(\d{1,6})\s') { [int]$Matches[1] } }) | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum }
|
||||
if (-not $seq) { $seq = 0 }
|
||||
$s0 = [int]$seq + 100; $s1 = $s0 + 1
|
||||
$mark = 0; if (Test-Path $helperLog) { $mark = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue).Count }
|
||||
Set-Content -LiteralPath $cmdFile -Value "$s0 dev $idx`n$s1 rgc`n" -NoNewline -Encoding ascii
|
||||
$t = 0; $hit = $null
|
||||
while ($t -lt 45 -and -not $hit) { Start-Sleep -Seconds 1; $t++; $new = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue); if ($new.Count -gt $mark) { $hit = $new[$mark..($new.Count - 1)] | Where-Object { $_ -match "\b$s1 nvidia-smi " } | Select-Object -First 1 } }
|
||||
if ($hit) { "RESULT reset $(Stamp) $hit" } else { "RESULT reset $(Stamp) no helper.log line for seq $s1 within 45 s" }
|
||||
Start-Sleep -Seconds 5
|
||||
"RESULT after $(Stamp) $((& $smi --query-gpu=index,name,clocks.sm,clocks.applications.graphics,clocks_throttle_reasons.active,power.draw --format=csv,noheader 2>&1 | Out-String).Trim() -replace "`r?`n", ' | ')"
|
||||
$app = @(& $smi --query-gpu=index,clocks.applications.graphics --format=csv,noheader 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match "^$idx," } | Select-Object -First 1
|
||||
if ($hit -and $app -notmatch '\b1[0-4]\d\d MHz') { 'SUMMARY {"job":"pc1-clocks-restore","status":"done"}'; exit 0 } else { 'SUMMARY {"job":"pc1-clocks-restore","status":"check"}'; exit 1 }
|
||||
74
tools/ca3-v4-amend/pc1-ember-card.ps1
Normal file
74
tools/ca3-v4-amend/pc1-ember-card.ps1
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Counter ASIC 3.0, PC 1 queue (7 October 2026): Ember Tune on ONE card of the installed app, by the job id (an id carrying
|
||||
# "9070" tunes the RX 9070 XT, "5080" the RTX 5080, otherwise the RTX 5090), the other cards untouched and mining on. The shape
|
||||
# is relay/playbooks/ember-installed-tune-pc1.ps1 (6 October 2026): the SHIPPED app tunes itself through the registered Power
|
||||
# Helper task, this script only asks (POST api/tune/goal climb on, POST api/sweep/start for the one card) and watches the TUNE
|
||||
# rows in the app's own log and api/state, then puts the climb switch back. On 0.3.21 (ember.rs may_set) a step is SET on an
|
||||
# AMD card only when igneum-gpu-telemetry beside the app gives the card a tune line (ADLX) and Power control is on, else the
|
||||
# tune is measure-only and the TUNE rows carry the reason; this script prints the card's tune_line before and after so the
|
||||
# "measure only" reading and its reason are on RESULT lines. Never quits, pauses or resumes the installed app, never posts to
|
||||
# /api/cards, never writes settings.json. Not elevated (the task carries the rights).
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Say([string] $s) { Write-Output ('[' + (Get-Date -Format 'HH:mm:ss') + '] ' + $s) }
|
||||
$appDir = Join-Path $env:LOCALAPPDATA 'igneum\app'
|
||||
$u = Join-Path $appDir 'app.url'
|
||||
if (-not (Test-Path -LiteralPath $u)) { Write-Output 'RESULT TUNE error=no_app_url (installed app not running)'; exit 1 }
|
||||
$base = (Get-Content -LiteralPath $u -Raw).Trim()
|
||||
function State() { try { return ((Invoke-WebRequest -Uri ($base + 'api/state') -UseBasicParsing -TimeoutSec 5).Content | ConvertFrom-Json) } catch { return $null } }
|
||||
function Post([string] $path, [string] $json) { try { Invoke-WebRequest -Uri ($base + $path) -Method POST -Body $json -ContentType 'application/json' -UseBasicParsing -TimeoutSec 5 | Out-Null; return $true } catch { Say ($path + ': ' + $_.Exception.Message); return $false } }
|
||||
$s = State
|
||||
if (-not $s) { Write-Output 'RESULT TUNE error=state_unreadable'; exit 1 }
|
||||
$ver = [string]$s.version
|
||||
Write-Output ('RESULT TUNE installed app ' + $ver + ' power_control=' + $s.settings.power_control + ' tuning_off=' + $s.settings.tuning_off + ' climb_before=' + $s.settings.tune_climb + ' goal_before=' + $s.settings.tune_goal)
|
||||
$t = Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue
|
||||
Write-Output ('RESULT TUNE task ' + $(if ($t) { $t.Actions[0].Execute + ' state=' + $t.State } else { 'NOT REGISTERED' }))
|
||||
if (-not $t) { Write-Output 'RESULT TUNE error=task_not_registered (the NVIDIA climbs would need a prompt; stopping)'; exit 1 }
|
||||
if (-not $s.settings.power_control) { Write-Output 'RESULT TUNE error=power_control_off'; exit 1 }
|
||||
$climbBefore = [bool]$s.settings.tune_climb
|
||||
$jobDir = $env:IGNEUM_JOB_DIR; $jobId = if ($jobDir) { Split-Path -Leaf $jobDir } else { '' }
|
||||
$want = if ($jobId -match '9070') { '9070' } elseif ($jobId -match '5080') { '5080' } else { '5090' }
|
||||
Write-Output ('RESULT TUNE job_id=' + $jobId + ' card_match=' + $want)
|
||||
$cards = @($s.mining.cards | Where-Object { $_.enabled -and ($_.vendor -eq 'nvidia' -or $_.vendor -eq 'amd') -and $_.name -notmatch 'Radeon\(TM\) Graphics' -and $_.name -match $want })
|
||||
if ($cards.Count -eq 0) { Write-Output ('RESULT TUNE error=no enabled card matching ' + $want + ' in api/state (names: ' + (($s.mining.cards | ForEach-Object { $_.name }) -join ', ') + ')'); exit 1 }
|
||||
Write-Output ('RESULT TUNE cards ' + (($cards | ForEach-Object { $_.key + ' (' + $_.name + ', ' + $_.vendor + ', sweep_state ' + $_.sweep_state + ', line "' + $_.tune_line + '")' }) -join ' | '))
|
||||
foreach ($c in $cards) { Write-Output ('RESULT TUNE before key=' + $c.key + ' power_w=' + $c.power_w + ' power_pct=' + $c.power_pct + ' clock_cap=' + $c.clock_cap_mhz + ' mhs=' + $c.mhs + ' line=' + $c.tune_line) }
|
||||
# the climb on (NVIDIA cards; an AMD card has no clock knob and runs its ladder regardless)
|
||||
if (-not (Post 'api/tune/goal' '{"goal":"balanced","climb":true}')) { Write-Output 'RESULT TUNE error=tune_goal_refused'; exit 1 }
|
||||
Start-Sleep -Seconds 2
|
||||
$order = @($cards | Where-Object { $_.vendor -eq 'amd' }) + @($cards | Where-Object { $_.vendor -eq 'nvidia' })
|
||||
$deadline = (Get-Date).AddMinutes(27)
|
||||
$logs = Join-Path $appDir 'logs'
|
||||
function LogMark() { $l = Get-ChildItem -Path $logs -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1; if ($l) { return @{ f = $l.FullName; n = (Get-Content -LiteralPath $l.FullName).Count } } else { return @{ f = ''; n = 0 } } }
|
||||
function LogSince($m) { if (-not $m.f) { return @() }; $all = @(Get-Content -LiteralPath $m.f -ErrorAction SilentlyContinue); if ($all.Count -le $m.n) { return @() }; return @($all[$m.n..($all.Count - 1)]) }
|
||||
$rows = 0
|
||||
foreach ($c in $order) {
|
||||
if ((Get-Date) -gt $deadline) { Say 'budget spent before ' + $c.key; break }
|
||||
$m = LogMark
|
||||
$body = '{"key":"' + ($c.key -replace '"', '\"') + '"}'
|
||||
if (-not (Post 'api/sweep/start' $body)) { Write-Output ('RESULT TUNE error=sweep_start_refused key=' + $c.key); continue }
|
||||
Say ('tune asked for ' + $c.key)
|
||||
$seen = 0; $done = $false; $started = Get-Date
|
||||
while (-not $done -and (Get-Date) -lt $deadline) {
|
||||
Start-Sleep -Seconds 10
|
||||
$new = @(LogSince $m)
|
||||
while ($seen -lt $new.Count) { $l = [string]$new[$seen]; $seen++; if ($l -match 'TUNE (card=|chosen|start|aborted|progress)') { if ($l -notmatch 'TUNE progress') { Write-Output ('RESULT ' + ($l -replace '^.*?(TUNE )', 'TUNE ')) }; if ($l -match 'TUNE card=') { $rows++ } } }
|
||||
$st = State
|
||||
if ($st) {
|
||||
$cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1
|
||||
if ($cc) {
|
||||
$running = ($cc.sweep_state -eq 'running')
|
||||
if (-not $running -and ((Get-Date) - $started).TotalSeconds -gt 180) { $done = $true }
|
||||
if ($running -and $cc.tune_steps -gt 0 -and ($seen % 6 -eq 0)) { Say ($c.key + ': step ' + $cc.tune_step + ' of ' + $cc.tune_steps + ', ' + $cc.sweep_note) }
|
||||
}
|
||||
}
|
||||
}
|
||||
$st = State
|
||||
if ($st) { $cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1; if ($cc) { Write-Output ('RESULT TUNE after key=' + $c.key + ' power_w=' + $cc.power_w + ' power_pct=' + $cc.power_pct + ' clock_cap=' + $cc.clock_cap_mhz + ' line=' + $cc.tune_line + ' source=' + $cc.tune_source + ' note=' + $cc.sweep_note); if ($cc.tune_curve) { Write-Output ('RESULT TUNE curve key=' + $c.key + ' ' + (($cc.tune_curve | ForEach-Object { ($_.clock_mhz) + 'MHz/' + ($_.mem_mhz) + 'mem/' + ($_.power_pct) + '%:' + $_.mhs + 'MH/s ' + $_.watts + 'W ' + $_.eff + ' ' + $_.mark }) -join ' | ')) } } }
|
||||
}
|
||||
# the climb switch back to what it was
|
||||
Post 'api/tune/goal' ('{"climb":' + $(if ($climbBefore) { 'true' } else { 'false' }) + '}') | Out-Null
|
||||
Write-Output ('RESULT TUNE climb_restored=' + $climbBefore + ' rows=' + $rows)
|
||||
$smi = Join-Path $env:ProgramFiles 'NVIDIA Corporation\NVSMI\nvidia-smi.exe'
|
||||
if (-not (Test-Path $smi)) { $smi = Join-Path $env:SystemRoot 'System32\nvidia-smi.exe' }
|
||||
if (Test-Path $smi) { Write-Output ('RESULT TUNE nvidia_now ' + ((& $smi --query-gpu=index,name,power.draw,power.limit,clocks.gr,clocks.mem --format=csv,noheader 2>&1 | Out-String) -replace "`r?`n", ' | ')) }
|
||||
if ($rows -eq 0) { Write-Output 'RESULT TUNE error=no_rows'; exit 1 }
|
||||
exit 0
|
||||
44
tools/ca3-v4-amend/pc1-helper-diag.ps1
Normal file
44
tools/ca3-v4-amend/pc1-helper-diag.ps1
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# PC 1: why does the Igneum Power Helper task give no answer (7 October 2026, 21:2x UTC)? The 5080 pass -c sent its commands
|
||||
# through cmd.txt with sequence numbers above every number in cmd.txt and helper.log and got no helper.log line within 30 s,
|
||||
# before and after a Stop/Start of the task. Read-only except the task restart and one `rgc` on the RTX 5080 (nothing is
|
||||
# locked there, so the reset is a no-op that proves the channel): the last 40 helper.log lines with their ages, cmd.txt as it
|
||||
# stands, the task's state and last run result, the helper process (igneum-app.exe --power-helper) with its start time and
|
||||
# its child nvidia-smi processes (a hung nvidia-smi call is the suspected class), every card's sweep_state and tune_step from
|
||||
# api/state (read only: the app's own tune writing the same file is the other suspect), then Stop-ScheduledTask, Start, one
|
||||
# `dev <idx>` + `rgc`, a 45 s wait for the answer, and the tail again. No prompt, no card switch, nothing posted to the app.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
$helperDir = Join-Path $env:LOCALAPPDATA 'igneum\app\sweep'
|
||||
$cmdFile = Join-Path $helperDir 'cmd.txt'; $helperLog = Join-Path $helperDir 'helper.log'
|
||||
$nowUnix = [int][double]::Parse((Get-Date -UFormat %s))
|
||||
"RESULT start $(Stamp)"
|
||||
if (Test-Path $helperLog) { foreach ($l in (Get-Content -LiteralPath $helperLog -Tail 40)) { $age = ''; if ($l -match '^(\d{9,10}) ') { $age = " (age $($nowUnix - [int]$Matches[1]) s)" }; "RESULT helperlog$age $l" } } else { 'RESULT helperlog none' }
|
||||
if (Test-Path $cmdFile) { "RESULT cmdtxt $((Get-Content -LiteralPath $cmdFile -Raw) -replace "`r?`n", ' | ')" } else { 'RESULT cmdtxt none' }
|
||||
$task = Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue
|
||||
if ($task) { $info = Get-ScheduledTaskInfo -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue; "RESULT task state=$($task.State) last_run=$($info.LastRunTime) last_result=$($info.LastTaskResult) next_run=$($info.NextRunTime) action=$($task.Actions[0].Execute) $($task.Actions[0].Arguments)" } else { 'RESULT task NOT REGISTERED' }
|
||||
$procs = @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match 'power-helper' -or $_.Name -eq 'nvidia-smi.exe' })
|
||||
foreach ($p in $procs) { $age = ''; try { $age = " age $([int]((Get-Date) - $p.ConvertToDateTime($p.CreationDate)).TotalSeconds) s" } catch { }; "RESULT proc pid=$($p.ProcessId) parent=$($p.ParentProcessId)$age cmd=$($p.CommandLine)" }
|
||||
if ($procs.Count -eq 0) { 'RESULT proc none (no igneum-app --power-helper and no nvidia-smi process)' }
|
||||
$appDir = Join-Path $env:LOCALAPPDATA 'igneum\app'; $u = Join-Path $appDir 'app.url'
|
||||
if (Test-Path $u) { try { $base = (Get-Content -LiteralPath $u -Raw).Trim(); $s = (Invoke-WebRequest -Uri ($base + 'api/state') -UseBasicParsing -TimeoutSec 5).Content | ConvertFrom-Json; "RESULT app version=$($s.version) power_control=$($s.settings.power_control) tuning_off=$($s.settings.tuning_off) climb=$($s.settings.tune_climb)"; foreach ($c in $s.mining.cards) { "RESULT appcard key=$($c.key) enabled=$($c.enabled) sweep_state=$($c.sweep_state) tune_step=$($c.tune_step)/$($c.tune_steps) clock_cap=$($c.clock_cap_mhz) note=$($c.sweep_note)" } } catch { "RESULT app state unreadable: $_" } } else { 'RESULT app no app.url' }
|
||||
$smi = (Get-Command nvidia-smi -ErrorAction SilentlyContinue).Source
|
||||
$idx = $null
|
||||
if ($smi) { $row = @(& $smi --query-gpu=index,name --format=csv,noheader 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match 'RTX 5080' } | Select-Object -First 1; if ($row) { $idx = ($row -split ',')[0].Trim() }; "RESULT gpus $((& $smi --query-gpu=index,name,clocks.sm,clocks.applications.graphics,power.draw --format=csv,noheader 2>&1 | Out-String).Trim() -replace "`r?`n", ' | ')" }
|
||||
if (-not $task) { 'SUMMARY {"job":"pc1-helper-diag","status":"no-task"}'; exit 3 }
|
||||
try { Stop-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction Stop; 'RESULT task stopped' } catch { "RESULT task stop failed: $_" }
|
||||
Start-Sleep -Seconds 3
|
||||
$left = @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match 'power-helper' })
|
||||
"RESULT after stop: helper processes left $($left.Count) $(($left | ForEach-Object { $_.ProcessId }) -join ' ')"
|
||||
try { Start-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction Stop; 'RESULT task started' } catch { "RESULT task start failed: $_" }
|
||||
Start-Sleep -Seconds 4
|
||||
"RESULT task state now $((Get-ScheduledTask -TaskName 'Igneum Power Helper').State); helper processes $(@(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match 'power-helper' }).Count)"
|
||||
$m = 0; foreach ($f in @($cmdFile, $helperLog)) { if (Test-Path $f) { foreach ($l in (Get-Content -LiteralPath $f -ErrorAction SilentlyContinue)) { if ($l -match '(?:^|\s)(\d{1,6}) (?:dev|pl|lgc|rgc|lmc|rmc|nvidia-smi|reregister)\b') { $v = [int]$Matches[1]; if ($v -gt $m) { $m = $v } } } } }
|
||||
$s0 = $m + 100; $s1 = $s0 + 1
|
||||
$mark = 0; if (Test-Path $helperLog) { $mark = @(Get-Content -LiteralPath $helperLog).Count }
|
||||
if ($idx) { Set-Content -LiteralPath $cmdFile -Value "$s0 dev $idx`n$s1 rgc`n" -NoNewline -Encoding ascii; "RESULT sent $(Stamp) '$s0 dev $idx' + '$s1 rgc'" } else { 'RESULT sent nothing (no 5080 index)' }
|
||||
$t = 0; $hit = $null
|
||||
while ($t -lt 45 -and -not $hit -and $idx) { Start-Sleep -Seconds 1; $t++; $new = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue); if ($new.Count -gt $mark) { $hit = $new[$mark..($new.Count - 1)] | Where-Object { $_ -match "\b$s1 nvidia-smi " } | Select-Object -First 1 } }
|
||||
if ($hit) { "RESULT answer after $t s: $hit" } else { "RESULT no answer within $t s" }
|
||||
if (Test-Path $helperLog) { foreach ($l in (Get-Content -LiteralPath $helperLog -Tail 6)) { "RESULT helperlog-after $l" } }
|
||||
if (Test-Path $cmdFile) { "RESULT cmdtxt-after $((Get-Content -LiteralPath $cmdFile -Raw) -replace "`r?`n", ' | ')" }
|
||||
if ($hit) { 'SUMMARY {"job":"pc1-helper-diag","status":"answered"}'; exit 0 } else { 'SUMMARY {"job":"pc1-helper-diag","status":"silent"}'; exit 1 }
|
||||
49
tools/ca3-v4-amend/pc1-helper-diag2.ps1
Normal file
49
tools/ca3-v4-amend/pc1-helper-diag2.ps1
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# PC 1: why does the Power Helper process die right after "helper started" (7 October 2026, 21:3x UTC)? Read only, plus one
|
||||
# 20-second UNELEVATED probe: the task's exe path and the file's mtime, version and sha256 against the running app's exe; the
|
||||
# task's scheduler history (events 100 to 203 of the last hour: start, action start, action end with its exit code); the helper
|
||||
# started by hand as a child of this job (`igneum-app.exe --power-helper`, no rights, so a `dev` line is answered with the
|
||||
# helper's own log line and an nvidia-smi permission text at most, which proves the loop runs) for 20 s, its exit code, and
|
||||
# helper.log's new lines; then the child is ended by its pid. Nothing is set on any card, nothing posted to the app, settings.json
|
||||
# untouched, no prompt. Rows: RESULT lines, a SUMMARY {json}.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
"RESULT start $(Stamp)"
|
||||
$task = Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue
|
||||
if (-not $task) { 'RESULT task NOT REGISTERED'; 'SUMMARY {"job":"pc1-helper-diag2","status":"no-task"}'; exit 3 }
|
||||
$exe = $task.Actions[0].Execute.Trim('"')
|
||||
$info = Get-ScheduledTaskInfo -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue
|
||||
"RESULT task state=$($task.State) exe=$exe args=$($task.Actions[0].Arguments) runlevel=$($task.Principal.RunLevel) user=$($task.Principal.UserId) last_run=$($info.LastRunTime) last_result=$($info.LastTaskResult) missed=$($info.NumberOfMissedRuns)"
|
||||
if (Test-Path -LiteralPath $exe) { $f = Get-Item -LiteralPath $exe; "RESULT taskexe mtime=$($f.LastWriteTimeUtc.ToString('yyyy-MM-ddTHH:mm:ssZ')) bytes=$($f.Length) version=$($f.VersionInfo.FileVersion) product=$($f.VersionInfo.ProductVersion) sha256=$((Get-FileHash -Algorithm SHA256 -LiteralPath $exe).Hash.ToLower())" } else { "RESULT taskexe MISSING at $exe" }
|
||||
$app = @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-app.exe'" -ErrorAction SilentlyContinue)
|
||||
foreach ($p in $app) { $path = $p.ExecutablePath; $v = ''; $sha = ''; if ($path -and (Test-Path -LiteralPath $path)) { $v = (Get-Item -LiteralPath $path).VersionInfo.FileVersion; $sha = (Get-FileHash -Algorithm SHA256 -LiteralPath $path).Hash.ToLower() }; "RESULT appproc pid=$($p.ProcessId) started=$($p.ConvertToDateTime($p.CreationDate).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')) path=$path version=$v sha256=$sha cmd=$($p.CommandLine)" }
|
||||
try { $ver = (& $exe --version 2>&1 | Out-String).Trim() -replace '\s+', ' '; "RESULT taskexe --version: $ver" } catch { "RESULT taskexe --version failed: $_" }
|
||||
# the scheduler's history for the task (needs the TaskScheduler operational log enabled; absent = says so)
|
||||
try {
|
||||
$since = (Get-Date).AddHours(-2)
|
||||
$ev = @(Get-WinEvent -FilterHashtable @{ LogName = 'Microsoft-Windows-TaskScheduler/Operational'; StartTime = $since } -ErrorAction Stop | Where-Object { $_.Message -match 'Igneum Power Helper' } | Sort-Object TimeCreated | Select-Object -Last 30)
|
||||
if ($ev.Count -eq 0) { 'RESULT taskhistory no events for the task in 2 h (the operational log may be off)' }
|
||||
foreach ($e in $ev) { "RESULT taskhistory $($e.TimeCreated.ToUniversalTime().ToString('HH:mm:ssZ')) id=$($e.Id) $(($e.Message -replace '\s+', ' ').Substring(0, [math]::Min(160, ($e.Message -replace '\s+', ' ').Length)))" }
|
||||
} catch { "RESULT taskhistory unreadable: $(($_ | Out-String).Trim() -replace '\s+', ' ')" }
|
||||
# the application log: a crash of igneum-app.exe in the last 2 h (Windows Error Reporting / Application Error)
|
||||
try {
|
||||
$crash = @(Get-WinEvent -FilterHashtable @{ LogName = 'Application'; StartTime = (Get-Date).AddHours(-2) } -ErrorAction Stop | Where-Object { $_.Message -match 'igneum-app' -and ($_.ProviderName -match 'Application Error|Windows Error Reporting|\.NET Runtime') } | Select-Object -Last 6)
|
||||
if ($crash.Count -eq 0) { 'RESULT crashlog no igneum-app crash events in 2 h' }
|
||||
foreach ($e in $crash) { "RESULT crashlog $($e.TimeCreated.ToUniversalTime().ToString('HH:mm:ssZ')) $($e.ProviderName): $(($e.Message -replace '\s+', ' ').Substring(0, [math]::Min(220, ($e.Message -replace '\s+', ' ').Length)))" }
|
||||
} catch { "RESULT crashlog unreadable: $(($_ | Out-String).Trim() -replace '\s+', ' ')" }
|
||||
# the unelevated probe: the helper loop as this user, 20 s, one dev line
|
||||
$helperDir = Join-Path $env:LOCALAPPDATA 'igneum\app\sweep'; $cmdFile = Join-Path $helperDir 'cmd.txt'; $helperLog = Join-Path $helperDir 'helper.log'
|
||||
$mark = 0; if (Test-Path $helperLog) { $mark = @(Get-Content -LiteralPath $helperLog).Count }
|
||||
$m = 0; foreach ($f in @($cmdFile, $helperLog)) { if (Test-Path $f) { foreach ($l in (Get-Content -LiteralPath $f -ErrorAction SilentlyContinue)) { if ($l -match '(?:^|\s)(\d{1,6}) (?:dev|pl|lgc|rgc|lmc|rmc|nvidia-smi|reregister)\b') { $v = [int]$Matches[1]; if ($v -gt $m) { $m = $v } } } } }
|
||||
$probeOut = Join-Path $env:TEMP 'igneum-helper-probe.out'; $probeErr = Join-Path $env:TEMP 'igneum-helper-probe.err'
|
||||
if (Test-Path $probeOut) { Remove-Item -LiteralPath $probeOut -Force }; if (Test-Path $probeErr) { Remove-Item -LiteralPath $probeErr -Force }
|
||||
$child = $null
|
||||
try { $child = Start-Process -FilePath $exe -ArgumentList '--power-helper' -WindowStyle Hidden -PassThru -RedirectStandardOutput $probeOut -RedirectStandardError $probeErr; "RESULT probe started pid=$($child.Id) $(Stamp)" } catch { "RESULT probe start failed: $_" }
|
||||
Start-Sleep -Seconds 4
|
||||
if ($child) { "RESULT probe alive_after_4s=$(-not $child.HasExited) exit=$(if ($child.HasExited) { $child.ExitCode } else { '' })" }
|
||||
if ($child -and -not $child.HasExited) { $s0 = $m + 100; Set-Content -LiteralPath $cmdFile -Value "$s0 dev 1`n" -NoNewline -Encoding ascii; "RESULT probe sent '$s0 dev 1'"; Start-Sleep -Seconds 6; "RESULT probe alive_after_10s=$(-not $child.HasExited)" }
|
||||
Start-Sleep -Seconds 10
|
||||
if ($child) { "RESULT probe alive_after_20s=$(-not $child.HasExited) exit=$(if ($child.HasExited) { $child.ExitCode } else { '' })"; if (-not $child.HasExited) { try { Stop-Process -Id $child.Id -Force; 'RESULT probe ended by pid' } catch { } } }
|
||||
foreach ($f in @($probeOut, $probeErr)) { if (Test-Path $f) { $t = (Get-Content -LiteralPath $f -Raw -ErrorAction SilentlyContinue); if ($t) { "RESULT probe $(Split-Path -Leaf $f): $(($t -replace '\s+', ' ').Substring(0, [math]::Min(400, ($t -replace '\s+', ' ').Length)))" } } }
|
||||
$new = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue); if ($new.Count -gt $mark) { foreach ($l in $new[$mark..($new.Count - 1)]) { "RESULT helperlog-new $l" } } else { 'RESULT helperlog-new none' }
|
||||
'SUMMARY {"job":"pc1-helper-diag2","status":"done"}'
|
||||
exit 0
|
||||
31
tools/ca3-v4-amend/pc1-helper-read.ps1
Normal file
31
tools/ca3-v4-amend/pc1-helper-read.ps1
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# PC 1, read only (the update-return lane's content, 7 October 2026, 22:3x BST, through the shipper): the Power Helper's state
|
||||
# for its owner. Under the signed-in user, nothing written, nothing set, nothing posted to the app, no prompt. (1) helper.log tail
|
||||
# 60, cmd.txt, helper.alive, the sweep folder listing with LastWriteTime; (2) schtasks /query /v /fo list; (3) every igneum
|
||||
# process with its path, parent and creation time; (4) Application log events 1000/1001/1026 naming igneum; (5) the Task
|
||||
# Scheduler operational events naming the task; (6) --version of the task's exe and of the install folder's exe; (7) the app's
|
||||
# log lines about the helper, power cap, power control and tune since 21:00 UTC. Every line printed as RESULT text.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Say([string] $tag, [string] $text) { foreach ($l in ($text -split "`r?`n")) { if ($l.Trim()) { "RESULT $tag $l" } } }
|
||||
"RESULT start $(Stamp)"
|
||||
$sweep = Join-Path $env:LOCALAPPDATA 'igneum\app\sweep'
|
||||
foreach ($f in @('helper.log', 'cmd.txt', 'helper.alive')) { $p = Join-Path $sweep $f; if (Test-Path -LiteralPath $p) { $t = if ($f -eq 'helper.log') { (Get-Content -LiteralPath $p -Tail 60) -join "`n" } else { Get-Content -LiteralPath $p -Raw }; Say "1-$f" $t } else { "RESULT 1-$f absent" } }
|
||||
if (Test-Path -LiteralPath $sweep) { foreach ($i in (Get-ChildItem -LiteralPath $sweep -Force)) { "RESULT 1-dir $($i.Name) bytes=$($i.Length) mtime=$($i.LastWriteTimeUtc.ToString('yyyy-MM-ddTHH:mm:ssZ'))" } } else { 'RESULT 1-dir sweep folder absent' }
|
||||
Say '2-schtasks' ((& schtasks /query /tn "Igneum Power Helper" /v /fo list 2>&1 | Out-String))
|
||||
$taskExe = $null; $tr = (& schtasks /query /tn "Igneum Power Helper" /v /fo list 2>&1 | Out-String) -split "`r?`n" | Where-Object { $_ -match '^Task To Run:' } | Select-Object -First 1
|
||||
if ($tr -and $tr -match '^Task To Run:\s*(.+)$') { $cmd = $Matches[1].Trim(); if ($cmd -match '^"([^"]+)"') { $taskExe = $Matches[1] } elseif ($cmd -match '^(\S+\.exe)') { $taskExe = $Matches[1] } }
|
||||
foreach ($p in (Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -like '*igneum*' })) { $c = ''; try { $c = $p.ConvertToDateTime($p.CreationDate).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') } catch { }; "RESULT 3-proc pid=$($p.ProcessId) parent=$($p.ParentProcessId) name=$($p.Name) path=$($p.ExecutablePath) created=$c cmd=$($p.CommandLine)" }
|
||||
try { $ev = @(Get-WinEvent -FilterHashtable @{ LogName = 'Application'; Id = 1000, 1001, 1026 } -MaxEvents 40 -ErrorAction Stop | Where-Object { $_.Message -like '*igneum*' }); if ($ev.Count -eq 0) { 'RESULT 4-apperr none of 1000/1001/1026 name igneum in the last 40' }; foreach ($e in $ev) { "RESULT 4-apperr $($e.TimeCreated.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')) id=$($e.Id) $(($e.Message -replace '\s+', ' '))" } } catch { "RESULT 4-apperr unreadable: $(($_ | Out-String).Trim() -replace '\s+', ' ')" }
|
||||
try { $ev = @(Get-WinEvent -LogName 'Microsoft-Windows-TaskScheduler/Operational' -MaxEvents 200 -ErrorAction Stop | Where-Object { $_.Message -like '*Igneum Power Helper*' }); if ($ev.Count -eq 0) { 'RESULT 5-sched no events naming the task in the last 200 (the operational log may be off)' }; foreach ($e in ($ev | Sort-Object TimeCreated)) { "RESULT 5-sched $($e.TimeCreated.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')) id=$($e.Id) $(($e.Message -replace '\s+', ' '))" } } catch { "RESULT 5-sched unreadable: $(($_ | Out-String).Trim() -replace '\s+', ' ')" }
|
||||
if ($taskExe -and (Test-Path -LiteralPath $taskExe)) { $f = Get-Item -LiteralPath $taskExe; "RESULT 6-taskexe path=$taskExe mtime=$($f.LastWriteTimeUtc.ToString('yyyy-MM-ddTHH:mm:ssZ')) bytes=$($f.Length) sha256=$((Get-FileHash -Algorithm SHA256 -LiteralPath $taskExe).Hash.ToLower())"; try { "RESULT 6-taskexe --version: $((& $taskExe --version 2>&1 | Out-String).Trim() -replace '\s+', ' ')" } catch { "RESULT 6-taskexe --version failed: $_" } } else { "RESULT 6-taskexe not found (Task To Run: $tr)" }
|
||||
$inst = Join-Path $env:LOCALAPPDATA 'Programs\Igneum Miner\igneum-app.exe'
|
||||
if (Test-Path -LiteralPath $inst) { $f = Get-Item -LiteralPath $inst; "RESULT 6-installexe path=$inst mtime=$($f.LastWriteTimeUtc.ToString('yyyy-MM-ddTHH:mm:ssZ')) bytes=$($f.Length) sha256=$((Get-FileHash -Algorithm SHA256 -LiteralPath $inst).Hash.ToLower())"; try { "RESULT 6-installexe --version: $((& $inst --version 2>&1 | Out-String).Trim() -replace '\s+', ' ')" } catch { "RESULT 6-installexe --version failed: $_" } } else { "RESULT 6-installexe absent at $inst" }
|
||||
$logs = Join-Path $env:LOCALAPPDATA 'igneum\app\logs'
|
||||
$since = (Get-Date).ToUniversalTime().Date.AddHours(21)
|
||||
foreach ($lf in (Get-ChildItem -Path (Join-Path $env:LOCALAPPDATA 'igneum\app') -Recurse -Filter '*.log' -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTimeUtc -ge $since.AddMinutes(-5) -and $_.Name -notmatch '^helper' } | Sort-Object LastWriteTime -Descending | Select-Object -First 3)) {
|
||||
$hits = @(Select-String -LiteralPath $lf.FullName -Pattern 'helper|power cap|power control|tune|Power Helper' -ErrorAction SilentlyContinue | Select-Object -Last 60)
|
||||
"RESULT 7-applog file=$($lf.FullName) hits=$($hits.Count)"
|
||||
foreach ($h in $hits) { "RESULT 7-applog $(($h.Line -replace '\s+', ' ').Substring(0, [math]::Min(260, ($h.Line -replace '\s+', ' ').Length)))" }
|
||||
}
|
||||
'SUMMARY {"job":"pc1-helper-read","status":"done"}'
|
||||
exit 0
|
||||
47
tools/ca3-v4-amend/pc1-publish-20261007.sh
Executable file
47
tools/ca3-v4-amend/pc1-publish-20261007.sh
Executable file
|
|
@ -0,0 +1,47 @@
|
|||
#!/usr/bin/env bash
|
||||
# The PC 1 queue of 7 October 2026 (Counter ASIC 3.0, the hash lane; main's orders through the coordinator): the exact publish
|
||||
# lines, one job at a time under the PC 1 rule (each job's closing report read before the next; nothing published before the
|
||||
# coordinator relays the shipper's "PC 1 released" line). PC 1 = machine ae432dc7 (RTX 5090, RTX 5080 on the dock, RTX 4070,
|
||||
# RX 9070 XT amd:gfx1201). Every measured card alone through the RUNNER's --cards-off (the app's own card path, restored on any
|
||||
# exit); the other cards mining on; the installed app never quit, paused, resumed or updated; the desk usable.
|
||||
# tools/ca3-v4-amend/pc1-publish-20261007.sh <step> [--deploy] steps: kit family-kit eff-5090 eff-5090-floor eff-5090-floor2 eff-5080 ca4-exe ca4-sparse ca4-mb-exe ca4-mb ca4-packs-kit ca4-packs helper-diag2 v5-kit v5-amd v5-intel amd-g1 family tune-5080 tune-9070
|
||||
# tools/ca3-v4-amend/pc1-publish-20261007.sh read <job id> the closing report (node tools/jobs.mjs <id> --all)
|
||||
# Kits (built 7 October 2026, 17:5x UTC; the exes cross-compiled on build-1 with mingw from this tree):
|
||||
# igneum-ca3-v4-sub3-pc1-kit.zip sha256 1d441f5a075d72b1b902d23941e57b0fa4bee08f91a14b10a7a545aaac58c413 (896,071 bytes, 167 files)
|
||||
# igneum-ca3-pc1-amd-family-kit-20261007.zip sha256 9dd82b11ed08ff9270ac7d59f634eaf99267b1bb48a9ae9fdfc0ce7fe41e63ef (39,399 bytes, 3 files)
|
||||
# The efficiency passes are NOT elevated (the 18:31Z run exit 251: the UAC prompt went unanswered); the clock lock goes through the
|
||||
# installed app's registered Igneum Power Helper task (no prompt); ids -b because a machine runs an id once.
|
||||
# Order (main, 7 October 2026 evening): eff-5090, eff-5080 (the class v4 efficiency pass; its clock grid carries the four Ember
|
||||
# caps, so the owed "5090 clock rows" fall out of it), amd-g1 (G1 + the AMD ladder on the sub-version 3 kit), family (item 6
|
||||
# on AMD), tune-5080 (the full Ember Tune on the 5080), tune-9070 (the 9070 XT tune pass). The dr736 derive job is NOT in this
|
||||
# queue: its packs are not in this tree (the 6 October kit carried them from branch ca3-pc1-amd).
|
||||
set -euo pipefail
|
||||
ROOT=$(cd "$(dirname "$0")/../.." && pwd); cd "$ROOT"
|
||||
KITS="${IGNEUM_PC1_KITS:-${TMPDIR:-/tmp}}"
|
||||
P=packaging/ota/publish-jobs.sh
|
||||
STEP="${1:-}"; shift || true
|
||||
DEPLOY=""; for a in "$@"; do [ "$a" = "--deploy" ] && DEPLOY="--deploy"; done
|
||||
case "$STEP" in
|
||||
kit) $P add --kind fetch --target ae432dc7 --id fetch-ca3-v4-sub3-pc1-20261007 --file "$KITS/igneum-ca3-v4-sub3-pc1-kit.zip" --dir jobs --extract --title "CA3 PC 1: the sub-version 3 kit (worker, 13 packs)" --expires-hours 36 $DEPLOY ;;
|
||||
family-kit) $P add --kind fetch --target ae432dc7 --id fetch-ca3-pc1-amd-family-20261007 --file "$KITS/igneum-ca3-pc1-amd-family-kit-20261007.zip" --dir jobs --extract --title "CA3 PC 1: the family probe kit (7 Oct)" --expires-hours 36 $DEPLOY ;;
|
||||
eff-5090) $P add --kind run --target ae432dc7 --id run-ca3-pc1-v4-eff-5090-20261007-b --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-v4-efficiency.ps1 --timeout-minutes 75 --title "CA3 PC 1: class v4 efficiency pass on the RTX 5090 (clock locks, v4 against v3, the card alone)" --expires-hours 36 $DEPLOY ;;
|
||||
eff-5090-floor) $P add --kind run --target ae432dc7 --id run-ca3-pc1-v4-eff-5090-floor-20261007 --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-v4-efficiency.ps1 --timeout-minutes 60 --title "CA3 PC 1: class v4 efficiency pass 2 on the RTX 5090 (1,400 MHz down to the driver floor, v4 against v3, the card alone)" --expires-hours 36 $DEPLOY ;;
|
||||
ca4-exe) $P add --kind fetch --target ae432dc7 --id fetch-ca4-sparse-exe-20261007 --file "$KITS/ca4/igneum-worker-cuda-ca4sparse.exe" --dir jobs --title "CA4 research: the SM-sparse worker exe (scratch build, counter-asic-4)" --expires-hours 36 $DEPLOY ;;
|
||||
ca4-sparse) $P add --kind run --target ae432dc7 --id run-ca4-pc1-ca4sparse-5090-20261007 --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-v4-efficiency.ps1 --timeout-minutes 70 --title "CA4 research: SM-sparse worker variants on the RTX 5090, unlocked and at the 1,300 MHz knee, v4 and v3 (the card alone)" --expires-hours 36 $DEPLOY ;;
|
||||
eff-5090-floor2) $P add --kind run --target ae432dc7 --id run-ca3-pc1-v4-eff-5090-floor2-20261007 --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-v4-efficiency.ps1 --timeout-minutes 45 --title "CA3 PC 1: class v4 efficiency pass 3 on the RTX 5090 (1,100 MHz down to the driver floor, v4 against v3, the card alone)" --expires-hours 36 $DEPLOY ;;
|
||||
eff-5080) $P add --kind run --target ae432dc7 --id run-ca3-pc1-v4-eff-5080-20261007-c --cards-off "nvidia:NVIDIA GeForce RTX 5080" --script tools/ca3-v4-amend/pc1-v4-efficiency.ps1 --timeout-minutes 75 --title "CA3 PC 1: class v4 efficiency pass on the RTX 5080 (clock locks, v4 against v3, the card alone)" --expires-hours 36 $DEPLOY ;;
|
||||
helper-diag2) $P add --kind run --target ae432dc7 --id run-ca3-pc1-helper-diag2-20261007 --script tools/ca3-v4-amend/pc1-helper-diag2.ps1 --timeout-minutes 5 --title "PC 1: Power Helper diagnostic 2 (the task exe against the running app, scheduler history, crash log, a 20 s unelevated helper probe)" --expires-hours 12 $DEPLOY ;;
|
||||
ca4-mb-exe) $P add --kind fetch --target ae432dc7 --id fetch-ca4-microbench-exe-20261007 --file "$KITS/ca4/igneum-worker-cuda-ca4mb.exe" --dir jobs --title "CA4 research: the microbench worker exe (scratch build)" --expires-hours 36 $DEPLOY ;;
|
||||
ca4-mb) $P add --kind run --target ae432dc7 --id run-ca4-pc1-microbench-5090-20261007 --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-ca4-microbench.ps1 --timeout-minutes 60 --title "CA4 research: per-block micro-benchmark on the RTX 5090, unlocked and at the 1,300 MHz knee (the card alone)" --expires-hours 36 $DEPLOY ;;
|
||||
ca4-packs-kit) $P add --kind fetch --target ae432dc7 --id fetch-ca4-packs-20261007 --file "$KITS/igneum-ca4-packs-20261007.zip" --dir jobs --extract --title "CA4 research: the two prototype classes' packs (both per-load exports) plus the mx8 control\" --expires-hours 36 $DEPLOY ;;
|
||||
ca4-packs) $P add --kind run --target ae432dc7 --id run-ca4-pc1-packs-5090-20261007 --cards-off "nvidia:NVIDIA GeForce RTX 5090" --script tools/ca3-v4-amend/pc1-ca4-packs.ps1 --timeout-minutes 60 --title "CA4 research: the per-load shadow and the int8 tile prototypes against mx8 and sh256x27 on the RTX 5090 (the card alone)" --expires-hours 36 $DEPLOY ;;
|
||||
v5-kit) $P add --kind fetch --target ae432dc7,1ccfe586 --id fetch-ca3-v5-kit-20261007 --file "$KITS/v5kit/packs-ca3-v5-20261007T221001Z.zip" --dir jobs --extract --title "Class v5 kit (the v5 lane, 7f58af97): packs, workers, SHA256SUMS" --expires-hours 36 $DEPLOY ;;
|
||||
v5-amd) $P add --kind run --target ae432dc7 --id run-ca3-pc1-v5-amd-bench-20261007 --script tools/ca3-v4-amend/pc1-v5-amd-bench-20261007.ps1 --timeout-minutes 12 --title "Class v5 kit fingerprint on the RX 9070 XT (beside the miners; the v5 lane's script)" --expires-hours 36 $DEPLOY ;;
|
||||
v5-intel) $P add --kind run --target 1ccfe586 --id run-ca3-pc2-v5-intel-bench-20261007 --script tools/ca3-v4-amend/pc1-v5-intel-bench-20261007.ps1 --timeout-minutes 12 --title "Class v5 kit fingerprint on PC 2's Arc B580 eGPU (the v5 lane's script, class-v5 a4b08245; beside the miners; only on the shipper's PC 2 clear)" --expires-hours 36 $DEPLOY ;;
|
||||
amd-g1) $P add --kind run --target ae432dc7 --id run-ca3-pc1-v4-sub3-amd-g1-20261007 --cards-off amd:gfx1201 --script tools/ca3-v4-amend/pc1-v4-sub3-amd-g1.ps1 --timeout-minutes 45 --title "CA3 PC 1: G1 on the sub-version 3 kit and the AMD ladder on the 9070 XT (the card alone)" --expires-hours 36 $DEPLOY ;;
|
||||
family) $P add --kind run --target ae432dc7 --id run-ca3-pc1-amd-family-20261007-e --cards-off amd:gfx1201 --script tools/ca3-v4-amend/pc1-amd-family-20261007.ps1 --timeout-minutes 20 --title "CA3 PC 1: item 6 family step costs on the 9070 XT (run e, the card alone)" --expires-hours 36 $DEPLOY ;;
|
||||
tune-5080) $P add --kind run --target ae432dc7 --id run-ca3-pc1-ember-5080-20261007 --script tools/ca3-v4-amend/pc1-ember-card.ps1 --timeout-minutes 35 --title "CA3 PC 1: Ember Tune on the RTX 5080 (the installed app tunes the one card)" --expires-hours 36 $DEPLOY ;;
|
||||
tune-9070) $P add --kind run --target ae432dc7 --id run-ca3-pc1-ember-9070-20261007 --script tools/ca3-v4-amend/pc1-ember-card.ps1 --timeout-minutes 35 --title "CA3 PC 1: Ember Tune pass on the RX 9070 XT (set or measure-only by the probe)" --expires-hours 36 $DEPLOY ;;
|
||||
read) node tools/jobs.mjs "$1" --all ;;
|
||||
*) sed -n 2,12p "$0"; exit 2 ;;
|
||||
esac
|
||||
319
tools/ca3-v4-amend/pc1-v4-efficiency.ps1
Normal file
319
tools/ca3-v4-amend/pc1-v4-efficiency.ps1
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
# Counter ASIC 3.0, PC 1: the class v4 efficiency pass (7 October 2026, main's question "profitable mining is all about
|
||||
# efficiency, is there a solution?"). Thesis under test: the latency-shadow work hides in the memory wait, so the core clock
|
||||
# (and with it the voltage along the driver's V/F curve) can drop until the work just fits the wait with no rate loss, and the
|
||||
# 5090's +80 W for class v4 at the unlocked 2,850 MHz mostly comes back. One job per card (the job id names the card: an id
|
||||
# carrying "5080" measures the RTX 5080, any other the RTX 5090), the card ALONE under the RUNNER's --cards-off (the app's own
|
||||
# card path, restored on any exit; the other cards keep mining), ELEVATED so nvidia-smi -lgc needs no prompt, no restart, the
|
||||
# desk usable. Per step: nvidia-smi -lgc <c>,<c> on the card, then the INSTALLED igneum-worker-cuda.exe --bench on the class v4
|
||||
# pack (v4-devnet-epoch0, the sub-version 3 kit) and on the class v3 control (mx8-devnet-epoch0), about 60 s each, the 2^24
|
||||
# fingerprint checked against the Mac's on every step (bit-exactness under the lock), while nvidia-smi samples power.draw,
|
||||
# clocks.sm, clocks.mem and the throttle reasons at 1 Hz (a child by pid, ended in finally). Steps: unlocked, then 2,850,
|
||||
# 2,781, 2,700, 2,550, 2,472, 2,400, 2,250, 2,163, 2,100, 1,950, 1,854, 1,800, 1,650, 1,500, 1,400 MHz (the four Ember caps
|
||||
# 2,781 / 2,472 / 2,163 / 1,854 are on the grid, so the owed "5090 clock rows" of counter-asic-3-status.md section 7 fall out of
|
||||
# this pass), then unlocked again as the drift check. The voltage lever: nvidia-smi exposes NO voltage offset (that is NVAPI,
|
||||
# Afterburner's lever); the clock lock moves the card down the driver's V/F curve, so the voltage falls with the lock and that
|
||||
# is the lever this pass has; stated on a RESULT line. Lines: `RESULT STEP card= pack= lock= sm_mhz= watts= mhs= mh_per_w=
|
||||
# fingerprint= match=`, one per step and pack; `RESULT TABLE` lines at the end (v3 best against v4 best, the recovered watts,
|
||||
# the rate lost); a `SUMMARY {json}` line. Never quits, pauses, resumes or updates the installed app; never posts to
|
||||
# /api/cards; never writes settings.json; -rgc in finally. Not elevated: the lock is refused, the unlocked rows are
|
||||
# measured and every lock row says why. The lock path when NOT elevated (the 18:31Z run: the UAC prompt went unanswered, exit 251):
|
||||
# the installed app's registered `Igneum Power Helper` task (app/igneum-app/src/powertask.rs, one approval ever): the script
|
||||
# starts it (Start-ScheduledTask by the owning user, no prompt) and talks to it through %LOCALAPPDATA%\igneum\app\sweep\cmd.txt
|
||||
# with the fixed verbs `<seq> dev <idx>`, `<seq> lgc <MHz>` (the helper runs nvidia-smi -lgc 0,<MHz>: a cap, which under load
|
||||
# is the lock), `<seq> rgc`, and reads the result from helper.log beside it; the same channel the app's own Ember tune uses,
|
||||
# so no new rights and no prompt. Kit: fetch job fetch-ca3-v4-sub3-pc1-20261007 (tools/ca3-v4-amend/pc1-publish-20261007.sh).
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-v4-efficiency'
|
||||
$kitId = 'fetch-ca3-v4-sub3-pc1-20261007'
|
||||
$started = Get-Date
|
||||
$deadline = $started.AddMinutes(66)
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) {
|
||||
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
|
||||
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||
}
|
||||
$jobDir = $env:IGNEUM_JOB_DIR; if (-not $jobDir) { $jobDir = Join-Path $env:TEMP 'igneum-ca3-pc1-eff' }
|
||||
New-Item -ItemType Directory -Force -Path $jobDir | Out-Null
|
||||
$jobId = Split-Path -Leaf $jobDir
|
||||
$cardName = if ($jobId -match '5080') { 'NVIDIA GeForce RTX 5080' } else { 'NVIDIA GeForce RTX 5090' }
|
||||
$expected = @{ 'mx8-devnet-epoch0' = '90f794dd556f7a3b'; 'v4-devnet-epoch0' = 'e370fb2080b7dbb1' }
|
||||
$packOrder = @('v4-devnet-epoch0', 'mx8-devnet-epoch0')
|
||||
# the grid by the job id: an id carrying "floor" runs the second pass, 1,400 MHz down to the driver's floor in 100 MHz steps (the
|
||||
# first pass found the best MH/W at its lowest lock, 1,400, so the knee sits below it); a lock the driver clamps shows in sm_mhz
|
||||
# the counter-asic-4 research mode (an id carrying "ca4sparse"): the research lane's SM-sparse worker variants (proto-cuda/nvrtc/worker.cpp on
|
||||
# branch counter-asic-4: `--variant sp<N>-w<W>` runs the pack's bound kernel as a persistent wrapper of N blocks of W warps, about N SMs busy,
|
||||
# the rest idle) on both packs at the unlocked clock and at the 1,300 MHz knee, 250 batches each, beside the base shape; the exe comes from
|
||||
# its own fetch job (fetch-ca4-sparse-exe-20261007, the scratch exe igneum-worker-cuda-ca4sparse.exe, sha256 ee8d0e70...); the rows carry
|
||||
# label=ca4-research and the worker's race line verbatim; nothing in any pack changes, the fingerprint is checked on every row
|
||||
$ca4 = $jobId -match 'ca4sparse'
|
||||
$variants = if ($ca4) { @('base', 'sp170-w32', 'sp85-w32', 'sp43-w32', 'sp21-w32', 'sp11-w32') } else { @('base') }
|
||||
$locks = if ($ca4) { @(1300) } elseif ($jobId -match '5080') { @(2850, 2781, 2700, 2550, 2472, 2400, 2250, 2163, 2100, 1950, 1854, 1800, 1650, 1500, 1400, 1300, 1200, 1100, 1000, 900, 800, 700, 600, 500, 400, 300) } elseif ($jobId -match 'floor2') { @(1100, 1000, 900, 800, 700, 600, 500, 400, 300) } elseif ($jobId -match 'floor') { @(1400, 1300, 1200, 1100, 1000, 900, 800, 700, 600, 500, 400, 300) } else { @(2850, 2781, 2700, 2550, 2472, 2400, 2250, 2163, 2100, 1950, 1854, 1800, 1650, 1500, 1400) }
|
||||
$stepSeconds = 60
|
||||
"RESULT start $(Stamp) job=$jobName job_id=$jobId card=$cardName grid=[$($locks -join ' ')] machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
|
||||
'RESULT lever voltage: nvidia-smi exposes no voltage offset (NVAPI only); the clock lock (-lgc) moves the card along the driver V/F curve, so the voltage falls with the lock; the power cap (-pl) is the other driver lever and is not moved here'
|
||||
$elevated = $false
|
||||
try { $elevated = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } catch { }
|
||||
"RESULT elevated=$elevated"
|
||||
|
||||
# ---- the kit (the wiped-jobs-folder rule: tested before use) ----
|
||||
$jobs = Split-Path $jobDir
|
||||
$kit = Join-Path $jobs $kitId
|
||||
$packs = Join-Path $kit 'packs'
|
||||
if (-not (Test-Path $packs)) { "RESULT error packs missing at $packs (the fetch job $kitId runs first; republish it after any app update)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
|
||||
foreach ($pk in $packOrder) { $d = Join-Path $packs $pk; if (-not (Test-Path $d)) { "RESULT error pack missing at $d"; Summary 'failed' @{ error = "pack $pk missing" }; exit 2 }; foreach ($f in (Get-ChildItem $d -File | Sort-Object Name)) { "RESULT kitfile $pk/$($f.Name) sha256 $((Get-FileHash -Algorithm SHA256 $f.FullName).Hash.ToLower())" } }
|
||||
|
||||
# ---- the installed worker (NVRTC, the pack's own kernel text) ----
|
||||
$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) { 'RESULT error no installed igneum-worker-cuda.exe'; Summary 'failed' @{ error = 'no worker' }; exit 2 }
|
||||
$exe = Join-Path $inst 'igneum-worker-cuda.exe'
|
||||
if ($ca4) {
|
||||
$exe = Join-Path (Join-Path $jobs 'fetch-ca4-sparse-exe-20261007') 'igneum-worker-cuda-ca4sparse.exe'
|
||||
if (-not (Test-Path $exe)) { "RESULT error the research worker is missing at $exe (the fetch job fetch-ca4-sparse-exe-20261007 runs first)"; Summary 'failed' @{ error = 'research worker missing' }; exit 2 }
|
||||
}
|
||||
"RESULT worker $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) bytes $((Get-Item $exe).Length)"
|
||||
$appExe = Join-Path $inst 'igneum-app.exe'
|
||||
if (Test-Path $appExe) { try { "RESULT app-version $((& $appExe --version 2>&1 | Out-String).Trim())" } catch { } }
|
||||
|
||||
# ---- nvidia-smi and the card ----
|
||||
$smi = $null
|
||||
$c = Get-Command nvidia-smi -ErrorAction SilentlyContinue; if ($c) { $smi = $c.Source }
|
||||
if (-not $smi) { foreach ($p in @((Join-Path $env:ProgramFiles 'NVIDIA Corporation\NVSMI\nvidia-smi.exe'), (Join-Path $env:SystemRoot 'System32\nvidia-smi.exe'))) { if (Test-Path $p) { $smi = $p; break } } }
|
||||
if (-not $smi) { 'RESULT error nvidia-smi not found'; Summary 'failed' @{ error = 'no nvidia-smi' }; exit 2 }
|
||||
$gpus = @(& $smi --query-gpu=index,name,uuid,pci.bus_id,power.limit,power.max_limit,clocks.max.sm,driver_version --format=csv,noheader 2>&1 | ForEach-Object { "$_" })
|
||||
$gpus | ForEach-Object { "RESULT gpu $_" }
|
||||
$row = $gpus | Where-Object { $_ -match [regex]::Escape($cardName) } | Select-Object -First 1
|
||||
if (-not $row) { "RESULT error card '$cardName' is not in nvidia-smi's list"; Summary 'failed' @{ error = 'card missing' }; exit 2 }
|
||||
$fld = $row -split ',\s*'
|
||||
$idx = $fld[0].Trim(); $uuid = $fld[2].Trim(); $bus = $fld[3].Trim(); $maxSm = $fld[6].Trim()
|
||||
"RESULT card index=$idx uuid=$uuid bus=$bus max_sm_mhz=$maxSm"
|
||||
|
||||
# ---- the card quiet (the RUNNER's --cards-off took): no igneum compute app on this bus id ----
|
||||
function BusApps { @(& $smi --query-compute-apps=gpu_bus_id,pid,process_name --format=csv,noheader 2>$null | ForEach-Object { "$_" } | Where-Object { $_ -match [regex]::Escape($bus) -and $_ -match 'igneum' }) }
|
||||
$t = 0; $apps = @(BusApps)
|
||||
while ($t -lt 150 -and $apps.Count -gt 0) { Start-Sleep -Seconds 5; $t += 5; $apps = @(BusApps) }
|
||||
$alone = ($apps.Count -eq 0)
|
||||
if ($alone) { "RESULT card $(Stamp) quiet after $t s: no igneum compute app on $bus (the runner's --cards-off took)" }
|
||||
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: $($apps -join '; ') (was the job published with --cards-off?); the rows below are LOADED-card figures" }
|
||||
|
||||
# ---- the sampler: nvidia-smi at 1 Hz on this card, a child by pid, its file read per step ----
|
||||
$samples = Join-Path $jobDir 'nv-samples.csv'
|
||||
if (Test-Path $samples) { Remove-Item -LiteralPath $samples -Force }
|
||||
$sampler = $null
|
||||
$rows = @()
|
||||
$lockState = 'unlocked'
|
||||
function ReadWindow([datetime] $t0, [datetime] $t1) {
|
||||
$sel = @()
|
||||
if (Test-Path $samples) {
|
||||
foreach ($l in (Get-Content -LiteralPath $samples -ErrorAction SilentlyContinue)) {
|
||||
$p = $l -split ',\s*'
|
||||
if ($p.Count -lt 5) { continue }
|
||||
$ts = $null
|
||||
try { $ts = [datetime]::ParseExact($p[0].Trim(), 'yyyy/MM/dd HH:mm:ss.fff', [cultureinfo]::InvariantCulture) } catch { continue }
|
||||
if ($ts -ge $t0 -and $ts -le $t1) { $sel += ,$p }
|
||||
}
|
||||
}
|
||||
return ,$sel
|
||||
}
|
||||
function Mean($vals) { $v = @($vals | ForEach-Object { [double]$_ }); if ($v.Count -eq 0) { return $null }; return [math]::Round((($v | Measure-Object -Sum).Sum / $v.Count), 1) }
|
||||
# ---- the Power Helper task (the lock path without a prompt) ----
|
||||
$helperDir = Join-Path $env:LOCALAPPDATA 'igneum\app\sweep'
|
||||
$cmdFile = Join-Path $helperDir 'cmd.txt'
|
||||
$helperLog = Join-Path $helperDir 'helper.log'
|
||||
# the 0.3.20 helper also skips the first N lines of cmd.txt, N = the line count when it started (fixed in 0.3.21, a64c193f): four
|
||||
# leading `dev` padding lines with climbing numbers absorb that skip (the engine writes 4-line files, so N is at most 4), the real
|
||||
# dev and the command stand after them; never pad after the command (update-return lane, 7 October 2026, 22:00Z)
|
||||
# the helper skips any sequence number at or under the last one it ran (powertask.rs: `seq <= last_seq`), and the installed app's
|
||||
# own Ember tune writes the same file with its own rising numbers while it tunes another card (the floor pass of 20:21Z: the app
|
||||
# tuned the 5080 at seqs 423596 to 423598 while this script wrote 419500 to 419527, so every lock from 1,000 MHz down and the
|
||||
# closing reset were skipped as stale and the card stayed locked); the base is therefore the maximum over cmd.txt AND helper.log,
|
||||
# re-read after every timeout
|
||||
function HelperSeqBase() {
|
||||
$m = 0
|
||||
foreach ($f in @($cmdFile, $helperLog)) { if (Test-Path $f) { foreach ($l in (Get-Content -LiteralPath $f -ErrorAction SilentlyContinue)) { if ($l -match '(?:^|\s)(\d{1,6}) (?:dev|pl|lgc|rgc|lmc|rmc|nvidia-smi|reregister)\b') { $v = [int]$Matches[1]; if ($v -gt $m) { $m = $v } } } } }
|
||||
return $m + 100
|
||||
}
|
||||
$viaHelper = $false
|
||||
$seq = 0
|
||||
if (-not $elevated) {
|
||||
$task = Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue
|
||||
if ($task) {
|
||||
try {
|
||||
$seq = HelperSeqBase
|
||||
Start-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction Stop
|
||||
Start-Sleep -Seconds 3
|
||||
$viaHelper = $true
|
||||
"RESULT helper task 'Igneum Power Helper' state=$((Get-ScheduledTask -TaskName 'Igneum Power Helper').State) started; commands through $cmdFile from seq $seq; results from $helperLog"
|
||||
} catch { "RESULT helper task present but Start-ScheduledTask failed: $_" }
|
||||
} else { 'RESULT helper task NOT REGISTERED (Power control off in the app): no lock path without a prompt' }
|
||||
}
|
||||
function HelperSend([string] $verb) {
|
||||
$script:seq += 2
|
||||
$s0 = $script:seq; $s1 = $script:seq + 1; $script:seq = $s1
|
||||
$mark = 0; if (Test-Path $helperLog) { $mark = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue).Count }
|
||||
$pad = ((0..3) | ForEach-Object { "$($s0 - 8 + 2 * $_) dev $idx" }) -join "`n"; Set-Content -LiteralPath $cmdFile -Value "$pad`n$s0 dev $idx`n$s1 $verb`n" -NoNewline -Encoding ascii
|
||||
$t = 0
|
||||
while ($t -lt 30) {
|
||||
Start-Sleep -Seconds 1; $t++
|
||||
$new = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue)
|
||||
if ($new.Count -gt $mark) {
|
||||
$hit = $new[$mark..($new.Count - 1)] | Where-Object { $_ -match "\b$s1 nvidia-smi " } | Select-Object -First 1
|
||||
if ($hit) { return ($hit -replace '^\d+\s+', '') }
|
||||
}
|
||||
}
|
||||
# a timeout: another writer may have raised the helper's sequence past ours; re-base once and send again
|
||||
$base = HelperSeqBase
|
||||
if ($base -gt $script:seq) {
|
||||
"RESULT helper seq re-based from $script:seq to $base (another writer, the app's own tune, raised the helper's sequence)"
|
||||
$script:seq = $base
|
||||
$s0 = $script:seq; $s1 = $script:seq + 1; $script:seq = $s1
|
||||
$mark = 0; if (Test-Path $helperLog) { $mark = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue).Count }
|
||||
$pad = ((0..3) | ForEach-Object { "$($s0 - 8 + 2 * $_) dev $idx" }) -join "`n"; Set-Content -LiteralPath $cmdFile -Value "$pad`n$s0 dev $idx`n$s1 $verb`n" -NoNewline -Encoding ascii
|
||||
$t = 0
|
||||
while ($t -lt 30) {
|
||||
Start-Sleep -Seconds 1; $t++
|
||||
$new = @(Get-Content -LiteralPath $helperLog -ErrorAction SilentlyContinue)
|
||||
if ($new.Count -gt $mark) { $hit = $new[$mark..($new.Count - 1)] | Where-Object { $_ -match "\b$s1 nvidia-smi " } | Select-Object -First 1; if ($hit) { return ($hit -replace '^\d+\s+', '') } }
|
||||
}
|
||||
}
|
||||
return "no helper.log line for seq $s1 within 30 s (is the task running?)"
|
||||
}
|
||||
function SetLock([int] $mhz) {
|
||||
if ($elevated) {
|
||||
$out = (& $smi -i $idx -lgc "$mhz,$mhz" 2>&1 | Out-String).Trim() -replace '\s+', ' '
|
||||
if ($out -match 'not have permission|No permission|Insufficient|not supported|Unable|ERROR') { return "refused: $out" }
|
||||
return "ok (direct): $out"
|
||||
}
|
||||
if ($viaHelper) {
|
||||
$out = HelperSend "lgc $mhz"
|
||||
if ($out -match 'not have permission|No permission|Insufficient|not supported|Unable|ERROR|no helper.log') { return "refused: $out" }
|
||||
return "ok (helper, -lgc 0,$mhz): $out"
|
||||
}
|
||||
return 'refused: not elevated and no helper task'
|
||||
}
|
||||
function RestartHelper() {
|
||||
# the floor pass of 20:21Z lost the helper at its 1,000 MHz step (no helper.log answer for that and every later command, the
|
||||
# lock most likely hanging inside the helper's nvidia-smi call) and its closing reset never ran: before a reset the task is
|
||||
# stopped and started again (the owning user may do both without a prompt), so a stuck helper never holds the card locked
|
||||
try { Stop-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue } catch { }
|
||||
Start-Sleep -Seconds 3
|
||||
try { Start-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction Stop; Start-Sleep -Seconds 3; return $true } catch { "RESULT helper restart failed: $_"; return $false }
|
||||
}
|
||||
function AppClock() { $a = @(& $smi -i $idx --query-gpu=clocks.applications.graphics --format=csv,noheader 2>&1 | ForEach-Object { "$_" }) | Select-Object -First 1; return "$a" }
|
||||
function ResetClocks() {
|
||||
if ($elevated) { return ((& $smi -i $idx -rgc 2>&1 | Out-String).Trim() -replace '\s+', ' ') }
|
||||
if ($viaHelper) {
|
||||
$out = HelperSend 'rgc'
|
||||
if ($out -match 'no helper.log') { if (RestartHelper) { $out = "after a task restart: $(HelperSend 'rgc')" } }
|
||||
return "$out ; applications clock now: $(AppClock)"
|
||||
}
|
||||
return 'not attempted (not elevated, no helper)'
|
||||
}
|
||||
function Bench([string] $pk, [int] $n, [string] $variant = 'base') {
|
||||
$d = Join-Path $packs $pk
|
||||
$env:CUDA_VISIBLE_DEVICES = $uuid
|
||||
$t0 = Get-Date
|
||||
$extra = @(); if ($variant -ne 'base') { $extra = @('--block-warps', '32', '--variant', $variant) } else { $extra = @('--block-warps', '1') }
|
||||
$lines = @(& $exe --bench --pack $d --batches $n --batch-log2 24 @extra --device 0 2>&1 | ForEach-Object { "$_" })
|
||||
$race = (($lines | Where-Object { $_ -match 'race' }) -join ' / ') -replace '\s+', ' '
|
||||
$rc = $LASTEXITCODE
|
||||
$t1 = Get-Date
|
||||
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
|
||||
$fp = ''; $mhs = $null; $check = ''; $dev = ''
|
||||
if ($res -and $res -match 'device=(.*?) arch=') { $dev = $Matches[1] }
|
||||
if ($res -and $res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
|
||||
if ($res -and $res -match 'mhs=([\d.]+)') { $mhs = [double]$Matches[1] }
|
||||
if ($res -and $res -match 'check=(\S+)') { $check = $Matches[1] }
|
||||
$err = ''
|
||||
if (-not $res -or $rc -ne 0) { $e = ($lines | Where-Object { $_ -match 'FAIL|error|MISMATCH' } | Select-Object -First 1); $err = if ($e) { ($e -replace '\s+', ' ') } else { "exit $rc, no RESULT line" } }
|
||||
return [pscustomobject]@{ pack = $pk; t0 = $t0; t1 = $t1; rc = $rc; fp = $fp; mhs = $mhs; check = $check; err = $err; res = $res; batches = $n; dev = $dev; variant = $variant; race = $race }
|
||||
}
|
||||
function Step([string] $label, [string] $lockNote) {
|
||||
foreach ($pk in $packOrder) { foreach ($variant in $variants) {
|
||||
if ((Get-Date) -gt $deadline) { "RESULT STEP card=$cardName pack=$pk lock=$label variant=$variant error=budget spent"; continue }
|
||||
$n = if ($ca4) { 250 } else { $nb[$pk] }
|
||||
$b = Bench $pk $n $variant
|
||||
if ($b.err) { "RESULT STEP card=$cardName pack=$pk lock=$label variant=$variant error=$($b.err) lock_note=$lockNote race=[$($b.race)]"; continue }
|
||||
$w = ReadWindow $b.t0.AddSeconds(8) $b.t1
|
||||
$watts = Mean ($w | ForEach-Object { $_[1] })
|
||||
$sm = Mean ($w | ForEach-Object { $_[2] })
|
||||
$mem = Mean ($w | ForEach-Object { $_[3] })
|
||||
$tmax = ($w | ForEach-Object { [int]$_[4] } | Measure-Object -Maximum).Maximum
|
||||
$thr = ($w | ForEach-Object { $_[5].Trim() } | Group-Object | Sort-Object Count -Descending | Select-Object -First 1).Name
|
||||
$wInst = Mean ($w | Where-Object { $_.Count -ge 8 -and $_[6] -match '^\s*[\d.]+\s*$' } | ForEach-Object { $_[6] })
|
||||
$wAvg = Mean ($w | Where-Object { $_.Count -ge 8 -and $_[7] -match '^\s*[\d.]+\s*$' } | ForEach-Object { $_[7] })
|
||||
$eff = if ($watts -and $watts -gt 0) { [math]::Round($b.mhs / $watts, 4) } else { $null }
|
||||
$want = $expected[$pk]
|
||||
$match = if ($want -and $b.fp -eq $want) { 'yes' } elseif ($want) { 'no' } else { 'unknown' }
|
||||
$wall = [int]($b.t1 - $b.t0).TotalSeconds
|
||||
$lab = if ($ca4) { 'ca4-research' } else { 'efficiency' }
|
||||
"RESULT STEP card=$cardName pack=$pk lock=$label variant=$variant label=$lab sm_mhz=$sm mem_mhz=$mem watts=$watts watts_instant=$wInst watts_average=$wAvg temp_max=$tmax throttle=$thr mhs=$($b.mhs) mh_per_w=$eff fingerprint=$($b.fp) match=$match expected=$want check=$($b.check) batches=$n wall_s=$wall samples=$($w.Count) device=$($b.dev) lock_note=$lockNote alone=$alone race=[$($b.race)]"
|
||||
if ($variant -eq 'base') { $script:rows += [pscustomobject]@{ pack = $pk; lock = $label; sm = $sm; watts = $watts; mhs = $b.mhs; eff = $eff; match = $match } }
|
||||
} }
|
||||
}
|
||||
try {
|
||||
$sampler = Start-Process -FilePath $smi -ArgumentList @('-i', $idx, '--query-gpu=timestamp,power.draw,clocks.sm,clocks.mem,temperature.gpu,clocks_throttle_reasons.active,power.draw.instant,power.draw.average', '--format=csv,noheader,nounits', '-lms', '1000') -RedirectStandardOutput $samples -WindowStyle Hidden -PassThru
|
||||
Start-Sleep -Seconds 6
|
||||
$first = @(Get-Content -LiteralPath $samples -ErrorAction SilentlyContinue | Select-Object -First 2)
|
||||
"RESULT sampler pid=$($sampler.Id) first=[$($first -join ' | ')]"
|
||||
if ($first.Count -eq 0) { 'RESULT sampler error=no samples in 6 s; watts will read empty on every row' }
|
||||
# the unlocked start: -rgc so the baseline is the driver's own choice, then the batch counts from a short probe per pack
|
||||
$r0 = ResetClocks
|
||||
"RESULT unlock start $r0"
|
||||
$nb = @{}
|
||||
foreach ($pk in $packOrder) {
|
||||
$p = Bench $pk 5
|
||||
if ($p.err) { "RESULT probe pack=$pk error=$($p.err)"; $nb[$pk] = 40 } else {
|
||||
$n = [int][math]::Ceiling($stepSeconds * $p.mhs * 1e6 / 16777216.0)
|
||||
$nb[$pk] = [math]::Max(20, [math]::Min(2000, $n))
|
||||
"RESULT probe pack=$pk mhs=$($p.mhs) fingerprint=$($p.fp) batches_for_${stepSeconds}s=$($nb[$pk])"
|
||||
}
|
||||
}
|
||||
Step 'unlocked' 'driver default (-rgc)'
|
||||
if (-not $elevated -and -not $viaHelper) {
|
||||
'RESULT locks skipped: the job is not elevated and the Power Helper task is not registered; the unlocked rows above are the measurement'
|
||||
foreach ($c in $locks) { foreach ($pk in $packOrder) { "RESULT STEP card=$cardName pack=$pk lock=$c error=no lock path (not elevated, no helper task)" } }
|
||||
} else {
|
||||
foreach ($c in $locks) {
|
||||
if ((Get-Date) -gt $deadline) { "RESULT STEP card=$cardName pack=* lock=$c error=budget spent"; continue }
|
||||
$note = SetLock $c
|
||||
$lockState = "locked $c"
|
||||
if ($note -match 'no helper.log') { foreach ($pk in $packOrder) { "RESULT STEP card=$cardName pack=$pk lock=$c error=lock $note" }; "RESULT locks stopped at $c MHz: the helper gave no answer (a lock under the driver's floor hangs its nvidia-smi call); the remaining locks are skipped and the task is restarted for the reset"; RestartHelper | Out-Null; break }
|
||||
if ($note -match '^refused') { foreach ($pk in $packOrder) { "RESULT STEP card=$cardName pack=$pk lock=$c error=lock $note" }; if ($c -eq $locks[0]) { 'RESULT locks refused on the first step: the remaining locks are skipped'; break } else { continue } }
|
||||
Start-Sleep -Seconds 3
|
||||
Step "$c" $note
|
||||
}
|
||||
$r1 = ResetClocks
|
||||
$lockState = 'unlocked'
|
||||
"RESULT unlock end $r1"
|
||||
Start-Sleep -Seconds 3
|
||||
Step 'unlocked-end' 'driver default (-rgc), the drift check'
|
||||
}
|
||||
# ---- the table ----
|
||||
$tab = @{}
|
||||
foreach ($pk in $packOrder) {
|
||||
$mine = @($rows | Where-Object { $_.pack -eq $pk -and $_.mhs })
|
||||
$un = $mine | Where-Object { $_.lock -eq 'unlocked' } | Select-Object -First 1
|
||||
$withW = @($mine | Where-Object { $_.eff })
|
||||
$best = $withW | Sort-Object eff -Descending | Select-Object -First 1
|
||||
$tab[$pk] = @{ un = $un; best = $best }
|
||||
if ($un -and $best) {
|
||||
$rec = [math]::Round($un.watts - $best.watts, 1)
|
||||
$lost = if ($un.mhs -gt 0) { [math]::Round(100.0 * ($un.mhs - $best.mhs) / $un.mhs, 2) } else { $null }
|
||||
"RESULT TABLE card=$cardName pack=$pk unlocked_sm_mhz=$($un.sm) unlocked_mhs=$($un.mhs) unlocked_w=$($un.watts) unlocked_mh_per_w=$($un.eff) best_lock=$($best.lock) best_sm_mhz=$($best.sm) best_mhs=$($best.mhs) best_w=$($best.watts) best_mh_per_w=$($best.eff) recovered_w=$rec rate_lost_pct=$lost fingerprints_all_match=$((@($mine | Where-Object { $_.match -ne 'yes' })).Count -eq 0)"
|
||||
} else { "RESULT TABLE card=$cardName pack=$pk error=no unlocked row or no row with watts" }
|
||||
}
|
||||
$u4 = $tab['v4-devnet-epoch0'].un; $u3 = $tab['mx8-devnet-epoch0'].un; $b4 = $tab['v4-devnet-epoch0'].best; $b3 = $tab['mx8-devnet-epoch0'].best
|
||||
if ($u4 -and $u3 -and $b4 -and $b3) {
|
||||
"RESULT TABLE card=$cardName v3_vs_v4 v4_extra_w_unlocked=$([math]::Round($u4.watts - $u3.watts, 1)) v4_extra_w_at_best=$([math]::Round($b4.watts - $b3.watts, 1)) v4_rate_vs_v3_unlocked_pct=$([math]::Round(100.0 * ($u4.mhs - $u3.mhs) / $u3.mhs, 2)) v4_rate_vs_v3_at_best_pct=$([math]::Round(100.0 * ($b4.mhs - $b3.mhs) / $b3.mhs, 2)) v4_best_lock=$($b4.lock) v3_best_lock=$($b3.lock)"
|
||||
}
|
||||
Summary 'done' @{ card = $cardName; elevated = $elevated; via_helper = $viaHelper; alone = $alone; steps = $rows.Count; mismatches = (@($rows | Where-Object { $_.match -eq 'no' })).Count }
|
||||
exit 0
|
||||
} finally {
|
||||
try {
|
||||
$r = ResetClocks; "RESULT restore clocks reset: $r (was $lockState)"
|
||||
if ($viaHelper -and (AppClock) -match '^\d+ MHz' -and (AppClock) -notmatch '^(0|\[N/A\]|N/A)') { if (RestartHelper) { $r2 = HelperSend 'rgc'; "RESULT restore second reset after a task restart: $r2 ; applications clock now: $(AppClock)" } }
|
||||
} catch { "RESULT restore clocks reset failed: $_" }
|
||||
if ($sampler -and -not $sampler.HasExited) { try { Stop-Process -Id $sampler.Id -Force -ErrorAction SilentlyContinue; "RESULT restore sampler pid $($sampler.Id) stopped" } catch { } }
|
||||
"RESULT gpus_after $(Stamp) $((& $smi --query-gpu=index,name,power.draw,clocks.sm,clocks.applications.graphics --format=csv,noheader 2>&1 | Out-String).Trim() -replace "`r?`n", ' | ')"
|
||||
}
|
||||
260
tools/ca3-v4-amend/pc1-v4-sub3-amd-g1.ps1
Normal file
260
tools/ca3-v4-amend/pc1-v4-sub3-amd-g1.ps1
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
# Counter ASIC 3.0, PC 1 queue job (7 October 2026): gate G1 on the sub-version 3 kit and item 8's AMD ladder rows on PC 1's
|
||||
# RX 9070 XT (machine ae432dc7, gfx1201 on the eGPU). The shape is tools/ca3-pc1-amd/pc1-amd-g1-shadow.ps1 (6 October 2026)
|
||||
# with the kit of 7 October: fetch job fetch-ca3-v4-sub3-pc1-20261007 (bin/igneum-worker-opencl.exe from this tree's
|
||||
# proto-opencl/host.c, cross-compiled on build-1 with mingw as proto-cuda/nvrtc/build-windows.sh builds the shipped worker;
|
||||
# packs/: the class v3 control mx8-devnet-epoch0, the seven sub-version 3 class v4 packs v4-devnet-epoch0, v4-era-0 .. v4-era-5
|
||||
# (program id a785001687d8688a), and the ladder packs sh256x13, sh256x27, sh256x53, sh256x88, sh64x52 re-exported under the
|
||||
# amended stream; src/; SHA256SUMS). Published with the RUNNER's --cards-off amd:gfx1201 (the index-free key form; the runner
|
||||
# matches the live card with or without the device index and restores it on ANY exit); the 5090, 5080 and 4070 keep mining.
|
||||
# The script never touches /api/cards, never quits, pauses, resumes or updates the installed app, never writes settings.json;
|
||||
# it confirms the card quiet by the process list, then runs the kit worker --bench-pack on every pack while the AMD helper
|
||||
# samples board watts (the sampler by its own pid). Per pack: the self-test, the 2^24 fingerprint at base nonce 0 against the
|
||||
# Mac's (G1: Metal = Apple OpenCL on 7 October 2026, 17:5x UTC), and a timed rate for the ladder row (MH/s, board watts,
|
||||
# microjoules per hash). Lines: `RESULT G1 pack= fingerprint= match=`, `RESULT LADDER pack= ops= mhs= watts= uj=`, a
|
||||
# `SUMMARY {json}` line. Read back with `node tools/jobs.mjs <job id>`.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-v4-sub3-amd-g1'
|
||||
$kitId = 'fetch-ca3-v4-sub3-pc1-20261007'
|
||||
$started = Get-Date
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) {
|
||||
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
|
||||
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||
}
|
||||
# the expected 2^24 fingerprints at base nonce 0 (the Mac on Metal and Apple OpenCL, the 5090 on CUDA: status file
|
||||
# section 3, docs/analysis/latency-shadow-2026-10-06.md sections 2 and 5, counter-asic-2-status.md 22:16 for the
|
||||
# control; re-checked on Apple OpenCL through this kit's host.c on 6 October 2026, 15:2x UTC)
|
||||
$expected = @{
|
||||
'mx8-devnet-epoch0' = '90f794dd556f7a3b'
|
||||
'v4-devnet-epoch0' = 'e370fb2080b7dbb1'
|
||||
'v4-era-0' = 'b7237555d31fc3cf'
|
||||
'v4-era-1' = 'b6b167fa15dfe2c9'
|
||||
'v4-era-2' = '28bdf65eff33f2c4'
|
||||
'v4-era-3' = 'e26d38c46f3f1b16'
|
||||
'v4-era-4' = 'dd8fdf6ff4f59eed'
|
||||
'v4-era-5' = '8bf40f5cb858d835'
|
||||
'sh256x13' = 'ff8f707210659eb1'
|
||||
'sh256x27' = '892b6d55a7ddcfcb'
|
||||
'sh256x53' = '663c73c61f62cc54'
|
||||
'sh256x88' = 'ec7ca430a061fa59'
|
||||
'sh64x52' = '9dd010f79d8ca9f4'
|
||||
}
|
||||
# ops per hash as the status file counts them (x1.83 counted ops per shadow instruction + 930 for the base program);
|
||||
# the shadow instruction count is read from each pack's program.json at run time and printed beside it
|
||||
$opsPerHash = @{ 'mx8-devnet-epoch0' = 930; 'v4-devnet-epoch0' = 102100; 'v4-era-0' = 102100; 'v4-era-1' = 102100; 'v4-era-2' = 102100; 'v4-era-3' = 102100; 'v4-era-4' = 102100; 'v4-era-5' = 102100; 'sh256x13' = 49700; 'sh256x27' = 102100; 'sh256x53' = 199600; 'sh256x88' = 330700; 'sh64x52' = 49700 }
|
||||
# timed dispatches of 2^24 nonces per pack: about 0.9 s each at 19 MH/s, longer where the shadow binds the card
|
||||
$batches = @{ 'mx8-devnet-epoch0' = 90; 'v4-devnet-epoch0' = 90; 'v4-era-0' = 40; 'v4-era-1' = 40; 'v4-era-2' = 40; 'v4-era-3' = 40; 'v4-era-4' = 40; 'v4-era-5' = 40; 'sh256x13' = 90; 'sh256x27' = 90; 'sh256x53' = 80; 'sh256x88' = 60; 'sh64x52' = 90 }
|
||||
$order = @('mx8-devnet-epoch0', 'v4-devnet-epoch0', 'v4-era-0', 'v4-era-1', 'v4-era-2', 'v4-era-3', 'v4-era-4', 'v4-era-5', 'sh256x13', 'sh256x27', 'sh256x53', 'sh256x88', 'sh64x52', 'mx8-devnet-epoch0')
|
||||
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
|
||||
# ---- the kit (the wiped-jobs-folder rule: tested before use; republish the fetch after any app update) ----
|
||||
$jobs = Split-Path $env:IGNEUM_JOB_DIR
|
||||
$kit = Join-Path $jobs $kitId
|
||||
if (-not (Test-Path $kit)) { "RESULT error kit missing at $kit (the fetch job $kitId runs first; republish it after any app update)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
|
||||
$exe = Join-Path $kit 'bin\igneum-worker-opencl.exe'
|
||||
$packs = Join-Path $kit 'packs'
|
||||
if (-not (Test-Path $exe)) { "RESULT error worker missing at $exe"; Summary 'failed' @{ error = 'worker missing' }; exit 2 }
|
||||
if (-not (Test-Path $packs)) { "RESULT error packs missing at $packs"; Summary 'failed' @{ error = 'packs missing' }; exit 2 }
|
||||
"RESULT worker kit $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) bytes $((Get-Item $exe).Length)"
|
||||
foreach ($f in (Get-ChildItem $kit -Recurse -File | Where-Object { $_.Name -match '\.(exe|c|json)$|SHA256SUMS' } | Sort-Object FullName)) { "RESULT kitfile $($f.FullName.Substring($kit.Length + 1).Replace('\', '/')) sha256 $((Get-FileHash -Algorithm SHA256 $f.FullName).Hash.ToLower()) bytes $($f.Length)" }
|
||||
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-ca3-pc1-amd' }; New-Item -ItemType Directory -Force -Path $job | Out-Null
|
||||
|
||||
# ---- the installed app: install folder (the installed worker for the cross-check and the AMD helper), app dir, URL ----
|
||||
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
|
||||
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
|
||||
$urlFile = Join-Path $appDir 'app.url'
|
||||
$base = $null
|
||||
if (Test-Path $urlFile) { $base = (Get-Content -LiteralPath $urlFile -Raw).Trim().TrimEnd('/') }
|
||||
"RESULT app install=$inst app_dir=$appDir url_present=$([bool]$base)"
|
||||
|
||||
# ---- the OpenCL device index of the 9070 XT, from the worker's own list (the current platform; the older platform's duplicate is marked dup) ----
|
||||
$listExe = $exe
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $listExe = Join-Path $inst 'igneum-worker-opencl.exe' }
|
||||
$list = @(& $listExe --list 2>&1 | ForEach-Object { "$_" })
|
||||
$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) { "RESULT error no gfx1201 device in --list (the eGPU is off the bus: every row OWED)"; Summary 'failed' @{ error = 'no gfx1201' }; exit 2 }
|
||||
"RESULT device $dev gfx1201 (list from $listExe)"
|
||||
|
||||
# ---- the card is switched off by the RUNNER (`--cards-off amd:gfx1201` at publish), never by this script (rule of
|
||||
# 6 October 2026, tools/ci/playbook-quit-check.sh rule 2); here only the read-only confirmation that its worker is gone ----
|
||||
function Workers {
|
||||
@(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe' OR Name = 'igneum-worker-cuda.exe' OR Name = 'igneum-worker-metal.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Name):$($_.ProcessId):[$($_.CommandLine -replace '\s+', ' ')]" })
|
||||
}
|
||||
function CardWorkers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match "--device\s+$dev(\s|$)" } | ForEach-Object { $_.ProcessId }) }
|
||||
"RESULT workers_before $(Stamp) $((Workers) -join ' ')"
|
||||
|
||||
# ---- the AMD helper: the installed app's igneum-gpu-telemetry.exe (0.3.10+), else the newest one under an amd-kit job folder ----
|
||||
$tele = $null
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-gpu-telemetry.exe'))) { $tele = Join-Path $inst 'igneum-gpu-telemetry.exe' }
|
||||
if (-not $tele) { $found = Get-ChildItem -Path (Join-Path $appDir 'jobs') -Recurse -Filter 'igneum-gpu-telemetry.exe' -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'amd-kit' } | Sort-Object LastWriteTime -Descending | Select-Object -First 1; if ($found) { $tele = $found.FullName } }
|
||||
$wattsSource = 'owed'
|
||||
if ($tele) {
|
||||
$one = @(& $tele 2>&1 | ForEach-Object { "$_" })
|
||||
$one | ForEach-Object { "RESULT tele_once $_" }
|
||||
$card9070 = $one | Where-Object { $_ -match '^amd \d+ .*name "[^"]*9070[^"]*" watts (\S+) .* source (\S+)' } | Select-Object -First 1
|
||||
if ($card9070 -and $card9070 -match ' watts (\S+) .* source (\S+)$') { if ($Matches[1] -ne '-') { $wattsSource = $Matches[2] } }
|
||||
"RESULT tele helper=$tele sha256=$((Get-FileHash -Algorithm SHA256 $tele).Hash.ToLower()) watts_source=$wattsSource (board watts through ADLX GPUPower, GPUTotalBoardPower as the fallback; 'owed' = the helper gives no watts for the 9070 XT)"
|
||||
} else { "RESULT tele none (no igneum-gpu-telemetry.exe in the install folder or under an amd-kit job folder): watts OWED" }
|
||||
|
||||
$samples = Join-Path $job 'amd-samples.log'
|
||||
$sampler = $null
|
||||
$windows = @()
|
||||
$results = @()
|
||||
$cardAlone = $false
|
||||
try {
|
||||
# ---- the card quiet? read-only: the runner switched it off before this script started; its worker must be gone ----
|
||||
$before = @(CardWorkers)
|
||||
"RESULT card_workers_before pids=[$($before -join ' ')]"
|
||||
if ($before.Count -eq 0) { "RESULT card already quiet: no opencl worker with --device $dev (the runner's --cards-off took, or --stop-miners)"; $cardAlone = $true }
|
||||
else {
|
||||
$t = 0
|
||||
while ($t -lt 150) { Start-Sleep -Seconds 5; $t += 5; if ((@(CardWorkers)).Count -eq 0) { break } }
|
||||
$left = @(CardWorkers)
|
||||
if ($left.Count -eq 0) { "RESULT card $(Stamp) quiet after $t s: no igneum-worker-opencl process with --device $dev (the card to itself)"; $cardAlone = $true }
|
||||
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: opencl worker pids [$($left -join ' ')] still carry --device $dev (was the job published with --cards-off amd:gfx1201?); the rows below are LOADED-card figures" }
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
"RESULT workers_during $(Stamp) $((Workers) -join ' ')"
|
||||
|
||||
# ---- the sampler: the helper at 1 Hz through a wrapper; its paths through the ENVIRONMENT (inherited by the child,
|
||||
# never on a command line: Start-Process -ArgumentList in Windows PowerShell 5.1 joins arguments WITHOUT quoting,
|
||||
# and "Igneum Miner" holds a space, which is why the 15:46Z run wrote no samples), the -File path quoted; ended
|
||||
# by its own pid in finally. The sampler PROVES itself in the 12 idle seconds: the first three raw lines are
|
||||
# printed, and without a 9070 XT line with watts the ladder is NOT run blind (the rates are on record from 15:46Z). ----
|
||||
$samplerOk = $false
|
||||
if ($tele -and $wattsSource -ne 'owed') {
|
||||
$wrapper = Join-Path $job 'sampler.ps1'
|
||||
$wrapperText = @'
|
||||
$exe = $env:IGNEUM_SAMPLER_EXE
|
||||
$out = $env:IGNEUM_SAMPLER_OUT
|
||||
Add-Content -LiteralPath $out -Value ('wrapper start ' + (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fff') + ' exe=' + $exe + ' exists=' + (Test-Path -LiteralPath $exe))
|
||||
& $exe -l 1 2>&1 | ForEach-Object { Add-Content -LiteralPath $out -Value ((Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fff') + ' ' + $_) }
|
||||
Add-Content -LiteralPath $out -Value ('wrapper end ' + (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fff') + ' exit=' + $LASTEXITCODE)
|
||||
'@
|
||||
[IO.File]::WriteAllText($wrapper, $wrapperText, (New-Object System.Text.UTF8Encoding $false))
|
||||
Remove-Item -LiteralPath $samples -Force -ErrorAction SilentlyContinue
|
||||
$env:IGNEUM_SAMPLER_EXE = $tele
|
||||
$env:IGNEUM_SAMPLER_OUT = $samples
|
||||
$sampler = Start-Process -FilePath 'powershell.exe' -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', ('"' + $wrapper + '"')) -WindowStyle Hidden -PassThru
|
||||
"RESULT sampler pid=$($sampler.Id) wrapper=$wrapper file=$samples exe_via_env=$tele"
|
||||
Start-Sleep -Seconds 12
|
||||
$raw = @(); if (Test-Path -LiteralPath $samples) { $raw = @(Get-Content -LiteralPath $samples) }
|
||||
$raw | Select-Object -First 3 | ForEach-Object { "RESULT sampler_raw $_" }
|
||||
$n9070 = @($raw | Where-Object { $_ -match '^\S+ amd \d+ .*name "[^"]*9070[^"]*" watts [\d.]+ ' }).Count
|
||||
"RESULT sampler_idle lines=$($raw.Count) lines_9070_with_watts=$n9070 sampler_alive=$(-not $sampler.HasExited)"
|
||||
if ($n9070 -eq 0) {
|
||||
$why = if ($raw.Count -eq 0) { 'the sampler wrote nothing in 12 s (file absent or empty)' } else { 'the sampler wrote ' + $raw.Count + ' line(s) but none is a 9070 XT line with a watts number (first lines above)' }
|
||||
"RESULT watts error=$why; the ladder is NOT run blind"
|
||||
foreach ($pk in $order) { "RESULT LADDER pack=$pk error=$why" }
|
||||
} else { $samplerOk = $true; "RESULT idle $(Stamp) sampler proven on the idle window" }
|
||||
} else { "RESULT watts error=no helper or no watts in its one-shot sample: the ladder runs with watts owed"; $samplerOk = $true }
|
||||
|
||||
# ---- the ladder ----
|
||||
$i = 0
|
||||
foreach ($pk in $order) {
|
||||
if (-not $samplerOk) { break }
|
||||
$i++
|
||||
$d = Join-Path $packs $pk
|
||||
if (-not (Test-Path $d)) { "RESULT G1 pack=$pk error=pack missing at $d"; "RESULT LADDER pack=$pk error=pack missing"; continue }
|
||||
$instrs = 0; $cls = ''
|
||||
try { $pj = Get-Content -LiteralPath (Join-Path $d 'program.json') -Raw | ConvertFrom-Json; $cls = [string] $pj.load_class; if ($pj.shadow) { $instrs = [int] $pj.shadow.instrs_per_hash } } catch { "RESULT note program.json of $pk unreadable: $($_.Exception.Message)" }
|
||||
$n = $batches[$pk]
|
||||
$tag = "$pk#$i"
|
||||
$t0 = (Get-Date).ToUniversalTime()
|
||||
"RESULT bench $tag start $(Stamp) batches=$n class=$cls shadow_instrs_per_hash=$instrs"
|
||||
$lines = @(& $exe --bench-pack --pack $d --batches $n --batch-log2 24 --device $dev 2>&1 | ForEach-Object { "$_" })
|
||||
$rc = $LASTEXITCODE
|
||||
$t1 = (Get-Date).ToUniversalTime()
|
||||
$lines | ForEach-Object { "RESULT bench $tag $_" }
|
||||
"RESULT bench $tag end $(Stamp) exit=$rc wall_s=$([int]($t1 - $t0).TotalSeconds)"
|
||||
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
|
||||
$fp = ''; $mhs = ''; $check = ''
|
||||
if ($res -and $res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
|
||||
if ($res -and $res -match 'mhs=([\d.]+)') { $mhs = $Matches[1] }
|
||||
if ($res -and $res -match 'check=(\S+)') { $check = $Matches[1] }
|
||||
$buildMs = ''; if (($lines | Where-Object { $_ -match '^build ([\d.]+) ms clBuildProgram' } | Select-Object -First 1) -match '^build ([\d.]+) ms') { $buildMs = $Matches[1] }
|
||||
$dsMs = ''; if (($lines | Where-Object { $_ -match '^pack .*: cache \d+ dataset (\d+) ' } | Select-Object -First 1) -match ' dataset (\d+) ') { $dsMs = $Matches[1] }
|
||||
if (-not $res -or $rc -ne 0) {
|
||||
$err = ($lines | Where-Object { $_ -match 'FAIL|error|MISMATCH' } | Select-Object -First 1); if (-not $err) { $err = "exit $rc, no RESULT line" }
|
||||
"RESULT G1 pack=$pk error=$($err -replace '\s+', ' ')"
|
||||
"RESULT LADDER pack=$pk error=$($err -replace '\s+', ' ')"
|
||||
continue
|
||||
}
|
||||
$want = $expected[$pk]
|
||||
$match = if ($want -and $fp -eq $want) { 'yes' } elseif ($want) { 'no' } else { 'unknown' }
|
||||
"RESULT G1 pack=$pk fingerprint=$fp match=$match expected=$want check=$check card=gfx1201 harness=opencl-kit"
|
||||
$results += [pscustomobject]@{ tag = $tag; pack = $pk; instrs = $instrs; ops = $opsPerHash[$pk]; mhs = $mhs; fp = $fp; match = $match; check = $check; build_ms = $buildMs; dataset_ms = $dsMs; t0 = $t0; t1 = $t1 }
|
||||
Start-Sleep -Seconds 3
|
||||
}
|
||||
|
||||
# ---- the cross-check: the INSTALLED worker on the control pack, if it carries --bench-pack ----
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) {
|
||||
$iexe = Join-Path $inst 'igneum-worker-opencl.exe'
|
||||
$help = (& $iexe --help 2>&1 | Out-String)
|
||||
if ($help -match '--bench-pack') {
|
||||
$d = Join-Path $packs 'mx8-devnet-epoch0'
|
||||
$t0 = (Get-Date).ToUniversalTime()
|
||||
"RESULT xcheck start $(Stamp) worker=installed sha256=$((Get-FileHash -Algorithm SHA256 $iexe).Hash.ToLower())"
|
||||
$lines = @(& $iexe --bench-pack --pack $d --batches 40 --batch-log2 24 --device $dev 2>&1 | ForEach-Object { "$_" })
|
||||
$rc = $LASTEXITCODE
|
||||
$t1 = (Get-Date).ToUniversalTime()
|
||||
$lines | Where-Object { $_ -match '^RESULT|^pack |^warm-up|FAIL|error' } | ForEach-Object { "RESULT xcheck $_" }
|
||||
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
|
||||
$fp = ''; $mhs = ''
|
||||
if ($res -and $res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
|
||||
if ($res -and $res -match 'mhs=([\d.]+)') { $mhs = $Matches[1] }
|
||||
if ($res) { "RESULT XCHECK worker=installed pack=mx8-devnet-epoch0 fingerprint=$fp match=$(if ($fp -eq $expected['mx8-devnet-epoch0']) { 'yes' } else { 'no' }) mhs=$mhs exit=$rc (the kit worker's control rows are the LADDER lines)"; $windows += [pscustomobject]@{ tag = 'xcheck-installed'; t0 = $t0; t1 = $t1 } }
|
||||
else { "RESULT XCHECK worker=installed error=exit $rc, no RESULT line" }
|
||||
} else { "RESULT XCHECK worker=installed skipped: the installed igneum-worker-opencl.exe has no --bench-pack" }
|
||||
}
|
||||
} finally {
|
||||
# ---- whatever happened: the sampler ended by its own pid (never by name: the app runs its own copy of the helper), the card back ----
|
||||
if ($sampler) { try { & taskkill /T /F /PID $sampler.Id 2>&1 | Out-Null; "RESULT sampler ended pid=$($sampler.Id)" } catch { "RESULT sampler end error=$($_.Exception.Message)" } }
|
||||
# the card comes back through the RUNNER (its restore on any exit), not here
|
||||
"RESULT workers_after $(Stamp) $((Workers) -join ' ')"
|
||||
}
|
||||
|
||||
# ---- the samples: board watts and clocks of the 9070 XT inside each bench window (after its first 12 s, before its last 2 s) ----
|
||||
$rows = @()
|
||||
if (Test-Path $samples) {
|
||||
foreach ($l in (Get-Content -LiteralPath $samples)) {
|
||||
if ($l -match '^(\S+) amd (\d+) bus (\S+) kind (\S+) name "([^"]*)" watts (\S+) temp_c (\S+) fan_rpm (\S+) fan_pct (\S+) mclk_mhz (\S+) gclk_mhz (\S+) util_pct (\S+) source (\S+)') {
|
||||
if ($Matches[5] -notmatch '9070') { continue }
|
||||
try { $ts = [datetime]::ParseExact($Matches[1], 'yyyy-MM-ddTHH:mm:ss.fff', [Globalization.CultureInfo]::InvariantCulture) } catch { continue }
|
||||
$w = if ($Matches[6] -eq '-') { $null } else { [double] $Matches[6] }
|
||||
$rows += [pscustomobject]@{ ts = $ts; w = $w; temp = $Matches[7]; fan = $Matches[9]; mclk = $Matches[10]; gclk = $Matches[11]; util = $Matches[12]; src = $Matches[13] }
|
||||
}
|
||||
}
|
||||
}
|
||||
"RESULT samples total $($rows.Count) file $samples (9070 XT lines)"
|
||||
function Window([datetime] $t0, [datetime] $t1) { @($rows | Where-Object { $_.ts -gt $t0.AddSeconds(12) -and $_.ts -lt $t1.AddSeconds(-2) -and $null -ne $_.w }) }
|
||||
$g1Match = 0; $ladderRows = 0
|
||||
foreach ($r in $results) {
|
||||
$in = @(Window $r.t0 $r.t1)
|
||||
$watts = 'owed'; $uj = 'owed'; $gclk = 'owed'; $mclk = 'owed'; $tmax = 'owed'; $wmin = ''; $wmax = ''
|
||||
if ($in.Count -gt 0) {
|
||||
$m = $in | Measure-Object -Property w -Average -Minimum -Maximum
|
||||
$watts = [math]::Round($m.Average, 1); $wmin = $m.Minimum; $wmax = $m.Maximum
|
||||
$gclk = [math]::Round((($in | ForEach-Object { [double] $_.gclk }) | Measure-Object -Average).Average)
|
||||
$mclk = [math]::Round((($in | ForEach-Object { [double] $_.mclk }) | Measure-Object -Average).Average)
|
||||
$tmax = (($in | ForEach-Object { [double] $_.temp }) | Measure-Object -Maximum).Maximum
|
||||
if ($r.mhs -and [double] $r.mhs -gt 0) { $uj = [math]::Round($m.Average / [double] $r.mhs, 3) }
|
||||
}
|
||||
if ($r.match -eq 'yes') { $g1Match++ }
|
||||
$ladderRows++
|
||||
"RESULT LADDER pack=$($r.pack) ops=$($r.ops) shadow_instrs=$($r.instrs) mhs=$($r.mhs) watts=$watts uj=$uj watts_min=$wmin watts_max=$wmax gclk_mhz=$gclk mclk_mhz=$mclk temp_max=$tmax samples=$($in.Count) window_s=$([int]($r.t1 - $r.t0).TotalSeconds) build_ms=$($r.build_ms) dataset_ms=$($r.dataset_ms) fingerprint=$($r.fp) match=$($r.match) card_alone=$cardAlone tag=$($r.tag)"
|
||||
}
|
||||
foreach ($win in $windows) { $in = @(Window $win.t0 $win.t1); if ($in.Count -gt 0) { $m = $in | Measure-Object -Property w -Average; "RESULT power $($win.tag) samples=$($in.Count) watts_mean=$([math]::Round($m.Average, 1))" } }
|
||||
if ($rows.Count -gt 0) {
|
||||
$idle = @($rows | Where-Object { $results.Count -gt 0 -and $_.ts -lt $results[0].t0 -and $null -ne $_.w })
|
||||
if ($idle.Count -gt 0) { "RESULT idle_power samples=$($idle.Count) watts_mean=$([math]::Round(($idle | Measure-Object -Property w -Average).Average, 1)) (the card switched off in the app, before the first bench)" }
|
||||
foreach ($r in $rows) { "RESULT sample $($r.ts.ToString('HH:mm:ss')) $($r.w) $($r.temp) $($r.fan) $($r.mclk) $($r.gclk) $($r.util) $($r.src)" }
|
||||
}
|
||||
$status = if (-not $samplerOk) { 'failed' } elseif ($results.Count -eq $order.Count) { 'done' } elseif ($results.Count -gt 0) { 'partial' } else { 'failed' }
|
||||
"RESULT end $(Stamp)"
|
||||
Summary $status @{ packs_run = $results.Count; packs_wanted = $order.Count; g1_match = $g1Match; ladder_rows = $ladderRows; watts = $wattsSource; sampler_proven = $samplerOk; card_alone = $cardAlone; device = $dev }
|
||||
if ($status -eq 'failed') { exit 1 }
|
||||
exit 0
|
||||
80
tools/ca3-v4-amend/pc1-v5-amd-bench-20261007.ps1
Normal file
80
tools/ca3-v4-amend/pc1-v5-amd-bench-20261007.ps1
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# Class v5 kit bench on PC 1's RX 9070 XT (7 October 2026, docs/design/class-v5-stored-state.md section 13, "the kit:
|
||||
# OpenCL (AMD)"): the kit's igneum-worker-opencl.exe (THIS tree's proto-opencl/host.c with the class v5 leaf upload:
|
||||
# igneum_build(ds, cache, leaves, nLeaves, nItems), the leaves of leaves.bin checked against the pack's FNV-1a 64 first)
|
||||
# runs --bench-pack on the first class v5 pack, proto-cuda/packs-ca3-v5/v5-dn3-epoch0 (program id e5a4ac5978462156, 11
|
||||
# leaves under state root 7e37a9fb..., cache FNV 7334fa46e5d972eb), on the gfx1201 device. Expected: the self-test
|
||||
# PASS line (cache head, last line and FNV; dataset head, word [268435455] and 64 samples; 96 of 96 vector lanes) and the
|
||||
# fingerprint of the 2^24 outputs at base nonce 0 equal to the Metal reading, 82b19cbde8557ea5 (the M5 Max, 7 October
|
||||
# 2026, 18:07:09Z). The v4-genesis control pack runs after it so the run has a known class v4 row beside the v5 row.
|
||||
# Beside the miners (a correctness and fingerprint gate; the rate row is labelled loaded when the card mines): no card is
|
||||
# switched, nothing is posted to the installed app, nothing is built on the PC. Published by the Counter ASIC coordinator
|
||||
# only (the PC 1 queue is its); the kit is fetch job $kitId. Every result line starts with RESULT; SUMMARY {json} ends it.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-amd-v5-bench'
|
||||
$kitId = $env:IGNEUM_V5_KIT_ID; if (-not $kitId) { $kitId = 'fetch-ca3-v5-kit-20261007' }
|
||||
$expected = '82b19cbde8557ea5'
|
||||
$started = Get-Date
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) {
|
||||
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
|
||||
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||
}
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION expected_fingerprint=$expected"
|
||||
$jobs = Split-Path $env:IGNEUM_JOB_DIR
|
||||
$kit = Join-Path $jobs $kitId
|
||||
if (-not (Test-Path $kit)) { "RESULT error kit missing at $kit (the fetch job $kitId runs first; republish it after any app update)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
|
||||
$exe = Join-Path $kit 'bin\windows\igneum-worker-opencl.exe'
|
||||
$packs = Join-Path $kit 'packs'
|
||||
if (-not (Test-Path $exe)) { "RESULT error worker missing at $exe"; Summary 'failed' @{ error = 'worker missing' }; exit 2 }
|
||||
"RESULT worker kit $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) bytes $((Get-Item $exe).Length)"
|
||||
foreach ($pk in @('v5-dn3-epoch0', 'v4-genesis')) {
|
||||
$d = Join-Path $packs $pk
|
||||
if (-not (Test-Path (Join-Path $d 'kernel_bound.cl'))) { "RESULT error pack $pk missing at $d"; Summary 'failed' @{ error = "pack $pk missing" }; exit 2 }
|
||||
"RESULT pack $pk kernel_bound.cl sha256 $((Get-FileHash -Algorithm SHA256 (Join-Path $d 'kernel_bound.cl')).Hash.ToLower()) program.h sha256 $((Get-FileHash -Algorithm SHA256 (Join-Path $d 'program.h')).Hash.ToLower())"
|
||||
}
|
||||
$leaves = Join-Path $packs 'v5-dn3-epoch0\leaves.bin'
|
||||
if (-not (Test-Path $leaves)) { "RESULT error leaves.bin missing at $leaves (a class v5 pack without its leaves builds nothing)"; Summary 'failed' @{ error = 'leaves missing' }; exit 2 }
|
||||
"RESULT leaves $leaves bytes $((Get-Item $leaves).Length) sha256 $((Get-FileHash -Algorithm SHA256 $leaves).Hash.ToLower())"
|
||||
# the OpenCL device index of the 9070 XT (the installed worker's list when present: the app's own indices)
|
||||
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
|
||||
$listExe = $exe
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $listExe = Join-Path $inst 'igneum-worker-opencl.exe' }
|
||||
$list = @(& $listExe --list 2>&1 | ForEach-Object { "$_" })
|
||||
$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) { "RESULT error no gfx1201 device in --list (the eGPU is off the bus: the AMD v5 row stays OWED)"; Summary 'failed' @{ error = 'no gfx1201' }; exit 2 }
|
||||
"RESULT device $dev gfx1201 (list from $listExe)"
|
||||
function Workers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe' OR Name = 'igneum-worker-cuda.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Name):$($_.ProcessId):[$($_.CommandLine -replace '\s+', ' ')]" }) }
|
||||
$w = @(Workers)
|
||||
$loaded = ($w | Where-Object { $_ -match "igneum-worker-opencl.*--device\s+$dev(\s|$)" }).Count -gt 0
|
||||
"RESULT workers_before $(Stamp) $($w -join ' ')"
|
||||
$state = if ($loaded) { 'loaded' } else { 'quiet' }
|
||||
"RESULT context card_state=$state (a fingerprint gate: the load changes the rate row, never the bytes)"
|
||||
$rows = @{}
|
||||
$fpOk = $false
|
||||
foreach ($pk in @('v5-dn3-epoch0', 'v4-genesis')) {
|
||||
$d = Join-Path $packs $pk
|
||||
$t0 = Get-Date
|
||||
"RESULT bench $pk start $(Stamp) cmd=igneum-worker-opencl.exe --bench-pack --pack $d --device $dev --batch-log2 24 --batches 5"
|
||||
$out = @(& $exe --bench-pack --pack $d --device $dev --batch-log2 24 --batches 5 2>&1 | ForEach-Object { "$_" })
|
||||
$code = $LASTEXITCODE
|
||||
$secs = [int]((Get-Date) - $t0).TotalSeconds
|
||||
foreach ($l in $out) { if ($l -match '^(pack |class v5|RESULT |FAIL|error|warm-up)') { "RESULT bench $pk out $l" } }
|
||||
$res = $out | Where-Object { $_ -match '^RESULT ' } | Select-Object -Last 1
|
||||
$fp = ''; $mhs = ''; $check = ''
|
||||
if ($res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
|
||||
if ($res -match 'mhs=([0-9.]+)') { $mhs = $Matches[1] }
|
||||
if ($res -match 'check=(\w+)') { $check = $Matches[1] }
|
||||
$rows[$pk] = [ordered]@{ exit = $code; seconds = $secs; fingerprint = $fp; mhs = $mhs; check = $check; card_state = $state }
|
||||
if ($pk -eq 'v5-dn3-epoch0') {
|
||||
$fpOk = ($fp -eq $expected -and $check -eq 'PASS')
|
||||
"RESULT v5 fingerprint=$fp expected=$expected match=$fpOk check=$check mhs=$mhs card_state=$state exit=$code seconds=$secs $(Stamp)"
|
||||
} else {
|
||||
"RESULT v4-control fingerprint=$fp check=$check mhs=$mhs card_state=$state exit=$code seconds=$secs $(Stamp)"
|
||||
}
|
||||
}
|
||||
"RESULT workers_after $(Stamp) $((@(Workers)) -join ' ')"
|
||||
Summary $(if ($fpOk) { 'done' } else { 'failed' }) @{ expected = $expected; v5 = $rows['v5-dn3-epoch0']; v4_control = $rows['v4-genesis']; device = $dev; kit = $kitId }
|
||||
exit $(if ($fpOk) { 0 } else { 1 })
|
||||
84
tools/ca3-v4-amend/pc1-v5-intel-bench-20261007.ps1
Normal file
84
tools/ca3-v4-amend/pc1-v5-intel-bench-20261007.ps1
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# Class v5 kit bench on PC 1's Intel Arc B580 (7 October 2026, 23:4x UK, main's re-route: the Intel fingerprint runs on PC 1 as
|
||||
# the same lock-free, non-elevated job class as the AMD one, after it): tools/class-v5/pc1-amd-v5-bench.ps1 with the device
|
||||
# match on the Intel OpenCL platform (Arc / B580) instead of gfx1201; everything else identical (the v5-dn3-epoch0 pack,
|
||||
# the v4-genesis control, RESULT lines, SUMMARY {json}). Expected fingerprint 82b19cbde8557ea5.
|
||||
# Class v5 kit bench on PC 1's RX 9070 XT (7 October 2026, docs/design/class-v5-stored-state.md section 13, "the kit:
|
||||
# OpenCL (AMD)"): the kit's igneum-worker-opencl.exe (THIS tree's proto-opencl/host.c with the class v5 leaf upload:
|
||||
# igneum_build(ds, cache, leaves, nLeaves, nItems), the leaves of leaves.bin checked against the pack's FNV-1a 64 first)
|
||||
# runs --bench-pack on the first class v5 pack, proto-cuda/packs-ca3-v5/v5-dn3-epoch0 (program id e5a4ac5978462156, 11
|
||||
# leaves under state root 7e37a9fb..., cache FNV 7334fa46e5d972eb), on the gfx1201 device. Expected: the self-test
|
||||
# PASS line (cache head, last line and FNV; dataset head, word [268435455] and 64 samples; 96 of 96 vector lanes) and the
|
||||
# fingerprint of the 2^24 outputs at base nonce 0 equal to the Metal reading, 82b19cbde8557ea5 (the M5 Max, 7 October
|
||||
# 2026, 18:07:09Z). The v4-genesis control pack runs after it so the run has a known class v4 row beside the v5 row.
|
||||
# Beside the miners (a correctness and fingerprint gate; the rate row is labelled loaded when the card mines): no card is
|
||||
# switched, nothing is posted to the installed app, nothing is built on the PC. Published by the Counter ASIC coordinator
|
||||
# only (the PC 1 queue is its); the kit is fetch job $kitId. Every result line starts with RESULT; SUMMARY {json} ends it.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-intel-v5-bench'
|
||||
$kitId = $env:IGNEUM_V5_KIT_ID; if (-not $kitId) { $kitId = 'fetch-ca3-v5-kit-20261007' }
|
||||
$expected = '82b19cbde8557ea5'
|
||||
$started = Get-Date
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) {
|
||||
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
|
||||
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||
}
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION expected_fingerprint=$expected"
|
||||
$jobs = Split-Path $env:IGNEUM_JOB_DIR
|
||||
$kit = Join-Path $jobs $kitId
|
||||
if (-not (Test-Path $kit)) { "RESULT error kit missing at $kit (the fetch job $kitId runs first; republish it after any app update)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
|
||||
$exe = Join-Path $kit 'bin\windows\igneum-worker-opencl.exe'
|
||||
$packs = Join-Path $kit 'packs'
|
||||
if (-not (Test-Path $exe)) { "RESULT error worker missing at $exe"; Summary 'failed' @{ error = 'worker missing' }; exit 2 }
|
||||
"RESULT worker kit $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) bytes $((Get-Item $exe).Length)"
|
||||
foreach ($pk in @('v5-dn3-epoch0', 'v4-genesis')) {
|
||||
$d = Join-Path $packs $pk
|
||||
if (-not (Test-Path (Join-Path $d 'kernel_bound.cl'))) { "RESULT error pack $pk missing at $d"; Summary 'failed' @{ error = "pack $pk missing" }; exit 2 }
|
||||
"RESULT pack $pk kernel_bound.cl sha256 $((Get-FileHash -Algorithm SHA256 (Join-Path $d 'kernel_bound.cl')).Hash.ToLower()) program.h sha256 $((Get-FileHash -Algorithm SHA256 (Join-Path $d 'program.h')).Hash.ToLower())"
|
||||
}
|
||||
$leaves = Join-Path $packs 'v5-dn3-epoch0\leaves.bin'
|
||||
if (-not (Test-Path $leaves)) { "RESULT error leaves.bin missing at $leaves (a class v5 pack without its leaves builds nothing)"; Summary 'failed' @{ error = 'leaves missing' }; exit 2 }
|
||||
"RESULT leaves $leaves bytes $((Get-Item $leaves).Length) sha256 $((Get-FileHash -Algorithm SHA256 $leaves).Hash.ToLower())"
|
||||
# the OpenCL device index of the 9070 XT (the installed worker's list when present: the app's own indices)
|
||||
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
|
||||
$listExe = $exe
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $listExe = Join-Path $inst 'igneum-worker-opencl.exe' }
|
||||
$list = @(& $listExe --list 2>&1 | ForEach-Object { "$_" })
|
||||
$list | ForEach-Object { "RESULT list $_" }
|
||||
$dev = $null
|
||||
foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*(Intel|Arc|B580)' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } }
|
||||
if ($null -eq $dev) { "RESULT error no Intel Arc device in --list (the B580 is off the bus or has no OpenCL runtime: the Intel v5 row stays OWED)"; Summary 'failed' @{ error = 'no intel arc' }; exit 2 }
|
||||
"RESULT device $dev Intel Arc (list from $listExe)"
|
||||
function Workers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe' OR Name = 'igneum-worker-cuda.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Name):$($_.ProcessId):[$($_.CommandLine -replace '\s+', ' ')]" }) }
|
||||
$w = @(Workers)
|
||||
$loaded = ($w | Where-Object { $_ -match "igneum-worker-opencl.*--device\s+$dev(\s|$)" }).Count -gt 0
|
||||
"RESULT workers_before $(Stamp) $($w -join ' ')"
|
||||
$state = if ($loaded) { 'loaded' } else { 'quiet' }
|
||||
"RESULT context card_state=$state (a fingerprint gate: the load changes the rate row, never the bytes)"
|
||||
$rows = @{}
|
||||
$fpOk = $false
|
||||
foreach ($pk in @('v5-dn3-epoch0', 'v4-genesis')) {
|
||||
$d = Join-Path $packs $pk
|
||||
$t0 = Get-Date
|
||||
"RESULT bench $pk start $(Stamp) cmd=igneum-worker-opencl.exe --bench-pack --pack $d --device $dev --batch-log2 24 --batches 5"
|
||||
$out = @(& $exe --bench-pack --pack $d --device $dev --batch-log2 24 --batches 5 2>&1 | ForEach-Object { "$_" })
|
||||
$code = $LASTEXITCODE
|
||||
$secs = [int]((Get-Date) - $t0).TotalSeconds
|
||||
foreach ($l in $out) { if ($l -match '^(pack |class v5|RESULT |FAIL|error|warm-up)') { "RESULT bench $pk out $l" } }
|
||||
$res = $out | Where-Object { $_ -match '^RESULT ' } | Select-Object -Last 1
|
||||
$fp = ''; $mhs = ''; $check = ''
|
||||
if ($res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
|
||||
if ($res -match 'mhs=([0-9.]+)') { $mhs = $Matches[1] }
|
||||
if ($res -match 'check=(\w+)') { $check = $Matches[1] }
|
||||
$rows[$pk] = [ordered]@{ exit = $code; seconds = $secs; fingerprint = $fp; mhs = $mhs; check = $check; card_state = $state }
|
||||
if ($pk -eq 'v5-dn3-epoch0') {
|
||||
$fpOk = ($fp -eq $expected -and $check -eq 'PASS')
|
||||
"RESULT v5 fingerprint=$fp expected=$expected match=$fpOk check=$check mhs=$mhs card_state=$state exit=$code seconds=$secs $(Stamp)"
|
||||
} else {
|
||||
"RESULT v4-control fingerprint=$fp check=$check mhs=$mhs card_state=$state exit=$code seconds=$secs $(Stamp)"
|
||||
}
|
||||
}
|
||||
"RESULT workers_after $(Stamp) $((@(Workers)) -join ' ')"
|
||||
Summary $(if ($fpOk) { 'done' } else { 'failed' }) @{ expected = $expected; v5 = $rows['v5-dn3-epoch0']; v4_control = $rows['v4-genesis']; device = $dev; kit = $kitId }
|
||||
exit $(if ($fpOk) { 0 } else { 1 })
|
||||
|
|
@ -22,6 +22,14 @@ check_file() {
|
|||
if grep -vE '^\s*#' "$f" | grep -qE "api/cards"; then
|
||||
echo "playbook-quit: $f sends a request to the installed app's api/cards (a script never switches cards; ask the runner: publish-jobs.sh add --kind run --cards-off <key,key>)"; bad=1
|
||||
fi
|
||||
# rule 3 (STANDING RULE, the founder through main, 7 October 2026, 18:5x UTC): no PC job raises a UAC prompt or needs a click, ever.
|
||||
# A script that launches anything with -Verb RunAs (or runas.exe) raises one; the rights path is the installed app's Igneum
|
||||
# Power Helper task (Start-ScheduledTask by the owning user, commands through its cmd.txt: app/igneum-app/src/powertask.rs).
|
||||
# publish-jobs.sh refuses --elevated for the same reason (the 18:27Z job run-ca3-pc1-v4-eff-5090-20261007: exit 251 after
|
||||
# two minutes waiting for a click).
|
||||
if grep -vE '^\s*#' "$f" | grep -qiE -e "(-Verb +['\"]?RunAs)|(\brunas(\.exe)? +/user)"; then
|
||||
echo "playbook-quit: $f raises an administrator prompt (-Verb RunAs or runas): no PC job prompts; use the Igneum Power Helper task (app/igneum-app/src/powertask.rs)"; bad=1
|
||||
fi
|
||||
grep -qE "api/(quit|pause|resume)" "$f" || return $bad
|
||||
if grep -vE '^\s*#' "$f" | grep -qE "igneum\\\\app\\\\app\.url|igneum/app/app\.url|Application Support/Igneum/app/app\.url|IGNEUM_APP_DIR[^\n]*app\.url|\\\$appDir[^\n]*'app\.url'"; then
|
||||
echo "playbook-quit: $f reads the installed app's URL file and sends quit, pause or resume to it (a job may only quit an engine it started: its own scratch URL file)"; bad=1
|
||||
|
|
@ -38,7 +46,12 @@ if [ "${1:-}" = "--self-test" ]; then
|
|||
if ! check_file "$t/good.ps1"; then echo "self-test FAILED: the good playbook failed"; exit 1; fi
|
||||
if check_file "$t/cards.ps1" >/dev/null; then echo "self-test FAILED: the api/cards switch passed"; exit 1; fi
|
||||
if ! check_file "$t/state.ps1"; then echo "self-test FAILED: a read of api/state (api/cards only in a comment) failed"; exit 1; fi
|
||||
rm -rf "$t"; echo "self-test passed: the installed app's URL file with a quit fails, a scratch URL file passes, a request to api/cards fails, a read of api/state passes"; exit 0
|
||||
printf '%s\n' '$p = Start-Process powershell.exe -Verb RunAs -ArgumentList @("-File", $s) -Wait -PassThru' > "$t/runas.ps1"
|
||||
printf '%s\n' '# the old shape ran Start-Process -Verb RunAs; now the Power Helper task carries the rights' 'Start-ScheduledTask -TaskName "Igneum Power Helper"' > "$t/helper.ps1"
|
||||
if check_file "$t/runas.ps1" >/dev/null; then echo "self-test FAILED: the RunAs launch passed"; exit 1; fi
|
||||
if ! check_file "$t/helper.ps1"; then echo "self-test FAILED: the Power Helper task start (RunAs only in a comment) failed"; exit 1; fi
|
||||
if ! bash packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id self-test-elevated --elevated --script "$t/helper.ps1" --title "self-test" --dest "$t/dest" >/dev/null 2>&1; then :; else echo "self-test FAILED: publish-jobs.sh accepted --elevated (the 18:27Z job as published)"; exit 1; fi
|
||||
rm -rf "$t"; echo "self-test passed: the installed app's URL file with a quit fails, a scratch URL file passes, a request to api/cards fails, a read of api/state passes, a -Verb RunAs launch fails, a Power Helper task start passes, publish-jobs.sh refuses --elevated"; exit 0
|
||||
fi
|
||||
# allowed senders: the installer's own stop step (the update-now path the rule names), and, pending the rule owner's
|
||||
# word (6 October 2026, 15:10 UTC): tools/proving-v1/pc2-agg-cost.ps1 and its restore step pc2-agg-cost-restore.ps1, which switch the 5090 off through /api/cards
|
||||
|
|
|
|||
Loading…
Reference in a new issue