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