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 188 : bool ScriptPubKeyMan::SetupGeneration(bool newKeypool, bool force, bool memOnly)
11 : {
12 188 : if (CanGenerateKeys() && !force) {
13 : return false;
14 : }
15 :
16 188 : SetHDSeed(GenerateNewSeed(), force, memOnly);
17 188 : 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 3855 : 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 3855 : LOCK(wallet->cs_wallet);
56 7716 : return IsHDEnabled() || wallet->GetVersion() < FEATURE_PRE_SPLIT_KEYPOOL;
57 : }
58 :
59 30572 : bool ScriptPubKeyMan::IsHDEnabled() const
60 : {
61 30572 : return !hdChain.IsNull();
62 : }
63 :
64 3295 : bool ScriptPubKeyMan::CanGetAddresses(const uint8_t& type)
65 : {
66 6590 : LOCK(wallet->cs_wallet);
67 : // Check if the keypool has keys
68 3295 : const bool isHDEnabled = IsHDEnabled();
69 3295 : bool keypool_has_keys = false;
70 3295 : if (isHDEnabled && type == HDChain::ChangeType::INTERNAL) {
71 431 : keypool_has_keys = setInternalKeyPool.size() > 0;
72 2864 : } 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 2850 : keypool_has_keys = KeypoolCountExternalKeys() > 0;
77 : }
78 : // If the keypool doesn't have keys, check if we can generate them
79 3295 : 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 2917 : size_t ScriptPubKeyMan::KeypoolCountExternalKeys()
118 : {
119 2917 : AssertLockHeld(wallet->cs_wallet);
120 2917 : return setExternalKeyPool.size() + set_pre_split_keypool.size();
121 : }
122 :
123 372 : unsigned int ScriptPubKeyMan::GetKeyPoolSize() const
124 : {
125 372 : AssertLockHeld(wallet->cs_wallet);
126 372 : 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 1125 : bool ScriptPubKeyMan::GetKeyFromPool(CPubKey& result, const uint8_t& changeType)
136 : {
137 1125 : if (!CanGetAddresses(changeType)) {
138 0 : LogPrintf("%s: Cannot get address, type: %d\n", __func__ ,changeType);
139 0 : return false;
140 : }
141 :
142 1125 : CKeyPool keypool;
143 1125 : {
144 1125 : LOCK(wallet->cs_wallet);
145 1125 : int64_t nIndex;
146 1125 : 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 1121 : KeepDestination(nIndex);
156 1121 : result = keypool.vchPubKey;
157 : }
158 1121 : return true;
159 : }
160 :
161 2170 : bool ScriptPubKeyMan::GetReservedKey(const uint8_t& changeType, int64_t& index, CKeyPool& keypool)
162 : {
163 2170 : if (!CanGetAddresses(changeType)) {
164 0 : return error("%s : Cannot get address for change type %d", __func__, changeType);
165 : }
166 2170 : 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 3295 : bool ScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, const uint8_t& type)
173 : {
174 3295 : nIndex = -1;
175 3295 : keypool.vchPubKey = CPubKey();
176 3295 : {
177 3295 : LOCK(wallet->cs_wallet);
178 :
179 3295 : bool isHDEnabled = IsHDEnabled();
180 3295 : bool fReturningInternal = type == HDChain::ChangeType::INTERNAL && isHDEnabled;
181 3295 : bool fReturningStaking = type == HDChain::ChangeType::STAKING && isHDEnabled;
182 3295 : bool use_split_keypool = set_pre_split_keypool.empty();
183 3295 : 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 3295 : if (setKeyPool.empty()) {
188 16 : return false;
189 : }
190 :
191 6574 : WalletBatch batch(wallet->GetDBHandle());
192 :
193 3287 : auto it = setKeyPool.begin();
194 3287 : nIndex = *it;
195 3287 : setKeyPool.erase(it);
196 3287 : if (!batch.ReadPool(nIndex, keypool)) {
197 0 : throw std::runtime_error(std::string(__func__) + ": read failed");
198 : }
199 3287 : CPubKey pk;
200 3287 : 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 3287 : if (use_split_keypool && keypool.IsInternal() != fReturningInternal) {
205 0 : throw std::runtime_error(std::string(__func__) + ": keypool internal entry misclassified");
206 : }
207 :
208 3287 : if (use_split_keypool && keypool.IsStaking() != fReturningStaking) {
209 0 : throw std::runtime_error(std::string(__func__) + ": keypool staking entry misclassified");
210 : }
211 :
212 3287 : if (!keypool.vchPubKey.IsValid()) {
213 0 : throw std::runtime_error(std::string(__func__) + ": keypool entry invalid");
214 : }
215 :
216 3287 : assert(m_index_to_reserved_key.count(nIndex) == 0);
217 3287 : m_index_to_reserved_key[nIndex] = keypool.vchPubKey.GetID();
218 3287 : m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
219 3287 : LogPrintf("%s: keypool reserve %d\n", __func__, nIndex);
220 : }
221 : //NotifyCanGetAddressesChanged();
222 3287 : return true;
223 : }
224 :
225 3272 : void ScriptPubKeyMan::KeepDestination(int64_t nIndex)
226 : {
227 : // Remove from key pool
228 3272 : WalletBatch batch(wallet->GetDBHandle());
229 3272 : batch.ErasePool(nIndex);
230 3272 : CPubKey pubkey;
231 3272 : bool have_pk = wallet->GetPubKey(m_index_to_reserved_key.at(nIndex), pubkey);
232 3272 : assert(have_pk);
233 3272 : m_index_to_reserved_key.erase(nIndex);
234 3272 : LogPrintf("keypool keep %d\n", nIndex);
235 3272 : }
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 46 : void ScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id)
261 : {
262 46 : AssertLockHeld(wallet->cs_wallet);
263 46 : bool internal = setInternalKeyPool.count(keypool_id);
264 46 : bool staking = setStakingKeyPool.count(keypool_id);
265 78 : if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id) || staking);
266 46 : std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ?
267 32 : (staking ? &setStakingKeyPool : &setExternalKeyPool) : &set_pre_split_keypool);
268 46 : auto it = setKeyPool->begin();
269 :
270 46 : WalletBatch batch(wallet->GetDBHandle());
271 204 : while (it != std::end(*setKeyPool)) {
272 183 : const int64_t& index = *(it);
273 183 : if (index > keypool_id) break; // set*KeyPool is ordered
274 :
275 158 : CKeyPool keypool;
276 158 : if (batch.ReadPool(index, keypool)) {
277 158 : const CKeyID& keyid = keypool.vchPubKey.GetID();
278 158 : m_pool_key_to_index.erase(keyid);
279 : // add missing receive addresses to the AddressBook
280 301 : if (!internal && !wallet->HasAddressBook(keyid)) {
281 572 : wallet->SetAddressBook(keyid, "", staking ? AddressBook::AddressBookPurpose::COLD_STAKING
282 : : AddressBook::AddressBookPurpose::RECEIVE);
283 : }
284 : }
285 158 : batch.ErasePool(index);
286 158 : LogPrintf("keypool index %d removed\n", index);
287 158 : it = setKeyPool->erase(it);
288 : }
289 46 : }
290 :
291 24320 : void ScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
292 : {
293 24320 : AssertLockHeld(wallet->cs_wallet);
294 : // extract addresses and check if they match with an unused keypool key
295 43457 : for (const auto& keyid : wallet->GetAffectedKeys(script)) {
296 19137 : std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid);
297 19137 : if (mi != m_pool_key_to_index.end()) {
298 46 : LogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__);
299 46 : MarkReserveKeysAsUsed(mi->second);
300 :
301 46 : if (!TopUp()) {
302 0 : LogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
303 : }
304 : }
305 : }
306 24320 : }
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 160 : bool ScriptPubKeyMan::NewKeyPool()
331 : {
332 160 : {
333 160 : LOCK(wallet->cs_wallet);
334 :
335 320 : WalletBatch batch(wallet->GetDBHandle());
336 : // Internal
337 356 : for (const int64_t nIndex : setInternalKeyPool) {
338 196 : batch.ErasePool(nIndex);
339 : }
340 160 : setInternalKeyPool.clear();
341 :
342 : // External
343 352 : for (const int64_t nIndex : setExternalKeyPool) {
344 192 : batch.ErasePool(nIndex);
345 : }
346 160 : setExternalKeyPool.clear();
347 :
348 : // Staking
349 357 : for (const int64_t nIndex : setStakingKeyPool) {
350 197 : batch.ErasePool(nIndex);
351 : }
352 160 : setStakingKeyPool.clear();
353 :
354 : // key -> index.
355 160 : m_pool_key_to_index.clear();
356 :
357 160 : if (!TopUp()) {
358 0 : return false;
359 : }
360 160 : LogPrintf("ScriptPubKeyMan::NewKeyPool rewrote keypool\n");
361 : }
362 160 : return true;
363 : }
364 :
365 : /**
366 : * Fill the key pool
367 : */
368 3659 : bool ScriptPubKeyMan::TopUp(unsigned int kpSize)
369 : {
370 3659 : if (!CanGenerateKeys()) {
371 : return false;
372 : }
373 3659 : {
374 3659 : LOCK(wallet->cs_wallet);
375 3671 : if (wallet->IsLocked()) return false;
376 :
377 : // Top up key pool
378 3647 : unsigned int nTargetSize;
379 3647 : if (kpSize > 0)
380 : nTargetSize = kpSize;
381 : else
382 3641 : 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 3750 : int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setExternalKeyPool.size(), (int64_t) 0);
387 3750 : int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setInternalKeyPool.size(), (int64_t) 0);
388 3750 : int64_t missingStaking = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setStakingKeyPool.size(), (int64_t) 0);
389 :
390 3647 : if (!IsHDEnabled()) {
391 : // don't create extra internal or staking keys
392 1 : missingInternal = 0;
393 1 : missingStaking = 0;
394 : }
395 :
396 7294 : WalletBatch batch(wallet->GetDBHandle());
397 3647 : GeneratePool(batch, missingExternal, HDChain::ChangeType::EXTERNAL);
398 3647 : GeneratePool(batch, missingInternal, HDChain::ChangeType::INTERNAL);
399 3647 : GeneratePool(batch, missingStaking, HDChain::ChangeType::STAKING);
400 :
401 3647 : if (missingInternal + missingExternal > 0) {
402 3233 : 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 3647 : if (missingStaking > 0) {
405 183 : LogPrintf("keypool added %d staking keys\n", setStakingKeyPool.size());
406 : }
407 : }
408 : // TODO: Implement this.
409 : //NotifyCanGetAddressesChanged();
410 3647 : return true;
411 : }
412 :
413 10941 : void ScriptPubKeyMan::GeneratePool(WalletBatch& batch, int64_t targetSize, const uint8_t& type)
414 : {
415 19102 : for (int64_t i = targetSize; i--;) {
416 8161 : CPubKey pubkey(GenerateNewKey(batch, type));
417 8161 : AddKeypoolPubkeyWithDB(pubkey, type, batch);
418 : }
419 10941 : }
420 :
421 8161 : void ScriptPubKeyMan::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const uint8_t& type, WalletBatch &batch)
422 : {
423 8161 : LOCK(wallet->cs_wallet);
424 8161 : assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); // How in the hell did you use so many keys?
425 8161 : int64_t index = ++m_max_keypool_index;
426 8161 : if (!batch.WritePool(index, CKeyPool(pubkey, type))) {
427 0 : throw std::runtime_error(std::string(__func__) + ": writing imported pubkey failed");
428 : }
429 :
430 8161 : const bool isHDEnabled = IsHDEnabled();
431 8161 : if (isHDEnabled && type == HDChain::ChangeType::INTERNAL) {
432 2051 : setInternalKeyPool.insert(index);
433 6110 : } else if (isHDEnabled && type == HDChain::ChangeType::STAKING) {
434 1665 : setStakingKeyPool.insert(index);
435 : } else {
436 4445 : setExternalKeyPool.insert(index);
437 : }
438 :
439 8161 : m_pool_key_to_index[pubkey.GetID()] = index;
440 8161 : }
441 :
442 : /**
443 : * Generate a new key and stores it in db.
444 : */
445 8162 : CPubKey ScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, const uint8_t& type)
446 : {
447 8162 : AssertLockHeld(wallet->cs_wallet);
448 8162 : bool fCompressed = wallet->CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
449 :
450 8162 : CKey secret;
451 :
452 : // Create new metadata
453 8162 : int64_t nCreationTime = GetTime();
454 16324 : CKeyMetadata metadata(nCreationTime);
455 :
456 : // use HD key derivation if HD was enabled during wallet creation and a seed is present
457 8162 : if (IsHDEnabled()) {
458 8161 : 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 8162 : if (fCompressed) {
465 7862 : wallet->SetMinVersion(FEATURE_COMPRPUBKEY);
466 : }
467 :
468 8162 : CPubKey pubkey = secret.GetPubKey();
469 8162 : assert(secret.VerifyPubKey(pubkey));
470 :
471 8162 : wallet->mapKeyMetadata[pubkey.GetID()] = metadata;
472 8162 : UpdateTimeFirstKey(nCreationTime);
473 :
474 8162 : if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) {
475 0 : throw std::runtime_error(std::string(__func__) + ": AddKey failed");
476 : }
477 16324 : return pubkey;
478 : }
479 :
480 8161 : void ScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, const uint8_t& changeType)
481 : {
482 8161 : AssertLockHeld(wallet->cs_wallet);
483 : // Use BIP44 keypath scheme i.e. m / purpose' / coin_type' / account' / change / address_index
484 8161 : CKey seed; //seed (256bit)
485 16322 : CExtKey masterKey; //hd master key
486 16322 : CExtKey purposeKey; //key at m/purpose' --> key at m/44'
487 16322 : CExtKey cointypeKey; //key at m/purpose'/coin_type' --> key at m/44'/119'
488 16322 : CExtKey accountKey; //key at m/purpose'/coin_type'/account' ---> key at m/44'/119'/account_num'
489 16322 : CExtKey changeKey; //key at m/purpose'/coin_type'/account'/change ---> key at m/44'/119'/account_num'/change', external = 0' or internal = 1'.
490 16322 : 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 8161 : int nAccountNumber = 0;
494 :
495 : // try to get the seed
496 8161 : if (!wallet->GetKey(hdChain.GetID(), seed))
497 0 : throw std::runtime_error(std::string(__func__) + ": seed not found");
498 :
499 16322 : masterKey.SetSeed(seed.begin(), seed.size());
500 :
501 : // derive m/0'
502 : // use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
503 8161 : masterKey.Derive(purposeKey, 44 | BIP32_HARDENED_KEY_LIMIT);
504 : // derive m/purpose'/coin_type'
505 8161 : purposeKey.Derive(cointypeKey, 119 | BIP32_HARDENED_KEY_LIMIT);
506 : // derive m/purpose'/coin_type'/account' // Hardcoded to account 0 for now.
507 8161 : cointypeKey.Derive(accountKey, nAccountNumber | BIP32_HARDENED_KEY_LIMIT);
508 : // derive m/purpose'/coin_type'/account'/change'
509 8161 : accountKey.Derive(changeKey, changeType | BIP32_HARDENED_KEY_LIMIT);
510 :
511 : // derive child key at next index, skip keys already known to the wallet
512 8161 : 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 8161 : metadata.key_origin.path.push_back(44 | BIP32_HARDENED_KEY_LIMIT);
519 8161 : metadata.key_origin.path.push_back(119 | BIP32_HARDENED_KEY_LIMIT);
520 8161 : metadata.key_origin.path.push_back(nAccountNumber | BIP32_HARDENED_KEY_LIMIT);
521 : // Child chain counter
522 8161 : uint32_t& chainCounter = hdChain.GetChainCounter(changeType);
523 8161 : changeKey.Derive(childKey, chainCounter | BIP32_HARDENED_KEY_LIMIT);
524 8161 : metadata.key_origin.path.push_back( changeType | BIP32_HARDENED_KEY_LIMIT);
525 8161 : metadata.key_origin.path.push_back(chainCounter | BIP32_HARDENED_KEY_LIMIT);
526 8161 : chainCounter++;
527 :
528 8161 : } while (wallet->HaveKey(childKey.key.GetPubKey().GetID()));
529 :
530 8161 : secret = childKey.key;
531 8161 : metadata.hd_seed_id = hdChain.GetID();
532 8161 : CKeyID master_id = masterKey.key.GetPubKey().GetID();
533 8161 : std::copy(master_id.begin(), master_id.begin() + 4, metadata.key_origin.fingerprint);
534 : // update the chain model in the database
535 8161 : if (!batch.WriteHDChain(hdChain))
536 0 : throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
537 8161 : }
538 :
539 2287 : void ScriptPubKeyMan::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
540 : {
541 2287 : AssertLockHeld(wallet->cs_wallet);
542 2287 : if (keypool.m_pre_split) {
543 180 : set_pre_split_keypool.insert(nIndex);
544 2107 : } else if (keypool.IsInternal()) {
545 735 : setInternalKeyPool.insert(nIndex);
546 1372 : } else if (keypool.IsExternal()){
547 627 : setExternalKeyPool.insert(nIndex);
548 745 : } else if (keypool.IsStaking()){
549 745 : setStakingKeyPool.insert(nIndex);
550 : } else {
551 0 : throw std::runtime_error(std::string(__func__) + ": invalid CKeypool type");
552 : }
553 2287 : m_max_keypool_index = std::max(m_max_keypool_index, nIndex);
554 2287 : 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 2287 : CKeyID keyid = keypool.vchPubKey.GetID();
560 2287 : if (wallet->mapKeyMetadata.count(keyid) == 0)
561 4574 : wallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
562 2287 : }
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 8162 : void ScriptPubKeyMan::UpdateTimeFirstKey(int64_t nCreateTime)
569 : {
570 8162 : AssertLockHeld(wallet->cs_wallet);
571 8162 : 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 8162 : } else if (!wallet->nTimeFirstKey || nCreateTime < wallet->nTimeFirstKey) {
576 159 : wallet->nTimeFirstKey = nCreateTime;
577 : }
578 8162 : }
579 :
580 8162 : bool ScriptPubKeyMan::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const CPubKey& pubkey)
581 : {
582 8162 : 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 8162 : bool needsDB = !encrypted_batch;
591 8162 : if (needsDB) {
592 8162 : encrypted_batch = &batch;
593 : }
594 8162 : if (!AddKeyPubKeyInner(secret, pubkey)) {
595 0 : if (needsDB) encrypted_batch = nullptr;
596 0 : return false;
597 : }
598 8162 : if (needsDB) encrypted_batch = nullptr;
599 : return true;
600 : }
601 :
602 8162 : bool ScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)
603 : {
604 16324 : LOCK(wallet->cs_KeyStore);
605 8162 : if (!wallet->HasEncryptionKeys()) {
606 7201 : return wallet->AddKeyPubKey(key, pubkey);
607 : }
608 :
609 961 : if (wallet->IsLocked()) {
610 : return false;
611 : }
612 :
613 9123 : 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 191 : CPubKey ScriptPubKeyMan::GenerateNewSeed()
628 : {
629 191 : CKey secret;
630 191 : secret.MakeNewKey(true);
631 382 : return DeriveNewSeed(secret);
632 : }
633 :
634 193 : CPubKey ScriptPubKeyMan::DeriveNewSeed(const CKey& key)
635 : {
636 193 : int64_t nCreationTime = GetTime();
637 193 : CKeyMetadata metadata(nCreationTime);
638 :
639 : // Calculate the seed
640 193 : CPubKey seed = key.GetPubKey();
641 193 : assert(key.VerifyPubKey(seed));
642 :
643 : // Set seed id
644 193 : metadata.hd_seed_id = seed.GetID();
645 :
646 193 : {
647 193 : LOCK(wallet->cs_wallet);
648 :
649 : // mem store the metadata
650 193 : wallet->mapKeyMetadata[seed.GetID()] = metadata;
651 :
652 : // write the key&metadata to the database
653 193 : if (!wallet->AddKeyPubKey(key, seed))
654 0 : throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed");
655 : }
656 :
657 193 : return seed;
658 : }
659 :
660 :
661 : //////////////////////////////////////////////////////////////////////
662 :
663 193 : void ScriptPubKeyMan::SetHDSeed(const CPubKey& seed, bool force, bool memOnly)
664 : {
665 193 : 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 193 : 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 193 : CHDChain newHdChain;
673 193 : if (!newHdChain.SetSeed(seed.GetID()) ) {
674 0 : throw std::runtime_error(std::string(__func__) + ": set hd seed failed");
675 : }
676 :
677 193 : SetHDChain(newHdChain, memOnly);
678 : // TODO: Connect this if is needed.
679 : //NotifyCanGetAddressesChanged();
680 193 : }
681 :
682 344 : void ScriptPubKeyMan::SetHDChain(CHDChain& chain, bool memonly)
683 : {
684 344 : LOCK(wallet->cs_wallet);
685 534 : if (!memonly && !WalletBatch(wallet->GetDBHandle()).WriteHDChain(chain))
686 0 : throw std::runtime_error(std::string(__func__) + ": writing chain failed");
687 :
688 344 : hdChain = chain;
689 :
690 : // Sanity check
691 344 : if (!wallet->HaveKey(hdChain.GetID()))
692 0 : throw std::runtime_error(std::string(__func__) + ": Not found seed in wallet");
693 344 : }
|