Bitcoin Core  22.99.0
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 <rpc/util.h>
6 #include <wallet/rpc/util.h>
7 #include <wallet/wallet.h>
8 
9 
10 namespace wallet {
12 {
13  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  {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet passphrase"},
21  {"timeout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."},
22  },
25  "\nUnlock the wallet for 60 seconds\n"
26  + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
27  "\nLock the wallet again (before 60 seconds)\n"
28  + HelpExampleCli("walletlock", "") +
29  "\nAs a JSON-RPC call\n"
30  + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
31  },
32  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
33 {
34  std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
35  if (!wallet) return NullUniValue;
36  CWallet* const pwallet = wallet.get();
37 
38  int64_t nSleepTime;
39  int64_t relock_time;
40  // Prevent concurrent calls to walletpassphrase with the same wallet.
41  LOCK(pwallet->m_unlock_mutex);
42  {
43  LOCK(pwallet->cs_wallet);
44 
45  if (!pwallet->IsCrypted()) {
46  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  SecureString strWalletPass;
51  strWalletPass.reserve(100);
52  // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
53  // Alternately, find a way to make request.params[0] mlock()'d to begin with.
54  strWalletPass = request.params[0].get_str().c_str();
55 
56  // Get the timeout
57  nSleepTime = request.params[1].get_int64();
58  // Timeout cannot be negative, otherwise it will relock immediately
59  if (nSleepTime < 0) {
60  throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
61  }
62  // Clamp timeout
63  constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
64  if (nSleepTime > MAX_SLEEP_TIME) {
65  nSleepTime = MAX_SLEEP_TIME;
66  }
67 
68  if (strWalletPass.empty()) {
69  throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
70  }
71 
72  if (!pwallet->Unlock(strWalletPass)) {
73  throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
74  }
75 
76  pwallet->TopUpKeyPool();
77 
78  pwallet->nRelockTime = GetTime() + nSleepTime;
79  relock_time = pwallet->nRelockTime;
80  }
81 
82  // rpcRunLater must be called without cs_wallet held otherwise a deadlock
83  // can occur. The deadlock would happen when RPCRunLater removes the
84  // previous timer (and waits for the callback to finish if already running)
85  // and the callback locks cs_wallet.
86  AssertLockNotHeld(wallet->cs_wallet);
87  // Keep a weak pointer to the wallet so that it is possible to unload the
88  // wallet before the following callback is called. If a valid shared pointer
89  // is acquired in the callback then the wallet is still loaded.
90  std::weak_ptr<CWallet> weak_wallet = wallet;
91  pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] {
92  if (auto shared_wallet = weak_wallet.lock()) {
93  LOCK(shared_wallet->cs_wallet);
94  // Skip if this is not the most recent rpcRunLater callback.
95  if (shared_wallet->nRelockTime != relock_time) return;
96  shared_wallet->Lock();
97  shared_wallet->nRelockTime = 0;
98  }
99  }, nSleepTime);
100 
101  return NullUniValue;
102 },
103  };
104 }
105 
106 
108 {
109  return RPCHelpMan{"walletpassphrasechange",
110  "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
111  {
112  {"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The current passphrase"},
113  {"newpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The new passphrase"},
114  },
116  RPCExamples{
117  HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"")
118  + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")
119  },
120  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
121 {
122  std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
123  if (!pwallet) return NullUniValue;
124 
125  LOCK(pwallet->cs_wallet);
126 
127  if (!pwallet->IsCrypted()) {
128  throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
129  }
130 
131  // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
132  // Alternately, find a way to make request.params[0] mlock()'d to begin with.
133  SecureString strOldWalletPass;
134  strOldWalletPass.reserve(100);
135  strOldWalletPass = request.params[0].get_str().c_str();
136 
137  SecureString strNewWalletPass;
138  strNewWalletPass.reserve(100);
139  strNewWalletPass = request.params[1].get_str().c_str();
140 
141  if (strOldWalletPass.empty() || strNewWalletPass.empty()) {
142  throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
143  }
144 
145  if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
146  throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
147  }
148 
149  return NullUniValue;
150 },
151  };
152 }
153 
154 
156 {
157  return RPCHelpMan{"walletlock",
158  "\nRemoves the wallet encryption key from memory, locking the wallet.\n"
159  "After calling this method, you will need to call walletpassphrase again\n"
160  "before being able to call any methods which require the wallet to be unlocked.\n",
161  {},
163  RPCExamples{
164  "\nSet the passphrase for 2 minutes to perform a transaction\n"
165  + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
166  "\nPerform a send (requires passphrase set)\n"
167  + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 1.0") +
168  "\nClear the passphrase since we are done before 2 minutes is up\n"
169  + HelpExampleCli("walletlock", "") +
170  "\nAs a JSON-RPC call\n"
171  + HelpExampleRpc("walletlock", "")
172  },
173  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
174 {
175  std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
176  if (!pwallet) return NullUniValue;
177 
178  LOCK(pwallet->cs_wallet);
179 
180  if (!pwallet->IsCrypted()) {
181  throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
182  }
183 
184  pwallet->Lock();
185  pwallet->nRelockTime = 0;
186 
187  return NullUniValue;
188 },
189  };
190 }
191 
192 
194 {
195  return RPCHelpMan{"encryptwallet",
196  "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
197  "After this, any calls that interact with private keys such as sending or signing \n"
198  "will require the passphrase to be set prior the making these calls.\n"
199  "Use the walletpassphrase call for this, and then walletlock call.\n"
200  "If the wallet is already encrypted, use the walletpassphrasechange call.\n",
201  {
202  {"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."},
203  },
204  RPCResult{RPCResult::Type::STR, "", "A string with further instructions"},
205  RPCExamples{
206  "\nEncrypt your wallet\n"
207  + HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
208  "\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n"
209  + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") +
210  "\nNow we can do something like sign\n"
211  + HelpExampleCli("signmessage", "\"address\" \"test message\"") +
212  "\nNow lock the wallet again by removing the passphrase\n"
213  + HelpExampleCli("walletlock", "") +
214  "\nAs a JSON-RPC call\n"
215  + HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
216  },
217  [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
218 {
219  std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
220  if (!pwallet) return NullUniValue;
221 
222  LOCK(pwallet->cs_wallet);
223 
224  if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
225  throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet does not contain private keys, nothing to encrypt.");
226  }
227 
228  if (pwallet->IsCrypted()) {
229  throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
230  }
231 
232  // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
233  // Alternately, find a way to make request.params[0] mlock()'d to begin with.
234  SecureString strWalletPass;
235  strWalletPass.reserve(100);
236  strWalletPass = request.params[0].get_str().c_str();
237 
238  if (strWalletPass.empty()) {
239  throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
240  }
241 
242  if (!pwallet->EncryptWallet(strWalletPass)) {
243  throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
244  }
245 
246  return "wallet encrypted; The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
247 },
248  };
249 }
250 } // namespace wallet
HelpExampleCli
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
Definition: util.cpp:156
wallet.h
wallet::CWallet::chain
interfaces::Chain & chain() const
Interface for accessing chain state.
Definition: wallet.h:413
RPC_WALLET_WRONG_ENC_STATE
@ RPC_WALLET_WRONG_ENC_STATE
Command given in wrong wallet encryption state (encrypting an encrypted wallet etc....
Definition: protocol.h:77
wallet::CWallet::m_unlock_mutex
Mutex m_unlock_mutex
Definition: wallet.h:487
wallet::encryptwallet
RPCHelpMan encryptwallet()
Definition: encrypt.cpp:193
RPCHelpMan
Definition: util.h:345
NullUniValue
const UniValue NullUniValue
Definition: univalue.cpp:13
RPC_INVALID_PARAMETER
@ RPC_INVALID_PARAMETER
Invalid, missing or duplicate parameter.
Definition: protocol.h:43
wallet::CWallet::IsCrypted
bool IsCrypted() const
Definition: wallet.cpp:3054
RPCArg::Optional::NO
@ NO
Required arg.
RPCArg::Type::STR
@ STR
GetTime
int64_t GetTime()
DEPRECATED Use either GetTimeSeconds (not mockable) or GetTime<T> (mockable)
Definition: time.cpp:26
wallet::GetWalletForJSONRPCRequest
std::shared_ptr< CWallet > GetWalletForJSONRPCRequest(const JSONRPCRequest &request)
Figures out what wallet, if any, to use for a JSONRPCRequest.
Definition: util.cpp:55
wallet::walletpassphrasechange
RPCHelpMan walletpassphrasechange()
Definition: encrypt.cpp:107
wallet
Definition: node.h:38
wallet::CWallet::Unlock
bool Unlock(const CKeyingMaterial &vMasterKeyIn, bool accept_no_keys=false)
Definition: wallet.cpp:3082
wallet
std::shared_ptr< CWallet > wallet
Definition: notifications.cpp:38
wallet::walletpassphrase
RPCHelpMan walletpassphrase()
Definition: encrypt.cpp:11
UniValue
Definition: univalue.h:17
RPCArg::Type::NUM
@ NUM
wallet::walletlock
RPCHelpMan walletlock()
Definition: encrypt.cpp:155
SecureString
std::basic_string< char, std::char_traits< char >, secure_allocator< char > > SecureString
Definition: secure.h:59
util.h
AssertLockNotHeld
#define AssertLockNotHeld(cs)
Definition: sync.h:84
RPCResult::Type::NONE
@ NONE
wallet::CWallet
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:232
RPC_WALLET_PASSPHRASE_INCORRECT
@ RPC_WALLET_PASSPHRASE_INCORRECT
The wallet passphrase entered was incorrect.
Definition: protocol.h:76
wallet::WALLET_FLAG_DISABLE_PRIVATE_KEYS
@ WALLET_FLAG_DISABLE_PRIVATE_KEYS
Definition: walletutil.h:51
RPC_WALLET_ENCRYPTION_FAILED
@ RPC_WALLET_ENCRYPTION_FAILED
Failed to encrypt the wallet.
Definition: protocol.h:78
RPCExamples
Definition: util.h:335
interfaces::Chain::rpcRunLater
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.
RPCResult::Type::STR
@ STR
HelpExampleRpc
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
Definition: util.cpp:174
wallet::CWallet::GetName
const std::string & GetName() const
Get a name for this wallet for logging/debugging purposes.
Definition: wallet.h:362
wallet::CWallet::cs_wallet
RecursiveMutex cs_wallet
Main wallet lock.
Definition: wallet.h:352
strprintf
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164
JSONRPCError
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:51
LOCK
#define LOCK(cs)
Definition: sync.h:226
wallet::CWallet::TopUpKeyPool
bool TopUpKeyPool(unsigned int kpSize=0)
Definition: wallet.cpp:2204
EXAMPLE_ADDRESS
const std::string EXAMPLE_ADDRESS[2]
Example bech32 addresses for the RPCExamples help documentation.
Definition: util.cpp:22
JSONRPCRequest
Definition: request.h:28
RPCResult
Definition: util.h:231