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