Branch data Line data Source code
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 {
11 : 10 : RPCHelpMan walletpassphrase()
12 : : {
13 : 10 : 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 [ + - ]: 10 : {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet passphrase"},
22 [ + - ]: 10 : {"timeout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."},
23 : : },
24 [ + - + - : 20 : RPCResult{RPCResult::Type::NONE, "", ""},
+ - ]
25 : 10 : RPCExamples{
26 : : "\nUnlock the wallet for 60 seconds\n"
27 [ + - + - : 20 : + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
+ - + - ]
28 : 10 : "\nLock the wallet again (before 60 seconds)\n"
29 [ + - + - : 40 : + HelpExampleCli("walletlock", "") +
+ - + - ]
30 : 10 : "\nAs a JSON-RPC call\n"
31 [ + - + - : 40 : + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
+ - + - ]
32 [ + - ]: 10 : },
33 : 0 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
34 : : {
35 : 0 : std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
36 [ # # ]: 0 : if (!wallet) return UniValue::VNULL;
37 : 0 : CWallet* const pwallet = wallet.get();
38 : :
39 : 0 : int64_t nSleepTime;
40 : 0 : int64_t relock_time;
41 : : // Prevent concurrent calls to walletpassphrase with the same wallet.
42 [ # # ]: 0 : LOCK(pwallet->m_unlock_mutex);
43 : 0 : {
44 [ # # ]: 0 : LOCK(pwallet->cs_wallet);
45 : :
46 [ # # # # ]: 0 : if (!pwallet->IsCrypted()) {
47 [ # # # # ]: 0 : 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 [ # # ]: 0 : SecureString strWalletPass;
52 [ # # ]: 0 : strWalletPass.reserve(100);
53 [ # # # # : 0 : strWalletPass = std::string_view{request.params[0].get_str()};
# # ]
54 : :
55 : : // Get the timeout
56 [ # # # # ]: 0 : nSleepTime = request.params[1].getInt<int64_t>();
57 : : // Timeout cannot be negative, otherwise it will relock immediately
58 [ # # ]: 0 : if (nSleepTime < 0) {
59 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
60 : : }
61 : : // Clamp timeout
62 : 0 : constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
63 [ # # ]: 0 : if (nSleepTime > MAX_SLEEP_TIME) {
64 : 0 : nSleepTime = MAX_SLEEP_TIME;
65 : : }
66 : :
67 [ # # ]: 0 : if (strWalletPass.empty()) {
68 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase cannot be empty");
69 : : }
70 : :
71 [ # # # # ]: 0 : if (!pwallet->Unlock(strWalletPass)) {
72 : : // Check if the passphrase has a null character (see #27067 for details)
73 [ # # ]: 0 : if (strWalletPass.find('\0') == std::string::npos) {
74 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
75 : : } else {
76 [ # # ]: 0 : 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 [ # # # # ]: 0 : "passphrase to avoid this issue in the future.");
82 : : }
83 : : }
84 : :
85 [ # # ]: 0 : pwallet->TopUpKeyPool();
86 : :
87 [ # # ]: 0 : pwallet->nRelockTime = GetTime() + nSleepTime;
88 : 0 : relock_time = pwallet->nRelockTime;
89 [ # # ]: 0 : }
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 [ # # ]: 0 : 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 [ # # ]: 0 : std::weak_ptr<CWallet> weak_wallet = wallet;
100 [ # # # # : 0 : pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] {
# # # # #
# # # #
# ]
101 [ # # ]: 0 : if (auto shared_wallet = weak_wallet.lock()) {
102 [ # # # # ]: 0 : LOCK2(shared_wallet->m_relock_mutex, shared_wallet->cs_wallet);
103 : : // Skip if this is not the most recent rpcRunLater callback.
104 [ # # # # ]: 0 : if (shared_wallet->nRelockTime != relock_time) return;
105 [ # # ]: 0 : shared_wallet->Lock();
106 [ # # ]: 0 : shared_wallet->nRelockTime = 0;
107 [ # # # # : 0 : }
# # ]
108 : : }, nSleepTime);
109 : :
110 [ # # ]: 0 : return UniValue::VNULL;
111 [ # # ]: 0 : },
112 [ + - + - : 120 : };
+ - + - +
- + - + -
+ - + + -
- ]
113 [ + - + - : 50 : }
+ - - - ]
114 : :
115 : :
116 : 10 : RPCHelpMan walletpassphrasechange()
117 : : {
118 : 10 : return RPCHelpMan{
119 : : "walletpassphrasechange",
120 : : "Changes the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
121 : : {
122 [ + - ]: 10 : {"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The current passphrase"},
123 [ + - ]: 10 : {"newpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The new passphrase"},
124 : : },
125 [ + - + - : 20 : RPCResult{RPCResult::Type::NONE, "", ""},
+ - ]
126 : 10 : RPCExamples{
127 [ + - + - : 20 : HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"")
+ - ]
128 [ + - + - : 40 : + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")
+ - + - ]
129 [ + - ]: 10 : },
130 : 0 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
131 : : {
132 : 0 : std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
133 [ # # ]: 0 : if (!pwallet) return UniValue::VNULL;
134 : :
135 [ # # # # ]: 0 : if (!pwallet->IsCrypted()) {
136 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
137 : : }
138 : :
139 [ # # ]: 0 : if (pwallet->IsScanningWithPassphrase()) {
140 [ # # # # ]: 0 : 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 [ # # # # ]: 0 : LOCK2(pwallet->m_relock_mutex, pwallet->cs_wallet);
144 : :
145 [ # # ]: 0 : SecureString strOldWalletPass;
146 [ # # ]: 0 : strOldWalletPass.reserve(100);
147 [ # # # # : 0 : strOldWalletPass = std::string_view{request.params[0].get_str()};
# # ]
148 : :
149 [ # # ]: 0 : SecureString strNewWalletPass;
150 [ # # ]: 0 : strNewWalletPass.reserve(100);
151 [ # # # # : 0 : strNewWalletPass = std::string_view{request.params[1].get_str()};
# # ]
152 : :
153 [ # # # # ]: 0 : if (strOldWalletPass.empty() || strNewWalletPass.empty()) {
154 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase cannot be empty");
155 : : }
156 : :
157 [ # # # # ]: 0 : if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
158 : : // Check if the old passphrase had a null character (see #27067 for details)
159 [ # # ]: 0 : if (strOldWalletPass.find('\0') == std::string::npos) {
160 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
161 : : } else {
162 [ # # ]: 0 : 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 [ # # # # ]: 0 : "the first null character.");
167 : : }
168 : : }
169 : :
170 : 0 : return UniValue::VNULL;
171 [ # # # # ]: 0 : },
172 [ + - + - : 120 : };
+ - + - +
- + - + -
+ - + + -
- ]
173 [ + - + - : 50 : }
+ - - - ]
174 : :
175 : :
176 : 10 : RPCHelpMan walletlock()
177 : : {
178 : 10 : 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 : : {},
184 [ + - + - : 20 : RPCResult{RPCResult::Type::NONE, "", ""},
+ - ]
185 : 10 : RPCExamples{
186 : : "\nSet the passphrase for 2 minutes to perform a transaction\n"
187 [ + - + - : 20 : + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
+ - + - ]
188 : 10 : "\nPerform a send (requires passphrase set)\n"
189 [ + - + - : 50 : + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 1.0") +
+ - + - ]
190 : 10 : "\nClear the passphrase since we are done before 2 minutes is up\n"
191 [ + - + - : 40 : + HelpExampleCli("walletlock", "") +
+ - + - ]
192 : 10 : "\nAs a JSON-RPC call\n"
193 [ + - + - : 40 : + HelpExampleRpc("walletlock", "")
+ - + - ]
194 [ + - ]: 10 : },
195 : 0 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
196 : : {
197 : 0 : std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
198 [ # # ]: 0 : if (!pwallet) return UniValue::VNULL;
199 : :
200 [ # # # # ]: 0 : if (!pwallet->IsCrypted()) {
201 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
202 : : }
203 : :
204 [ # # ]: 0 : if (pwallet->IsScanningWithPassphrase()) {
205 [ # # # # ]: 0 : 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 [ # # # # ]: 0 : LOCK2(pwallet->m_relock_mutex, pwallet->cs_wallet);
209 : :
210 [ # # ]: 0 : pwallet->Lock();
211 : 0 : pwallet->nRelockTime = 0;
212 : :
213 [ # # ]: 0 : return UniValue::VNULL;
214 [ # # ]: 0 : },
215 [ + - + - : 60 : };
+ - + - ]
216 : : }
217 : :
218 : :
219 : 10 : RPCHelpMan encryptwallet()
220 : : {
221 : 10 : 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 [ + - ]: 10 : {"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 [ + - + - : 20 : RPCResult{RPCResult::Type::STR, "", "A string with further instructions"},
+ - ]
236 : 10 : RPCExamples{
237 : : "\nEncrypt your wallet\n"
238 [ + - + - : 20 : + HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
+ - + - ]
239 : 10 : "\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n"
240 [ + - + - : 40 : + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") +
+ - + - ]
241 : 10 : "\nNow we can do something like sign\n"
242 [ + - + - : 40 : + HelpExampleCli("signmessage", "\"address\" \"test message\"") +
+ - + - ]
243 : 10 : "\nNow lock the wallet again by removing the passphrase\n"
244 [ + - + - : 40 : + HelpExampleCli("walletlock", "") +
+ - + - ]
245 : 10 : "\nAs a JSON-RPC call\n"
246 [ + - + - : 40 : + HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
+ - + - ]
247 [ + - ]: 10 : },
248 : 0 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
249 : : {
250 : 0 : std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
251 [ # # ]: 0 : if (!pwallet) return UniValue::VNULL;
252 : :
253 [ # # # # ]: 0 : if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
254 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet does not contain private keys, nothing to encrypt.");
255 : : }
256 : :
257 [ # # # # ]: 0 : if (pwallet->IsCrypted()) {
258 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
259 : : }
260 : :
261 [ # # ]: 0 : if (pwallet->IsScanningWithPassphrase()) {
262 [ # # # # ]: 0 : 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 [ # # # # ]: 0 : LOCK2(pwallet->m_relock_mutex, pwallet->cs_wallet);
266 : :
267 [ # # ]: 0 : SecureString strWalletPass;
268 [ # # ]: 0 : strWalletPass.reserve(100);
269 [ # # # # : 0 : strWalletPass = std::string_view{request.params[0].get_str()};
# # ]
270 : :
271 [ # # ]: 0 : if (strWalletPass.empty()) {
272 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase cannot be empty");
273 : : }
274 : :
275 [ # # # # ]: 0 : if (!pwallet->EncryptWallet(strWalletPass)) {
276 [ # # # # ]: 0 : throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
277 : : }
278 : :
279 [ # # ]: 0 : 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 [ # # # # ]: 0 : },
281 [ + - + - : 90 : };
+ - + - +
- + - + +
- - ]
282 [ + - + - ]: 30 : }
283 : : } // namespace wallet
|