What changed, and why it matters
This commit is a routine internal cleanup in Bitcoin Core's wallet code. It removes an old string-to-string metadata map called mapValue from wallet transactions and replaces it with direct typed fields. The change keeps the same data on disk and in RPC output, just organized differently. There is no indication this fixes a security bug or introduces a new vulnerability.
No security action required. Treat as normal code-quality/maintenance change. Reviewers may verify that wallet.dat backward compatibility is preserved, which the diff indicates it is.
Security signals we found
No security-relevant keywords in commit title or message
No bug-fix or CVE references in commit or supplied materials
Change is a structural refactor with equivalent serialization behavior
Removal of generic string map reduces attack surface for unexpected key injection
No input validation, memory safety, or cryptography changes observed
Evidence from the diff
The patch refactors CWalletTx by removing the mapValue_t member and the WalletValueMap interface type. Previously, fields such as from, message, comment, to, replaces_txid, replaced_by_txid, nOrderPos, and nTimeSmart were copied into a temporary std::map
Changed components
src/wallet/transaction.hsrc/wallet/interfaces.cppsrc/wallet/rpc/transactions.cppsrc/wallet/wallet.cppsrc/interfaces/wallet.hsrc/qt/transactionrecord.cppInspect captured patch +16 / −65
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index ce1c69b0..20785a81 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -60,7 +60,6 @@ struct WalletTxStatus;
struct WalletMigrationResult;
using WalletOrderForm = std::vector<std::pair<std::string, std::string>>;
-using WalletValueMap = std::map<std::string, std::string>;
//! Interface for accessing a wallet.
class Wallet
@@ -395,7 +394,6 @@ struct WalletTx
std::optional<std::string> message; // Deprecated
std::optional<std::string> comment;
std::optional<std::string> comment_to;
- std::map<std::string, std::string> value_map;
bool is_coinbase;
bool operator<(const WalletTx& a) const { return tx->GetHash() < a.tx->GetHash(); }
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 1f323590..fdabcf63 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -32,7 +32,6 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
CAmount nDebit = wtx.debit;
CAmount nNet = nCredit - nDebit;
Txid hash = wtx.tx->GetHash();
- std::map<std::string, std::string> mapValue = wtx.value_map;
bool all_from_me = true;
bool any_from_me = false;
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index d95e0574..dcdd512b 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -48,7 +48,6 @@ using interfaces::WalletOrderForm;
using interfaces::WalletTx;
using interfaces::WalletTxOut;
using interfaces::WalletTxStatus;
-using interfaces::WalletValueMap;
namespace wallet {
// All members of the classes in this namespace are intentionally public, as the
@@ -83,7 +82,6 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
result.message = wtx.m_message;
result.comment = wtx.m_comment;
result.comment_to = wtx.m_comment_to;
- result.value_map = wtx.mapValue;
result.is_coinbase = wtx.IsCoinBase();
return result;
}
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index 6660bf87..480a346f 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -66,9 +66,6 @@ static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue
if (wtx.m_comment_to) entry.pushKV("to", *wtx.m_comment_to);
if (wtx.m_replaces_txid) entry.pushKV("replaces_txid", wtx.m_replaces_txid->ToString());
if (wtx.m_replaced_by_txid) entry.pushKV("replaced_by_txid", wtx.m_replaced_by_txid->ToString());
-
- for (const std::pair<const std::string, std::string>& item : wtx.mapValue)
- entry.pushKV(item.first, item.second);
}
struct tallyitem
diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h
index 23f99042..21c0e267 100644
--- a/src/wallet/transaction.h
+++ b/src/wallet/transaction.h
@@ -164,9 +164,6 @@ struct CachableAmount
};
-typedef std::map<std::string, std::string> mapValue_t;
-
-
/** Legacy class used for deserializing vtxPrev for backwards compatibility.
* vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
* but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
@@ -204,28 +201,6 @@ public:
std::optional<std::string> m_comment_to;
std::optional<Txid> m_replaces_txid;
std::optional<Txid> m_replaced_by_txid;
- /**
- * Key/value map with information about the transaction.
- *
- * The following keys are serialized in the wallet database, but shouldn't
- * be read or written through the map (they will be temporarily added and
- * removed from the map during serialization):
- *
- * "fromaccount" - serialized strFromAccount value
- * "n" - serialized nOrderPos value
- * "timesmart" - serialized nTimeSmart value
- * "spent" - serialized vfSpent value that existed prior to
- * 2014 (removed in commit 93a18a3)
- * "from", "message" - obsolete fields that could be set in UI prior to
- * 2011 (removed in commit 4d9b223)
- * "comment", "to" - comment strings provided to sendtoaddress,
- * and sendmany wallet RPCs
- * "replaces_txid" - txid (as HexStr) of transaction replaced by
- * bumpfee on transaction created by bumpfee
- * "replaced_by_txid" - txid (as HexStr) of transaction created by
- * bumpfee on transaction replaced by bumpfee
- */
- mapValue_t mapValue;
std::vector<std::pair<std::string, std::string> > vOrderForm;
unsigned int nTimeReceived; //!< time received by this node
/**
@@ -263,7 +238,6 @@ public:
void Init()
{
- mapValue.clear();
vOrderForm.clear();
nTimeReceived = 0;
nTimeSmart = 0;
@@ -290,21 +264,16 @@ public:
template<typename Stream>
void Serialize(Stream& s) const
{
- mapValue_t mapValueCopy = mapValue;
- if (m_from) mapValueCopy["from"] = *m_from;
- if (m_message) mapValueCopy["message"] = *m_message;
- if (m_comment) mapValueCopy["comment"] = *m_comment;
- if (m_comment_to) mapValueCopy["to"] = *m_comment_to;
- if (m_replaces_txid) mapValueCopy["replaces_txid"] = m_replaces_txid->ToString();
- if (m_replaced_by_txid) mapValueCopy["replaced_by_txid"] = m_replaced_by_txid->ToString();
-
- mapValueCopy["fromaccount"] = "";
- if (nOrderPos != -1) {
- mapValueCopy["n"] = util::ToString(nOrderPos);
- }
- if (nTimeSmart) {
- mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
- }
+ std::map<std::string, std::string> string_values;
+ if (m_from) string_values["from"] = *m_from;
+ if (m_message) string_values["message"] = *m_message;
+ if (m_comment) string_values["comment"] = *m_comment;
+ if (m_comment_to) string_values["to"] = *m_comment_to;
+ if (m_replaces_txid) string_values["replaces_txid"] = m_replaces_txid->ToString();
+ if (m_replaced_by_txid) string_values["replaced_by_txid"] = m_replaced_by_txid->ToString();
+ string_values["fromaccount"] = "";
+ if (nOrderPos != -1) string_values["n"] = util::ToString(nOrderPos);
+ if (nTimeSmart) string_values["timesmart"] = strprintf("%u", nTimeSmart);
std::vector<uint8_t> dummy_vector1; // Used to be vMerkleBranch
std::vector<uint8_t> dummy_vector2; // Used to be vtxPrev
@@ -312,7 +281,7 @@ public:
uint32_t dummy_int = 0; // Used to be fTimeReceivedIsTxTime
uint256 serializedHash = TxStateSerializedBlockHash(m_state);
int serializedIndex = TxStateSerializedIndex(m_state);
- s << TX_WITH_WITNESS(tx) << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << dummy_int << nTimeReceived << dummy_bool << dummy_bool;
+ s << TX_WITH_WITNESS(tx) << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << string_values << vOrderForm << dummy_int << nTimeReceived << dummy_bool << dummy_bool;
}
template<typename Stream>
@@ -326,13 +295,14 @@ public:
uint32_t dummy_int; // Used to be fTimeReceivedIsTxTime
uint256 serialized_block_hash;
int serializedIndex;
- s >> TX_WITH_WITNESS(tx) >> serialized_block_hash >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> dummy_int >> nTimeReceived >> dummy_bool >> dummy_bool;
+ std::map<std::string, std::string> string_values;
+ s >> TX_WITH_WITNESS(tx) >> serialized_block_hash >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> string_values >> vOrderForm >> dummy_int >> nTimeReceived >> dummy_bool >> dummy_bool;
m_state = TxStateInterpretSerialized({serialized_block_hash, serializedIndex});
- mapValue.erase("fromaccount");
- mapValue.erase("spent");
- for (const auto& [key, value] : mapValue) {
+ string_values.erase("fromaccount");
+ string_values.erase("spent");
+ for (const auto& [key, value] : string_values) {
if (key == "n") nOrderPos = LocaleIndependentAtoi<int64_t>(value);
else if (key == "timesmart") nTimeSmart = LocaleIndependentAtoi<int64_t>(value);
else if (key == "from") m_from = value;
@@ -345,15 +315,6 @@ public:
throw std::runtime_error("Unexpected value in CWalletTx strings value map");
}
}
-
- mapValue.erase("n");
- mapValue.erase("timesmart");
- mapValue.erase("from");
- mapValue.erase("message");
- mapValue.erase("comment");
- mapValue.erase("to");
- mapValue.erase("replaces_txid");
- mapValue.erase("replaced_by_txid");
}
void SetTx(CTransactionRef arg)
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 0fc9a9a3..86c40de0 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -748,7 +748,6 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran
copyTo->m_comment_to = copyFrom->m_comment_to;
copyTo->m_replaces_txid = copyFrom->m_replaces_txid;
copyTo->m_replaced_by_txid = copyFrom->m_replaced_by_txid;
- copyTo->mapValue = copyFrom->mapValue;
copyTo->vOrderForm = copyFrom->vOrderForm;
// nTimeReceived not copied on purpose
copyTo->nTimeSmart = copyFrom->nTimeSmart;
@@ -2344,7 +2343,6 @@ void CWallet::CommitTransaction(
// Add tx to wallet, because if it has change it's also ours,
// otherwise just for transaction history.
CWalletTx* wtx = AddToWallet(tx, TxStateInactive{}, [&](CWalletTx& wtx, bool new_tx) {
- CHECK_NONFATAL(wtx.mapValue.empty());
CHECK_NONFATAL(wtx.vOrderForm.empty());
if (replaces_txid) wtx.m_replaces_txid = replaces_txid;
if (comment) wtx.m_comment = comment;
Why this scored 19/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.