From 96be28bc71bb38c83ab0946df3f4e6ac9d30c68e Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Wed, 7 Oct 2026 21:04:25 +0000 Subject: [PATCH] Pool daemon: rejections by code in STATUS and /api/stats, the ledger's life and this run apart (7 October 2026, Devnet 3 pool-b: STATUS rejected=235,016 was the ledger's lifetime across the daemon's eras while the member had no rejected line in the hour; the two counters now say what they count) Co-Authored-By: Claude Fable 5.1 --- pool/src/api.rs | 2 +- pool/src/main.rs | 10 ++++++++-- pool/src/server.rs | 4 ++-- pool/src/state.rs | 17 +++++++++++++++-- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/pool/src/api.rs b/pool/src/api.rs index 781790902..cfba6efb4 100644 --- a/pool/src/api.rs +++ b/pool/src/api.rs @@ -87,7 +87,7 @@ pub fn stats(pool: &Pool) -> Value { "scheme": "PPLNS", "fee_percent": pool.cfg.fee_percent, "min_payout_ign": pool.cfg.min_payout_ign, "pplns_window_blocks": pool.cfg.pplns_window_blocks, "hashrate": round(s.hashrate(600, None, None), 0), "hashrate_unit": "H/s", "hashrate_1h": round(s.hashrate(3600, None, None), 0), "miners": miners, "workers": workers, - "shares": { "accepted": s.accepted, "stale": s.stale, "rejected": s.rejected, "window_weight_blocks": round(s.pplns.total_weight, 4), "window_shares": s.pplns.shares.len() }, + "shares": { "accepted": s.accepted, "stale": s.stale, "rejected": s.rejected, "window_weight_blocks": round(s.pplns.total_weight, 4), "window_shares": s.pplns.shares.len(), "rejected_by_code": s.rejected_by_code, "rejected_by_code_this_run": s.rejected_by_code_run, "counters_since": "ledger (rejected, stale, accepted, rejected_by_code are the ledger's life across restarts; rejected_by_code_this_run is this process)" }, "blocks_24h": f24, "blocks_confirmed_24h": c24, "blocks_orphaned_24h": o24, "blocks_total": s.blocks_found, "blocks_confirmed_total": s.blocks_confirmed, "blocks_orphaned_total": s.blocks_orphaned, "last_block": last.map(block_json), diff --git a/pool/src/main.rs b/pool/src/main.rs index 54614eefb..9595e0644 100644 --- a/pool/src/main.rs +++ b/pool/src/main.rs @@ -261,8 +261,12 @@ async fn main() { let (miners, workers) = pool.online(); let s = pool.state.lock().unwrap(); let (n, mean, p50, p99, max) = s.check_cost(); + let by_code = |m: &std::collections::BTreeMap| { + let v: Vec = m.iter().filter(|(c, n)| **n > 0 && c.as_str() != "stale").map(|(c, n)| format!("{c}={n}")).collect(); + if v.is_empty() { "none".to_string() } else { v.join(",") } + }; println!( - "{} STATUS miners={miners} workers={workers} accepted={} stale={} rejected={} hashrate={:.0} H/s blocks={} confirmed={} orphans={} window={:.3} blocks/{} shares check_ms n={n} mean={mean:.3} p50={p50:.3} p99={p99:.3} max={max:.3}", + "{} STATUS miners={miners} workers={workers} accepted={} stale={} rejected={} hashrate={:.0} H/s blocks={} confirmed={} orphans={} window={:.3} blocks/{} shares check_ms n={n} mean={mean:.3} p50={p50:.3} p99={p99:.3} max={max:.3} rejected_by_code(ledger)={} rejected_by_code(this run)={}", state::unix_ms(), s.accepted, s.stale, @@ -272,7 +276,9 @@ async fn main() { s.blocks_confirmed, s.blocks_orphaned, s.pplns.total_weight, - s.pplns.shares.len() + s.pplns.shares.len(), + by_code(&s.rejected_by_code), + by_code(&s.rejected_by_code_run) ); } }); diff --git a/pool/src/server.rs b/pool/src/server.rs index 554d37f59..3edc334ea 100644 --- a/pool/src/server.rs +++ b/pool/src/server.rs @@ -329,7 +329,7 @@ async fn handle_share(pool: &Arc, m: &Arc, job_id: u64, nonce: u64 Err(code) => { { let mut s = pool.state.lock().unwrap(); - s.refuse_share(&m.address, &m.worker, code == Code::Stale, None); + s.refuse_share(&m.address, &m.worker, code.as_str(), None); } { let mut g = m.inner.lock().unwrap(); @@ -352,7 +352,7 @@ async fn handle_share(pool: &Arc, m: &Arc, job_id: u64, nonce: u64 if v.code != Code::Ok { { let mut s = pool.state.lock().unwrap(); - s.refuse_share(&m.address, &m.worker, false, Some(v.cost_ms)); + s.refuse_share(&m.address, &m.worker, v.code.as_str(), Some(v.cost_ms)); } m.inner.lock().unwrap().rejected += 1; m.send(Msg::ShareResult { job_id, nonce: hex_u64(nonce), accepted: false, code: v.code.as_str().into(), weight: 0.0, block: false }.line()); diff --git a/pool/src/state.rs b/pool/src/state.rs index 8ca4aabeb..e125675a8 100644 --- a/pool/src/state.rs +++ b/pool/src/state.rs @@ -3,7 +3,7 @@ use crate::pplns::{Payee, Pplns, ShareRec}; use serde::{Deserialize, Serialize}; -use std::collections::{HashMap, VecDeque}; +use std::collections::{BTreeMap, HashMap, VecDeque}; use std::path::Path; pub const WEI_PER_IGN: u128 = 1_000_000_000_000_000_000; @@ -110,6 +110,14 @@ pub struct State { /// Q72: addresses already alerted for silence (cleared when they send again) #[serde(default)] pub alerted: HashMap, + /// Rejections by code over the ledger's life (persisted with it, as `rejected` is: 7 October 2026, Devnet 3 + /// pool-b, the daemon's STATUS said rejected=235,016 while its member had not one rejected line in the hour; + /// the daemon's counter is the ledger's lifetime, the member's is its own run) + #[serde(default)] + pub rejected_by_code: BTreeMap, + /// Rejections by code since this process started (what a reader beside a member's own counters wants) + #[serde(skip)] + pub rejected_by_code_run: BTreeMap, #[serde(skip)] pub dirty: bool, } @@ -136,6 +144,8 @@ impl State { check_ms: VecDeque::new(), history: HashMap::new(), alerted: HashMap::new(), + rejected_by_code: BTreeMap::new(), + rejected_by_code_run: BTreeMap::new(), dirty: false, } } @@ -245,8 +255,9 @@ impl State { out } - pub fn refuse_share(&mut self, address: &str, worker: &str, stale: bool, cost_ms: Option) { + pub fn refuse_share(&mut self, address: &str, worker: &str, code: &str, cost_ms: Option) { let now = unix_ms(); + let stale = code == "stale"; if let Some(c) = cost_ms { self.check_ms.push_back(c); } @@ -255,6 +266,8 @@ impl State { } else { self.rejected += 1; } + *self.rejected_by_code.entry(code.to_string()).or_insert(0) += 1; + *self.rejected_by_code_run.entry(code.to_string()).or_insert(0) += 1; let w = self.miner(address, worker, now); if stale { w.stale += 1