Skip to content

Commit 406b71a

Browse files
committed
wallet: Migrate entire address book entries
1 parent 5800c55 commit 406b71a

File tree

2 files changed

+69
-27
lines changed

2 files changed

+69
-27
lines changed

src/wallet/wallet.cpp

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3960,25 +3960,17 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
39603960
if (data.watchonly_wallet) {
39613961
LOCK(data.watchonly_wallet->cs_wallet);
39623962
if (data.watchonly_wallet->IsMine(addr_pair.first)) {
3963-
// Add to the watchonly. Preserve the labels, purpose, and change-ness
3964-
std::string label = addr_pair.second.GetLabel();
3965-
data.watchonly_wallet->m_address_book[addr_pair.first].purpose = addr_pair.second.purpose;
3966-
if (!addr_pair.second.IsChange()) {
3967-
data.watchonly_wallet->m_address_book[addr_pair.first].SetLabel(label);
3968-
}
3963+
// Add to the watchonly. Copy the entire address book entry
3964+
data.watchonly_wallet->m_address_book[addr_pair.first] = addr_pair.second;
39693965
dests_to_delete.push_back(addr_pair.first);
39703966
continue;
39713967
}
39723968
}
39733969
if (data.solvable_wallet) {
39743970
LOCK(data.solvable_wallet->cs_wallet);
39753971
if (data.solvable_wallet->IsMine(addr_pair.first)) {
3976-
// Add to the solvable. Preserve the labels, purpose, and change-ness
3977-
std::string label = addr_pair.second.GetLabel();
3978-
data.solvable_wallet->m_address_book[addr_pair.first].purpose = addr_pair.second.purpose;
3979-
if (!addr_pair.second.IsChange()) {
3980-
data.solvable_wallet->m_address_book[addr_pair.first].SetLabel(label);
3981-
}
3972+
// Add to the solvable. Copy the entire address book entry
3973+
data.solvable_wallet->m_address_book[addr_pair.first] = addr_pair.second;
39823974
dests_to_delete.push_back(addr_pair.first);
39833975
continue;
39843976
}
@@ -3998,21 +3990,13 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
39983990
// Labels for everything else ("send") should be cloned to all
39993991
if (data.watchonly_wallet) {
40003992
LOCK(data.watchonly_wallet->cs_wallet);
4001-
// Add to the watchonly. Preserve the labels, purpose, and change-ness
4002-
std::string label = addr_pair.second.GetLabel();
4003-
data.watchonly_wallet->m_address_book[addr_pair.first].purpose = addr_pair.second.purpose;
4004-
if (!addr_pair.second.IsChange()) {
4005-
data.watchonly_wallet->m_address_book[addr_pair.first].SetLabel(label);
4006-
}
3993+
// Add to the watchonly. Copy the entire address book entry
3994+
data.watchonly_wallet->m_address_book[addr_pair.first] = addr_pair.second;
40073995
}
40083996
if (data.solvable_wallet) {
40093997
LOCK(data.solvable_wallet->cs_wallet);
4010-
// Add to the solvable. Preserve the labels, purpose, and change-ness
4011-
std::string label = addr_pair.second.GetLabel();
4012-
data.solvable_wallet->m_address_book[addr_pair.first].purpose = addr_pair.second.purpose;
4013-
if (!addr_pair.second.IsChange()) {
4014-
data.solvable_wallet->m_address_book[addr_pair.first].SetLabel(label);
4015-
}
3998+
// Add to the solvable. Copy the entire address book entry
3999+
data.solvable_wallet->m_address_book[addr_pair.first] = addr_pair.second;
40164000
}
40174001
}
40184002
}
@@ -4023,10 +4007,12 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
40234007
WalletBatch batch{wallet.GetDatabase()};
40244008
for (const auto& [destination, addr_book_data] : wallet.m_address_book) {
40254009
auto address{EncodeDestination(destination)};
4026-
std::optional<std::string> label = addr_book_data.IsChange() ? std::nullopt : std::make_optional(addr_book_data.GetLabel());
4027-
// don't bother writing default values (unknown purpose)
40284010
if (addr_book_data.purpose) batch.WritePurpose(address, PurposeToString(*addr_book_data.purpose));
4029-
if (label) batch.WriteName(address, *label);
4011+
if (addr_book_data.label) batch.WriteName(address, *addr_book_data.label);
4012+
for (const auto& [id, request] : addr_book_data.receive_requests) {
4013+
batch.WriteAddressReceiveRequest(destination, id, request);
4014+
}
4015+
if (addr_book_data.previously_spent) batch.WriteAddressPreviouslySpent(destination, true);
40304016
}
40314017
};
40324018
if (data.watchonly_wallet) persist_address_book(*data.watchonly_wallet);

