Skip to content

Conversation

kallewoof
Copy link
Contributor

@kallewoof kallewoof commented Aug 24, 2021

const std::shared_ptr<CWallet> wallet = x;

means we can not do wallet = y, but we can totally do wallet->DestructiveOperation(), contrary to what that line looks like.

This PR

  • introduces a new convention: always use const shared pointers to CWallets (even when we mutate the pointed-to thing)
  • uses const shared_ptr<const CWallet> everywhere where wallets are not modified

In the future, this should preferably apply to all shared pointers, not limited to just CWallets.

Both of these serve the same purpose: to dispell the misconception that const shared_ptr<X> immutates X. It doesn't, and it's dangerous to leave this misconception as is, for obvious reasons.

@DrahtBot
Copy link
Contributor

DrahtBot commented Aug 24, 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:

  • #22928 (refactor: Remove gArgs from wallet.h and wallet.cpp (2) by kiminuo)
  • #22260 (Make bech32m the default for RPC, opt-in for GUI by Sjors)

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.

@jnewbery
Copy link
Contributor

jnewbery commented Aug 24, 2021

I'm a NACK on this. We use the (common) convention of putting const to the left of the thing that's const, except in the case of a const pointer to type (T* const) where the const needs to go to the right of the * to indicate that it's a const pointer rather than a pointer to const. Changing the style just in this case would be inconsistent and confusing.

Hopefully now that this has bitten you once, you'll not get tripped up by it again. FWIW, I think the notation for smart pointers:

  • const std::shared_ptr<T> "constant shared pointer to mutable T"
  • std::shared_ptr<const T> "mutable shared pointer to constant T"

is a bit easier to read than for raw pointers:

  • const T* "mutable pointer to constant T"
  • T* const "constant pointer to mutable T"

This is also similar for STL containers:

  • const std::vector<T> - constant vector of Ts
  • std::vector<const T> - mutable vector of constant Ts

@kallewoof
Copy link
Contributor Author

kallewoof commented Aug 24, 2021

I'm a NACK on this. We use the (common) convention of putting const to the left of the thing that's const, except in the case of a const pointer to type (T* const) where the const needs to go to the right of the * to indicate that it's a const pointer rather than a pointer to const. Changing the style just in this case would be inconsistent and confusing.

I can easily move the const to the left-hand side. We are definitely not consistent with putting it on the left, as you can see all the cases of wallet const shared pointers right now (search for std::shared_ptr<CWallet> const in the code base for an example).

That's not what my PR is about though: it's about putting const inside the shared pointer, which is a feature that is supported, and that makes the thing inside it immutable, which is what you expect when you see the const sign. You do not expect to be able to call destructive methods or delete things inside a wallet that is marked const, which you can do with the above shared pointer.

Hopefully now that this has bitten you once, you'll not get tripped up by it again.

It is definitely not the first time it's bitten me, personally, but maybe I'm just slow. :)

Edit: it bites me often because I keep thinking of a const std::shared_ptr<X> as having a const X inside of it, due to it being const, but in reality it's simply a shared pointer that cannot be e.g. .reset().

Copy link
Contributor

@jnewbery jnewbery left a comment

Choose a reason for hiding this comment

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

That's not what my PR is about though: it's about putting const inside the shared pointer, which is a feature that is supported, and that makes the thing inside it immutable, which is what you expect when you see the const sign.

Ah, sorry - I misunderstood. I have no objection to making const things const, although I have a strong preference for following the convention of placing const on the left side of the type.

std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings) override
{
std::shared_ptr<CWallet> wallet;
std::shared_ptr<CWallet> const wallet;
Copy link
Contributor

Choose a reason for hiding this comment

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

Declaring a local const std::shared_ptr<T> const_ptr; is pointless. It can't be set to anything after this declaration since it's const.

In fact, this wallet variable isn't used, so I think it can just be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this one just sorta tagged along. I have no idea why it is there, so I didn't touch it beyond letting it tag along with the convention change.

@kallewoof
Copy link
Contributor Author

I have a strong preference for following the convention of placing const on the left side of the type.

I'm totally fine with putting const to the left. However, that's definitely not what is happening in the code, to the point where I ended up putting it on the right here to stay with the code style, despite having no preference about it myself.

@kallewoof
Copy link
Contributor Author

Switched to left-hand-const in all touched places.

@kallewoof kallewoof force-pushed the 202108-const-shared-ptrs branch from 247e7a3 to cf89cbe Compare August 25, 2021 07:43
@meshcollider
Copy link
Contributor

Concept ACK

@theStack
Copy link
Contributor

Concept ACK

Const-correctness is important; to quote Herb Sutter on this subject:

Safety-incorrect riflemen are not long for this world. Nor are const-incorrect programmers, carpenters who don't have time for
hard-hats, and electricians who don't have time to identify the live wire. There is no excuse for ignoring the safety mechanisms
provided with a product, and there is particularly no excuse for programmers too lazy to write const-correct code.

(source: "Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions")

@lsilva01
Copy link
Contributor

Concept ACK

@kiminuo
Copy link
Contributor

kiminuo commented Aug 26, 2021

Concept ACK

(For historians, the discussion started here: https://github.com/bitcoin/bitcoin/pull/22751/files#r693517224)

Edit: Maybe some of these can be converted too:

@kallewoof
Copy link
Contributor Author

@kiminuo I'm limiting the scope on this PR to only shared pointers to CWallets to avoid a too big diff.

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.

Code-review ACK 6b98ce3 🍶

Based on this PR, I opened a follow-up #22805 which also uses CWallet const shared pointers on the dumpprivkey and dumpwallet RPC methods.

Copy link
Contributor

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

6b98ce3 could just change to std::shared_ptr<const T> though.

@kallewoof
Copy link
Contributor Author

kallewoof commented Aug 30, 2021

could just change to std::shared_ptr<const T> though.

I'm not sure I follow. Are you suggesting replacing const std::shared_ptr<const CWallet> with const std::shared_ptr<const T>? Where does T come from?

@bitcoin bitcoin deleted a comment from jaysonmald35 Aug 30, 2021
@bitcoin bitcoin deleted a comment from jaysonmald35 Aug 30, 2021
promag referenced this pull request Sep 2, 2021
While const shared_ptr<X> gives us an immutable shared pointer to a mutable X (we can't set it to some other X later), shared_ptr<const X> gives us a shared pointer to an immutable X. Importantly, we can recast shared_ptr<X> into shared_ptr<const X>, but not the other way around. We do this for two reasons: because it makes the code safer to guarantee the wallet is not modified, and because it further dispells the misconception that const shared_ptr<X> gives immutability to X.
@kallewoof
Copy link
Contributor Author

@promag That makes sense to me, but it isn't clear to the reviewer why some lines are left as ... const ... and some are changed to const ... ....

@practicalswift
Copy link
Contributor

Concept ACK

@kiminuo
Copy link
Contributor

kiminuo commented Sep 19, 2021

I'm limiting the scope on this PR to only shared pointers to CWallets to avoid a too big diff.

So should the lines like the following ones be fixed too? I mean to move const from the right side to the left side.

src\wallet\rpcdump.cpp:122:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcdump.cpp:213:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcdump.cpp:251:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcdump.cpp:339:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcdump.cpp:400:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcdump.cpp:447:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcdump.cpp:528:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcdump.cpp:684:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcdump.cpp:734:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcdump.cpp:1346:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(mainRequest);
src\wallet\rpcdump.cpp:1661:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(main_request);
src\wallet\rpcwallet.cpp:255:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:308:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:354:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:491:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:915:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:988:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:1786:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:1859:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:1912:    std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:2000:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:2053:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:2097:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:2170:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:2324:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:2586:    std::shared_ptr<CWallet> const wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings);
src\wallet\rpcwallet.cpp:2673:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:3356:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:3539:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:3678:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:4203:            std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:4330:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:4527:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:4607:    std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
src\wallet\rpcwallet.cpp:4664:            std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);

Produced by:

Get-ChildItem -Recurse *.* | Select-String "std::shared_ptr<CWallet>"  | Select-String "const std::shared_ptr<CWallet>" -notMatch

@kallewoof
Copy link
Contributor Author

So should the lines like the following ones be fixed too? I mean to move const from the right side to the left side.

It's tempting. Right now I'm only changing the lines that I'm touching for other reasons. If others agree, I can ramp it up to include all cases.

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.

re-ACK 52a26e0

Verified via git range-diff 6b98ce32...52a26e08 that changes since my previous ACK were only rebase-related.

Introduce convention to use const shared pointers everywhere, unless the shared pointer is modified at some point, which it very rarely is.

We want this convention, as it helps alleviate the misconception that a const shared pointer somehow results in a pointer to an immutable object, which is false.
While const shared_ptr<X> gives us an immutable shared pointer to a mutable X (we can't set it to some other X later), shared_ptr<const X> gives us a shared pointer to an immutable X. Importantly, we can recast shared_ptr<X> into shared_ptr<const X>, but not the other way around. We do this for two reasons: because it makes the code safer to guarantee the wallet is not modified, and because it further dispells the misconception that const shared_ptr<X> gives immutability to X.
@kallewoof kallewoof force-pushed the 202108-const-shared-ptrs branch from 52a26e0 to 54011e7 Compare October 25, 2021 07:13
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.

re-ACK 54011e7

Verified via git range-diff 52a26e08...54011e7a that changes since my previous ACK were only rebase-related.

@maflcko maflcko merged commit baa9fc9 into bitcoin:master Oct 29, 2021
@kallewoof kallewoof deleted the 202108-const-shared-ptrs branch October 29, 2021 12:25
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Oct 29, 2021
54011e7 refactor: use CWallet const shared pointers when possible (Karl-Johan Alm)
9646198 refactor: const shared_ptrs (Karl-Johan Alm)

Pull request description:

  ```C++
  const std::shared_ptr<CWallet> wallet = x;
  ```
  means we can not do `wallet = y`, but we can totally do `wallet->DestructiveOperation()`, contrary to what that line looks like.

  This PR

  * introduces a new convention: always use const shared pointers to `CWallet`s (even when we mutate the pointed-to thing)
  * uses `const shared_ptr<const CWallet>` everywhere where wallets are not modified

  In the future, this should preferably apply to all shared pointers, not limited to just `CWallet`s.

  Both of these serve the same purpose: to dispell the misconception that `const shared_ptr<X>` immutates `X`. It doesn't, and it's dangerous to leave this misconception as is, for obvious reasons.

ACKs for top commit:
  theStack:
    re-ACK 54011e7

Tree-SHA512: 3bf4062fc821751be30770c6b4ead10a016847970f155a0a5156f304347d221b9830840030c2fbfba8cd1e282f4eda45f5b4107fe6df8138afdcb6c2e95a2836
laanwj added a commit that referenced this pull request Nov 10, 2021
…vkey,wallet}

d150fe3 refactor: use `CWallet` const shared pointers in dump{privkey,wallet} RPCs (Sebastian Falbesoner)
ec2792d refactor: use const `LegacyScriptPubKeyMan` references in dump{privkey,wallet} RPCs (Sebastian Falbesoner)
29905c0 refactor: avoid multiple key->metadata lookups in dumpwallet RPC (Sebastian Falbesoner)

Pull request description:

  ~~This PR is based on #22787 ("refactor: actual immutable pointing"), which should be reviewed first.~~ (merged by now)

  It aims to make the CWallet shared pointers actually immutable also for the `dumpprivkey` and `dumpwallet` RPC methods. For doing that, some more preparations are needed; we need a const-counterpart to the helper `EnsureLegacyScriptPubKeyMan` that accepts a const CWallet pointer and accordingly also returns a const `LegacyScriptPubKeyMan` instance. The metadata lookup in `dumpwallet` is changed to not need a mutable `ScriptPubKeyMan` instance by avoiding using the `operator[]` in its mapKeyMetadata map, which also avoids repeated lookups.

ACKs for top commit:
  laanwj:
    Code review ACK d150fe3

Tree-SHA512: 90ac05e21f40c6d0ebb479a71c545da2fd89940b9ca3409d9f932abc983bf8830d34c45086e68880d0d1e994846fbefee7534eec142ff4268e0fa28a1a411d36
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Nov 10, 2021
…ump{privkey,wallet}

d150fe3 refactor: use `CWallet` const shared pointers in dump{privkey,wallet} RPCs (Sebastian Falbesoner)
ec2792d refactor: use const `LegacyScriptPubKeyMan` references in dump{privkey,wallet} RPCs (Sebastian Falbesoner)
29905c0 refactor: avoid multiple key->metadata lookups in dumpwallet RPC (Sebastian Falbesoner)

Pull request description:

  ~~This PR is based on bitcoin#22787 ("refactor: actual immutable pointing"), which should be reviewed first.~~ (merged by now)

  It aims to make the CWallet shared pointers actually immutable also for the `dumpprivkey` and `dumpwallet` RPC methods. For doing that, some more preparations are needed; we need a const-counterpart to the helper `EnsureLegacyScriptPubKeyMan` that accepts a const CWallet pointer and accordingly also returns a const `LegacyScriptPubKeyMan` instance. The metadata lookup in `dumpwallet` is changed to not need a mutable `ScriptPubKeyMan` instance by avoiding using the `operator[]` in its mapKeyMetadata map, which also avoids repeated lookups.

ACKs for top commit:
  laanwj:
    Code review ACK d150fe3

Tree-SHA512: 90ac05e21f40c6d0ebb479a71c545da2fd89940b9ca3409d9f932abc983bf8830d34c45086e68880d0d1e994846fbefee7534eec142ff4268e0fa28a1a411d36
@bitcoin bitcoin locked and limited conversation to collaborators Oct 30, 2022
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.