Skip to content

Conversation

furszy
Copy link
Member

@furszy furszy commented Jun 11, 2022

Context

The wallet's m_address_book field is being accessed directly from several places across the sources.

Problem

Code structure wise, we shouldn't be accessing it directly. It could end up being modified by mistake (from a place that has nothing to do with the wallet like an RPC command or the GUI) and cause a bigger issue: like an address book entry 'purpose' string change, which if done badly (from 'send' to 'receive'), could end up in a user sharing a "receive" address that he/she doesn't own.

Solution

Encapsulate m_address_book access inside the wallet.


Extra Note:

This is the initial step towards decoupling the address book functionality from the wallet's sources. In other words, the creation of the AddressBookManager (which will be coming in a follow-up PR).

@fanquake
Copy link
Member

https://github.com/bitcoin/bitcoin/pull/25337/checks?check_run_id=6844212884:

  CXX      wallet/rpc/libbitcoin_wallet_a-coins.o
wallet/interfaces.cpp:213:49: error: calling function 'IsMine' requires holding mutex 'm_wallet->cs_wallet' exclusively [-Werror,-Wthread-safety-analysis]
            result.emplace_back(dest, m_wallet->IsMine(dest), label, purpose);
                                                ^

@furszy
Copy link
Member Author

furszy commented Jun 13, 2022

Thanks @fanquake 👍🏼.

Strangely, cs_wallet is actually locked there.. but well, It should be fine now, ended up cleaning a good number of external cs_wallet locks with 61b4654.

Copy link
Member

@hebasto hebasto left a comment

Choose a reason for hiding this comment

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

Wrt thread-safety annotations, I'd suggest to:

  • rebase this PR to include #24931
  • ensure that every commit does not introduce new -Wthread-safety warnings when compiling with clang
  • in the "wallet: implement ForEachAddrBookEntry method" commit (ead512a) apply the following patch:
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -653,7 +653,8 @@ public:
      * Stops when the provided 'ListAddrBookFunc' returns false.
      */
     using ListAddrBookFunc = std::function<void(const CTxDestination& dest, const std::string& label, const std::string& purpose, bool is_change)>;
