The purpose of the mempool is to store the best candidates for inclusion in a block, i.e., the ones with the highest fees (including fee-bumped ones). Non-mining nodes use the mempool to make block validation faster and to aid transaction relay. If transaction relay and mempool logic are working well, all transactions paying reasonable fees should make it to miners, and all nodes with (even small) mempools should have the transactions that will be in the next blocks.
Currently, we limit the size of our mempool (default 300MB) and only validate transactions one at a time - this creates a limitation in a node's ability to determine which transactions have the highest fees, because a low fee transaction can have a high fee child. (Essentially, CPFP only makes a difference for transactions that make it into a miner's mempool). Increasing the default size is not an option.
"Package Relay" End Goal: we can use package validation to accept a group of transactions that might not all meet our minimum fee requirements with individual base fees, but are attractive as a package. When receiving/sending transactions on the P2P network, we are able to decide and communicate when package validation (rather than individual transaction validation) should be used.
Of course, we want to achieve this without creating new DoS and pinning attack vectors.
Previous work on Package Relay:
- Package relay design questions
- sdaftuar PoC + gist
- ariard PoC
- Package validation for testmempoolaccept
Q: What is the minimum package type that is sufficient for your use cases?
Examples:
- Simply 1 parent + 1 child
- Two generations only, with 1 or more sponsored parents + 1 sponsoring child
- Two generations only, with 1 or more sponsored parents + 1 or more children
- More than 2 generations, with the bottom transaction serving as the sole sponsor
- Any group of transactions, with a maximum number of X transactions
Q: Similarly, what other simplifications can we make?
- In #20833, we decided to disable BIP125 for now. Is this okay?
- Can we assume a certain level of connectivity within packages? Will you ever need independent packages?
Q: Should we do witness replacements? (i.e. #19645)
There has been a lot of focus on the P2P side of package relay but we are quite far from it. Here is a sequence of work that needs to be done (imo) before it's safe to expose package validation on the P2P network:
These PRs implement local package mempool acceptance. Any feedback on approach or implementation is very welcome:
- #20833 was merged recently, it adds a chunk of package validation code. It's also the base PR for a separate project for test-accepting L2 transaction chains (see #21413 which is a RFC).
- #21800 is open, it implements mempool ancestor/descendant checking for packages
- #22290 is my first draft of package mempool submission, still WIP but I would like to be transparent about my work
- #22252 is a package policy that I think is relevant
Since package validation and individual transaction validation may have different policies (e.g. no RBF in packages) and one might be more appropriate than the other, it does not make sense to always use package validation. If your node supports package validation, you can decide when to use it, whether or not your peers tell you to. You could start using package validation when you feel like it (e.g. orphan + parent rejected for low feerate heuristic like in #16401 commit). I think this would be called receiver-side package relay, and perhaps doesn't require P2P messages.
If we decide to make P2P changes...
Personally, I don't actually think we need to send tx data in packages; nodes can still send transactions via regular tx relay (+ Erlay when we have that).
Once nodes have a package validation module, I think the only P2P communication that needs to happen in addition to transaction relay is a hint saying "psst, you should use package validation on these transactions."
Also, some kind of sendpackages
message to say "I can validate packages" and "please only send me these types of packages".
This will probably just require an RPC and maybe a BroadcastPackage()
similar to BroadcastTransaction()
in our interface code.
I don't think lay users should have to think about packages beyond telling their wallet to fee-bump their transactions and still having their maxfeerate configurations respected when doing so.
I had an afterthought from the IRC meeting about package RBF. We said we'd likely go with
1 parent + 1 child
packages and allow a package to replace another (ie we could submit [A', B'] to replace [A, B]).@harding mentioned reusing BIP 125 logic, however I believe rule 5 ("the number of original transactions to be replaced and their descendant transactions which will be evicted from the mempool must not exceed a total of 100 transactions.") would be a real problem: an attacker could easily pin a package [A, B] in some mempools by adding 98 child transactions to B (and having B pay a low enough fee to ensure A and B won't confirm quickly). If that makes it irreplaceable by [A', B'], it would be a problem for lightning (and potentially other L2 protocols?), it means we're unable to relay our package regardless of how much fees we're ready to pay.
The CPFP carve-out rule had to be hacked in to work around this issue for single transactions (such as lightning commitment txs), but I don't think it can save us here in the general case.
WDYT?