Skip to content

Conversation

ryanofsky
Copy link
Contributor

@ryanofsky ryanofsky commented Dec 19, 2016

This is a pure refactoring, no behavior is changing.

Instead of AddToWallet taking a temporary CWalletTx object and then potentially merging it with a pre-existing CWalletTx, have it take a callback so callers can update the pre-existing CWalletTx directly.

This makes AddToWallet simpler because now it is only has to be concerned with saving CWalletTx objects and not merging them.

This makes AddToWallet calls clearer because they can now make direct updates to CWalletTx entries without having to make temporary objects and then worry about how they will be merged.

Motivation for this change came from the bumpfee PR #8456 where we wanted to be able to call AddToWallet to make a simple update to an existing transaction, but were reluctant to, because the existing CWalletTx merging logic did not apply and seemed dangerous try to update as part of that PR. After this refactoring, the bumpfee PR could call AddToWallet safely instead of implementing a duplicate AddToWallet function.

This also allows getting rid of the CWalletTx copy constructor to prevent unintentional copying.

@ryanofsky
Copy link
Contributor Author

Rebased ed457a3 -> b1ac3cf to resolve merge conflicts with bumpfee (#8456).

@ryanofsky
Copy link
Contributor Author

I'm thinking of splitting this up into two commits to make it easier to review. First commit would change CreateTransaction to return a CTransactionRef instead of CWalletTx. Second commit would change AddToWallet to accept a CTransactionRef instead of a CWalletTx.

If any reviewers would prefer this you can let me know.

@TheBlueMatt
Copy link
Contributor

TheBlueMatt commented Jan 25, 2017 via email

@TheBlueMatt
Copy link
Contributor

Hum, reading this (finally) now, I'm not sure if I'm a big fan of this approach. The call-function-which-callsback-to-let-you-fill-in-things-that-really-should-have-been-arguments pattern really sucks. On the flip side, passing in CWalletTxes, and copying those into mapWallet can also lead to issues if you try to do anything to the object you just passed in thinking it is what got stored in the wallet (a mistake I made recently).

Maybe we should adapt some of these functions (AddToWallet/CommitTransaction/whatever) to just always take an rvalue to a CWalletTx. This leaves only AddToWalletIfInvolvingMe (I think) which has to update-or-add, and that can just use an internal version of AddToWallet which takes the CWalletDB as an argument instead of creating its own.

ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Feb 2, 2017
Preserves comment strings, order form and account strings from the original
wallet transaction.

Also sets fTimeReceivedIsTxTime and fFromMe fields (which are set in
CWallet::CreateTransaction) which don't actually influence wallet behavior, but
record that the transaction originated in the wallet (instead of coming from
the network or sendrawtransaction). Setting these fields also simplifies
CWalletTx cleanup in bitcoin#9381.
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Feb 2, 2017
Preserves comment strings, order form and account strings from the original
wallet transaction.

Also sets fTimeReceivedIsTxTime and fFromMe fields (which are set in
CWallet::CreateTransaction) which don't actually influence wallet behavior, but
record that the transaction originated in the wallet (instead of coming from
the network or sendrawtransaction). Setting these fields also simplifies
CWalletTx cleanup in bitcoin#9381.
@ryanofsky ryanofsky changed the title Remove CWalletTx merging logic from AddToWallet Remove CWalletTx merging logic from AddToWallet (on top of #9673) Feb 2, 2017
@ryanofsky
Copy link
Contributor Author

The call-function-which-callsback-to-let-you-fill-in-things-that-really-should-have-been-arguments pattern really sucks.

If you are adamant about this, could you say more about why it sucks? I think a callback is exactly the thing you want when you are doing an in-place update to a data structure.

In any case, I agree that CommitTransaction shouldn't take a callback, so I changed it to just take a transaction ref. So now there are way fewer callbacks (I think only 3 left in non-test code).

@TheBlueMatt
Copy link
Contributor

I'm just generally not a fan of callbacks making ownership models inconsistent. eg this kind of thing is where people always fuck up lockordering (though admittedly less so in this particular case, more the general case of callbacks going in both directions between modules).

So I do like this version much better, but still not sure the call back is required to be publicly exposed...Can we just leave MarkReplaced and add a similar function for importpruned funds to use (I dont believe the double-disk-sync in importprunedfunds will make for inconsistent/bad on-disk state?). Then we can make AddToWallet(..., callback) private and use AddToWalletIfInvolvingMe (which should probably also take a CTransactionRef, not a CTransaction, though maybe thats for a separate PR) publicly.

@ryanofsky ryanofsky changed the title Remove CWalletTx merging logic from AddToWallet (on top of #9673) Remove CWalletTx merging logic from AddToWallet (on top of #9680) Feb 3, 2017
@ryanofsky
Copy link
Contributor Author

Can we just leave MarkReplaced and add a similar function for importpruned funds to use

Will try that. In the meantime I moved the non-AddToWallet cleanup to #9680 so it can be considered separately. By the way #9369 is another PR which significantly simplifies AddToWallet.

@ryanofsky ryanofsky changed the title Remove CWalletTx merging logic from AddToWallet (on top of #9680) WIP: Remove CWalletTx merging logic from AddToWallet (on top of #9680) Feb 6, 2017
@ryanofsky ryanofsky force-pushed the pr/atw-nomerge branch 2 times, most recently from 777c297 to 4a20b3c Compare August 16, 2017 17:36
CWalletTx initialization has been fixed so it's no longer necessary to change
which wallet a transaction is bound to.
@ryanofsky
Copy link
Contributor Author

Rebased ece5d1c -> f2892d8 (pr/atw-nomerge.78 -> pr/atw-nomerge.79, compare) due to conflict with #16426

@Sjors
Copy link
Member

Sjors commented May 4, 2020

Light re-utACK f2892d8, looks like a reasonably straight-forward rebase.

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 f2892d8

Copy link
Member

@maflcko maflcko left a comment

Choose a reason for hiding this comment

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

ACK f2892d8

Just some questions

CWalletTx* wtx = AddToWallet(std::move(tx), {}, [&](CWalletTx& wtx, bool new_tx) {
CHECK_NONFATAL(wtx.mapValue.empty());
CHECK_NONFATAL(wtx.vOrderForm.empty());
wtx.mapValue = std::move(mapValue);
Copy link
Member

Choose a reason for hiding this comment

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

Does the std::move still work after going through a lambda capture by reference?

Copy link
Member

Choose a reason for hiding this comment

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

Yes. mapValue is a reference to the same thing it was outside of the lambda. std::move casts it to an rvalue reference.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, this has nothing to do with lambdas. I didn't know that references (as long as they are not const) can be moved.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

re: #9381 (comment)

Oh, this has nothing to do with lambdas. I didn't know that references (as long as they are not const) can be moved.

Yep, std::move isn't affected by outside things like this. std::move(mapValue) is only a type cast from mapValue_t& to mapValue_t&& that makes the compiler favor the operator=(mapValue_t&&) assignment overload instead of the operator=(const mapValue_t&) one for wtx.mapValue =

Copy link
Member

Choose a reason for hiding this comment

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

Even const ones, actually - though unless they have mutable fields, there is no effect.

Copy link
Member

Choose a reason for hiding this comment

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

With "no effect" you mean const& will stay const&, right? See also the example, which does not compile, because the const& copy constructor is deleted:

#include <memory>

struct Test{
    const std::unique_ptr<int>b;
    Test(const std::unique_ptr<int>& a):b{std::move(a)}{}
};

Copy link
Member

@sipa sipa May 5, 2020

Choose a reason for hiding this comment

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

std::move(a) where a is const T&, returns something of type const T&&. If T has for example a operator=(const T&&), then that one would be selected in assignment. If there is no such operator, operator=(const T&) will be selected instead. In general a const T&& assignment/constructor operator only makes sense if a class has mutable fields.

Copy link
Member

Choose a reason for hiding this comment

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

TIL that const T&& exists.


// Notify that old coins are spent
for (const CTxIn& txin : wtxNew.tx->vin) {
for (const CTxIn& txin : wtx->tx->vin) {
Copy link
Member

@maflcko maflcko May 5, 2020

Choose a reason for hiding this comment

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

Doesn't this line crash the node when the wallet can not write the tx to disk?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

re: #9381 (comment)

Doesn't this line crash the node when the wallet can not write the tx to disk?

Wow, good catch! I'm not sure current behavior is ideal either but it wasn't my intention to change it. Updated PR

Copy link
Contributor Author

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Updated f2892d8 -> 28b112e (pr/atw-nomerge.79 -> pr/atw-nomerge.80, compare) reverting some CommitTransaction changes to avoid changing behavior and crashing on failure


// Notify that old coins are spent
for (const CTxIn& txin : wtxNew.tx->vin) {
for (const CTxIn& txin : wtx->tx->vin) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

re: #9381 (comment)

Doesn't this line crash the node when the wallet can not write the tx to disk?

Wow, good catch! I'm not sure current behavior is ideal either but it wasn't my intention to change it. Updated PR

CWalletTx* wtx = AddToWallet(std::move(tx), {}, [&](CWalletTx& wtx, bool new_tx) {
CHECK_NONFATAL(wtx.mapValue.empty());
CHECK_NONFATAL(wtx.vOrderForm.empty());
wtx.mapValue = std::move(mapValue);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

re: #9381 (comment)

Oh, this has nothing to do with lambdas. I didn't know that references (as long as they are not const) can be moved.

Yep, std::move isn't affected by outside things like this. std::move(mapValue) is only a type cast from mapValue_t& to mapValue_t&& that makes the compiler favor the operator=(mapValue_t&&) assignment overload instead of the operator=(const mapValue_t&) one for wtx.mapValue =

@maflcko
Copy link
Member

maflcko commented May 5, 2020

Ah sorry, no need to revert your changes. If the wallet can't write to disk, a simple CHECK_NONFATAL(wtx) should be sufficient

@ryanofsky
Copy link
Contributor Author

Ah sorry, no need to revert your changes. If the wallet can't write to disk, a simple CHECK_NONFATAL(wtx) should be sufficient

I don't think CHECK_NONFATAL would be right, because this is checking a runtime error, not an internal assertion about how code works. I can go back to the earlier version and add this if that's your preference. But actually I like the update better because it makes the overall diff smaller and avoids changing behavior in a refactoring PR. I think ideally if different error handling is needed here, it would be handled in a standalone PR with a unit test.

@maflcko
Copy link
Member

maflcko commented May 5, 2020

Well, our assumption in the code is that the wallet can write to disk. If that assumption is violated it seems fine to assert or do that CHECK thingy. But I also see your point to make it a separate runtime error.

Anyway, re-ACK 28b112e

@meshcollider
Copy link
Contributor

re-utACK 28b112e

@meshcollider meshcollider merged commit 60091d2 into bitcoin:master May 5, 2020
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request May 12, 2020
28b112e Get rid of BindWallet (Russell Yanofsky)
d002f9d Disable CWalletTx copy constructor (Russell Yanofsky)
65b9d8f Avoid copying CWalletTx in LoadToWallet (Russell Yanofsky)
bd2fbc7 Get rid of unneeded CWalletTx::Init parameter (Russell Yanofsky)
2b9cba2 Remove CWalletTx merging logic from AddToWallet (Russell Yanofsky)

Pull request description:

  This is a pure refactoring, no behavior is changing.

  Instead of AddToWallet taking a temporary CWalletTx object and then potentially merging it with a pre-existing CWalletTx, have it take a callback so callers can update the pre-existing CWalletTx directly.

  This makes AddToWallet simpler because now it is only has to be concerned with saving CWalletTx objects and not merging them.

  This makes AddToWallet calls clearer because they can now make direct updates to CWalletTx entries without having to make temporary objects and then worry about how they will be merged.

  Motivation for this change came from the bumpfee PR bitcoin#8456 where we wanted to be able to call AddToWallet to make a simple update to an existing transaction, but were reluctant to, because the existing CWalletTx merging logic did not apply and seemed dangerous try to update as part of that PR. After this refactoring, the bumpfee PR could call AddToWallet safely instead of implementing a duplicate AddToWallet function.

  This also allows getting rid of the CWalletTx copy constructor to prevent unintentional copying.

ACKs for top commit:
  MarcoFalke:
    Anyway, re-ACK 28b112e

Tree-SHA512: 528dd088714472a237500b200f4433db850bdb7fc29c5e5d81cae48072061dfb967f7c37edd90b33f24901239f9be982988547c1f8c80abc25fb243fbf7330ef
xdustinface pushed a commit to xdustinface/dash that referenced this pull request Aug 12, 2020
Construct CWalletTx objects in CWallet::CommitTransaction, instead of having
callers do it. This ensures CWalletTx objects are constructed in a uniform way
and all fields are set.

This also makes it possible to avoid confusing and wasteful CWalletTx copies in
bitcoin#9381

There is no change in behavior.
xdustinface pushed a commit to xdustinface/dash that referenced this pull request Aug 12, 2020
Construct CWalletTx objects in CWallet::CommitTransaction, instead of having
callers do it. This ensures CWalletTx objects are constructed in a uniform way
and all fields are set.

This also makes it possible to avoid confusing and wasteful CWalletTx copies in
bitcoin#9381

There is no change in behavior.
xdustinface pushed a commit to xdustinface/dash that referenced this pull request Aug 27, 2020
Construct CWalletTx objects in CWallet::CommitTransaction, instead of having
callers do it. This ensures CWalletTx objects are constructed in a uniform way
and all fields are set.

This also makes it possible to avoid confusing and wasteful CWalletTx copies in
bitcoin#9381

There is no change in behavior.
xdustinface pushed a commit to xdustinface/dash that referenced this pull request Aug 28, 2020
Construct CWalletTx objects in CWallet::CommitTransaction, instead of having
callers do it. This ensures CWalletTx objects are constructed in a uniform way
and all fields are set.

This also makes it possible to avoid confusing and wasteful CWalletTx copies in
bitcoin#9381

There is no change in behavior.
xdustinface pushed a commit to xdustinface/dash that referenced this pull request Aug 29, 2020
Construct CWalletTx objects in CWallet::CommitTransaction, instead of having
callers do it. This ensures CWalletTx objects are constructed in a uniform way
and all fields are set.

This also makes it possible to avoid confusing and wasteful CWalletTx copies in
bitcoin#9381

There is no change in behavior.
xdustinface pushed a commit to xdustinface/dash that referenced this pull request Sep 1, 2020
Construct CWalletTx objects in CWallet::CommitTransaction, instead of having
callers do it. This ensures CWalletTx objects are constructed in a uniform way
and all fields are set.

This also makes it possible to avoid confusing and wasteful CWalletTx copies in
bitcoin#9381

There is no change in behavior.
UdjinM6 added a commit to dashpay/dash that referenced this pull request Sep 21, 2020
* [wallet] Construct CWalletTx objects in CommitTransaction

Construct CWalletTx objects in CWallet::CommitTransaction, instead of having
callers do it. This ensures CWalletTx objects are constructed in a uniform way
and all fields are set.

This also makes it possible to avoid confusing and wasteful CWalletTx copies in
bitcoin#9381

There is no change in behavior.

* [wallet] Get rid of CWalletTx default constructor

No change in behavior in the normal case. But buggy mapWallet lookups with
invalid txids will now throw exceptions instead of inserting dummy entries into
the map, and potentially causing segfaults and other failures.

This also makes it a compiler error to use the mapWallet[hash] syntax which
could create dummy entries.

* Apply suggestions from code review

Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>

Co-authored-by: Russell Yanofsky <russ@yanofsky.org>
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 27, 2021
Summary:
> Instead of AddToWallet taking a temporary CWalletTx object and then potentially
> merging it with a pre-existing CWalletTx, have it take a callback so callers
> can update the pre-existing CWalletTx directly.
>
> This makes AddToWallet simpler because now it is only has to be concerned with
> saving CWalletTx objects and not merging them.
>
> This makes AddToWallet calls clearer because they can now make direct updates to
> CWalletTx entries without having to make temporary objects and then worry about
> how they will be merged.
>
> This is a pure refactoring, no behavior is changing.

This is a backport of Core [[bitcoin/bitcoin#9381 | PR9381]] [1/5]
bitcoin/bitcoin@2b9cba2

Test Plan: `ninja all check-all`

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D9074
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 27, 2021
Summary:
This is a backport of Core [[bitcoin/bitcoin#9381 | PR9381]] [2/5]
bitcoin/bitcoin@bd2fbc7
Depends on D9074

Test Plan: `ninja all check-all`

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D9075
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 27, 2021
Summary:
The change in walletdb.cpp is easier to review ignoring whitespace.

This change is need to get rid of CWalletTx copy constructor.

This is a backport of Core [[bitcoin/bitcoin#9381 | PR9381]] [3/5]
bitcoin/bitcoin@65b9d8f
Depends on D9075

Test Plan: `ninja all check-all`

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D9076
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 27, 2021
Summary:
Disable copying of CWalletTx objects to prevent bugs where instances get copied
in and out of the mapWallet map and fields are updated in the wrong copy.

This is a backport of Core [[bitcoin/bitcoin#9381 | PR9381]] [4/5]
bitcoin/bitcoin@d002f9d
Depends on D9076

Test Plan: `ninja all check-all`

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Subscribers: majcosta

Differential Revision: https://reviews.bitcoinabc.org/D9077
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 27, 2021
Summary:
CWalletTx initialization has been fixed so it's no longer necessary to change
which wallet a transaction is bound to.

This concludes backport of Core [[bitcoin/bitcoin#9381 | PR9381]] [5/5]
bitcoin/bitcoin@28b112e
Depends on D9077

Test Plan: `ninja all check-all`

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D9078
gades pushed a commit to cosanta/cosanta-core that referenced this pull request Mar 8, 2022
* [wallet] Construct CWalletTx objects in CommitTransaction

Construct CWalletTx objects in CWallet::CommitTransaction, instead of having
callers do it. This ensures CWalletTx objects are constructed in a uniform way
and all fields are set.

This also makes it possible to avoid confusing and wasteful CWalletTx copies in
bitcoin#9381

There is no change in behavior.

* [wallet] Get rid of CWalletTx default constructor

No change in behavior in the normal case. But buggy mapWallet lookups with
invalid txids will now throw exceptions instead of inserting dummy entries into
the map, and potentially causing segfaults and other failures.

This also makes it a compiler error to use the mapWallet[hash] syntax which
could create dummy entries.

* Apply suggestions from code review

Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>

Co-authored-by: Russell Yanofsky <russ@yanofsky.org>
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Aug 18, 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.