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