Skip to content

Conversation

@Roasbeef
Copy link
Member

@Roasbeef Roasbeef commented Nov 6, 2025

In this PR, we continue our v2 mempool series with the addition of a new SubmitPackage RPC call. As is, the submit package RPC call is piece-wise, in that it's possbile for some transactions to be accepted while others are rejected. I went a bit back and forth on this, w.r.t if it's better and more consistent for an end-user if we instead implement atomic package submission (all or nothing). For now, I settled on keeping it incremental, as this matches bitcoind's behavior today. Perhaps we'll add a new flag to signal that the package should instead be all or nothing.

Integration tests for basic cases of package-RBF have been added along-side unit tests. We also implement BIP 431 that enables a zero or low fee v3 transaction to be accepted into the mempool, as long as it's a part of the final package.

There're quite a few things I don't really like about the current implementation. One is the split of responsibilities between the new txgraph, and the PolicyEnforcer, at times it feels like we're doing redundant work, and one of the two systems should gain more responsibility.

One follow up is to extend testmempoolaccept to be able to accept multiple transactions, basically calling the new CheckPackageAcceptance method that we've built out.

After that, the next PRs in this series include:

  • Optimistic 1p1c + sibling eviction
  • Equivalence tests between the v1 and v2 mempool

In this commit, we add the foundational types and validation logic
needed to support package relay via the submitpackage RPC. Package relay
enables submitting multiple related transactions together, which is
essential for BIP 431 TRUC transactions where zero-fee parents can be
combined with fee-paying children.

The PackageAcceptResult and TxAcceptResult types provide detailed
per-transaction feedback including acceptance status, fees, virtual
sizes, and rejection reasons. This granular reporting allows clients to
understand exactly which transactions in a package were accepted or
rejected and why.

The ValidatePackageTopology function enforces the requirements for
package relay: topological sorting (parents before children), count
limits (maximum 25 transactions matching Bitcoin Core), and weight
limits (404000 weight units). The topology validation catches circular
dependencies and ensures packages can be processed without deadlocks.
…ackages

In this commit, we implement BIP 431 Rule 6 which allows individual TRUC
(v3) transactions to be below the minimum relay fee when they are part
of a valid package that meets the mempool's feerate requirements.

The implementation adds a PackageContext type that tracks whether a
transaction is being validated as part of a TRUC package along with the
aggregate package feerate. The BuildPackageContext method leverages the
existing txgraph infrastructure (BuildPackageNodes, CreatePackage with
dry-run mode, and AnalyzePackageType) to properly classify packages and
calculate aggregate metrics without duplicating logic.

The PolicyEnforcer interface is extended with BuildPackageContext and
the PolicyGraph interface gains BuildPackageNodes and CreatePackage
methods to expose the necessary txgraph operations. The
StandardPolicyEnforcer.ValidateRelayFee method now checks the package
context before enforcing minimum relay fees, allowing zero-fee TRUC
transactions to skip the individual fee check when the package as a
whole meets requirements.

The packageContext parameter is threaded through the entire validation
chain from checkMempoolAcceptance through maybeAcceptTransactionLocked
to ValidateRelayFee, enabling the policy enforcer to make informed
decisions about fee exemptions.
…age relay

In this commit, we implement the core package submission methods that
enable the submitpackage RPC functionality. ProcessPackage accepts a
package of transactions and processes them progressively with proper
deduplication, while CheckPackageAcceptance provides dry-run validation
without modifying mempool state.

The ProcessPackage method implements Bitcoin Core's progressive
acceptance model where transactions are validated and accepted
one-by-one. Transactions already in the mempool are automatically
deduplicated (skipped), allowing clients to resubmit packages without
errors. The implementation properly tracks package-level metrics
including total fees, total virtual size, and aggregate feerate for
validation purposes.

Package RBF (Replace-By-Fee) is fully supported through pre-calculation
of package fees, validation via ValidatePackageReplacement, and atomic
removal of all conflicting transactions before accepting the new
package. The package context is built to include fees from both new
transactions and deduplicated mempool transactions, ensuring accurate
feerate calculations for replacement validation. When conflicts are
detected, the entire conflict set is removed atomically before any new
transactions are added, preventing partial state and ensuring
all-or-nothing package replacement semantics.

The package context is passed through to each transaction's validation
via maybeAcceptTransactionLocked, enabling BIP 431 Rule 6 support where
zero-fee TRUC transactions are allowed as part of valid packages. After
successful acceptance, orphan transactions that may now have all parents
available are processed, and the result includes comprehensive
per-transaction details including acceptance status, fees, vsizes, and
any errors encountered.

CheckPackageAcceptance mirrors ProcessPackage but uses dry-run
validation (checkMempoolAcceptance directly with read lock) to test
package acceptance without modifying the mempool. This is useful for
clients to validate packages before actual submission.
In this commit, we add the submitpackage RPC handler which enables
clients to submit packages of raw transactions to the mempool.
In this commit, we add the rpcclient wrapper methods for the
submitpackage RPC call, enabling programmatic package submission from Go
code. This completes the client-side API surface for package relay
functionality.
In this commit, we add integration tests that validate the complete
submitpackage RPC functionality from end-to-end using the rpctest
harness. These tests verify the critical functionality needed for BIP
431 TRUC transaction relay and package RBF.

The test suite covers five essential scenarios. First, basic package
submission with a valid 1-parent-1-child v3 package ensures the
fundamental flow works. Second, topology validation is tested by
submitting transactions in the wrong order (child before parent),
verifying that improper packages are rejected with clear error messages.

Third, we test BIP 431 Rule 6 support by submitting a package with a
zero-fee v3 parent and high-fee v3 child. This verifies that the parent
is accepted despite having zero fees because the package as a whole
meets the mempool's feerate requirements. This is the critical use case
for Lightning Network commitment transactions with anchor outputs.

Fourth, we test package RBF where an existing package [A, B] is replaced
by [A, B'] where A is deduplicated (already in mempool) and B' replaces
the original child B. This tests deduplication logic combined with
package-level RBF validation and verifies that the final mempool state
contains A + B' with B removed.

Fifth and most importantly, we test true package RBF where both
transactions conflict. Using the rpctest harness's WithInputs functional
option, we create A' that spends the same inputs as A, forcing a
double-spend conflict. When package [A', B''] is submitted, our
implementation correctly validates the package-level feerate against the
existing package, atomically removes both A and B, and accepts both A'
and B''. This demonstrates that full package-level RBF works correctly
with proper conflict aggregation and atomic replacement.

All tests include proper mempool cleanup to ensure isolated test
execution, and they verify both the RPC result structure and the actual
mempool state to catch bugs where transactions might be marked
"accepted" but not actually added to the graph.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant