Bitcoin Core 31.99.0
P2P Digital Currency
export.cpp
Go to the documentation of this file.
1// Copyright (c) 2026-present The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or https://www.opensource.org/licenses/mit-license.php.
4
5#include <wallet/export.h>
6
7#include <key_io.h>
8#include <util/fs.h>
9#include <util/expected.h>
11#include <wallet/context.h>
12#include <wallet/sqlite.h>
13#include <wallet/wallet.h>
14
15#include <fstream>
16
17namespace wallet {
19{
20 AssertLockHeld(wallet.cs_wallet);
21 std::vector<WalletDescInfo> wallet_descriptors;
22 for (const auto& spk_man : wallet.GetAllScriptPubKeyMans()) {
23 const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
24 if (!desc_spk_man) {
25 return util::Unexpected{"Unexpected ScriptPubKey manager type."};
26 }
27 LOCK(desc_spk_man->cs_desc_man);
28 const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
29 std::string descriptor;
30 if (!Assume(desc_spk_man->GetDescriptorString(descriptor, export_private))) {
31 return util::Unexpected{"Can't get descriptor string."};
32 }
33 const bool is_range = wallet_descriptor.descriptor->IsRange();
34 wallet_descriptors.emplace_back(
35 descriptor,
36 wallet_descriptor.creation_time,
37 wallet.IsActiveScriptPubKeyMan(*desc_spk_man),
38 wallet.IsInternalScriptPubKeyMan(desc_spk_man),
39 is_range ? std::optional(std::make_pair(wallet_descriptor.range_start, wallet_descriptor.range_end)) : std::nullopt,
40 wallet_descriptor.next_index
41 );
42 }
43 return wallet_descriptors;
44}
45
46util::Result<std::string> ExportWatchOnlyWallet(const CWallet& wallet, const fs::path& destination, WalletContext& context)
47{
48 AssertLockHeld(wallet.cs_wallet);
49
50 if (destination.empty()) {
51 return util::Error{_("Error: Export destination cannot be empty")};
52 }
53 if (fs::exists(destination)) {
54 return util::Error{strprintf(_("Error: Export destination '%s' already exists"), fs::PathToString(destination))};
55 }
56 if (!std::ofstream{fs::PathToString(destination)}) {
57 return util::Error{strprintf(_("Error: Could not create file '%s'"), fs::PathToString(destination))};
58 }
59 bool success = false;
60 auto cleanup_destination = interfaces::MakeCleanupHandler([&success, &destination] {
61 if (!success) fs::remove(destination);
62 });
63
64 // Get the descriptors from this wallet
65 util::Expected<std::vector<WalletDescInfo>, std::string> exported = ExportDescriptors(wallet, /*export_private=*/false);
66 if (!exported) {
67 return util::Error{Untranslated(exported.error())};
68 }
69 if (exported->empty()) {
70 return util::Error{_("Error: Wallet has no descriptors to export")};
71 }
72
73 // Make the wallet with the same flags as this wallet, but without private keys
74 const uint64_t create_flags = wallet.GetWalletFlags() | WALLET_FLAG_DISABLE_PRIVATE_KEYS;
75
76 // Create the temporary watchonly wallet in memory to avoid leaving files on disk
77 std::vector<bilingual_str> warnings;
78 bilingual_str error;
79 WalletContext empty_context;
80 empty_context.args = context.args;
81 std::shared_ptr<CWallet> watchonly_wallet = CWallet::CreateNew(empty_context, /*name=*/wallet.GetName() + "_watchonly_temp", MakeInMemoryWalletDatabase(), create_flags, /*born_encrypted=*/false, error, warnings);
82 if (!watchonly_wallet) {
83 return util::Error{strprintf(_("Error: Failed to create new watchonly wallet. %s"), error)};
84 }
85
86 {
87 LOCK(watchonly_wallet->cs_wallet);
88
89 // Parse the descriptors and add them to the new wallet
90 for (const WalletDescInfo& desc_info : *Assert(exported)) {
91 // Parse the descriptor
92 FlatSigningProvider dummy_keys;
93 std::string dummy_err;
94 std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_info.descriptor, dummy_keys, dummy_err, /*require_checksum=*/true);
95 CHECK_NONFATAL(descs.size() == 1); // All of our descriptors should be valid, and not multipath
96 CHECK_NONFATAL(dummy_keys.keys.size() == 0); // No private keys should be present in our exported descriptors
97
98 // Get the range if there is one
99 int32_t range_start = 0;
100 int32_t range_end = 0;
101 if (desc_info.range) {
102 range_start = desc_info.range->first;
103 range_end = desc_info.range->second;
104 }
105
106 WalletDescriptor w_desc(std::move(descs.at(0)), desc_info.creation_time, range_start, range_end, desc_info.next_index);
107
108 // For descriptors that cannot self expand (i.e. needs private keys or cache), retrieve the cache
109 uint256 desc_id = w_desc.id;
110 if (!w_desc.descriptor->CanSelfExpand()) {
111 DescriptorScriptPubKeyMan* desc_spkm = dynamic_cast<DescriptorScriptPubKeyMan*>(wallet.GetScriptPubKeyMan(desc_id));
112 w_desc.cache = WITH_LOCK(desc_spkm->cs_desc_man, return desc_spkm->GetWalletDescriptor().cache);
113 }
114
115 // Add to the watchonly wallet
116 if (auto spkm_res = watchonly_wallet->AddWalletDescriptor(w_desc, dummy_keys, /*label=*/"", /*internal=*/false); !spkm_res) {
117 return util::Error{util::ErrorString(spkm_res)};
118 }
119
120 // Set active spkms as active
121 if (desc_info.active) {
122 // Determine whether this descriptor is internal
123 // This is only set for active spkms
124 bool internal = false;
125 if (desc_info.internal) {
126 internal = *desc_info.internal;
127 }
128 watchonly_wallet->AddActiveScriptPubKeyMan(desc_id, *Assert(w_desc.descriptor->GetOutputType()), internal);
129 }
130 }
131
132 // Copy locked coins that are persisted
133 for (const auto& [coin, persisted] : wallet.m_locked_coins) {
134 if (!persisted) continue;
135 watchonly_wallet->LockCoin(coin, persisted);
136 }
137
138 {
139 // Make a WalletBatch for the watchonly wallet so that everything else can be written atomically
140 WalletBatch watchonly_batch(watchonly_wallet->GetDatabase());
141 if (!watchonly_batch.TxnBegin()) {
142 return util::Error{strprintf(_("Error: database transaction cannot be executed for new watchonly wallet %s"), watchonly_wallet->GetName())};
143 }
144
145 // Copy orderPosNext
146 watchonly_batch.WriteOrderPosNext(wallet.nOrderPosNext);
147
148 // Write the best block locator to avoid rescanning on reload
149 CBlockLocator best_block_locator;
150 {
151 WalletBatch local_wallet_batch(wallet.GetDatabase());
152 if (!local_wallet_batch.ReadBestBlock(best_block_locator)) {
153 return util::Error{_("Error: Unable to read wallet's best block locator record")};
154 }
155 }
156 if (!watchonly_batch.WriteBestBlock(best_block_locator)) {
157 return util::Error{_("Error: Unable to write watchonly wallet best block locator record")};
158 }
159
160 // Copy the transactions
161 for (const auto& [txid, wtx] : wallet.mapWallet) {
162 if (!watchonly_wallet->LoadToWallet(txid, [&](CWalletTx& ins_wtx, bool new_tx) EXCLUSIVE_LOCKS_REQUIRED(watchonly_wallet->cs_wallet) {
163 if (!new_tx) return false;
164 ins_wtx.SetTx(wtx.tx);
165 ins_wtx.CopyFrom(wtx);
166 return true;
167 })) {
168 return util::Error{strprintf(_("Error: Could not add tx %s to watchonly wallet"), txid.GetHex())};
169 }
170 watchonly_batch.WriteTx(watchonly_wallet->mapWallet.at(txid));
171 }
172
173 // Copy address book
174 for (const auto& [dest, entry] : wallet.m_address_book) {
175 auto address{EncodeDestination(dest)};
176 if (entry.purpose) watchonly_batch.WritePurpose(address, PurposeToString(*entry.purpose));
177 if (entry.label) watchonly_batch.WriteName(address, *entry.label);
178 for (const auto& [id, request] : entry.receive_requests) {
179 watchonly_batch.WriteAddressReceiveRequest(dest, id, request);
180 }
181 if (entry.previously_spent) watchonly_batch.WriteAddressPreviouslySpent(dest, true);
182 }
183
184 if (!watchonly_batch.TxnCommit()) {
185 return util::Error{_("Error: cannot commit db transaction for watchonly wallet export")};
186 }
187 }
188
189 // Make a backup of this wallet at the specified destination directory
190 if (!watchonly_wallet->BackupWallet(fs::PathToString(destination))) {
191 return util::Error{_("Error: Unable to write the exported wallet")};
192 }
193 success = true;
194 }
195
196 return fs::PathToString(destination);
197}
198} // namespace wallet
#define CHECK_NONFATAL(condition)
Identity function.
Definition: check.h:112
#define Assert(val)
Identity function.
Definition: check.h:116
#define Assume(val)
Assume is the identity function.
Definition: check.h:128
256-bit opaque blob.
Definition: uint256.h:196
The util::Expected class provides a standard way for low-level functions to return either error value...
Definition: expected.h:44
The util::Unexpected class represents an unexpected value stored in util::Expected.
Definition: expected.h:21
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:310
static std::shared_ptr< CWallet > CreateNew(WalletContext &context, const std::string &name, std::unique_ptr< WalletDatabase > database, uint64_t wallet_creation_flags, bool born_encrypted, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: wallet.cpp:3116
A transaction with a bunch of additional info that only the owner cares about.
Definition: transaction.h:192
WalletDescriptor GetWalletDescriptor() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man)
Access to the wallet database.
Definition: walletdb.h:197
bool WriteBestBlock(const CBlockLocator &locator)
Definition: walletdb.cpp:177
bool ReadBestBlock(CBlockLocator &locator)
Definition: walletdb.cpp:183
bool TxnBegin()
Begin a new transaction.
Definition: walletdb.cpp:1260
bool WriteAddressPreviouslySpent(const CTxDestination &dest, bool previously_spent)
Definition: walletdb.cpp:1225
bool TxnCommit()
Commit current transaction.
Definition: walletdb.cpp:1265
bool WriteName(const std::string &strAddress, const std::string &strName)
Definition: walletdb.cpp:76
bool WritePurpose(const std::string &strAddress, const std::string &purpose)
Definition: walletdb.cpp:88
bool WriteOrderPosNext(int64_t nOrderPosNext)
Definition: walletdb.cpp:200
bool WriteTx(const CWalletTx &wtx)
Definition: walletdb.cpp:98
bool WriteAddressReceiveRequest(const CTxDestination &dest, const std::string &id, const std::string &receive_request)
Definition: walletdb.cpp:1231
Descriptor with some wallet metadata.
Definition: walletutil.h:64
std::shared_ptr< Descriptor > descriptor
Definition: walletutil.h:66
DescriptorCache cache
Definition: walletutil.h:72
static UniValue Parse(std::string_view raw, ParamFormat format=ParamFormat::JSON)
Parse string to UniValue or throw runtime_error if string contains invalid JSON.
Definition: client.cpp:402
static bool exists(const path &p)
Definition: fs.h:96
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:162
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:295
std::unique_ptr< Handler > MakeCleanupHandler(std::function< void()> cleanup)
Return handler wrapping a cleanup function.
Definition: interfaces.cpp:42
bilingual_str ErrorString(const Result< T > &result)
Definition: result.h:93
std::unique_ptr< WalletDatabase > MakeInMemoryWalletDatabase()
Definition: sqlite.cpp:729
std::string PurposeToString(AddressPurpose p)
Definition: wallet.h:280
util::Result< std::string > ExportWatchOnlyWallet(const CWallet &wallet, const fs::path &destination, WalletContext &context)
Make a new watchonly wallet file containing the public descriptors from this wallet The exported watc...
Definition: export.cpp:46
util::Expected< std::vector< WalletDescInfo >, std::string > ExportDescriptors(const CWallet &wallet, bool export_private)
Export the descriptors from a wallet so that they can be imported elsewhere.
Definition: export.cpp:18
@ WALLET_FLAG_DISABLE_PRIVATE_KEYS
Definition: walletutil.h:30
Describes a place in the block chain to another node such that if the other node doesn't have the sam...
Definition: block.h:117
std::map< CKeyID, CKey > keys
Bilingual messages:
Definition: translation.h:24
WalletContext struct containing references to state shared between CWallet instances,...
Definition: context.h:36
ArgsManager * args
Definition: context.h:39
#define LOCK(cs)
Definition: sync.h:268
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:299
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:49
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
consteval auto _(util::TranslatedLiteral str)
Definition: translation.h:79
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:82
AssertLockHeld(pool.cs)