Skip to content

Conversation

@darosior
Copy link
Member

@darosior darosior commented Aug 8, 2021

We were still reporting transactions that inherited signaling as being replaceable in IsRBFOptIn.
In addition, the second commits does a bunch of refactoring by getting rid of IsRBFOptIn entirely and removing the check for RBF from the node interface since we actually don't need access to the mempool.

Related to #22209, kinda followup of #21946.

Copy link
Contributor

@mjdietzx mjdietzx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the simplification this PR provides and think it's nicely done. But, my main concern is that existing applications/users might currently rely on bip125-replaceable for this type of behavior:

// Applications relying on first-seen mempool behavior should
// check all unconfirmed ancestors; otherwise an opt-in ancestor
// might be replaced, causing removal of this descendant.

which after this PR won't be the case.

Based on the description of bip125-replaceable:

Whether this transaction could be replaced due to BIP125 (replace-by-fee)

even though the transaction can't be directly replaced in the mempool, it can effectively be replaced by replacing one of its ancestors that does explicitly signal RBF.

What are your thoughts on this? I'm just wondering what direction this PR moves us in long-term as I saw some good discussion/ideas from @ariard and @ajtowns in #21946

@DrahtBot
Copy link
Contributor

DrahtBot commented Aug 8, 2021

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #22798 (doc: Fix RPC result documentation by MarcoFalke)
  • #22796 (RBF move (1/3): extract BIP125 Rule 5 into policy/rbf by glozow)
  • #22788 (scripted-diff: Use generate* from TestFramework by MarcoFalke)
  • #22698 (Implement RBF inherited signaling and fix getmempoolentry returned bip125-replaceable status by mjdietzx)
  • #22689 (rpc: deprecate top-level fee fields in getmempool RPCs by josibake)
  • #22677 ([RFC] cut the validation <-> txmempool circular dependency by glozow)
  • #22675 (MOVEONLY policy: extract RBF logic into policy/rbf by glozow)
  • #22650 (Remove -deprecatedrpc=addresses flag and corresponding code/logic by mjdietzx)
  • #22290 (Package Mempool Submission with Package Fee-Bumping by glozow)
  • #21260 (wallet: indicate whether a transaction is in the mempool by danben)
  • #21245 (rpc: Add level 3 verbosity to getblock RPC call. by fyquah)
  • #19792 (rpc: Add dumpcoinstats by fjahr)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@DrahtBot DrahtBot mentioned this pull request Aug 9, 2021
@darosior
Copy link
Member Author

darosior commented Aug 9, 2021

my main concern is that existing applications/users might currently rely on bip125-replaceable for this type of behavior

If they rely on this, then they are already broken (as demonstrated by CVE-2021-31876). This is the point of this PR actually, and i would argue it is actually a bugfix.

@mjdietzx
Copy link
Contributor

mjdietzx commented Aug 11, 2021

If they rely on this, then they are already broken (as demonstrated by CVE-2021-31876). This is the point of this PR actually, and i would argue it is actually a bugfix.

I'm specifically referring to this comment in MemPoolAccept::PreChecks:

// Applications relying on first-seen mempool behavior should
// check all unconfirmed ancestors; otherwise an opt-in ancestor
// might be replaced, causing removal of this descendant.

