2022-12-24 23:49:50 +00:00
// Copyright (c) 2011-2022 The Bitcoin Core developers
2021-12-01 14:50:19 +13:00
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include <rpc/util.h>
# include <wallet/rpc/util.h>
# include <wallet/wallet.h>
2021-11-12 11:13:29 -05:00
namespace wallet {
2021-12-01 14:50:19 +13:00
RPCHelpMan walletpassphrase ( )
{
return RPCHelpMan { " walletpassphrase " ,
" \n Stores the wallet decryption key in memory for 'timeout' seconds. \n "
" This is needed prior to performing transactions related to private keys such as sending bitcoins \n "
" \n Note: \n "
" Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock \n "
" time that overrides the old one. \n " ,
{
{ " passphrase " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The wallet passphrase " } ,
{ " timeout " , RPCArg : : Type : : NUM , RPCArg : : Optional : : NO , " The time to keep the decryption key in seconds; capped at 100000000 (~3 years). " } ,
} ,
RPCResult { RPCResult : : Type : : NONE , " " , " " } ,
RPCExamples {
" \n Unlock the wallet for 60 seconds \n "
+ HelpExampleCli ( " walletpassphrase " , " \" my pass phrase \" 60 " ) +
" \n Lock the wallet again (before 60 seconds) \n "
+ HelpExampleCli ( " walletlock " , " " ) +
" \n As a JSON-RPC call \n "
+ HelpExampleRpc ( " walletpassphrase " , " \" my pass phrase \" , 60 " )
} ,
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
std : : shared_ptr < CWallet > const wallet = GetWalletForJSONRPCRequest ( request ) ;
2022-07-25 16:52:00 +02:00
if ( ! wallet ) return UniValue : : VNULL ;
2021-12-01 14:50:19 +13:00
CWallet * const pwallet = wallet . get ( ) ;
int64_t nSleepTime ;
int64_t relock_time ;
// Prevent concurrent calls to walletpassphrase with the same wallet.
LOCK ( pwallet - > m_unlock_mutex ) ;
{
LOCK ( pwallet - > cs_wallet ) ;
if ( ! pwallet - > IsCrypted ( ) ) {
throw JSONRPCError ( RPC_WALLET_WRONG_ENC_STATE , " Error: running with an unencrypted wallet, but walletpassphrase was called. " ) ;
}
// Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
SecureString strWalletPass ;
strWalletPass . reserve ( 100 ) ;
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
strWalletPass = request . params [ 0 ] . get_str ( ) . c_str ( ) ;
// Get the timeout
2022-05-13 12:32:59 +02:00
nSleepTime = request . params [ 1 ] . getInt < int64_t > ( ) ;
2021-12-01 14:50:19 +13:00
// Timeout cannot be negative, otherwise it will relock immediately
if ( nSleepTime < 0 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Timeout cannot be negative. " ) ;
}
// Clamp timeout
constexpr int64_t MAX_SLEEP_TIME = 100000000 ; // larger values trigger a macos/libevent bug?
if ( nSleepTime > MAX_SLEEP_TIME ) {
nSleepTime = MAX_SLEEP_TIME ;
}
if ( strWalletPass . empty ( ) ) {
2022-02-17 12:54:39 +01:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " passphrase cannot be empty " ) ;
2021-12-01 14:50:19 +13:00
}
if ( ! pwallet - > Unlock ( strWalletPass ) ) {
throw JSONRPCError ( RPC_WALLET_PASSPHRASE_INCORRECT , " Error: The wallet passphrase entered was incorrect. " ) ;
}
pwallet - > TopUpKeyPool ( ) ;
pwallet - > nRelockTime = GetTime ( ) + nSleepTime ;
relock_time = pwallet - > nRelockTime ;
}
// rpcRunLater must be called without cs_wallet held otherwise a deadlock
// can occur. The deadlock would happen when RPCRunLater removes the
// previous timer (and waits for the callback to finish if already running)
// and the callback locks cs_wallet.
AssertLockNotHeld ( wallet - > cs_wallet ) ;
// Keep a weak pointer to the wallet so that it is possible to unload the
// wallet before the following callback is called. If a valid shared pointer
// is acquired in the callback then the wallet is still loaded.
std : : weak_ptr < CWallet > weak_wallet = wallet ;
pwallet - > chain ( ) . rpcRunLater ( strprintf ( " lockwallet(%s) " , pwallet - > GetName ( ) ) , [ weak_wallet , relock_time ] {
if ( auto shared_wallet = weak_wallet . lock ( ) ) {
LOCK ( shared_wallet - > cs_wallet ) ;
// Skip if this is not the most recent rpcRunLater callback.
if ( shared_wallet - > nRelockTime ! = relock_time ) return ;
shared_wallet - > Lock ( ) ;
shared_wallet - > nRelockTime = 0 ;
}
} , nSleepTime ) ;
2022-07-25 16:52:00 +02:00
return UniValue : : VNULL ;
2021-12-01 14:50:19 +13:00
} ,
} ;
}
RPCHelpMan walletpassphrasechange ( )
{
return RPCHelpMan { " walletpassphrasechange " ,
" \n Changes the wallet passphrase from 'oldpassphrase' to 'newpassphrase'. \n " ,
{
{ " oldpassphrase " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The current passphrase " } ,
{ " newpassphrase " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The new passphrase " } ,
} ,
RPCResult { RPCResult : : Type : : NONE , " " , " " } ,
RPCExamples {
HelpExampleCli ( " walletpassphrasechange " , " \" old one \" \" new one \" " )
+ HelpExampleRpc ( " walletpassphrasechange " , " \" old one \" , \" new one \" " )
} ,
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
2022-07-25 16:52:00 +02:00
if ( ! pwallet ) return UniValue : : VNULL ;
2021-12-01 14:50:19 +13:00
LOCK ( pwallet - > cs_wallet ) ;
if ( ! pwallet - > IsCrypted ( ) ) {
throw JSONRPCError ( RPC_WALLET_WRONG_ENC_STATE , " Error: running with an unencrypted wallet, but walletpassphrasechange was called. " ) ;
}
// TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
SecureString strOldWalletPass ;
strOldWalletPass . reserve ( 100 ) ;
strOldWalletPass = request . params [ 0 ] . get_str ( ) . c_str ( ) ;
SecureString strNewWalletPass ;
strNewWalletPass . reserve ( 100 ) ;
strNewWalletPass = request . params [ 1 ] . get_str ( ) . c_str ( ) ;
if ( strOldWalletPass . empty ( ) | | strNewWalletPass . empty ( ) ) {
2022-02-17 12:54:39 +01:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " passphrase cannot be empty " ) ;
2021-12-01 14:50:19 +13:00
}
if ( ! pwallet - > ChangeWalletPassphrase ( strOldWalletPass , strNewWalletPass ) ) {
throw JSONRPCError ( RPC_WALLET_PASSPHRASE_INCORRECT , " Error: The wallet passphrase entered was incorrect. " ) ;
}
2022-07-25 16:52:00 +02:00
return UniValue : : VNULL ;
2021-12-01 14:50:19 +13:00
} ,
} ;
}
RPCHelpMan walletlock ( )
{
return RPCHelpMan { " walletlock " ,
" \n Removes the wallet encryption key from memory, locking the wallet. \n "
" After calling this method, you will need to call walletpassphrase again \n "
" before being able to call any methods which require the wallet to be unlocked. \n " ,
{ } ,
RPCResult { RPCResult : : Type : : NONE , " " , " " } ,
RPCExamples {
" \n Set the passphrase for 2 minutes to perform a transaction \n "
+ HelpExampleCli ( " walletpassphrase " , " \" my pass phrase \" 120 " ) +
" \n Perform a send (requires passphrase set) \n "
+ HelpExampleCli ( " sendtoaddress " , " \" " + EXAMPLE_ADDRESS [ 0 ] + " \" 1.0 " ) +
" \n Clear the passphrase since we are done before 2 minutes is up \n "
+ HelpExampleCli ( " walletlock " , " " ) +
" \n As a JSON-RPC call \n "
+ HelpExampleRpc ( " walletlock " , " " )
} ,
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
2022-07-25 16:52:00 +02:00
if ( ! pwallet ) return UniValue : : VNULL ;
2021-12-01 14:50:19 +13:00
LOCK ( pwallet - > cs_wallet ) ;
if ( ! pwallet - > IsCrypted ( ) ) {
throw JSONRPCError ( RPC_WALLET_WRONG_ENC_STATE , " Error: running with an unencrypted wallet, but walletlock was called. " ) ;
}
pwallet - > Lock ( ) ;
pwallet - > nRelockTime = 0 ;
2022-07-25 16:52:00 +02:00
return UniValue : : VNULL ;
2021-12-01 14:50:19 +13:00
} ,
} ;
}
RPCHelpMan encryptwallet ( )
{
return RPCHelpMan { " encryptwallet " ,
" \n Encrypts the wallet with 'passphrase'. This is for first time encryption. \n "
" After this, any calls that interact with private keys such as sending or signing \n "
" will require the passphrase to be set prior the making these calls. \n "
" Use the walletpassphrase call for this, and then walletlock call. \n "
" If the wallet is already encrypted, use the walletpassphrasechange call. \n " ,
{
{ " 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. " } ,
} ,
RPCResult { RPCResult : : Type : : STR , " " , " A string with further instructions " } ,
RPCExamples {
" \n Encrypt your wallet \n "
+ HelpExampleCli ( " encryptwallet " , " \" my pass phrase \" " ) +
" \n Now set the passphrase to use the wallet, such as for signing or sending bitcoin \n "
+ HelpExampleCli ( " walletpassphrase " , " \" my pass phrase \" " ) +
" \n Now we can do something like sign \n "
+ HelpExampleCli ( " signmessage " , " \" address \" \" test message \" " ) +
" \n Now lock the wallet again by removing the passphrase \n "
+ HelpExampleCli ( " walletlock " , " " ) +
" \n As a JSON-RPC call \n "
+ HelpExampleRpc ( " encryptwallet " , " \" my pass phrase \" " )
} ,
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
2022-07-25 16:52:00 +02:00
if ( ! pwallet ) return UniValue : : VNULL ;
2021-12-01 14:50:19 +13:00
LOCK ( pwallet - > cs_wallet ) ;
if ( pwallet - > IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) {
throw JSONRPCError ( RPC_WALLET_ENCRYPTION_FAILED , " Error: wallet does not contain private keys, nothing to encrypt. " ) ;
}
if ( pwallet - > IsCrypted ( ) ) {
throw JSONRPCError ( RPC_WALLET_WRONG_ENC_STATE , " Error: running with an encrypted wallet, but encryptwallet was called. " ) ;
}
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
SecureString strWalletPass ;
strWalletPass . reserve ( 100 ) ;
strWalletPass = request . params [ 0 ] . get_str ( ) . c_str ( ) ;
if ( strWalletPass . empty ( ) ) {
2022-02-17 12:54:39 +01:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " passphrase cannot be empty " ) ;
2021-12-01 14:50:19 +13:00
}
if ( ! pwallet - > EncryptWallet ( strWalletPass ) ) {
throw JSONRPCError ( RPC_WALLET_ENCRYPTION_FAILED , " Error: Failed to encrypt the wallet. " ) ;
}
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. " ;
} ,
} ;
}
2021-11-12 11:13:29 -05:00
} // namespace wallet