The PR branch HEAD was 7daf3d0 at the time of this review club meeting.
Notes
Package Mempool Accept is a
project to add policies for accepting packages of transactions (i.e. ones that would not otherwise
be accepted individually) into the mempool, in preparation for package relay. We have covered other
Package Mempool Accept PRs in previous review club meetings: PR #20833,
PR #21800, and PR #22674.
PR #24152 is the next PR in the series. It is
centered around creating an incentive-compatible policy for assessing package feerates. It
introduces three new concepts:
Transactions are validated individually first at the start of package validation. Any
transactions that pass individually are removed from the package. Afterwards, only the
leftover transactions are submitted through AcceptMultipleTransactions().
Package feerate is defined as the total modified fees divided by the total virtual size of
all transactions in the package after de-duplication and individual submission of each
transaction. For a âpackageâ of 1 transaction, this is equivalent to its modified feerate.
Package feerate, instead of individual feerate, is used to evaluate the transactions in the
two feerate policies: the static Minimum Relay Feerate (minRelayTxFee) and dynamic mempool
minimum feerate.
Incentive compatibility is the main concern of this PR. The following capabilities and
restrictions encapsulate the behavior we are aiming for for users:
A child can pay for both itself and its parent within a package. This is also known as
Child Pays For Parent (CPFP).
A child must pay for both itself and its parent within the package. The
package feerate must meet the minimum requirements.
Ensure we use modified feerate rather than base feerate. Modified fees
include base fees and any fee delta from prioritisetransaction, which can
be used by miners to give priority to their own transactions in their mempools.
A transaction should not help the feerate of another transaction if it is not an ancestor
(i.e. not necessary for the other transaction to be mined). Discourage âparent pays for
childâ and âsibling pays for siblingâ behavior.
A transaction should not harm the feerate of another transaction if it is not an ancestor
(i.e. not necessary for the other transaction to be mined). A low-fee child in the package
should not stop its parent(s) from being accepted if they have sufficient feerates individually.
A transactionâs fees should never be âdouble counted.â Once a transaction has been submitted to
the mempool, donât include its fee or size in feerate calculations again.
Tests are added to represent various desirable and undesirable fee-related policies. The
packages tested are illustrated in the diagrams below:
Quiz: Letâs say we have transactions {A, B, C, D} where {A, B, C} are parents of D.
For simplification, transactions are 100vB. All {A, B, C} pay 0 fees, and D pays 30,000sat in fees.
The user makes this series of calls:
Now we submit package {A, B, C, D} and it succeeds. What is the package feerate returned?
(A) 100sat/vB
(B) 150sat/vB
(C) 200sat/vB
(D) 300sat/vB
(E) 350sat/vB
(F) no package feerate
Why is submitting individually first and using this definition of package feerate sufficient to
ensure we are assessing feerates properly? (Hint: we restrict package
topology)
Would this definition of package feerate work for all types of packages? For example, packages
with three generations, a parent with multiple children, unrelated transactions, etc.
Do you agree with the 6 incentive-related behaviors described in the notes above? Can you think
of anything missing?
Do the tests cover all 6 points? (It may help to go through each point and see which test covers
each). Do you have any suggestions for more tests cases?
If AcceptSingleTransaction() fails
here
due to a consensus rule, is there any reason we should do package validation later? Is there any
reason we should continue to validate the other transactions in the package?
Why do we need to create a new set of ATMPArgshere?
Why canât we just use args for individual submission? Why must we pass in args when
creating single_args? Why donât we just create a new ATMPArgs from scratch?
<larryruane> when evaluating a package, instead of just checking the overall package feerate, look inside and check the individual tx first, accept any as individuals if possible, then check the (reduced) package (what's remaining0
<svav> A basic question - what is the rationale behind wanting to have packages in Bitcoin at all? Is it just to help low fee transactions get processed?
<larryruane> svav: that's a great question, maybe I can make an attempt... Suppose you have a tx that has SUCH a low fee that mempools won't even accept it at all ... and you don't have the ability to create a new tx, you only have that one ... so with package feature, you can combine it with a high fee child and get them both accepted
<glozow> so one example is, if your mempool is full and the minimum feerate is higher than the feerate of an individual transaction, right now you won't be able to use CPFP to broadcast it, because we only consider transactions individually
<bit-pleb-paul> In regards to lightning - if a channel is force closed by one party, but the closing transaction doesnât have a high enough fee, it  wonât even be accepted into the mempool, as described above. Thereby lightning force closures wouldnât work, and I the outcome of that, I think, is that counterpartoes could steal funds
<glozow> bit-pleb-paul: indeed. commitment transactions also cannot replace each other via RBF, and if the feerate isn't enough to make it into the mempool, your hands are tied
<larryruane> You may be wondering why would someone create a tx with such a low feerate that it won't even be accepted into the mempool (much less mined)? I think the answer is that this tx may have been created months ago when fees were very low, and now fees are much higher, and we can't *change* the tx ... I think this happens with Lightning?
<glozow> with package relay and the policy in this PR, you could actually just put 0 fees on the commitment transaction and adjust your fee-bump based on the current feerate market when you go to broadcast it
<svav> With a Child Pays for Parent Transaction being used to bump another transaction, is the Child transaction always initiated by the same person that sent the parent transaction?
<larryruane> glozow: very cool, I guess the tradeoff is, if you try to estimate a fee ahead of time, and it turns out to be enough, then you can save overall fees because you don't need that child ... whereas, if you create the tx with 0 fee, then it's guaranteed you'll need a child - 2 transactions, more expensive
<glozow> and anyone else, feel free to ask more questions about motivation, i'm happy to describe further. the PR isn't very exciting if we don't know why we're doing this
<glozow> svav: nope! if the parent is a payment, the child could be created by either the sender (from their change output) or recipient (from their payment output)
<larryruane> svav: if the sender wants to change the fee, he or she has the option of RBF, which probably is a little cheaper overall (other things equal)
<ziggie> so this package is only a package for relaying, in the mempool the structure stays the same as of today ? And as soon as a tx is in the mempool other rules to accept it into a block apply ?
<glozow> ziggie: correct, there would be no changes to the mempool data structure itself. our block template building logic has used CPFP since at least the past few years
<theStack_> why is RBF not possible for lightning commitment transactions? is it because we would need a signature from the channel partner, which is possibly not able to cooperate (or not online at all)?
<glozow> michaelfolkson: that's a good question, since DoS is a very front-and-center concern when looking at this area of the code. but i think incentive compatibility and preventing censorship are a more relevant concerns for package cpfp specifically.
<ziggie> theStack_as soon as you are using a lightning commitment tx your are "unilateraly closing channel" so no way to get a newer transaction (RBF) with more fees
<glozow> theStack_: correct. for one commitment tx to replace the other, it would need to have pretty significantly higher fees. since you negotiated the fees ahead of time (and they would be the same for both you and your counterparty's tx) and you can't get them to sign a new one, it's not possible.
<bit-pleb-paul> I really liked the incentive where a child with no fee ought not be able to âdragdownâ a parent from entering the mempool (by not increasing the fee while increasing the vybtes )
<glozow> bit-pleb-paul: thanks! :) i wouldn't call that a DoS (it's not exhausting resources), but censorship. somebody shouldn't be able to get your transaction rejected just by adding a low-feerate child to it and sending them as a package.
<michaelfolkson> There is a commitment transaction that can be broadcast to close a channel unilaterally and a justice transaction if your counterparty broadcasts an old commitment transaction
<ziggie> can somebody explain why we are removing transactions from the package which are valid without the package, it it not better for the package to be higher in feerate ?
<lightlike> why couldn't the original transaction be several months (or even years) old until it needed to be CPFP'ed? surely lighting channels could stay open that long?
<glozow> lightlike: oh yes true, it could be months old if it's from a very old state e.g. when the counterparty had a much higher balance. i guess i didn't really imagine that happening normally
<larryruane> ziggie: i think "better" is a complicated concept here, if one thing is better, everything else is relatively slightly worse, so may not get mined (or possibly even get evicted from the mempool) ... but that's still a good question!
<glozow> let's have an example. Letâs say we have transactions {A, B, C, D} where {A, B, C} are parents of D. For simplification, transactions are 100vB. All {A, B, C} pay 0 fees, and D pays 30,000sat in fees.
<larryruane> (I like the way you name those, A-D, very small suggestion, in the new packages.md text, they're the other way around, B is the parent and A is the child, maybe reverse those?)
<glozow> yep, thank you ziggie. we validate all transactions individually first, and then try as a package. this ensures that ancestors don't pay for descendants
<theStack_> isn't another other issue that bumping A would change its txid and therefore the outpoint for the child's input, needing to resign the child tx?
<glozow> svav: that is the simplest example for why we first validate individually, to eliminate the parents from the package if they don't need to be paid for.
<michaelfolkson> [17:41:13] <glozow> theStack_: yes exactly. in general, a transaction B should only "pay for" another transaction A if B requires A in order to be mined.
<lightlike> bit-pleb-paul: but they need to be able to build a package with the parent, so they need to be the sender or receiver. they can't just tip random transactions they like.
<glozow> right. there's no reason why the tip should be a child. it could be a separate transaction or, better yet, the sender should just create a replacement with the new payment value.
<glozow> michaelfolkson: yes. the only reason to consider package feerate together is for descendants to pay for ancestors. the nice thing about only allowing child-with-parents packages is that simply validating them individually first achieves this.
<neha> bit-pleb-paul: the intuition i use is that running a node incurs a cost, and you want people proposing txns to pay for the resources they use. the only way we can do that before txn confirmation is to only really consider things that have a chance of getting confirmed on chain, where the txn proposer will have to pay a fee. otherwise, an attacker could create a lot of work for a node "for
<neha> so consider your tips in that analogy -- we don't want there to be a way for a transaction proposer to create a bunch of low-feerate txns without the required cost of paying something higher fee at some point. otherwise, people could spam nodes with low-feerate tips.
<larryruane> michaelfolkson: "... only allowing child-with-parents packages ..." You bring up a point I wanted to confirm, even with this PR (and the package relay ones that came previously), the full DAG package isn't supported, right? It's restricted to just 2 levels, and more than that, a single child with possibly many parents?
<glozow> the biggest reason why this works is because we only allow child-with-parents packages. "validate each individually first" is equivalent "validate all ancestors individually first," which eliminates the problem of ancestor-paying-for-descendant, and we can just use a package feerate
<glozow> ok sure. Would this definition of package feerate work for all types of packages? For example, packages with three generations, a parent with multiple children, unrelated transactions, etc.
<lightlike> no. it might be incentive-compatible to just accept the parent and a child of the first generation, but discard all children later generations, so it would need a more sophisticated definition.
<glozow> lightlike: exactly. package feerate in this definition is ignorant of topology, which is crucial when we're thinking about incentive compatibility of multiple transactions.
<glozow> like we've talked about multiple times, unless there is a dependency relationship, there's no reason why another transaction's fees should be relevant in validation
<bit-pleb-paul> What would happen if a child's fee were still to low, due to user error, e.g. lowballing? The child would simply be replaced by another?
<glozow> Do you agree with the 6 incentive-related behaviors described in the notes above (https://bitcoincore.reviews/24152)? Can you think of anything missing?
<glozow> neha: ah yes that's a very good observation! we'll get to that with package RBF. but at this point, no conflicts are allowed in package validation.