8000 wallet: Use GetSelectionAmount in ApproximateBestSubset by achow101 · Pull Request #22686 · bitcoin/bitcoin · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

wallet: Use GetSelectionAmount in ApproximateBestSubset #22686

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

Merged
merged 3 commits into from
Aug 16, 2021

Conversation

achow101
Copy link
Member

The m_value used for the target calculation in ApproximateBestSubset is incorrect, it should be GetSelectionAmount. This causes a bug that is only apparent when the minimum relay fee is set to be very high.

A test case is added for this, in addition to an assert in CreateTransactionInternal that would have also caught this issue if someone were able to hit the edge case.

Fixes #22670

@achow101
Copy link
Member Author

For 22.0 backport (this bug may cause other issues that are not as immediately obvious).

@fanquake
Copy link
Member

Failure in the previous releases CI. Similar to #18737 / #18737:

 test  2021-08-12T02:39:40.038000Z TestFramework (ERROR): Assertion failed 
                                   Traceback (most recent call last):
                                     File "/tmp/cirrus-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/test/functional/test_framework/test_framework.py", line 131, in main
                                       self.run_test()
                                     File "/tmp/cirrus-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/test/functional/feature_fee_estimation.py", line 271, in run_test
                                       check_estimates(self.nodes[1], self.fees_per_kb)
                                     File "/tmp/cirrus-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/test/functional/feature_fee_estimation.py", line 155, in check_estimates
                                       check_smart_estimates(node, fees_seen)
                                     File "/tmp/cirrus-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/test/functional/feature_fee_estimation.py", line 145, in check_smart_estimates
                                       % (feerate, last_feerate))
                                   AssertionError: Estimated fee (0.000238) larger than last fee (0.000201) for lower number of confirms
 test  2021-08-12T02:39:40.040000Z TestFramework (DEBUG): Closing down network thread 
 test  2021-08-12T02:39:40.041000Z TestFramework (INFO): Stopping nodes 
 test  2021-08-12T02:39:40.041000Z TestFramework.node0 (DEBUG): Stopping node 

@DrahtBot
Copy link
Contributor

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

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #22049 (rpc: allow specifying min chain depth for inputs in fund calls by champo)
  • #17211 (Allow fundrawtransaction and walletcreatefundedpsbt to take external inputs by achow101)

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.

@fanquake
Copy link
Member

cc @xekyo

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.

To be clear, the bug is that it is using the output value, even if we're not subtracting fee from outputs. We use GetSelectionAmount to switch between effective and output value as our target based on subtracting fee from outputs or not.

This explanation should go in the commit message, or at least the OP to get pulled in on merge.

For target value calculations, GetSelectionAmount should be used, not
m_effective_value or m_value.

Specifically, ApproximateBestSubset mistakenly uses m_value when
calculating whether the target value has been met. This has been changed
to use GetSelectionAmount.
When the fee is not subtracted from the outputs, the amount that has
been reserved for the fee (change_and_fee - change_amount) must be
enough to cover the fee that is needed. It would be a bug to not do so,
so use an assert to make this obvious if such a situation were to occur.
ApproximateBestSubset had an edge case (due to not using
GetSelectionAmount) where it was possible for it to return success but
fail to select enough to cover transaction fees. A test is added that
could trigger this failure prior to the fix being implemented.
@achow101
Copy link
Member Author

To be clear, the bug is that it is using the output value, even if we're not subtracting fee from outputs. We use GetSelectionAmount to switch between effective and output value as our target based on subtracting fee from outputs or not.

This explanation should go in the commit message, or at least the OP to get pulled in on merge.

I've added a specific explanation in the first commit

@achow101 achow101 force-pushed the use-getselectionvalue branch from 5a89197 to 92885c4 Compare August 13, 2021 04:35
@fanquake fanquake requested a review from meshcollider August 13, 2021 06:43
Copy link
Contributor
@meshcollider meshcollider left a comment

Choose a reason for hiding this comment

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

utACK 6e6a7e1

Verified the test fails on master but passes with this PR

@instagibbs
Copy link
Member

utACK 92885c4

@murchandamus
Copy link
Contributor

Post-merge utACK 92885c4

@achow101 achow101 mentioned this pull request Aug 18, 2021
@fanquake
Copy link
Member

@xekyo thanks for following up.

hebasto pushed a commit to hebasto/bitcoin that referenced this pull request Aug 19, 2021
For target value calculations, GetSelectionAmount should be used, not
m_effective_value or m_value.

Specifically, ApproximateBestSubset mistakenly uses m_value when
calculating whether the target value has been met. This has been changed
to use GetSelectionAmount.

Github-Pull: bitcoin#22686
Rebased-From: 2de222c
hebasto pushed a commit to hebasto/bitcoin that referenced this pull request Aug 19, 2021
When the fee is not subtracted from the outputs, the amount that has
been reserved for the fee (change_and_fee - change_amount) must be
enough to cover the fee that is needed. It would be a bug to not do so,
so use an assert to make this obvious if such a situation were to occur.

Github-Pull: bitcoin#22686
Rebased-From: d926232
hebasto pushed a commit to hebasto/bitcoin that referenced this pull request Aug 19, 2021
ApproximateBestSubset had an edge case (due to not using
GetSelectionAmount) where it was possible for it to return success but
fail to select enough to cover transaction fees. A test is added that
could trigger this failure prior to the fix being implemented.

Github-Pull: bitcoin#22686
Rebased-From: 92885c4
@hebasto
Copy link
Member
hebasto commented Aug 19, 2021

Backported in #22629.

Copy link
Contributor
@S3RK S3RK left a comment

Choose a reason for hiding this comment

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

See #22742

tx = tester.createrawtransaction([], [{funds.getnewaddress(): 2}])
no_change_tx = tester.fundrawtransaction(tx, {"subtractFeeFromOutputs": [0]})

overhead_fees = feerate * len(tx) / 2 / 1000
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we divide by 2 here?

Copy link
Member Author

Choose a reason for hiding this comment

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

To get the byte length of the hex string.

lower_bound = Decimal("2.00000001") - fees
# The target value must be at most 2 - cost_of_change - not_input_fees - min_change (these are all
# included in the target before ApproximateBestSubset).
upper_bound = Decimal("2.0") - cost_of_change - overhead_fees - Decimal("0.01")
Copy link
Contributor

Choose a reason for hiding this comment

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

The test also passes with much higher upper_bound.

Copy link
Member Author

Choose a reason for hiding this comment

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

The lower and upper bounds are the minimum and maximum values that cause this test to fail prior to this PR. So a higher upper bound should still pass after this PR, and all values in between should pass after this PR.

maflcko pushed a commit to maflcko/bitcoin-core that referenced this pull request Aug 19, 2021
8dcbbbe test: fix bug in 22686 (S3RK)

Pull request description:

  It seems there is a bug in the test in bitcoin#22686, the code behaviour itself looks correct.

  Instead of verifying the scenario from bitcoin#22670 with both `upper_bound` and `lower_bound` for the transaction amount, the tests verified `lower_bound` two times. This fix is to properly use function parameter instead of a variable from the scope. The test still passes with both values, so no code changes are required.

ACKs for top commit:
  achow101:
    ACK 8dcbbbe

Tree-SHA512: 82837b2e00bd075a7b6b7a31031f4d3ba1ab4bd5967e90488f527fcc618aeb3945d6ae3ed66b9eb11616dda3ef376c5001559d2a7a79a759fc6800358bf52537
hebasto pushed a commit to hebasto/bitcoin that referenced this pull request Aug 20, 2021
For target value calculations, GetSelectionAmount should be used, not
m_effective_value or m_value.

Specifically, ApproximateBestSubset mistakenly uses m_value when
calculating whether the target value has been met. This has been changed
to use GetSelectionAmount.

Github-Pull: bitcoin#22686
Rebased-From: 2de222c
hebasto pushed a commit to hebasto/bitcoin that referenced this pull request Aug 20, 2021
When the fee is not subtracted from the outputs, the amount that has
been reserved for the fee (change_and_fee - change_amount) must be
enough to cover the fee that is needed. It would be a bug to not do so,
so use an assert to make this obvious if such a situation were to occur.

Github-Pull: bitcoin#22686
Rebased-From: d926232
hebasto pushed a commit to hebasto/bitcoin that referenced this pull request Aug 20, 2021
ApproximateBestSubset had an edge case (due to not using
GetSelectionAmount) where it was possible for it to return success but
fail to select enough to cover transaction fees. A test is added that
could trigger this failure prior to the fix being implemented.

Github-Pull: bitcoin#22686
Rebased-From: 92885c4
hebasto pushed a commit to hebasto/bitcoin that referenced this pull request Aug 20, 2021
For target value calculations, GetSelectionAmount should be used, not
m_effective_value or m_value.

Specifically, ApproximateBestSubset mistakenly uses m_value when
calculating whether the target value has been met. This has been changed
to use GetSelectionAmount.

Github-Pull: bitcoin#22686
Rebased-From: 2de222c
hebasto pushed a commit to hebasto/bitcoin that referenced this pull request Aug 20, 2021
When the fee is not subtracted from the outputs, the amount that has
been reserved for the fee (change_and_fee - change_amount) must be
enough to cover the fee that is needed. It would be a bug to not do so,
so use an assert to make this obvious if such a situation were to occur.

Github-Pull: bitcoin#22686
Rebased-From: d926232
hebasto pushed a commit to hebasto/bitcoin that referenced this pull request Aug 20, 2021
ApproximateBestSubset had an edge case (due to not using
GetSelectionAmount) where it was possible for it to return success but
fail to select enough to cover transaction fees. A test is added that
could trigger this failure prior to the fix being implemented.

Github-Pull: bitcoin#22686
Rebased-From: 92885c4
hebasto pushed a commit to hebasto/bitcoin that referenced this pull request Aug 20, 2021
For target value calculations, GetSelectionAmount should be used, not
m_effective_value or m_value.

Specifically, ApproximateBestSubset mistakenly uses m_value when
calculating whether the target value has been met. This has been changed
to use GetSelectionAmount.

Github-Pull: bitcoin#22686
Rebased-From: 2de222c
hebasto pushed a commit to hebasto/bitcoin that referenced this pull request Aug 20, 2021
When the fee is not subtracted from the outputs, the amount that has
been reserved for the fee (change_and_fee - change_amount) must be
enough to cover the fee that is needed. It would be a bug to not do so,
so use an assert to make this obvious if such a situation were to occur.

Github-Pull: bitcoin#22686
Rebased-From: d926232
hebasto pushed a commit to hebasto/bitcoin that referenced this pull request Aug 20, 2021
ApproximateBestSubset had an edge case (due to not using
GetSelectionAmount) where it was possible for it to return success but
fail to select enough to cover transaction fees. A test is added that
could trigger this failure prior to the fix being implemented.

Github-Pull: bitcoin#22686
Rebased-From: 92885c4
laanwj added a commit that referenced this pull request Aug 26, 2021
32e1424 Fix build with Boost 1.77.0 (Rafael Sadowski)
cb34a0a qt: Handle new added plurals in bitcoin_en.ts (Hennadii Stepanov)
068985c doc: Mention the flat directory structure for uploads (Andrew Chow)
27d43e5 guix: Don't include directory name in SHA256SUMS (Andrew Chow)
88fb7e3 test: fix bug in 22686 (S3RK)
63fec7e clientversion: No suffix #if CLIENT_VERSION_IS_RELEASE (Carl Dong)
dfaffbe test: Test for ApproximateBestSubset edge case with too little fees (Andrew Chow)
e86b023 wallet: Assert that enough was selected to cover the fees (Andrew Chow)
ffc81e2 wallet: Use GetSelectionAmount for target value calculations (Andrew Chow)
ce77b45 release: Release with separate SHA256SUMS and sig files (Carl Dong)
cb491bd guix-verify: 
10000
Non-zero exit code when anything fails (Carl Dong)
6a611d2 gui: ensure external signer option remains disabled without signers (Andrew Chow)
e9b4487 qt: Fix regression in "Encrypt Wallet" menu item (Hennadii Stepanov)
57fce06 consensus/params: simplify ValidDeployment check to avoid gcc warning (Anthony Towns)
e9d30fb ci: Run fuzzer task for the master branch only (Hennadii Stepanov)

Pull request description:

  Backported:

  1) #22730
  1) bitcoin-core/gui#393
  1) #22597
  1) bitcoin-core/gui#396
  1) #22643
  1) #22642
  1) #22685
  1) #22686
  1) #22654
  1) #22742
  1) bitcoin-core/gui#406
  1) #22713

ACKs for top commit:
  laanwj:
    Code list-of-backported-PRs review ACK 32e1424

Tree-SHA512: f5e2dd1be6cdcd39368eeb5d297b3ff4418d0bf2e70c90e59ab4ba1dbf16f773045d877b4997511de58c3aca75a978dcf043e338bad23951557e2a27ccc845f6
fujicoin pushed a commit to fujicoin/fujicoin-22.0 that referenced this pull request Aug 27, 2021
For target value calculations, GetSelectionAmount should be used, not
m_effective_value or m_value.

Specifically, ApproximateBestSubset mistakenly uses m_value when
calculating whether the target value has been met. This has been changed
to use GetSelectionAmount.

Github-Pull: bitcoin/bitcoin#22686
Rebased-From: 2de222c40198d3d528668d78cc52e2ce3fa96765
fujicoin pushed a commit to fujicoin/fujicoin-22.0 that referenced this pull request Aug 27, 2021
When the fee is not subtracted from the outputs, the amount that has
been reserved for the fee (change_and_fee - change_amount) must be
enough to cover the fee that is needed. It would be a bug to not do so,
so use an assert to make this obvious if such a situation were to occur.

Github-Pull: bitcoin/bitcoin#22686
Rebased-From: d9262324e80da32725e21d4e0fbfee56f25490b1
fujicoin pushed a commit to fujicoin/fujicoin-22.0 that referenced this pull request Aug 27, 2021
ApproximateBestSubset had an edge case (due to not using
GetSelectionAmount) where it was possible for it to return success but
fail to select enough to cover transaction fees. A test is added that
could trigger this failure prior to the fix being implemented.

Github-Pull: bitcoin/bitcoin#22686
Rebased-From: 92885c4f69a5e6fc4989677d6e5be8a666fbff0d
gwillen pushed a commit to gwillen/elements that referenced this pull request Jul 27, 2022
For target value calculations, GetSelectionAmount should be used, not
m_effective_value or m_value.

Specifically, ApproximateBestSubset mistakenly uses m_value when
calculating whether the target value has been met. This has been changed
to use GetSelectionAmount.

Github-Pull: bitcoin/bitcoin#22686
Rebased-From: 2de222c
gwillen pushed a commit to gwillen/elements that referenced this pull request Jul 27, 2022
When the fee is not subtracted from the outputs, the amount that has
been reserved for the fee (change_and_fee - change_amount) must be
enough to cover the fee that is needed. It would be a bug to not do so,
so use an assert to make this obvious if such a situation were to occur.

Github-Pull: bitcoin/bitcoin#22686
Rebased-From: d926232
gwillen pushed a commit to gwillen/elements that referenced this pull request Jul 27, 2022
ApproximateBestSubset had an edge case (due to not using
GetSelectionAmount) where it was possible for it to return success but
fail to select enough to cover transaction fees. A test is added that
could trigger this failure prior to the fix being implemented.

Github-Pull: bitcoin/bitcoin#22686
Rebased-From: 92885c4
gwillen pushed a commit to gwillen/elements that referenced this pull request Aug 1, 2022
For target value calculations, GetSelectionAmount should be used, not
m_effective_value or m_value.

Specifically, ApproximateBestSubset mistakenly uses m_value when
calculating whether the target value has been met. This has been changed
to use GetSelectionAmount.

Github-Pull: bitcoin/bitcoin#22686
Rebased-From: 2de222c
gwillen pushed a commit to gwillen/elements that referenced this pull request Aug 1, 2022
When the fee is not subtracted from the outputs, the amount that has
been reserved for the fee (change_and_fee - change_amount) must be
enough to cover the fee that is needed. It would be a bug to not do so,
so use an assert to make this obvious if such a situation were to occur.

Github-Pull: bitcoin/bitcoin#22686
Rebased-From: d926232
gwillen pushed a commit to gwillen/elements that referenced this pull request Aug 1, 2022
ApproximateBestSubset had an edge case (due to not using
GetSelectionAmount) where it was possible for it to return success but
fail to select enough to cover transaction fees. A test is added that
could trigger this failure prior to the fix being implemented.

Github-Pull: bitcoin/bitcoin#22686
Rebased-From: 92885c4
// The only time that fee_needed should be less than the amount available for fees (in change_and_fee - change_amount) is when
// we are subtracting the fee from the outputs. If this occurs at any other time, it is a bug.
assert(coin_selection_params.m_subtract_fee_outputs || fee_needed <= change_and_fee - change_amount);

// Update nFeeRet in case fee_needed changed due to dropping the change output
if (fee_needed <= change_and_fee - change_amount) {
nFeeRet = change_and_fee - change_amount;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this logic looks a little bit funny now. I think it might make more sense to have modified this if block as

    if (fee_needed <= change_and_fee - change_amount) {
        nFeeRet = change_and_fee - change_amount;
    } else {
        // The only time that fee_needed should be less than the amount available for fees (in change_and_fee - change_amount) is when
        // we are subtracting the fee from the outputs. If this occurs at any other time, it is a bug.
        assert(coin_selection_params.m_subtract_fee_outputs);
    }

which is logically equivalent but doesn't do the same check twice

Copy link
Contributor

Choose a reason for hiding this comment

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

JFYI #25647 changes these lines

apoelstra pushed a commit to apoelstra/elements that referenced this pull request Sep 20, 2022
When the fee is not subtracted from the outputs, the amount that has
been reserved for the fee (change_and_fee - change_amount) must be
enough to cover the fee that is needed. It would be a bug to not do so,
so use an assert to make this obvious if such a situation were to occur.

Github-Pull: bitcoin/bitcoin#22686
Rebased-From: d926232
delta1 added a commit to delta1/elements that referenced this pull request Apr 9, 2023
@bitcoin bitcoin locked and limited conversation to collaborators Aug 8, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[22.0rc2] There are cases where the fee calculation is incorrect
9 participants
0