wallet: persist synced metadata and do not sync on load
What changed, and why it matters
This Bitcoin Core wallet change makes two related adjustments to how metadata is kept in sync across 'malleated' transaction variants (different versions of related transactions stored in the wallet). First, when metadata is synchronized between variants during normal operation, the change is now immediately written to the wallet database. Second, the wallet no longer performs that synchronization step while loading transactions from disk, because the data should already be persisted. The patch is small and appears to be a correctness/durability improvement rather than a fix for an active exploit, but it prevents a potential inconsistency where in-memory metadata could differ from what is stored on disk.
Review whether any existing wallet state could contain divergent malleated-variant metadata written before this change; consider a migration or re-sync for wallets with malleated transactions. Otherwise, treat as a routine correctness improvement and include in release notes as a wallet robustness fix.
Security signals we found
Wallet metadata consistency change
Persistence of previously in-memory-only synchronization
Removal of load-time metadata repair path
Potential for on-disk/in-memory state divergence before patch
Evidence from the diff
The commit modifies CWallet::SyncMalleatedTxMetadata to accept a WalletBatch reference and persist updated metadata for each malleated variant via batch.WriteTxMetadata. It also removes the call to SyncMalleatedTxMetadata from LoadToWallet, since metadata is now written at insertion time and does not need recomputation during load. The change is a refactor toward consistent on-disk state and avoids relying on load-time recomputation to repair metadata divergence.
Changed components
src/wallet/wallet.cppsrc/wallet/wallet.hCWallet::SyncMalleatedTxMetadataCWallet::AddToWalletCWallet::LoadToWalletInspect captured patch +4 / −4
### src/wallet/wallet.cpp
@@ -762,7 +762,7 @@ std::set<CWalletTx*, WalletTxOrderComparator> CWallet::GetMalleatedVariants(cons
return txs;
}
-void CWallet::SyncMalleatedTxMetadata(const CWalletTx& wtx)
+void CWallet::SyncMalleatedTxMetadata(WalletBatch& batch, const CWalletTx& wtx)
{
const auto txs = GetMalleatedVariants(wtx);
if (txs.size() <= 1) return; // no variants, nothing to do
@@ -782,6 +782,7 @@ void CWallet::SyncMalleatedTxMetadata(const CWalletTx& wtx)
for (CWalletTx* copyTo : txs) {
if (copyTo == copyFrom) continue;
metadata(*copyTo) = metadata(*copyFrom);
+ (void)batch.WriteTxMetadata(*copyTo);
}
}
@@ -1105,7 +1106,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block);
AddToSpends(wtx);
- SyncMalleatedTxMetadata(wtx);
+ SyncMalleatedTxMetadata(batch, wtx);
// Update birth time when tx time is older than it.
MaybeUpdateBirthTime(wtx.GetTxTime());
@@ -1213,7 +1214,6 @@ bool CWallet::LoadToWallet(CWalletTx&& wtx_in)
}
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
AddToSpends(wtx);
- SyncMalleatedTxMetadata(wtx);
for (const CTxIn& txin : wtx.GetTx()->vin) {
auto it = mapWallet.find(txin.prevout.hash);
if (it != mapWallet.end()) {
### src/wallet/wallet.h
@@ -377,7 +377,7 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
* plus wtx itself. Sorted by the order in which they were inserted in the wallet (CWalletTx::nOrderPos) */
std::set<CWalletTx*, WalletTxOrderComparator> GetMalleatedVariants(const CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void SyncMalleatedTxMetadata(const CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void SyncMalleatedTxMetadata(WalletBatch& batch, const CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool SyncTransaction(const CTransactionRef& tx, const SyncTxState& state, bool rescanning_old_block = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
Why this scored 35/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.