Documentation
¶
Overview ¶
Package pruner implements a background service that bounds on-disk storage by deleting block data older than a configurable retention window.
The retention floor is anchored on the lower of the L1-confirmed head and the local L2 head: floor = min(l1Head, l2Head) - numRetainedBlocks. In normal operation L1 lags L2, so the floor tracks L1; during catch-up where L2 is behind L1, we conservatively anchor on L2 instead. Either way the floor stays at or below L1, so pruned blocks cannot be reorged. The upper bound is the L2 head simply because that is the highest block the node has.
In addition to the block-count floor, the pruner enforces an optional wallclock minimum-age floor (see WithMinAge): blocks whose on-chain timestamp is younger than minAge are protected from pruning. The combined floor is min(standardFloor, minAgeFloor). The floor is suppressed in the L2 path during deep catch-up sync, where incoming blocks carry historical timestamps.
The Pruner runs as a service.Service. It listens to two trigger feeds — new L2 heads and new L1 heads — to know when to re-evaluate the floor; the events themselves are only triggers, the retention bound is always recomputed from the L1 head.
Index ¶
- Variables
- func FindOldestBlockAtOrAfter(database db.KeyValueStore, lower, upper uint64, cutoff time.Time) (uint64, error)
- func HeaderByHashIfStateRetained(r db.KeyValueReader, blockHash *felt.Felt) (*core.Header, error)
- func HeaderByNumberIfStateRetained(r db.KeyValueReader, blockNumber uint64) (*core.Header, error)
- func InitializeRunningEventFilter(database db.KeyValueStore) (*core.RunningEventFilter, error)
- func OldestRetainedBlock(r db.KeyValueReader) (uint64, error)
- func PruneBlockDataUpto(w db.KeyValueRangeDeleter, rangeEndExclusive uint64) error
- func PruneUpto(ctx context.Context, database db.KeyValueStore, endExclusive uint64, ...) (blocksPruned, oldestKept uint64, err error)
- func RequireRetained(r db.KeyValueReader, blockNumber uint64) error
- type BlockPrunedError
- type EventListener
- type Option
- type Pruner
- type SelectiveListener
Constants ¶
This section is empty.
Variables ¶
var ErrBlockPruned = errors.New("block has been pruned")
ErrBlockPruned is the sentinel matched by errors.Is for any BlockPrunedError. Use errors.As when the requested-block / oldest-retained fields are needed.
var ErrNoBlockInWindow = errors.New("no block in window")
ErrNoBlockInWindow is returned by FindOldestBlockAtOrAfter when no block in [lower, upper] has Timestamp >= cutoff.
Functions ¶
func FindOldestBlockAtOrAfter ¶
func FindOldestBlockAtOrAfter( database db.KeyValueStore, lower, upper uint64, cutoff time.Time, ) (uint64, error)
FindOldestBlockAtOrAfter binary-searches block numbers in [lower, upper] for the smallest one whose Header.Timestamp is at or after cutoff. The caller must pass a range of fully-retained blocks.
func HeaderByHashIfStateRetained ¶
HeaderByHashIfStateRetained returns the header for blockHash only if state at that block is queryable. Use this for state access only; for block-level data use RequireRetained + the plain core accessors instead. See PruneUpto for the carve-out semantics. Returns db.ErrKeyNotFound when state at blockHash is not available.
func HeaderByNumberIfStateRetained ¶
HeaderByNumberIfStateRetained returns the header for blockNumber only if state at that block is queryable. Use this for state access only; for block-level data (transactions, receipts, etc.) use RequireRetained + the plain core accessors instead — the two checks diverge at oldestKept-1, where state is preserved by carve-out but block-level data is not. See PruneUpto for the carve-out semantics. Returns db.ErrKeyNotFound when state at blockNumber is not available.
func InitializeRunningEventFilter ¶
func InitializeRunningEventFilter(database db.KeyValueStore) (*core.RunningEventFilter, error)
InitializeRunningEventFilter is the pruning-aware initializer. Brings the running event filter up to chain head, reusing the persisted snapshot when possible and rebuilding otherwise. Use core.InitializeRunningEventFilter on non-pruning nodes.
func OldestRetainedBlock ¶
func OldestRetainedBlock(r db.KeyValueReader) (uint64, error)
OldestRetainedBlock returns the lowest block number still fully retained, found by scanning BlockCommitments — the source of truth, since it has no carve-out (see PruneUpto). Returns db.ErrKeyNotFound on an empty database.
func PruneBlockDataUpto ¶
func PruneBlockDataUpto(w db.KeyValueRangeDeleter, rangeEndExclusive uint64) error
PruneBlockDataUpto prunes the number-keyed block data for every block strictly below rangeEndExclusive.
Deleted for every block below rangeEndExclusive:
- block commitments
- state update
- block transactions
- aggregated bloom filters fully below rangeEndExclusive
Retained below rangeEndExclusive (intentional carve-out):
- Block headers in [rangeEndExclusive-BlockHashLag, rangeEndExclusive). The get_block_hash_syscall, run during execution of any block in [rangeEndExclusive, rangeEndExclusive+BlockHashLag), reads a header by number from this window — pruning it would break the syscall.
func PruneUpto ¶
func PruneUpto( ctx context.Context, database db.KeyValueStore, endExclusive uint64, targetBatchByteSize int, ) (blocksPruned, oldestKept uint64, err error)
PruneUpto deletes every block strictly below endExclusive. Returns the number of blocks actually pruned (which may be less than the full window if ctx is cancelled mid-loop).
Resumes automatically. The lower bound of the per-block sweep is derived from OldestRetainedBlock — i.e., wherever the previous call left off — so two consecutive calls do no overlapping per-block work. If a previous call exited mid-loop (ctx cancelled), the next call picks up from the same block. If endExclusive is at or below the current oldest kept block, the call is a no-op.
Deleted for every block in [oldestKept, endExclusive):
- block commitments
- state update
- block transactions and their hash → (block, index) reverse lookups
- L1 handler message-hash → tx-hash reverse lookups
- contract storage / nonce / class-hash history snapshots
Retained below endExclusive (intentional carve-outs):
Block headers in [endExclusive-BlockHashLag, endExclusive). The get_block_hash_syscall (see core.BlockHashLag), run during execution of any block in [endExclusive, endExclusive+BlockHashLag), reads a header by number from this window — pruning it would break the syscall.
The hash → number mapping for endExclusive-1. Resolving StateAtBlockHash(endExclusive.parentHash) needs this single mapping; it is cleaned up by the next PruneUpto call's oldestKept-1 sweep, so between calls exactly one extra mapping survives below endExclusive.
ctx cancellation aborts the per-block loop after the current iteration; any work already queued plus the range delete for the partial window is still flushed, so the next call resumes from where this one stopped.
Returns (blocksPruned, oldestKept, err): blocksPruned is how many blocks this call removed, and oldestKept is the lowest block number still present after the call (= blockNum reached by the loop, which equals endExclusive on full completion). On the no-op paths (empty database, endExclusive ≤ existing oldest), oldestKept reflects the unchanged pre-call state — 0 if the database is empty.
func RequireRetained ¶
func RequireRetained(r db.KeyValueReader, blockNumber uint64) error
RequireRetained returns nil if blockNumber is fully retained, otherwise a *BlockPrunedError. Retention is probed via BlockCommitments — the source of truth for "block has not been pruned", since it has no carve-out (see PruneUpto).
Types ¶
type BlockPrunedError ¶
BlockPrunedError reports that a requested block has been removed by the pruner. OldestRetained is zero when the node has no retained blocks (empty database) or when the lookup itself failed.
func (*BlockPrunedError) Error ¶
func (e *BlockPrunedError) Error() string
func (*BlockPrunedError) Is ¶
func (e *BlockPrunedError) Is(target error) bool
type EventListener ¶
type Option ¶
type Option func(*options)
func WithFloorTickInterval ¶
WithFloorTickInterval overrides how often the min-age floor is refreshed via binary search. Smaller values tighten the floor's drift bound at the cost of more frequent refreshes.
func WithL2HeadsPerPrune ¶
WithL2HeadsPerPrune overrides how many L2 head events the L2 path coalesces before triggering a prune. See [defaultL2HeadsPerPrune].
func WithListener ¶
func WithListener(listener EventListener) Option
func WithMinAge ¶
WithMinAge enables the wallclock minimum-age floor: blocks whose on-chain timestamp is younger than duration are protected from pruning, in addition to the standard --prune-mode block-count floor. Zero disables the floor.
func WithTargetBatchByteSize ¶
type Pruner ¶
type Pruner struct {
// contains filtered or unexported fields
}
Pruner deletes block data older than a retention window in response to new-head events. Construct via New; do not zero-value.
func New ¶
func New( database db.KeyValueStore, retainedBlocks uint64, newHeadSub *feed.Subscription[*core.Block], l1HeadSub *feed.Subscription[*core.L1Head], logger log.StructuredLogger, opts ...Option, ) *Pruner
New constructs a Pruner. retainedBlocks is the number of blocks retained below the retention pivot (= min(l1Head, l2Head); the pivot itself is always retained), so the pruner keeps blocks in [pivot - retainedBlocks, l2Head] and deletes everything below. newHeadSub and l1HeadSub are the two trigger feeds (see Pruner). Subscriptions are feed.Subscription.Unsubscribe'd when Pruner.Run returns.
func (*Pruner) Run ¶
Run drives the prune loop until ctx is cancelled. It dispatches each new L2 or L1 head event to the corresponding handler. Errors from a handler are logged but do not stop the loop — errors returned from service terminates the node. After some time without L1 heads received, warnings are logged periodically. With no L1 heads the pruner cannot work.
type SelectiveListener ¶
type SelectiveListener struct {
OnPruneCb func(oldestBlockKept uint64, blocksPruned uint64, took time.Duration)
OnPruneErrorCb func(err error)
OnL1StaleCb func()
}
func (*SelectiveListener) OnL1Stale ¶
func (l *SelectiveListener) OnL1Stale()
func (*SelectiveListener) OnPrune ¶
func (l *SelectiveListener) OnPrune( oldestBlockKept uint64, blocksPruned uint64, took time.Duration, )
func (*SelectiveListener) OnPruneError ¶
func (l *SelectiveListener) OnPruneError(err error)