-    void ForEachAddrBookEntry(const ListAddrBookFunc& func = [](const CTxDestination&, const std::string&, const std::string&, bool is_change){ return; }) const;
+    void ForEachAddrBookEntry(const ListAddrBookFunc& func = [](const CTxDestination&, const std::string&, const std::string&, bool is_change){ return; }) const
+        EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
 
     /**
      * Marks all outputs in each one of the destinations dirty, so their cache is
  • in the "refactor: use ForEachAddrBookEntry in interfaces::wallet::getAddresses" commit (c629bef) apply the following patch:
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -208,7 +208,7 @@ public:
     {
         LOCK(m_wallet->cs_wallet);
         std::vector<WalletAddress> result;
-        m_wallet->ForEachAddrBookEntry([&](const CTxDestination& dest, const std::string& label, const std::string& purpose, bool is_change) {
+        m_wallet->ForEachAddrBookEntry([&](const CTxDestination& dest, const std::string& label, const std::string& purpose, bool is_change) EXCLUSIVE_LOCKS_REQUIRED(m_wallet->cs_wallet) {
             if (is_change) return;
             result.emplace_back(dest, m_wallet->IsMine(dest), label, purpose);
         });
  • and maybe skip the last commit?

@furszy furszy force-pushed the 2022_encapsulate_addressbook_access branch from 61b4654 to 005d111 Compare June 14, 2022 14:59
@furszy
Copy link
Member Author

furszy commented Jun 14, 2022

Thanks for the review hebasto!

Great suggestions 👌🏼 , have applied them all.

ensure that every commit does not introduce new -Wthread-safety warnings when compiling with clang

Ensured. Have compiled every commit with --enable-debug`.

and maybe skip the last commit?

Done. Agree that it's not related to the scope of this work. Will push it in another PR.

@furszy furszy force-pushed the 2022_encapsulate_addressbook_access branch 2 times, most recently from 3648db9 to 21c6342 Compare June 14, 2022 21:09
@furszy
Copy link
Member Author

furszy commented Jun 14, 2022

Thanks for the review achow101

Updated per feedback. Removed ForEachAddrBookEntry default parameter.

@achow101
Copy link
Member

ACK 21c6342

Copy link
Contributor

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

@DrahtBot
Copy link
Contributor

DrahtBot commented Jun 21, 2022

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

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #25297 (WIP, wallet: speedup transactions sync, rescan and load not flushing to db constantly by furszy)

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.

@furszy furszy force-pushed the 2022_encapsulate_addressbook_access branch from 21c6342 to 4be2c0d Compare June 21, 2022 13:24
@furszy
Copy link
Member Author

furszy commented Jun 21, 2022

Thanks for the feedback!

Only modification is a variable rename filter_change -> ignore_change.

Copy link
Contributor

@theStack theStack left a comment

Choose a reason for hiding this comment

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

Almost-ACK, changes look good to me. Just two suggestions below, one nit, one potential behaviour change.

@furszy furszy force-pushed the 2022_encapsulate_addressbook_access branch from 4be2c0d to 2e94d08 Compare June 22, 2022 15:49
@furszy furszy force-pushed the 2022_encapsulate_addressbook_access branch from 2e94d08 to d69045e Compare June 22, 2022 15:51
@furszy
Copy link
Member Author

furszy commented Jun 22, 2022

Thanks! feedback tackled, changes:

  1. Added missing "change" addrs skip in ListReceived (Squashed inside b459fc1).
  2. Added test coverage for point (1) --> verifying that no "change" address is returned by listreceivedbyaddress (d69045e)

Copy link
Contributor

@theStack theStack left a comment

Choose a reason for hiding this comment

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

ACK d69045e

@achow101
Copy link
Member

achow101 commented Jul 6, 2022

ACK d69045e

Copy link
Contributor

@w0xlt w0xlt left a comment

Choose a reason for hiding this comment

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

ACK d69045e

It's an improvement in code organization, as ideally only CWallet would be aware of m_address_book.
This field is still called in walletdb.cpp and wallettool.cpp, but may eventually be handled in another PR.

@achow101 achow101 merged commit b9f9ed4 into bitcoin:master Jul 8, 2022
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jul 8, 2022
d69045e test: add coverage for 'listreceivedbyaddress' no change addrs return (furszy)
324f00a refactor: 'ListReceived' use optional for filtered address (furszy)
b459fc1 refactor: RPC 'ListReceived', encapsulate m_address_book access (furszy)
fa9f2ab refactor: RPC 'listlabels', encapsulate 'CWallet::ListAddrBookLabels' functionality (furszy)
83e42c4 refactor: use 'ForEachAddrBookEntry' in RPC 'getaddressesbylabel' (furszy)
2b48642 refactor: use ForEachAddrBookEntry in interfaces::getAddresses (furszy)
032842a wallet: implement ForEachAddrBookEntry method (furszy)
09649bc refactor: implement general 'ListAddrBookAddresses' for addressbook destinations lookup (furszy)
192eb1e refactor: getAddress don't access m_address_book, use FindAddressEntry function (furszy)

Pull request description:

  ### Context

  The wallet's `m_address_book` field is being accessed directly from several places across the sources.

  ### Problem

  Code structure wise, we shouldn't be accessing it directly. It could end up being modified by mistake (from a place that has nothing to do with the wallet like an RPC command or the GUI) and cause a bigger issue: like an address book entry 'purpose' string change, which if done badly (from 'send' to 'receive'), could end up in a user sharing a "receive" address that he/she doesn't own.

  ### Solution

  Encapsulate `m_address_book` access inside the wallet.

  -------------------------------------------------------

  Extra Note:

  This is the initial step towards decoupling the address book functionality from the wallet's sources. In other words, the creation of the `AddressBookManager` (which will be coming in a follow-up PR).

ACKs for top commit:
  achow101:
    ACK d69045e
  theStack:
    ACK d69045e ✅
  w0xlt:
    ACK bitcoin@d69045e

Tree-SHA512: dba17acd86f171b4e9af0223bbbcad380048570f6a2f6a92732a51f01abe8806debaf65c9e9e5569fa76a541903cbb50adcb5f56ef77858151c698ae6b218e2a
@@ -114,7 +114,7 @@ class Wallet
std::string* purpose) = 0;

//! Get wallet address list.
virtual std::vector<WalletAddress> getAddresses() = 0;
virtual std::vector<WalletAddress> getAddresses() const = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Just for future reference, virtual src/interfaces/ methods should not be const since it breaks multiprocess code in #10102, which adds alternate implementation of these interfaces which do IPC, and don't wrap local CWallets.

In general virtual const methods don't make sense unless you can guarantee that every override of the virtual method can const, which is not something you normally know.

Copy link
Member Author

Choose a reason for hiding this comment

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

k 👍🏼, got it.

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.

8 participants