Skip to content

sgns::TxPruner

The real prune-cycle service (PRUNE-04). More...

#include <TxPruner.hpp>

Public Functions

Name
outcome::result< std::shared_ptr< TxPruner > > New(NodeType node_type, const PruningConfig & config)
Fail-closed factory (the construction gate).
bool IsPermanentStop(const std::error_code & error)
Whether a RunCycle error is a permanent-stop class.
void SetCycleInterruptHookForTest(std::function< bool(int step)> hook)
Test-only interruption seam for the cycle.
outcome::result< PruneCycleStats > RunCycle(crdt::GlobalDB & globaldb, TransactionManager & transaction_manager, UTXOManager & utxo_manager, uint64_t generation)
Runs ONE erase cycle over the passed dependencies.
PruneStatus GetPruneStatus() const
Returns a copy of the current status (RECL-04).

Public Attributes

Name
constexpr int kInterruptAfterEvaluation
Interrupt seam step: after Evaluate(), before grouping.
constexpr int kInterruptBeforeErase
constexpr int kInterruptBeforeStatus
Interrupt seam step: after the erase loop, before the status update.
constexpr int kInterruptAfterStatus

Detailed Description

class sgns::TxPruner;

The real prune-cycle service (PRUNE-04).

Timer-driven ONLY (D-C6): never invoked from CRDT element callbacks (they fire pre-batch-commit). Construction fails closed unless the node role is Light (GATE-01 — the PruneEligibility::New role-gate mirror; full/archive never erase history); the PruningConfig default-disabled consent gate is the wiring's duty (SC-5) and is not re-checked here.

Public Functions Documentation

function New

static outcome::result< std::shared_ptr< TxPruner > > New(
    NodeType node_type,
    const PruningConfig & config
)

Fail-closed factory (the construction gate).

Parameters:

  • node_type Deployment role of the constructing node.
  • config Validated pruning policy (the caller — GeniusNode — owns the consent gate).

Return: The service, or outcome::failure for a non-Light role.

Mirrors PruneEligibility::New: rejects any role other than NodeType::Light with std::errc::operation_not_permitted before any state exists (GATE-01).

function IsPermanentStop

static bool IsPermanentStop(
    const std::error_code & error
)

Whether a RunCycle error is a permanent-stop class.

Parameters:

  • error A RunCycle failure code.

Return: true when the caller must stop instead of reschedule.

The 17-06 fail-closed error split re-hosted as ONE classification home: std::errc::invalid_argument (Evaluate's fail-closed no-window contract) and std::errc::operation_not_permitted (a non-Light role at either factory) are broken policy states no retry can fix — the caller stops the cycle instead of spinning on it every 10 minutes. Every other error is transient (skip this tick, keep the cadence).

function SetCycleInterruptHookForTest

static void SetCycleInterruptHookForTest(
    std::function< bool(int step)> hook
)

Test-only interruption seam for the cycle.

Parameters:

  • hook invoked at the steps above; a true return aborts the cycle AT that point. An aborted cycle is NOT a completed cycle: the status (including the watermark) is left untouched for steps 1-3 — the crash-matrix positions 18-06 asserts against. Pass nullptr to clear.

Note: No production caller may invoke this method. Fixture discipline: a hook armed by a failing case must never leak further — clear it in the fixture destructor (the GlobalDB::SetEraseInterruptHookForTest shape).

function RunCycle

outcome::result< PruneCycleStats > RunCycle(
    crdt::GlobalDB & globaldb,
    TransactionManager & transaction_manager,
    UTXOManager & utxo_manager,
    uint64_t generation
)

Runs ONE erase cycle over the passed dependencies.

Parameters:

  • globaldb Committed-state reader (watermark height).
  • transaction_manager Tick-gate inputs, re-verify fetch, and the pruned-erase driver.
  • utxo_manager Fresh spent-ness re-verify.
  • generation The caller's account-generation tag.

Return: The cycle counters, or outcome::failure: an invalid_argument-class Evaluate failure (and a non-Light evaluator construction) is a PERMANENT-stop signal — see IsPermanentStop — while any other error is transient (retry next cycle). Failures leave the status untouched.

Cycle order (research Pattern 1): (1) tick gate — TransactionManager::GetState() == State::READY, !HasMissingTxHashes(), and the settle window elapsed (the clock starts when the gate FIRST fully holds, skips until settle_seconds after it, and restarts whenever the gate re-opens — a cycle cannot fire during initial sync, D-C2); a gate-skipped tick returns zeroed stats and touches no status. (2) A per-cycle PruneEligibility is constructed from the PASSED deps and Evaluate()d VERBATIM — never under any lock, and never re-implemented (the window arithmetic's one home). (3) Eligible candidates are grouped by delta-block CID; a group whose block report is erasable is taken WHOLE when it fits the remaining K budget, else DEFERRED whole (held_blocks) — never a partial group (Pitfall 1). (4) Per tx inside a taken group: a FRESH spent-ness re-verify (IsOutPointGenuinelyConsumed per outpoint) runs immediately before the erase; a reverted spend (or an unreadable value) skips the tx — never aborts the cycle; then the atomic EraseTransactionEntryPruned commits value + marker in one batch (18-03), with a per-tx failure recorded and skipped past (log-and-continue is sanctioned ONLY for this per-tx scan contract). (5) The status is updated and ONE INFO summary emitted (RECL-03).

GENERATION CONTRACT (D-C4): the supersession check is the CALLER's pre-erase duty — the wiring arm checks the generation is still current BEFORE invoking this cycle (this function is the only erase path, so the check precedes every erase). generation is carried onto the returned stats and the INFO line as the tag. Dependencies are never cached: the caller passes a consistent per-tick snapshot and holds it for the whole cycle.

function GetPruneStatus

PruneStatus GetPruneStatus() const

Returns a copy of the current status (RECL-04).

Return: The watermark + last-cycle counters.

Thread-safe: taken under the status mutex, so a concurrent reader never observes a half-updated cycle.

Public Attributes Documentation

variable kInterruptAfterEvaluation

static constexpr int kInterruptAfterEvaluation = 1;

Interrupt seam step: after Evaluate(), before grouping.

variable kInterruptBeforeErase

static constexpr int kInterruptBeforeErase = 2;

Interrupt seam step: immediately before EACH per-tx erase call (the first erase included — "between erases" plus the pre-first position; mid-compose steps are covered by GlobalDB's own seam).

variable kInterruptBeforeStatus

static constexpr int kInterruptBeforeStatus = 3;

Interrupt seam step: after the erase loop, before the status update.

variable kInterruptAfterStatus

static constexpr int kInterruptAfterStatus = 4;

Interrupt seam step: after the status update, before the INFO summary (the status IS updated at this point).


Updated on 2026-10-22 at 19:28:22 +0000