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