Documentation
¶
Index ¶
- Variables
- type ChainReader
- func (c *ChainReader) Head() *pending.PreConfirmed
- func (c *ChainReader) Length() int
- func (c *ChainReader) NewestFirst() iter.Seq[*pending.PreConfirmed]
- func (c *ChainReader) OldestFirst() iter.Seq[*pending.PreConfirmed]
- func (c *ChainReader) PreConfirmedStateAt(blockNumber uint64, bcReader blockchain.Reader) (core.StateReader, blockchain.StateCloser, error)
- func (c *ChainReader) PreConfirmedStateBeforeIndexAt(blockNumber uint64, index uint, bcReader blockchain.Reader) (core.StateReader, blockchain.StateCloser, error)
- func (c *ChainReader) ReceiptByHash(hash *felt.Felt) (*core.TransactionReceipt, uint64, error)
- func (c *ChainReader) TransactionByHash(hash *felt.Felt) (core.Transaction, error)
- type ChainStorage
- type DataSource
- type Poller
Constants ¶
This section is empty.
Variables ¶
var ErrBaseTxCountMismatch = errors.New("pre-confirmed base transaction count mismatch")
ErrBaseTxCountMismatch is returned when a Delta update's baseTxCount hint doesn't match the targeted slot's current tx count. Defensive against any non-poller writer (or future race) that could drift the slot between the wire send and the storage apply.
Functions ¶
This section is empty.
Types ¶
type ChainReader ¶
type ChainReader struct {
// contains filtered or unexported fields
}
ChainReader is an immutable snapshot of a contiguous run of pre-confirmed blocks, ordered newest-first via parent pointers. Iteration must respect Length — head-aligned views (see ChainStorage.SnapshotForHead) may stop before the underlying linked list's nil terminator.
func NewChain ¶
func NewChain(entries ...*pending.PreConfirmed) (ChainReader, error)
NewChain builds a ChainReader from non-nil pre-confirmed entries given in oldest-first order with contiguous block numbers. No args returns the zero-value ChainReader.
func (*ChainReader) Head ¶
func (c *ChainReader) Head() *pending.PreConfirmed
Head returns the most recent pre-confirmed in the view, or nil if empty.
func (*ChainReader) Length ¶
func (c *ChainReader) Length() int
Length is the number of entries in this chain view.
func (*ChainReader) NewestFirst ¶
func (c *ChainReader) NewestFirst() iter.Seq[*pending.PreConfirmed]
NewestFirst yields entries from the most recent down to head+1, bounded by Length.
func (*ChainReader) OldestFirst ¶
func (c *ChainReader) OldestFirst() iter.Seq[*pending.PreConfirmed]
OldestFirst yields entries from head+1 up to the most recent, bounded by Length.
func (*ChainReader) PreConfirmedStateAt ¶
func (c *ChainReader) PreConfirmedStateAt( blockNumber uint64, bcReader blockchain.Reader, ) (core.StateReader, blockchain.StateCloser, error)
PreConfirmedStateAt returns the chain's view of state at blockNumber. The chain owns base resolution: it opens the canonical state immediately below its own bottom (derived from tip - length + 1).
Returns [pending.ErrPreConfirmedNotFound] if blockNumber falls outside the chain.
func (*ChainReader) PreConfirmedStateBeforeIndexAt ¶
func (c *ChainReader) PreConfirmedStateBeforeIndexAt( blockNumber uint64, index uint, bcReader blockchain.Reader, ) (core.StateReader, blockchain.StateCloser, error)
PreConfirmedStateBeforeIndexAt returns the chain's view of state immediately before transaction `index` at blockNumber. See PreConfirmedStateAt for the base- resolution contract; here the chain additionally layers the target slot's per-transaction diffs up to (but not including) `index`. Returns pending.ErrPreConfirmedNotFound if blockNumber isn't in the chain, or pending.ErrTransactionIndexOutOfBounds if `index` exceeds the target's transaction count.
func (*ChainReader) ReceiptByHash ¶
func (c *ChainReader) ReceiptByHash( hash *felt.Felt, ) (*core.TransactionReceipt, uint64, error)
ReceiptByHash scans every chain entry's Block.Receipts. Returns the receipt and the number of the block it lives in. ErrTransactionReceiptNotFound when missing.
func (*ChainReader) TransactionByHash ¶
func (c *ChainReader) TransactionByHash(hash *felt.Felt) (core.Transaction, error)
TransactionByHash scans every chain entry's Block.Transactions.
Returns pending.ErrTransactionNotFound when missing.
type ChainStorage ¶
type ChainStorage struct {
// contains filtered or unexported fields
}
ChainStorage holds a contiguous run of pre-confirmed blocks above the canonical head. Readers obtain a head-aligned view via SnapshotForHead. Single writer (polling loop) with many concurrent readers; reads are lock-free via atomic.Pointer.
func NewChainStorage ¶
func NewChainStorage() *ChainStorage
func (*ChainStorage) AdvanceTo ¶
func (s *ChainStorage) AdvanceTo(head *core.Header) bool
AdvanceTo realigns the chain to a new canonical head. Three outcomes:
- wantBottom == bottom: chain already aligned, no-op.
- wantBottom > mostRecent (head advanced past everything we stored) OR wantBottom < bottom (head reverted below us — every entry's parent now references a discarded block): drop the whole chain. The next poll bootstraps fresh against the new head.
- bottom < wantBottom <= mostRecent: rebuild from the new bottom up so the surviving nodes nil-terminate cleanly and the dropped tail is GC-able.
Pre-pop readers retain their *ChainReader and walk the original (still intact) nodes; the new chain references only fresh nodes.
Single-writer: like ApplyUpdate, this assumes the pre-confirmed poller goroutine is the only writer.
func (*ChainStorage) ApplyUpdate ¶
func (s *ChainStorage) ApplyUpdate( update starknet.PreConfirmedUpdate, blockNumber uint64, baseTxCount uint64, head *core.Header, ) (*pending.PreConfirmed, error)
ApplyUpdate atomically evolves the stored chain from a wire-side update. blockNumber is the height targeted by the update; baseTxCount is the knownTransactionCount the poller sent (consulted only for the Delta case as a defensive race-check against the targeted slot). head MUST be the canonical chain head, or nil at genesis. Returns the affected entry, or nil when the update was a no-op (NoChange, preserved, rejected at cap, etc.).
On CAS failure the chain changed between Load and CompareAndSwap; we return an error instead of retrying.
func (*ChainStorage) SnapshotForHead ¶
func (s *ChainStorage) SnapshotForHead(head *core.Header) ChainReader
SnapshotForHead returns a head-aligned view of the pre-confirmed chain: its conceptual bottom is head.Number+1 (or 0 at genesis with head == nil), so entries at or below the canonical head are excluded via the reported length. If the stored chain briefly extends below head+1 (AdvanceTo hasn't run yet against this head advance), the view reports a shorter length, excluding the now-committed entries.
The view is uncapped: it exposes every stored entry from head+1 up to the stored tip, even when the chain runs more than core.BlockHashLag ahead of the canonical head. Reading pre-confirmed state at such a far-ahead block can fail at execution time, when a get_block_hash lookup falls outside the available window; that failure is intentionally left to the execution layer rather than masked by truncating the view here.
Returns the zero-value ChainReader (length 0) if the chain does not cover head+1 (head advanced past the stored tip, or the chain's bottom sits above head+1); callers should branch on Length().
type DataSource ¶
type DataSource interface {
PreConfirmedBlockLatest(
ctx context.Context,
identifier string,
txCount uint64,
) (starknet.PreConfirmedUpdate, uint64, error)
PreConfirmedBlockByNumber(
ctx context.Context,
blockNumber uint64,
identifier string,
txCount uint64,
) (starknet.PreConfirmedUpdate, error)
}
DataSource is the narrow surface the Poller needs from the wire side. Any type implementing both methods (e.g. sync.DataSource) satisfies it.
type Poller ¶
type Poller struct {
// contains filtered or unexported fields
}
Poller drives the pre-confirmed chain from a single goroutine.
One tick reads as: poll the server's latest pre-confirmed, backfill any gap below it, then insert the latest. backfill is a no-op when there's no gap; otherwise it finalises the current mostRecent (re-polls its number to capture the last delta before the sequencer moved past it) and then walks explicit-number polls up to latest-1. Same-height polls (latest matches our mostRecent) skip backfill and land in insert as delta / preserve / replace.
func NewPoller ¶
func NewPoller( dataSource DataSource, storage *ChainStorage, bc *blockchain.Blockchain, out *feed.Feed[*pending.PreConfirmed], highestBlockHeader *atomic.Pointer[core.Header], interval time.Duration, logger log.StructuredLogger, ) *Poller