Skip to content

mempool startup can consume tip action before close-to-tip gate opens #480

Description

@ValarDragon

Problem

The mempool startup gate currently requires both:

  • SyncStatus::is_close_to_tip() to be true, and
  • a fresh TipAction from ChainTipChange::last_tip_change() so the mempool can initialize last_seen_tip_hash.

But poll_ready() calls last_tip_change() before checking whether startup is allowed. last_tip_change() is consuming: once it returns a tip action, it records that block hash as last_change_hash, so the same action is not returned again.

That creates a startup latch edge case:

  1. A disabled mempool polls and consumes a tip action.
  2. Zebra is still considered far from tip, so update_state() matches (false, false, _) and leaves the mempool disabled.
  3. Later, RecentSyncLengths / SyncStatus independently changes so is_close_to_tip() becomes true.
  4. No new tip action exists, so last_tip_change() returns None.
  5. update_state() matches (true, false, None) and still refuses to enable.

The mempool then remains disabled until another chain tip change arrives.

Desired behavior

Startup should not require a fresh, unconsumed TipAction at the exact moment the close-to-tip gate opens.

Possible fixes:

  • initialize the disabled mempool directly from LatestChainTip once is_caught_up_to_start() is true, or
  • preserve a consumed tip action until activation succeeds.

This is separate from active-mempool reset behavior. Resetting/clearing the mempool on TipAction::Reset is fine; the issue is only that disabled startup can consume its one available tip action too early.

Code paths

  • zebrad/src/components/mempool.rs: poll_ready() calls chain_tip_change.last_tip_change() before update_state().
  • zebrad/src/components/mempool.rs: update_state() refuses (true, false, None) because there is no tip action to seed last_seen_tip_hash.
  • zebra-state/src/service/chain_tip.rs: ChainTipChange::last_tip_change() updates last_change_hash when it returns an action.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions