pruner

package
v0.16.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 9, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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.

View Source
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

func HeaderByHashIfStateRetained(r db.KeyValueReader, blockHash *felt.Felt) (*core.Header, error)

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

func HeaderByNumberIfStateRetained(r db.KeyValueReader, blockNumber uint64) (*core.Header, error)

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):

  1. 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.

  2. 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

type BlockPrunedError struct {
	BlockNumber    uint64
	OldestRetained uint64
}

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 EventListener interface {
	OnPrune(oldestBlockKept uint64, blocksPruned uint64, took time.Duration)
	OnPruneError(err error)
	OnL1Stale()
}

type Option

type Option func(*options)

func WithFloorTickInterval

func WithFloorTickInterval(duration time.Duration) Option

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

func WithL2HeadsPerPrune(n uint64) Option

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

func WithMinAge(duration time.Duration) Option

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

func WithTargetBatchByteSize(size int) Option

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

func (p *Pruner) Run(ctx context.Context) error

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)

Directories

Path Synopsis
Package testutils provides shared fixtures for tests that exercise the pruner's data layout.
Package testutils provides shared fixtures for tests that exercise the pruner's data layout.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL