Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

validation, bugfix: provide more info in *MempoolAcceptResult #26646

Merged
merged 10 commits into from
Jan 11, 2023

Conversation

glozow
Copy link
Member

@glozow glozow commented Dec 6, 2022

This PR fixes a bug and improves the mempool accept interface to return information more predictably.

Bug: In package validation, we first try the transactions individually (see doc/policy/packages.md for more explanation) and, if they all failed for missing inputs and policy-related (i.e. fee) reasons, we'll try package validation. Otherwise, we'll just "quit early" since, for example, if a transaction had an invalid signature, adding a child will not help make it valid. Currently, when we quit early, we're not setting the package_state to be invalid, so the caller might think it succeeded. Also, we're returning no results - it makes more sense to return the individual transaction failure. Thanks instagibbs for catching #25038 (comment)!

Also, make the package results interface generally more useful/predictable:

  • Always return the feerate at which a transaction was considered for CheckFeeRate in MempoolAcceptResult::m_effective_feerate when it was successful. This can replace the current PackageMempoolAcceptResult::m_package_feerate, which only sometimes exists.
  • Always provide an entry for every transaction in PackageMempoolAcceptResult::m_tx_results when the error is PCKG_TX.

@DrahtBot
Copy link
Contributor

DrahtBot commented Dec 6, 2022

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

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK instagibbs, achow101, naumenkogs
Approach ACK stickies-v
Stale ACK vincenzopalazzo

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #26711 ([WIP] validate package transactions with their in-package ancestor sets by glozow)
  • #25429 (refactor: Avoid UniValue copies by MarcoFalke)

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.

Copy link
Member

@instagibbs instagibbs left a comment

Choose a reason for hiding this comment

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

Liking the new interfaces significantly more so far, just naming qualms for now

src/validation.cpp Outdated Show resolved Hide resolved
src/validation.cpp Outdated Show resolved Hide resolved
src/validation.cpp Outdated Show resolved Hide resolved
Copy link
Member

@instagibbs instagibbs left a comment

Choose a reason for hiding this comment

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

ACK c5ca075 naming nit left aside

src/validation.cpp Outdated Show resolved Hide resolved
/** If we're doing package validation (i.e. m_package_feerates=true), the "effective"
* package feerate of this transaction, which may include fee and vsize of its ancestors
* and/or descendants. */
CFeeRate m_package_feerate{0};
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering how well-defined this value is. Am I right in thinking that the calculation here is "sum(fees_of_package_transactions)/sum(size_of_package_transactions)"?

What happens in this case: we have 3 transactions A -> B, C, where C is a child of both A and B.

A has a low feerate and cannot make it in to the mempool by itself. (A+B) has a high enough feerate for acceptance, but we receive for validation (A+B+C) instead. B cannot make it in by itself, since it would be missing a parent, and we never try A+B, right? So the package feerate for C would reflect the fees and feerate of B (which could be higher than C's own feerate, I think?).

Do we require that C pay a higher feerate than any of its parents when doing package acceptance? If not, then I'm not sure this value will always make sense, ie the package feerate of a transaction with no children ought never be greater than its own feerate.

Copy link
Member

Choose a reason for hiding this comment

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

Does this comment equally apply to package submission in master?

Copy link
Member

Choose a reason for hiding this comment

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

I think so, but I was about to write a test using the example above, except where C is 0-fee, and see if it makes it in...

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, master does not currently handle this case because we're currently doing A, B, C individually, so we'd get 1 "too low fee" and 2 "missing inputs," and then reject all of them. When I first tried to implement for 1-child-with-parents, I had incorrectly assumed this was okay and the code made it in before I realized - my apologies for that. The intention is definitely to fix it.

Requiring C pay a higher feerate than its parents is ok in the child-with-parents scenario but not generic ancestor package scenario (imagine low fee grandparent, lower fee parent, high fee child). I think we've discussed "for each tx in topological order, calculate its in-package ancestor set that hasn't made it in yet, and submit that as a package" - I have drafted this here.

Btw this test case is implemented here (it should fail on master as you expect).

Copy link
Member

Choose a reason for hiding this comment

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

Can confirm the patch fails as expected: the entire triangle package makes it in the mempool

Copy link
Member Author

Choose a reason for hiding this comment

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

Re: "I'm wondering how well-defined this value is." Last push adds the wtxids of which transactions are included in the feerate, to both MempoolAcceptResult and the testmempoolaccept/submitpackage RPCs.

Re: the problem where we don't always end up with the right transactions, might be better for another PR. I've opened #26711 to do this.

Copy link
Member Author

Choose a reason for hiding this comment

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

@sdaftuar comfortable with resolving for this PR?

Copy link
Contributor

@stickies-v stickies-v 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 - fixes the bug as specified and imo makes the interface more clear

A few more general comments:

  • commit message in b00bebb I think should be "remove PackageMempoolAcceptResult::m_package_feerate" instead of "remove PackageMempoolAccept::m_package_feerate"
  • requires a release notes file highlighting the RPC changes
  • the docstring of assert_equal_package_results (rpc_packages.py) still mentions package feerate which I think is now out of date
  • nit: a723541 could be split in two commits: one for rename, and one for the actual change

src/test/txpackage_tests.cpp Outdated Show resolved Hide resolved
src/validation.cpp Outdated Show resolved Hide resolved
src/rpc/mempool.cpp Outdated Show resolved Hide resolved
test/functional/rpc_packages.py Outdated Show resolved Hide resolved
src/validation.cpp Outdated Show resolved Hide resolved
src/validation.cpp Outdated Show resolved Hide resolved
src/validation.cpp Outdated Show resolved Hide resolved
src/validation.cpp Outdated Show resolved Hide resolved
src/validation.cpp Outdated Show resolved Hide resolved
src/validation.cpp Outdated Show resolved Hide resolved
Copy link
Member

@instagibbs instagibbs left a comment

Choose a reason for hiding this comment

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

reACK 0d383a6

Doc changes, and the returning of wtxids considered for the package feerate being the major changes.

src/validation.cpp Outdated Show resolved Hide resolved
src/validation.cpp Show resolved Hide resolved
Copy link

@vincenzopalazzo vincenzopalazzo left a comment

Choose a reason for hiding this comment

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

ACK 0d383a6

My comments are just comments that included just some considerations where I do not have an answer on why it is done, I do not want to appear like an annoying person :)

Copy link

@vincenzopalazzo vincenzopalazzo left a comment

Choose a reason for hiding this comment

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

reACK 48f81f2

@instagibbs
Copy link
Member

ACK 48f81f2

changes are:
removing redundant nonfatal check
giving it->second a variable for ease of reading
static case of m_vsize to uint32_t
doc and suggested variable renamings

Copy link
Contributor

@stickies-v stickies-v 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 48f81f2 - not feeling super comfortable yet with my review so want to mull it over a bit more.

Mostly a few nits, and also I think effective-feerate being optional in the RPC isn't ideal (but I can move past it). Additionally, I think the last 3 comments from my previous review haven't been addressed yet? The missing RPC docs/release notes is probably the most important one there.

src/validation.cpp Outdated Show resolved Hide resolved
src/validation.cpp Show resolved Hide resolved
src/validation.cpp Outdated Show resolved Hide resolved
src/validation.cpp Outdated Show resolved Hide resolved
src/rpc/mempool.cpp Outdated Show resolved Hide resolved
@naumenkogs
Copy link
Member

ACK 48f81f2

I don't have a big picture about package relay yet, but these changes are good on their own.

src/validation.cpp Outdated Show resolved Hide resolved
glozow and others added 3 commits January 6, 2023 17:37
…and tx result

Bug: not setting package_state means package_state.IsValid() == true and
the caller does not know that this failed.

We won't be validating this transaction again, so it makes sense to return this
failure to the caller.

Rename package_state to package_state_quit_early to make it more clear
what this variable is used for and what its scope is.

Co-authored-by: Greg Sanders <gsanders87@gmail.com>
@glozow
Copy link
Member Author

glozow commented Jan 6, 2023

Sorry for missing a few of your review comments @stickies-v!

  • requires a release notes file highlighting the RPC changes

Added for testmempoolaccept, submitpackage is regtest-only.

  • the docstring of assert_equal_package_results (rpc_packages.py) still mentions package feerate which I think is now out of date

This refers to the "package feerate" concept documented here, rather than the RPC result "package-feerate". So left as is, sorry for that being a bit confusing.

  • nit: a723541 could be split in two commits: one for rename, and one for the actual change

Done

@naumenkogs
Copy link
Member

reACK ada89cb

Copy link
Member

@instagibbs instagibbs left a comment

Choose a reason for hiding this comment

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

reACK ada89cb

src/validation.cpp Show resolved Hide resolved
Copy link
Member

@achow101 achow101 left a comment

Choose a reason for hiding this comment

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

A few not-necessarily blocking comments, but would like to get some rationale for some decisions.

Comment on lines 1281 to 1282
Assume(!args.m_package_feerates);
const std::vector<uint256> single_wtxid{ws.m_ptx->GetWitnessHash()};
Copy link
Member

Choose a reason for hiding this comment

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

In 92d789b "[validation] return wtxids of other transactions whose fees were used"

Since the previous commit adds the calculation for effective_feerate here depending on m_package_feerates, I would expect that the calculation for the involved wtxids would do that as well. Or the previous commit could add this Assume and just do the single tx feerate calculation. The behavior for setting the effective feerate and the involved txids should be unified to avoid bugs.

Copy link
Member Author

Choose a reason for hiding this comment

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

Agree this is more bug-preventative. The downside is we end up constructing all_wtxids without using it when doing a test package mempool accept, but seems alright.

Comment on lines -305 to +316
tx_result = submitpackage_result["tx-results"][tx.getwtxid()]
assert_equal(tx_result, {
"txid": package_txn["txid"],
"vsize": tx.get_vsize(),
"fees": {
"base": DEFAULT_FEE,
}
})
wtxid = tx.getwtxid()
assert wtxid in submitpackage_result["tx-results"]
tx_result = submitpackage_result["tx-results"][wtxid]
assert_equal(tx_result["txid"], tx.rehash())
assert_equal(tx_result["vsize"], tx.get_vsize())
assert_equal(tx_result["fees"]["base"], DEFAULT_FEE)
Copy link
Member

Choose a reason for hiding this comment

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

In 57b56b7 "[rpc] return effective-feerate in testmempoolaccept and submitpackage"

This change doesn't seem to be meaningful? The test passes either way.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ended up keeping it in latest push, as the result now includes optional fields and a feerate so we need to use assert_fee_amount

