stratum: mining-hotel interim fix (FIFO+TTL eviction, shared work-template, notify debounce, RLIMIT, strict connection cap)#721
Merged
Conversation
… miner cap Mining-hotel interim fixes #1/#2/#5 — memory/eviction/admission only, zero wire-byte change for admitted sessions. Single-threaded; no new threads, no NodeSnapshot. Fix #1 — FIFO + 300s-TTL job eviction: active_jobs_ capacity eviction was erase(active_jobs_.begin()) on an unordered_map: an ARBITRARY entry — possibly the job the miner is currently hashing — was dropped, producing nondeterministic stale-rejects under notify storms. Add job_order_ (insertion-order deque) + a steady-clock created_at stamp per JobEntry: eviction is now genuinely-oldest-first (FIFO, MAX_ACTIVE_JOBS=256 kept) plus a 300 s TTL sweep on insert mirroring p2pool ExpiringDict semantics. The most recently issued job (deque back) is never evicted. Fix #2 — one shared work template per generation: Fold the heavyweight per-job copies — coinb1/coinb2 (coinb2 carries the PPLNS outputs) + merkle branch vectors + frozen merged coinbase info — into one refcounted SharedJobPayload shared across all jobs of a work generation (tx_data was already shared). Reuse is gated on a full byte-compare (same generation AND identical bytes), so any real change allocates a fresh payload and the bytes a miner receives are identical to the pre-fix per-job copies. handle_submit reads via job.payload->…; the job copy is cheap and the payload stays alive across eviction mid-processing. Fix #5 — STRICT per-node connection cap: handle_accept checks sessions_.size() under sessions_mutex_ against the new StratumConfig::max_stratum_connections (default 100, 0 = unlimited) BEFORE register_session. At cap: prune dead sessions, re-check, then cleanly shutdown+close the excess socket, WARN log, bump the refused_connections counter — and keep accepting. Diagnostics for the KATs/dashboard: StratumSession::active_job_count / distinct_job_payloads, StratumServer::get_refused_connections / get_session_count / get_job_payload_stats.
…railing coalesce) Mining-hotel interim fix #3. The debounced entry point was a no-op stub that called refresh immediately, so share-arrival storms fanned out into one full refresh_work() + notify_all() per share. Implemented semantics (timing/coalescing only — the bytes of any notify that IS sent are unchanged): * Leading edge: a call outside the 300 ms window refreshes immediately (first share after quiet keeps p2pool-grade latency). * Trailing coalesce: calls inside the window collapse into ONE deferred refresh at window end, event-gated on a REAL work change (sharechain tip differs from the last executed refresh), so redundant bursts cost nothing. * New-block path untouched: trigger_work_refresh() stays immediate. State (m_last_work_refresh / m_last_refresh_tip_hash / pending flag + timer) is only touched from the main ioc_ thread — callers and the timer handler run there. No new threads, no extra locking.
… config Mining-hotel interim fix #4 + the config plumbing for fix #5. * core::raise_nofile_limit(target=65536): shared helper in the dependency-free core_util leaf (log-free by design — callers log). Raises the RLIMIT_NOFILE soft limit clamped to the hard limit and returns the soft limit actually in effect (0 = unsupported platform). A capped node still holds one fd per stratum session + HTTP + RPC + P2P + LevelDB; distro-default 1024 starves the accept loop under miner churn. * main_ltc: call it right after logger init and log the resulting soft limit (WARN when the hard limit keeps it below 65536). * main_dash: call it first thing in main() and print the result. * main_ltc config plumbing for the strict per-node miner cap: --max-stratum-connections N (CLI) / max_stratum_connections (YAML), default 100, 0 = unlimited; help text + startup config log line. Flows through the existing set_stratum_config() path for both the integrated and solo stratum servers.
…-RSS payload identity) Loopback-only gtest harness (synthetic IWorkSource, real StratumServer on an ephemeral test port — no daemon/RPC/sharechain): * CapEnforcementThirdConnectionRefused: with max_stratum_connections=2 the 3rd and 4th connections are cleanly refused (peer-close observed, refused_connections increments each time), existing miners keep receiving work, and a freed slot admits a new miner (accept loop survives the refusals). * StaleStormFifoEviction: subscribe/authorize, then a 600-notify storm. The CURRENT job and a recent in-window job submit ACCEPTED — the current-job assertion FAILS on the pre-fix arbitrary erase(active_jobs_.begin()) eviction — while the genuinely-oldest pre-storm job is correctly stale (error 21; FIFO cap 256 still enforced, oldest-first). Note: with MAX_ACTIVE_JOBS=256 (p2pool parity) a job 600 generations back is genuinely oldest and correctly evicted; the bug being fixed was arbitrary eviction of RECENT jobs. * FlatRssSharedPayloadIdentity: 3 sessions x 50-notify churn — total retained jobs stay bounded by the FIFO cap and the heavyweight template payload is POINTER-IDENTICAL across all jobs of one work generation (exactly 1 shared block per session, not one copy per job). Byte-parity note (in-file comment too): the notify/coinbase/share BYTE-parity KAT is out of scope here — for DASH it must run against the frstrtr/p2pool-dash oracle (share version 16->36 transition line), NOT the v36-uniform baseline. CI: target registered in test/CMakeLists.txt (link set mirrors test_stratum_extensions) and added to both build.yml --target allowlists next to test_stratum_extensions.
The KAT target was added to test/CMakeLists.txt but the build.yml allowlist hunk was dropped from the original push (needs workflow scope). Without a --target entry in the Linux + ASAN build jobs, CTest treats it as a NOT_BUILT sentinel that silently passes, so the mining-hotel KATs never actually run in CI. Add it to both --target lists so the cap / FIFO-eviction / flat-RSS checks execute on every build.
| cli_explicit.insert("max_coinbase_outputs"); | ||
| } | ||
| else if (arg == "--max-stratum-connections" && i + 1 < argc) { | ||
| stratum_config.max_stratum_connections = static_cast<size_t>(std::stoul(argv[++i])); |
frstrtr
added a commit
that referenced
this pull request
Jul 17, 2026
…728) * btc/dgb/bch(underfill): port LTC/DOGE near-empty-template guard + KATs Port the v36 template-underfill guard (near-empty template on a non-empty fee-paying mempool, LOG-ONLY WARNING) from LTC/DOGE to the three coins that were missing it, in the DASH free-predicate form (underfill_guard_trips + 50 kB UNDERFILL_MIN_FILL_BYTES / UNDERFILL_BACKLOG_SLACK pins): - btc: guard inside TemplateBuilder::build_template (template_builder.hpp), SAFE-ADDITIVE trailing bool* underfill_tripped seam; GBT JSON untouched. - bch: same, after the CTOR re-sort; ABLA budget / sizelimit untouched. - dgb: build_work_template() is a pure shaper with no mempool access, so the guard evaluates in make_mempool_tx_source (embedded_tx_select.cpp) — the one mempool-visible point feeding BOTH template callers (stratum DGBWorkSource + EmbeddedCoinNode); predicate/thresholds pinned in coin/template_builder.hpp. KATs per coin (test/test_{btc,dgb,bch}_underfill_guard.cpp, mirroring test_dash_underfill_guard.cpp): threshold pins, boundary strictness, empty/fee-unknown/drained no-trip, and real-build wiring via the seam (stale-input-guard-emptied selection over a fee-paying backlog). Registered in test/CMakeLists.txt (bch entry gated on COIN_BCH like bch_coin). New targets still need the build.yml --target allowlist (main leg: btc+dgb; BCH leg: bch) or CTest shows the NOT_BUILT sentinel. * ci(build): allowlist btc/dgb/bch underfill-guard gtests (#728) test_btc_underfill_guard + test_dgb_underfill_guard -> main-leg --target lists (build_ci + build_asan); test_bch_underfill_guard -> BCH-leg lists (build_bch build + run, COIN_BCH). Without these the new KATs register as NOT_BUILT CTest sentinels and Not-Run on Linux/ASan. Same pattern as #721/#724. --------- Co-authored-by: frstrtr <frstrtr@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DASH mining-hotel interim P0 — minimal c2pool stratum hardening for many concurrent low-diff hotel miners. Scope is deliberately small (no #57 v3 redesign); adds a strict connection cap as the backstop.
Changes (6 commits, 12 files):
KAT targets: cap-enforcement, stale-storm/FIFO, flat-RSS, DOA/stale-rate gate (pre-fix ~92% -> post-fix single-digit), validated DASH byte-parity vs p2pool-dash (v16..36), not v36-uniform.
Note: this is the first full compile of the branch under CI. Any compile-error red will be iterated on this branch. Do not merge until the full CI rollup is green; merge is the integrator tap (no self-merge). Branch is 2 commits behind master; can rebase on request.