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.SnapshotForBlock) 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. If an entry has block number 0 behaviour is undefined.
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 oldest slot (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 [SnapshotForBlock]. 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(oldestPreConf uint64) bool
AdvanceTo realigns the chain to a new canonical head, given as oldestPreConf — the slot the chain's oldest entry must occupy after the realignment, i.e. the first pre-confirmed slot above that head (head number+1). Three outcomes:
- oldestPreConf == the chain's current oldest: already aligned, no-op.
- oldestPreConf > mostRecent (head advanced past everything we stored) OR oldestPreConf < the current oldest (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.
- current oldest < oldestPreConf <= mostRecent: rebuild from the new oldest slot 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, oldestPreConf uint64, newClasses map[felt.Felt]core.ClassDefinition, ) (*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). oldestPreConf is the slot the chain's oldest entry is expected to occupy: the first pre-confirmed slot above the canonical head, head number+1. 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.
A NoChange still registers newClasses on the targeted (tip) slot when non-empty: a re-poll that reported no content change may still carry declared classes we only just fetched for that block.
func (*ChainStorage) SnapshotForBlock ¶
func (s *ChainStorage) SnapshotForBlock(blockNumber uint64) ChainReader
SnapshotForBlock returns a head-aligned view of the pre-confirmed chain based on the input `blockNumber`. If the `blockNumber` sits outside the pre-confirmed chain range, an empty chain is returned. Otherwise, a new chain is returned in the [blockNumber, chain.tip()] range.
The total size of the pre-confirmed chain is uncapped and it can exceed core.BlockHashLag, causing execution simulations on longer chain to fail due to this.
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)
Class(ctx context.Context, classHash *felt.Felt) (core.ClassDefinition, error)
}
DataSource is the narrow surface the Poller needs from the wire side. Any type implementing these 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 apply the latest. backfill runs only when the tip jumped ahead; it re-polls the old mostRecent with delta hints and the intermediate slots up to latest-1 as full blocks (capturing each slot's declared classes) and applies each. Same-height polls (latest matches our mostRecent) skip backfill and land in apply 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