Bitcoin Core  31.0.0
P2P Digital Currency
encrypt.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-present 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 <scheduler.h>
7 #include <wallet/context.h>
8 #include <wallet/rpc/util.h>
9 #include <wallet/wallet.h>
10 
11 
12 namespace wallet {
14 {
15  return RPCHelpMan{
16  "walletpassphrase",
17  "Stores the wallet decryption key in memory for 'timeout' seconds.\n"
18  "This is needed prior to performing transactions related to private keys such as sending bitcoins\n"
19  "\nNote:\n"
20  "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n"
21  "time that overrides the old one.\n",
22  {
23  {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet passphrase"},
24  {"timeout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."},
25  },
28  "\nUnlock the wallet for 60 seconds\n"
29  + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
30  "\nLock the wallet again (before 60 seconds)\n"
31  + HelpExampleCli("walletlock", "") +
32  "\nAs a JSON-RPC call\n"
33  + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
34  },
35  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
36 {
37  std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
38  if (!wallet) return UniValue::VNULL;
39  CWallet* const pwallet = wallet.get();
40 
41  int64_t nSleepTime;
42  int64_t relock_time;
43  // Prevent concurrent calls to walletpassphrase with the same wallet.
44  LOCK(pwallet->m_unlock_mutex);
45  {
46  LOCK(pwallet->cs_wallet);
47 
48  if (!pwallet->HasEncryptionKeys()) {
49  throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
50  }
51 
52  // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
53  SecureString strWalletPass;
54  strWalletPass.reserve(100);
55  strWalletPass = std::string_view{request.params[0].get_str()};
56 
57  // Get the timeout
58  nSleepTime = request.params[1].getInt<int64_t>();
59  // Timeout cannot be negative, otherwise it will relock immediately
60  if (nSleepTime < 0) {
61  throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
62  }
63  // Clamp timeout
64  constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
65  if (nSleepTime > MAX_SLEEP_TIME) {
66  nSleepTime = MAX_SLEEP_TIME;
67  }
68 
69  if (strWalletPass.empty()) {
70  throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase cannot be empty");
71  }
72 
73  if (!pwallet->Unlock(strWalletPass)) {
74  // Check if the passphrase has a null character (see #27067 for details)
75  if (strWalletPass.find('\0') == std::string::npos) {
76  throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
77  } else {
78  throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered is incorrect. "
79  "It contains a null character (ie - a zero byte). "
80  "If the passphrase was set with a version of this software prior to 25.0, "
81  "please try again with only the characters up to — but not including — "
82  "the first null character. If this is successful, please set a new "
83  "passphrase to avoid this issue in the future.");
84  }
85  }
86 
87  pwallet->TopUpKeyPool();
88 
89  pwallet->nRelockTime = GetTime() + nSleepTime;
90  relock_time = pwallet->nRelockTime;
91  }
92 
93  // Get wallet scheduler to queue up the relock callback in the future.
94  // Scheduled events don't get destructed until they are executed,
95  // and they are executed in series in a single scheduler thread so
96  // no cs_wallet lock is needed.
97  WalletContext& context = EnsureWalletContext(request.context);
98  // Keep a weak pointer to the wallet so that it is possible to unload the
99  // wallet before the following callback is called. If a valid shared pointer
100  // is acquired in the callback then the wallet is still loaded.
101  std::weak_ptr<CWallet> weak_wallet = wallet;
102  context.scheduler->scheduleFromNow([weak_wallet, relock_time] {
103  if (auto shared_wallet = weak_wallet.lock()) {
104  LOCK2(shared_wallet->m_relock_mutex, shared_wallet->cs_wallet);
105  // Skip if this is not the most recent relock callback.
106  if (shared_wallet->nRelockTime != relock_time) return;
107  shared_wallet->Lock();
108  shared_wallet->nRelockTime = 0;
109  }
110  }, std::chrono::seconds(nSleepTime));
111 
112  return UniValue::VNULL;
113 },
114  };
115 }
116 
117 
119 {
120  return RPCHelpMan{
121  "walletpassphrasechange",
122  "Changes the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
123  {
124  {"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The current passphrase"},
125  {"newpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The new passphrase"},
126  },
128  RPCExamples{
129  HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"")
130  + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")
131  },
132  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
133 {
134  std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
135  if (!pwallet) return UniValue::VNULL;
136 
137  if (!pwallet->HasEncryptionKeys()) {
138  throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
139  }
140 
141  if (pwallet->IsScanningWithPassphrase()) {
142  throw JSONRPCError(RPC_WALLET_ERROR, "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before changing the passphrase.");
143  }
144 
145  LOCK2(pwallet->m_relock_mutex, pwallet->cs_wallet);
146 
147  SecureString strOldWalletPass;
148  strOldWalletPass.reserve(100);
149  strOldWalletPass = std::string_view{request.params[0].get_str()};
150 
151  SecureString strNewWalletPass;
152  strNewWalletPass.reserve(100);
153  strNewWalletPass = std::string_view{request.params[1].get_str()};
154 
155  if (strOldWalletPass.empty() || strNewWalletPass.empty()) {
156  throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase cannot be empty");
157  }
158 
159  if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
160  // Check if the old passphrase had a null character (see #27067 for details)
161  if (strOldWalletPass.find('\0') == std::string::npos) {
162  throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
163  } else {
164  throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The old wallet passphrase entered is incorrect. "
165  "It contains a null character (ie - a zero byte). "
166  "If the old passphrase was set with a version of this software prior to 25.0, "
167  "please try again with only the characters up to — but not including — "
168  "the first null character.");
169  }
170  }
171 
172  return UniValue::VNULL;
173 },
174  };
175 }
176 
177 
179 {
180  return RPCHelpMan{
181  "walletlock",
182  "Removes the wallet encryption key from memory, locking the wallet.\n"
183  "After calling this method, you will need to call walletpassphrase again\n"
184  "before being able to call any methods which require the wallet to be unlocked.\n",
185  {},
187  RPCExamples{
188  "\nSet the passphrase for 2 minutes to perform a transaction\n"
189  + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
190  "\nPerform a send (requires passphrase set)\n"
191  + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 1.0") +
192  "\nClear the passphrase since we are done before 2 minutes is up\n"
193  + HelpExampleCli("walletlock", "") +
194  "\nAs a JSON-RPC call\n"
195  + HelpExampleRpc("walletlock", "")
196  },
197  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
198 {
199  std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
200  if (!pwallet) return UniValue::VNULL;
201 
202  if (!pwallet->HasEncryptionKeys()) {
203  throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
204  }
205 
206  if (pwallet->IsScanningWithPassphrase()) {
207  throw JSONRPCError(RPC_WALLET_ERROR, "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before locking the wallet.");
208  }
209 
210  LOCK2(pwallet->m_relock_mutex, pwallet->cs_wallet);
211 
212  pwallet->Lock();
213  pwallet->nRelockTime = 0;
214 
215  return UniValue::VNULL;
216 },
217  };
218 }
219 
220 
222 {
223  return RPCHelpMan{
224  "encryptwallet",
225  "Encrypts the wallet with 'passphrase'. This is for first time encryption.\n"
226  "After this, any calls that interact with private keys such as sending or signing \n"
227  "will require the passphrase to be set prior to making these calls.\n"
228  "Use the walletpassphrase call for this, and then walletlock call.\n"
229  "If the wallet is already encrypted, use the walletpassphrasechange call.\n"
230  "** IMPORTANT **\n"
231  "For security reasons, the encryption process will generate a new HD seed, resulting\n"
232  "in the creation of a fresh set of active descriptors. Therefore, it is crucial to\n"
233  "securely back up the newly generated wallet file using the backupwallet RPC.\n",
234  {
235  {"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."},
236  },
237  RPCResult{RPCResult::Type::STR, "", "A string with further instructions"},
238  RPCExamples{
239  "\nEncrypt your wallet\n"
240  + HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
241  "\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n"
242  + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") +
243  "\nNow we can do something like sign\n"
244  + HelpExampleCli("signmessage", "\"address\" \"test message\"") +
245  "\nNow lock the wallet again by removing the passphrase\n"
246  + HelpExampleCli("walletlock", "") +
247  "\nAs a JSON-RPC call\n"
248  + HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
249  },
250  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
251 {
252  std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
253  if (!pwallet) return UniValue::VNULL;
254 
255  if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
256  throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet does not contain private keys, nothing to encrypt.");
257  }
258 
259  if (pwallet->HasEncryptionKeys()) {
260  throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
261  }
262 
263  if (pwallet->IsScanningWithPassphrase()) {
264  throw JSONRPCError(RPC_WALLET_ERROR, "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before encrypting the wallet.");
265  }
266 
267  LOCK2(pwallet->m_relock_mutex, pwallet->cs_wallet);
268 
269  SecureString strWalletPass;
270  strWalletPass.reserve(100);
271  strWalletPass = std::string_view{request.params[0].get_str()};
272 
273  if (strWalletPass.empty()) {
274  throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase cannot be empty");
275  }
276 
277  if (!pwallet->EncryptWallet(strWalletPass)) {
278  throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
279  }
280 
281  return "wallet encrypted; The keypool has been flushed and a new HD seed was generated. You need to make a new backup with the backupwallet RPC.";
282 },
283  };
284 }
285 } // namespace wallet
RPCHelpMan walletlock()
Definition: encrypt.cpp:178
bool HasEncryptionKeys() const override
Definition: wallet.cpp:3539
Required arg.
RPCHelpMan walletpassphrase()
Definition: encrypt.cpp:13
int64_t GetTime()
DEPRECATED, see GetTime.
Definition: time.cpp:81
std::basic_string< char, std::char_traits< char >, secure_allocator< char > > SecureString
Definition: secure.h:53
RecursiveMutex cs_wallet
Main wallet lock.
Definition: wallet.h:460
const std::string EXAMPLE_ADDRESS[2]
Example bech32 addresses for the RPCExamples help documentation.
Definition: util.cpp:44
CScheduler * scheduler
Definition: context.h:38
Invalid, missing or duplicate parameter.
Definition: protocol.h:44
The wallet passphrase entered was incorrect.
Definition: protocol.h:76
RPCHelpMan walletpassphrasechange()
Definition: encrypt.cpp:118
#define LOCK2(cs1, cs2)
Definition: sync.h:259
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
Definition: util.cpp:201
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:70
bool Unlock(const CKeyingMaterial &vMasterKeyIn)
Definition: wallet.cpp:3377
#define LOCK(cs)
Definition: sync.h:258
WalletContext & EnsureWalletContext(const std::any &context)
Definition: util.cpp:95
Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
Definition: protocol.h:77
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:309
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
Definition: util.cpp:183
RPCHelpMan encryptwallet()
Definition: encrypt.cpp:221
Failed to encrypt the wallet.
Definition: protocol.h:78
void scheduleFromNow(Function f, std::chrono::milliseconds delta) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
Call f once after the delta has passed.
Definition: scheduler.h:53
bool TopUpKeyPool(unsigned int kpSize=0)
Definition: wallet.cpp:2591
WalletContext struct containing references to state shared between CWallet instances, like the reference to the chain interface, and the list of opened wallets.
Definition: context.h:36
Wallet errors.
Definition: protocol.h:71
Mutex m_unlock_mutex
Definition: wallet.h:592
std::shared_ptr< CWallet > GetWalletForJSONRPCRequest(const JSONRPCRequest &request)
Figures out what wallet, if any, to use for a JSONRPCRequest.
Definition: util.cpp:64