2017-08-02 13:19:28 +02:00
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-11-10 01:57:53 +01:00
# include <wallet/init.h>
2017-08-02 13:19:28 +02:00
2017-11-10 01:57:53 +01:00
# include <net.h>
# include <util.h>
# include <utilmoneystr.h>
# include <validation.h>
# include <wallet/rpcwallet.h>
2017-10-08 22:48:07 +02:00
# include <wallet/wallet.h>
# include <wallet/walletutil.h>
2017-08-02 13:48:52 +02:00
std : : string GetWalletHelpString ( bool showDebug )
{
std : : string strUsage = HelpMessageGroup ( _ ( " Wallet options: " ) ) ;
strUsage + = HelpMessageOpt ( " -disablewallet " , _ ( " Do not load the wallet and disable wallet RPC calls " ) ) ;
strUsage + = HelpMessageOpt ( " -keypool=<n> " , strprintf ( _ ( " Set key pool size to <n> (default: %u) " ) , DEFAULT_KEYPOOL_SIZE ) ) ;
strUsage + = HelpMessageOpt ( " -fallbackfee=<amt> " , strprintf ( _ ( " A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s) " ) ,
CURRENCY_UNIT , FormatMoney ( DEFAULT_FALLBACK_FEE ) ) ) ;
strUsage + = HelpMessageOpt ( " -discardfee=<amt> " , strprintf ( _ ( " The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
" Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target " ) ,
CURRENCY_UNIT , FormatMoney ( DEFAULT_DISCARD_FEE ) ) ) ;
strUsage + = HelpMessageOpt ( " -mintxfee=<amt> " , strprintf ( _ ( " Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s) " ) ,
CURRENCY_UNIT , FormatMoney ( DEFAULT_TRANSACTION_MINFEE ) ) ) ;
strUsage + = HelpMessageOpt ( " -paytxfee=<amt> " , strprintf ( _ ( " Fee (in %s/kB) to add to transactions you send (default: %s) " ) ,
CURRENCY_UNIT , FormatMoney ( payTxFee . GetFeePerK ( ) ) ) ) ;
strUsage + = HelpMessageOpt ( " -rescan " , _ ( " Rescan the block chain for missing wallet transactions on startup " ) ) ;
strUsage + = HelpMessageOpt ( " -salvagewallet " , _ ( " Attempt to recover private keys from a corrupt wallet on startup " ) ) ;
strUsage + = HelpMessageOpt ( " -spendzeroconfchange " , strprintf ( _ ( " Spend unconfirmed change when sending transactions (default: %u) " ) , DEFAULT_SPEND_ZEROCONF_CHANGE ) ) ;
strUsage + = HelpMessageOpt ( " -txconfirmtarget=<n> " , strprintf ( _ ( " If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u) " ) , DEFAULT_TX_CONFIRM_TARGET ) ) ;
strUsage + = HelpMessageOpt ( " -walletrbf " , strprintf ( _ ( " Send transactions with full-RBF opt-in enabled (default: %u) " ) , DEFAULT_WALLET_RBF ) ) ;
strUsage + = HelpMessageOpt ( " -upgradewallet " , _ ( " Upgrade wallet to latest format on startup " ) ) ;
strUsage + = HelpMessageOpt ( " -wallet=<file> " , _ ( " Specify wallet file (within data directory) " ) + " " + strprintf ( _ ( " (default: %s) " ) , DEFAULT_WALLET_DAT ) ) ;
strUsage + = HelpMessageOpt ( " -walletbroadcast " , _ ( " Make the wallet broadcast transactions " ) + " " + strprintf ( _ ( " (default: %u) " ) , DEFAULT_WALLETBROADCAST ) ) ;
2017-10-08 22:48:07 +02:00
strUsage + = HelpMessageOpt ( " -walletdir=<dir> " , _ ( " Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>) " ) ) ;
2017-08-02 13:48:52 +02:00
strUsage + = HelpMessageOpt ( " -walletnotify=<cmd> " , _ ( " Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) " ) ) ;
strUsage + = HelpMessageOpt ( " -zapwallettxes=<mode> " , _ ( " Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup " ) +
" " + _ ( " (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) " ) ) ;
if ( showDebug )
{
strUsage + = HelpMessageGroup ( _ ( " Wallet debugging/testing options: " ) ) ;
strUsage + = HelpMessageOpt ( " -dblogsize=<n> " , strprintf ( " Flush wallet database activity from memory to disk log every <n> megabytes (default: %u) " , DEFAULT_WALLET_DBLOGSIZE ) ) ;
strUsage + = HelpMessageOpt ( " -flushwallet " , strprintf ( " Run a thread to flush wallet periodically (default: %u) " , DEFAULT_FLUSHWALLET ) ) ;
strUsage + = HelpMessageOpt ( " -privdb " , strprintf ( " Sets the DB_PRIVATE flag in the wallet db environment (default: %u) " , DEFAULT_WALLET_PRIVDB ) ) ;
strUsage + = HelpMessageOpt ( " -walletrejectlongchains " , strprintf ( _ ( " Wallet will not create transactions that violate mempool chain limits (default: %u) " ) , DEFAULT_WALLET_REJECT_LONG_CHAINS ) ) ;
}
return strUsage ;
}
bool WalletParameterInteraction ( )
{
2017-11-02 01:39:23 +01:00
if ( gArgs . GetBoolArg ( " -disablewallet " , DEFAULT_DISABLE_WALLET ) ) {
for ( const std : : string & wallet : gArgs . GetArgs ( " -wallet " ) ) {
LogPrintf ( " %s: parameter interaction: -disablewallet -> ignoring -wallet=%s \n " , __func__ , wallet ) ;
}
2017-08-02 13:48:52 +02:00
return true ;
2017-11-02 01:39:23 +01:00
}
gArgs . SoftSetArg ( " -wallet " , DEFAULT_WALLET_DAT ) ;
const bool is_multiwallet = gArgs . GetArgs ( " -wallet " ) . size ( ) > 1 ;
2017-08-02 13:48:52 +02:00
if ( gArgs . GetBoolArg ( " -blocksonly " , DEFAULT_BLOCKSONLY ) & & gArgs . SoftSetBoolArg ( " -walletbroadcast " , false ) ) {
LogPrintf ( " %s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0 \n " , __func__ ) ;
}
if ( gArgs . GetBoolArg ( " -salvagewallet " , false ) ) {
if ( is_multiwallet ) {
return InitError ( strprintf ( " %s is only allowed with a single wallet file " , " -salvagewallet " ) ) ;
}
// Rewrite just private keys: rescan to find transactions
if ( gArgs . SoftSetBoolArg ( " -rescan " , true ) ) {
LogPrintf ( " %s: parameter interaction: -salvagewallet=1 -> setting -rescan=1 \n " , __func__ ) ;
}
}
int zapwallettxes = gArgs . GetArg ( " -zapwallettxes " , 0 ) ;
// -zapwallettxes implies dropping the mempool on startup
if ( zapwallettxes ! = 0 & & gArgs . SoftSetBoolArg ( " -persistmempool " , false ) ) {
LogPrintf ( " %s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0 \n " , __func__ , zapwallettxes ) ;
}
// -zapwallettxes implies a rescan
if ( zapwallettxes ! = 0 ) {
if ( is_multiwallet ) {
return InitError ( strprintf ( " %s is only allowed with a single wallet file " , " -zapwallettxes " ) ) ;
}
if ( gArgs . SoftSetBoolArg ( " -rescan " , true ) ) {
LogPrintf ( " %s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1 \n " , __func__ , zapwallettxes ) ;
}
}
if ( is_multiwallet ) {
if ( gArgs . GetBoolArg ( " -upgradewallet " , false ) ) {
return InitError ( strprintf ( " %s is only allowed with a single wallet file " , " -upgradewallet " ) ) ;
}
}
if ( gArgs . GetBoolArg ( " -sysperms " , false ) )
return InitError ( " -sysperms is not allowed in combination with enabled wallet functionality " ) ;
if ( gArgs . GetArg ( " -prune " , 0 ) & & gArgs . GetBoolArg ( " -rescan " , false ) )
return InitError ( _ ( " Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again. " ) ) ;
if ( : : minRelayTxFee . GetFeePerK ( ) > HIGH_TX_FEE_PER_KB )
InitWarning ( AmountHighWarn ( " -minrelaytxfee " ) + " " +
_ ( " The wallet will avoid paying less than the minimum relay fee. " ) ) ;
if ( gArgs . IsArgSet ( " -mintxfee " ) )
{
CAmount n = 0 ;
if ( ! ParseMoney ( gArgs . GetArg ( " -mintxfee " , " " ) , n ) | | 0 = = n )
return InitError ( AmountErrMsg ( " mintxfee " , gArgs . GetArg ( " -mintxfee " , " " ) ) ) ;
if ( n > HIGH_TX_FEE_PER_KB )
InitWarning ( AmountHighWarn ( " -mintxfee " ) + " " +
_ ( " This is the minimum transaction fee you pay on every transaction. " ) ) ;
CWallet : : minTxFee = CFeeRate ( n ) ;
}
if ( gArgs . IsArgSet ( " -fallbackfee " ) )
{
CAmount nFeePerK = 0 ;
if ( ! ParseMoney ( gArgs . GetArg ( " -fallbackfee " , " " ) , nFeePerK ) )
return InitError ( strprintf ( _ ( " Invalid amount for -fallbackfee=<amount>: '%s' " ) , gArgs . GetArg ( " -fallbackfee " , " " ) ) ) ;
if ( nFeePerK > HIGH_TX_FEE_PER_KB )
InitWarning ( AmountHighWarn ( " -fallbackfee " ) + " " +
_ ( " This is the transaction fee you may pay when fee estimates are not available. " ) ) ;
CWallet : : fallbackFee = CFeeRate ( nFeePerK ) ;
}
if ( gArgs . IsArgSet ( " -discardfee " ) )
{
CAmount nFeePerK = 0 ;
if ( ! ParseMoney ( gArgs . GetArg ( " -discardfee " , " " ) , nFeePerK ) )
return InitError ( strprintf ( _ ( " Invalid amount for -discardfee=<amount>: '%s' " ) , gArgs . GetArg ( " -discardfee " , " " ) ) ) ;
if ( nFeePerK > HIGH_TX_FEE_PER_KB )
InitWarning ( AmountHighWarn ( " -discardfee " ) + " " +
_ ( " This is the transaction fee you may discard if change is smaller than dust at this level " ) ) ;
CWallet : : m_discard_rate = CFeeRate ( nFeePerK ) ;
}
if ( gArgs . IsArgSet ( " -paytxfee " ) )
{
CAmount nFeePerK = 0 ;
if ( ! ParseMoney ( gArgs . GetArg ( " -paytxfee " , " " ) , nFeePerK ) )
return InitError ( AmountErrMsg ( " paytxfee " , gArgs . GetArg ( " -paytxfee " , " " ) ) ) ;
if ( nFeePerK > HIGH_TX_FEE_PER_KB )
InitWarning ( AmountHighWarn ( " -paytxfee " ) + " " +
_ ( " This is the transaction fee you will pay if you send a transaction. " ) ) ;
payTxFee = CFeeRate ( nFeePerK , 1000 ) ;
if ( payTxFee < : : minRelayTxFee )
{
return InitError ( strprintf ( _ ( " Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s) " ) ,
gArgs . GetArg ( " -paytxfee " , " " ) , : : minRelayTxFee . ToString ( ) ) ) ;
}
}
if ( gArgs . IsArgSet ( " -maxtxfee " ) )
{
CAmount nMaxFee = 0 ;
if ( ! ParseMoney ( gArgs . GetArg ( " -maxtxfee " , " " ) , nMaxFee ) )
return InitError ( AmountErrMsg ( " maxtxfee " , gArgs . GetArg ( " -maxtxfee " , " " ) ) ) ;
if ( nMaxFee > HIGH_MAX_TX_FEE )
InitWarning ( _ ( " -maxtxfee is set very high! Fees this large could be paid on a single transaction. " ) ) ;
maxTxFee = nMaxFee ;
if ( CFeeRate ( maxTxFee , 1000 ) < : : minRelayTxFee )
{
return InitError ( strprintf ( _ ( " Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions) " ) ,
gArgs . GetArg ( " -maxtxfee " , " " ) , : : minRelayTxFee . ToString ( ) ) ) ;
}
}
nTxConfirmTarget = gArgs . GetArg ( " -txconfirmtarget " , DEFAULT_TX_CONFIRM_TARGET ) ;
bSpendZeroConfChange = gArgs . GetBoolArg ( " -spendzeroconfchange " , DEFAULT_SPEND_ZEROCONF_CHANGE ) ;
fWalletRbf = gArgs . GetBoolArg ( " -walletrbf " , DEFAULT_WALLET_RBF ) ;
return true ;
}
2017-08-28 19:33:59 +02:00
void RegisterWalletRPC ( CRPCTable & t )
{
2017-11-02 01:41:29 +01:00
if ( gArgs . GetBoolArg ( " -disablewallet " , DEFAULT_DISABLE_WALLET ) ) {
return ;
}
2017-08-28 19:33:59 +02:00
RegisterWalletRPCCommands ( t ) ;
}
2017-08-29 17:51:02 +02:00
bool VerifyWallets ( )
2017-08-02 13:48:52 +02:00
{
2017-11-02 01:41:29 +01:00
if ( gArgs . GetBoolArg ( " -disablewallet " , DEFAULT_DISABLE_WALLET ) ) {
2017-08-02 13:48:52 +02:00
return true ;
2017-11-02 01:41:29 +01:00
}
2017-08-02 13:48:52 +02:00
2017-10-12 11:04:46 +02:00
if ( gArgs . IsArgSet ( " -walletdir " ) & & ! fs : : is_directory ( GetWalletDir ( ) ) ) {
2017-11-18 01:36:37 +01:00
if ( fs : : exists ( fs : : system_complete ( gArgs . GetArg ( " -walletdir " , " " ) ) ) ) {
return InitError ( strprintf ( _ ( " Specified -walletdir \" %s \" is not a directory " ) , gArgs . GetArg ( " -walletdir " , " " ) . c_str ( ) ) ) ;
}
return InitError ( strprintf ( _ ( " Specified -walletdir \" %s \" does not exist " ) , gArgs . GetArg ( " -walletdir " , " " ) . c_str ( ) ) ) ;
2017-10-12 11:04:46 +02:00
}
LogPrintf ( " Using wallet directory %s \n " , GetWalletDir ( ) . string ( ) ) ;
2017-08-02 13:48:52 +02:00
uiInterface . InitMessage ( _ ( " Verifying wallet(s)... " ) ) ;
// Keep track of each wallet absolute path to detect duplicates.
std : : set < fs : : path > wallet_paths ;
for ( const std : : string & walletFile : gArgs . GetArgs ( " -wallet " ) ) {
if ( boost : : filesystem : : path ( walletFile ) . filename ( ) ! = walletFile ) {
return InitError ( strprintf ( _ ( " Error loading wallet %s. -wallet parameter must only specify a filename (not a path) . " ), walletFile)) ;
}
if ( SanitizeString ( walletFile , SAFE_CHARS_FILENAME ) ! = walletFile ) {
return InitError ( strprintf ( _ ( " Error loading wallet %s. Invalid characters in -wallet filename. " ) , walletFile ) ) ;
}
2017-10-08 22:48:07 +02:00
fs : : path wallet_path = fs : : absolute ( walletFile , GetWalletDir ( ) ) ;
2017-08-02 13:48:52 +02:00
if ( fs : : exists ( wallet_path ) & & ( ! fs : : is_regular_file ( wallet_path ) | | fs : : is_symlink ( wallet_path ) ) ) {
return InitError ( strprintf ( _ ( " Error loading wallet %s. -wallet filename must be a regular file. " ) , walletFile ) ) ;
}
if ( ! wallet_paths . insert ( wallet_path ) . second ) {
return InitError ( strprintf ( _ ( " Error loading wallet %s. Duplicate -wallet filename specified. " ) , walletFile ) ) ;
}
std : : string strError ;
2017-10-08 22:48:07 +02:00
if ( ! CWalletDB : : VerifyEnvironment ( walletFile , GetWalletDir ( ) . string ( ) , strError ) ) {
2017-08-02 13:48:52 +02:00
return InitError ( strError ) ;
}
if ( gArgs . GetBoolArg ( " -salvagewallet " , false ) ) {
// Recover readable keypairs:
CWallet dummyWallet ;
std : : string backup_filename ;
if ( ! CWalletDB : : Recover ( walletFile , ( void * ) & dummyWallet , CWalletDB : : RecoverKeysOnlyFilter , backup_filename ) ) {
return false ;
}
}
std : : string strWarning ;
2017-10-08 22:48:07 +02:00
bool dbV = CWalletDB : : VerifyDatabaseFile ( walletFile , GetWalletDir ( ) . string ( ) , strWarning , strError ) ;
2017-08-02 13:48:52 +02:00
if ( ! strWarning . empty ( ) ) {
InitWarning ( strWarning ) ;
}
if ( ! dbV ) {
InitError ( strError ) ;
return false ;
}
}
return true ;
}
2017-08-29 17:48:25 +02:00
bool OpenWallets ( )
2017-08-02 13:48:52 +02:00
{
if ( gArgs . GetBoolArg ( " -disablewallet " , DEFAULT_DISABLE_WALLET ) ) {
LogPrintf ( " Wallet disabled! \n " ) ;
return true ;
}
for ( const std : : string & walletFile : gArgs . GetArgs ( " -wallet " ) ) {
CWallet * const pwallet = CWallet : : CreateWalletFromFile ( walletFile ) ;
if ( ! pwallet ) {
return false ;
}
vpwallets . push_back ( pwallet ) ;
}
return true ;
}
2017-08-28 18:31:53 +02:00
2017-08-29 17:47:06 +02:00
void StartWallets ( CScheduler & scheduler ) {
for ( CWalletRef pwallet : vpwallets ) {
pwallet - > postInitProcess ( scheduler ) ;
}
}
2017-08-28 18:53:56 +02:00
void FlushWallets ( ) {
2017-08-28 18:31:53 +02:00
for ( CWalletRef pwallet : vpwallets ) {
2017-08-28 18:53:56 +02:00
pwallet - > Flush ( false ) ;
}
}
void StopWallets ( ) {
for ( CWalletRef pwallet : vpwallets ) {
pwallet - > Flush ( true ) ;
2017-08-28 18:31:53 +02:00
}
}
2017-08-28 18:41:33 +02:00
void CloseWallets ( ) {
for ( CWalletRef pwallet : vpwallets ) {
delete pwallet ;
}
vpwallets . clear ( ) ;
}