Bitcoin ABC  0.29.2
P2P Digital Currency
encrypt.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2021 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 <wallet/rpc/encrypt.h>
6 
7 #include <rpc/server.h>
8 #include <rpc/util.h>
9 #include <wallet/rpc/util.h>
10 #include <wallet/wallet.h>
11 
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 "
17  "keys such as sending bitcoins\n"
18  "\nNote:\n"
19  "Issuing the walletpassphrase command while the wallet is already "
20  "unlocked will set a new unlock\n"
21  "time that overrides the old one.\n",
22  {
24  "The wallet passphrase"},
26  "The time to keep the decryption key in seconds; capped at "
27  "100000000 (~3 years)."},
28  },
31  "\nUnlock the wallet for 60 seconds\n" +
32  HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
33  "\nLock the wallet again (before 60 seconds)\n" +
34  HelpExampleCli("walletlock", "") + "\nAs a JSON-RPC call\n" +
35  HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")},
36  [&](const RPCHelpMan &self, const Config &config,
37  const JSONRPCRequest &request) -> UniValue {
38  std::shared_ptr<CWallet> const wallet =
40  if (!wallet) {
41  return NullUniValue;
42  }
43  CWallet *const pwallet = wallet.get();
44 
45  int64_t nSleepTime;
46  int64_t relock_time;
47  // Prevent concurrent calls to walletpassphrase with the same
48  // wallet.
49  LOCK(pwallet->m_unlock_mutex);
50  {
51  LOCK(pwallet->cs_wallet);
52 
53  if (!pwallet->IsCrypted()) {
54  throw JSONRPCError(
56  "Error: running with an unencrypted wallet, but "
57  "walletpassphrase was called.");
58  }
59 
60  // Note that the walletpassphrase is stored in request.params[0]
61  // which is not mlock()ed
62  SecureString strWalletPass;
63  strWalletPass.reserve(100);
64  // TODO: get rid of this .c_str() by implementing
65  // SecureString::operator=(std::string)
66  // Alternately, find a way to make request.params[0] mlock()'d
67  // to begin with.
68  strWalletPass = request.params[0].get_str().c_str();
69 
70  // Get the timeout
71  nSleepTime = request.params[1].get_int64();
72  // Timeout cannot be negative, otherwise it will relock
73  // immediately
74  if (nSleepTime < 0) {
76  "Timeout cannot be negative.");
77  }
78  // Clamp timeout
79  // larger values trigger a macos/libevent bug?
80  constexpr int64_t MAX_SLEEP_TIME = 100000000;
81  if (nSleepTime > MAX_SLEEP_TIME) {
82  nSleepTime = MAX_SLEEP_TIME;
83  }
84 
85  if (strWalletPass.empty()) {
87  "passphrase can not be empty");
88  }
89 
90  if (!pwallet->Unlock(strWalletPass)) {
91  throw JSONRPCError(
93  "Error: The wallet passphrase entered was incorrect.");
94  }
95 
96  pwallet->TopUpKeyPool();
97 
98  pwallet->nRelockTime = GetTime() + nSleepTime;
99  relock_time = pwallet->nRelockTime;
100  }
101 
102  // rpcRunLater must be called without cs_wallet held otherwise a
103  // deadlock can occur. The deadlock would happen when RPCRunLater
104  // removes the previous timer (and waits for the callback to finish
105  // if already running) and the callback locks cs_wallet.
106  AssertLockNotHeld(wallet->cs_wallet);
107  // Keep a weak pointer to the wallet so that it is possible to
108  // unload the wallet before the following callback is called. If a
109  // valid shared pointer is acquired in the callback then the wallet
110  // is still loaded.
111  std::weak_ptr<CWallet> weak_wallet = wallet;
112  pwallet->chain().rpcRunLater(
113  strprintf("lockwallet(%s)", pwallet->GetName()),
114  [weak_wallet, relock_time] {
115  if (auto shared_wallet = weak_wallet.lock()) {
116  LOCK(shared_wallet->cs_wallet);
117  // Skip if this is not the most recent rpcRunLater
118  // callback.
119  if (shared_wallet->nRelockTime != relock_time) {
120  return;
121  }
122  shared_wallet->Lock();
123  shared_wallet->nRelockTime = 0;
124  }
125  },
126  nSleepTime);
127 
128  return NullUniValue;
129  },
130  };
131 }
132 
134  return RPCHelpMan{
135  "walletpassphrasechange",
136  "Changes the wallet passphrase from 'oldpassphrase' to "
137  "'newpassphrase'.\n",
138  {
139  {"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO,
140  "The current passphrase"},
141  {"newpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO,
142  "The new passphrase"},
143  },
145  RPCExamples{HelpExampleCli("walletpassphrasechange",
146  "\"old one\" \"new one\"") +
147  HelpExampleRpc("walletpassphrasechange",
148  "\"old one\", \"new one\"")},
149  [&](const RPCHelpMan &self, const Config &config,
150  const JSONRPCRequest &request) -> UniValue {
151  std::shared_ptr<CWallet> const wallet =
153  if (!wallet) {
154  return NullUniValue;
155  }
156  CWallet *const pwallet = wallet.get();
157 
158  LOCK(pwallet->cs_wallet);
159 
160  if (!pwallet->IsCrypted()) {
161  throw JSONRPCError(
163  "Error: running with an unencrypted wallet, but "
164  "walletpassphrasechange was called.");
165  }
166 
167  // TODO: get rid of these .c_str() calls by implementing
168  // SecureString::operator=(std::string)
169  // Alternately, find a way to make request.params[0] mlock()'d to
170  // begin with.
171  SecureString strOldWalletPass;
172  strOldWalletPass.reserve(100);
173  strOldWalletPass = request.params[0].get_str().c_str();
174 
175  SecureString strNewWalletPass;
176  strNewWalletPass.reserve(100);
177  strNewWalletPass = request.params[1].get_str().c_str();
178 
179  if (strOldWalletPass.empty() || strNewWalletPass.empty()) {
181  "passphrase can not be empty");
182  }
183 
184  if (!pwallet->ChangeWalletPassphrase(strOldWalletPass,
185  strNewWalletPass)) {
186  throw JSONRPCError(
188  "Error: The wallet passphrase entered was incorrect.");
189  }
190 
191  return NullUniValue;
192  },
193  };
194 }
195 
197  return RPCHelpMan{
198  "walletlock",
199  "Removes the wallet encryption key from memory, locking the wallet.\n"
200  "After calling this method, you will need to call walletpassphrase "
201  "again\n"
202  "before being able to call any methods which require the wallet to be "
203  "unlocked.\n",
204  {},
206  RPCExamples{
207  "\nSet the passphrase for 2 minutes to perform a transaction\n" +
208  HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
209  "\nPerform a send (requires passphrase set)\n" +
210  HelpExampleCli("sendtoaddress",
211  "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") +
212  "\nClear the passphrase since we are done before 2 minutes is "
213  "up\n" +
214  HelpExampleCli("walletlock", "") + "\nAs a JSON-RPC call\n" +
215  HelpExampleRpc("walletlock", "")},
216  [&](const RPCHelpMan &self, const Config &config,
217  const JSONRPCRequest &request) -> UniValue {
218  std::shared_ptr<CWallet> const wallet =
220  if (!wallet) {
221  return NullUniValue;
222  }
223  CWallet *const pwallet = wallet.get();
224 
225  LOCK(pwallet->cs_wallet);
226 
227  if (!pwallet->IsCrypted()) {
228  throw JSONRPCError(
230  "Error: running with an unencrypted wallet, but "
231  "walletlock was called.");
232  }
233 
234  pwallet->Lock();
235  pwallet->nRelockTime = 0;
236 
237  return NullUniValue;
238  },
239  };
240 }
241 
243  return RPCHelpMan{
244  "encryptwallet",
245  "Encrypts the wallet with 'passphrase'. This is for first time "
246  "encryption.\n"
247  "After this, any calls that interact with private keys such as sending "
248  "or signing \n"
249  "will require the passphrase to be set prior the making these calls.\n"
250  "Use the walletpassphrase call for this, and then walletlock call.\n"
251  "If the wallet is already encrypted, use the walletpassphrasechange "
252  "call.\n",
253  {
254  {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO,
255  "The pass phrase to encrypt the wallet with. It must be at least "
256  "1 character, but should be long."},
257  },
259  "A string with further instructions"},
260  RPCExamples{
261  "\nEncrypt your wallet\n" +
262  HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
263  "\nNow set the passphrase to use the wallet, such as for signing "
264  "or sending bitcoin\n" +
265  HelpExampleCli("walletpassphrase", "\"my pass phrase\"") +
266  "\nNow we can do something like sign\n" +
267  HelpExampleCli("signmessage", "\"address\" \"test message\"") +
268  "\nNow lock the wallet again by removing the passphrase\n" +
269  HelpExampleCli("walletlock", "") + "\nAs a JSON-RPC call\n" +
270  HelpExampleRpc("encryptwallet", "\"my pass phrase\"")},
271  [&](const RPCHelpMan &self, const Config &config,
272  const JSONRPCRequest &request) -> UniValue {
273  std::shared_ptr<CWallet> const wallet =
275  if (!wallet) {
276  return NullUniValue;
277  }
278  CWallet *const pwallet = wallet.get();
279 
280  LOCK(pwallet->cs_wallet);
281 
284  "Error: wallet does not contain private "
285  "keys, nothing to encrypt.");
286  }
287 
288  if (pwallet->IsCrypted()) {
289  throw JSONRPCError(
291  "Error: running with an encrypted wallet, but "
292  "encryptwallet was called.");
293  }
294 
295  // TODO: get rid of this .c_str() by implementing
296  // SecureString::operator=(std::string)
297  // Alternately, find a way to make request.params[0] mlock()'d to
298  // begin with.
299  SecureString strWalletPass;
300  strWalletPass.reserve(100);
301  strWalletPass = request.params[0].get_str().c_str();
302 
303  if (strWalletPass.empty()) {
305  "passphrase can not be empty");
306  }
307 
308  if (!pwallet->EncryptWallet(strWalletPass)) {
310  "Error: Failed to encrypt the wallet.");
311  }
312 
313  return "wallet encrypted; The keypool has been flushed and a new "
314  "HD seed "
315  "was generated (if you are using HD). You need to make a "
316  "new "
317  "backup.";
318  },
319  };
320 }
321 
323  // clang-format off
324  static const CRPCCommand commands[] = {
325  // category actor (function)
326  // ------------------ ----------------------
327  { "wallet", encryptwallet, },
328  { "wallet", walletlock, },
329  { "wallet", walletpassphrase, },
330  { "wallet", walletpassphrasechange, },
331  };
332  // clang-format on
333 
334  return commands;
335 }
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:253
bool Lock()
Definition: wallet.cpp:3153
Mutex m_unlock_mutex
Definition: wallet.h:562
const std::string & GetName() const
Get a name for this wallet for logging/debugging purposes.
Definition: wallet.h:400
RecursiveMutex cs_wallet
Definition: wallet.h:388
bool Unlock(const CKeyingMaterial &vMasterKeyIn, bool accept_no_keys=false)
Definition: wallet.cpp:3172
interfaces::Chain & chain() const
Interface for accessing chain state.
Definition: wallet.h:447
bool IsCrypted() const
Definition: wallet.cpp:3141
Definition: config.h:17
A Span is an object that can refer to a contiguous sequence of objects.
Definition: span.h:93
virtual void rpcRunLater(const std::string &name, std::function< void()> fn, int64_t seconds)=0
Run function after given number of seconds.
RPCHelpMan walletpassphrasechange()
Definition: encrypt.cpp:133
RPCHelpMan encryptwallet()
Definition: encrypt.cpp:242
Span< const CRPCCommand > GetWalletEncryptRPCCommands()
Definition: encrypt.cpp:322
RPCHelpMan walletlock()
Definition: encrypt.cpp:196
RPCHelpMan walletpassphrase()
Definition: encrypt.cpp:12
bool TopUpKeyPool(unsigned int kpSize=0)
Definition: wallet.cpp:2320
bool ChangeWalletPassphrase(const SecureString &strOldWalletPassphrase, const SecureString &strNewWalletPassphrase)
Definition: wallet.cpp:444
bool EncryptWallet(const SecureString &strWalletPassphrase)
Definition: wallet.cpp:704
bool IsWalletFlagSet(uint64_t flag) const override
Check if a certain wallet flag is set.
Definition: wallet.cpp:1515
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:57
@ 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:105
@ RPC_INVALID_PARAMETER
Invalid, missing or duplicate parameter.
Definition: protocol.h:46
@ RPC_WALLET_PASSPHRASE_INCORRECT
The wallet passphrase entered was incorrect.
Definition: protocol.h:100
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
Definition: util.cpp:175
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
Definition: util.cpp:192
std::basic_string< char, std::char_traits< char >, secure_allocator< char > > SecureString
Definition: secure.h:55
@ NO
Required arg.
#define AssertLockNotHeld(cs)
Definition: sync.h:163
#define LOCK(cs)
Definition: sync.h:306
int64_t GetTime()
Definition: time.cpp:109
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
const UniValue NullUniValue
Definition: univalue.cpp:13
std::shared_ptr< CWallet > GetWalletForJSONRPCRequest(const JSONRPCRequest &request)
Figures out what wallet, if any, to use for a JSONRPCRequest.
Definition: util.cpp:62
@ WALLET_FLAG_DISABLE_PRIVATE_KEYS
Definition: walletutil.h:55