Before your PR, applications could do this check by just reading the result of {RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") because isRBFOptIn went through all the transaction's ancestors and checked if they are opt-in.

But after your PR, this is no longer the case. I see that your PR fixes the bug from the perspective of "I know I can replace this transaction based on the value of bip125-replaceable, and it wont get unexpectedly rejected". But I do see it as a change in behavior from the perspective of "I'm an application that relies on first-seen mempool behavior, and I'm looking at the value of bip125-replaceable to know if this transaction might be removed due to an opt-in unconfirmed parent transaction being replaced somewhere up the ancestry tree"

Copy link
Member

@glozow glozow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK. Technically, we are returning the correct BIP125 replaceability of a transaction, but it's not useful for a client to get this information since it doesn't actually reflect what would happen in their mempool. I also agree with the cleanup of RBFTransactionState and other no-longer-used code.

I think it would be better to add a new RPC field "replaceable" (since it's not really BIP125) to return this as a bool result, and remove "bip125-replaceable" or make it an alias of "replaceable" and remove it later.

@Talkless
Copy link

Is this commit message: since we don't need the mempool anymore since we don't need the mempool anymore.. OK..?

@darosior
Copy link
Member Author

I think it would be better to add a new RPC field "replaceable" (since it's not really BIP125) to return this as a bool result

Good call. Done in a50a332.

Also applied your and @Talkless 's comments.

Copy link
Member

@glozow glozow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach ACK

have a few tiny nits 😅 could also use a functional test for the deprecation behavior, e.g. something like this

Copy link
Member

@maflcko maflcko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Funny that this bugfix removes more than 100 lines of code

@michaelfolkson
Copy link

This was already done as part of announcing the CVE

The approach that Core would be taking to address the discrepancy between the BIP and the Core implementation surely wasn't?

@michaelfolkson
Copy link

I don't think this is tricky at all.

Under the assumption that Core will maintain its current mempool behavior (versus changing it to meet the BIP wording because that is deemed superior logic) I agree it isn't tricky. But making that assumption in the first place seems tricky to me. A short term stopgap with minimal code changes in anticipation of full RBF or a more invasive code change for what some deem superior logic in the meantime as full RBF might never land. (I think that's where we are anyway). Let's at least be transparent about that rather than glossing over it.

@maflcko
Copy link
Member

maflcko commented Aug 26, 2021

Fixes #22209, kinda followup of #21946.

Does it really fix #22209? The new replacable field still doesn't accurately reflect whether a transaction can be replaced by a higher fee tx. See #22809. Both fields (the newly added one and the removed one in this pull) are needed to say that. And for users to interpret the fields for practical purposes, they will also need to know who controls the keys that were used to sign the inputs of the transactions.

@darosior
Copy link
Member Author

Does it really fix #22209? The new replacable field still doesn't accurately reflect whether a transaction can be replaced by a higher fee tx.

The replaceable field does reflect whether a transaction signals for RBF. Maybe it'd be wiser that I update the documentation to explicitly state it, rather than maybe implying (i don't parse it like that but maybe it could be confused as such, idk) that !replaceable is an assurance that "your transaction won't be evicted not matter what" which we of course can't provide.
However i'm curious what should be done to fix #22209. But happy to remove the link from the OP (updated it).

Both fields (the newly added one and the removed one in this pull) are needed to say that

The newly added one is a subset of the removed one here, so i'm not sure to get how having both would help.

@maflcko
Copy link
Member

maflcko commented Aug 27, 2021

!replaceable is an assurance that "your transaction won't be evicted not matter what" which we of course can't provide.

I know that the new field doesn't do that. My point was that with the old field, you could see !bip125-replaceable and deduce that with the state of the blockchain at the time of the call and the policy rules of your node, the tx won't be replaced by another transaction with a higher fee in the mempool. (Obviously that doesn't mean you or your wallet should trust such a tx, but this discussion seems unrelated to returning the information for the raw mempool).

@maflcko
Copy link
Member

maflcko commented Aug 27, 2021

However i'm curious what should be done to fix #22209.

I have no idea what is needed to fix the issue. Maybe the reason should be reported as an enum? #16490?

The states could be:

  • opt-in-signal
  • rbf-inheritance
  • (full-rbf)
  • (timeout-rbf)
  • ...

The ones in braces are for not-(yet) Bitcoin Core policies.

@darosior darosior force-pushed the rbf_optin_nomempool branch from 2280b31 to 7ead459 Compare August 29, 2021 12:38
@darosior
Copy link
Member Author

Fixed the bad english in the bip125-replaceable doc and removed needless includes in the fuzz target after @jnewbery 's review. Thanks!

@michaelfolkson
Copy link

michaelfolkson commented Aug 30, 2021

I think the discussion on the Approach (N)ACK part of this PR and comparing the approaches of #22665 versus #22698 has been weak and almost dismissive (the BIP isn't a standard etc). In my view @mjdietzx @rajarshimaitra have a valid perspective including in @mjdietzx's case coding up and opening an alternative PR.

@laanwj said on IRC:

There are no other mempool policy implementations in wide use

There's a lot of inertia involved in trying to change the implementation, if something gets merged it will take a long time before that version is widely spread on the network to be behavior other software can rely on

From the above I'm an Approach ACK on this PR meeting shorter term user needs and think a rebased #22698 can/should be considered post the merging of this PR. But my gosh, unless we want to ditch the Concept ACK, Approach ACK part of the PR review process I'd hope we can do better in these kinds of scenarios where someone presents a competing PR....

@michaelfolkson
Copy link

michaelfolkson commented Aug 30, 2021

Great comment too earlier from @glozow on this PR (which was deleted but I don't think it should have been)

I don't think this is tricky at all. Not thinking about the BIP while reviewing this PR:

In our mempool code, a transaction that doesn't signal RBF is not opting in. IsRBFOptIn returns a result that is inconsistent with what our mempool will do. Since the RPC code uses IsRBFOptIn(), it returns bip125-replaceable as "yes" or "unknown" for mempool transactions that are not actually RBFable. This can be very confusing and frustrating for users. We should not just change the result of bip125-replaceable, as this can also be confusing. So this PR adds a new field, replaceable, which tells users whether or not our mempool code thinks this transaction opts in to RBF.

darosior and others added 6 commits September 1, 2021 08:23
As disclosed by CVE-2021-31876, the mempool acceptance logic doesn't
implement BIP125's inheritance rule.
However, we were still mistakenly reporting transactions that don't
explicitly signal for RBF as being replaceable.

Signed-off-by: Antoine Poinsot <[email protected]>
The enum was only needed for the "unknown" RBF state of a transaction
when we can't fetch its ancestors. Since we don't actually implement
inherited RBF signaling, the replaceability of a transaction is only a
function of itself and cannot be unknown.

This also does a bit of cleanup, as there is no need for the RBF check
to be part of the node interface since we don't need the mempool anymore..

Signed-off-by: Antoine Poinsot <[email protected]>
To avoid linking against server. For example, using it to deprecate an
RPC in wallet/rpcwallet would require libbitcoin_wallet to link against
rpc/server and all its dependencies.

Signed-off-by: Antoine Poinsot <[email protected]>
This doesn't need to be a string anymore. And mentioning BIP125 is
confusing, as we technically don't return the replaceability as defined
by BIP125 (which includes the inheritance rule).

Signed-off-by: Antoine Poinsot <[email protected]>
@darosior darosior force-pushed the rbf_optin_nomempool branch from 7ead459 to cc091fa Compare September 1, 2021 07:28
@darosior
Copy link
Member Author

darosior commented Sep 1, 2021

Rebased after #22796 merge.

Copy link
Member

@glozow glozow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK cc091fa

My assumption was that this RPC result would be used to see if the inputs can be used in an RBF transaction (I don't think anything can be expected to stay in the mempool)? And since a transaction can always be evicted due to higher fee transactions squeezing it out of the mempool, I thought of replaceable as "does this transaction signal for rbf" rather than "can this transaction be replaced" which is always yes.

I can also see how replaceable could naturally be defined as "can this transaction be evicted due to RBF," so maybe calling the result "signals-rbf" would be more accurate. @MarcoFalke's suggestion of an enum of reasons makes sense to me too.

entry.pushKV("bip125-replaceable", rbfStatus);
const bool signals_rbf = confirms <= 0 && SignalsOptInRBF(*wtx.tx);
entry.pushKV("replaceable", signals_rbf);
if (IsDeprecatedRPCEnabled("bip125")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 26c7fc0 rpc: deprecate 'bip125-replaceable' in favour of 'replaceable'

Could you grab this via the Chain interface, i.e. call chain.rpcEnableDeprecated("bip125") instead of moving IsDeprecatedRPCEnabled() to util? Or is it better to do it this way and take the opportunity to remove rpcEnableDeprecated by changing this one too? (maybe scope creepish, sorry)

@maflcko
Copy link
Member

maflcko commented Sep 1, 2021

I think there is a difference between "This tx can be evicted due to mempool size if there are 300 MB worth of txs on top of it" and "This tx can be replaced by a single tx that pays a higher fee than the tx(s) it evicts".

I think it makes sense to distinguish the mempool removal reasons, as it is done internally in the code as well:

enum class MemPoolRemovalReason {
    EXPIRY,      //!< Expired from mempool
    SIZELIMIT,   //!< Removed in size limiting
    REORG,       //!< Removed for reorganization
    BLOCK,       //!< Removed for block
    CONFLICT,    //!< Removed for conflict with in-block transaction
    REPLACED,    //!< Removed for replacement
};

NACK from me on the changes here.

@darosior
Copy link
Member Author

darosior commented Sep 1, 2021

Sigh. Removal reason has nothing to do with this PR.

I have no intention to continue tweaking the RPC code nor keeping up with your nitpicks, i initially picked this up because the current interface is wrong and misleading hence potentially dangerous for downstream users. But as you wish, i guess.

@darosior darosior closed this Sep 1, 2021
@darosior
Copy link
Member Author

darosior commented Sep 1, 2021

Thanks to everyone who helped moving this forward. This is up for grabs, i guess.

@maflcko
Copy link
Member

maflcko commented Sep 1, 2021

Sigh. Removal reason has nothing to do with this PR.

This was a reply to the previous comment (#22665 (review)), which mentioned that txs can be evicted from the mempool for reaching the size limit.

But as you wish, i guess.

I hope you didn't close this pull request because I left a comment with a concern (which is not a nitpick). I want to apologise for leaving nitpicks before reviewing the concept of the pull.

the current interface is wrong and misleading

I agree. Though, I also think that the interface proposed in this pull request isn't crystal clear about the fee replacability status of the tx, which is why I raised the concern.

@darosior
Copy link
Member Author

darosior commented Sep 2, 2021

@MarcoFalke sorry for overreacting.

I also think that the interface proposed in this pull request isn't crystal clear about the fee replacability status of the tx

Ok, then we could go for the minimal fix (which i initially proposed, to stop returning "yes" or "unknown" if the transaction is not replaceable), and introduce the new interface in a following PR?

Regarding the interface, you proposed:

I have no idea what is needed to fix the issue. Maybe the reason should be reported as an enum? #16490?
The states could be:
- opt-in-signal
- rbf-inheritance
- (full-rbf)
- (timeout-rbf)
- ...
The ones in braces are for not-(yet) Bitcoin Core policies.

I think of those only opt-in-signal makes sense to have: reporting that it signals by inheritance while we don't implement this rule sounds confusing to me.
However i understand the future-proofing argument raised in #16490 and we now have the opportunity to introduce it. Maybe we can at least introduce the enum with a single variant for now?

So, how about going first going for the minimal fix here and then proceed with something like #16490 to deprecate bip125-replaceable in favour of a new field more complete about the replaceability status of the transaction?

@maflcko
Copy link
Member

maflcko commented Sep 2, 2021

reporting that it signals by inheritance while we don't implement this rule sounds confusing to me.

The rule isn't implemented fully, but surely is implemented partially in Bitcoin Core. Bitcoin Core simply has the additional requirement that an rbf-signalling parent must be replaced in the same instance.

Not implementing the rule (or rather implementing the inverse of the rule) would mean: A non-signalling child tx could remove the signalling status from a parent, and thus prevent replacement. This is what a plain no for the non-signalling child would imply (this pr), which is my concern.

@michaelfolkson
Copy link

For the benefit of reviewers this PR was NACKed by @MarcoFalke and it appears that @glozow is now comfortable with the approach taken in #22698 which was earlier seen as a competing PR.

This PR is currently closed but I think it is more important we get the approach right than just blindly picking whichever PR is kept open by its author. However, it does seem that @MarcoFalke, @glozow @mjdietzx @ariard etc are comfortable with the approach taken in #22698.

Thanks for the work on this @darosior, sorry for the frustration you've experienced. I've certainly wavered on this and seems like others have been unsure too.

@bitcoin bitcoin locked and limited conversation to collaborators Jul 11, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants