Skip to content

Commit 63dad67

Browse files
author
MarcoFalke
committed
Merge #18546: Bugfix: Wallet: Safely deal with change in the address book [part 2]
7a2ecf1 Wallet: Change IsMine check in CWallet::DelAddressBook from assert to failure (Luke Dashjr) 2952c46 Wallet: Replace CAddressBookData.name with GetLabel() method (Luke Dashjr) d7092c3 QA: Test that change doesn't turn into non-change when spent in an avoid-reuse wallet (Luke Dashjr) Pull request description: Follow-up to #18192, not strictly necessary for 0.20 ACKs for top commit: MarcoFalke: re-ACK 7a2ecf1, only change is adding an assert_equal in the test 🔰 jnewbery: utACK 7a2ecf1 Tree-SHA512: e0933ee40f705b751697dc27249e1868ed4874254b174ebdd0a7150125d8c818402e66df2371718c7eeb90e67ee2317215fb260aa9b9d7b9b45ee436de2988ff
2 parents d12568e + 7a2ecf1 commit 63dad67

File tree

6 files changed

+45
-17
lines changed

6 files changed

+45
-17
lines changed

src/interfaces/wallet.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class WalletImpl : public Wallet
156156
return false;
157157
}
158158
if (name) {
159-
*name = it->second.name;
159+
*name = it->second.GetLabel();
160160
}
161161
if (is_mine) {
162162
*is_mine = m_wallet->IsMine(dest);
@@ -172,7 +172,7 @@ class WalletImpl : public Wallet
172172
std::vector<WalletAddress> result;
173173
for (const auto& item : m_wallet->m_address_book) {
174174
if (item.second.IsChange()) continue;
175-
result.emplace_back(item.first, m_wallet->IsMine(item.first), item.second.name, item.second.purpose);
175+
result.emplace_back(item.first, m_wallet->IsMine(item.first), item.second.GetLabel(), item.second.purpose);
176176
}
177177
return result;
178178
}

src/wallet/rpcdump.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, const CWall
6666
strAddr += ",";
6767
}
6868
strAddr += EncodeDestination(dest);
69-
strLabel = EncodeDumpString(address_book_entry->name);
69+
strLabel = EncodeDumpString(address_book_entry->GetLabel());
7070
fLabelFound = true;
7171
}
7272
}

src/wallet/rpcwallet.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
501501
{
502502
const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
503503
if (address_book_entry) {
504-
addressInfo.push_back(address_book_entry->name);
504+
addressInfo.push_back(address_book_entry->GetLabel());
505505
}
506506
}
507507
jsonGrouping.push_back(addressInfo);
@@ -1109,7 +1109,7 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, const CWalle
11091109
{
11101110
if (item_it->second.IsChange()) continue;
11111111
const CTxDestination& address = item_it->first;
1112-
const std::string& label = item_it->second.name;
1112+
const std::string& label = item_it->second.GetLabel();
11131113
auto it = mapTally.find(address);
11141114
if (it == mapTally.end() && !fIncludeEmpty)
11151115
continue;
@@ -1311,7 +1311,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWalle
13111311
entry.pushKV("amount", ValueFromAmount(-s.amount));
13121312
const auto* address_book_entry = pwallet->FindAddressBookEntry(s.destination);
13131313
if (address_book_entry) {
1314-
entry.pushKV("label", address_book_entry->name);
1314+
entry.pushKV("label", address_book_entry->GetLabel());
13151315
}
13161316
entry.pushKV("vout", s.vout);
13171317
entry.pushKV("fee", ValueFromAmount(-nFee));
@@ -1329,7 +1329,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWalle
13291329
std::string label;
13301330
const auto* address_book_entry = pwallet->FindAddressBookEntry(r.destination);
13311331
if (address_book_entry) {
1332-
label = address_book_entry->name;
1332+
label = address_book_entry->GetLabel();
13331333
}
13341334
if (filter_label && label != *filter_label) {
13351335
continue;
@@ -2963,7 +2963,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
29632963

29642964
const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
29652965
if (address_book_entry) {
2966-
entry.pushKV("label", address_book_entry->name);
2966+
entry.pushKV("label", address_book_entry->GetLabel());
29672967
}
29682968

29692969
std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
@@ -3710,7 +3710,7 @@ static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool v
37103710
{
37113711
UniValue ret(UniValue::VOBJ);
37123712
if (verbose) {
3713-
ret.pushKV("name", data.name);
3713+
ret.pushKV("name", data.GetLabel());
37143714
}
37153715
ret.pushKV("purpose", data.purpose);
37163716
return ret;
@@ -3822,7 +3822,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
38223822
// value of the name key/value pair in the labels array below.
38233823
const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
38243824
if (pwallet->chain().rpcEnableDeprecated("label") && address_book_entry) {
3825-
ret.pushKV("label", address_book_entry->name);
3825+
ret.pushKV("label", address_book_entry->GetLabel());
38263826
}
38273827

38283828
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
@@ -3851,7 +3851,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
38513851
if (pwallet->chain().rpcEnableDeprecated("labelspurpose")) {
38523852
labels.push_back(AddressBookDataToJSON(*address_book_entry, true));
38533853
} else {
3854-
labels.push_back(address_book_entry->name);
3854+
labels.push_back(address_book_entry->GetLabel());
38553855
}
38563856
}
38573857
ret.pushKV("labels", std::move(labels));
@@ -3897,7 +3897,7 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
38973897
std::set<std::string> addresses;
38983898
for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->m_address_book) {
38993899
if (item.second.IsChange()) continue;
3900-
if (item.second.name == label) {
3900+
if (item.second.GetLabel() == label) {
39013901
std::string address = EncodeDestination(item.first);
39023902
// CWallet::m_address_book is not expected to contain duplicate
39033903
// address strings, but build a separate set as a precaution just in
@@ -3963,7 +3963,7 @@ static UniValue listlabels(const JSONRPCRequest& request)
39633963
for (const std::pair<const CTxDestination, CAddressBookData>& entry : pwallet->m_address_book) {
39643964
if (entry.second.IsChange()) continue;
39653965
if (purpose.empty() || entry.second.purpose == purpose) {
3966-
label_set.insert(entry.second.name);
3966+
label_set.insert(entry.second.GetLabel());
39673967
}
39683968
}
39693969

src/wallet/wallet.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3221,7 +3221,10 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
32213221
// If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
32223222
// NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
32233223
// When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
3224-
assert(!IsMine(address));
3224+
if (IsMine(address)) {
3225+
WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
3226+
return false;
3227+
}
32253228

32263229
{
32273230
LOCK(cs_wallet);
@@ -3472,7 +3475,7 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co
34723475
{
34733476
if (item.second.IsChange()) continue;
34743477
const CTxDestination& address = item.first;
3475-
const std::string& strName = item.second.name;
3478+
const std::string& strName = item.second.GetLabel();
34763479
if (strName == label)
34773480
result.insert(address);
34783481
}

src/wallet/wallet.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,15 @@ class CAddressBookData
185185
bool m_change{true};
186186
std::string m_label;
187187
public:
188-
const std::string& name;
189188
std::string purpose;
190189

191-
CAddressBookData() : name(m_label), purpose("unknown") {}
190+
CAddressBookData() : purpose("unknown") {}
192191

193192
typedef std::map<std::string, std::string> StringMap;
194193
StringMap destdata;
195194

196195
bool IsChange() const { return m_change; }
196+
const std::string& GetLabel() const { return m_label; }
197197
void SetLabel(const std::string& label) {
198198
m_change = false;
199199
m_label = label;

test/functional/wallet_avoidreuse.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def run_test(self):
8383

8484
self.nodes[0].generate(110)
8585
self.sync_all()
86+
self.test_change_remains_change(self.nodes[1])
8687
reset_balance(self.nodes[1], self.nodes[0].getnewaddress())
8788
self.test_fund_send_fund_senddirty()
8889
reset_balance(self.nodes[1], self.nodes[0].getnewaddress())
@@ -137,6 +138,30 @@ def test_immutable(self):
137138
# Unload temp wallet
138139
self.nodes[1].unloadwallet(tempwallet)
139140

141+
def test_change_remains_change(self, node):
142+
self.log.info("Test that change doesn't turn into non-change when spent")
143+
144+
reset_balance(node, node.getnewaddress())
145+
addr = node.getnewaddress()
146+
txid = node.sendtoaddress(addr, 1)
147+
out = node.listunspent(minconf=0, query_options={'minimumAmount': 2})
148+
assert_equal(len(out), 1)
149+
assert_equal(out[0]['txid'], txid)
150+
changeaddr = out[0]['address']
151+
152+
# Make sure it's starting out as change as expected
153+
assert node.getaddressinfo(changeaddr)['ischange']
154+
for logical_tx in node.listtransactions():
155+
assert logical_tx.get('address') != changeaddr
156+
157+
# Spend it
158+
reset_balance(node, node.getnewaddress())
159+
160+
# It should still be change
161+
assert node.getaddressinfo(changeaddr)['ischange']
162+
for logical_tx in node.listtransactions():
163+
assert logical_tx.get('address') != changeaddr
164+
140165
def test_fund_send_fund_senddirty(self):
141166
'''
142167
Test the same as test_fund_send_fund_send, except send the 10 BTC with

0 commit comments

Comments
 (0)