Skip to content

Commit 60091d2

Browse files
committed
Merge #9381: Remove CWalletTx merging logic from AddToWallet
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 #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
2 parents fbd5227 + 28b112e commit 60091d2

File tree

9 files changed

+126
-134
lines changed

9 files changed

+126
-134
lines changed

src/wallet/rpcdump.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,6 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
345345
if (!DecodeHexTx(tx, request.params[0].get_str()))
346346
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
347347
uint256 hashTx = tx.GetHash();
348-
CWalletTx wtx(pwallet, MakeTransactionRef(std::move(tx)));
349348

350349
CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION);
351350
CMerkleBlock merkleBlock;
@@ -372,10 +371,10 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
372371
unsigned int txnIndex = vIndex[it - vMatch.begin()];
373372

374373
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, merkleBlock.header.GetHash(), txnIndex);
375-
wtx.m_confirm = confirm;
376374

377-
if (pwallet->IsMine(*wtx.tx)) {
378-
pwallet->AddToWallet(wtx, false);
375+
CTransactionRef tx_ref = MakeTransactionRef(tx);
376+
if (pwallet->IsMine(*tx_ref)) {
377+
pwallet->AddToWallet(std::move(tx_ref), confirm);
379378
return NullUniValue;
380379
}
381380

src/wallet/rpcwallet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1583,7 +1583,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
15831583
UniValue transactions(UniValue::VARR);
15841584

15851585
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
1586-
CWalletTx tx = pairWtx.second;
1586+
const CWalletTx& tx = pairWtx.second;
15871587

15881588
if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) {
15891589
ListTransactions(pwallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);

src/wallet/test/coinselector_tests.cpp

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ BOOST_FIXTURE_TEST_SUITE(coinselector_tests, WalletTestingSetup)
2424
// we repeat those tests this many times and only complain if all iterations of the test fail
2525
#define RANDOM_REPEATS 5
2626

27-
std::vector<std::unique_ptr<CWalletTx>> wtxn;
28-
2927
typedef std::set<CInputCoin> CoinSet;
3028

3129
static std::vector<COutput> vCoins;
@@ -74,16 +72,14 @@ static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bo
7472
// so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
7573
tx.vin.resize(1);
7674
}
77-
std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx)));
75+
CWalletTx* wtx = wallet.AddToWallet(MakeTransactionRef(std::move(tx)), /* confirm= */ {});
7876
if (fIsFromMe)
7977
{
8078
wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
8179
wtx->m_is_cache_empty = false;
8280
}
83-
COutput output(wtx.get(), nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
81+
COutput output(wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
8482
vCoins.push_back(output);
85-
wallet.AddToWallet(*wtx.get());
86-
wtxn.emplace_back(std::move(wtx));
8783
}
8884
static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
8985
{
@@ -93,7 +89,6 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
9389
static void empty_wallet(void)
9490
{
9591
vCoins.clear();
96-
wtxn.clear();
9792
balance = 0;
9893
}
9994

src/wallet/test/psbt_wallet_tests.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,12 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
2222
CDataStream s_prev_tx1(ParseHex("0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000"), SER_NETWORK, PROTOCOL_VERSION);
2323
CTransactionRef prev_tx1;
2424
s_prev_tx1 >> prev_tx1;
25-
CWalletTx prev_wtx1(&m_wallet, prev_tx1);
26-
m_wallet.mapWallet.emplace(prev_wtx1.GetHash(), std::move(prev_wtx1));
25+
m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx1->GetHash()), std::forward_as_tuple(&m_wallet, prev_tx1));
2726

2827
CDataStream s_prev_tx2(ParseHex("0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000"), SER_NETWORK, PROTOCOL_VERSION);
2928
CTransactionRef prev_tx2;
3029
s_prev_tx2 >> prev_tx2;
31-
CWalletTx prev_wtx2(&m_wallet, prev_tx2);
32-
m_wallet.mapWallet.emplace(prev_wtx2.GetHash(), std::move(prev_wtx2));
30+
m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx2->GetHash()), std::forward_as_tuple(&m_wallet, prev_tx2));
3331

3432
// Add scripts
3533
CScript rs1;

src/wallet/test/wallet_tests.cpp

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
332332
static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
333333
{
334334
CMutableTransaction tx;
335+
CWalletTx::Confirmation confirm;
335336
tx.nLockTime = lockTime;
336337
SetMockTime(mockTime);
337338
CBlockIndex* block = nullptr;
@@ -342,23 +343,15 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
342343
block = inserted.first->second;
343344
block->nTime = blockTime;
344345
block->phashBlock = &hash;
346+
confirm = {CWalletTx::Status::CONFIRMED, block->nHeight, hash, 0};
345347
}
346348

347-
CWalletTx wtx(&wallet, MakeTransactionRef(tx));
348-
LOCK(wallet.cs_wallet);
349349
// If transaction is already in map, to avoid inconsistencies, unconfirmation
350350
// is needed before confirm again with different block.
351-
std::map<uint256, CWalletTx>::iterator it = wallet.mapWallet.find(wtx.GetHash());
352-
if (it != wallet.mapWallet.end()) {
351+
return wallet.AddToWallet(MakeTransactionRef(tx), confirm, [&](CWalletTx& wtx, bool /* new_tx */) {
353352
wtx.setUnconfirmed();
354-
wallet.AddToWallet(wtx);
355-
}
356-
if (block) {
357-
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, block->nHeight, block->GetBlockHash(), 0);
358-
wtx.m_confirm = confirm;
359-
}
360-
wallet.AddToWallet(wtx);
361-
return wallet.mapWallet.at(wtx.GetHash()).nTimeSmart;
353+
return true;
354+
})->nTimeSmart;
362355
}
363356

364357
// Simple test to verify assignment of CWalletTx::nSmartTime value. Could be

src/wallet/wallet.cpp

Lines changed: 50 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -784,19 +784,19 @@ bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
784784
return false;
785785
}
786786

787-
bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
787+
CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx, bool fFlushOnClose)
788788
{
789789
LOCK(cs_wallet);
790790

791791
WalletBatch batch(*database, "r+", fFlushOnClose);
792792

793-
uint256 hash = wtxIn.GetHash();
793+
uint256 hash = tx->GetHash();
794794

795795
if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
796796
// Mark used destinations
797797
std::set<CTxDestination> tx_destinations;
798798

799-
for (const CTxIn& txin : wtxIn.tx->vin) {
799+
for (const CTxIn& txin : tx->vin) {
800800
const COutPoint& op = txin.prevout;
801801
SetSpentKeyState(batch, op.hash, op.n, true, tx_destinations);
802802
}
@@ -805,55 +805,50 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
805805
}
806806

807807
// Inserts only if not already there, returns tx inserted or tx found
808-
std::pair<std::map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(std::make_pair(hash, wtxIn));
808+
auto ret = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(this, tx));
809809
CWalletTx& wtx = (*ret.first).second;
810-
wtx.BindWallet(this);
811810
bool fInsertedNew = ret.second;
811+
bool fUpdated = update_wtx && update_wtx(wtx, fInsertedNew);
812812
if (fInsertedNew) {
813+
wtx.m_confirm = confirm;
813814
wtx.nTimeReceived = chain().getAdjustedTime();
814815
wtx.nOrderPos = IncOrderPosNext(&batch);
815816
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
816817
wtx.nTimeSmart = ComputeTimeSmart(wtx);
817818
AddToSpends(hash);
818819
}
819820

820-
bool fUpdated = false;
821821
if (!fInsertedNew)
822822
{
823-
if (wtxIn.m_confirm.status != wtx.m_confirm.status) {
824-
wtx.m_confirm.status = wtxIn.m_confirm.status;
825-
wtx.m_confirm.nIndex = wtxIn.m_confirm.nIndex;
826-
wtx.m_confirm.hashBlock = wtxIn.m_confirm.hashBlock;
827-
wtx.m_confirm.block_height = wtxIn.m_confirm.block_height;
823+
if (confirm.status != wtx.m_confirm.status) {
824+
wtx.m_confirm.status = confirm.status;
825+
wtx.m_confirm.nIndex = confirm.nIndex;
826+
wtx.m_confirm.hashBlock = confirm.hashBlock;
827+
wtx.m_confirm.block_height = confirm.block_height;
828828
fUpdated = true;
829829
} else {
830-
assert(wtx.m_confirm.nIndex == wtxIn.m_confirm.nIndex);
831-
assert(wtx.m_confirm.hashBlock == wtxIn.m_confirm.hashBlock);
832-
assert(wtx.m_confirm.block_height == wtxIn.m_confirm.block_height);
833-
}
834-
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe)
835-
{
836-
wtx.fFromMe = wtxIn.fFromMe;
837-
fUpdated = true;
830+
assert(wtx.m_confirm.nIndex == confirm.nIndex);
831+
assert(wtx.m_confirm.hashBlock == confirm.hashBlock);
832+
assert(wtx.m_confirm.block_height == confirm.block_height);
838833
}
839834
// If we have a witness-stripped version of this transaction, and we
840835
// see a new version with a witness, then we must be upgrading a pre-segwit
841836
// wallet. Store the new version of the transaction with the witness,
842837
// as the stripped-version must be invalid.
843838
// TODO: Store all versions of the transaction, instead of just one.
844-
if (wtxIn.tx->HasWitness() && !wtx.tx->HasWitness()) {
845-
wtx.SetTx(wtxIn.tx);
839+
if (tx->HasWitness() && !wtx.tx->HasWitness()) {
840+
wtx.SetTx(tx);
846841
fUpdated = true;
847842
}
848843
}
849844

850845
//// debug print
851-
WalletLogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
846+
WalletLogPrintf("AddToWallet %s %s%s\n", hash.ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
852847

853848
// Write to disk
854849
if (fInsertedNew || fUpdated)
855850
if (!batch.WriteTx(wtx))
856-
return false;
851+
return nullptr;
857852

858853
// Break debit/credit balance caches:
859854
wtx.MarkDirty();
@@ -867,7 +862,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
867862

868863
if (!strCmd.empty())
869864
{
870-
boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());
865+
boost::replace_all(strCmd, "%s", hash.GetHex());
871866
#ifndef WIN32
872867
// Substituting the wallet name isn't currently supported on windows
873868
// because windows shell escaping has not been implemented yet:
@@ -881,35 +876,36 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
881876
}
882877
#endif
883878

884-
return true;
879+
return &wtx;
885880
}
886881

887-
void CWallet::LoadToWallet(CWalletTx& wtxIn)
882+
bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx)
888883
{
884+
const auto& ins = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(this, nullptr));
885+
CWalletTx& wtx = ins.first->second;
886+
if (!fill_wtx(wtx, ins.second)) {
887+
return false;
888+
}
889889
// If wallet doesn't have a chain (e.g wallet-tool), don't bother to update txn.
890890
if (HaveChain()) {
891-
Optional<int> block_height = chain().getBlockHeight(wtxIn.m_confirm.hashBlock);
891+
Optional<int> block_height = chain().getBlockHeight(wtx.m_confirm.hashBlock);
892892
if (block_height) {
893893
// Update cached block height variable since it not stored in the
894894
// serialized transaction.
895-
wtxIn.m_confirm.block_height = *block_height;
896-
} else if (wtxIn.isConflicted() || wtxIn.isConfirmed()) {
895+
wtx.m_confirm.block_height = *block_height;
896+
} else if (wtx.isConflicted() || wtx.isConfirmed()) {
897897
// If tx block (or conflicting block) was reorged out of chain
898898
// while the wallet was shutdown, change tx status to UNCONFIRMED
899899
// and reset block height, hash, and index. ABANDONED tx don't have
900900
// associated blocks and don't need to be updated. The case where a
901901
// transaction was reorged out while online and then reconfirmed
902902
// while offline is covered by the rescan logic.
903-
wtxIn.setUnconfirmed();
904-
wtxIn.m_confirm.hashBlock = uint256();
905-
wtxIn.m_confirm.block_height = 0;
906-
wtxIn.m_confirm.nIndex = 0;
903+
wtx.setUnconfirmed();
904+
wtx.m_confirm.hashBlock = uint256();
905+
wtx.m_confirm.block_height = 0;
906+
wtx.m_confirm.nIndex = 0;
907907
}
908908
}
909-
uint256 hash = wtxIn.GetHash();
910-
const auto& ins = mapWallet.emplace(hash, wtxIn);
911-
CWalletTx& wtx = ins.first->second;
912-
wtx.BindWallet(this);
913909
if (/* insertion took place */ ins.second) {
914910
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
915911
}
@@ -923,6 +919,7 @@ void CWallet::LoadToWallet(CWalletTx& wtxIn)
923919
}
924920
}
925921
}
922+
return true;
926923
}
927924

928925
bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool fUpdate)
@@ -961,13 +958,9 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Co
961958
}
962959
}
963960

964-
CWalletTx wtx(this, ptx);
965-
966961
// Block disconnection override an abandoned tx as unconfirmed
967962
// which means user may have to call abandontransaction again
968-
wtx.m_confirm = confirm;
969-
970-
return AddToWallet(wtx, false);
963+
return AddToWallet(MakeTransactionRef(tx), confirm, /* update_wtx= */ nullptr, /* fFlushOnClose= */ false);
971964
}
972965
}
973966
return false;
@@ -3008,29 +3001,30 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
30083001
void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm)
30093002
{
30103003
LOCK(cs_wallet);
3011-
3012-
CWalletTx wtxNew(this, std::move(tx));
3013-
wtxNew.mapValue = std::move(mapValue);
3014-
wtxNew.vOrderForm = std::move(orderForm);
3015-
wtxNew.fTimeReceivedIsTxTime = true;
3016-
wtxNew.fFromMe = true;
3017-
3018-
WalletLogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); /* Continued */
3004+
WalletLogPrintf("CommitTransaction:\n%s", tx->ToString()); /* Continued */
30193005

30203006
// Add tx to wallet, because if it has change it's also ours,
30213007
// otherwise just for transaction history.
3022-
AddToWallet(wtxNew);
3008+
AddToWallet(tx, {}, [&](CWalletTx& wtx, bool new_tx) {
3009+
CHECK_NONFATAL(wtx.mapValue.empty());
3010+
CHECK_NONFATAL(wtx.vOrderForm.empty());
3011+
wtx.mapValue = std::move(mapValue);
3012+
wtx.vOrderForm = std::move(orderForm);
3013+
wtx.fTimeReceivedIsTxTime = true;
3014+
wtx.fFromMe = true;
3015+
return true;
3016+
});
30233017

30243018
// Notify that old coins are spent
3025-
for (const CTxIn& txin : wtxNew.tx->vin) {
3019+
for (const CTxIn& txin : tx->vin) {
30263020
CWalletTx &coin = mapWallet.at(txin.prevout.hash);
3027-
coin.BindWallet(this);
3021+
coin.MarkDirty();
30283022
NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);
30293023
}
30303024

30313025
// Get the inserted-CWalletTx from mapWallet so that the
30323026
// fInMempool flag is cached properly
3033-
CWalletTx& wtx = mapWallet.at(wtxNew.GetHash());
3027+
CWalletTx& wtx = mapWallet.at(tx->GetHash());
30343028

30353029
if (!fBroadcastTransactions) {
30363030
// Don't submit tx to the mempool
@@ -3102,7 +3096,7 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
31023096
return DBErrors::LOAD_OK;
31033097
}
31043098

3105-
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
3099+
DBErrors CWallet::ZapWalletTx(std::list<CWalletTx>& vWtx)
31063100
{
31073101
DBErrors nZapWalletTxRet = WalletBatch(*database,"cr+").ZapWalletTx(vWtx);
31083102
if (nZapWalletTxRet == DBErrors::NEED_REWRITE)
@@ -3714,7 +3708,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
37143708
const std::string walletFile = WalletDataFilePath(location.GetPath()).string();
37153709

37163710
// needed to restore wallet transaction meta data after -zapwallettxes
3717-
std::vector<CWalletTx> vWtx;
3711+
std::list<CWalletTx> vWtx;
37183712

37193713
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
37203714
chain.initMessage(_("Zapping all transactions from wallet...").translated);

0 commit comments

Comments
 (0)