Line data Source code
1 : // Copyright (c) 2019 The Bitcoin Core developers
2 : // Copyright (c) 2020-2021 The PIVX Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "wallet/scriptpubkeyman.h"
7 : #include "crypter.h"
8 : #include "script/standard.h"
9 :
10 242 : bool ScriptPubKeyMan::SetupGeneration(bool newKeypool, bool force, bool memOnly)
11 : {
12 242 : if (CanGenerateKeys() && !force) {
13 : return false;
14 : }
15 :
16 242 : SetHDSeed(GenerateNewSeed(), force, memOnly);
17 242 : if (newKeypool && !NewKeyPool()) {
18 0 : return false;
19 : }
20 : return true;
21 : }
22 :
23 2 : bool ScriptPubKeyMan::Upgrade(const int prev_version, std::string& error)
24 : {
25 4 : LOCK(wallet->cs_KeyStore);
26 2 : error = "";
27 2 : bool hd_upgrade = false;
28 2 : if (!IsHDEnabled()) {
29 2 : LogPrintf("Upgrading wallet to use HD chain split\n");
30 2 : wallet->SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL);
31 :
32 : // generate a new master key
33 2 : CPubKey masterPubKey = GenerateNewSeed();
34 2 : SetHDSeed(masterPubKey);
35 2 : hd_upgrade = true;
36 : }
37 :
38 : // Regenerate the keypool if upgraded to HD
39 2 : if (hd_upgrade) {
40 : // Mark all keys currently in the keypool as pre-split
41 2 : MarkPreSplitKeys();
42 :
43 : // Fill keypool
44 2 : if (!TopUp()) {
45 0 : error = _("Unable to generate keys");
46 0 : return false;
47 : }
48 : }
49 : return true;
50 : }
51 :
52 7217 : bool ScriptPubKeyMan::CanGenerateKeys()
53 : {
54 : // A wallet can generate keys if it has an HD seed (IsHDEnabled) or it is a non-HD wallet (pre FEATURE_HD)
55 7217 : LOCK(wallet->cs_wallet);
56 14440 : return IsHDEnabled() || wallet->GetVersion() < FEATURE_PRE_SPLIT_KEYPOOL;
57 : }
58 :
59 50330 : bool ScriptPubKeyMan::IsHDEnabled() const
60 : {
61 50330 : return !hdChain.IsNull();
62 : }
63 :
64 6494 : bool ScriptPubKeyMan::CanGetAddresses(const uint8_t& type)
65 : {
66 12988 : LOCK(wallet->cs_wallet);
67 : // Check if the keypool has keys
68 6494 : const bool isHDEnabled = IsHDEnabled();
69 6494 : bool keypool_has_keys = false;
70 6494 : if (isHDEnabled && type == HDChain::ChangeType::INTERNAL) {
71 1560 : keypool_has_keys = setInternalKeyPool.size() > 0;
72 4934 : } else if (isHDEnabled && type == HDChain::ChangeType::STAKING) {
73 14 : keypool_has_keys = setStakingKeyPool.size() > 0;
74 : } else {
75 : // either external key was requested or HD is not enabled
76 4920 : keypool_has_keys = KeypoolCountExternalKeys() > 0;
77 : }
78 : // If the keypool doesn't have keys, check if we can generate them
79 6494 : if (!keypool_has_keys) {
80 8 : return CanGenerateKeys();
81 : }
82 : return keypool_has_keys;
83 : }
84 :
85 199 : static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, WalletBatch& batch) {
86 199 : if (setKeyPool.empty()) {
87 46 : return GetTime();
88 : }
89 :
90 153 : CKeyPool keypool;
91 153 : int64_t nIndex = *(setKeyPool.begin());
92 153 : if (!batch.ReadPool(nIndex, keypool)) {
93 0 : throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed");
94 : }
95 153 : assert(keypool.vchPubKey.IsValid());
96 153 : return keypool.nTime;
97 : }
98 :
99 67 : int64_t ScriptPubKeyMan::GetOldestKeyPoolTime()
100 : {
101 67 : LOCK(wallet->cs_KeyStore);
102 :
103 134 : WalletBatch batch(wallet->GetDBHandle());
104 : // load oldest key from keypool, get time and return
105 67 : int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch);
106 67 : if (IsHDEnabled()) {
107 66 : oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), oldestKey);
108 66 : oldestKey = std::max(GetOldestKeyTimeInPool(setStakingKeyPool, batch), oldestKey);
109 66 : if (!set_pre_split_keypool.empty()) {
110 0 : oldestKey = std::max(GetOldestKeyTimeInPool(set_pre_split_keypool, batch), oldestKey);
111 : }
112 : }
113 :
114 134 : return oldestKey;
115 : }
116 :
117 4987 : size_t ScriptPubKeyMan::KeypoolCountExternalKeys()
118 : {
119 4987 : AssertLockHeld(wallet->cs_wallet);
120 4987 : return setExternalKeyPool.size() + set_pre_split_keypool.size();
121 : }
122 :
123 434 : unsigned int ScriptPubKeyMan::GetKeyPoolSize() const
124 : {
125 434 : AssertLockHeld(wallet->cs_wallet);
126 434 : return setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size();
127 : }
128 :
129 65 : unsigned int ScriptPubKeyMan::GetStakingKeyPoolSize() const
130 : {
131 65 : AssertLockHeld(wallet->cs_wallet);
132 65 : return setStakingKeyPool.size();
133 : }
134 :
135 2289 : bool ScriptPubKeyMan::GetKeyFromPool(CPubKey& result, const uint8_t& changeType)
136 : {
137 2289 : if (!CanGetAddresses(changeType)) {
138 0 : LogPrintf("%s: Cannot get address, type: %d\n", __func__ ,changeType);
139 0 : return false;
140 : }
141 :
142 2289 : CKeyPool keypool;
143 2289 : {
144 2289 : LOCK(wallet->cs_wallet);
145 2289 : int64_t nIndex;
146 2289 : if (!ReserveKeyFromKeyPool(nIndex, keypool, changeType)) {
147 4 : if (wallet->IsLocked()) {
148 3 : LogPrintf("%s: Wallet locked, cannot get address\n", __func__);
149 : return false;
150 : }
151 5 : WalletBatch batch(wallet->GetDBHandle());
152 1 : result = GenerateNewKey(batch, changeType);
153 1 : return true;
154 : }
155 2285 : KeepDestination(nIndex);
156 2285 : result = keypool.vchPubKey;
157 : }
158 2285 : return true;
159 : }
160 :
161 4205 : bool ScriptPubKeyMan::GetReservedKey(const uint8_t& changeType, int64_t& index, CKeyPool& keypool)
162 : {
163 4205 : if (!CanGetAddresses(changeType)) {
164 0 : return error("%s : Cannot get address for change type %d", __func__, changeType);
165 : }
166 4205 : if (!ReserveKeyFromKeyPool(index, keypool, changeType)) {
167 4 : return error("%s : Cannot reserve key from pool for change type %d", __func__, changeType);
168 : }
169 : return true;
170 : }
171 :
172 6494 : bool ScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, const uint8_t& type)
173 : {
174 6494 : nIndex = -1;
175 6494 : keypool.vchPubKey = CPubKey();
176 6494 : {
177 6494 : LOCK(wallet->cs_wallet);
178 :
179 6494 : bool isHDEnabled = IsHDEnabled();
180 6494 : bool fReturningInternal = type == HDChain::ChangeType::INTERNAL && isHDEnabled;
181 6494 : bool fReturningStaking = type == HDChain::ChangeType::STAKING && isHDEnabled;
182 6494 : bool use_split_keypool = set_pre_split_keypool.empty();
183 6494 : std::set<int64_t>& setKeyPool = use_split_keypool ?
184 : ( fReturningInternal ? setInternalKeyPool : (fReturningStaking ? setStakingKeyPool : setExternalKeyPool) ) : set_pre_split_keypool;
185 :
186 : // Get the oldest key
187 6494 : if (setKeyPool.empty()) {
188 16 : return false;
189 : }
190 :
191 12972 : WalletBatch batch(wallet->GetDBHandle());
192 :
193 6486 : auto it = setKeyPool.begin();
194 6486 : nIndex = *it;
195 6486 : setKeyPool.erase(it);
196 6486 : if (!batch.ReadPool(nIndex, keypool)) {
197 0 : throw std::runtime_error(std::string(__func__) + ": read failed");
198 : }
199 6486 : CPubKey pk;
200 6486 : if (!wallet->GetPubKey(keypool.vchPubKey.GetID(), pk)) {
201 0 : throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
202 : }
203 : // If the key was pre-split keypool, we don't care about what type it is
204 6486 : if (use_split_keypool && keypool.IsInternal() != fReturningInternal) {
205 0 : throw std::runtime_error(std::string(__func__) + ": keypool internal entry misclassified");
206 : }
207 :
208 6486 : if (use_split_keypool && keypool.IsStaking() != fReturningStaking) {
209 0 : throw std::runtime_error(std::string(__func__) + ": keypool staking entry misclassified");
210 : }
211 :
212 6486 : if (!keypool.vchPubKey.IsValid()) {
213 0 : throw std::runtime_error(std::string(__func__) + ": keypool entry invalid");
214 : }
215 :
216 6486 : assert(m_index_to_reserved_key.count(nIndex) == 0);
217 6486 : m_index_to_reserved_key[nIndex] = keypool.vchPubKey.GetID();
218 6486 : m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
219 6486 : LogPrintf("%s: keypool reserve %d\n", __func__, nIndex);
220 : }
221 : //NotifyCanGetAddressesChanged();
222 6486 : return true;
223 : }
224 :
225 6471 : void ScriptPubKeyMan::KeepDestination(int64_t nIndex)
226 : {
227 : // Remove from key pool
228 6471 : WalletBatch batch(wallet->GetDBHandle());
229 6471 : batch.ErasePool(nIndex);
230 6471 : CPubKey pubkey;
231 6471 : bool have_pk = wallet->GetPubKey(m_index_to_reserved_key.at(nIndex), pubkey);
232 6471 : assert(have_pk);
233 6471 : m_index_to_reserved_key.erase(nIndex);
234 6471 : LogPrintf("keypool keep %d\n", nIndex);
235 6471 : }
236 :
237 15 : void ScriptPubKeyMan::ReturnDestination(int64_t nIndex, const uint8_t& type, const CTxDestination&)
238 : {
239 : // Return to key pool
240 15 : {
241 15 : LOCK(wallet->cs_wallet);
242 15 : const bool isHDEnabled = IsHDEnabled();
243 15 : if (isHDEnabled && type == HDChain::ChangeType::INTERNAL) {
244 15 : setInternalKeyPool.insert(nIndex);
245 0 : } else if (isHDEnabled && type == HDChain::ChangeType::STAKING) {
246 0 : setStakingKeyPool.insert(nIndex);
247 0 : } else if (isHDEnabled && !set_pre_split_keypool.empty()) {
248 0 : set_pre_split_keypool.insert(nIndex);
249 : } else {
250 0 : setExternalKeyPool.insert(nIndex);
251 : }
252 15 : CKeyID& pubkey_id = m_index_to_reserved_key.at(nIndex);
253 15 : m_pool_key_to_index[pubkey_id] = nIndex;
254 15 : m_index_to_reserved_key.erase(nIndex);
255 : //NotifyCanGetAddressesChanged();
256 : }
257 15 : LogPrintf("%s: keypool return %d\n", __func__, nIndex);
258 15 : }
259 :
260 47 : void ScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id)
261 : {
262 47 : AssertLockHeld(wallet->cs_wallet);
263 47 : bool internal = setInternalKeyPool.count(keypool_id);
264 47 : bool staking = setStakingKeyPool.count(keypool_id);
265 80 : if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id) || staking);
266 47 : std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ?
267 33 : (staking ? &setStakingKeyPool : &setExternalKeyPool) : &set_pre_split_keypool);
268 47 : auto it = setKeyPool->begin();
269 :
270 47 : WalletBatch batch(wallet->GetDBHandle());
271 207 : while (it != std::end(*setKeyPool)) {
272 186 : const int64_t& index = *(it);
273 186 : if (index > keypool_id) break; // set*KeyPool is ordered
274 :
275 160 : CKeyPool keypool;
276 160 : if (batch.ReadPool(index, keypool)) {
277 160 : const CKeyID& keyid = keypool.vchPubKey.GetID();
278 160 : m_pool_key_to_index.erase(keyid);
279 : // add missing receive addresses to the AddressBook
280 303 : if (!internal && !wallet->HasAddressBook(keyid)) {
281 572 : wallet->SetAddressBook(keyid, "", staking ? AddressBook::AddressBookPurpose::COLD_STAKING
282 : : AddressBook::AddressBookPurpose::RECEIVE);
283 : }
284 : }
285 160 : batch.ErasePool(index);
286 160 : LogPrintf("keypool index %d removed\n", index);
287 160 : it = setKeyPool->erase(it);
288 : }
289 47 : }
290 :
291 1135032 : void ScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
292 : {
293 1135032 : AssertLockHeld(wallet->cs_wallet);
294 : // extract addresses and check if they match with an unused keypool key
295 2262994 : for (const auto& keyid : wallet->GetAffectedKeys(script)) {
296 1127952 : std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid);
297 1127952 : if (mi != m_pool_key_to_index.end()) {
298 47 : LogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__);
299 47 : MarkReserveKeysAsUsed(mi->second);
300 :
301 47 : if (!TopUp()) {
302 0 : LogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
303 : }
304 : }
305 : }
306 1135032 : }
307 :
308 2 : void ScriptPubKeyMan::MarkPreSplitKeys()
309 : {
310 2 : WalletBatch batch(wallet->GetDBHandle());
311 3 : for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) {
312 1 : int64_t index = *it;
313 1 : CKeyPool keypool;
314 1 : if (!batch.ReadPool(index, keypool)) {
315 0 : throw std::runtime_error(std::string(__func__) + ": read keypool entry failed");
316 : }
317 1 : keypool.m_pre_split = true;
318 1 : if (!batch.WritePool(index, keypool)) {
319 0 : throw std::runtime_error(std::string(__func__) + ": writing modified keypool entry failed");
320 : }
321 1 : set_pre_split_keypool.insert(index);
322 1 : it = setExternalKeyPool.erase(it);
323 : }
324 2 : }
325 :
326 : /**
327 : * Mark old keypool keys as used,
328 : * and generate all new keys
329 : */
330 214 : bool ScriptPubKeyMan::NewKeyPool()
331 : {
332 214 : {
333 214 : LOCK(wallet->cs_wallet);
334 :
335 428 : WalletBatch batch(wallet->GetDBHandle());
336 : // Internal
337 410 : for (const int64_t nIndex : setInternalKeyPool) {
338 196 : batch.ErasePool(nIndex);
339 : }
340 214 : setInternalKeyPool.clear();
341 :
342 : // External
343 406 : for (const int64_t nIndex : setExternalKeyPool) {
344 192 : batch.ErasePool(nIndex);
345 : }
346 214 : setExternalKeyPool.clear();
347 :
348 : // Staking
349 411 : for (const int64_t nIndex : setStakingKeyPool) {
350 197 : batch.ErasePool(nIndex);
351 : }
352 214 : setStakingKeyPool.clear();
353 :
354 : // key -> index.
355 214 : m_pool_key_to_index.clear();
356 :
357 214 : if (!TopUp()) {
358 0 : return false;
359 : }
360 214 : LogPrintf("ScriptPubKeyMan::NewKeyPool rewrote keypool\n");
361 : }
362 214 : return true;
363 : }
364 :
365 : /**
366 : * Fill the key pool
367 : */
368 6967 : bool ScriptPubKeyMan::TopUp(unsigned int kpSize)
369 : {
370 6967 : if (!CanGenerateKeys()) {
371 : return false;
372 : }
373 6967 : {
374 6967 : LOCK(wallet->cs_wallet);
375 6979 : if (wallet->IsLocked()) return false;
376 :
377 : // Top up key pool
378 6955 : unsigned int nTargetSize;
379 6955 : if (kpSize > 0)
380 : nTargetSize = kpSize;
381 : else
382 6949 : nTargetSize = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);
383 :
384 : // Count amount of available keys (internal, external)
385 : // make sure the keypool of external and internal keys fits the user selected target (-keypool)
386 7058 : int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setExternalKeyPool.size(), (int64_t) 0);
387 7058 : int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setInternalKeyPool.size(), (int64_t) 0);
388 7058 : int64_t missingStaking = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setStakingKeyPool.size(), (int64_t) 0);
389 :
390 6955 : if (!IsHDEnabled()) {
391 : // don't create extra internal or staking keys
392 1 : missingInternal = 0;
393 1 : missingStaking = 0;
394 : }
395 :
396 13910 : WalletBatch batch(wallet->GetDBHandle());
397 6955 : GeneratePool(batch, missingExternal, HDChain::ChangeType::EXTERNAL);
398 6955 : GeneratePool(batch, missingInternal, HDChain::ChangeType::INTERNAL);
399 6955 : GeneratePool(batch, missingStaking, HDChain::ChangeType::STAKING);
400 :
401 6955 : if (missingInternal + missingExternal > 0) {
402 6469 : LogPrintf("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());
403 : }
404 6955 : if (missingStaking > 0) {
405 237 : LogPrintf("keypool added %d staking keys\n", setStakingKeyPool.size());
406 : }
407 : }
408 : // TODO: Implement this.
409 : //NotifyCanGetAddressesChanged();
410 6955 : return true;
411 : }
412 :
413 20865 : void ScriptPubKeyMan::GeneratePool(WalletBatch& batch, int64_t targetSize, const uint8_t& type)
414 : {
415 32371 : for (int64_t i = targetSize; i--;) {
416 11506 : CPubKey pubkey(GenerateNewKey(batch, type));
417 11506 : AddKeypoolPubkeyWithDB(pubkey, type, batch);
418 : }
419 20865 : }
420 :
421 11506 : void ScriptPubKeyMan::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const uint8_t& type, WalletBatch &batch)
422 : {
423 11506 : LOCK(wallet->cs_wallet);
424 11506 : assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); // How in the hell did you use so many keys?
425 11506 : int64_t index = ++m_max_keypool_index;
426 11506 : if (!batch.WritePool(index, CKeyPool(pubkey, type))) {
427 0 : throw std::runtime_error(std::string(__func__) + ": writing imported pubkey failed");
428 : }
429 :
430 11506 : const bool isHDEnabled = IsHDEnabled();
431 11506 : if (isHDEnabled && type == HDChain::ChangeType::INTERNAL) {
432 3228 : setInternalKeyPool.insert(index);
433 8278 : } else if (isHDEnabled && type == HDChain::ChangeType::STAKING) {
434 1719 : setStakingKeyPool.insert(index);
435 : } else {
436 6559 : setExternalKeyPool.insert(index);
437 : }
438 :
439 11506 : m_pool_key_to_index[pubkey.GetID()] = index;
440 11506 : }
441 :
442 : /**
443 : * Generate a new key and stores it in db.
444 : */
445 11507 : CPubKey ScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, const uint8_t& type)
446 : {
447 11507 : AssertLockHeld(wallet->cs_wallet);
448 11507 : bool fCompressed = wallet->CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
449 :
450 11507 : CKey secret;
451 :
452 : // Create new metadata
453 11507 : int64_t nCreationTime = GetTime();
454 23014 : CKeyMetadata metadata(nCreationTime);
455 :
456 : // use HD key derivation if HD was enabled during wallet creation and a seed is present
457 11507 : if (IsHDEnabled()) {
458 11506 : DeriveNewChildKey(batch, metadata, secret, type);
459 : } else {
460 1 : secret.MakeNewKey(fCompressed);
461 : }
462 :
463 : // Compressed public keys were introduced in version 0.6.0
464 11507 : if (fCompressed) {
465 11207 : wallet->SetMinVersion(FEATURE_COMPRPUBKEY);
466 : }
467 :
468 11507 : CPubKey pubkey = secret.GetPubKey();
469 11507 : assert(secret.VerifyPubKey(pubkey));
470 :
471 11507 : wallet->mapKeyMetadata[pubkey.GetID()] = metadata;
472 11507 : UpdateTimeFirstKey(nCreationTime);
473 :
474 11507 : if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) {
475 0 : throw std::runtime_error(std::string(__func__) + ": AddKey failed");
476 : }
477 23014 : return pubkey;
478 : }
479 :
480 11506 : void ScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, const uint8_t& changeType)
481 : {
482 11506 : AssertLockHeld(wallet->cs_wallet);
483 : // Use BIP44 keypath scheme i.e. m / purpose' / coin_type' / account' / change / address_index
484 11506 : CKey seed; //seed (256bit)
485 23012 : CExtKey masterKey; //hd master key
486 23012 : CExtKey purposeKey; //key at m/purpose' --> key at m/44'
487 23012 : CExtKey cointypeKey; //key at m/purpose'/coin_type' --> key at m/44'/119'
488 23012 : CExtKey accountKey; //key at m/purpose'/coin_type'/account' ---> key at m/44'/119'/account_num'
489 23012 : CExtKey changeKey; //key at m/purpose'/coin_type'/account'/change ---> key at m/44'/119'/account_num'/change', external = 0' or internal = 1'.
490 23012 : CExtKey childKey; //key at m/purpose'/coin_type'/account'/change/address_index ---> key at m/44'/119'/account_num'/change'/<n>'
491 :
492 : // For now only one account.
493 11506 : int nAccountNumber = 0;
494 :
495 : // try to get the seed
496 11506 : if (!wallet->GetKey(hdChain.GetID(), seed))
497 0 : throw std::runtime_error(std::string(__func__) + ": seed not found");
498 :
499 23012 : masterKey.SetSeed(seed.begin(), seed.size());
500 :
501 : // derive m/0'
502 : // use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
503 11506 : masterKey.Derive(purposeKey, 44 | BIP32_HARDENED_KEY_LIMIT);
504 : // derive m/purpose'/coin_type'
505 11506 : purposeKey.Derive(cointypeKey, 119 | BIP32_HARDENED_KEY_LIMIT);
506 : // derive m/purpose'/coin_type'/account' // Hardcoded to account 0 for now.
507 11506 : cointypeKey.Derive(accountKey, nAccountNumber | BIP32_HARDENED_KEY_LIMIT);
508 : // derive m/purpose'/coin_type'/account'/change'
509 11506 : accountKey.Derive(changeKey, changeType | BIP32_HARDENED_KEY_LIMIT);
510 :
511 : // derive child key at next index, skip keys already known to the wallet
512 11506 : do {
513 : // always derive hardened keys
514 : // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range
515 : // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
516 :
517 : // m/44'/119'/account_num/change'/<n>'
518 11506 : metadata.key_origin.path.push_back(44 | BIP32_HARDENED_KEY_LIMIT);
519 11506 : metadata.key_origin.path.push_back(119 | BIP32_HARDENED_KEY_LIMIT);
520 11506 : metadata.key_origin.path.push_back(nAccountNumber | BIP32_HARDENED_KEY_LIMIT);
521 : // Child chain counter
522 11506 : uint32_t& chainCounter = hdChain.GetChainCounter(changeType);
523 11506 : changeKey.Derive(childKey, chainCounter | BIP32_HARDENED_KEY_LIMIT);
524 11506 : metadata.key_origin.path.push_back( changeType | BIP32_HARDENED_KEY_LIMIT);
525 11506 : metadata.key_origin.path.push_back(chainCounter | BIP32_HARDENED_KEY_LIMIT);
526 11506 : chainCounter++;
527 :
528 11506 : } while (wallet->HaveKey(childKey.key.GetPubKey().GetID()));
529 :
530 11506 : secret = childKey.key;
531 11506 : metadata.hd_seed_id = hdChain.GetID();
532 11506 : CKeyID master_id = masterKey.key.GetPubKey().GetID();
533 11506 : std::copy(master_id.begin(), master_id.begin() + 4, metadata.key_origin.fingerprint);
534 : // update the chain model in the database
535 11506 : if (!batch.WriteHDChain(hdChain))
536 0 : throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
537 11506 : }
538 :
539 2307 : void ScriptPubKeyMan::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
540 : {
541 2307 : AssertLockHeld(wallet->cs_wallet);
542 2307 : if (keypool.m_pre_split) {
543 180 : set_pre_split_keypool.insert(nIndex);
544 2127 : } else if (keypool.IsInternal()) {
545 741 : setInternalKeyPool.insert(nIndex);
546 1386 : } else if (keypool.IsExternal()){
547 633 : setExternalKeyPool.insert(nIndex);
548 753 : } else if (keypool.IsStaking()){
549 753 : setStakingKeyPool.insert(nIndex);
550 : } else {
551 0 : throw std::runtime_error(std::string(__func__) + ": invalid CKeypool type");
552 : }
553 2307 : m_max_keypool_index = std::max(m_max_keypool_index, nIndex);
554 2307 : m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex;
555 :
556 : // If no metadata exists yet, create a default with the pool key's
557 : // creation time. Note that this may be overwritten by actually
558 : // stored metadata for that key later, which is fine.
559 2307 : CKeyID keyid = keypool.vchPubKey.GetID();
560 2307 : if (wallet->mapKeyMetadata.count(keyid) == 0)
561 4614 : wallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
562 2307 : }
563 :
564 : /**
565 : * Update wallet first key creation time. This should be called whenever keys
566 : * are added to the wallet, with the oldest key creation time.
567 : */
568 11507 : void ScriptPubKeyMan::UpdateTimeFirstKey(int64_t nCreateTime)
569 : {
570 11507 : AssertLockHeld(wallet->cs_wallet);
571 11507 : if (nCreateTime <= 1) {
572 : // Cannot determine birthday information, so set the wallet birthday to
573 : // the beginning of time.
574 0 : wallet->nTimeFirstKey = 1;
575 11507 : } else if (!wallet->nTimeFirstKey || nCreateTime < wallet->nTimeFirstKey) {
576 213 : wallet->nTimeFirstKey = nCreateTime;
577 : }
578 11507 : }
579 :
580 11507 : bool ScriptPubKeyMan::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const CPubKey& pubkey)
581 : {
582 11507 : AssertLockHeld(wallet->cs_wallet);
583 :
584 : // Make sure we aren't adding private keys to private key disabled wallets
585 : //assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
586 :
587 : // FillableSigningProvider has no concept of wallet databases, but calls AddCryptedKey
588 : // which is overridden below. To avoid flushes, the database handle is
589 : // tunneled through to it.
590 11507 : bool needsDB = !encrypted_batch;
591 11507 : if (needsDB) {
592 11507 : encrypted_batch = &batch;
593 : }
594 11507 : if (!AddKeyPubKeyInner(secret, pubkey)) {
595 0 : if (needsDB) encrypted_batch = nullptr;
596 0 : return false;
597 : }
598 11507 : if (needsDB) encrypted_batch = nullptr;
599 : return true;
600 : }
601 :
602 11507 : bool ScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)
603 : {
604 23014 : LOCK(wallet->cs_KeyStore);
605 11507 : if (!wallet->HasEncryptionKeys()) {
606 10546 : return wallet->AddKeyPubKey(key, pubkey);
607 : }
608 :
609 961 : if (wallet->IsLocked()) {
610 : return false;
611 : }
612 :
613 12468 : std::vector<unsigned char> vchCryptedSecret;
614 2883 : CKeyingMaterial vchSecret(key.begin(), key.end());
615 961 : if (!EncryptSecret(wallet->GetEncryptionKey(), vchSecret, pubkey.GetHash(), vchCryptedSecret)) {
616 : return false;
617 : }
618 :
619 961 : if (!wallet->AddCryptedKey(pubkey, vchCryptedSecret)) {
620 0 : return false;
621 : }
622 : return true;
623 : }
624 :
625 : ////////////////////// Seed Generation ///////////////////////////////////
626 :
627 245 : CPubKey ScriptPubKeyMan::GenerateNewSeed()
628 : {
629 245 : CKey secret;
630 245 : secret.MakeNewKey(true);
631 490 : return DeriveNewSeed(secret);
632 : }
633 :
634 247 : CPubKey ScriptPubKeyMan::DeriveNewSeed(const CKey& key)
635 : {
636 247 : int64_t nCreationTime = GetTime();
637 247 : CKeyMetadata metadata(nCreationTime);
638 :
639 : // Calculate the seed
640 247 : CPubKey seed = key.GetPubKey();
641 247 : assert(key.VerifyPubKey(seed));
642 :
643 : // Set seed id
644 247 : metadata.hd_seed_id = seed.GetID();
645 :
646 247 : {
647 247 : LOCK(wallet->cs_wallet);
648 :
649 : // mem store the metadata
650 247 : wallet->mapKeyMetadata[seed.GetID()] = metadata;
651 :
652 : // write the key&metadata to the database
653 247 : if (!wallet->AddKeyPubKey(key, seed))
654 0 : throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed");
655 : }
656 :
657 247 : return seed;
658 : }
659 :
660 :
661 : //////////////////////////////////////////////////////////////////////
662 :
663 247 : void ScriptPubKeyMan::SetHDSeed(const CPubKey& seed, bool force, bool memOnly)
664 : {
665 247 : if (!hdChain.IsNull() && !force)
666 0 : throw std::runtime_error(std::string(__func__) + ": trying to set a hd seed on an already created chain");
667 :
668 247 : LOCK(wallet->cs_wallet);
669 : // store the keyid (hash160) together with
670 : // the child index counter in the database
671 : // as a hdChain object
672 247 : CHDChain newHdChain;
673 247 : if (!newHdChain.SetSeed(seed.GetID()) ) {
674 0 : throw std::runtime_error(std::string(__func__) + ": set hd seed failed");
675 : }
676 :
677 247 : SetHDChain(newHdChain, memOnly);
678 : // TODO: Connect this if is needed.
679 : //NotifyCanGetAddressesChanged();
680 247 : }
681 :
682 406 : void ScriptPubKeyMan::SetHDChain(CHDChain& chain, bool memonly)
683 : {
684 406 : LOCK(wallet->cs_wallet);
685 650 : if (!memonly && !WalletBatch(wallet->GetDBHandle()).WriteHDChain(chain))
686 0 : throw std::runtime_error(std::string(__func__) + ": writing chain failed");
687 :
688 406 : hdChain = chain;
689 :
690 : // Sanity check
691 406 : if (!wallet->HaveKey(hdChain.GetID()))
692 0 : throw std::runtime_error(std::string(__func__) + ": Not found seed in wallet");
693 406 : }
|