Skip to main content
The pack subsystem prioritizes and schedules Solana transactions to maximize validator profitability while respecting consensus-critical resource limits.

Overview

Firedancer’s pack system:
  • Maintains a pool of pending transactions
  • Prioritizes transactions by fee/compute unit ratio
  • Schedules transactions into microblocks
  • Respects consensus-critical limits (cost, data size)
  • Handles account conflicts and write locks
  • Supports atomic transaction bundles
  • Tracks per-account write costs

Core Concepts

Transaction Ordering

Transactions are prioritized by:
The pack system uses:
  • rewards: Transaction fee in lamports
  • compute_est: Estimated compute units
Transactions with higher reward-to-compute ratios are scheduled first.

Consensus-Critical Limits

These limits are either consensus-critical (agreed cluster-wide) or implementation-specific (Firedancer memory bounds). A block exceeding consensus limits is invalid.

Microblock Overhead

Empty microblocks are dropped to avoid this overhead.

Data Structures

Transaction Ordering Structure

The structure is carefully designed with no padding between the union and other fields, allowing pointer casting between fd_txn_p_t and the full structure.

Account Address Usage

This structure serves three purposes:
  1. Track which accounts are in use by executing microblocks
  2. Track total write cost per account (consensus limit)
  3. Track bundle-specific account usage

Transaction Trees

Pack maintains multiple treaps (randomized binary search trees) for different transaction categories:

Root Types

Pending treap: Regular transactions ordered by reward/compute Vote treap: Vote transactions (separate cost limit) Bundle treap: Atomic transaction bundles Penalty treaps: Transactions blocked on specific accounts

Penalty Treaps

When a transaction can’t be scheduled due to account conflicts:
  1. Moved to penalty treap for the conflicting account
  2. Checked again when that account becomes available
  3. Avoids repeatedly checking transactions that can’t execute

Bitset Optimization

Pack uses bitsets to quickly check account conflicts:

Bitset Assignment

Special bit values:
  • FD_PACK_BITSET_FIRST_INSTANCE: Account referenced by only one transaction
  • FD_PACK_BITSET_SLOWPATH: Too many accounts, use slow path
Accounts referenced by only one transaction don’t need a bit allocated. This optimization is important as most accounts are only touched by a single transaction.

Bundle Support

Bundles are sequences of 1-5 transactions that execute atomically:

Bundle Flags

Initializer Bundle State Machine

Initializer bundles must succeed before normal bundles can be scheduled:
  1. NOT_INITIALIZED: Block starts, no initializer bundle
  2. PENDING: Initializer bundle scheduled, awaiting result
  3. FAILED: Initializer bundle failed (bug, insufficient balance, expired blockhash)
  4. READY: Initializer bundle succeeded, normal bundles can proceed

Scheduling Algorithm

Account Conflict Detection

Before scheduling a transaction:

Scheduling Steps

  1. Select highest priority transaction from appropriate treap
  2. Check resource limits: cost, data bytes, microblock capacity
  3. Check account conflicts: read/write locks, write cost limits
  4. Reserve accounts: mark as in-use by this microblock
  5. Update resource usage: cost, data bytes, write costs
  6. Add to microblock: append transaction to current microblock
  7. Repeat until microblock full or no schedulable transactions

Skip Counter Optimization

If a transaction is skipped multiple times for persistent reasons:
  • Increment skip counter
  • Skip quickly without full conflict checking
  • Compressed slot number stored when threshold reached
  • Re-evaluated when slot changes

Write Cost Tracking

Per-account write costs are tracked to enforce consensus limits:
If more than 16,384 accounts are written in a block:
  • Clear entire writer cost map instead of individual entries
  • Avoids overhead of tracking too many individual accounts

Top Writers Heap

Tracks the top 5 accounts by write cost in each slot:
Useful for monitoring and metrics.

Fee Model

Fee Per Signature

Fee Burn Percentage

Half of transaction fees are burned, half go to the validator.

Resource Limits

Data Size Limit

Pack limits data size to ensure the shred tile can process the block without exceeding shred limits.

Cost Limits

Pack doesn’t know exactly how the block will be shredded (separation of concerns), so it uses conservative data size limits that guarantee the shred tile won’t exceed shred counts.

Expiration Handling

Expiration Priority Queue

Transactions are tracked in an expiration priority queue:
  • Ordered by expiration time
  • Periodically checked for expired transactions
  • Expired transactions removed from all data structures

Invariants

The priority queue maintains back-pointers so transactions can be efficiently removed when scheduled or deleted.

Bank Tile Integration

Pack can schedule to multiple bank tiles:

Account Usage Bitmask

Each bit represents one bank tile:
  • Bit set: account in use by that bank
  • Allows parallel execution on different banks
  • Account can’t be used by same bank until microblock completes

Performance Considerations

Memory Layout

Critical fields are cache-line aligned to avoid false sharing between CPU cores.

Non-Temporal Memcpy

Uses non-temporal memory copies to avoid polluting CPU cache when copying transaction data.

Address Sanitizer Workaround

Sorting code has excessive ASan slowdown, so sanitizers are disabled for performance.

Smallest Transaction Tracking

Tracks the smallest transaction in each treap:
  • If remaining space < smallest transaction, skip entire treap
  • Avoids checking transactions that can’t possibly fit
  • Conservative estimate maintained as transactions are deleted
Both CU and byte limits matter, so track both dimensions.