Skip to content

Architectural boundary: durable chain events and independent derived-state consumers #77

Description

@gosunuts

Problem

Optional products currently perform work directly in synchronous block apply: txindex ingestion, BIP157/158 filters, mempool eviction, coinstats updates, and ZMQ publication. Electrum depends on txindex storage; Utreexo has a separate state model; the BIP300/301 enforcer consumes post-apply interfaces. Each new product requirement can therefore add locks, persistence ordering, and reorg handling to consensus-critical code.

Goal

Keep full-node chainstate transitions authoritative and small, while non-consensus derived state becomes independently recoverable consumers of one durable, ordered chain-event boundary.

P2P -> headers/bodies -> consensus -> UTXO + body/undo + applied tip
                                                   |
                                      durable ChainEvent source
                                                   |
    txindex | Electrum | filter index | enforcer adapter | Utreexo mode

The default full-node path must not wait for, trust, or become unavailable because a derived consumer is slow, disabled, failed, or rebuilding.

Requirements

Authoritative core

The core apply/disconnect path owns consensus validation, default full-UTXO mutation, required body/undo persistence, the authoritative applied tip, and durable emission of ordered state-transition records. Optional indexes, Electrum serving, external notifications, and experimental Utreexo state are not consensus prerequisites.

Durable event contract

Define a versioned, ordered record containing at least:

  • ConnectedBlock: height, hash, parent hash, body locator, and data needed for derived state.
  • DisconnectedBlock: height, hash, prior/replacement context, and a durable undo/body locator as needed.
  • A monotonic event cursor independent of the ZMQ transport sequence.
  • Schema/version and retention guarantees.

The contract must allow a consumer to restart from a stored cursor without relying on best-effort ZMQ. It may be an append-only log or an authoritative-storage journal with equivalent replay guarantees.

Mempool acceptance/removal should later use the same abstraction, but must not block the block-event boundary.

Consumer lifecycle

Every optional consumer owns:

  • an independent durable cursor expressed as event sequence plus height/hash;
  • state: disabled, building, caught-up, paused, failed, or stale;
  • backfill from a named active-chain anchor;
  • idempotent connect/disconnect processing;
  • interruption/event-retention/reorg recovery; and
  • an explicit query-readiness contract.

A consumer failure must not invalidate chainstate. Its API becomes unavailable or explicitly partial until it catches up.

Ordering, reorgs, and external notifications

  • Events reflect only committed authoritative transitions.
  • Reorgs preserve tip-first disconnect then replacement-branch connect ordering.
  • Consumer backfill first establishes a cursor/subscription, scans its historical anchor, then drains subsequent events.
  • Required body, undo, and event retention must be reconciled with pruning and each consumer's rebuild procedure.
  • ZMQ remains a low-latency best-effort projection of committed events, not a recovery log.
  • The BIP300/301 enforcer remains an external sidecar and must not hold core apply locks or gate ordinary Bitcoin block application.

Migration plan

  1. Specify and test the connect/disconnect event, cursor, and recovery contract.
  2. Extract txindex and filter-index work from inline apply into cursor-owning consumers; implement Add resumable post-IBD backfill for txindex, Electrum, and block filters #75 on this boundary.
  3. Attach Electrum readiness and notifications to txindex consumer state.
  4. Add mempool consumer semantics and A/R events; see Complete BIP300/301 enforcer integration with mempool A/R pubsequence events #74.
  5. Use the boundary for experimental Utreexo state progression without making it a default-consensus dependency; see Implement an experimental Utreexo compact-validator node mode #76.
  6. Retire direct optional side effects from apply.rs after parity/reorg/recovery coverage passes.

Acceptance criteria

  • The event schema, ordering, durability, cursor semantics, and retention policy are versioned and documented.
  • A consumer can stop at any event, restart, and converge to the same state as uninterrupted processing.
  • Connect/disconnect ordering is proven across ordinary and invalidation-triggered reorgs plus restart boundaries.
  • Default full-node apply remains correct and available when all optional consumers are disabled, paused, slow, or failed.
  • Txindex/filter inline work moves to independently catch-up-capable consumers without changing final index rows.
  • Electrum exposes source-index readiness and does not claim complete history while rebuilding.
  • ZMQ event loss has a defined consumer recovery path.
  • Enforcer integration remains asynchronous and external I/O occurs outside consensus/apply locks.
  • Utreexo stays isolated behind its experimental mode and consumes the declared transition contract.
  • Default IBD has no optional-index work on its critical path.

Related issues

Non-goals

  • Changing Bitcoin consensus or weakening default full-UTXO validation.
  • Requiring optional consumers for normal node operation.
  • Treating ZMQ as a durable queue.
  • Making the external enforcer an inline consensus oracle.
  • Defining Utreexo proof protocol details, which Implement an experimental Utreexo compact-validator node mode #76 owns.

Metadata

Metadata

Assignees

No one assigned

    Labels

    wayfinder:taskPrerequisite task for a decision

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions