LCOV - code coverage report
Current view: top level - src/wallet/rpc - encrypt.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 176 182 96.7 %
Date: 2022-04-21 14:51:19 Functions: 9 9 100.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 31 40 77.5 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2011-2021 The Bitcoin Core developers
#       2                 :            : // Distributed under the MIT software license, see the accompanying
#       3                 :            : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#       4                 :            : 
#       5                 :            : #include <rpc/util.h>
#       6                 :            : #include <wallet/rpc/util.h>
#       7                 :            : #include <wallet/wallet.h>
#       8                 :            : 
#       9                 :            : 
#      10                 :            : namespace wallet {
#      11                 :            : RPCHelpMan walletpassphrase()
#      12                 :       1662 : {
#      13                 :       1662 :     return RPCHelpMan{"walletpassphrase",
#      14                 :       1662 :                 "\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
#      15                 :       1662 :                 "This is needed prior to performing transactions related to private keys such as sending bitcoins\n"
#      16                 :       1662 :             "\nNote:\n"
#      17                 :       1662 :             "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n"
#      18                 :       1662 :             "time that overrides the old one.\n",
#      19                 :       1662 :                 {
#      20                 :       1662 :                     {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet passphrase"},
#      21                 :       1662 :                     {"timeout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."},
#      22                 :       1662 :                 },
#      23                 :       1662 :                 RPCResult{RPCResult::Type::NONE, "", ""},
#      24                 :       1662 :                 RPCExamples{
#      25                 :       1662 :             "\nUnlock the wallet for 60 seconds\n"
#      26                 :       1662 :             + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
#      27                 :       1662 :             "\nLock the wallet again (before 60 seconds)\n"
#      28                 :       1662 :             + HelpExampleCli("walletlock", "") +
#      29                 :       1662 :             "\nAs a JSON-RPC call\n"
#      30                 :       1662 :             + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
#      31                 :       1662 :                 },
#      32                 :       1662 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
#      33                 :       1662 : {
#      34                 :         78 :     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
#      35         [ -  + ]:         78 :     if (!wallet) return NullUniValue;
#      36                 :         78 :     CWallet* const pwallet = wallet.get();
#      37                 :            : 
#      38                 :         78 :     int64_t nSleepTime;
#      39                 :         78 :     int64_t relock_time;
#      40                 :            :     // Prevent concurrent calls to walletpassphrase with the same wallet.
#      41                 :         78 :     LOCK(pwallet->m_unlock_mutex);
#      42                 :         78 :     {
#      43                 :         78 :         LOCK(pwallet->cs_wallet);
#      44                 :            : 
#      45         [ +  + ]:         78 :         if (!pwallet->IsCrypted()) {
#      46                 :          8 :             throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
#      47                 :          8 :         }
#      48                 :            : 
#      49                 :            :         // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
#      50                 :         70 :         SecureString strWalletPass;
#      51                 :         70 :         strWalletPass.reserve(100);
#      52                 :            :         // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
#      53                 :            :         // Alternately, find a way to make request.params[0] mlock()'d to begin with.
#      54                 :         70 :         strWalletPass = request.params[0].get_str().c_str();
#      55                 :            : 
#      56                 :            :         // Get the timeout
#      57                 :         70 :         nSleepTime = request.params[1].get_int64();
#      58                 :            :         // Timeout cannot be negative, otherwise it will relock immediately
#      59         [ +  + ]:         70 :         if (nSleepTime < 0) {
#      60                 :          2 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
#      61                 :          2 :         }
#      62                 :            :         // Clamp timeout
#      63                 :         68 :         constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
#      64         [ +  + ]:         68 :         if (nSleepTime > MAX_SLEEP_TIME) {
#      65                 :          2 :             nSleepTime = MAX_SLEEP_TIME;
#      66                 :          2 :         }
#      67                 :            : 
#      68         [ +  + ]:         68 :         if (strWalletPass.empty()) {
#      69                 :          2 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase cannot be empty");
#      70                 :          2 :         }
#      71                 :            : 
#      72         [ +  + ]:         66 :         if (!pwallet->Unlock(strWalletPass)) {
#      73                 :          4 :             throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
#      74                 :          4 :         }
#      75                 :            : 
#      76                 :         62 :         pwallet->TopUpKeyPool();
#      77                 :            : 
#      78                 :         62 :         pwallet->nRelockTime = GetTime() + nSleepTime;
#      79                 :         62 :         relock_time = pwallet->nRelockTime;
#      80                 :         62 :     }
#      81                 :            : 
#      82                 :            :     // rpcRunLater must be called without cs_wallet held otherwise a deadlock
#      83                 :            :     // can occur. The deadlock would happen when RPCRunLater removes the
#      84                 :            :     // previous timer (and waits for the callback to finish if already running)
#      85                 :            :     // and the callback locks cs_wallet.
#      86                 :         62 :     AssertLockNotHeld(wallet->cs_wallet);
#      87                 :            :     // Keep a weak pointer to the wallet so that it is possible to unload the
#      88                 :            :     // wallet before the following callback is called. If a valid shared pointer
#      89                 :            :     // is acquired in the callback then the wallet is still loaded.
#      90                 :         62 :     std::weak_ptr<CWallet> weak_wallet = wallet;
#      91                 :         62 :     pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] {
#      92         [ +  + ]:          7 :         if (auto shared_wallet = weak_wallet.lock()) {
#      93                 :          4 :             LOCK(shared_wallet->cs_wallet);
#      94                 :            :             // Skip if this is not the most recent rpcRunLater callback.
#      95         [ -  + ]:          4 :             if (shared_wallet->nRelockTime != relock_time) return;
#      96                 :          4 :             shared_wallet->Lock();
#      97                 :          4 :             shared_wallet->nRelockTime = 0;
#      98                 :          4 :         }
#      99                 :          7 :     }, nSleepTime);
#     100                 :            : 
#     101                 :         62 :     return NullUniValue;
#     102                 :         66 : },
#     103                 :       1662 :     };
#     104                 :       1662 : }
#     105                 :            : 
#     106                 :            : 
#     107                 :            : RPCHelpMan walletpassphrasechange()
#     108                 :       1590 : {
#     109                 :       1590 :     return RPCHelpMan{"walletpassphrasechange",
#     110                 :       1590 :                 "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
#     111                 :       1590 :                 {
#     112                 :       1590 :                     {"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The current passphrase"},
#     113                 :       1590 :                     {"newpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The new passphrase"},
#     114                 :       1590 :                 },
#     115                 :       1590 :                 RPCResult{RPCResult::Type::NONE, "", ""},
#     116                 :       1590 :                 RPCExamples{
#     117                 :       1590 :                     HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"")
#     118                 :       1590 :             + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")
#     119                 :       1590 :                 },
#     120                 :       1590 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
#     121                 :       1590 : {
#     122                 :          6 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
#     123         [ -  + ]:          6 :     if (!pwallet) return NullUniValue;
#     124                 :            : 
#     125                 :          6 :     LOCK(pwallet->cs_wallet);
#     126                 :            : 
#     127         [ +  + ]:          6 :     if (!pwallet->IsCrypted()) {
#     128                 :          2 :         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
#     129                 :          2 :     }
#     130                 :            : 
#     131                 :            :     // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
#     132                 :            :     // Alternately, find a way to make request.params[0] mlock()'d to begin with.
#     133                 :          4 :     SecureString strOldWalletPass;
#     134                 :          4 :     strOldWalletPass.reserve(100);
#     135                 :          4 :     strOldWalletPass = request.params[0].get_str().c_str();
#     136                 :            : 
#     137                 :          4 :     SecureString strNewWalletPass;
#     138                 :          4 :     strNewWalletPass.reserve(100);
#     139                 :          4 :     strNewWalletPass = request.params[1].get_str().c_str();
#     140                 :            : 
#     141 [ +  + ][ -  + ]:          4 :     if (strOldWalletPass.empty() || strNewWalletPass.empty()) {
#     142                 :          2 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase cannot be empty");
#     143                 :          2 :     }
#     144                 :            : 
#     145         [ -  + ]:          2 :     if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
#     146                 :          0 :         throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
#     147                 :          0 :     }
#     148                 :            : 
#     149                 :          2 :     return NullUniValue;
#     150                 :          2 : },
#     151                 :       1590 :     };
#     152                 :       1590 : }
#     153                 :            : 
#     154                 :            : 
#     155                 :            : RPCHelpMan walletlock()
#     156                 :       1600 : {
#     157                 :       1600 :     return RPCHelpMan{"walletlock",
#     158                 :       1600 :                 "\nRemoves the wallet encryption key from memory, locking the wallet.\n"
#     159                 :       1600 :                 "After calling this method, you will need to call walletpassphrase again\n"
#     160                 :       1600 :                 "before being able to call any methods which require the wallet to be unlocked.\n",
#     161                 :       1600 :                 {},
#     162                 :       1600 :                 RPCResult{RPCResult::Type::NONE, "", ""},
#     163                 :       1600 :                 RPCExamples{
#     164                 :       1600 :             "\nSet the passphrase for 2 minutes to perform a transaction\n"
#     165                 :       1600 :             + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
#     166                 :       1600 :             "\nPerform a send (requires passphrase set)\n"
#     167                 :       1600 :             + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 1.0") +
#     168                 :       1600 :             "\nClear the passphrase since we are done before 2 minutes is up\n"
#     169                 :       1600 :             + HelpExampleCli("walletlock", "") +
#     170                 :       1600 :             "\nAs a JSON-RPC call\n"
#     171                 :       1600 :             + HelpExampleRpc("walletlock", "")
#     172                 :       1600 :                 },
#     173                 :       1600 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
#     174                 :       1600 : {
#     175                 :         16 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
#     176         [ -  + ]:         16 :     if (!pwallet) return NullUniValue;
#     177                 :            : 
#     178                 :         16 :     LOCK(pwallet->cs_wallet);
#     179                 :            : 
#     180         [ -  + ]:         16 :     if (!pwallet->IsCrypted()) {
#     181                 :          0 :         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
#     182                 :          0 :     }
#     183                 :            : 
#     184                 :         16 :     pwallet->Lock();
#     185                 :         16 :     pwallet->nRelockTime = 0;
#     186                 :            : 
#     187                 :         16 :     return NullUniValue;
#     188                 :         16 : },
#     189                 :       1600 :     };
#     190                 :       1600 : }
#     191                 :            : 
#     192                 :            : 
#     193                 :            : RPCHelpMan encryptwallet()
#     194                 :       1616 : {
#     195                 :       1616 :     return RPCHelpMan{"encryptwallet",
#     196                 :       1616 :                 "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
#     197                 :       1616 :                 "After this, any calls that interact with private keys such as sending or signing \n"
#     198                 :       1616 :                 "will require the passphrase to be set prior the making these calls.\n"
#     199                 :       1616 :                 "Use the walletpassphrase call for this, and then walletlock call.\n"
#     200                 :       1616 :                 "If the wallet is already encrypted, use the walletpassphrasechange call.\n",
#     201                 :       1616 :                 {
#     202                 :       1616 :                     {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long."},
#     203                 :       1616 :                 },
#     204                 :       1616 :                 RPCResult{RPCResult::Type::STR, "", "A string with further instructions"},
#     205                 :       1616 :                 RPCExamples{
#     206                 :       1616 :             "\nEncrypt your wallet\n"
#     207                 :       1616 :             + HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
#     208                 :       1616 :             "\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n"
#     209                 :       1616 :             + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") +
#     210                 :       1616 :             "\nNow we can do something like sign\n"
#     211                 :       1616 :             + HelpExampleCli("signmessage", "\"address\" \"test message\"") +
#     212                 :       1616 :             "\nNow lock the wallet again by removing the passphrase\n"
#     213                 :       1616 :             + HelpExampleCli("walletlock", "") +
#     214                 :       1616 :             "\nAs a JSON-RPC call\n"
#     215                 :       1616 :             + HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
#     216                 :       1616 :                 },
#     217                 :       1616 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
#     218                 :       1616 : {
#     219                 :         32 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
#     220         [ -  + ]:         32 :     if (!pwallet) return NullUniValue;
#     221                 :            : 
#     222                 :         32 :     LOCK(pwallet->cs_wallet);
#     223                 :            : 
#     224         [ +  + ]:         32 :     if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
#     225                 :          3 :         throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet does not contain private keys, nothing to encrypt.");
#     226                 :          3 :     }
#     227                 :            : 
#     228         [ +  + ]:         29 :     if (pwallet->IsCrypted()) {
#     229                 :          2 :         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
#     230                 :          2 :     }
#     231                 :            : 
#     232                 :            :     // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
#     233                 :            :     // Alternately, find a way to make request.params[0] mlock()'d to begin with.
#     234                 :         27 :     SecureString strWalletPass;
#     235                 :         27 :     strWalletPass.reserve(100);
#     236                 :         27 :     strWalletPass = request.params[0].get_str().c_str();
#     237                 :            : 
#     238         [ +  + ]:         27 :     if (strWalletPass.empty()) {
#     239                 :          2 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase cannot be empty");
#     240                 :          2 :     }
#     241                 :            : 
#     242         [ -  + ]:         25 :     if (!pwallet->EncryptWallet(strWalletPass)) {
#     243                 :          0 :         throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
#     244                 :          0 :     }
#     245                 :            : 
#     246                 :         25 :     return "wallet encrypted; The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
#     247                 :         25 : },
#     248                 :       1616 :     };
#     249                 :       1616 : }
#     250                 :            : } // namespace wallet

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a