/root/bitcoin/src/wallet/scriptpubkeyman.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2019-2022 The Bitcoin Core developers |
2 | | // Distributed under the MIT software license, see the accompanying |
3 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | | |
5 | | #include <hash.h> |
6 | | #include <key_io.h> |
7 | | #include <logging.h> |
8 | | #include <node/types.h> |
9 | | #include <outputtype.h> |
10 | | #include <script/descriptor.h> |
11 | | #include <script/script.h> |
12 | | #include <script/sign.h> |
13 | | #include <script/solver.h> |
14 | | #include <util/bip32.h> |
15 | | #include <util/check.h> |
16 | | #include <util/strencodings.h> |
17 | | #include <util/string.h> |
18 | | #include <util/time.h> |
19 | | #include <util/translation.h> |
20 | | #include <wallet/scriptpubkeyman.h> |
21 | | |
22 | | #include <optional> |
23 | | |
24 | | using common::PSBTError; |
25 | | using util::ToString; |
26 | | |
27 | | namespace wallet { |
28 | | //! Value for the first BIP 32 hardened derivation. Can be used as a bit mask and as a value. See BIP 32 for more details. |
29 | | const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; |
30 | | |
31 | | util::Result<CTxDestination> LegacyScriptPubKeyMan::GetNewDestination(const OutputType type) |
32 | 0 | { |
33 | 0 | if (LEGACY_OUTPUT_TYPES.count(type) == 0) { |
34 | 0 | return util::Error{_("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types")}; |
35 | 0 | } |
36 | 0 | assert(type != OutputType::BECH32M); |
37 | | |
38 | | // Fill-up keypool if needed |
39 | 0 | TopUp(); |
40 | |
|
41 | 0 | LOCK(cs_KeyStore); |
42 | | |
43 | | // Generate a new key that is added to wallet |
44 | 0 | CPubKey new_key; |
45 | 0 | if (!GetKeyFromPool(new_key, type)) { |
46 | 0 | return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")}; |
47 | 0 | } |
48 | 0 | LearnRelatedScripts(new_key, type); |
49 | 0 | return GetDestinationForKey(new_key, type); |
50 | 0 | } |
51 | | |
52 | | typedef std::vector<unsigned char> valtype; |
53 | | |
54 | | namespace { |
55 | | |
56 | | /** |
57 | | * This is an enum that tracks the execution context of a script, similar to |
58 | | * SigVersion in script/interpreter. It is separate however because we want to |
59 | | * distinguish between top-level scriptPubKey execution and P2SH redeemScript |
60 | | * execution (a distinction that has no impact on consensus rules). |
61 | | */ |
62 | | enum class IsMineSigVersion |
63 | | { |
64 | | TOP = 0, //!< scriptPubKey execution |
65 | | P2SH = 1, //!< P2SH redeemScript |
66 | | WITNESS_V0 = 2, //!< P2WSH witness script execution |
67 | | }; |
68 | | |
69 | | /** |
70 | | * This is an internal representation of isminetype + invalidity. |
71 | | * Its order is significant, as we return the max of all explored |
72 | | * possibilities. |
73 | | */ |
74 | | enum class IsMineResult |
75 | | { |
76 | | NO = 0, //!< Not ours |
77 | | WATCH_ONLY = 1, //!< Included in watch-only balance |
78 | | SPENDABLE = 2, //!< Included in all balances |
79 | | INVALID = 3, //!< Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness) |
80 | | }; |
81 | | |
82 | | bool PermitsUncompressed(IsMineSigVersion sigversion) |
83 | 0 | { |
84 | 0 | return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH; |
85 | 0 | } |
86 | | |
87 | | bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyDataSPKM& keystore) |
88 | 0 | { |
89 | 0 | for (const valtype& pubkey : pubkeys) { |
90 | 0 | CKeyID keyID = CPubKey(pubkey).GetID(); |
91 | 0 | if (!keystore.HaveKey(keyID)) return false; |
92 | 0 | } |
93 | 0 | return true; |
94 | 0 | } |
95 | | |
96 | | //! Recursively solve script and return spendable/watchonly/invalid status. |
97 | | //! |
98 | | //! @param keystore legacy key and script store |
99 | | //! @param scriptPubKey script to solve |
100 | | //! @param sigversion script type (top-level / redeemscript / witnessscript) |
101 | | //! @param recurse_scripthash whether to recurse into nested p2sh and p2wsh |
102 | | //! scripts or simply treat any script that has been |
103 | | //! stored in the keystore as spendable |
104 | | // NOLINTNEXTLINE(misc-no-recursion) |
105 | | IsMineResult IsMineInner(const LegacyDataSPKM& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion, bool recurse_scripthash=true) |
106 | 0 | { |
107 | 0 | IsMineResult ret = IsMineResult::NO; |
108 | |
|
109 | 0 | std::vector<valtype> vSolutions; |
110 | 0 | TxoutType whichType = Solver(scriptPubKey, vSolutions); |
111 | |
|
112 | 0 | CKeyID keyID; |
113 | 0 | switch (whichType) { |
114 | 0 | case TxoutType::NONSTANDARD: |
115 | 0 | case TxoutType::NULL_DATA: |
116 | 0 | case TxoutType::WITNESS_UNKNOWN: |
117 | 0 | case TxoutType::WITNESS_V1_TAPROOT: |
118 | 0 | case TxoutType::ANCHOR: |
119 | 0 | break; |
120 | 0 | case TxoutType::PUBKEY: |
121 | 0 | keyID = CPubKey(vSolutions[0]).GetID(); |
122 | 0 | if (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) { |
123 | 0 | return IsMineResult::INVALID; |
124 | 0 | } |
125 | 0 | if (keystore.HaveKey(keyID)) { |
126 | 0 | ret = std::max(ret, IsMineResult::SPENDABLE); |
127 | 0 | } |
128 | 0 | break; |
129 | 0 | case TxoutType::WITNESS_V0_KEYHASH: |
130 | 0 | { |
131 | 0 | if (sigversion == IsMineSigVersion::WITNESS_V0) { |
132 | | // P2WPKH inside P2WSH is invalid. |
133 | 0 | return IsMineResult::INVALID; |
134 | 0 | } |
135 | 0 | if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { |
136 | | // We do not support bare witness outputs unless the P2SH version of it would be |
137 | | // acceptable as well. This protects against matching before segwit activates. |
138 | | // This also applies to the P2WSH case. |
139 | 0 | break; |
140 | 0 | } |
141 | 0 | ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(PKHash(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0)); |
142 | 0 | break; |
143 | 0 | } |
144 | 0 | case TxoutType::PUBKEYHASH: |
145 | 0 | keyID = CKeyID(uint160(vSolutions[0])); |
146 | 0 | if (!PermitsUncompressed(sigversion)) { |
147 | 0 | CPubKey pubkey; |
148 | 0 | if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { |
149 | 0 | return IsMineResult::INVALID; |
150 | 0 | } |
151 | 0 | } |
152 | 0 | if (keystore.HaveKey(keyID)) { |
153 | 0 | ret = std::max(ret, IsMineResult::SPENDABLE); |
154 | 0 | } |
155 | 0 | break; |
156 | 0 | case TxoutType::SCRIPTHASH: |
157 | 0 | { |
158 | 0 | if (sigversion != IsMineSigVersion::TOP) { |
159 | | // P2SH inside P2WSH or P2SH is invalid. |
160 | 0 | return IsMineResult::INVALID; |
161 | 0 | } |
162 | 0 | CScriptID scriptID = CScriptID(uint160(vSolutions[0])); |
163 | 0 | CScript subscript; |
164 | 0 | if (keystore.GetCScript(scriptID, subscript)) { |
165 | 0 | ret = std::max(ret, recurse_scripthash ? IsMineInner(keystore, subscript, IsMineSigVersion::P2SH) : IsMineResult::SPENDABLE); |
166 | 0 | } |
167 | 0 | break; |
168 | 0 | } |
169 | 0 | case TxoutType::WITNESS_V0_SCRIPTHASH: |
170 | 0 | { |
171 | 0 | if (sigversion == IsMineSigVersion::WITNESS_V0) { |
172 | | // P2WSH inside P2WSH is invalid. |
173 | 0 | return IsMineResult::INVALID; |
174 | 0 | } |
175 | 0 | if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { |
176 | 0 | break; |
177 | 0 | } |
178 | 0 | CScriptID scriptID{RIPEMD160(vSolutions[0])}; |
179 | 0 | CScript subscript; |
180 | 0 | if (keystore.GetCScript(scriptID, subscript)) { |
181 | 0 | ret = std::max(ret, recurse_scripthash ? IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0) : IsMineResult::SPENDABLE); |
182 | 0 | } |
183 | 0 | break; |
184 | 0 | } |
185 | | |
186 | 0 | case TxoutType::MULTISIG: |
187 | 0 | { |
188 | | // Never treat bare multisig outputs as ours (they can still be made watchonly-though) |
189 | 0 | if (sigversion == IsMineSigVersion::TOP) { |
190 | 0 | break; |
191 | 0 | } |
192 | | |
193 | | // Only consider transactions "mine" if we own ALL the |
194 | | // keys involved. Multi-signature transactions that are |
195 | | // partially owned (somebody else has a key that can spend |
196 | | // them) enable spend-out-from-under-you attacks, especially |
197 | | // in shared-wallet situations. |
198 | 0 | std::vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); |
199 | 0 | if (!PermitsUncompressed(sigversion)) { |
200 | 0 | for (size_t i = 0; i < keys.size(); i++) { |
201 | 0 | if (keys[i].size() != 33) { |
202 | 0 | return IsMineResult::INVALID; |
203 | 0 | } |
204 | 0 | } |
205 | 0 | } |
206 | 0 | if (HaveKeys(keys, keystore)) { |
207 | 0 | ret = std::max(ret, IsMineResult::SPENDABLE); |
208 | 0 | } |
209 | 0 | break; |
210 | 0 | } |
211 | 0 | } // no default case, so the compiler can warn about missing cases |
212 | | |
213 | 0 | if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) { |
214 | 0 | ret = std::max(ret, IsMineResult::WATCH_ONLY); |
215 | 0 | } |
216 | 0 | return ret; |
217 | 0 | } |
218 | | |
219 | | } // namespace |
220 | | |
221 | | isminetype LegacyDataSPKM::IsMine(const CScript& script) const |
222 | 0 | { |
223 | 0 | switch (IsMineInner(*this, script, IsMineSigVersion::TOP)) { |
224 | 0 | case IsMineResult::INVALID: |
225 | 0 | case IsMineResult::NO: |
226 | 0 | return ISMINE_NO; |
227 | 0 | case IsMineResult::WATCH_ONLY: |
228 | 0 | return ISMINE_WATCH_ONLY; |
229 | 0 | case IsMineResult::SPENDABLE: |
230 | 0 | return ISMINE_SPENDABLE; |
231 | 0 | } |
232 | 0 | assert(false); |
233 | 0 | } |
234 | | |
235 | | bool LegacyDataSPKM::CheckDecryptionKey(const CKeyingMaterial& master_key) |
236 | 0 | { |
237 | 0 | { |
238 | 0 | LOCK(cs_KeyStore); |
239 | 0 | assert(mapKeys.empty()); |
240 | | |
241 | 0 | bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys |
242 | 0 | bool keyFail = false; |
243 | 0 | CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); |
244 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
245 | 0 | for (; mi != mapCryptedKeys.end(); ++mi) |
246 | 0 | { |
247 | 0 | const CPubKey &vchPubKey = (*mi).second.first; |
248 | 0 | const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; |
249 | 0 | CKey key; |
250 | 0 | if (!DecryptKey(master_key, vchCryptedSecret, vchPubKey, key)) |
251 | 0 | { |
252 | 0 | keyFail = true; |
253 | 0 | break; |
254 | 0 | } |
255 | 0 | keyPass = true; |
256 | 0 | if (fDecryptionThoroughlyChecked) |
257 | 0 | break; |
258 | 0 | else { |
259 | | // Rewrite these encrypted keys with checksums |
260 | 0 | batch.WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); |
261 | 0 | } |
262 | 0 | } |
263 | 0 | if (keyPass && keyFail) |
264 | 0 | { |
265 | 0 | LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n"); |
266 | 0 | throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt."); |
267 | 0 | } |
268 | 0 | if (keyFail || !keyPass) |
269 | 0 | return false; |
270 | 0 | fDecryptionThoroughlyChecked = true; |
271 | 0 | } |
272 | 0 | return true; |
273 | 0 | } |
274 | | |
275 | | bool LegacyScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) |
276 | 0 | { |
277 | 0 | LOCK(cs_KeyStore); |
278 | 0 | encrypted_batch = batch; |
279 | 0 | if (!mapCryptedKeys.empty()) { |
280 | 0 | encrypted_batch = nullptr; |
281 | 0 | return false; |
282 | 0 | } |
283 | | |
284 | 0 | KeyMap keys_to_encrypt; |
285 | 0 | keys_to_encrypt.swap(mapKeys); // Clear mapKeys so AddCryptedKeyInner will succeed. |
286 | 0 | for (const KeyMap::value_type& mKey : keys_to_encrypt) |
287 | 0 | { |
288 | 0 | const CKey &key = mKey.second; |
289 | 0 | CPubKey vchPubKey = key.GetPubKey(); |
290 | 0 | CKeyingMaterial vchSecret{UCharCast(key.begin()), UCharCast(key.end())}; |
291 | 0 | std::vector<unsigned char> vchCryptedSecret; |
292 | 0 | if (!EncryptSecret(master_key, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) { |
293 | 0 | encrypted_batch = nullptr; |
294 | 0 | return false; |
295 | 0 | } |
296 | 0 | if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) { |
297 | 0 | encrypted_batch = nullptr; |
298 | 0 | return false; |
299 | 0 | } |
300 | 0 | } |
301 | 0 | encrypted_batch = nullptr; |
302 | 0 | return true; |
303 | 0 | } |
304 | | |
305 | | util::Result<CTxDestination> LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) |
306 | 0 | { |
307 | 0 | if (LEGACY_OUTPUT_TYPES.count(type) == 0) { |
308 | 0 | return util::Error{_("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types")}; |
309 | 0 | } |
310 | 0 | assert(type != OutputType::BECH32M); |
311 | | |
312 | 0 | LOCK(cs_KeyStore); |
313 | 0 | if (!CanGetAddresses(internal)) { |
314 | 0 | return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")}; |
315 | 0 | } |
316 | | |
317 | | // Fill-up keypool if needed |
318 | 0 | TopUp(); |
319 | |
|
320 | 0 | if (!ReserveKeyFromKeyPool(index, keypool, internal)) { |
321 | 0 | return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")}; |
322 | 0 | } |
323 | 0 | return GetDestinationForKey(keypool.vchPubKey, type); |
324 | 0 | } |
325 | | |
326 | | bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal) |
327 | 0 | { |
328 | 0 | LOCK(cs_KeyStore); |
329 | |
|
330 | 0 | auto it = m_inactive_hd_chains.find(seed_id); |
331 | 0 | if (it == m_inactive_hd_chains.end()) { |
332 | 0 | return false; |
333 | 0 | } |
334 | | |
335 | 0 | CHDChain& chain = it->second; |
336 | |
|
337 | 0 | if (internal) { |
338 | 0 | chain.m_next_internal_index = std::max(chain.m_next_internal_index, index + 1); |
339 | 0 | } else { |
340 | 0 | chain.m_next_external_index = std::max(chain.m_next_external_index, index + 1); |
341 | 0 | } |
342 | |
|
343 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
344 | 0 | TopUpChain(batch, chain, 0); |
345 | |
|
346 | 0 | return true; |
347 | 0 | } |
348 | | |
349 | | std::vector<WalletDestination> LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script) |
350 | 0 | { |
351 | 0 | LOCK(cs_KeyStore); |
352 | 0 | std::vector<WalletDestination> result; |
353 | | // extract addresses and check if they match with an unused keypool key |
354 | 0 | for (const auto& keyid : GetAffectedKeys(script, *this)) { |
355 | 0 | std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid); |
356 | 0 | if (mi != m_pool_key_to_index.end()) { |
357 | 0 | WalletLogPrintf("%s: Detected a used keypool key, mark all keypool keys up to this key as used\n", __func__); |
358 | 0 | for (const auto& keypool : MarkReserveKeysAsUsed(mi->second)) { |
359 | | // derive all possible destinations as any of them could have been used |
360 | 0 | for (const auto& type : LEGACY_OUTPUT_TYPES) { |
361 | 0 | const auto& dest = GetDestinationForKey(keypool.vchPubKey, type); |
362 | 0 | result.push_back({dest, keypool.fInternal}); |
363 | 0 | } |
364 | 0 | } |
365 | |
|
366 | 0 | if (!TopUp()) { |
367 | 0 | WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__); |
368 | 0 | } |
369 | 0 | } |
370 | | |
371 | | // Find the key's metadata and check if it's seed id (if it has one) is inactive, i.e. it is not the current m_hd_chain seed id. |
372 | | // If so, TopUp the inactive hd chain |
373 | 0 | auto it = mapKeyMetadata.find(keyid); |
374 | 0 | if (it != mapKeyMetadata.end()){ |
375 | 0 | CKeyMetadata meta = it->second; |
376 | 0 | if (!meta.hd_seed_id.IsNull() && meta.hd_seed_id != m_hd_chain.seed_id) { |
377 | 0 | std::vector<uint32_t> path; |
378 | 0 | if (meta.has_key_origin) { |
379 | 0 | path = meta.key_origin.path; |
380 | 0 | } else if (!ParseHDKeypath(meta.hdKeypath, path)) { |
381 | 0 | WalletLogPrintf("%s: Adding inactive seed keys failed, invalid hdKeypath: %s\n", |
382 | 0 | __func__, |
383 | 0 | meta.hdKeypath); |
384 | 0 | } |
385 | 0 | if (path.size() != 3) { |
386 | 0 | WalletLogPrintf("%s: Adding inactive seed keys failed, invalid path size: %d, has_key_origin: %s\n", |
387 | 0 | __func__, |
388 | 0 | path.size(), |
389 | 0 | meta.has_key_origin); |
390 | 0 | } else { |
391 | 0 | bool internal = (path[1] & ~BIP32_HARDENED_KEY_LIMIT) != 0; |
392 | 0 | int64_t index = path[2] & ~BIP32_HARDENED_KEY_LIMIT; |
393 | |
|
394 | 0 | if (!TopUpInactiveHDChain(meta.hd_seed_id, index, internal)) { |
395 | 0 | WalletLogPrintf("%s: Adding inactive seed keys failed\n", __func__); |
396 | 0 | } |
397 | 0 | } |
398 | 0 | } |
399 | 0 | } |
400 | 0 | } |
401 | |
|
402 | 0 | return result; |
403 | 0 | } |
404 | | |
405 | | void LegacyScriptPubKeyMan::UpgradeKeyMetadata() |
406 | 0 | { |
407 | 0 | LOCK(cs_KeyStore); |
408 | 0 | if (m_storage.IsLocked() || m_storage.IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) { |
409 | 0 | return; |
410 | 0 | } |
411 | | |
412 | 0 | std::unique_ptr<WalletBatch> batch = std::make_unique<WalletBatch>(m_storage.GetDatabase()); |
413 | 0 | for (auto& meta_pair : mapKeyMetadata) { |
414 | 0 | CKeyMetadata& meta = meta_pair.second; |
415 | 0 | if (!meta.hd_seed_id.IsNull() && !meta.has_key_origin && meta.hdKeypath != "s") { // If the hdKeypath is "s", that's the seed and it doesn't have a key origin |
416 | 0 | CKey key; |
417 | 0 | GetKey(meta.hd_seed_id, key); |
418 | 0 | CExtKey masterKey; |
419 | 0 | masterKey.SetSeed(key); |
420 | | // Add to map |
421 | 0 | CKeyID master_id = masterKey.key.GetPubKey().GetID(); |
422 | 0 | std::copy(master_id.begin(), master_id.begin() + 4, meta.key_origin.fingerprint); |
423 | 0 | if (!ParseHDKeypath(meta.hdKeypath, meta.key_origin.path)) { |
424 | 0 | throw std::runtime_error("Invalid stored hdKeypath"); |
425 | 0 | } |
426 | 0 | meta.has_key_origin = true; |
427 | 0 | if (meta.nVersion < CKeyMetadata::VERSION_WITH_KEY_ORIGIN) { |
428 | 0 | meta.nVersion = CKeyMetadata::VERSION_WITH_KEY_ORIGIN; |
429 | 0 | } |
430 | | |
431 | | // Write meta to wallet |
432 | 0 | CPubKey pubkey; |
433 | 0 | if (GetPubKey(meta_pair.first, pubkey)) { |
434 | 0 | batch->WriteKeyMetadata(meta, pubkey, true); |
435 | 0 | } |
436 | 0 | } |
437 | 0 | } |
438 | 0 | } |
439 | | |
440 | | bool LegacyScriptPubKeyMan::SetupGeneration(bool force) |
441 | 0 | { |
442 | 0 | if ((CanGenerateKeys() && !force) || m_storage.IsLocked()) { |
443 | 0 | return false; |
444 | 0 | } |
445 | | |
446 | 0 | SetHDSeed(GenerateNewSeed()); |
447 | 0 | if (!NewKeyPool()) { |
448 | 0 | return false; |
449 | 0 | } |
450 | 0 | return true; |
451 | 0 | } |
452 | | |
453 | | bool LegacyScriptPubKeyMan::IsHDEnabled() const |
454 | 0 | { |
455 | 0 | return !m_hd_chain.seed_id.IsNull(); |
456 | 0 | } |
457 | | |
458 | | bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal) const |
459 | 0 | { |
460 | 0 | LOCK(cs_KeyStore); |
461 | | // Check if the keypool has keys |
462 | 0 | bool keypool_has_keys; |
463 | 0 | if (internal && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { |
464 | 0 | keypool_has_keys = setInternalKeyPool.size() > 0; |
465 | 0 | } else { |
466 | 0 | keypool_has_keys = KeypoolCountExternalKeys() > 0; |
467 | 0 | } |
468 | | // If the keypool doesn't have keys, check if we can generate them |
469 | 0 | if (!keypool_has_keys) { |
470 | 0 | return CanGenerateKeys(); |
471 | 0 | } |
472 | 0 | return keypool_has_keys; |
473 | 0 | } |
474 | | |
475 | | bool LegacyScriptPubKeyMan::Upgrade(int prev_version, int new_version, bilingual_str& error) |
476 | 0 | { |
477 | 0 | LOCK(cs_KeyStore); |
478 | |
|
479 | 0 | if (m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { |
480 | | // Nothing to do here if private keys are not enabled |
481 | 0 | return true; |
482 | 0 | } |
483 | | |
484 | 0 | bool hd_upgrade = false; |
485 | 0 | bool split_upgrade = false; |
486 | 0 | if (IsFeatureSupported(new_version, FEATURE_HD) && !IsHDEnabled()) { |
487 | 0 | WalletLogPrintf("Upgrading wallet to HD\n"); |
488 | 0 | m_storage.SetMinVersion(FEATURE_HD); |
489 | | |
490 | | // generate a new master key |
491 | 0 | CPubKey masterPubKey = GenerateNewSeed(); |
492 | 0 | SetHDSeed(masterPubKey); |
493 | 0 | hd_upgrade = true; |
494 | 0 | } |
495 | | // Upgrade to HD chain split if necessary |
496 | 0 | if (!IsFeatureSupported(prev_version, FEATURE_HD_SPLIT) && IsFeatureSupported(new_version, FEATURE_HD_SPLIT)) { |
497 | 0 | WalletLogPrintf("Upgrading wallet to use HD chain split\n"); |
498 | 0 | m_storage.SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL); |
499 | 0 | split_upgrade = FEATURE_HD_SPLIT > prev_version; |
500 | | // Upgrade the HDChain |
501 | 0 | if (m_hd_chain.nVersion < CHDChain::VERSION_HD_CHAIN_SPLIT) { |
502 | 0 | m_hd_chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT; |
503 | 0 | if (!WalletBatch(m_storage.GetDatabase()).WriteHDChain(m_hd_chain)) { |
504 | 0 | throw std::runtime_error(std::string(__func__) + ": writing chain failed"); |
505 | 0 | } |
506 | 0 | } |
507 | 0 | } |
508 | | // Mark all keys currently in the keypool as pre-split |
509 | 0 | if (split_upgrade) { |
510 | 0 | MarkPreSplitKeys(); |
511 | 0 | } |
512 | | // Regenerate the keypool if upgraded to HD |
513 | 0 | if (hd_upgrade) { |
514 | 0 | if (!NewKeyPool()) { |
515 | 0 | error = _("Unable to generate keys"); |
516 | 0 | return false; |
517 | 0 | } |
518 | 0 | } |
519 | 0 | return true; |
520 | 0 | } |
521 | | |
522 | | bool LegacyScriptPubKeyMan::HavePrivateKeys() const |
523 | 0 | { |
524 | 0 | LOCK(cs_KeyStore); |
525 | 0 | return !mapKeys.empty() || !mapCryptedKeys.empty(); |
526 | 0 | } |
527 | | |
528 | | void LegacyScriptPubKeyMan::RewriteDB() |
529 | 0 | { |
530 | 0 | LOCK(cs_KeyStore); |
531 | 0 | setInternalKeyPool.clear(); |
532 | 0 | setExternalKeyPool.clear(); |
533 | 0 | m_pool_key_to_index.clear(); |
534 | | // Note: can't top-up keypool here, because wallet is locked. |
535 | | // User will be prompted to unlock wallet the next operation |
536 | | // that requires a new key. |
537 | 0 | } |
538 | | |
539 | 0 | static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, WalletBatch& batch) { |
540 | 0 | if (setKeyPool.empty()) { |
541 | 0 | return GetTime(); |
542 | 0 | } |
543 | | |
544 | 0 | CKeyPool keypool; |
545 | 0 | int64_t nIndex = *(setKeyPool.begin()); |
546 | 0 | if (!batch.ReadPool(nIndex, keypool)) { |
547 | 0 | throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed"); |
548 | 0 | } |
549 | 0 | assert(keypool.vchPubKey.IsValid()); |
550 | 0 | return keypool.nTime; |
551 | 0 | } |
552 | | |
553 | | std::optional<int64_t> LegacyScriptPubKeyMan::GetOldestKeyPoolTime() const |
554 | 0 | { |
555 | 0 | LOCK(cs_KeyStore); |
556 | |
|
557 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
558 | | |
559 | | // load oldest key from keypool, get time and return |
560 | 0 | int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch); |
561 | 0 | if (IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { |
562 | 0 | oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), oldestKey); |
563 | 0 | if (!set_pre_split_keypool.empty()) { |
564 | 0 | oldestKey = std::max(GetOldestKeyTimeInPool(set_pre_split_keypool, batch), oldestKey); |
565 | 0 | } |
566 | 0 | } |
567 | |
|
568 | 0 | return oldestKey; |
569 | 0 | } |
570 | | |
571 | | size_t LegacyScriptPubKeyMan::KeypoolCountExternalKeys() const |
572 | 0 | { |
573 | 0 | LOCK(cs_KeyStore); |
574 | 0 | return setExternalKeyPool.size() + set_pre_split_keypool.size(); |
575 | 0 | } |
576 | | |
577 | | unsigned int LegacyScriptPubKeyMan::GetKeyPoolSize() const |
578 | 0 | { |
579 | 0 | LOCK(cs_KeyStore); |
580 | 0 | return setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(); |
581 | 0 | } |
582 | | |
583 | | int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const |
584 | 0 | { |
585 | 0 | LOCK(cs_KeyStore); |
586 | 0 | return nTimeFirstKey; |
587 | 0 | } |
588 | | |
589 | | std::unique_ptr<SigningProvider> LegacyDataSPKM::GetSolvingProvider(const CScript& script) const |
590 | 0 | { |
591 | 0 | return std::make_unique<LegacySigningProvider>(*this); |
592 | 0 | } |
593 | | |
594 | | bool LegacyScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& sigdata) |
595 | 0 | { |
596 | 0 | IsMineResult ismine = IsMineInner(*this, script, IsMineSigVersion::TOP, /* recurse_scripthash= */ false); |
597 | 0 | if (ismine == IsMineResult::SPENDABLE || ismine == IsMineResult::WATCH_ONLY) { |
598 | | // If ismine, it means we recognize keys or script ids in the script, or |
599 | | // are watching the script itself, and we can at least provide metadata |
600 | | // or solving information, even if not able to sign fully. |
601 | 0 | return true; |
602 | 0 | } else { |
603 | | // If, given the stuff in sigdata, we could make a valid signature, then we can provide for this script |
604 | 0 | ProduceSignature(*this, DUMMY_SIGNATURE_CREATOR, script, sigdata); |
605 | 0 | if (!sigdata.signatures.empty()) { |
606 | | // If we could make signatures, make sure we have a private key to actually make a signature |
607 | 0 | bool has_privkeys = false; |
608 | 0 | for (const auto& key_sig_pair : sigdata.signatures) { |
609 | 0 | has_privkeys |= HaveKey(key_sig_pair.first); |
610 | 0 | } |
611 | 0 | return has_privkeys; |
612 | 0 | } |
613 | 0 | return false; |
614 | 0 | } |
615 | 0 | } |
616 | | |
617 | | bool LegacyScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const |
618 | 0 | { |
619 | 0 | return ::SignTransaction(tx, this, coins, sighash, input_errors); |
620 | 0 | } |
621 | | |
622 | | SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const |
623 | 0 | { |
624 | 0 | CKey key; |
625 | 0 | if (!GetKey(ToKeyID(pkhash), key)) { |
626 | 0 | return SigningResult::PRIVATE_KEY_NOT_AVAILABLE; |
627 | 0 | } |
628 | | |
629 | 0 | if (MessageSign(key, message, str_sig)) { |
630 | 0 | return SigningResult::OK; |
631 | 0 | } |
632 | 0 | return SigningResult::SIGNING_FAILED; |
633 | 0 | } |
634 | | |
635 | | std::optional<PSBTError> LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const |
636 | 0 | { |
637 | 0 | if (n_signed) { |
638 | 0 | *n_signed = 0; |
639 | 0 | } |
640 | 0 | for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { |
641 | 0 | const CTxIn& txin = psbtx.tx->vin[i]; |
642 | 0 | PSBTInput& input = psbtx.inputs.at(i); |
643 | |
|
644 | 0 | if (PSBTInputSigned(input)) { |
645 | 0 | continue; |
646 | 0 | } |
647 | | |
648 | | // Get the Sighash type |
649 | 0 | if (sign && input.sighash_type != std::nullopt && *input.sighash_type != sighash_type) { |
650 | 0 | return PSBTError::SIGHASH_MISMATCH; |
651 | 0 | } |
652 | | |
653 | | // Check non_witness_utxo has specified prevout |
654 | 0 | if (input.non_witness_utxo) { |
655 | 0 | if (txin.prevout.n >= input.non_witness_utxo->vout.size()) { |
656 | 0 | return PSBTError::MISSING_INPUTS; |
657 | 0 | } |
658 | 0 | } else if (input.witness_utxo.IsNull()) { |
659 | | // There's no UTXO so we can just skip this now |
660 | 0 | continue; |
661 | 0 | } |
662 | 0 | SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, &txdata, sighash_type, nullptr, finalize); |
663 | |
|
664 | 0 | bool signed_one = PSBTInputSigned(input); |
665 | 0 | if (n_signed && (signed_one || !sign)) { |
666 | | // If sign is false, we assume that we _could_ sign if we get here. This |
667 | | // will never have false negatives; it is hard to tell under what i |
668 | | // circumstances it could have false positives. |
669 | 0 | (*n_signed)++; |
670 | 0 | } |
671 | 0 | } |
672 | | |
673 | | // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change |
674 | 0 | for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) { |
675 | 0 | UpdatePSBTOutput(HidingSigningProvider(this, true, !bip32derivs), psbtx, i); |
676 | 0 | } |
677 | |
|
678 | 0 | return {}; |
679 | 0 | } |
680 | | |
681 | | std::unique_ptr<CKeyMetadata> LegacyScriptPubKeyMan::GetMetadata(const CTxDestination& dest) const |
682 | 0 | { |
683 | 0 | LOCK(cs_KeyStore); |
684 | |
|
685 | 0 | CKeyID key_id = GetKeyForDestination(*this, dest); |
686 | 0 | if (!key_id.IsNull()) { |
687 | 0 | auto it = mapKeyMetadata.find(key_id); |
688 | 0 | if (it != mapKeyMetadata.end()) { |
689 | 0 | return std::make_unique<CKeyMetadata>(it->second); |
690 | 0 | } |
691 | 0 | } |
692 | | |
693 | 0 | CScript scriptPubKey = GetScriptForDestination(dest); |
694 | 0 | auto it = m_script_metadata.find(CScriptID(scriptPubKey)); |
695 | 0 | if (it != m_script_metadata.end()) { |
696 | 0 | return std::make_unique<CKeyMetadata>(it->second); |
697 | 0 | } |
698 | | |
699 | 0 | return nullptr; |
700 | 0 | } |
701 | | |
702 | | uint256 LegacyScriptPubKeyMan::GetID() const |
703 | 0 | { |
704 | 0 | return uint256::ONE; |
705 | 0 | } |
706 | | |
707 | | /** |
708 | | * Update wallet first key creation time. This should be called whenever keys |
709 | | * are added to the wallet, with the oldest key creation time. |
710 | | */ |
711 | | void LegacyScriptPubKeyMan::UpdateTimeFirstKey(int64_t nCreateTime) |
712 | 0 | { |
713 | 0 | AssertLockHeld(cs_KeyStore); |
714 | 0 | if (nCreateTime <= 1) { |
715 | | // Cannot determine birthday information, so set the wallet birthday to |
716 | | // the beginning of time. |
717 | 0 | nTimeFirstKey = 1; |
718 | 0 | } else if (nTimeFirstKey == UNKNOWN_TIME || nCreateTime < nTimeFirstKey) { |
719 | 0 | nTimeFirstKey = nCreateTime; |
720 | 0 | } |
721 | |
|
722 | 0 | NotifyFirstKeyTimeChanged(this, nTimeFirstKey); |
723 | 0 | } |
724 | | |
725 | | bool LegacyDataSPKM::LoadKey(const CKey& key, const CPubKey &pubkey) |
726 | 0 | { |
727 | 0 | return AddKeyPubKeyInner(key, pubkey); |
728 | 0 | } |
729 | | |
730 | | bool LegacyScriptPubKeyMan::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) |
731 | 0 | { |
732 | 0 | LOCK(cs_KeyStore); |
733 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
734 | 0 | return LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(batch, secret, pubkey); |
735 | 0 | } |
736 | | |
737 | | bool LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const CPubKey& pubkey) |
738 | 0 | { |
739 | 0 | AssertLockHeld(cs_KeyStore); |
740 | | |
741 | | // Make sure we aren't adding private keys to private key disabled wallets |
742 | 0 | assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); |
743 | | |
744 | | // FillableSigningProvider has no concept of wallet databases, but calls AddCryptedKey |
745 | | // which is overridden below. To avoid flushes, the database handle is |
746 | | // tunneled through to it. |
747 | 0 | bool needsDB = !encrypted_batch; |
748 | 0 | if (needsDB) { |
749 | 0 | encrypted_batch = &batch; |
750 | 0 | } |
751 | 0 | if (!AddKeyPubKeyInner(secret, pubkey)) { |
752 | 0 | if (needsDB) encrypted_batch = nullptr; |
753 | 0 | return false; |
754 | 0 | } |
755 | 0 | if (needsDB) encrypted_batch = nullptr; |
756 | | |
757 | | // check if we need to remove from watch-only |
758 | 0 | CScript script; |
759 | 0 | script = GetScriptForDestination(PKHash(pubkey)); |
760 | 0 | if (HaveWatchOnly(script)) { |
761 | 0 | RemoveWatchOnly(script); |
762 | 0 | } |
763 | 0 | script = GetScriptForRawPubKey(pubkey); |
764 | 0 | if (HaveWatchOnly(script)) { |
765 | 0 | RemoveWatchOnly(script); |
766 | 0 | } |
767 | |
|
768 | 0 | m_storage.UnsetBlankWalletFlag(batch); |
769 | 0 | if (!m_storage.HasEncryptionKeys()) { |
770 | 0 | return batch.WriteKey(pubkey, |
771 | 0 | secret.GetPrivKey(), |
772 | 0 | mapKeyMetadata[pubkey.GetID()]); |
773 | 0 | } |
774 | 0 | return true; |
775 | 0 | } |
776 | | |
777 | | bool LegacyDataSPKM::LoadCScript(const CScript& redeemScript) |
778 | 0 | { |
779 | | /* A sanity check was added in pull #3843 to avoid adding redeemScripts |
780 | | * that never can be redeemed. However, old wallets may still contain |
781 | | * these. Do not add them to the wallet and warn. */ |
782 | 0 | if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) |
783 | 0 | { |
784 | 0 | std::string strAddr = EncodeDestination(ScriptHash(redeemScript)); |
785 | 0 | WalletLogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr); |
786 | 0 | return true; |
787 | 0 | } |
788 | | |
789 | 0 | return FillableSigningProvider::AddCScript(redeemScript); |
790 | 0 | } |
791 | | |
792 | | void LegacyDataSPKM::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata& meta) |
793 | 0 | { |
794 | 0 | LOCK(cs_KeyStore); |
795 | 0 | mapKeyMetadata[keyID] = meta; |
796 | 0 | } |
797 | | |
798 | | void LegacyScriptPubKeyMan::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata& meta) |
799 | 0 | { |
800 | 0 | LOCK(cs_KeyStore); |
801 | 0 | LegacyDataSPKM::LoadKeyMetadata(keyID, meta); |
802 | 0 | UpdateTimeFirstKey(meta.nCreateTime); |
803 | 0 | } |
804 | | |
805 | | void LegacyDataSPKM::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata& meta) |
806 | 0 | { |
807 | 0 | LOCK(cs_KeyStore); |
808 | 0 | m_script_metadata[script_id] = meta; |
809 | 0 | } |
810 | | |
811 | | void LegacyScriptPubKeyMan::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata& meta) |
812 | 0 | { |
813 | 0 | LOCK(cs_KeyStore); |
814 | 0 | LegacyDataSPKM::LoadScriptMetadata(script_id, meta); |
815 | 0 | UpdateTimeFirstKey(meta.nCreateTime); |
816 | 0 | } |
817 | | |
818 | | bool LegacyDataSPKM::AddKeyPubKeyInner(const CKey& key, const CPubKey& pubkey) |
819 | 0 | { |
820 | 0 | LOCK(cs_KeyStore); |
821 | 0 | return FillableSigningProvider::AddKeyPubKey(key, pubkey); |
822 | 0 | } |
823 | | |
824 | | bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey) |
825 | 0 | { |
826 | 0 | LOCK(cs_KeyStore); |
827 | 0 | if (!m_storage.HasEncryptionKeys()) { |
828 | 0 | return FillableSigningProvider::AddKeyPubKey(key, pubkey); |
829 | 0 | } |
830 | | |
831 | 0 | if (m_storage.IsLocked()) { |
832 | 0 | return false; |
833 | 0 | } |
834 | | |
835 | 0 | std::vector<unsigned char> vchCryptedSecret; |
836 | 0 | CKeyingMaterial vchSecret{UCharCast(key.begin()), UCharCast(key.end())}; |
837 | 0 | if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) { |
838 | 0 | return EncryptSecret(encryption_key, vchSecret, pubkey.GetHash(), vchCryptedSecret); |
839 | 0 | })) { |
840 | 0 | return false; |
841 | 0 | } |
842 | | |
843 | 0 | if (!AddCryptedKey(pubkey, vchCryptedSecret)) { |
844 | 0 | return false; |
845 | 0 | } |
846 | 0 | return true; |
847 | 0 | } |
848 | | |
849 | | bool LegacyDataSPKM::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid) |
850 | 0 | { |
851 | | // Set fDecryptionThoroughlyChecked to false when the checksum is invalid |
852 | 0 | if (!checksum_valid) { |
853 | 0 | fDecryptionThoroughlyChecked = false; |
854 | 0 | } |
855 | |
|
856 | 0 | return AddCryptedKeyInner(vchPubKey, vchCryptedSecret); |
857 | 0 | } |
858 | | |
859 | | bool LegacyDataSPKM::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) |
860 | 0 | { |
861 | 0 | LOCK(cs_KeyStore); |
862 | 0 | assert(mapKeys.empty()); |
863 | | |
864 | 0 | mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); |
865 | 0 | ImplicitlyLearnRelatedKeyScripts(vchPubKey); |
866 | 0 | return true; |
867 | 0 | } |
868 | | |
869 | | bool LegacyScriptPubKeyMan::AddCryptedKey(const CPubKey &vchPubKey, |
870 | | const std::vector<unsigned char> &vchCryptedSecret) |
871 | 0 | { |
872 | 0 | if (!AddCryptedKeyInner(vchPubKey, vchCryptedSecret)) |
873 | 0 | return false; |
874 | 0 | { |
875 | 0 | LOCK(cs_KeyStore); |
876 | 0 | if (encrypted_batch) |
877 | 0 | return encrypted_batch->WriteCryptedKey(vchPubKey, |
878 | 0 | vchCryptedSecret, |
879 | 0 | mapKeyMetadata[vchPubKey.GetID()]); |
880 | 0 | else |
881 | 0 | return WalletBatch(m_storage.GetDatabase()).WriteCryptedKey(vchPubKey, |
882 | 0 | vchCryptedSecret, |
883 | 0 | mapKeyMetadata[vchPubKey.GetID()]); |
884 | 0 | } |
885 | 0 | } |
886 | | |
887 | | bool LegacyDataSPKM::HaveWatchOnly(const CScript &dest) const |
888 | 0 | { |
889 | 0 | LOCK(cs_KeyStore); |
890 | 0 | return setWatchOnly.count(dest) > 0; |
891 | 0 | } |
892 | | |
893 | | bool LegacyDataSPKM::HaveWatchOnly() const |
894 | 0 | { |
895 | 0 | LOCK(cs_KeyStore); |
896 | 0 | return (!setWatchOnly.empty()); |
897 | 0 | } |
898 | | |
899 | | static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut) |
900 | 0 | { |
901 | 0 | std::vector<std::vector<unsigned char>> solutions; |
902 | 0 | return Solver(dest, solutions) == TxoutType::PUBKEY && |
903 | 0 | (pubKeyOut = CPubKey(solutions[0])).IsFullyValid(); |
904 | 0 | } |
905 | | |
906 | | bool LegacyScriptPubKeyMan::RemoveWatchOnly(const CScript &dest) |
907 | 0 | { |
908 | 0 | { |
909 | 0 | LOCK(cs_KeyStore); |
910 | 0 | setWatchOnly.erase(dest); |
911 | 0 | CPubKey pubKey; |
912 | 0 | if (ExtractPubKey(dest, pubKey)) { |
913 | 0 | mapWatchKeys.erase(pubKey.GetID()); |
914 | 0 | } |
915 | | // Related CScripts are not removed; having superfluous scripts around is |
916 | | // harmless (see comment in ImplicitlyLearnRelatedKeyScripts). |
917 | 0 | } |
918 | |
|
919 | 0 | if (!HaveWatchOnly()) |
920 | 0 | NotifyWatchonlyChanged(false); |
921 | 0 | if (!WalletBatch(m_storage.GetDatabase()).EraseWatchOnly(dest)) |
922 | 0 | return false; |
923 | | |
924 | 0 | return true; |
925 | 0 | } |
926 | | |
927 | | bool LegacyDataSPKM::LoadWatchOnly(const CScript &dest) |
928 | 0 | { |
929 | 0 | return AddWatchOnlyInMem(dest); |
930 | 0 | } |
931 | | |
932 | | bool LegacyDataSPKM::AddWatchOnlyInMem(const CScript &dest) |
933 | 0 | { |
934 | 0 | LOCK(cs_KeyStore); |
935 | 0 | setWatchOnly.insert(dest); |
936 | 0 | CPubKey pubKey; |
937 | 0 | if (ExtractPubKey(dest, pubKey)) { |
938 | 0 | mapWatchKeys[pubKey.GetID()] = pubKey; |
939 | 0 | ImplicitlyLearnRelatedKeyScripts(pubKey); |
940 | 0 | } |
941 | 0 | return true; |
942 | 0 | } |
943 | | |
944 | | bool LegacyScriptPubKeyMan::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) |
945 | 0 | { |
946 | 0 | if (!AddWatchOnlyInMem(dest)) |
947 | 0 | return false; |
948 | 0 | const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)]; |
949 | 0 | UpdateTimeFirstKey(meta.nCreateTime); |
950 | 0 | NotifyWatchonlyChanged(true); |
951 | 0 | if (batch.WriteWatchOnly(dest, meta)) { |
952 | 0 | m_storage.UnsetBlankWalletFlag(batch); |
953 | 0 | return true; |
954 | 0 | } |
955 | 0 | return false; |
956 | 0 | } |
957 | | |
958 | | bool LegacyScriptPubKeyMan::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) |
959 | 0 | { |
960 | 0 | m_script_metadata[CScriptID(dest)].nCreateTime = create_time; |
961 | 0 | return AddWatchOnlyWithDB(batch, dest); |
962 | 0 | } |
963 | | |
964 | | bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript& dest) |
965 | 0 | { |
966 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
967 | 0 | return AddWatchOnlyWithDB(batch, dest); |
968 | 0 | } |
969 | | |
970 | | bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript& dest, int64_t nCreateTime) |
971 | 0 | { |
972 | 0 | m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime; |
973 | 0 | return AddWatchOnly(dest); |
974 | 0 | } |
975 | | |
976 | | void LegacyDataSPKM::LoadHDChain(const CHDChain& chain) |
977 | 0 | { |
978 | 0 | LOCK(cs_KeyStore); |
979 | 0 | m_hd_chain = chain; |
980 | 0 | } |
981 | | |
982 | | void LegacyScriptPubKeyMan::AddHDChain(const CHDChain& chain) |
983 | 0 | { |
984 | 0 | LOCK(cs_KeyStore); |
985 | | // Store the new chain |
986 | 0 | if (!WalletBatch(m_storage.GetDatabase()).WriteHDChain(chain)) { |
987 | 0 | throw std::runtime_error(std::string(__func__) + ": writing chain failed"); |
988 | 0 | } |
989 | | // When there's an old chain, add it as an inactive chain as we are now rotating hd chains |
990 | 0 | if (!m_hd_chain.seed_id.IsNull()) { |
991 | 0 | AddInactiveHDChain(m_hd_chain); |
992 | 0 | } |
993 | |
|
994 | 0 | m_hd_chain = chain; |
995 | 0 | } |
996 | | |
997 | | void LegacyDataSPKM::AddInactiveHDChain(const CHDChain& chain) |
998 | 0 | { |
999 | 0 | LOCK(cs_KeyStore); |
1000 | 0 | assert(!chain.seed_id.IsNull()); |
1001 | 0 | m_inactive_hd_chains[chain.seed_id] = chain; |
1002 | 0 | } |
1003 | | |
1004 | | bool LegacyDataSPKM::HaveKey(const CKeyID &address) const |
1005 | 0 | { |
1006 | 0 | LOCK(cs_KeyStore); |
1007 | 0 | if (!m_storage.HasEncryptionKeys()) { |
1008 | 0 | return FillableSigningProvider::HaveKey(address); |
1009 | 0 | } |
1010 | 0 | return mapCryptedKeys.count(address) > 0; |
1011 | 0 | } |
1012 | | |
1013 | | bool LegacyDataSPKM::GetKey(const CKeyID &address, CKey& keyOut) const |
1014 | 0 | { |
1015 | 0 | LOCK(cs_KeyStore); |
1016 | 0 | if (!m_storage.HasEncryptionKeys()) { |
1017 | 0 | return FillableSigningProvider::GetKey(address, keyOut); |
1018 | 0 | } |
1019 | | |
1020 | 0 | CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); |
1021 | 0 | if (mi != mapCryptedKeys.end()) |
1022 | 0 | { |
1023 | 0 | const CPubKey &vchPubKey = (*mi).second.first; |
1024 | 0 | const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; |
1025 | 0 | return m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) { |
1026 | 0 | return DecryptKey(encryption_key, vchCryptedSecret, vchPubKey, keyOut); |
1027 | 0 | }); |
1028 | 0 | } |
1029 | 0 | return false; |
1030 | 0 | } |
1031 | | |
1032 | | bool LegacyDataSPKM::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const |
1033 | 0 | { |
1034 | 0 | CKeyMetadata meta; |
1035 | 0 | { |
1036 | 0 | LOCK(cs_KeyStore); |
1037 | 0 | auto it = mapKeyMetadata.find(keyID); |
1038 | 0 | if (it == mapKeyMetadata.end()) { |
1039 | 0 | return false; |
1040 | 0 | } |
1041 | 0 | meta = it->second; |
1042 | 0 | } |
1043 | 0 | if (meta.has_key_origin) { |
1044 | 0 | std::copy(meta.key_origin.fingerprint, meta.key_origin.fingerprint + 4, info.fingerprint); |
1045 | 0 | info.path = meta.key_origin.path; |
1046 | 0 | } else { // Single pubkeys get the master fingerprint of themselves |
1047 | 0 | std::copy(keyID.begin(), keyID.begin() + 4, info.fingerprint); |
1048 | 0 | } |
1049 | 0 | return true; |
1050 | 0 | } |
1051 | | |
1052 | | bool LegacyDataSPKM::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const |
1053 | 0 | { |
1054 | 0 | LOCK(cs_KeyStore); |
1055 | 0 | WatchKeyMap::const_iterator it = mapWatchKeys.find(address); |
1056 | 0 | if (it != mapWatchKeys.end()) { |
1057 | 0 | pubkey_out = it->second; |
1058 | 0 | return true; |
1059 | 0 | } |
1060 | 0 | return false; |
1061 | 0 | } |
1062 | | |
1063 | | bool LegacyDataSPKM::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const |
1064 | 0 | { |
1065 | 0 | LOCK(cs_KeyStore); |
1066 | 0 | if (!m_storage.HasEncryptionKeys()) { |
1067 | 0 | if (!FillableSigningProvider::GetPubKey(address, vchPubKeyOut)) { |
1068 | 0 | return GetWatchPubKey(address, vchPubKeyOut); |
1069 | 0 | } |
1070 | 0 | return true; |
1071 | 0 | } |
1072 | | |
1073 | 0 | CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); |
1074 | 0 | if (mi != mapCryptedKeys.end()) |
1075 | 0 | { |
1076 | 0 | vchPubKeyOut = (*mi).second.first; |
1077 | 0 | return true; |
1078 | 0 | } |
1079 | | // Check for watch-only pubkeys |
1080 | 0 | return GetWatchPubKey(address, vchPubKeyOut); |
1081 | 0 | } |
1082 | | |
1083 | | CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, CHDChain& hd_chain, bool internal) |
1084 | 0 | { |
1085 | 0 | assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); |
1086 | 0 | assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)); |
1087 | 0 | AssertLockHeld(cs_KeyStore); |
1088 | 0 | bool fCompressed = m_storage.CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets |
1089 | |
|
1090 | 0 | CKey secret; |
1091 | | |
1092 | | // Create new metadata |
1093 | 0 | int64_t nCreationTime = GetTime(); |
1094 | 0 | CKeyMetadata metadata(nCreationTime); |
1095 | | |
1096 | | // use HD key derivation if HD was enabled during wallet creation and a seed is present |
1097 | 0 | if (IsHDEnabled()) { |
1098 | 0 | DeriveNewChildKey(batch, metadata, secret, hd_chain, (m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); |
1099 | 0 | } else { |
1100 | 0 | secret.MakeNewKey(fCompressed); |
1101 | 0 | } |
1102 | | |
1103 | | // Compressed public keys were introduced in version 0.6.0 |
1104 | 0 | if (fCompressed) { |
1105 | 0 | m_storage.SetMinVersion(FEATURE_COMPRPUBKEY); |
1106 | 0 | } |
1107 | |
|
1108 | 0 | CPubKey pubkey = secret.GetPubKey(); |
1109 | 0 | assert(secret.VerifyPubKey(pubkey)); |
1110 | | |
1111 | 0 | mapKeyMetadata[pubkey.GetID()] = metadata; |
1112 | 0 | UpdateTimeFirstKey(nCreationTime); |
1113 | |
|
1114 | 0 | if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) { |
1115 | 0 | throw std::runtime_error(std::string(__func__) + ": AddKey failed"); |
1116 | 0 | } |
1117 | 0 | return pubkey; |
1118 | 0 | } |
1119 | | |
1120 | | //! Try to derive an extended key, throw if it fails. |
1121 | 0 | static void DeriveExtKey(CExtKey& key_in, unsigned int index, CExtKey& key_out) { |
1122 | 0 | if (!key_in.Derive(key_out, index)) { |
1123 | 0 | throw std::runtime_error("Could not derive extended key"); |
1124 | 0 | } |
1125 | 0 | } |
1126 | | |
1127 | | void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal) |
1128 | 0 | { |
1129 | | // for now we use a fixed keypath scheme of m/0'/0'/k |
1130 | 0 | CKey seed; //seed (256bit) |
1131 | 0 | CExtKey masterKey; //hd master key |
1132 | 0 | CExtKey accountKey; //key at m/0' |
1133 | 0 | CExtKey chainChildKey; //key at m/0'/0' (external) or m/0'/1' (internal) |
1134 | 0 | CExtKey childKey; //key at m/0'/0'/<n>' |
1135 | | |
1136 | | // try to get the seed |
1137 | 0 | if (!GetKey(hd_chain.seed_id, seed)) |
1138 | 0 | throw std::runtime_error(std::string(__func__) + ": seed not found"); |
1139 | | |
1140 | 0 | masterKey.SetSeed(seed); |
1141 | | |
1142 | | // derive m/0' |
1143 | | // use hardened derivation (child keys >= 0x80000000 are hardened after bip32) |
1144 | 0 | DeriveExtKey(masterKey, BIP32_HARDENED_KEY_LIMIT, accountKey); |
1145 | | |
1146 | | // derive m/0'/0' (external chain) OR m/0'/1' (internal chain) |
1147 | 0 | assert(internal ? m_storage.CanSupportFeature(FEATURE_HD_SPLIT) : true); |
1148 | 0 | DeriveExtKey(accountKey, BIP32_HARDENED_KEY_LIMIT+(internal ? 1 : 0), chainChildKey); |
1149 | | |
1150 | | // derive child key at next index, skip keys already known to the wallet |
1151 | 0 | do { |
1152 | | // always derive hardened keys |
1153 | | // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range |
1154 | | // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 |
1155 | 0 | if (internal) { |
1156 | 0 | DeriveExtKey(chainChildKey, hd_chain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT, childKey); |
1157 | 0 | metadata.hdKeypath = "m/0'/1'/" + ToString(hd_chain.nInternalChainCounter) + "'"; |
1158 | 0 | metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); |
1159 | 0 | metadata.key_origin.path.push_back(1 | BIP32_HARDENED_KEY_LIMIT); |
1160 | 0 | metadata.key_origin.path.push_back(hd_chain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT); |
1161 | 0 | hd_chain.nInternalChainCounter++; |
1162 | 0 | } |
1163 | 0 | else { |
1164 | 0 | DeriveExtKey(chainChildKey, hd_chain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT, childKey); |
1165 | 0 | metadata.hdKeypath = "m/0'/0'/" + ToString(hd_chain.nExternalChainCounter) + "'"; |
1166 | 0 | metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); |
1167 | 0 | metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); |
1168 | 0 | metadata.key_origin.path.push_back(hd_chain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); |
1169 | 0 | hd_chain.nExternalChainCounter++; |
1170 | 0 | } |
1171 | 0 | } while (HaveKey(childKey.key.GetPubKey().GetID())); |
1172 | 0 | secret = childKey.key; |
1173 | 0 | metadata.hd_seed_id = hd_chain.seed_id; |
1174 | 0 | CKeyID master_id = masterKey.key.GetPubKey().GetID(); |
1175 | 0 | std::copy(master_id.begin(), master_id.begin() + 4, metadata.key_origin.fingerprint); |
1176 | 0 | metadata.has_key_origin = true; |
1177 | | // update the chain model in the database |
1178 | 0 | if (hd_chain.seed_id == m_hd_chain.seed_id && !batch.WriteHDChain(hd_chain)) |
1179 | 0 | throw std::runtime_error(std::string(__func__) + ": writing HD chain model failed"); |
1180 | 0 | } |
1181 | | |
1182 | | void LegacyDataSPKM::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) |
1183 | 0 | { |
1184 | 0 | LOCK(cs_KeyStore); |
1185 | 0 | if (keypool.m_pre_split) { |
1186 | 0 | set_pre_split_keypool.insert(nIndex); |
1187 | 0 | } else if (keypool.fInternal) { |
1188 | 0 | setInternalKeyPool.insert(nIndex); |
1189 | 0 | } else { |
1190 | 0 | setExternalKeyPool.insert(nIndex); |
1191 | 0 | } |
1192 | 0 | m_max_keypool_index = std::max(m_max_keypool_index, nIndex); |
1193 | 0 | m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex; |
1194 | | |
1195 | | // If no metadata exists yet, create a default with the pool key's |
1196 | | // creation time. Note that this may be overwritten by actually |
1197 | | // stored metadata for that key later, which is fine. |
1198 | 0 | CKeyID keyid = keypool.vchPubKey.GetID(); |
1199 | 0 | if (mapKeyMetadata.count(keyid) == 0) |
1200 | 0 | mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); |
1201 | 0 | } |
1202 | | |
1203 | | bool LegacyScriptPubKeyMan::CanGenerateKeys() const |
1204 | 0 | { |
1205 | | // A wallet can generate keys if it has an HD seed (IsHDEnabled) or it is a non-HD wallet (pre FEATURE_HD) |
1206 | 0 | LOCK(cs_KeyStore); |
1207 | 0 | return IsHDEnabled() || !m_storage.CanSupportFeature(FEATURE_HD); |
1208 | 0 | } |
1209 | | |
1210 | | CPubKey LegacyScriptPubKeyMan::GenerateNewSeed() |
1211 | 0 | { |
1212 | 0 | assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); |
1213 | 0 | CKey key = GenerateRandomKey(); |
1214 | 0 | return DeriveNewSeed(key); |
1215 | 0 | } |
1216 | | |
1217 | | CPubKey LegacyScriptPubKeyMan::DeriveNewSeed(const CKey& key) |
1218 | 0 | { |
1219 | 0 | int64_t nCreationTime = GetTime(); |
1220 | 0 | CKeyMetadata metadata(nCreationTime); |
1221 | | |
1222 | | // calculate the seed |
1223 | 0 | CPubKey seed = key.GetPubKey(); |
1224 | 0 | assert(key.VerifyPubKey(seed)); |
1225 | | |
1226 | | // set the hd keypath to "s" -> Seed, refers the seed to itself |
1227 | 0 | metadata.hdKeypath = "s"; |
1228 | 0 | metadata.has_key_origin = false; |
1229 | 0 | metadata.hd_seed_id = seed.GetID(); |
1230 | |
|
1231 | 0 | { |
1232 | 0 | LOCK(cs_KeyStore); |
1233 | | |
1234 | | // mem store the metadata |
1235 | 0 | mapKeyMetadata[seed.GetID()] = metadata; |
1236 | | |
1237 | | // write the key&metadata to the database |
1238 | 0 | if (!AddKeyPubKey(key, seed)) |
1239 | 0 | throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed"); |
1240 | 0 | } |
1241 | | |
1242 | 0 | return seed; |
1243 | 0 | } |
1244 | | |
1245 | | void LegacyScriptPubKeyMan::SetHDSeed(const CPubKey& seed) |
1246 | 0 | { |
1247 | 0 | LOCK(cs_KeyStore); |
1248 | | // store the keyid (hash160) together with |
1249 | | // the child index counter in the database |
1250 | | // as a hdchain object |
1251 | 0 | CHDChain newHdChain; |
1252 | 0 | newHdChain.nVersion = m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE; |
1253 | 0 | newHdChain.seed_id = seed.GetID(); |
1254 | 0 | AddHDChain(newHdChain); |
1255 | 0 | NotifyCanGetAddressesChanged(); |
1256 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1257 | 0 | m_storage.UnsetBlankWalletFlag(batch); |
1258 | 0 | } |
1259 | | |
1260 | | /** |
1261 | | * Mark old keypool keys as used, |
1262 | | * and generate all new keys |
1263 | | */ |
1264 | | bool LegacyScriptPubKeyMan::NewKeyPool() |
1265 | 0 | { |
1266 | 0 | if (m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { |
1267 | 0 | return false; |
1268 | 0 | } |
1269 | 0 | { |
1270 | 0 | LOCK(cs_KeyStore); |
1271 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1272 | |
|
1273 | 0 | for (const int64_t nIndex : setInternalKeyPool) { |
1274 | 0 | batch.ErasePool(nIndex); |
1275 | 0 | } |
1276 | 0 | setInternalKeyPool.clear(); |
1277 | |
|
1278 | 0 | for (const int64_t nIndex : setExternalKeyPool) { |
1279 | 0 | batch.ErasePool(nIndex); |
1280 | 0 | } |
1281 | 0 | setExternalKeyPool.clear(); |
1282 | |
|
1283 | 0 | for (const int64_t nIndex : set_pre_split_keypool) { |
1284 | 0 | batch.ErasePool(nIndex); |
1285 | 0 | } |
1286 | 0 | set_pre_split_keypool.clear(); |
1287 | |
|
1288 | 0 | m_pool_key_to_index.clear(); |
1289 | |
|
1290 | 0 | if (!TopUp()) { |
1291 | 0 | return false; |
1292 | 0 | } |
1293 | 0 | WalletLogPrintf("LegacyScriptPubKeyMan::NewKeyPool rewrote keypool\n"); |
1294 | 0 | } |
1295 | 0 | return true; |
1296 | 0 | } |
1297 | | |
1298 | | bool LegacyScriptPubKeyMan::TopUp(unsigned int kpSize) |
1299 | 0 | { |
1300 | 0 | if (!CanGenerateKeys()) { |
1301 | 0 | return false; |
1302 | 0 | } |
1303 | | |
1304 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1305 | 0 | if (!batch.TxnBegin()) return false; |
1306 | 0 | if (!TopUpChain(batch, m_hd_chain, kpSize)) { |
1307 | 0 | return false; |
1308 | 0 | } |
1309 | 0 | for (auto& [chain_id, chain] : m_inactive_hd_chains) { |
1310 | 0 | if (!TopUpChain(batch, chain, kpSize)) { |
1311 | 0 | return false; |
1312 | 0 | } |
1313 | 0 | } |
1314 | 0 | if (!batch.TxnCommit()) throw std::runtime_error(strprintf("Error during keypool top up. Cannot commit changes for wallet %s", m_storage.GetDisplayName())); |
1315 | 0 | NotifyCanGetAddressesChanged(); |
1316 | | // Note: Unlike with DescriptorSPKM, LegacySPKM does not need to call |
1317 | | // m_storage.TopUpCallback() as we do not know what new scripts the LegacySPKM is |
1318 | | // watching for. CWallet's scriptPubKey cache is not used for LegacySPKMs. |
1319 | 0 | return true; |
1320 | 0 | } |
1321 | | |
1322 | | bool LegacyScriptPubKeyMan::TopUpChain(WalletBatch& batch, CHDChain& chain, unsigned int kpSize) |
1323 | 0 | { |
1324 | 0 | LOCK(cs_KeyStore); |
1325 | |
|
1326 | 0 | if (m_storage.IsLocked()) return false; |
1327 | | |
1328 | | // Top up key pool |
1329 | 0 | unsigned int nTargetSize; |
1330 | 0 | if (kpSize > 0) { |
1331 | 0 | nTargetSize = kpSize; |
1332 | 0 | } else { |
1333 | 0 | nTargetSize = m_keypool_size; |
1334 | 0 | } |
1335 | 0 | int64_t target = std::max((int64_t) nTargetSize, int64_t{1}); |
1336 | | |
1337 | | // count amount of available keys (internal, external) |
1338 | | // make sure the keypool of external and internal keys fits the user selected target (-keypool) |
1339 | 0 | int64_t missingExternal; |
1340 | 0 | int64_t missingInternal; |
1341 | 0 | if (chain == m_hd_chain) { |
1342 | 0 | missingExternal = std::max(target - (int64_t)setExternalKeyPool.size(), int64_t{0}); |
1343 | 0 | missingInternal = std::max(target - (int64_t)setInternalKeyPool.size(), int64_t{0}); |
1344 | 0 | } else { |
1345 | 0 | missingExternal = std::max(target - (chain.nExternalChainCounter - chain.m_next_external_index), int64_t{0}); |
1346 | 0 | missingInternal = std::max(target - (chain.nInternalChainCounter - chain.m_next_internal_index), int64_t{0}); |
1347 | 0 | } |
1348 | |
|
1349 | 0 | if (!IsHDEnabled() || !m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { |
1350 | | // don't create extra internal keys |
1351 | 0 | missingInternal = 0; |
1352 | 0 | } |
1353 | 0 | bool internal = false; |
1354 | 0 | for (int64_t i = missingInternal + missingExternal; i--;) { |
1355 | 0 | if (i < missingInternal) { |
1356 | 0 | internal = true; |
1357 | 0 | } |
1358 | |
|
1359 | 0 | CPubKey pubkey(GenerateNewKey(batch, chain, internal)); |
1360 | 0 | if (chain == m_hd_chain) { |
1361 | 0 | AddKeypoolPubkeyWithDB(pubkey, internal, batch); |
1362 | 0 | } |
1363 | 0 | } |
1364 | 0 | if (missingInternal + missingExternal > 0) { |
1365 | 0 | if (chain == m_hd_chain) { |
1366 | 0 | WalletLogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(), setInternalKeyPool.size()); |
1367 | 0 | } else { |
1368 | 0 | WalletLogPrintf("inactive seed with id %s added %d external keys, %d internal keys\n", HexStr(chain.seed_id), missingExternal, missingInternal); |
1369 | 0 | } |
1370 | 0 | } |
1371 | 0 | return true; |
1372 | 0 | } |
1373 | | |
1374 | | void LegacyScriptPubKeyMan::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch) |
1375 | 0 | { |
1376 | 0 | LOCK(cs_KeyStore); |
1377 | 0 | assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); // How in the hell did you use so many keys? |
1378 | 0 | int64_t index = ++m_max_keypool_index; |
1379 | 0 | if (!batch.WritePool(index, CKeyPool(pubkey, internal))) { |
1380 | 0 | throw std::runtime_error(std::string(__func__) + ": writing imported pubkey failed"); |
1381 | 0 | } |
1382 | 0 | if (internal) { |
1383 | 0 | setInternalKeyPool.insert(index); |
1384 | 0 | } else { |
1385 | 0 | setExternalKeyPool.insert(index); |
1386 | 0 | } |
1387 | 0 | m_pool_key_to_index[pubkey.GetID()] = index; |
1388 | 0 | } |
1389 | | |
1390 | | void LegacyScriptPubKeyMan::KeepDestination(int64_t nIndex, const OutputType& type) |
1391 | 0 | { |
1392 | 0 | assert(type != OutputType::BECH32M); |
1393 | | // Remove from key pool |
1394 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1395 | 0 | batch.ErasePool(nIndex); |
1396 | 0 | CPubKey pubkey; |
1397 | 0 | bool have_pk = GetPubKey(m_index_to_reserved_key.at(nIndex), pubkey); |
1398 | 0 | assert(have_pk); |
1399 | 0 | LearnRelatedScripts(pubkey, type); |
1400 | 0 | m_index_to_reserved_key.erase(nIndex); |
1401 | 0 | WalletLogPrintf("keypool keep %d\n", nIndex); |
1402 | 0 | } |
1403 | | |
1404 | | void LegacyScriptPubKeyMan::ReturnDestination(int64_t nIndex, bool fInternal, const CTxDestination&) |
1405 | 0 | { |
1406 | | // Return to key pool |
1407 | 0 | { |
1408 | 0 | LOCK(cs_KeyStore); |
1409 | 0 | if (fInternal) { |
1410 | 0 | setInternalKeyPool.insert(nIndex); |
1411 | 0 | } else if (!set_pre_split_keypool.empty()) { |
1412 | 0 | set_pre_split_keypool.insert(nIndex); |
1413 | 0 | } else { |
1414 | 0 | setExternalKeyPool.insert(nIndex); |
1415 | 0 | } |
1416 | 0 | CKeyID& pubkey_id = m_index_to_reserved_key.at(nIndex); |
1417 | 0 | m_pool_key_to_index[pubkey_id] = nIndex; |
1418 | 0 | m_index_to_reserved_key.erase(nIndex); |
1419 | 0 | NotifyCanGetAddressesChanged(); |
1420 | 0 | } |
1421 | 0 | WalletLogPrintf("keypool return %d\n", nIndex); |
1422 | 0 | } |
1423 | | |
1424 | | bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey& result, const OutputType type) |
1425 | 0 | { |
1426 | 0 | assert(type != OutputType::BECH32M); |
1427 | 0 | if (!CanGetAddresses(/*internal=*/ false)) { |
1428 | 0 | return false; |
1429 | 0 | } |
1430 | | |
1431 | 0 | CKeyPool keypool; |
1432 | 0 | { |
1433 | 0 | LOCK(cs_KeyStore); |
1434 | 0 | int64_t nIndex; |
1435 | 0 | if (!ReserveKeyFromKeyPool(nIndex, keypool, /*fRequestedInternal=*/ false) && !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { |
1436 | 0 | if (m_storage.IsLocked()) return false; |
1437 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1438 | 0 | result = GenerateNewKey(batch, m_hd_chain, /*internal=*/ false); |
1439 | 0 | return true; |
1440 | 0 | } |
1441 | 0 | KeepDestination(nIndex, type); |
1442 | 0 | result = keypool.vchPubKey; |
1443 | 0 | } |
1444 | 0 | return true; |
1445 | 0 | } |
1446 | | |
1447 | | bool LegacyScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal) |
1448 | 0 | { |
1449 | 0 | nIndex = -1; |
1450 | 0 | keypool.vchPubKey = CPubKey(); |
1451 | 0 | { |
1452 | 0 | LOCK(cs_KeyStore); |
1453 | |
|
1454 | 0 | bool fReturningInternal = fRequestedInternal; |
1455 | 0 | fReturningInternal &= (IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) || m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); |
1456 | 0 | bool use_split_keypool = set_pre_split_keypool.empty(); |
1457 | 0 | std::set<int64_t>& setKeyPool = use_split_keypool ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool; |
1458 | | |
1459 | | // Get the oldest key |
1460 | 0 | if (setKeyPool.empty()) { |
1461 | 0 | return false; |
1462 | 0 | } |
1463 | | |
1464 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1465 | |
|
1466 | 0 | auto it = setKeyPool.begin(); |
1467 | 0 | nIndex = *it; |
1468 | 0 | setKeyPool.erase(it); |
1469 | 0 | if (!batch.ReadPool(nIndex, keypool)) { |
1470 | 0 | throw std::runtime_error(std::string(__func__) + ": read failed"); |
1471 | 0 | } |
1472 | 0 | CPubKey pk; |
1473 | 0 | if (!GetPubKey(keypool.vchPubKey.GetID(), pk)) { |
1474 | 0 | throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); |
1475 | 0 | } |
1476 | | // If the key was pre-split keypool, we don't care about what type it is |
1477 | 0 | if (use_split_keypool && keypool.fInternal != fReturningInternal) { |
1478 | 0 | throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified"); |
1479 | 0 | } |
1480 | 0 | if (!keypool.vchPubKey.IsValid()) { |
1481 | 0 | throw std::runtime_error(std::string(__func__) + ": keypool entry invalid"); |
1482 | 0 | } |
1483 | | |
1484 | 0 | assert(m_index_to_reserved_key.count(nIndex) == 0); |
1485 | 0 | m_index_to_reserved_key[nIndex] = keypool.vchPubKey.GetID(); |
1486 | 0 | m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); |
1487 | 0 | WalletLogPrintf("keypool reserve %d\n", nIndex); |
1488 | 0 | } |
1489 | 0 | NotifyCanGetAddressesChanged(); |
1490 | 0 | return true; |
1491 | 0 | } |
1492 | | |
1493 | | void LegacyScriptPubKeyMan::LearnRelatedScripts(const CPubKey& key, OutputType type) |
1494 | 0 | { |
1495 | 0 | assert(type != OutputType::BECH32M); |
1496 | 0 | if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) { |
1497 | 0 | CTxDestination witdest = WitnessV0KeyHash(key.GetID()); |
1498 | 0 | CScript witprog = GetScriptForDestination(witdest); |
1499 | | // Make sure the resulting program is solvable. |
1500 | 0 | const auto desc = InferDescriptor(witprog, *this); |
1501 | 0 | assert(desc && desc->IsSolvable()); |
1502 | 0 | AddCScript(witprog); |
1503 | 0 | } |
1504 | 0 | } |
1505 | | |
1506 | | void LegacyScriptPubKeyMan::LearnAllRelatedScripts(const CPubKey& key) |
1507 | 0 | { |
1508 | | // OutputType::P2SH_SEGWIT always adds all necessary scripts for all types. |
1509 | 0 | LearnRelatedScripts(key, OutputType::P2SH_SEGWIT); |
1510 | 0 | } |
1511 | | |
1512 | | std::vector<CKeyPool> LegacyScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id) |
1513 | 0 | { |
1514 | 0 | AssertLockHeld(cs_KeyStore); |
1515 | 0 | bool internal = setInternalKeyPool.count(keypool_id); |
1516 | 0 | if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id)); |
1517 | 0 | std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ? &setExternalKeyPool : &set_pre_split_keypool); |
1518 | 0 | auto it = setKeyPool->begin(); |
1519 | |
|
1520 | 0 | std::vector<CKeyPool> result; |
1521 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1522 | 0 | while (it != std::end(*setKeyPool)) { |
1523 | 0 | const int64_t& index = *(it); |
1524 | 0 | if (index > keypool_id) break; // set*KeyPool is ordered |
1525 | | |
1526 | 0 | CKeyPool keypool; |
1527 | 0 | if (batch.ReadPool(index, keypool)) { //TODO: This should be unnecessary |
1528 | 0 | m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); |
1529 | 0 | } |
1530 | 0 | LearnAllRelatedScripts(keypool.vchPubKey); |
1531 | 0 | batch.ErasePool(index); |
1532 | 0 | WalletLogPrintf("keypool index %d removed\n", index); |
1533 | 0 | it = setKeyPool->erase(it); |
1534 | 0 | result.push_back(std::move(keypool)); |
1535 | 0 | } |
1536 | |
|
1537 | 0 | return result; |
1538 | 0 | } |
1539 | | |
1540 | | std::vector<CKeyID> GetAffectedKeys(const CScript& spk, const SigningProvider& provider) |
1541 | 0 | { |
1542 | 0 | std::vector<CScript> dummy; |
1543 | 0 | FlatSigningProvider out; |
1544 | 0 | InferDescriptor(spk, provider)->Expand(0, DUMMY_SIGNING_PROVIDER, dummy, out); |
1545 | 0 | std::vector<CKeyID> ret; |
1546 | 0 | ret.reserve(out.pubkeys.size()); |
1547 | 0 | for (const auto& entry : out.pubkeys) { |
1548 | 0 | ret.push_back(entry.first); |
1549 | 0 | } |
1550 | 0 | return ret; |
1551 | 0 | } |
1552 | | |
1553 | | void LegacyScriptPubKeyMan::MarkPreSplitKeys() |
1554 | 0 | { |
1555 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1556 | 0 | for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) { |
1557 | 0 | int64_t index = *it; |
1558 | 0 | CKeyPool keypool; |
1559 | 0 | if (!batch.ReadPool(index, keypool)) { |
1560 | 0 | throw std::runtime_error(std::string(__func__) + ": read keypool entry failed"); |
1561 | 0 | } |
1562 | 0 | keypool.m_pre_split = true; |
1563 | 0 | if (!batch.WritePool(index, keypool)) { |
1564 | 0 | throw std::runtime_error(std::string(__func__) + ": writing modified keypool entry failed"); |
1565 | 0 | } |
1566 | 0 | set_pre_split_keypool.insert(index); |
1567 | 0 | it = setExternalKeyPool.erase(it); |
1568 | 0 | } |
1569 | 0 | } |
1570 | | |
1571 | | bool LegacyScriptPubKeyMan::AddCScript(const CScript& redeemScript) |
1572 | 0 | { |
1573 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1574 | 0 | return AddCScriptWithDB(batch, redeemScript); |
1575 | 0 | } |
1576 | | |
1577 | | bool LegacyScriptPubKeyMan::AddCScriptWithDB(WalletBatch& batch, const CScript& redeemScript) |
1578 | 0 | { |
1579 | 0 | if (!FillableSigningProvider::AddCScript(redeemScript)) |
1580 | 0 | return false; |
1581 | 0 | if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) { |
1582 | 0 | m_storage.UnsetBlankWalletFlag(batch); |
1583 | 0 | return true; |
1584 | 0 | } |
1585 | 0 | return false; |
1586 | 0 | } |
1587 | | |
1588 | | bool LegacyScriptPubKeyMan::AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info) |
1589 | 0 | { |
1590 | 0 | LOCK(cs_KeyStore); |
1591 | 0 | std::copy(info.fingerprint, info.fingerprint + 4, mapKeyMetadata[pubkey.GetID()].key_origin.fingerprint); |
1592 | 0 | mapKeyMetadata[pubkey.GetID()].key_origin.path = info.path; |
1593 | 0 | mapKeyMetadata[pubkey.GetID()].has_key_origin = true; |
1594 | 0 | mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path, /*apostrophe=*/true); |
1595 | 0 | return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true); |
1596 | 0 | } |
1597 | | |
1598 | | bool LegacyScriptPubKeyMan::ImportScripts(const std::set<CScript> scripts, int64_t timestamp) |
1599 | 0 | { |
1600 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1601 | 0 | for (const auto& entry : scripts) { |
1602 | 0 | CScriptID id(entry); |
1603 | 0 | if (HaveCScript(id)) { |
1604 | 0 | WalletLogPrintf("Already have script %s, skipping\n", HexStr(entry)); |
1605 | 0 | continue; |
1606 | 0 | } |
1607 | 0 | if (!AddCScriptWithDB(batch, entry)) { |
1608 | 0 | return false; |
1609 | 0 | } |
1610 | | |
1611 | 0 | if (timestamp > 0) { |
1612 | 0 | m_script_metadata[CScriptID(entry)].nCreateTime = timestamp; |
1613 | 0 | } |
1614 | 0 | } |
1615 | 0 | if (timestamp > 0) { |
1616 | 0 | UpdateTimeFirstKey(timestamp); |
1617 | 0 | } |
1618 | |
|
1619 | 0 | return true; |
1620 | 0 | } |
1621 | | |
1622 | | bool LegacyScriptPubKeyMan::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) |
1623 | 0 | { |
1624 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1625 | 0 | for (const auto& entry : privkey_map) { |
1626 | 0 | const CKey& key = entry.second; |
1627 | 0 | CPubKey pubkey = key.GetPubKey(); |
1628 | 0 | const CKeyID& id = entry.first; |
1629 | 0 | assert(key.VerifyPubKey(pubkey)); |
1630 | | // Skip if we already have the key |
1631 | 0 | if (HaveKey(id)) { |
1632 | 0 | WalletLogPrintf("Already have key with pubkey %s, skipping\n", HexStr(pubkey)); |
1633 | 0 | continue; |
1634 | 0 | } |
1635 | 0 | mapKeyMetadata[id].nCreateTime = timestamp; |
1636 | | // If the private key is not present in the wallet, insert it. |
1637 | 0 | if (!AddKeyPubKeyWithDB(batch, key, pubkey)) { |
1638 | 0 | return false; |
1639 | 0 | } |
1640 | 0 | UpdateTimeFirstKey(timestamp); |
1641 | 0 | } |
1642 | 0 | return true; |
1643 | 0 | } |
1644 | | |
1645 | | bool LegacyScriptPubKeyMan::ImportPubKeys(const std::vector<std::pair<CKeyID, bool>>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const int64_t timestamp) |
1646 | 0 | { |
1647 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1648 | 0 | for (const auto& entry : key_origins) { |
1649 | 0 | AddKeyOriginWithDB(batch, entry.second.first, entry.second.second); |
1650 | 0 | } |
1651 | 0 | for (const auto& [id, internal] : ordered_pubkeys) { |
1652 | 0 | auto entry = pubkey_map.find(id); |
1653 | 0 | if (entry == pubkey_map.end()) { |
1654 | 0 | continue; |
1655 | 0 | } |
1656 | 0 | const CPubKey& pubkey = entry->second; |
1657 | 0 | CPubKey temp; |
1658 | 0 | if (GetPubKey(id, temp)) { |
1659 | | // Already have pubkey, skipping |
1660 | 0 | WalletLogPrintf("Already have pubkey %s, skipping\n", HexStr(temp)); |
1661 | 0 | continue; |
1662 | 0 | } |
1663 | 0 | if (!AddWatchOnlyWithDB(batch, GetScriptForRawPubKey(pubkey), timestamp)) { |
1664 | 0 | return false; |
1665 | 0 | } |
1666 | 0 | mapKeyMetadata[id].nCreateTime = timestamp; |
1667 | | |
1668 | | // Add to keypool only works with pubkeys |
1669 | 0 | if (add_keypool) { |
1670 | 0 | AddKeypoolPubkeyWithDB(pubkey, internal, batch); |
1671 | 0 | NotifyCanGetAddressesChanged(); |
1672 | 0 | } |
1673 | 0 | } |
1674 | 0 | return true; |
1675 | 0 | } |
1676 | | |
1677 | | bool LegacyScriptPubKeyMan::ImportScriptPubKeys(const std::set<CScript>& script_pub_keys, const bool have_solving_data, const int64_t timestamp) |
1678 | 0 | { |
1679 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1680 | 0 | for (const CScript& script : script_pub_keys) { |
1681 | 0 | if (!have_solving_data || !IsMine(script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated |
1682 | 0 | if (!AddWatchOnlyWithDB(batch, script, timestamp)) { |
1683 | 0 | return false; |
1684 | 0 | } |
1685 | 0 | } |
1686 | 0 | } |
1687 | 0 | return true; |
1688 | 0 | } |
1689 | | |
1690 | | std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const |
1691 | 0 | { |
1692 | 0 | LOCK(cs_KeyStore); |
1693 | 0 | if (!m_storage.HasEncryptionKeys()) { |
1694 | 0 | return FillableSigningProvider::GetKeys(); |
1695 | 0 | } |
1696 | 0 | std::set<CKeyID> set_address; |
1697 | 0 | for (const auto& mi : mapCryptedKeys) { |
1698 | 0 | set_address.insert(mi.first); |
1699 | 0 | } |
1700 | 0 | return set_address; |
1701 | 0 | } |
1702 | | |
1703 | | std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetScriptPubKeys() const |
1704 | 0 | { |
1705 | 0 | LOCK(cs_KeyStore); |
1706 | 0 | std::unordered_set<CScript, SaltedSipHasher> spks; |
1707 | | |
1708 | | // All keys have at least P2PK and P2PKH |
1709 | 0 | for (const auto& key_pair : mapKeys) { |
1710 | 0 | const CPubKey& pub = key_pair.second.GetPubKey(); |
1711 | 0 | spks.insert(GetScriptForRawPubKey(pub)); |
1712 | 0 | spks.insert(GetScriptForDestination(PKHash(pub))); |
1713 | 0 | } |
1714 | 0 | for (const auto& key_pair : mapCryptedKeys) { |
1715 | 0 | const CPubKey& pub = key_pair.second.first; |
1716 | 0 | spks.insert(GetScriptForRawPubKey(pub)); |
1717 | 0 | spks.insert(GetScriptForDestination(PKHash(pub))); |
1718 | 0 | } |
1719 | | |
1720 | | // For every script in mapScript, only the ISMINE_SPENDABLE ones are being tracked. |
1721 | | // The watchonly ones will be in setWatchOnly which we deal with later |
1722 | | // For all keys, if they have segwit scripts, those scripts will end up in mapScripts |
1723 | 0 | for (const auto& script_pair : mapScripts) { |
1724 | 0 | const CScript& script = script_pair.second; |
1725 | 0 | if (IsMine(script) == ISMINE_SPENDABLE) { |
1726 | | // Add ScriptHash for scripts that are not already P2SH |
1727 | 0 | if (!script.IsPayToScriptHash()) { |
1728 | 0 | spks.insert(GetScriptForDestination(ScriptHash(script))); |
1729 | 0 | } |
1730 | | // For segwit scripts, we only consider them spendable if we have the segwit spk |
1731 | 0 | int wit_ver = -1; |
1732 | 0 | std::vector<unsigned char> witprog; |
1733 | 0 | if (script.IsWitnessProgram(wit_ver, witprog) && wit_ver == 0) { |
1734 | 0 | spks.insert(script); |
1735 | 0 | } |
1736 | 0 | } else { |
1737 | | // Multisigs are special. They don't show up as ISMINE_SPENDABLE unless they are in a P2SH |
1738 | | // So check the P2SH of a multisig to see if we should insert it |
1739 | 0 | std::vector<std::vector<unsigned char>> sols; |
1740 | 0 | TxoutType type = Solver(script, sols); |
1741 | 0 | if (type == TxoutType::MULTISIG) { |
1742 | 0 | CScript ms_spk = GetScriptForDestination(ScriptHash(script)); |
1743 | 0 | if (IsMine(ms_spk) != ISMINE_NO) { |
1744 | 0 | spks.insert(ms_spk); |
1745 | 0 | } |
1746 | 0 | } |
1747 | 0 | } |
1748 | 0 | } |
1749 | | |
1750 | | // All watchonly scripts are raw |
1751 | 0 | for (const CScript& script : setWatchOnly) { |
1752 | | // As the legacy wallet allowed to import any script, we need to verify the validity here. |
1753 | | // LegacyScriptPubKeyMan::IsMine() return 'ISMINE_NO' for invalid or not watched scripts (IsMineResult::INVALID or IsMineResult::NO). |
1754 | | // e.g. a "sh(sh(pkh()))" which legacy wallets allowed to import!. |
1755 | 0 | if (IsMine(script) != ISMINE_NO) spks.insert(script); |
1756 | 0 | } |
1757 | |
|
1758 | 0 | return spks; |
1759 | 0 | } |
1760 | | |
1761 | | std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetNotMineScriptPubKeys() const |
1762 | 0 | { |
1763 | 0 | LOCK(cs_KeyStore); |
1764 | 0 | std::unordered_set<CScript, SaltedSipHasher> spks; |
1765 | 0 | for (const CScript& script : setWatchOnly) { |
1766 | 0 | if (IsMine(script) == ISMINE_NO) spks.insert(script); |
1767 | 0 | } |
1768 | 0 | return spks; |
1769 | 0 | } |
1770 | | |
1771 | | std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor() |
1772 | 0 | { |
1773 | 0 | LOCK(cs_KeyStore); |
1774 | 0 | if (m_storage.IsLocked()) { |
1775 | 0 | return std::nullopt; |
1776 | 0 | } |
1777 | | |
1778 | 0 | MigrationData out; |
1779 | |
|
1780 | 0 | std::unordered_set<CScript, SaltedSipHasher> spks{GetScriptPubKeys()}; |
1781 | | |
1782 | | // Get all key ids |
1783 | 0 | std::set<CKeyID> keyids; |
1784 | 0 | for (const auto& key_pair : mapKeys) { |
1785 | 0 | keyids.insert(key_pair.first); |
1786 | 0 | } |
1787 | 0 | for (const auto& key_pair : mapCryptedKeys) { |
1788 | 0 | keyids.insert(key_pair.first); |
1789 | 0 | } |
1790 | | |
1791 | | // Get key metadata and figure out which keys don't have a seed |
1792 | | // Note that we do not ignore the seeds themselves because they are considered IsMine! |
1793 | 0 | for (auto keyid_it = keyids.begin(); keyid_it != keyids.end();) { |
1794 | 0 | const CKeyID& keyid = *keyid_it; |
1795 | 0 | const auto& it = mapKeyMetadata.find(keyid); |
1796 | 0 | if (it != mapKeyMetadata.end()) { |
1797 | 0 | const CKeyMetadata& meta = it->second; |
1798 | 0 | if (meta.hdKeypath == "s" || meta.hdKeypath == "m") { |
1799 | 0 | keyid_it++; |
1800 | 0 | continue; |
1801 | 0 | } |
1802 | 0 | if (m_hd_chain.seed_id == meta.hd_seed_id || m_inactive_hd_chains.count(meta.hd_seed_id) > 0) { |
1803 | 0 | keyid_it = keyids.erase(keyid_it); |
1804 | 0 | continue; |
1805 | 0 | } |
1806 | 0 | } |
1807 | 0 | keyid_it++; |
1808 | 0 | } |
1809 | |
|
1810 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
1811 | 0 | if (!batch.TxnBegin()) { |
1812 | 0 | LogPrintf("Error generating descriptors for migration, cannot initialize db transaction\n"); |
1813 | 0 | return std::nullopt; |
1814 | 0 | } |
1815 | | |
1816 | | // keyids is now all non-HD keys. Each key will have its own combo descriptor |
1817 | 0 | for (const CKeyID& keyid : keyids) { |
1818 | 0 | CKey key; |
1819 | 0 | if (!GetKey(keyid, key)) { |
1820 | 0 | assert(false); |
1821 | 0 | } |
1822 | | |
1823 | | // Get birthdate from key meta |
1824 | 0 | uint64_t creation_time = 0; |
1825 | 0 | const auto& it = mapKeyMetadata.find(keyid); |
1826 | 0 | if (it != mapKeyMetadata.end()) { |
1827 | 0 | creation_time = it->second.nCreateTime; |
1828 | 0 | } |
1829 | | |
1830 | | // Get the key origin |
1831 | | // Maybe this doesn't matter because floating keys here shouldn't have origins |
1832 | 0 | KeyOriginInfo info; |
1833 | 0 | bool has_info = GetKeyOrigin(keyid, info); |
1834 | 0 | std::string origin_str = has_info ? "[" + HexStr(info.fingerprint) + FormatHDKeypath(info.path) + "]" : ""; |
1835 | | |
1836 | | // Construct the combo descriptor |
1837 | 0 | std::string desc_str = "combo(" + origin_str + HexStr(key.GetPubKey()) + ")"; |
1838 | 0 | FlatSigningProvider keys; |
1839 | 0 | std::string error; |
1840 | 0 | std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, keys, error, false); |
1841 | 0 | CHECK_NONFATAL(descs.size() == 1); // It shouldn't be possible to have an invalid or multipath descriptor |
1842 | 0 | WalletDescriptor w_desc(std::move(descs.at(0)), creation_time, 0, 0, 0); |
1843 | | |
1844 | | // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys |
1845 | 0 | auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0); |
1846 | 0 | WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, key, key.GetPubKey())); |
1847 | 0 | desc_spk_man->TopUpWithDB(batch); |
1848 | 0 | auto desc_spks = desc_spk_man->GetScriptPubKeys(); |
1849 | | |
1850 | | // Remove the scriptPubKeys from our current set |
1851 | 0 | for (const CScript& spk : desc_spks) { |
1852 | 0 | size_t erased = spks.erase(spk); |
1853 | 0 | assert(erased == 1); |
1854 | 0 | assert(IsMine(spk) == ISMINE_SPENDABLE); |
1855 | 0 | } |
1856 | | |
1857 | 0 | out.desc_spkms.push_back(std::move(desc_spk_man)); |
1858 | 0 | } |
1859 | | |
1860 | | // Handle HD keys by using the CHDChains |
1861 | 0 | std::vector<CHDChain> chains; |
1862 | 0 | chains.push_back(m_hd_chain); |
1863 | 0 | for (const auto& chain_pair : m_inactive_hd_chains) { |
1864 | 0 | chains.push_back(chain_pair.second); |
1865 | 0 | } |
1866 | 0 | for (const CHDChain& chain : chains) { |
1867 | 0 | for (int i = 0; i < 2; ++i) { |
1868 | | // Skip if doing internal chain and split chain is not supported |
1869 | 0 | if (chain.seed_id.IsNull() || (i == 1 && !m_storage.CanSupportFeature(FEATURE_HD_SPLIT))) { |
1870 | 0 | continue; |
1871 | 0 | } |
1872 | | // Get the master xprv |
1873 | 0 | CKey seed_key; |
1874 | 0 | if (!GetKey(chain.seed_id, seed_key)) { |
1875 | 0 | assert(false); |
1876 | 0 | } |
1877 | 0 | CExtKey master_key; |
1878 | 0 | master_key.SetSeed(seed_key); |
1879 | | |
1880 | | // Make the combo descriptor |
1881 | 0 | std::string xpub = EncodeExtPubKey(master_key.Neuter()); |
1882 | 0 | std::string desc_str = "combo(" + xpub + "/0h/" + ToString(i) + "h/*h)"; |
1883 | 0 | FlatSigningProvider keys; |
1884 | 0 | std::string error; |
1885 | 0 | std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, keys, error, false); |
1886 | 0 | CHECK_NONFATAL(descs.size() == 1); // It shouldn't be possible to have an invalid or multipath descriptor |
1887 | 0 | uint32_t chain_counter = std::max((i == 1 ? chain.nInternalChainCounter : chain.nExternalChainCounter), (uint32_t)0); |
1888 | 0 | WalletDescriptor w_desc(std::move(descs.at(0)), 0, 0, chain_counter, 0); |
1889 | | |
1890 | | // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys |
1891 | 0 | auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0); |
1892 | 0 | WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, master_key.key, master_key.key.GetPubKey())); |
1893 | 0 | desc_spk_man->TopUpWithDB(batch); |
1894 | 0 | auto desc_spks = desc_spk_man->GetScriptPubKeys(); |
1895 | | |
1896 | | // Remove the scriptPubKeys from our current set |
1897 | 0 | for (const CScript& spk : desc_spks) { |
1898 | 0 | size_t erased = spks.erase(spk); |
1899 | 0 | assert(erased == 1); |
1900 | 0 | assert(IsMine(spk) == ISMINE_SPENDABLE); |
1901 | 0 | } |
1902 | | |
1903 | 0 | out.desc_spkms.push_back(std::move(desc_spk_man)); |
1904 | 0 | } |
1905 | 0 | } |
1906 | | // Add the current master seed to the migration data |
1907 | 0 | if (!m_hd_chain.seed_id.IsNull()) { |
1908 | 0 | CKey seed_key; |
1909 | 0 | if (!GetKey(m_hd_chain.seed_id, seed_key)) { |
1910 | 0 | assert(false); |
1911 | 0 | } |
1912 | 0 | out.master_key.SetSeed(seed_key); |
1913 | 0 | } |
1914 | | |
1915 | | // Handle the rest of the scriptPubKeys which must be imports and may not have all info |
1916 | 0 | for (auto it = spks.begin(); it != spks.end();) { |
1917 | 0 | const CScript& spk = *it; |
1918 | | |
1919 | | // Get birthdate from script meta |
1920 | 0 | uint64_t creation_time = 0; |
1921 | 0 | const auto& mit = m_script_metadata.find(CScriptID(spk)); |
1922 | 0 | if (mit != m_script_metadata.end()) { |
1923 | 0 | creation_time = mit->second.nCreateTime; |
1924 | 0 | } |
1925 | | |
1926 | | // InferDescriptor as that will get us all the solving info if it is there |
1927 | 0 | std::unique_ptr<Descriptor> desc = InferDescriptor(spk, *GetSolvingProvider(spk)); |
1928 | | // Get the private keys for this descriptor |
1929 | 0 | std::vector<CScript> scripts; |
1930 | 0 | FlatSigningProvider keys; |
1931 | 0 | if (!desc->Expand(0, DUMMY_SIGNING_PROVIDER, scripts, keys)) { |
1932 | 0 | assert(false); |
1933 | 0 | } |
1934 | 0 | std::set<CKeyID> privkeyids; |
1935 | 0 | for (const auto& key_orig_pair : keys.origins) { |
1936 | 0 | privkeyids.insert(key_orig_pair.first); |
1937 | 0 | } |
1938 | |
|
1939 | 0 | std::vector<CScript> desc_spks; |
1940 | | |
1941 | | // Make the descriptor string with private keys |
1942 | 0 | std::string desc_str; |
1943 | 0 | bool watchonly = !desc->ToPrivateString(*this, desc_str); |
1944 | 0 | if (watchonly && !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { |
1945 | 0 | out.watch_descs.emplace_back(desc->ToString(), creation_time); |
1946 | | |
1947 | | // Get the scriptPubKeys without writing this to the wallet |
1948 | 0 | FlatSigningProvider provider; |
1949 | 0 | desc->Expand(0, provider, desc_spks, provider); |
1950 | 0 | } else { |
1951 | | // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys |
1952 | 0 | WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); |
1953 | 0 | auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0); |
1954 | 0 | for (const auto& keyid : privkeyids) { |
1955 | 0 | CKey key; |
1956 | 0 | if (!GetKey(keyid, key)) { |
1957 | 0 | continue; |
1958 | 0 | } |
1959 | 0 | WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, key, key.GetPubKey())); |
1960 | 0 | } |
1961 | 0 | desc_spk_man->TopUpWithDB(batch); |
1962 | 0 | auto desc_spks_set = desc_spk_man->GetScriptPubKeys(); |
1963 | 0 | desc_spks.insert(desc_spks.end(), desc_spks_set.begin(), desc_spks_set.end()); |
1964 | |
|
1965 | 0 | out.desc_spkms.push_back(std::move(desc_spk_man)); |
1966 | 0 | } |
1967 | | |
1968 | | // Remove the scriptPubKeys from our current set |
1969 | 0 | for (const CScript& desc_spk : desc_spks) { |
1970 | 0 | auto del_it = spks.find(desc_spk); |
1971 | 0 | assert(del_it != spks.end()); |
1972 | 0 | assert(IsMine(desc_spk) != ISMINE_NO); |
1973 | 0 | it = spks.erase(del_it); |
1974 | 0 | } |
1975 | 0 | } |
1976 | | |
1977 | | // Multisigs are special. They don't show up as ISMINE_SPENDABLE unless they are in a P2SH |
1978 | | // So we have to check if any of our scripts are a multisig and if so, add the P2SH |
1979 | 0 | for (const auto& script_pair : mapScripts) { |
1980 | 0 | const CScript script = script_pair.second; |
1981 | | |
1982 | | // Get birthdate from script meta |
1983 | 0 | uint64_t creation_time = 0; |
1984 | 0 | const auto& it = m_script_metadata.find(CScriptID(script)); |
1985 | 0 | if (it != m_script_metadata.end()) { |
1986 | 0 | creation_time = it->second.nCreateTime; |
1987 | 0 | } |
1988 | |
|
1989 | 0 | std::vector<std::vector<unsigned char>> sols; |
1990 | 0 | TxoutType type = Solver(script, sols); |
1991 | 0 | if (type == TxoutType::MULTISIG) { |
1992 | 0 | CScript sh_spk = GetScriptForDestination(ScriptHash(script)); |
1993 | 0 | CTxDestination witdest = WitnessV0ScriptHash(script); |
1994 | 0 | CScript witprog = GetScriptForDestination(witdest); |
1995 | 0 | CScript sh_wsh_spk = GetScriptForDestination(ScriptHash(witprog)); |
1996 | | |
1997 | | // We only want the multisigs that we have not already seen, i.e. they are not watchonly and not spendable |
1998 | | // For P2SH, a multisig is not ISMINE_NO when: |
1999 | | // * All keys are in the wallet |
2000 | | // * The multisig itself is watch only |
2001 | | // * The P2SH is watch only |
2002 | | // For P2SH-P2WSH, if the script is in the wallet, then it will have the same conditions as P2SH. |
2003 | | // For P2WSH, a multisig is not ISMINE_NO when, other than the P2SH conditions: |
2004 | | // * The P2WSH script is in the wallet and it is being watched |
2005 | 0 | std::vector<std::vector<unsigned char>> keys(sols.begin() + 1, sols.begin() + sols.size() - 1); |
2006 | 0 | if (HaveWatchOnly(sh_spk) || HaveWatchOnly(script) || HaveKeys(keys, *this) || (HaveCScript(CScriptID(witprog)) && HaveWatchOnly(witprog))) { |
2007 | | // The above emulates IsMine for these 3 scriptPubKeys, so double check that by running IsMine |
2008 | 0 | assert(IsMine(sh_spk) != ISMINE_NO || IsMine(witprog) != ISMINE_NO || IsMine(sh_wsh_spk) != ISMINE_NO); |
2009 | 0 | continue; |
2010 | 0 | } |
2011 | 0 | assert(IsMine(sh_spk) == ISMINE_NO && IsMine(witprog) == ISMINE_NO && IsMine(sh_wsh_spk) == ISMINE_NO); |
2012 | | |
2013 | 0 | std::unique_ptr<Descriptor> sh_desc = InferDescriptor(sh_spk, *GetSolvingProvider(sh_spk)); |
2014 | 0 | out.solvable_descs.emplace_back(sh_desc->ToString(), creation_time); |
2015 | |
|
2016 | 0 | const auto desc = InferDescriptor(witprog, *this); |
2017 | 0 | if (desc->IsSolvable()) { |
2018 | 0 | std::unique_ptr<Descriptor> wsh_desc = InferDescriptor(witprog, *GetSolvingProvider(witprog)); |
2019 | 0 | out.solvable_descs.emplace_back(wsh_desc->ToString(), creation_time); |
2020 | 0 | std::unique_ptr<Descriptor> sh_wsh_desc = InferDescriptor(sh_wsh_spk, *GetSolvingProvider(sh_wsh_spk)); |
2021 | 0 | out.solvable_descs.emplace_back(sh_wsh_desc->ToString(), creation_time); |
2022 | 0 | } |
2023 | 0 | } |
2024 | 0 | } |
2025 | | |
2026 | | // Make sure that we have accounted for all scriptPubKeys |
2027 | 0 | assert(spks.size() == 0); |
2028 | | |
2029 | | // Finalize transaction |
2030 | 0 | if (!batch.TxnCommit()) { |
2031 | 0 | LogPrintf("Error generating descriptors for migration, cannot commit db transaction\n"); |
2032 | 0 | return std::nullopt; |
2033 | 0 | } |
2034 | | |
2035 | 0 | return out; |
2036 | 0 | } |
2037 | | |
2038 | | bool LegacyDataSPKM::DeleteRecords() |
2039 | 0 | { |
2040 | 0 | return RunWithinTxn(m_storage.GetDatabase(), /*process_desc=*/"delete legacy records", [&](WalletBatch& batch){ |
2041 | 0 | return DeleteRecordsWithDB(batch); |
2042 | 0 | }); |
2043 | 0 | } |
2044 | | |
2045 | | bool LegacyDataSPKM::DeleteRecordsWithDB(WalletBatch& batch) |
2046 | 0 | { |
2047 | 0 | LOCK(cs_KeyStore); |
2048 | 0 | return batch.EraseRecords(DBKeys::LEGACY_TYPES); |
2049 | 0 | } |
2050 | | |
2051 | | util::Result<CTxDestination> DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type) |
2052 | 0 | { |
2053 | | // Returns true if this descriptor supports getting new addresses. Conditions where we may be unable to fetch them (e.g. locked) are caught later |
2054 | 0 | if (!CanGetAddresses()) { |
2055 | 0 | return util::Error{_("No addresses available")}; |
2056 | 0 | } |
2057 | 0 | { |
2058 | 0 | LOCK(cs_desc_man); |
2059 | 0 | assert(m_wallet_descriptor.descriptor->IsSingleType()); // This is a combo descriptor which should not be an active descriptor |
2060 | 0 | std::optional<OutputType> desc_addr_type = m_wallet_descriptor.descriptor->GetOutputType(); |
2061 | 0 | assert(desc_addr_type); |
2062 | 0 | if (type != *desc_addr_type) { |
2063 | 0 | throw std::runtime_error(std::string(__func__) + ": Types are inconsistent. Stored type does not match type of newly generated address"); |
2064 | 0 | } |
2065 | | |
2066 | 0 | TopUp(); |
2067 | | |
2068 | | // Get the scriptPubKey from the descriptor |
2069 | 0 | FlatSigningProvider out_keys; |
2070 | 0 | std::vector<CScript> scripts_temp; |
2071 | 0 | if (m_wallet_descriptor.range_end <= m_max_cached_index && !TopUp(1)) { |
2072 | | // We can't generate anymore keys |
2073 | 0 | return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")}; |
2074 | 0 | } |
2075 | 0 | if (!m_wallet_descriptor.descriptor->ExpandFromCache(m_wallet_descriptor.next_index, m_wallet_descriptor.cache, scripts_temp, out_keys)) { |
2076 | | // We can't generate anymore keys |
2077 | 0 | return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")}; |
2078 | 0 | } |
2079 | | |
2080 | 0 | CTxDestination dest; |
2081 | 0 | if (!ExtractDestination(scripts_temp[0], dest)) { |
2082 | 0 | return util::Error{_("Error: Cannot extract destination from the generated scriptpubkey")}; // shouldn't happen |
2083 | 0 | } |
2084 | 0 | m_wallet_descriptor.next_index++; |
2085 | 0 | WalletBatch(m_storage.GetDatabase()).WriteDescriptor(GetID(), m_wallet_descriptor); |
2086 | 0 | return dest; |
2087 | 0 | } |
2088 | 0 | } |
2089 | | |
2090 | | isminetype DescriptorScriptPubKeyMan::IsMine(const CScript& script) const |
2091 | 0 | { |
2092 | 0 | LOCK(cs_desc_man); |
2093 | 0 | if (m_map_script_pub_keys.count(script) > 0) { |
2094 | 0 | return ISMINE_SPENDABLE; |
2095 | 0 | } |
2096 | 0 | return ISMINE_NO; |
2097 | 0 | } |
2098 | | |
2099 | | bool DescriptorScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master_key) |
2100 | 0 | { |
2101 | 0 | LOCK(cs_desc_man); |
2102 | 0 | if (!m_map_keys.empty()) { |
2103 | 0 | return false; |
2104 | 0 | } |
2105 | | |
2106 | 0 | bool keyPass = m_map_crypted_keys.empty(); // Always pass when there are no encrypted keys |
2107 | 0 | bool keyFail = false; |
2108 | 0 | for (const auto& mi : m_map_crypted_keys) { |
2109 | 0 | const CPubKey &pubkey = mi.second.first; |
2110 | 0 | const std::vector<unsigned char> &crypted_secret = mi.second.second; |
2111 | 0 | CKey key; |
2112 | 0 | if (!DecryptKey(master_key, crypted_secret, pubkey, key)) { |
2113 | 0 | keyFail = true; |
2114 | 0 | break; |
2115 | 0 | } |
2116 | 0 | keyPass = true; |
2117 | 0 | if (m_decryption_thoroughly_checked) |
2118 | 0 | break; |
2119 | 0 | } |
2120 | 0 | if (keyPass && keyFail) { |
2121 | 0 | LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n"); |
2122 | 0 | throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt."); |
2123 | 0 | } |
2124 | 0 | if (keyFail || !keyPass) { |
2125 | 0 | return false; |
2126 | 0 | } |
2127 | 0 | m_decryption_thoroughly_checked = true; |
2128 | 0 | return true; |
2129 | 0 | } |
2130 | | |
2131 | | bool DescriptorScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) |
2132 | 0 | { |
2133 | 0 | LOCK(cs_desc_man); |
2134 | 0 | if (!m_map_crypted_keys.empty()) { |
2135 | 0 | return false; |
2136 | 0 | } |
2137 | | |
2138 | 0 | for (const KeyMap::value_type& key_in : m_map_keys) |
2139 | 0 | { |
2140 | 0 | const CKey &key = key_in.second; |
2141 | 0 | CPubKey pubkey = key.GetPubKey(); |
2142 | 0 | CKeyingMaterial secret{UCharCast(key.begin()), UCharCast(key.end())}; |
2143 | 0 | std::vector<unsigned char> crypted_secret; |
2144 | 0 | if (!EncryptSecret(master_key, secret, pubkey.GetHash(), crypted_secret)) { |
2145 | 0 | return false; |
2146 | 0 | } |
2147 | 0 | m_map_crypted_keys[pubkey.GetID()] = make_pair(pubkey, crypted_secret); |
2148 | 0 | batch->WriteCryptedDescriptorKey(GetID(), pubkey, crypted_secret); |
2149 | 0 | } |
2150 | 0 | m_map_keys.clear(); |
2151 | 0 | return true; |
2152 | 0 | } |
2153 | | |
2154 | | util::Result<CTxDestination> DescriptorScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) |
2155 | 0 | { |
2156 | 0 | LOCK(cs_desc_man); |
2157 | 0 | auto op_dest = GetNewDestination(type); |
2158 | 0 | index = m_wallet_descriptor.next_index - 1; |
2159 | 0 | return op_dest; |
2160 | 0 | } |
2161 | | |
2162 | | void DescriptorScriptPubKeyMan::ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) |
2163 | 0 | { |
2164 | 0 | LOCK(cs_desc_man); |
2165 | | // Only return when the index was the most recent |
2166 | 0 | if (m_wallet_descriptor.next_index - 1 == index) { |
2167 | 0 | m_wallet_descriptor.next_index--; |
2168 | 0 | } |
2169 | 0 | WalletBatch(m_storage.GetDatabase()).WriteDescriptor(GetID(), m_wallet_descriptor); |
2170 | 0 | NotifyCanGetAddressesChanged(); |
2171 | 0 | } |
2172 | | |
2173 | | std::map<CKeyID, CKey> DescriptorScriptPubKeyMan::GetKeys() const |
2174 | 0 | { |
2175 | 0 | AssertLockHeld(cs_desc_man); |
2176 | 0 | if (m_storage.HasEncryptionKeys() && !m_storage.IsLocked()) { |
2177 | 0 | KeyMap keys; |
2178 | 0 | for (const auto& key_pair : m_map_crypted_keys) { |
2179 | 0 | const CPubKey& pubkey = key_pair.second.first; |
2180 | 0 | const std::vector<unsigned char>& crypted_secret = key_pair.second.second; |
2181 | 0 | CKey key; |
2182 | 0 | m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) { |
2183 | 0 | return DecryptKey(encryption_key, crypted_secret, pubkey, key); |
2184 | 0 | }); |
2185 | 0 | keys[pubkey.GetID()] = key; |
2186 | 0 | } |
2187 | 0 | return keys; |
2188 | 0 | } |
2189 | 0 | return m_map_keys; |
2190 | 0 | } |
2191 | | |
2192 | | bool DescriptorScriptPubKeyMan::HasPrivKey(const CKeyID& keyid) const |
2193 | 0 | { |
2194 | 0 | AssertLockHeld(cs_desc_man); |
2195 | 0 | return m_map_keys.contains(keyid) || m_map_crypted_keys.contains(keyid); |
2196 | 0 | } |
2197 | | |
2198 | | std::optional<CKey> DescriptorScriptPubKeyMan::GetKey(const CKeyID& keyid) const |
2199 | 0 | { |
2200 | 0 | AssertLockHeld(cs_desc_man); |
2201 | 0 | if (m_storage.HasEncryptionKeys() && !m_storage.IsLocked()) { |
2202 | 0 | const auto& it = m_map_crypted_keys.find(keyid); |
2203 | 0 | if (it == m_map_crypted_keys.end()) { |
2204 | 0 | return std::nullopt; |
2205 | 0 | } |
2206 | 0 | const std::vector<unsigned char>& crypted_secret = it->second.second; |
2207 | 0 | CKey key; |
2208 | 0 | if (!Assume(m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) { |
2209 | 0 | return DecryptKey(encryption_key, crypted_secret, it->second.first, key); |
2210 | 0 | }))) { |
2211 | 0 | return std::nullopt; |
2212 | 0 | } |
2213 | 0 | return key; |
2214 | 0 | } |
2215 | 0 | const auto& it = m_map_keys.find(keyid); |
2216 | 0 | if (it == m_map_keys.end()) { |
2217 | 0 | return std::nullopt; |
2218 | 0 | } |
2219 | 0 | return it->second; |
2220 | 0 | } |
2221 | | |
2222 | | bool DescriptorScriptPubKeyMan::TopUp(unsigned int size) |
2223 | 0 | { |
2224 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
2225 | 0 | if (!batch.TxnBegin()) return false; |
2226 | 0 | bool res = TopUpWithDB(batch, size); |
2227 | 0 | if (!batch.TxnCommit()) throw std::runtime_error(strprintf("Error during descriptors keypool top up. Cannot commit changes for wallet %s", m_storage.GetDisplayName())); |
2228 | 0 | return res; |
2229 | 0 | } |
2230 | | |
2231 | | bool DescriptorScriptPubKeyMan::TopUpWithDB(WalletBatch& batch, unsigned int size) |
2232 | 0 | { |
2233 | 0 | LOCK(cs_desc_man); |
2234 | 0 | std::set<CScript> new_spks; |
2235 | 0 | unsigned int target_size; |
2236 | 0 | if (size > 0) { |
2237 | 0 | target_size = size; |
2238 | 0 | } else { |
2239 | 0 | target_size = m_keypool_size; |
2240 | 0 | } |
2241 | | |
2242 | | // Calculate the new range_end |
2243 | 0 | int32_t new_range_end = std::max(m_wallet_descriptor.next_index + (int32_t)target_size, m_wallet_descriptor.range_end); |
2244 | | |
2245 | | // If the descriptor is not ranged, we actually just want to fill the first cache item |
2246 | 0 | if (!m_wallet_descriptor.descriptor->IsRange()) { |
2247 | 0 | new_range_end = 1; |
2248 | 0 | m_wallet_descriptor.range_end = 1; |
2249 | 0 | m_wallet_descriptor.range_start = 0; |
2250 | 0 | } |
2251 | |
|
2252 | 0 | FlatSigningProvider provider; |
2253 | 0 | provider.keys = GetKeys(); |
2254 | |
|
2255 | 0 | uint256 id = GetID(); |
2256 | 0 | for (int32_t i = m_max_cached_index + 1; i < new_range_end; ++i) { |
2257 | 0 | FlatSigningProvider out_keys; |
2258 | 0 | std::vector<CScript> scripts_temp; |
2259 | 0 | DescriptorCache temp_cache; |
2260 | | // Maybe we have a cached xpub and we can expand from the cache first |
2261 | 0 | if (!m_wallet_descriptor.descriptor->ExpandFromCache(i, m_wallet_descriptor.cache, scripts_temp, out_keys)) { |
2262 | 0 | if (!m_wallet_descriptor.descriptor->Expand(i, provider, scripts_temp, out_keys, &temp_cache)) return false; |
2263 | 0 | } |
2264 | | // Add all of the scriptPubKeys to the scriptPubKey set |
2265 | 0 | new_spks.insert(scripts_temp.begin(), scripts_temp.end()); |
2266 | 0 | for (const CScript& script : scripts_temp) { |
2267 | 0 | m_map_script_pub_keys[script] = i; |
2268 | 0 | } |
2269 | 0 | for (const auto& pk_pair : out_keys.pubkeys) { |
2270 | 0 | const CPubKey& pubkey = pk_pair.second; |
2271 | 0 | if (m_map_pubkeys.count(pubkey) != 0) { |
2272 | | // We don't need to give an error here. |
2273 | | // It doesn't matter which of many valid indexes the pubkey has, we just need an index where we can derive it and it's private key |
2274 | 0 | continue; |
2275 | 0 | } |
2276 | 0 | m_map_pubkeys[pubkey] = i; |
2277 | 0 | } |
2278 | | // Merge and write the cache |
2279 | 0 | DescriptorCache new_items = m_wallet_descriptor.cache.MergeAndDiff(temp_cache); |
2280 | 0 | if (!batch.WriteDescriptorCacheItems(id, new_items)) { |
2281 | 0 | throw std::runtime_error(std::string(__func__) + ": writing cache items failed"); |
2282 | 0 | } |
2283 | 0 | m_max_cached_index++; |
2284 | 0 | } |
2285 | 0 | m_wallet_descriptor.range_end = new_range_end; |
2286 | 0 | batch.WriteDescriptor(GetID(), m_wallet_descriptor); |
2287 | | |
2288 | | // By this point, the cache size should be the size of the entire range |
2289 | 0 | assert(m_wallet_descriptor.range_end - 1 == m_max_cached_index); |
2290 | | |
2291 | 0 | m_storage.TopUpCallback(new_spks, this); |
2292 | 0 | NotifyCanGetAddressesChanged(); |
2293 | 0 | return true; |
2294 | 0 | } |
2295 | | |
2296 | | std::vector<WalletDestination> DescriptorScriptPubKeyMan::MarkUnusedAddresses(const CScript& script) |
2297 | 0 | { |
2298 | 0 | LOCK(cs_desc_man); |
2299 | 0 | std::vector<WalletDestination> result; |
2300 | 0 | if (IsMine(script)) { |
2301 | 0 | int32_t index = m_map_script_pub_keys[script]; |
2302 | 0 | if (index >= m_wallet_descriptor.next_index) { |
2303 | 0 | WalletLogPrintf("%s: Detected a used keypool item at index %d, mark all keypool items up to this item as used\n", __func__, index); |
2304 | 0 | auto out_keys = std::make_unique<FlatSigningProvider>(); |
2305 | 0 | std::vector<CScript> scripts_temp; |
2306 | 0 | while (index >= m_wallet_descriptor.next_index) { |
2307 | 0 | if (!m_wallet_descriptor.descriptor->ExpandFromCache(m_wallet_descriptor.next_index, m_wallet_descriptor.cache, scripts_temp, *out_keys)) { |
2308 | 0 | throw std::runtime_error(std::string(__func__) + ": Unable to expand descriptor from cache"); |
2309 | 0 | } |
2310 | 0 | CTxDestination dest; |
2311 | 0 | ExtractDestination(scripts_temp[0], dest); |
2312 | 0 | result.push_back({dest, std::nullopt}); |
2313 | 0 | m_wallet_descriptor.next_index++; |
2314 | 0 | } |
2315 | 0 | } |
2316 | 0 | if (!TopUp()) { |
2317 | 0 | WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__); |
2318 | 0 | } |
2319 | 0 | } |
2320 | | |
2321 | 0 | return result; |
2322 | 0 | } |
2323 | | |
2324 | | void DescriptorScriptPubKeyMan::AddDescriptorKey(const CKey& key, const CPubKey &pubkey) |
2325 | 0 | { |
2326 | 0 | LOCK(cs_desc_man); |
2327 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
2328 | 0 | if (!AddDescriptorKeyWithDB(batch, key, pubkey)) { |
2329 | 0 | throw std::runtime_error(std::string(__func__) + ": writing descriptor private key failed"); |
2330 | 0 | } |
2331 | 0 | } |
2332 | | |
2333 | | bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey) |
2334 | 0 | { |
2335 | 0 | AssertLockHeld(cs_desc_man); |
2336 | 0 | assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); |
2337 | | |
2338 | | // Check if provided key already exists |
2339 | 0 | if (m_map_keys.find(pubkey.GetID()) != m_map_keys.end() || |
2340 | 0 | m_map_crypted_keys.find(pubkey.GetID()) != m_map_crypted_keys.end()) { |
2341 | 0 | return true; |
2342 | 0 | } |
2343 | | |
2344 | 0 | if (m_storage.HasEncryptionKeys()) { |
2345 | 0 | if (m_storage.IsLocked()) { |
2346 | 0 | return false; |
2347 | 0 | } |
2348 | | |
2349 | 0 | std::vector<unsigned char> crypted_secret; |
2350 | 0 | CKeyingMaterial secret{UCharCast(key.begin()), UCharCast(key.end())}; |
2351 | 0 | if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) { |
2352 | 0 | return EncryptSecret(encryption_key, secret, pubkey.GetHash(), crypted_secret); |
2353 | 0 | })) { |
2354 | 0 | return false; |
2355 | 0 | } |
2356 | | |
2357 | 0 | m_map_crypted_keys[pubkey.GetID()] = make_pair(pubkey, crypted_secret); |
2358 | 0 | return batch.WriteCryptedDescriptorKey(GetID(), pubkey, crypted_secret); |
2359 | 0 | } else { |
2360 | 0 | m_map_keys[pubkey.GetID()] = key; |
2361 | 0 | return batch.WriteDescriptorKey(GetID(), pubkey, key.GetPrivKey()); |
2362 | 0 | } |
2363 | 0 | } |
2364 | | |
2365 | | bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(WalletBatch& batch, const CExtKey& master_key, OutputType addr_type, bool internal) |
2366 | 0 | { |
2367 | 0 | LOCK(cs_desc_man); |
2368 | 0 | assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); |
2369 | | |
2370 | | // Ignore when there is already a descriptor |
2371 | 0 | if (m_wallet_descriptor.descriptor) { |
2372 | 0 | return false; |
2373 | 0 | } |
2374 | | |
2375 | 0 | m_wallet_descriptor = GenerateWalletDescriptor(master_key.Neuter(), addr_type, internal); |
2376 | | |
2377 | | // Store the master private key, and descriptor |
2378 | 0 | if (!AddDescriptorKeyWithDB(batch, master_key.key, master_key.key.GetPubKey())) { |
2379 | 0 | throw std::runtime_error(std::string(__func__) + ": writing descriptor master private key failed"); |
2380 | 0 | } |
2381 | 0 | if (!batch.WriteDescriptor(GetID(), m_wallet_descriptor)) { |
2382 | 0 | throw std::runtime_error(std::string(__func__) + ": writing descriptor failed"); |
2383 | 0 | } |
2384 | | |
2385 | | // TopUp |
2386 | 0 | TopUpWithDB(batch); |
2387 | |
|
2388 | 0 | m_storage.UnsetBlankWalletFlag(batch); |
2389 | 0 | return true; |
2390 | 0 | } |
2391 | | |
2392 | | bool DescriptorScriptPubKeyMan::IsHDEnabled() const |
2393 | 0 | { |
2394 | 0 | LOCK(cs_desc_man); |
2395 | 0 | return m_wallet_descriptor.descriptor->IsRange(); |
2396 | 0 | } |
2397 | | |
2398 | | bool DescriptorScriptPubKeyMan::CanGetAddresses(bool internal) const |
2399 | 0 | { |
2400 | | // We can only give out addresses from descriptors that are single type (not combo), ranged, |
2401 | | // and either have cached keys or can generate more keys (ignoring encryption) |
2402 | 0 | LOCK(cs_desc_man); |
2403 | 0 | return m_wallet_descriptor.descriptor->IsSingleType() && |
2404 | 0 | m_wallet_descriptor.descriptor->IsRange() && |
2405 | 0 | (HavePrivateKeys() || m_wallet_descriptor.next_index < m_wallet_descriptor.range_end); |
2406 | 0 | } |
2407 | | |
2408 | | bool DescriptorScriptPubKeyMan::HavePrivateKeys() const |
2409 | 0 | { |
2410 | 0 | LOCK(cs_desc_man); |
2411 | 0 | return m_map_keys.size() > 0 || m_map_crypted_keys.size() > 0; |
2412 | 0 | } |
2413 | | |
2414 | | std::optional<int64_t> DescriptorScriptPubKeyMan::GetOldestKeyPoolTime() const |
2415 | 0 | { |
2416 | | // This is only used for getwalletinfo output and isn't relevant to descriptor wallets. |
2417 | 0 | return std::nullopt; |
2418 | 0 | } |
2419 | | |
2420 | | |
2421 | | unsigned int DescriptorScriptPubKeyMan::GetKeyPoolSize() const |
2422 | 0 | { |
2423 | 0 | LOCK(cs_desc_man); |
2424 | 0 | return m_wallet_descriptor.range_end - m_wallet_descriptor.next_index; |
2425 | 0 | } |
2426 | | |
2427 | | int64_t DescriptorScriptPubKeyMan::GetTimeFirstKey() const |
2428 | 0 | { |
2429 | 0 | LOCK(cs_desc_man); |
2430 | 0 | return m_wallet_descriptor.creation_time; |
2431 | 0 | } |
2432 | | |
2433 | | std::unique_ptr<FlatSigningProvider> DescriptorScriptPubKeyMan::GetSigningProvider(const CScript& script, bool include_private) const |
2434 | 0 | { |
2435 | 0 | LOCK(cs_desc_man); |
2436 | | |
2437 | | // Find the index of the script |
2438 | 0 | auto it = m_map_script_pub_keys.find(script); |
2439 | 0 | if (it == m_map_script_pub_keys.end()) { |
2440 | 0 | return nullptr; |
2441 | 0 | } |
2442 | 0 | int32_t index = it->second; |
2443 | |
|
2444 | 0 | return GetSigningProvider(index, include_private); |
2445 | 0 | } |
2446 | | |
2447 | | std::unique_ptr<FlatSigningProvider> DescriptorScriptPubKeyMan::GetSigningProvider(const CPubKey& pubkey) const |
2448 | 0 | { |
2449 | 0 | LOCK(cs_desc_man); |
2450 | | |
2451 | | // Find index of the pubkey |
2452 | 0 | auto it = m_map_pubkeys.find(pubkey); |
2453 | 0 | if (it == m_map_pubkeys.end()) { |
2454 | 0 | return nullptr; |
2455 | 0 | } |
2456 | 0 | int32_t index = it->second; |
2457 | | |
2458 | | // Always try to get the signing provider with private keys. This function should only be called during signing anyways |
2459 | 0 | return GetSigningProvider(index, true); |
2460 | 0 | } |
2461 | | |
2462 | | std::unique_ptr<FlatSigningProvider> DescriptorScriptPubKeyMan::GetSigningProvider(int32_t index, bool include_private) const |
2463 | 0 | { |
2464 | 0 | AssertLockHeld(cs_desc_man); |
2465 | |
|
2466 | 0 | std::unique_ptr<FlatSigningProvider> out_keys = std::make_unique<FlatSigningProvider>(); |
2467 | | |
2468 | | // Fetch SigningProvider from cache to avoid re-deriving |
2469 | 0 | auto it = m_map_signing_providers.find(index); |
2470 | 0 | if (it != m_map_signing_providers.end()) { |
2471 | 0 | out_keys->Merge(FlatSigningProvider{it->second}); |
2472 | 0 | } else { |
2473 | | // Get the scripts, keys, and key origins for this script |
2474 | 0 | std::vector<CScript> scripts_temp; |
2475 | 0 | if (!m_wallet_descriptor.descriptor->ExpandFromCache(index, m_wallet_descriptor.cache, scripts_temp, *out_keys)) return nullptr; |
2476 | | |
2477 | | // Cache SigningProvider so we don't need to re-derive if we need this SigningProvider again |
2478 | 0 | m_map_signing_providers[index] = *out_keys; |
2479 | 0 | } |
2480 | | |
2481 | 0 | if (HavePrivateKeys() && include_private) { |
2482 | 0 | FlatSigningProvider master_provider; |
2483 | 0 | master_provider.keys = GetKeys(); |
2484 | 0 | m_wallet_descriptor.descriptor->ExpandPrivate(index, master_provider, *out_keys); |
2485 | 0 | } |
2486 | |
|
2487 | 0 | return out_keys; |
2488 | 0 | } |
2489 | | |
2490 | | std::unique_ptr<SigningProvider> DescriptorScriptPubKeyMan::GetSolvingProvider(const CScript& script) const |
2491 | 0 | { |
2492 | 0 | return GetSigningProvider(script, false); |
2493 | 0 | } |
2494 | | |
2495 | | bool DescriptorScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& sigdata) |
2496 | 0 | { |
2497 | 0 | return IsMine(script); |
2498 | 0 | } |
2499 | | |
2500 | | bool DescriptorScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const |
2501 | 0 | { |
2502 | 0 | std::unique_ptr<FlatSigningProvider> keys = std::make_unique<FlatSigningProvider>(); |
2503 | 0 | for (const auto& coin_pair : coins) { |
2504 | 0 | std::unique_ptr<FlatSigningProvider> coin_keys = GetSigningProvider(coin_pair.second.out.scriptPubKey, true); |
2505 | 0 | if (!coin_keys) { |
2506 | 0 | continue; |
2507 | 0 | } |
2508 | 0 | keys->Merge(std::move(*coin_keys)); |
2509 | 0 | } |
2510 | |
|
2511 | 0 | return ::SignTransaction(tx, keys.get(), coins, sighash, input_errors); |
2512 | 0 | } |
2513 | | |
2514 | | SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const |
2515 | 0 | { |
2516 | 0 | std::unique_ptr<FlatSigningProvider> keys = GetSigningProvider(GetScriptForDestination(pkhash), true); |
2517 | 0 | if (!keys) { |
2518 | 0 | return SigningResult::PRIVATE_KEY_NOT_AVAILABLE; |
2519 | 0 | } |
2520 | | |
2521 | 0 | CKey key; |
2522 | 0 | if (!keys->GetKey(ToKeyID(pkhash), key)) { |
2523 | 0 | return SigningResult::PRIVATE_KEY_NOT_AVAILABLE; |
2524 | 0 | } |
2525 | | |
2526 | 0 | if (!MessageSign(key, message, str_sig)) { |
2527 | 0 | return SigningResult::SIGNING_FAILED; |
2528 | 0 | } |
2529 | 0 | return SigningResult::OK; |
2530 | 0 | } |
2531 | | |
2532 | | std::optional<PSBTError> DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const |
2533 | 0 | { |
2534 | 0 | if (n_signed) { |
2535 | 0 | *n_signed = 0; |
2536 | 0 | } |
2537 | 0 | for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { |
2538 | 0 | const CTxIn& txin = psbtx.tx->vin[i]; |
2539 | 0 | PSBTInput& input = psbtx.inputs.at(i); |
2540 | |
|
2541 | 0 | if (PSBTInputSigned(input)) { |
2542 | 0 | continue; |
2543 | 0 | } |
2544 | | |
2545 | | // Get the Sighash type |
2546 | 0 | if (sign && input.sighash_type != std::nullopt && *input.sighash_type != sighash_type) { |
2547 | 0 | return PSBTError::SIGHASH_MISMATCH; |
2548 | 0 | } |
2549 | | |
2550 | | // Get the scriptPubKey to know which SigningProvider to use |
2551 | 0 | CScript script; |
2552 | 0 | if (!input.witness_utxo.IsNull()) { |
2553 | 0 | script = input.witness_utxo.scriptPubKey; |
2554 | 0 | } else if (input.non_witness_utxo) { |
2555 | 0 | if (txin.prevout.n >= input.non_witness_utxo->vout.size()) { |
2556 | 0 | return PSBTError::MISSING_INPUTS; |
2557 | 0 | } |
2558 | 0 | script = input.non_witness_utxo->vout[txin.prevout.n].scriptPubKey; |
2559 | 0 | } else { |
2560 | | // There's no UTXO so we can just skip this now |
2561 | 0 | continue; |
2562 | 0 | } |
2563 | | |
2564 | 0 | std::unique_ptr<FlatSigningProvider> keys = std::make_unique<FlatSigningProvider>(); |
2565 | 0 | std::unique_ptr<FlatSigningProvider> script_keys = GetSigningProvider(script, /*include_private=*/sign); |
2566 | 0 | if (script_keys) { |
2567 | 0 | keys->Merge(std::move(*script_keys)); |
2568 | 0 | } else { |
2569 | | // Maybe there are pubkeys listed that we can sign for |
2570 | 0 | std::vector<CPubKey> pubkeys; |
2571 | 0 | pubkeys.reserve(input.hd_keypaths.size() + 2); |
2572 | | |
2573 | | // ECDSA Pubkeys |
2574 | 0 | for (const auto& [pk, _] : input.hd_keypaths) { |
2575 | 0 | pubkeys.push_back(pk); |
2576 | 0 | } |
2577 | | |
2578 | | // Taproot output pubkey |
2579 | 0 | std::vector<std::vector<unsigned char>> sols; |
2580 | 0 | if (Solver(script, sols) == TxoutType::WITNESS_V1_TAPROOT) { |
2581 | 0 | sols[0].insert(sols[0].begin(), 0x02); |
2582 | 0 | pubkeys.emplace_back(sols[0]); |
2583 | 0 | sols[0][0] = 0x03; |
2584 | 0 | pubkeys.emplace_back(sols[0]); |
2585 | 0 | } |
2586 | | |
2587 | | // Taproot pubkeys |
2588 | 0 | for (const auto& pk_pair : input.m_tap_bip32_paths) { |
2589 | 0 | const XOnlyPubKey& pubkey = pk_pair.first; |
2590 | 0 | for (unsigned char prefix : {0x02, 0x03}) { |
2591 | 0 | unsigned char b[33] = {prefix}; |
2592 | 0 | std::copy(pubkey.begin(), pubkey.end(), b + 1); |
2593 | 0 | CPubKey fullpubkey; |
2594 | 0 | fullpubkey.Set(b, b + 33); |
2595 | 0 | pubkeys.push_back(fullpubkey); |
2596 | 0 | } |
2597 | 0 | } |
2598 | |
|
2599 | 0 | for (const auto& pubkey : pubkeys) { |
2600 | 0 | std::unique_ptr<FlatSigningProvider> pk_keys = GetSigningProvider(pubkey); |
2601 | 0 | if (pk_keys) { |
2602 | 0 | keys->Merge(std::move(*pk_keys)); |
2603 | 0 | } |
2604 | 0 | } |
2605 | 0 | } |
2606 | |
|
2607 | 0 | SignPSBTInput(HidingSigningProvider(keys.get(), /*hide_secret=*/!sign, /*hide_origin=*/!bip32derivs), psbtx, i, &txdata, sighash_type, nullptr, finalize); |
2608 | |
|
2609 | 0 | bool signed_one = PSBTInputSigned(input); |
2610 | 0 | if (n_signed && (signed_one || !sign)) { |
2611 | | // If sign is false, we assume that we _could_ sign if we get here. This |
2612 | | // will never have false negatives; it is hard to tell under what i |
2613 | | // circumstances it could have false positives. |
2614 | 0 | (*n_signed)++; |
2615 | 0 | } |
2616 | 0 | } |
2617 | | |
2618 | | // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change |
2619 | 0 | for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) { |
2620 | 0 | std::unique_ptr<SigningProvider> keys = GetSolvingProvider(psbtx.tx->vout.at(i).scriptPubKey); |
2621 | 0 | if (!keys) { |
2622 | 0 | continue; |
2623 | 0 | } |
2624 | 0 | UpdatePSBTOutput(HidingSigningProvider(keys.get(), /*hide_secret=*/true, /*hide_origin=*/!bip32derivs), psbtx, i); |
2625 | 0 | } |
2626 | |
|
2627 | 0 | return {}; |
2628 | 0 | } |
2629 | | |
2630 | | std::unique_ptr<CKeyMetadata> DescriptorScriptPubKeyMan::GetMetadata(const CTxDestination& dest) const |
2631 | 0 | { |
2632 | 0 | std::unique_ptr<SigningProvider> provider = GetSigningProvider(GetScriptForDestination(dest)); |
2633 | 0 | if (provider) { |
2634 | 0 | KeyOriginInfo orig; |
2635 | 0 | CKeyID key_id = GetKeyForDestination(*provider, dest); |
2636 | 0 | if (provider->GetKeyOrigin(key_id, orig)) { |
2637 | 0 | LOCK(cs_desc_man); |
2638 | 0 | std::unique_ptr<CKeyMetadata> meta = std::make_unique<CKeyMetadata>(); |
2639 | 0 | meta->key_origin = orig; |
2640 | 0 | meta->has_key_origin = true; |
2641 | 0 | meta->nCreateTime = m_wallet_descriptor.creation_time; |
2642 | 0 | return meta; |
2643 | 0 | } |
2644 | 0 | } |
2645 | 0 | return nullptr; |
2646 | 0 | } |
2647 | | |
2648 | | uint256 DescriptorScriptPubKeyMan::GetID() const |
2649 | 0 | { |
2650 | 0 | LOCK(cs_desc_man); |
2651 | 0 | return m_wallet_descriptor.id; |
2652 | 0 | } |
2653 | | |
2654 | | void DescriptorScriptPubKeyMan::SetCache(const DescriptorCache& cache) |
2655 | 0 | { |
2656 | 0 | LOCK(cs_desc_man); |
2657 | 0 | std::set<CScript> new_spks; |
2658 | 0 | m_wallet_descriptor.cache = cache; |
2659 | 0 | for (int32_t i = m_wallet_descriptor.range_start; i < m_wallet_descriptor.range_end; ++i) { |
2660 | 0 | FlatSigningProvider out_keys; |
2661 | 0 | std::vector<CScript> scripts_temp; |
2662 | 0 | if (!m_wallet_descriptor.descriptor->ExpandFromCache(i, m_wallet_descriptor.cache, scripts_temp, out_keys)) { |
2663 | 0 | throw std::runtime_error("Error: Unable to expand wallet descriptor from cache"); |
2664 | 0 | } |
2665 | | // Add all of the scriptPubKeys to the scriptPubKey set |
2666 | 0 | new_spks.insert(scripts_temp.begin(), scripts_temp.end()); |
2667 | 0 | for (const CScript& script : scripts_temp) { |
2668 | 0 | if (m_map_script_pub_keys.count(script) != 0) { |
2669 | 0 | throw std::runtime_error(strprintf("Error: Already loaded script at index %d as being at index %d", i, m_map_script_pub_keys[script])); |
2670 | 0 | } |
2671 | 0 | m_map_script_pub_keys[script] = i; |
2672 | 0 | } |
2673 | 0 | for (const auto& pk_pair : out_keys.pubkeys) { |
2674 | 0 | const CPubKey& pubkey = pk_pair.second; |
2675 | 0 | if (m_map_pubkeys.count(pubkey) != 0) { |
2676 | | // We don't need to give an error here. |
2677 | | // It doesn't matter which of many valid indexes the pubkey has, we just need an index where we can derive it and it's private key |
2678 | 0 | continue; |
2679 | 0 | } |
2680 | 0 | m_map_pubkeys[pubkey] = i; |
2681 | 0 | } |
2682 | 0 | m_max_cached_index++; |
2683 | 0 | } |
2684 | | // Make sure the wallet knows about our new spks |
2685 | 0 | m_storage.TopUpCallback(new_spks, this); |
2686 | 0 | } |
2687 | | |
2688 | | bool DescriptorScriptPubKeyMan::AddKey(const CKeyID& key_id, const CKey& key) |
2689 | 0 | { |
2690 | 0 | LOCK(cs_desc_man); |
2691 | 0 | m_map_keys[key_id] = key; |
2692 | 0 | return true; |
2693 | 0 | } |
2694 | | |
2695 | | bool DescriptorScriptPubKeyMan::AddCryptedKey(const CKeyID& key_id, const CPubKey& pubkey, const std::vector<unsigned char>& crypted_key) |
2696 | 0 | { |
2697 | 0 | LOCK(cs_desc_man); |
2698 | 0 | if (!m_map_keys.empty()) { |
2699 | 0 | return false; |
2700 | 0 | } |
2701 | | |
2702 | 0 | m_map_crypted_keys[key_id] = make_pair(pubkey, crypted_key); |
2703 | 0 | return true; |
2704 | 0 | } |
2705 | | |
2706 | | bool DescriptorScriptPubKeyMan::HasWalletDescriptor(const WalletDescriptor& desc) const |
2707 | 0 | { |
2708 | 0 | LOCK(cs_desc_man); |
2709 | 0 | return !m_wallet_descriptor.id.IsNull() && !desc.id.IsNull() && m_wallet_descriptor.id == desc.id; |
2710 | 0 | } |
2711 | | |
2712 | | void DescriptorScriptPubKeyMan::WriteDescriptor() |
2713 | 0 | { |
2714 | 0 | LOCK(cs_desc_man); |
2715 | 0 | WalletBatch batch(m_storage.GetDatabase()); |
2716 | 0 | if (!batch.WriteDescriptor(GetID(), m_wallet_descriptor)) { |
2717 | 0 | throw std::runtime_error(std::string(__func__) + ": writing descriptor failed"); |
2718 | 0 | } |
2719 | 0 | } |
2720 | | |
2721 | | WalletDescriptor DescriptorScriptPubKeyMan::GetWalletDescriptor() const |
2722 | 0 | { |
2723 | 0 | return m_wallet_descriptor; |
2724 | 0 | } |
2725 | | |
2726 | | std::unordered_set<CScript, SaltedSipHasher> DescriptorScriptPubKeyMan::GetScriptPubKeys() const |
2727 | 0 | { |
2728 | 0 | return GetScriptPubKeys(0); |
2729 | 0 | } |
2730 | | |
2731 | | std::unordered_set<CScript, SaltedSipHasher> DescriptorScriptPubKeyMan::GetScriptPubKeys(int32_t minimum_index) const |
2732 | 0 | { |
2733 | 0 | LOCK(cs_desc_man); |
2734 | 0 | std::unordered_set<CScript, SaltedSipHasher> script_pub_keys; |
2735 | 0 | script_pub_keys.reserve(m_map_script_pub_keys.size()); |
2736 | |
|
2737 | 0 | for (auto const& [script_pub_key, index] : m_map_script_pub_keys) { |
2738 | 0 | if (index >= minimum_index) script_pub_keys.insert(script_pub_key); |
2739 | 0 | } |
2740 | 0 | return script_pub_keys; |
2741 | 0 | } |
2742 | | |
2743 | | int32_t DescriptorScriptPubKeyMan::GetEndRange() const |
2744 | 0 | { |
2745 | 0 | return m_max_cached_index + 1; |
2746 | 0 | } |
2747 | | |
2748 | | bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out, const bool priv) const |
2749 | 0 | { |
2750 | 0 | LOCK(cs_desc_man); |
2751 | |
|
2752 | 0 | FlatSigningProvider provider; |
2753 | 0 | provider.keys = GetKeys(); |
2754 | |
|
2755 | 0 | if (priv) { |
2756 | | // For the private version, always return the master key to avoid |
2757 | | // exposing child private keys. The risk implications of exposing child |
2758 | | // private keys together with the parent xpub may be non-obvious for users. |
2759 | 0 | return m_wallet_descriptor.descriptor->ToPrivateString(provider, out); |
2760 | 0 | } |
2761 | | |
2762 | 0 | return m_wallet_descriptor.descriptor->ToNormalizedString(provider, out, &m_wallet_descriptor.cache); |
2763 | 0 | } |
2764 | | |
2765 | | void DescriptorScriptPubKeyMan::UpgradeDescriptorCache() |
2766 | 0 | { |
2767 | 0 | LOCK(cs_desc_man); |
2768 | 0 | if (m_storage.IsLocked() || m_storage.IsWalletFlagSet(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED)) { |
2769 | 0 | return; |
2770 | 0 | } |
2771 | | |
2772 | | // Skip if we have the last hardened xpub cache |
2773 | 0 | if (m_wallet_descriptor.cache.GetCachedLastHardenedExtPubKeys().size() > 0) { |
2774 | 0 | return; |
2775 | 0 | } |
2776 | | |
2777 | | // Expand the descriptor |
2778 | 0 | FlatSigningProvider provider; |
2779 | 0 | provider.keys = GetKeys(); |
2780 | 0 | FlatSigningProvider out_keys; |
2781 | 0 | std::vector<CScript> scripts_temp; |
2782 | 0 | DescriptorCache temp_cache; |
2783 | 0 | if (!m_wallet_descriptor.descriptor->Expand(0, provider, scripts_temp, out_keys, &temp_cache)){ |
2784 | 0 | throw std::runtime_error("Unable to expand descriptor"); |
2785 | 0 | } |
2786 | | |
2787 | | // Cache the last hardened xpubs |
2788 | 0 | DescriptorCache diff = m_wallet_descriptor.cache.MergeAndDiff(temp_cache); |
2789 | 0 | if (!WalletBatch(m_storage.GetDatabase()).WriteDescriptorCacheItems(GetID(), diff)) { |
2790 | 0 | throw std::runtime_error(std::string(__func__) + ": writing cache items failed"); |
2791 | 0 | } |
2792 | 0 | } |
2793 | | |
2794 | | void DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor) |
2795 | 0 | { |
2796 | 0 | LOCK(cs_desc_man); |
2797 | 0 | std::string error; |
2798 | 0 | if (!CanUpdateToWalletDescriptor(descriptor, error)) { |
2799 | 0 | throw std::runtime_error(std::string(__func__) + ": " + error); |
2800 | 0 | } |
2801 | | |
2802 | 0 | m_map_pubkeys.clear(); |
2803 | 0 | m_map_script_pub_keys.clear(); |
2804 | 0 | m_max_cached_index = -1; |
2805 | 0 | m_wallet_descriptor = descriptor; |
2806 | |
|
2807 | 0 | NotifyFirstKeyTimeChanged(this, m_wallet_descriptor.creation_time); |
2808 | 0 | } |
2809 | | |
2810 | | bool DescriptorScriptPubKeyMan::CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error) |
2811 | 0 | { |
2812 | 0 | LOCK(cs_desc_man); |
2813 | 0 | if (!HasWalletDescriptor(descriptor)) { |
2814 | 0 | error = "can only update matching descriptor"; |
2815 | 0 | return false; |
2816 | 0 | } |
2817 | | |
2818 | 0 | if (descriptor.range_start > m_wallet_descriptor.range_start || |
2819 | 0 | descriptor.range_end < m_wallet_descriptor.range_end) { |
2820 | | // Use inclusive range for error |
2821 | 0 | error = strprintf("new range must include current range = [%d,%d]", |
2822 | 0 | m_wallet_descriptor.range_start, |
2823 | 0 | m_wallet_descriptor.range_end - 1); |
2824 | 0 | return false; |
2825 | 0 | } |
2826 | | |
2827 | 0 | return true; |
2828 | 0 | } |
2829 | | } // namespace wallet |