mpool

package
v1.9.2 Latest Latest
Warning

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

Go to latest
Published: Jul 23, 2026 License: Apache-2.0, MIT Imports: 18 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrDryRun = errors.New("mpool: dry-run mode, not publishing")

ErrDryRun is returned by Pool.Publish when DryRun is true.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Topic name. Defaults to build.MainnetGossipTopicMessages.
	Topic string
	// DryRun: when true, Publish validates locally and records the
	// would-be publish but doesn't actually push to gossipsub. Useful
	// for production safety while we shake out the libp2p mesh.
	DryRun bool
	// OnMessage is fired for every signed message received from the
	// network. Nil means "ignore incoming traffic".
	OnMessage func(*ltypes.SignedMessage, peer.ID)

	// --- #47: pending confirm + rebroadcast loop ---
	//
	// ConfirmAfterEpochs is the confidence window: a published message not
	// seen on chain for at least this many epochs is rebroadcast (identical
	// bytes, same nonce/CID). Default 3 (~90s mainnet). Long enough that a
	// slow-but-valid inclusion isn't mistaken for failure.
	ConfirmAfterEpochs int64
	// MaxRetries caps rebroadcasts before a message is marked failed.
	// Default 5. 0 uses the default; use a negative value for "unlimited".
	MaxRetries int
	// OnFailed, if set, is invoked once when a pending message gives up
	// (max retries exhausted). The message is also moved to a failed set
	// observable via Stats.Failed. Never silently stuck.
	OnFailed func(*ltypes.SignedMessage, string)

	// --- #119: durable pending-message store ---
	//
	// PersistPath is the on-disk JSONL journal Lantern uses to keep the
	// pending set alive across daemon restart. When empty (the historical
	// default), the pool is memory-only and behaves like pre-#119.
	//
	// When set:
	//   - New() opens the file, replays the journal, and re-registers every
	//     live entry into the in-memory pending set. Nonce derivation
	//     (MpoolGetNonce) reads Pending() so a restart transparently keeps
	//     each account's next nonce correct.
	//   - Publish() fsyncs an "add" line before returning success.
	//   - Reconcile confirm/fail branches fsync a "tombstone" line.
	//   - Rebroadcast fsyncs a "retry" line with the bumped counter.
	//
	// The file lives at <home>/<network>/mpool/pending.jsonl. It is chain-
	// side of the secrets boundary (never in keystore/, secrets/, or
	// backups/), but `lantern reset --chain-state` deliberately does NOT
	// wipe it: user-signed pending messages are user state, not rebuildable
	// chain state.
	PersistPath string

	// --- #123: alternate wire sink for devnet ---
	//
	// Sink, when non-nil, replaces the gossipsub topic.Publish call in
	// Publish(). This is the devnet lotus-RPC path: on a single-node
	// docker devnet the gossipsub mesh can't form, so signed messages
	// are POST'd directly to the devnet lotus via Filecoin.MpoolPush
	// instead. All other Pool semantics (persist journal, pending set,
	// nonce derivation, reconcile/retry loop) work identically.
	//
	// When Sink is set, New() may be called with a nil pubsub instance;
	// no topic is joined and no subscription is created. The reconcile
	// loop still runs, rebroadcasting via Sink until inclusion.
	Sink func(ctx context.Context, sm *ltypes.SignedMessage, raw []byte) (cid.Cid, error)
}

Config configures a Pool.

type PersistEntry

type PersistEntry struct {
	CID           cid.Cid
	Raw           []byte
	PublishedAt   int64
	Retries       int
	FirstSeenWall time.Time
}

PersistEntry is one live pending message in the durable store. Retries and PublishedAt are updated in place on rebroadcast; the raw bytes and FirstSeenWall are captured once at publish and never mutated.

func (*PersistEntry) SignedMessage

func (e *PersistEntry) SignedMessage() (*ltypes.SignedMessage, error)

SignedMessage decodes the raw bytes back into a live SignedMessage. Returns an error if the bytes are corrupt (shouldn't happen for entries we wrote ourselves, but be defensive on load).

type Pool

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

Pool is a libp2p-gossipsub-backed message pool.

func New

func New(ctx context.Context, ps *pubsub.PubSub, cfg Config) (*Pool, error)

New starts a Pool: joins the topic, subscribes, and (if OnMessage is set) dispatches incoming messages to the handler in a background goroutine.

When ps is nil, no gossipsub topic is joined and no subscription is created. In that mode cfg.Sink must be set (used for the devnet lotus-RPC send-path per #123). Persist + reconcile loop + pending set still work identically; the difference is only the wire transport used by Publish and Rebroadcast.

func (*Pool) Close

func (p *Pool) Close() error

Close stops the subscription and topic, and closes the persist journal (when open). Safe when the pool was constructed without a pubsub instance (devnet Sink mode); the nil topic/sub branches are skipped.

func (*Pool) Forget

func (p *Pool) Forget(c cid.Cid)

Forget drops a CID from the pending set (call after the message is confirmed on-chain).

func (*Pool) Pending

func (p *Pool) Pending() []*ltypes.SignedMessage

Pending returns a snapshot of locally pushed message CIDs (and their signed-message bodies) still awaiting inclusion.

func (*Pool) Publish

func (p *Pool) Publish(ctx context.Context, sm *ltypes.SignedMessage) (cid.Cid, error)

Publish validates `sm` superficially, marshals it, and publishes to the gossipsub topic. Returns the message CID and any error. In DryRun mode it returns the CID + ErrDryRun without publishing.

func (*Pool) Reconcile

func (p *Pool) Reconcile(ctx context.Context, headEpoch int64, search SearchFunc)

Reconcile runs one pass of the confirm/retry state machine at the given chain head epoch. Intended to be called once per new head. It never holds the pool lock across the SearchFunc call or a rebroadcast.

func (*Pool) Stats

func (p *Pool) Stats() Stats

Stats returns activity counters.

type SearchFunc

type SearchFunc func(ctx context.Context, msgCID cid.Cid) (SearchResult, error)

SearchFunc resolves whether a published message CID has landed on chain. The daemon wires this to ChainAPI.StateSearchMsg (already local + zero Glif). It must be safe to call without holding the pool lock.

type SearchResult

type SearchResult int

SearchResult reports whether a message was found on chain.

const (
	// SearchUnknown: not found (yet) — keep waiting / retry per policy.
	SearchUnknown SearchResult = iota
	// SearchFound: confirmed on chain — drop from pending.
	SearchFound
)

type Stats

type Stats struct {
	Received     uint64
	Rejected     uint64
	Published    uint64
	Rebroadcasts uint64 // #47
	Confirmed    uint64 // #47
	Failed       uint64 // #47
	PendingCnt   int
	Topic        string
	Restored     uint64 // #119 — entries re-registered from persist journal on startup
	PersistPath  string // #119 — empty when persistence is disabled
}

Stats reports observable counters.

Jump to

Keyboard shortcuts

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