@@ -217,6 +218,9 @@ static RPCHelpMan testmempoolaccept()
result_inner.pushKV("vsize", virtual_size);
UniValue fees(UniValue::VOBJ);
fees.pushKV("base", ValueFromAmount(fee));
if (fee != tx_result.m_effective_feerate.value().GetFee(virtual_size)) {
Copy link
Member

Choose a reason for hiding this comment

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

In 57b56b7 "[rpc] return effective-feerate in testmempoolaccept and submitpackage"

For here and in submitpackage, it's not clear to me why effective-feerate and effective-includes should be guarded by whether it is different from the base fees. The presence (or lack thereof) of this field indicates whether this transaction was validated as part of the package, but that is also more explicitly represented by effective-includes. I don't think it's harmful to provide this information for all transactions.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah thanks, I think I agree. For submitpackage we should continue to gate on whether the tx was in the mempool already - in that case we wouldn't know whether package feerate was used so I wouldn't want to report a potentially inaccurate feerate. Added a comment about this as well. Since testmempoolaccept doesn't allow already-in-mempool transactions, it should always have this for successful transactions.

Base != effective is no longer a gate. Edited the optional bool for these results to reflect the status.
Some tests needed to be changed (just dropping the effective-* results there because they aren't relevant in those tests and comparing feerates isn't handled properly in assert_equal).

This value creates an extremely confusing interface as its existence is
dependent upon implementation details (whether something was submitted
on its own, etc). MempoolAcceptResult::m_effective_feerate is much more
helpful, as it always exists for submitted transactions.
No release note for submitpackage because it is regtest-only.
This makes the interface more predictable and useful. The caller
understands one or more transactions failed, and can learn what happened
with each transaction. We already have this information, so we might as
well return it.

It doesn't make sense to do this for other PackageValidationResult
values because:
- PCKG_RESULT_UNSET: this means everything succeeded, so the individual
  failures are no longer accurate.
- PCKG_MEMPOOL_ERROR: something went wrong with the mempool logic;
  transaction failures might not be meaningful.
- PCKG_POLICY: this means something was wrong with the package as a
  whole. The caller should use the PackageValidationState to find the
  error, rather than looking at individual MempoolAcceptResults.
Copy link
Member Author

@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.

Thanks @achow101, agree with your comments, addressed.

@@ -217,6 +218,9 @@ static RPCHelpMan testmempoolaccept()
result_inner.pushKV("vsize", virtual_size);
UniValue fees(UniValue::VOBJ);
fees.pushKV("base", ValueFromAmount(fee));
if (fee != tx_result.m_effective_feerate.value().GetFee(virtual_size)) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Ah thanks, I think I agree. For submitpackage we should continue to gate on whether the tx was in the mempool already - in that case we wouldn't know whether package feerate was used so I wouldn't want to report a potentially inaccurate feerate. Added a comment about this as well. Since testmempoolaccept doesn't allow already-in-mempool transactions, it should always have this for successful transactions.

Base != effective is no longer a gate. Edited the optional bool for these results to reflect the status.
Some tests needed to be changed (just dropping the effective-* results there because they aren't relevant in those tests and comparing feerates isn't handled properly in assert_equal).

Comment on lines -305 to +316
tx_result = submitpackage_result["tx-results"][tx.getwtxid()]
assert_equal(tx_result, {
"txid": package_txn["txid"],
"vsize": tx.get_vsize(),
"fees": {
"base": DEFAULT_FEE,
}
})
wtxid = tx.getwtxid()
assert wtxid in submitpackage_result["tx-results"]
tx_result = submitpackage_result["tx-results"][wtxid]
assert_equal(tx_result["txid"], tx.rehash())
assert_equal(tx_result["vsize"], tx.get_vsize())
assert_equal(tx_result["fees"]["base"], DEFAULT_FEE)
Copy link
Member Author

Choose a reason for hiding this comment

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

Ended up keeping it in latest push, as the result now includes optional fields and a feerate so we need to use assert_fee_amount

Comment on lines 1281 to 1282
Assume(!args.m_package_feerates);
const std::vector<uint256> single_wtxid{ws.m_ptx->GetWitnessHash()};
Copy link
Member Author

Choose a reason for hiding this comment

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

Agree this is more bug-preventative. The downside is we end up constructing all_wtxids without using it when doing a test package mempool accept, but seems alright.

@instagibbs
Copy link
Member

reACK 264f9ef

Copy link
Member

@instagibbs instagibbs left a comment

Choose a reason for hiding this comment

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

micro nit

@@ -864,6 +878,17 @@ static RPCHelpMan submitpackage()
result_inner.pushKV("vsize", int64_t{it->second.m_vsize.value()});
UniValue fees(UniValue::VOBJ);
fees.pushKV("base", ValueFromAmount(it->second.m_base_fees.value()));
if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
// Effective feerate is not provided for MEMPOOL_ENTRY transactions even
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Effective feerate is not provided for MEMPOOL_ENTRY transactions even
// Effective feerate is not provided for MEMPOOL_ENTRY(already-in-mempool) transactions even

@achow101
Copy link
Member

ACK 264f9ef

@naumenkogs
Copy link
Member

reACK 264f9ef

@glozow glozow merged commit 2600257 into bitcoin:master Jan 11, 2023
@glozow glozow deleted the package-single-tx-result branch January 11, 2023 13:26
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jan 11, 2023
…lAcceptResult

264f9ef [validation] return MempoolAcceptResult for every tx on PCKG_TX failure (glozow)
dae81e0 [refactor] rename variables in AcceptPackage for clarity (glozow)
da484bc [doc] release note effective-feerate and effective-includes RPC results (glozow)
5eab397 [validation] remove PackageMempoolAcceptResult::m_package_feerate (glozow)
601bac8 [rpc] return effective-includes in testmempoolaccept and submitpackage (glozow)
1691eaa [rpc] return effective-feerate in testmempoolaccept and submitpackage (glozow)
d6c7b78 [validation] return wtxids of other transactions whose fees were used (glozow)
1605886 [validation] return effective feerate from mempool validation (glozow)
5d35b4a [test] package validation quits early due to non-policy, non-missing-inputs failure (glozow)
be2e4d9 [validation] when quitting early in AcceptPackage, set package_state and tx result (glozow)

Pull request description:

  This PR fixes a bug and improves the mempool accept interface to return information more predictably.

  Bug: In package validation, we first try the transactions individually (see doc/policy/packages.md for more explanation) and, if they all failed for missing inputs and policy-related (i.e. fee) reasons, we'll try package validation. Otherwise, we'll just "quit early" since, for example, if a transaction had an invalid signature, adding a child will not help make it valid. Currently, when we quit early, we're not setting the `package_state` to be invalid, so the caller might think it succeeded. Also, we're returning no results - it makes more sense to return the individual transaction failure. Thanks instagibbs for catching bitcoin#25038 (comment)!

  Also, make the package results interface generally more useful/predictable:
  - Always return the feerate at which a transaction was considered for `CheckFeeRate` in `MempoolAcceptResult::m_effective_feerate` when it was successful. This can replace the current `PackageMempoolAcceptResult::m_package_feerate`, which only sometimes exists.
  - Always provide an entry for every transaction in `PackageMempoolAcceptResult::m_tx_results` when the error is `PCKG_TX`.

ACKs for top commit:
  instagibbs:
    reACK bitcoin@264f9ef
  achow101:
    ACK 264f9ef
  naumenkogs:
    reACK 264f9ef

Tree-SHA512: ce7fd9927a80030317cc6157822596e85a540feff5dbf5eea7c62da2eb50c917cdddc9da1e2ff62cc18b98b27d360151811546bd9d498859679a04bbee090837
maflcko pushed a commit that referenced this pull request Jan 12, 2023
c28d461 doc: move errant release note to doc/ (fanquake)

Pull request description:

  Release note from #26646 should be in doc/.

Top commit has no ACKs.

Tree-SHA512: 741323c0bc526163d65e441c9e677ee3613ed0c55b1880b99fb63b0f3306aeca0cad83dadef05c23def8d0fc6a224956b27ae1bf4b1ff980a1be2d31feef7e3d
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jan 12, 2023
c28d461 doc: move errant release note to doc/ (fanquake)

Pull request description:

  Release note from bitcoin#26646 should be in doc/.

Top commit has no ACKs.

Tree-SHA512: 741323c0bc526163d65e441c9e677ee3613ed0c55b1880b99fb63b0f3306aeca0cad83dadef05c23def8d0fc6a224956b27ae1bf4b1ff980a1be2d31feef7e3d
@glozow glozow mentioned this pull request Sep 27, 2023
54 tasks
@bitcoin bitcoin locked and limited conversation to collaborators Jan 11, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
Development

Successfully merging this pull request may close these issues.

8 participants