Problem
The checked-in BIP300/301 Compose integration currently runs the upstream enforcer in block-only mode. bitcoin-rs publishes Core-compatible pubsequence block connect/disconnect events (C/D), but deliberately omits mempool add/remove events (A/R). As a result, Compose cannot safely enable the enforcer's mempool mode.
The missing work is not just adding two ZMQ labels. Mempool mutations currently occur through multiple direct RwLock<Mempool> call sites, while the pool exposes only a coarse mutation counter and removal paths do not retain a complete, ordered event record with explicit causes. Publishing from selected callers would permit missing, duplicated, or reordered events.
Current state
crates/node/src/zmq_publisher.rs implements the sequence topic and topic-local u32 transport counter for block C/D events.
crates/mempool/src/pool.rs maintains a u64 change counter that increments on successful insert/remove operations.
- RPC, wallet, mining, node apply, and other paths can acquire the shared mempool write lock directly.
- Block connection removes confirmed transactions and descendants from the mempool in
crates/node/src/apply.rs.
tools/bip300301-enforcer/docker-compose.yaml deliberately omits --enable-mempool.
docs/rest-interface.md and CONCEPTS.md document this limitation.
Goal
Provide a single ordered mempool-mutation boundary that can emit Bitcoin Core-compatible pubsequence A/R events without gaps or ordering ambiguity, then enable and validate the upstream enforcer's mempool mode.
Proposed work
1. Freeze compatibility semantics
Before implementation, ground the exact Bitcoin Core and pinned upstream enforcer wire contract:
- payload layout for
A and R
- relationship between the ZMQ topic sequence and mempool sequence
- byte order and counter widths
- ordering for parent/descendant removals
- behavior for transactions confirmed in a block
- behavior for conflicts, replacement, expiry, size/fee eviction, explicit removal, clear, reorg, and re-admission
- expected recovery behavior after subscriber or publisher event loss
Record the chosen semantics in tests and project documentation.
2. Introduce a unified mutation result/event model
Represent each successful mutation as an ordered set of transaction-level changes, for example:
- accepted transaction: txid plus assigned mempool sequence
- removed transaction: txid, assigned mempool sequence, and an explicit internal removal reason
Removal reasons should distinguish at least:
- block inclusion
- conflict or replacement
- descendant removal
- fee/size policy eviction
- explicit removal or wholesale reset
- reorg-related removal/re-admission where applicable
The internal reason model may be richer than the ZMQ wire format, but it must be available to other validated-chain consumers and tests.
3. Centralize mempool mutation
Route every production insert/remove/clear path through one mutation gateway rather than publishing opportunistically from individual callers.
Audit at least:
- transaction RPC admission
- P2P transaction admission
- wallet-originated admission
- mining/policy eviction
- block-connect eviction
- reorg reconsideration
- shutdown/reload/clear behavior
State mutation and event ordering must be serialized. ZMQ remains best-effort and must not roll back a valid mempool mutation, but later publishers must not overtake earlier mutations.
4. Extend pubsequence
Extend SequenceEvent and all publisher implementations with Core-compatible transaction A/R payloads.
Preserve:
- existing block
C/D behavior
- one monotonically ordered topic-local transport sequence
- reorg ordering: tip-first disconnects before replacement-branch connects
- no external I/O while holding consensus-critical or mempool locks longer than necessary
- no consensus/apply failure caused by notification failure
5. Enable the enforcer mempool mode
After compatibility and end-to-end tests pass:
- add
--enable-mempool to the checked-in enforcer Compose command
- validate startup and synchronization against the pinned
ENFORCER_REVISION
- update
docs/rest-interface.md, README.md, overlapping docs/solutions/ knowledge, and CONCEPTS.md
Acceptance criteria
Non-goals
- Making the external enforcer a synchronous prerequisite for ordinary Bitcoin block application.
- Calling enforcer gRPC while holding the chain-transition/apply lock.
- Treating BIP300/301 rejection as native Bitcoin consensus failure.
- Changing the default Bitcoin-only deployment path.
- Implementing a fail-open/fail-closed external consensus policy in this issue.
Follow-up design: optional enforcer-backed block policy
If bitcoin-rs should later refuse a branch based on BIP300/301 validation, track that as a separate architecture issue. It requires a pre-apply candidate-block API, deterministic connect/disconnect state transitions, crash/restart reconciliation, timeout and fail-open/fail-closed policy, explicit header-status semantics, and IBD performance analysis. The current post-apply RPC/REST/ZMQ sidecar topology is not sufficient for an atomic consensus gate.
Problem
The checked-in BIP300/301 Compose integration currently runs the upstream enforcer in block-only mode. bitcoin-rs publishes Core-compatible
pubsequenceblock connect/disconnect events (C/D), but deliberately omits mempool add/remove events (A/R). As a result, Compose cannot safely enable the enforcer's mempool mode.The missing work is not just adding two ZMQ labels. Mempool mutations currently occur through multiple direct
RwLock<Mempool>call sites, while the pool exposes only a coarse mutation counter and removal paths do not retain a complete, ordered event record with explicit causes. Publishing from selected callers would permit missing, duplicated, or reordered events.Current state
crates/node/src/zmq_publisher.rsimplements thesequencetopic and topic-localu32transport counter for blockC/Devents.crates/mempool/src/pool.rsmaintains au64change counter that increments on successful insert/remove operations.crates/node/src/apply.rs.tools/bip300301-enforcer/docker-compose.yamldeliberately omits--enable-mempool.docs/rest-interface.mdandCONCEPTS.mddocument this limitation.Goal
Provide a single ordered mempool-mutation boundary that can emit Bitcoin Core-compatible
pubsequenceA/Revents without gaps or ordering ambiguity, then enable and validate the upstream enforcer's mempool mode.Proposed work
1. Freeze compatibility semantics
Before implementation, ground the exact Bitcoin Core and pinned upstream enforcer wire contract:
AandRRecord the chosen semantics in tests and project documentation.
2. Introduce a unified mutation result/event model
Represent each successful mutation as an ordered set of transaction-level changes, for example:
Removal reasons should distinguish at least:
The internal reason model may be richer than the ZMQ wire format, but it must be available to other validated-chain consumers and tests.
3. Centralize mempool mutation
Route every production insert/remove/clear path through one mutation gateway rather than publishing opportunistically from individual callers.
Audit at least:
State mutation and event ordering must be serialized. ZMQ remains best-effort and must not roll back a valid mempool mutation, but later publishers must not overtake earlier mutations.
4. Extend pubsequence
Extend
SequenceEventand all publisher implementations with Core-compatible transactionA/Rpayloads.Preserve:
C/Dbehavior5. Enable the enforcer mempool mode
After compatibility and end-to-end tests pass:
--enable-mempoolto the checked-in enforcer Compose commandENFORCER_REVISIONdocs/rest-interface.md,README.md, overlappingdocs/solutions/knowledge, andCONCEPTS.mdAcceptance criteria
A/Rpayload and sequencing contract is captured in compatibility tests.Aevent.Revent, including descendants.Revents without changing existing blockCsemantics.--enable-mempooland follows an end-to-end transaction lifecycle.Non-goals
Follow-up design: optional enforcer-backed block policy
If bitcoin-rs should later refuse a branch based on BIP300/301 validation, track that as a separate architecture issue. It requires a pre-apply candidate-block API, deterministic connect/disconnect state transitions, crash/restart reconciliation, timeout and fail-open/fail-closed policy, explicit header-status semantics, and IBD performance analysis. The current post-apply RPC/REST/ZMQ sidecar topology is not sufficient for an atomic consensus gate.