sequences

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 16, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package sequences is the kit's durable sequence manager (FR-034, data model §6). Manager.Reserve is the ONLY sequence source for transaction builders: it serializes per (chain_id, source_address) through the storage backend's account-lock primitive (Postgres SELECT … FOR UPDATE on the chain_account_locks row; SQLite single-writer + BEGIN IMMEDIATE), derives the next sequence from chain truth reconciled with open reservations, and binds exactly one reservation to one work item (withdrawal or sweep).

Reconciliation is state-dependent and never blind (§6 reconciliation rule): only RESERVED reservations — nothing signed — are ever released; a SIGNED/BROADCAST/ambiguous reservation whose transaction cannot be found is quarantined as RECONCILIATION_REQUIRED, because valid signed bytes may still redeem the sequence from a mempool, another process, or the signer system. Recovery rebroadcasts the exact persisted signed bytes (RebroadcastPersisted) — never re-signs, never auto-releases.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrReleasedSlotUnusable reports that a work item's reservation was
	// RELEASED during reconciliation and its sequence slot has since been
	// consumed on chain, so the same work_ref cannot be re-bound. The work
	// item must be quarantined for operator review — never silently rebound
	// to a different sequence.
	ErrReleasedSlotUnusable = errors.New("sequences: released reservation slot is no longer reservable")

	// ErrNoAccountLock reports a storage backend that does not expose the
	// per-account serialization primitive (R7).
	ErrNoAccountLock = errors.New("sequences: storage backend does not implement AcquireAccountLock")
)

Functions

This section is empty.

Types

type Action

type Action struct {
	ReservationID int64
	WorkRef       storage.WorkRef
	Sequence      uint64
	From          storage.SequenceReservationStatus
	To            storage.SequenceReservationStatus
	Reason        string
}

Action records one reconciliation decision for the report/audit trail.

type Chain

type Chain interface {
	Account(ctx context.Context, addr string) (accountNumber, sequence uint64, err error)
	Tx(ctx context.Context, hash string) (*client.TxInfo, error)
	Broadcast(ctx context.Context, txBytes []byte, mode client.BroadcastMode) (*client.BroadcastResult, error)
}

Chain is the subset of client.Client the manager needs. *client.Client implementations satisfy it.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager hands out durable sequence reservations. Safe for concurrent use; concurrency is serialized per account by the storage backend, and the UNIQUE(chain_id, source_address, sequence) constraint is the last-line guarantee in all cases.

func NewManager

func NewManager(store storage.Store, chain Chain, opts ...Option) *Manager

NewManager builds a Manager over one storage backend and one chain client.

func (*Manager) RebroadcastPersisted

func (m *Manager) RebroadcastPersisted(ctx context.Context, signedTxBytes []byte) (RebroadcastResult, error)

RebroadcastPersisted is the ONLY recovery path for a quarantined signed transaction: it searches for the exact persisted bytes by hash first and, only when absent, rebroadcasts those identical bytes. It takes bytes, not a work item — re-signing is structurally impossible here.

func (*Manager) ReconcileAccount

func (m *Manager) ReconcileAccount(ctx context.Context, chainID, source string) (Report, error)

ReconcileAccount re-derives every unconsumed reservation for one account from chain truth. Run it at startup and after any detected mismatch.

Resolution is state-dependent (§6): a transaction found on chain by hash consumes its reservation; a RESERVED reservation with no signed bytes is released; SIGNED, BROADCAST, and every ambiguous case (signed bytes exist, work item unreadable, tx not found) is quarantined as RECONCILIATION_REQUIRED. A GetTx not-found is never sufficient to release a sequence that signed bytes may still redeem.

func (*Manager) Reserve

func (m *Manager) Reserve(ctx context.Context, chainID, source string, ref storage.WorkRef) (storage.SequenceReservation, error)

Reserve returns the reservation bound to ref, creating it if absent.

Idempotent per work_ref: a second call for the same work item returns the existing reservation in whatever status it has reached (the caller inspects Status). A reservation RELEASED by reconciliation is re-bound to the same slot when that slot is still ahead of chain truth; a burned slot returns ErrReleasedSlotUnusable so the caller quarantines the work item.

The next sequence is chain truth reconciled with open reservations: max(account.sequence, highest unconsumed reservation + 1). An open reservation strictly below account.sequence is a mismatch — counted, logged, and left for ReconcileAccount; Reserve never auto-resolves it.

type Option

type Option func(*Manager)

Option configures a Manager.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger replaces the default logger.

func WithMetrics

func WithMetrics(s *metrics.Set) Option

WithMetrics attaches the adapter metric set (sequence-mismatch counter).

type RebroadcastResult

type RebroadcastResult struct {
	TxHash string
	// AlreadyIncluded is true when the transaction was found on chain and
	// nothing was broadcast.
	AlreadyIncluded bool
	Height          int64
	Code            uint32
	RawLog          string
	// Accepted is true when a broadcast was performed and CheckTx passed.
	Accepted bool
}

RebroadcastResult reports what RebroadcastPersisted found or did.

type Report

type Report struct {
	ChainID       string
	SourceAddress string
	ChainSequence uint64
	Actions       []Action
	Quarantined   int
	Released      int
	Consumed      int
}

Report is the outcome of one ReconcileAccount pass.

Jump to

Keyboard shortcuts

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