Page MenuHomePhabricator

[backport#16766] wallet: Make IsTrusted scan parents recursively
ClosedPublic

Authored by majcosta on Oct 1 2020, 00:22.

Details

Summary

Expand on wallet_balance.py comment from https://github.com/bitcoin/bitcoin/pull/16766\#issuecomment-527563982 (Jeremy Rubin)
Update release notes to mention changes to IsTrusted and impact on wallet (Jeremy Rubin)
Systematize style of IsTrusted single line if (Jeremy Rubin)
update variable naming conventions for IsTrusted (Jeremy Rubin)
Update comment in test/functional/wallet_balance.py (Jeremy Rubin)
Update wallet_balance.py test to reflect new behavior (Jeremy Rubin)
Reuse trustedParents in looped calls to IsTrusted (Jeremy Rubin)
Cache tx Trust per-call to avoid DoS (Jeremy Rubin)
Make IsTrusted scan parents recursively (Jeremy Rubin)

Pull request description:

This slightly modifies the behavior of IsTrusted to recursively check the parents of a transaction. Otherwise, it's possible that a parent is not IsTrusted but a child is. If a parent is not trusted, then a child should not be either.

This recursive scan can be a little expensive, so ~it might be beneficial to have a way of caching IsTrusted state, but this is a little complex because various conditions can change between calls to IsTrusted (e.g., re-org).~ I added a cache which works per call/across calls, but does not store the results semi-permanently. Which reduces DoS risk of this change. There is no risk of untrusted parents causing a resource exploitation, as we immediately return once that is detected.

This is a change that came up as a bug-fix esque change while working on OP_SECURETHEBAG. You can see the branch where this change is important here: https://github.com/bitcoin/bitcoin/compare/master...JeremyRubin:stb-with-rpc?expand=1. Essentially, without this change, we can be tricked into accepting an OP_SECURETHEBAG output because we don't properly check the parents. As this was a change which, on its own, was not dependent on OP_SECURETHEBAG, I broke it out as I felt the change stands on its own by fixing a long standing wallet bug.

The test wallet_balance.py has been corrected to meet the new behavior. The below comment, reproduced, explains what the issue is and the edge cases that can arise before this change.

        # Before `test_balance()`, we have had two nodes with a balance of 50
        # each and then we:
        #
        # 1) Sent 40 from node A to node B with fee 0.01
        # 2) Sent 60 from node B to node A with fee 0.01
        #
        # Then we check the balances:
        #
        # 1) As is
        # 2) With transaction 2 from above with 2x the fee
        #
        # Prior to #16766, in this situation, the node would immediately report
        # a balance of 30 on node B as unconfirmed and trusted.
        #
        # After #16766, we show that balance as unconfirmed.
        #
        # The balance is indeed "trusted" and "confirmed" insofar as removing
        # the mempool transactions would return at least that much money. But
        # the algorithm after #16766 marks it as unconfirmed because the 'taint'
        # tracking of transaction trust for summing balances doesn't consider
        # which inputs belong to a user. In this case, the change output in
        # question could be "destroyed" by replace the 1st transaction above.
        #
        # The post #16766 behavior is correct; we shouldn't be treating those
        # funds as confirmed. If you want to rely on that specific UTXO existing
        # which has given you that balance, you cannot, as a third party
        # spending the other input would destroy that unconfirmed.
        #
        # For example, if the test transactions were:
        #
        # 1) Sent 40 from node A to node B with fee 0.01
        # 2) Sent 10 from node B to node A with fee 0.01
        #
        # Then our node would report a confirmed balance of 40 + 50 - 10 = 80
        # BTC, which is more than would be available if transaction 1 were
        # replaced.

The release notes have been updated to note the new behavior.

Backport of Core PR16766

Test Plan
ninja check check-functional

Diff Detail

Repository
rABC Bitcoin ABC
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

[Bot Message]
One or more PR numbers were detected in the summary.
Links to those PRs have been inserted into the summary for reference.

Snippet of first build failure:

rpc_help.py                             | ✓ Passed  | 1 s
rpc_invalidateblock.py                  | ✓ Passed  | 6 s
rpc_misc.py                             | ✓ Passed  | 1 s
rpc_named_arguments.py                  | ✓ Passed  | 1 s
rpc_net.py                              | ✓ Passed  | 1 s
rpc_preciousblock.py                    | ✓ Passed  | 1 s
rpc_psbt.py                             | ✓ Passed  | 18 s
rpc_scantxoutset.py                     | ✓ Passed  | 4 s
rpc_setban.py                           | ✓ Passed  | 2 s
rpc_signmessage.py                      | ✓ Passed  | 1 s
rpc_signrawtransaction.py               | ✓ Passed  | 1 s
rpc_txoutproof.py                       | ✓ Passed  | 2 s
rpc_uptime.py                           | ✓ Passed  | 1 s
rpc_users.py                            | ✓ Passed  | 2 s
rpc_whitelist.py                        | ✓ Passed  | 1 s
tool_wallet.py                          | ✓ Passed  | 3 s
wallet_abandonconflict.py               | ✓ Passed  | 5 s
wallet_address_types.py                 | ✓ Passed  | 12 s
wallet_avoidreuse.py                    | ✓ Passed  | 3 s
wallet_backup.py                        | ✓ Passed  | 38 s
wallet_basic.py                         | ✓ Passed  | 30 s
wallet_coinbase_category.py             | ✓ Passed  | 1 s
wallet_create_tx.py                     | ✓ Passed  | 6 s
wallet_createwallet.py                  | ✓ Passed  | 2 s
wallet_createwallet.py --usecli         | ✓ Passed  | 3 s
wallet_disable.py                       | ✓ Passed  | 1 s
wallet_dump.py                          | ✓ Passed  | 3 s
wallet_encryption.py                    | ✓ Passed  | 5 s
wallet_groups.py                        | ✓ Passed  | 12 s
wallet_hd.py                            | ✓ Passed  | 5 s
wallet_import_rescan.py                 | ✓ Passed  | 4 s
wallet_import_with_label.py             | ✓ Passed  | 1 s
wallet_importmulti.py                   | ✓ Passed  | 3 s
wallet_importprunedfunds.py             | ✓ Passed  | 2 s
wallet_keypool.py                       | ✓ Passed  | 2 s
wallet_keypool_topup.py                 | ✓ Passed  | 3 s
wallet_labels.py                        | ✓ Passed  | 1 s
wallet_listreceivedby.py                | ✓ Passed  | 24 s
wallet_listsinceblock.py                | ✓ Passed  | 3 s
wallet_listtransactions.py              | ✓ Passed  | 18 s
wallet_multiwallet.py                   | ✓ Passed  | 13 s
wallet_multiwallet.py --usecli          | ✓ Passed  | 13 s
wallet_reorgsrestore.py                 | ✓ Passed  | 3 s
wallet_resendwallettransactions.py      | ✓ Passed  | 24 s
wallet_txn_clone.py                     | ✓ Passed  | 2 s
wallet_txn_clone.py --mineblock         | ✓ Passed  | 3 s
wallet_txn_doublespend.py               | ✓ Passed  | 1 s
wallet_txn_doublespend.py --mineblock   | ✓ Passed  | 3 s
wallet_watchonly.py                     | ✓ Passed  | 1 s
wallet_watchonly.py --usecli            | ✓ Passed  | 1 s
wallet_zapwallettxes.py                 | ✓ Passed  | 3 s
wallet_balance.py                       | ✖ Failed  | 2 s

ALL                                     | ✖ Failed  | 726 s (accumulated) 
Runtime: 146 s

FAILED: test/CMakeFiles/check-functional-upgrade-activated 
cd /work/abc-ci-builds/build-diff/test && /usr/bin/cmake -E make_directory /work/abc-ci-builds/build-diff/test/junit && /usr/bin/cmake -E make_directory /work/abc-ci-builds/build-diff/test/log && /usr/bin/cmake -E env /usr/bin/python3.7 ./functional/test_runner.py "--testsuitename=Bitcoin ABC functional tests with the next upgrade activated" --junitoutput=/work/abc-ci-builds/build-diff/test/junit/functional_tests_with_the_next_upgrade_activated.xml --with-axionactivation
ninja: build stopped: cannot make progress due to previous errors.
Build build-diff failed with exit code 1

Each failure log is accessible here:
Bitcoin ABC functional tests: wallet_balance.py
Bitcoin ABC functional tests with the next upgrade activated: wallet_balance.py

majcosta published this revision for review.Oct 1 2020, 01:41
jasonbcox requested changes to this revision.Oct 1 2020, 01:44
jasonbcox added a subscriber: jasonbcox.
jasonbcox added inline comments.
doc/release-notes.md
12 ↗(On Diff #24093)

Reword this so it makes sense in ABC context. IMO we don't even need to reference this diff per se, but the nature of the change should be described enough so that users know what to do with it.

This revision now requires changes to proceed.Oct 1 2020, 01:44
This revision is now accepted and ready to land.Oct 1 2020, 02:05