diff --git a/src/Makefile.am b/src/Makefile.am index 06b09404a76..467fe2a6a34 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -158,6 +158,7 @@ BITCOIN_CORE_H = \ validation.h \ validationinterface.h \ versionbits.h \ + walletinitinterface.h \ wallet/coincontrol.h \ wallet/crypter.h \ wallet/db.h \ @@ -165,6 +166,7 @@ BITCOIN_CORE_H = \ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ + wallet/walletinit.h \ warnings.h \ zmq/zmqabstractnotifier.h \ zmq/zmqconfig.h\ @@ -219,6 +221,7 @@ libbitcoin_server_a_SOURCES = \ validation.cpp \ validationinterface.cpp \ versionbits.cpp \ + walletinitinterface.cpp \ $(BITCOIN_CORE_H) if ENABLE_ZMQ @@ -243,6 +246,7 @@ libbitcoin_wallet_a_SOURCES = \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ wallet/walletdb.cpp \ + wallet/walletinit.cpp \ $(BITCOIN_CORE_H) # crypto primitives library diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 374678310c6..90b5b136b9e 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -19,6 +19,10 @@ #include "httpserver.h" #include "httprpc.h" #include "utilstrencodings.h" +#if ENABLE_WALLET +#include "walletinitinterface.h" +#include "wallet/walletinit.h" +#endif #include @@ -67,6 +71,12 @@ bool AppInit(int argc, char* argv[]) bool fRet = false; +#if ENABLE_WALLET + // Register wallet initialization callbacks + WalletInit wallet_init; + RegisterWalletInitInterface(&wallet_init); +#endif + // // Parameters // diff --git a/src/init.cpp b/src/init.cpp index 672ef77e80f..00b0574219a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -41,10 +41,8 @@ #include "ui_interface.h" #include "util.h" #include "utilmoneystr.h" +#include "walletinitinterface.h" #include "validationinterface.h" -#ifdef ENABLE_WALLET -#include "wallet/wallet.h" -#endif #include "warnings.h" #include #include @@ -187,11 +185,6 @@ void Shutdown() StopREST(); StopRPC(); StopHTTPServer(); -#ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - pwallet->Flush(false); - } -#endif MapPort(false); UnregisterValidationInterface(peerLogic.get()); peerLogic.reset(); @@ -229,11 +222,7 @@ void Shutdown() delete pblocktree; pblocktree = NULL; } -#ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - pwallet->Flush(true); - } -#endif + GetWalletInitSignals().ShutdownWallets(); #if ENABLE_ZMQ if (pzmqNotificationInterface) { @@ -251,12 +240,7 @@ void Shutdown() } #endif UnregisterAllValidationInterfaces(); -#ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - delete pwallet; - } - vpwallets.clear(); -#endif + GetWalletInitSignals().DeleteWallets(); globalVerifyHandle.reset(); ECC_Stop(); LogPrintf("%s: done\n", __func__); @@ -403,9 +387,8 @@ std::string HelpMessage(HelpMessageMode mode) " " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway")); strUsage += HelpMessageOpt("-maxuploadtarget=", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET)); -#ifdef ENABLE_WALLET - strUsage += CWallet::GetWalletHelpString(showDebug); -#endif + boost::optional wallet_rpc_help_string = GetWalletInitSignals().GetWalletHelpString(showDebug); + if (wallet_rpc_help_string) strUsage += *wallet_rpc_help_string; #if ENABLE_ZMQ strUsage += HelpMessageGroup(_("ZeroMQ notification options:")); @@ -1006,9 +989,7 @@ bool AppInitParameterInteraction() } RegisterAllCoreRPCCommands(tableRPC); -#ifdef ENABLE_WALLET - RegisterWalletRPCCommands(tableRPC); -#endif + GetWalletInitSignals().RegisterWalletRPC(tableRPC); nConnectTimeout = GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT); if (nConnectTimeout <= 0) @@ -1051,10 +1032,8 @@ bool AppInitParameterInteraction() return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString())); nBytesPerSigOp = GetArg("-bytespersigop", nBytesPerSigOp); -#ifdef ENABLE_WALLET - if (!CWallet::ParameterInteraction()) - return false; -#endif + boost::optional param_interaction_success = GetWalletInitSignals().WalletParameterInteraction(); + if (param_interaction_success && !(*param_interaction_success)) return false; fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG); fAcceptDatacarrier = GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER); @@ -1218,10 +1197,9 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) int64_t nStart; // ********************************************************* Step 5: verify wallet database integrity -#ifdef ENABLE_WALLET - if (!CWallet::Verify()) - return false; -#endif + boost::optional wallet_verify_success = GetWalletInitSignals().VerifyWallets(); + if (wallet_verify_success && !(*wallet_verify_success)) return false; + // ********************************************************* Step 6: network initialization // Note that we absolutely cannot open any actual connections // until the very end ("start node") as the UTXO/block state @@ -1506,12 +1484,13 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) fFeeEstimatesInitialized = true; // ********************************************************* Step 8: load wallet -#ifdef ENABLE_WALLET - if (!CWallet::InitLoadWallet()) + + boost::optional wallet_load_success = GetWalletInitSignals().InitLoadWallets(); + if (!wallet_load_success) { + LogPrintf("No wallet support compiled in!\n"); + } else if (!(*wallet_load_success)) { return false; -#else - LogPrintf("No wallet support compiled in!\n"); -#endif + } // ********************************************************* Step 9: data directory maintenance @@ -1637,11 +1616,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) SetRPCWarmupFinished(); uiInterface.InitMessage(_("Done loading")); -#ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - pwallet->postInitProcess(scheduler); - } -#endif + GetWalletInitSignals().WalletCompleteStartup(scheduler); return !fRequestShutdown; } diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 8a745cadce7..e6e80cc1f97 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -34,6 +34,8 @@ #include "warnings.h" #ifdef ENABLE_WALLET +#include "walletinitinterface.h" +#include "wallet/walletinit.h" #include "wallet/wallet.h" #endif @@ -662,6 +664,10 @@ int main(int argc, char *argv[]) // Start up the payment server early, too, so impatient users that click on // bitcoin: links repeatedly have their payment requests routed to this process: app.createPaymentServer(); + + // Register wallet initialization callbacks + WalletInit wallet_init; + RegisterWalletInitInterface(&wallet_init); #endif /// 9. Main GUI initialization diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index ff1eb59f16b..a0e63119bef 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -184,7 +184,7 @@ void TestSendCoins() BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, false /* cancel */); BumpFee(transactionView, txid2, true /* expect disabled */, "already bumped" /* expected error */, false /* cancel */); - bitdb.Flush(true); + bitdb.Flush("wallet_test.dat"); bitdb.Reset(); } diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index da2d1807563..aa4789d6c4e 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -226,40 +226,12 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr) { - LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); - LogPrintf("Using wallet %s\n", walletFile); - - // Wallet file must be a plain filename without a directory - if (walletFile != fs::basename(walletFile) + fs::extension(walletFile)) - { - errorStr = strprintf(_("Wallet %s resides outside data directory %s"), walletFile, dataDir.string()); - return false; - } - - if (!bitdb.Open(dataDir)) - { - // try moving the database env out of the way - fs::path pathDatabase = dataDir / "database"; - fs::path pathDatabaseBak = dataDir / strprintf("database.%d.bak", GetTime()); - try { - fs::rename(pathDatabase, pathDatabaseBak); - LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); - } catch (const fs::filesystem_error&) { - // failure is ok (well, not really, but it's not worse than what we started with) - } - - // try again - if (!bitdb.Open(dataDir)) { - // if it still fails, it probably means we can't even create the database env - errorStr = strprintf(_("Error initializing wallet database environment %s!"), GetDataDir()); - return false; - } - } return true; } bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc) { + LogPrintf("Using wallet %s\n", walletFile); if (fs::exists(dataDir / walletFile)) { std::string backup_filename; @@ -557,22 +529,32 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) return false; } +void CDBEnv::Shutdown() +{ + char** listp; + if (mapFileUseCount.empty()) { + dbenv->log_archive(&listp, DB_ARCH_REMOVE); + Close(); + if (!fMockDb) + fs::remove_all(fs::path(strPath) / "database"); + } +} -void CDBEnv::Flush(bool fShutdown) +void CDBEnv::Flush(std::string strFile) { int64_t nStart = GetTimeMillis(); // Flush log data to the actual data file on all files that are not in use - LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started"); + LogPrint(BCLog::DB, "CDBEnv::Flush: Flush%s\n", fDbEnvInit ? "" : " database not started"); if (!fDbEnvInit) return; { LOCK(cs_db); std::map::iterator mi = mapFileUseCount.begin(); while (mi != mapFileUseCount.end()) { - std::string strFile = (*mi).first; + std::string foundStrFile = (*mi).first; int nRefCount = (*mi).second; - LogPrint(BCLog::DB, "CDBEnv::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount); - if (nRefCount == 0) { + if (foundStrFile == strFile && nRefCount == 0) { + LogPrint(BCLog::DB, "CDBEnv::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount); // Move log data to the dat file CloseDb(strFile); LogPrint(BCLog::DB, "CDBEnv::Flush: %s checkpoint\n", strFile); @@ -585,16 +567,7 @@ void CDBEnv::Flush(bool fShutdown) } else mi++; } - LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart); - if (fShutdown) { - char** listp; - if (mapFileUseCount.empty()) { - dbenv->log_archive(&listp, DB_ARCH_REMOVE); - Close(); - if (!fMockDb) - fs::remove_all(fs::path(strPath) / "database"); - } - } + LogPrint(BCLog::DB, "CDBEnv::Flush: Flush took %15dms\n", GetTimeMillis() - nStart); } } @@ -683,9 +656,9 @@ bool CWalletDBWrapper::Backup(const std::string& strDest) return false; } -void CWalletDBWrapper::Flush(bool shutdown) +void CWalletDBWrapper::Flush() { if (!IsDummy()) { - env->Flush(shutdown); + env->Flush(strFile); } } diff --git a/src/wallet/db.h b/src/wallet/db.h index 7cccc65660d..4c2268a12c8 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -70,7 +70,8 @@ class CDBEnv bool Open(const fs::path& path); void Close(); - void Flush(bool fShutdown); + void Flush(std::string strFile); + void Shutdown(); void CheckpointLSN(const std::string& strFile); void CloseDb(const std::string& strFile); @@ -119,7 +120,7 @@ class CWalletDBWrapper /** Make sure all changes are flushed to disk. */ - void Flush(bool shutdown); + void Flush(); void IncrementUpdateCounter(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 867ccd42444..cbc26a8a8dc 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3038,9 +3038,8 @@ static const CRPCCommand commands[] = void RegisterWalletRPCCommands(CRPCTable &t) { - if (GetBoolArg("-disablewallet", false)) - return; - - for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { t.appendCommand(commands[vcidx].name, &commands[vcidx]); + } } + diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index 922fcc8e898..26ed8a99c00 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -30,6 +30,7 @@ WalletTestingSetup::~WalletTestingSetup() delete pwalletMain; pwalletMain = NULL; - bitdb.Flush(true); + bitdb.Flush("wallet_test.dat"); + bitdb.Shutdown(); bitdb.Reset(); } diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 96a1b14b60e..156403d60ed 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -606,7 +606,8 @@ class ListCoinsTestingSetup : public TestChain100Setup ~ListCoinsTestingSetup() { wallet.reset(); - ::bitdb.Flush(true); + ::bitdb.Flush("wallet_test.dat"); + ::bitdb.Shutdown(); ::bitdb.Reset(); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0d1a86dd244..65a0274d778 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -433,51 +433,9 @@ bool CWallet::HasWalletSpend(const uint256& txid) const return (iter != mapTxSpends.end() && iter->first.hash == txid); } -void CWallet::Flush(bool shutdown) +void CWallet::Flush() { - dbw->Flush(shutdown); -} - -bool CWallet::Verify() -{ - if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) - return true; - - uiInterface.InitMessage(_("Verifying wallet(s)...")); - - for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - if (boost::filesystem::path(walletFile).filename() != walletFile) { - return InitError(_("-wallet parameter must only specify a filename (not a path)")); - } else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { - return InitError(_("Invalid characters in -wallet filename")); - } - - std::string strError; - if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { - return InitError(strError); - } - - if (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; - bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError); - if (!strWarning.empty()) { - InitWarning(strWarning); - } - if (!dbV) { - InitError(strError); - return false; - } - } - - return true; + dbw->Flush(); } void CWallet::SyncMetaData(std::pair range) @@ -3731,41 +3689,28 @@ std::vector CWallet::GetDestValues(const std::string& prefix) const return values; } -std::string CWallet::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=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); - strUsage += HelpMessageOpt("-fallbackfee=", 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("-mintxfee=", 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=", 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=", 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("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); - 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=", _("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)); - strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); - strUsage += HelpMessageOpt("-zapwallettxes=", _("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=", strprintf("Flush wallet database activity from memory to disk log every 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)); +bool VerifyWallet(std::string walletFile, bool salvage_wallet) +{ + if (salvage_wallet) { + // Recover readable keypairs: + CWallet dummyWallet; + std::string backup_filename; + if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) { + return false; + } } - return strUsage; + std::string strError; + std::string strWarning; + bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError); + if (!strWarning.empty()) { + InitWarning(strWarning); + } + if (!dbV) { + InitError(strError); + return false; + } + return true; } CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) @@ -3958,24 +3903,6 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) return walletInstance; } -bool CWallet::InitLoadWallet() -{ - if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - LogPrintf("Wallet disabled!\n"); - return true; - } - - for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - CWallet * const pwallet = CreateWalletFromFile(walletFile); - if (!pwallet) { - return false; - } - vpwallets.push_back(pwallet); - } - - return true; -} - std::atomic CWallet::fFlushScheduled(false); void CWallet::postInitProcess(CScheduler& scheduler) @@ -3990,106 +3917,6 @@ void CWallet::postInitProcess(CScheduler& scheduler) } } -bool CWallet::ParameterInteraction() -{ - SoftSetArg("-wallet", DEFAULT_WALLET_DAT); - const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; - - if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) - return true; - - if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && SoftSetBoolArg("-walletbroadcast", false)) { - LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); - } - - if (GetBoolArg("-salvagewallet", false) && SoftSetBoolArg("-rescan", true)) { - if (is_multiwallet) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); - } - // Rewrite just private keys: rescan to find transactions - LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); - } - - // -zapwallettx implies a rescan - if (GetBoolArg("-zapwallettxes", false) && SoftSetBoolArg("-rescan", true)) { - if (is_multiwallet) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); - } - LogPrintf("%s: parameter interaction: -zapwallettxes= -> setting -rescan=1\n", __func__); - } - - if (is_multiwallet) { - if (GetBoolArg("-upgradewallet", false)) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet")); - } - } - - if (GetBoolArg("-sysperms", false)) - return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); - if (GetArg("-prune", 0) && 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 (IsArgSet("-mintxfee")) - { - CAmount n = 0; - if (!ParseMoney(GetArg("-mintxfee", ""), n) || 0 == n) - return InitError(AmountErrMsg("mintxfee", 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 (IsArgSet("-fallbackfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(GetArg("-fallbackfee", ""), nFeePerK)) - return InitError(strprintf(_("Invalid amount for -fallbackfee=: '%s'"), 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 (IsArgSet("-paytxfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(GetArg("-paytxfee", ""), nFeePerK)) - return InitError(AmountErrMsg("paytxfee", 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=: '%s' (must be at least %s)"), - GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); - } - } - if (IsArgSet("-maxtxfee")) - { - CAmount nMaxFee = 0; - if (!ParseMoney(GetArg("-maxtxfee", ""), nMaxFee)) - return InitError(AmountErrMsg("maxtxfee", 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=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), - GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); - } - } - nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); - bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); - fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); - - return true; -} - bool CWallet::BackupWallet(const std::string& strDest) { return dbw->Backup(strDest); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4f558adc772..4d8f5338af1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -650,7 +650,6 @@ class CAccountingEntry class CWallet : public CCryptoKeyStore, public CValidationInterface { private: - static std::atomic fFlushScheduled; std::atomic fAbortRescan; std::atomic fScanningWallet; @@ -796,6 +795,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface fScanningWallet = false; } + static std::atomic fFlushScheduled; std::map mapWallet; std::list laccentries; @@ -1051,11 +1051,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool HasWalletSpend(const uint256& txid) const; //! Flush wallet (bitdb flush) - void Flush(bool shutdown=false); + void Flush(); - //! Verify the wallet database and perform salvage if required - static bool Verify(); - /** * Address book entry changed. * @note called with lock cs_wallet held. @@ -1092,12 +1089,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /** Mark a transaction as replaced by another transaction (e.g., BIP 125). */ bool MarkReplaced(const uint256& originalHash, const uint256& newHash); - /* Returns the wallets help message */ - static std::string GetWalletHelpString(bool showDebug); - /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ static CWallet* CreateWalletFromFile(const std::string walletFile); - static bool InitLoadWallet(); /** * Wallet post-init setup @@ -1105,9 +1098,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface */ void postInitProcess(CScheduler& scheduler); - /* Wallets parameter interaction */ - static bool ParameterInteraction(); - bool BackupWallet(const std::string& strDest); /* Set the HD chain model (chain child index counters) */ @@ -1127,6 +1117,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool SetHDMasterKey(const CPubKey& key); }; +//! Verify the database for a single wallet and perform salvage if required +bool VerifyWallet(std::string walletFile, bool salvage_wallet); + /** A key allocated from the key pool. */ class CReserveKey : public CReserveScript { diff --git a/src/wallet/walletinit.cpp b/src/wallet/walletinit.cpp new file mode 100644 index 00000000000..511bf25a584 --- /dev/null +++ b/src/wallet/walletinit.cpp @@ -0,0 +1,251 @@ +#include "fs.h" +#include "net.h" +#include "scheduler.h" +#include "util.h" +#include "utilmoneystr.h" +#include "validation.h" +#include "wallet/walletinit.h" + +std::string WalletInit::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=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); + strUsage += HelpMessageOpt("-fallbackfee=", 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("-mintxfee=", 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=", 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=", 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("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); + 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=", _("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)); + strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); + strUsage += HelpMessageOpt("-zapwallettxes=", _("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=", strprintf("Flush wallet database activity from memory to disk log every 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 WalletInit::WalletParameterInteraction() +{ + SoftSetArg("-wallet", DEFAULT_WALLET_DAT); + const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; + + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + return true; + + if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && SoftSetBoolArg("-walletbroadcast", false)) { + LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); + } + + if (GetBoolArg("-salvagewallet", false) && SoftSetBoolArg("-rescan", true)) { + if (is_multiwallet) { + return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); + } + // Rewrite just private keys: rescan to find transactions + LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); + } + + // -zapwallettx implies a rescan + if (GetBoolArg("-zapwallettxes", false) && SoftSetBoolArg("-rescan", true)) { + if (is_multiwallet) { + return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); + } + LogPrintf("%s: parameter interaction: -zapwallettxes= -> setting -rescan=1\n", __func__); + } + + if (is_multiwallet) { + if (GetBoolArg("-upgradewallet", false)) { + return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet")); + } + } + + if (GetBoolArg("-sysperms", false)) + return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); + if (GetArg("-prune", 0) && 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 (IsArgSet("-mintxfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-mintxfee", ""), n) || 0 == n) + return InitError(AmountErrMsg("mintxfee", 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 (IsArgSet("-fallbackfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(GetArg("-fallbackfee", ""), nFeePerK)) + return InitError(strprintf(_("Invalid amount for -fallbackfee=: '%s'"), 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 (IsArgSet("-paytxfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(GetArg("-paytxfee", ""), nFeePerK)) + return InitError(AmountErrMsg("paytxfee", 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=: '%s' (must be at least %s)"), + GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); + } + } + if (IsArgSet("-maxtxfee")) + { + CAmount nMaxFee = 0; + if (!ParseMoney(GetArg("-maxtxfee", ""), nMaxFee)) + return InitError(AmountErrMsg("maxtxfee", 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=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), + GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); + } + } + nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); + bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); + fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); + + return true; +} + +void WalletInit::RegisterWalletRPC(CRPCTable &t) +{ + if (GetBoolArg("-disablewallet", false)) return; + + RegisterWalletRPCCommands(t); +} + +bool WalletInit::VerifyWallets() +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + return true; + + uiInterface.InitMessage(_("Verifying wallet(s)...")); + + LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); + + fs::path dataDir = GetDataDir().string(); + + if (!bitdb.Open(dataDir)) + { + // try moving the database env out of the way + fs::path pathDatabase = dataDir / "database"; + fs::path pathDatabaseBak = dataDir / strprintf("database.%d.bak", GetTime()); + try { + fs::rename(pathDatabase, pathDatabaseBak); + LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); + } catch (const fs::filesystem_error&) { + // failure is ok (well, not really, but it's not worse than what we started with) + } + + // try again + if (!bitdb.Open(dataDir)) { + // if it still fails, it probably means we can't even create the database env + return InitError(strprintf(_("Error initializing wallet database environment %s!"), GetDataDir())); + } + } + + for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { + if (boost::filesystem::path(walletFile).filename() != walletFile) { + return InitError(_("-wallet parameter must only specify a filename (not a path)")); + } else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { + return InitError(_("Invalid characters in -wallet filename")); + } + + // -salvagewallet is only permitted when loading a single wallet + // (belt-and-suspenders - WalletParameterInteraction() should prevent -salvagewallet with multiwallet) + bool salvage_wallet = GetBoolArg("-salvagewallet", false) && (gArgs.GetArgs("-wallet").size()) == 1; + if (!VerifyWallet(walletFile, salvage_wallet)) return false; + } + return true; +} + +bool WalletInit::InitLoadWallets() +{ + if (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; +} + +void WalletInit::WalletCompleteStartup(CScheduler& scheduler) +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return; + + for (CWalletRef pwallet : vpwallets) { + // Add wallet transactions that aren't already in a block to mempool + // Do this here as mempool requires genesis block to be loaded + pwallet->ReacceptWalletTransactions(); + } + + // Run a thread to flush wallet periodically + if (!CWallet::fFlushScheduled.exchange(true)) { + scheduler.scheduleEvery(MaybeCompactWalletDB, 500); + } +} + +void WalletInit::ShutdownWallets() +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return; + + LOCK(bitdb.cs_db); + for (CWalletRef pwallet : vpwallets) { + pwallet->Flush(); + } + bitdb.Shutdown(); +} + +void WalletInit::DeleteWallets() +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return; + + for (CWalletRef pwallet : vpwallets) { + delete pwallet; + } + vpwallets.clear(); +} diff --git a/src/wallet/walletinit.h b/src/wallet/walletinit.h new file mode 100644 index 00000000000..47f78332e36 --- /dev/null +++ b/src/wallet/walletinit.h @@ -0,0 +1,41 @@ +#include "walletinitinterface.h" +#include "wallet/wallet.h" +#include "wallet/rpcwallet.h" + +#include + +class CRPCTable; + +class WalletInit : public WalletInitInterface +{ +/* public: */ + /* WalletInit(); */ + /* virtual ~WalletInit(); */ + +protected: + + //!Returns the wallets help message + /* static std::string GetWalletHelpString(bool showDebug) override; */ + std::string GetWalletHelpString(bool showDebug) override; + + //!Register Wallet RPC + void RegisterWalletRPC(CRPCTable &t); + + //!Wallet parameter interaction + bool WalletParameterInteraction(); + + //! Read and validate the -wallet arguments and verify the wallet database. + bool VerifyWallets(); + + //! Load walllets from file on startup + bool InitLoadWallets(); + + //! Post-init setup for wallets + void WalletCompleteStartup(CScheduler& scheduler); + + //! Flush wallets + void ShutdownWallets(); + + //! Delete wallets + void DeleteWallets(); +}; diff --git a/src/walletinitinterface.cpp b/src/walletinitinterface.cpp new file mode 100644 index 00000000000..0394b157021 --- /dev/null +++ b/src/walletinitinterface.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 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. + +#include "walletinitinterface.h" + +WalletInitSignals wallet_init_signals; + +WalletInitSignals& GetWalletInitSignals() +{ + return wallet_init_signals; +} + +void RegisterWalletInitInterface(WalletInitInterface* wallet_interface) { + wallet_init_signals.GetWalletHelpString.connect(boost::bind(&WalletInitInterface::GetWalletHelpString, wallet_interface, _1)); + wallet_init_signals.WalletParameterInteraction.connect(boost::bind(&WalletInitInterface::WalletParameterInteraction, wallet_interface)); + wallet_init_signals.RegisterWalletRPC.connect(boost::bind(&WalletInitInterface::RegisterWalletRPC, wallet_interface, _1)); + wallet_init_signals.VerifyWallets.connect(boost::bind(&WalletInitInterface::VerifyWallets, wallet_interface)); + wallet_init_signals.InitLoadWallets.connect(boost::bind(&WalletInitInterface::InitLoadWallets, wallet_interface)); + wallet_init_signals.WalletCompleteStartup.connect(boost::bind(&WalletInitInterface::WalletCompleteStartup, wallet_interface, _1)); + wallet_init_signals.ShutdownWallets.connect(boost::bind(&WalletInitInterface::ShutdownWallets, wallet_interface)); + wallet_init_signals.DeleteWallets.connect(boost::bind(&WalletInitInterface::DeleteWallets, wallet_interface)); +} + +void UnregisterWalletInitInterface(WalletInitInterface* pwalletIn) { + wallet_init_signals.GetWalletHelpString.disconnect(boost::bind(&WalletInitInterface::GetWalletHelpString, pwalletIn, _1)); + wallet_init_signals.WalletParameterInteraction.disconnect(boost::bind(&WalletInitInterface::WalletParameterInteraction, pwalletIn)); + wallet_init_signals.RegisterWalletRPC.disconnect(boost::bind(&WalletInitInterface::RegisterWalletRPC, pwalletIn, _1)); + wallet_init_signals.VerifyWallets.disconnect(boost::bind(&WalletInitInterface::VerifyWallets, pwalletIn)); + wallet_init_signals.InitLoadWallets.disconnect(boost::bind(&WalletInitInterface::InitLoadWallets, pwalletIn)); + wallet_init_signals.WalletCompleteStartup.disconnect(boost::bind(&WalletInitInterface::WalletCompleteStartup, pwalletIn, _1)); + wallet_init_signals.ShutdownWallets.disconnect(boost::bind(&WalletInitInterface::ShutdownWallets, pwalletIn)); + wallet_init_signals.DeleteWallets.disconnect(boost::bind(&WalletInitInterface::DeleteWallets, pwalletIn)); +} + +void UnregisterAllWalletInitInterfaces() { + wallet_init_signals.GetWalletHelpString.disconnect_all_slots(); + wallet_init_signals.WalletParameterInteraction.disconnect_all_slots(); + wallet_init_signals.RegisterWalletRPC.disconnect_all_slots(); + wallet_init_signals.VerifyWallets.disconnect_all_slots(); + wallet_init_signals.InitLoadWallets.disconnect_all_slots(); + wallet_init_signals.WalletCompleteStartup.disconnect_all_slots(); + wallet_init_signals.ShutdownWallets.disconnect_all_slots(); + wallet_init_signals.DeleteWallets.disconnect_all_slots(); +} diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h new file mode 100644 index 00000000000..bf00b71da34 --- /dev/null +++ b/src/walletinitinterface.h @@ -0,0 +1,57 @@ +// Copyright (c) 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. + +#ifndef WALLETINITINTERFACE_H +#define WALLETINITINTERFACE_H + +#include + +class CScheduler; +class CRPCTable; +class WalletInitInterface; + +/** Register a wallet initialization/shutdown interface from bitcoin_server*/ +void RegisterWalletInitInterface(WalletInitInterface* wallet_interface); +/** unregister a wallet initialization/shutdown interface from bitcoin_server*/ +void UnregisterWalletInitInterface(WalletInitInterface* wallet_interface); +/** unregister all wallet initialization/shutdown interfaces from bitcoin_server*/ +void UnregisterAllWalletInitInterfaces(); + +class WalletInitInterface { +protected: + virtual std::string GetWalletHelpString(bool showDebug) {return (std::string)"";} + virtual bool WalletParameterInteraction() {return false;} + virtual void RegisterWalletRPC(CRPCTable &) {} + virtual bool VerifyWallets() {return false;} + virtual bool InitLoadWallets() {return false;} + virtual void WalletCompleteStartup(CScheduler& scheduler) {} + virtual void ShutdownWallets() {} + virtual void DeleteWallets() {}; + friend void ::RegisterWalletInitInterface(WalletInitInterface*); + friend void ::UnregisterWalletInitInterface(WalletInitInterface*); + friend void ::UnregisterAllWalletInitInterfaces(); +}; + +struct WalletInitSignals { + /** Callback to get wallet help string */ + boost::signals2::signal GetWalletHelpString; + /** Callback to check wallet parameter interaction */ + boost::signals2::signal WalletParameterInteraction; + /** Callback to register wallet RPC*/ + boost::signals2::signal RegisterWalletRPC; + /** Callback to verify wallets */ + boost::signals2::signal VerifyWallets; + /** Callback to start loading wallets*/ + boost::signals2::signal InitLoadWallets; + /** Callback to complete loading wallets*/ + boost::signals2::signal WalletCompleteStartup; + /** Callback to Shutdown Wallets*/ + boost::signals2::signal ShutdownWallets; + /** Callback to delete wallets */ + boost::signals2::signal DeleteWallets; +}; + +WalletInitSignals& GetWalletInitSignals(); + +#endif // WALLETINITINTERFACE_H