diff --git a/src/test/consensus/SteppingCsf_test.cpp b/src/test/consensus/SteppingCsf_test.cpp index 7789cde292..2135583481 100644 --- a/src/test/consensus/SteppingCsf_test.cpp +++ b/src/test/consensus/SteppingCsf_test.cpp @@ -50,6 +50,8 @@ class SteppingCsf_test : public beast::unit_test::suite std::uint64_t clampHits = 0; std::int64_t requestedMs = 0; std::int64_t consumedMs = 0; + // Highest virtual time one beat consumed. A pinned 2000 is that + // observed maximum, not a fixed per-beat budget. std::int64_t maxConsumedBeatMs = 0; std::int64_t schedulerMs = 0; std::uint64_t heartbeatEvents = 0; @@ -553,7 +555,7 @@ class SteppingCsf_test : public beast::unit_test::suite out.minValidated = net.minValidatedSeq(); for (std::uint32_t i = 0; i < 5; ++i) out.maxValidated = std::max(out.maxValidated, net.validSeq(i)); - out.forkCheckedSeqs = out.maxValidated >= 2 ? out.maxValidated - 1 : 0; + out.forkCheckedSeqs = net.forkCheckedSeqs(); out.clampHits = stats.clampHits; out.requestedMs = asMs(stats.requestedVirtualAdvance); out.consumedMs = asMs(stats.consumedVirtualAdvance); diff --git a/src/test/consensus/SteppingDeterminism_test.cpp b/src/test/consensus/SteppingDeterminism_test.cpp index 086d6c79ae..1d181e64ad 100644 --- a/src/test/consensus/SteppingDeterminism_test.cpp +++ b/src/test/consensus/SteppingDeterminism_test.cpp @@ -225,7 +225,7 @@ class SteppingDeterminism_test : public beast::unit_test::suite out.minValidated = net.minValidatedSeq(); for (std::uint32_t i = 0; i < 3; ++i) out.maxValidated = std::max(out.maxValidated, net.validSeq(i)); - out.forkCheckedSeqs = out.maxValidated >= 2 ? out.maxValidated - 1 : 0; + out.forkCheckedSeqs = net.forkCheckedSeqs(); out.clampHits = stats.clampHits; out.requestedMs = asMs(stats.requestedVirtualAdvance); out.consumedMs = asMs(stats.consumedVirtualAdvance); diff --git a/src/test/consensus/SteppingTrust_test.cpp b/src/test/consensus/SteppingTrust_test.cpp index dfb678fcf3..310777bcca 100644 --- a/src/test/consensus/SteppingTrust_test.cpp +++ b/src/test/consensus/SteppingTrust_test.cpp @@ -562,7 +562,7 @@ class SteppingTrust_test : public beast::unit_test::suite out.minValidated = net.minValidatedSeq(); for (std::uint32_t i = 0; i < kForkPeers; ++i) out.maxValidated = std::max(out.maxValidated, net.validSeq(i)); - out.forkCheckedSeqs = out.maxValidated >= 2 ? out.maxValidated - 1 : 0; + out.forkCheckedSeqs = net.forkCheckedSeqs(); out.clampHits = stats.clampHits; out.requestedMs = asMs(stats.requestedVirtualAdvance); out.consumedMs = asMs(stats.consumedVirtualAdvance); diff --git a/src/test/jtx/HarnessScheduler.h b/src/test/jtx/HarnessScheduler.h index 4db5ff1f79..4e83855176 100644 --- a/src/test/jtx/HarnessScheduler.h +++ b/src/test/jtx/HarnessScheduler.h @@ -59,6 +59,11 @@ public: // advances time in the same units it will later drive. using time_point = std::chrono::steady_clock::time_point; using duration = std::chrono::steady_clock::duration; + // Fingerprints fold duration::count() as-is. This clock is nanoseconds; + // a coarser period would move every pinned fingerprint. + static_assert( + std::ratio_equal_v, + "trace fingerprints assume a nanosecond steady_clock"); // Priority class among events at the SAME virtual instant (lower runs // first). Spaced by 10 so intermediate tiers can be slotted in without @@ -93,8 +98,9 @@ public: }; // PROVENANCE, not ordering: what CREATED an event. Orthogonal to Tier (its - // same-instant priority). Used in fence-violation diagnostics today and in - // beat traces later; never consulted for scheduling decisions. + // same-instant priority). The queue orders by (when, tier, nodeId, seq), + // so Kind is not a sort key. Profiled pacing does consult it: event cost + // scales by the kind weight. enum class Kind { other = 0, heartbeat, // a driver's per-node heartbeat trigger @@ -231,8 +237,19 @@ public: [[nodiscard]] duration eventCost(std::uint32_t nodeId, Kind kind) const { - return unitCost * static_cast(k) * - static_cast(eventWeight(nodeId, kind)); + auto const scale = static_cast(k) * + static_cast(eventWeight(nodeId, kind)); + if (unitCost.count() < 0) + Throw( + "HarnessScheduler::ProfiledPacer event cost overflow"); + auto const unit = static_cast(unitCost.count()); + if (unit != 0 && + scale > static_cast( + std::numeric_limits::max()) / + unit) + Throw( + "HarnessScheduler::ProfiledPacer event cost overflow"); + return unitCost * static_cast(scale); } [[nodiscard]] duration diff --git a/src/test/jtx/SteppingNetwork.h b/src/test/jtx/SteppingNetwork.h index 63a5996b61..2c85f79d86 100644 --- a/src/test/jtx/SteppingNetwork.h +++ b/src/test/jtx/SteppingNetwork.h @@ -1567,6 +1567,19 @@ public: return true; } + // How many sequences validatedForkFree walks: 2 through the highest live + // validated sequence, inclusive. That is max-1 when max >= 2. Not a count + // of pairwise hash comparisons. + [[nodiscard]] std::uint32_t + forkCheckedSeqs() + { + std::uint32_t hi = 0; + for (std::uint32_t i = 0; i < net_.size(); ++i) + if (net_.isLive(i)) + hi = std::max(hi, validSeq(i)); + return hi >= 2 ? hi - 1 : 0; + } + [[nodiscard]] std::shared_ptr ledger(std::uint32_t node, std::uint32_t seq) {