LCOV - code coverage report
Current view: top level - src/wallet - scriptpubkeyman.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 340 376 90.4 %
Date: 2025-02-23 09:33:43 Functions: 32 32 100.0 %

          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 : }

Generated by: LCOV version 1.14