test/functional/wallet_migration.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,61 @@ def test_failed_migration_cleanup(self):
877877
assert_equal(magic, BTREE_MAGIC)
878878

879879

880+
def test_avoidreuse(self):
881+
self.log.info("Test that avoidreuse persists after migration")
882+
def_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
883+
884+
wallet = self.create_legacy_wallet("avoidreuse")
885+
wallet.setwalletflag("avoid_reuse", True)
886+
887+
# Import a pubkey to the test wallet and send some funds to it
888+
reused_imported_addr = def_wallet.getnewaddress()
889+
wallet.importpubkey(def_wallet.getaddressinfo(reused_imported_addr)["pubkey"])
890+
imported_utxos = self.create_outpoints(def_wallet, outputs=[{reused_imported_addr: 2}])
891+
def_wallet.lockunspent(False, imported_utxos)
892+
893+
# Send funds to the test wallet
894+
reused_addr = wallet.getnewaddress()
895+
def_wallet.sendtoaddress(reused_addr, 2)
896+
897+
self.generate(self.nodes[0], 1)
898+
899+
# Send funds from the test wallet with both its own and the imported
900+
wallet.sendall([def_wallet.getnewaddress()])
901+
def_wallet.sendall(recipients=[def_wallet.getnewaddress()], inputs=imported_utxos)
902+
self.generate(self.nodes[0], 1)
903+
balances = wallet.getbalances()
904+
assert_equal(balances["mine"]["trusted"], 0)
905+
assert_equal(balances["watchonly"]["trusted"], 0)
906+
907+
# Reuse the addresses
908+
def_wallet.sendtoaddress(reused_addr, 1)
909+
def_wallet.sendtoaddress(reused_imported_addr, 1)
910+
self.generate(self.nodes[0], 1)
911+
balances = wallet.getbalances()
912+
assert_equal(balances["mine"]["used"], 1)
913+
# Reused watchonly will not show up in balances
914+
assert_equal(balances["watchonly"]["trusted"], 0)
915+
assert_equal(balances["watchonly"]["untrusted_pending"], 0)
916+
assert_equal(balances["watchonly"]["immature"], 0)
917+
918+
utxos = wallet.listunspent()
919+
assert_equal(len(utxos), 2)
920+
for utxo in utxos:
921+
assert_equal(utxo["reused"], True)
922+
923+
# Migrate
924+
migrate_res = wallet.migratewallet()
925+
watchonly_wallet = self.nodes[0].get_wallet_rpc(migrate_res["watchonly_name"])
926+
927+
# One utxo in each wallet, marked used
928+
utxos = wallet.listunspent()
929+
assert_equal(len(utxos), 1)
930+
assert_equal(utxos[0]["reused"], True)
931+
watchonly_utxos = watchonly_wallet.listunspent()
932+
assert_equal(len(watchonly_utxos), 1)
933+
assert_equal(watchonly_utxos[0]["reused"], True)
934+
880935
def run_test(self):
881936
self.generate(self.nodes[0], 101)
882937

@@ -896,6 +951,7 @@ def run_test(self):
896951
self.test_conflict_txs()
897952
self.test_hybrid_pubkey()
898953
self.test_failed_migration_cleanup()
954+
self.test_avoidreuse()
899955

900956
if __name__ == '__main__':
901957
WalletMigrationTest().main()

0 commit comments

Comments
 (0)