diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp index 7395235537..e08e7e202b 100644 --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -1,244 +1,254 @@ // Copyright (c) 2018 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 #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include namespace interfaces { namespace { class LockImpl : public Chain::Lock { Optional getHeight() override { int height = ::ChainActive().Height(); if (height >= 0) { return height; } return nullopt; } Optional getBlockHeight(const BlockHash &hash) override { CBlockIndex *block = LookupBlockIndex(hash); if (block && ::ChainActive().Contains(block)) { return block->nHeight; } return nullopt; } int getBlockDepth(const BlockHash &hash) override { const Optional tip_height = getHeight(); const Optional height = getBlockHeight(hash); return tip_height && height ? *tip_height - *height + 1 : 0; } BlockHash getBlockHash(int height) override { CBlockIndex *block = ::ChainActive()[height]; assert(block != nullptr); return block->GetBlockHash(); } int64_t getBlockTime(int height) override { CBlockIndex *block = ::ChainActive()[height]; assert(block != nullptr); return block->GetBlockTime(); } int64_t getBlockMedianTimePast(int height) override { CBlockIndex *block = ::ChainActive()[height]; assert(block != nullptr); return block->GetMedianTimePast(); } bool haveBlockOnDisk(int height) override { CBlockIndex *block = ::ChainActive()[height]; return block && (block->nStatus.hasData() != 0) && block->nTx > 0; } Optional findFirstBlockWithTime(int64_t time, BlockHash *hash) override { CBlockIndex *block = ::ChainActive().FindEarliestAtLeast(time); if (block) { if (hash) { *hash = block->GetBlockHash(); } return block->nHeight; } return nullopt; } Optional findFirstBlockWithTimeAndHeight(int64_t time, int height) override { // TODO: Could update CChain::FindEarliestAtLeast() to take a height // parameter and use it with std::lower_bound() to make this // implementation more efficient and allow combining // findFirstBlockWithTime and findFirstBlockWithTimeAndHeight into // one method. for (CBlockIndex *block = ::ChainActive()[height]; block; block = ::ChainActive().Next(block)) { if (block->GetBlockTime() >= time) { return block->nHeight; } } return nullopt; } Optional findPruned(int start_height, Optional stop_height) override { if (::fPruneMode) { CBlockIndex *block = stop_height ? ::ChainActive()[*stop_height] : ::ChainActive().Tip(); while (block && block->nHeight >= start_height) { if (block->nStatus.hasData() == 0) { return block->nHeight; } block = block->pprev; } } return nullopt; } Optional findFork(const BlockHash &hash, Optional *height) override { const CBlockIndex *block = LookupBlockIndex(hash); const CBlockIndex *fork = block ? ::ChainActive().FindFork(block) : nullptr; if (height) { if (block) { *height = block->nHeight; } else { height->reset(); } } if (fork) { return fork->nHeight; } return nullopt; } bool isPotentialTip(const BlockHash &hash) override { if (::ChainActive().Tip()->GetBlockHash() == hash) { return true; } CBlockIndex *block = LookupBlockIndex(hash); return block && block->GetAncestor(::ChainActive().Height()) == ::ChainActive().Tip(); } CBlockLocator getLocator() override { return ::ChainActive().GetLocator(); } Optional findLocatorFork(const CBlockLocator &locator) override { LockAnnotation lock(::cs_main); if (CBlockIndex *fork = FindForkInGlobalIndex(::ChainActive(), locator)) { return fork->nHeight; } return nullopt; } bool contextualCheckTransactionForCurrentBlock( const Consensus::Params ¶ms, const CTransaction &tx, CValidationState &state) override { LockAnnotation lock(::cs_main); return ContextualCheckTransactionForCurrentBlock(params, tx, state); } }; class LockingStateImpl : public LockImpl, public UniqueLock { using UniqueLock::UniqueLock; }; class ChainImpl : public Chain { public: std::unique_ptr lock(bool try_lock) override { auto result = std::make_unique( ::cs_main, "cs_main", __FILE__, __LINE__, try_lock); if (try_lock && result && !*result) { return {}; } return result; } std::unique_ptr assumeLocked() override { return std::make_unique(); } bool findBlock(const BlockHash &hash, CBlock *block, int64_t *time, int64_t *time_max) override { CBlockIndex *index; { LOCK(cs_main); index = LookupBlockIndex(hash); if (!index) { return false; } if (time) { *time = index->GetBlockTime(); } if (time_max) { *time_max = index->GetBlockTimeMax(); } } if (block && !ReadBlockFromDisk(*block, index, Params().GetConsensus())) { block->SetNull(); } return true; } double guessVerificationProgress(const BlockHash &block_hash) override { LOCK(cs_main); return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash)); } bool hasDescendantsInMempool(const TxId &txid) override { LOCK(::g_mempool.cs); auto it_mp = ::g_mempool.mapTx.find(txid); return it_mp != ::g_mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1; } void relayTransaction(const TxId &txid) override { CInv inv(MSG_TX, txid); g_connman->ForEachNode( [&inv](CNode *node) { node->PushInventory(inv); }); } void getTransactionAncestry(const TxId &txid, size_t &ancestors, size_t &descendants) override { ::g_mempool.GetTransactionAncestry(txid, ancestors, descendants); } bool checkChainLimits(CTransactionRef tx) override { LockPoints lp; CTxMemPoolEntry entry(tx, Amount(), 0, 0, false, 0, lp); CTxMemPool::setEntries ancestors; auto limit_ancestor_count = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); auto limit_ancestor_size = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; auto limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); auto limit_descendant_size = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; std::string unused_error_string; LOCK(::g_mempool.cs); return ::g_mempool.CalculateMemPoolAncestors( entry, ancestors, limit_ancestor_count, limit_ancestor_size, limit_descendant_count, limit_descendant_size, unused_error_string); } bool getPruneMode() override { return ::fPruneMode; } bool p2pEnabled() override { return g_connman != nullptr; } int64_t getAdjustedTime() override { return GetAdjustedTime(); } + void initMessage(const std::string &message) override { + ::uiInterface.InitMessage(message); + } + void initWarning(const std::string &message) override { + InitWarning(message); + } + void initError(const std::string &message) override { + InitError(message); + } }; } // namespace std::unique_ptr MakeChain() { return std::make_unique(); } } // namespace interfaces diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index b97d853828..c39b2ba66e 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -1,212 +1,221 @@ // Copyright (c) 2018 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 BITCOIN_INTERFACES_CHAIN_H #define BITCOIN_INTERFACES_CHAIN_H #include #include #include #include #include #include #include #include struct BlockHash; class CBlock; struct CBlockLocator; class CChainParams; class CScheduler; class CTransaction; class CValidationState; namespace Consensus { struct Params; } namespace interfaces { //! Interface for giving wallet processes access to blockchain state. class Chain { public: virtual ~Chain() {} //! Interface for querying locked chain state, used by legacy code that //! assumes state won't change between calls. New code should avoid using //! the Lock interface and instead call higher-level Chain methods //! that return more information so the chain doesn't need to stay locked //! between calls. class Lock { public: virtual ~Lock() {} //! Get current chain height, not including genesis block (returns 0 if //! chain only contains genesis block, nullopt if chain does not contain //! any blocks). virtual Optional getHeight() = 0; //! Get block height above genesis block. Returns 0 for genesis block, //! 1 for following block, and so on. Returns nullopt for a block not //! included in the current chain. virtual Optional getBlockHeight(const BlockHash &hash) = 0; //! Get block depth. Returns 1 for chain tip, 2 for preceding block, and //! so on. Returns 0 for a block not included in the current chain. virtual int getBlockDepth(const BlockHash &hash) = 0; //! Get block hash. Height must be valid or this function will abort. virtual BlockHash getBlockHash(int height) = 0; //! Get block time. Height must be valid or this function will abort. virtual int64_t getBlockTime(int height) = 0; //! Get block median time past. Height must be valid or this function //! will abort. virtual int64_t getBlockMedianTimePast(int height) = 0; //! Check that the block is available on disk (i.e. has not been //! pruned), and contains transactions. virtual bool haveBlockOnDisk(int height) = 0; //! Return height of the first block in the chain with timestamp equal //! or greater than the given time, or nullopt if there is no block with //! a high enough timestamp. Also return the block hash as an optional //! output parameter (to avoid the cost of a second lookup in case this //! information is needed.) virtual Optional findFirstBlockWithTime(int64_t time, BlockHash *hash) = 0; //! Return height of the first block in the chain with timestamp equal //! or greater than the given time and height equal or greater than the //! given height, or nullopt if there is no such block. //! //! Calling this with height 0 is equivalent to calling //! findFirstBlockWithTime, but less efficient because it requires a //! linear instead of a binary search. virtual Optional findFirstBlockWithTimeAndHeight(int64_t time, int height) = 0; //! Return height of last block in the specified range which is pruned, //! or nullopt if no block in the range is pruned. Range is inclusive. virtual Optional findPruned(int start_height = 0, Optional stop_height = nullopt) = 0; //! Return height of the highest block on the chain that is an ancestor //! of the specified block, or nullopt if no common ancestor is found. //! Also return the height of the specified block as an optional output //! parameter (to avoid the cost of a second hash lookup in case this //! information is desired). virtual Optional findFork(const BlockHash &hash, Optional *height) = 0; //! Return true if block hash points to the current chain tip, or to a //! possible descendant of the current chain tip that isn't currently //! connected. virtual bool isPotentialTip(const BlockHash &hash) = 0; //! Get locator for the current chain tip. virtual CBlockLocator getLocator() = 0; //! Return height of the latest block common to locator and chain, which //! is guaranteed to be an ancestor of the block used to create the //! locator. virtual Optional findLocatorFork(const CBlockLocator &locator) = 0; //! Check if transaction will be final given chain height current time. virtual bool contextualCheckTransactionForCurrentBlock( const Consensus::Params ¶ms, const CTransaction &tx, CValidationState &state) = 0; }; //! Return Lock interface. Chain is locked when this is called, and //! unlocked when the returned interface is freed. virtual std::unique_ptr lock(bool try_lock = false) = 0; //! Return Lock interface assuming chain is already locked. This //! method is temporary and is only used in a few places to avoid changing //! behavior while code is transitioned to use the Chain::Lock interface. virtual std::unique_ptr assumeLocked() = 0; //! Return whether node has the block and optionally return block metadata //! or contents. //! //! If a block pointer is provided to retrieve the block contents, and the //! block exists but doesn't have data (for example due to pruning), the //! block will be empty and all fields set to null. virtual bool findBlock(const BlockHash &hash, CBlock *block = nullptr, int64_t *time = nullptr, int64_t *max_time = nullptr) = 0; //! Estimate fraction of total transactions verified if blocks up to //! the specified block hash are verified. virtual double guessVerificationProgress(const BlockHash &block_hash) = 0; //! Check if transaction has descendants in mempool. virtual bool hasDescendantsInMempool(const TxId &txid) = 0; //! Calculate mempool ancestor and descendant counts for the given //! transaction. virtual void getTransactionAncestry(const TxId &txid, size_t &ancestors, size_t &descendants) = 0; //! Relay transaction. virtual void relayTransaction(const TxId &txid) = 0; //! Check chain limits. virtual bool checkChainLimits(CTransactionRef tx) = 0; //! Check if pruning is enabled. virtual bool getPruneMode() = 0; //! Check if p2p enabled. virtual bool p2pEnabled() = 0; //! Get adjusted time. virtual int64_t getAdjustedTime() = 0; + + //! Send init message. + virtual void initMessage(const std::string &message) = 0; + + //! Send init warning. + virtual void initWarning(const std::string &message) = 0; + + //! Send init error. + virtual void initError(const std::string &message) = 0; }; //! Interface to let node manage chain clients (wallets, or maybe tools for //! monitoring and analysis in the future). class ChainClient { public: virtual ~ChainClient() {} //! Register rpcs. virtual void registerRpcs() = 0; //! Check for errors before loading. virtual bool verify(const CChainParams &chainParams) = 0; //! Load saved state. virtual bool load(const CChainParams &chainParams) = 0; //! Start client execution and provide a scheduler. virtual void start(CScheduler &scheduler) = 0; //! Save state to disk. virtual void flush() = 0; //! Shut down client. virtual void stop() = 0; }; //! Return implementation of Chain interface. std::unique_ptr MakeChain(); //! Return implementation of ChainClient interface for a wallet client. This //! function will be undefined in builds where ENABLE_WALLET is false. //! //! Currently, wallets are the only chain clients. But in the future, other //! types of chain clients could be added, such as tools for monitoring, //! analysis, or fee estimation. These clients need to expose their own //! MakeXXXClient functions returning their implementations of the ChainClient //! interface. std::unique_ptr MakeWalletClient(Chain &chain, std::vector wallet_filenames); } // namespace interfaces #endif // BITCOIN_INTERFACES_CHAIN_H diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 2a642c5f94..dc5efbcb81 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -1,379 +1,383 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2017 The Bitcoin Core developers // Copyright (c) 2018-2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include class WalletInit : public WalletInitInterface { public: //! Was the wallet component compiled in. bool HasWalletSupport() const override { return true; } //! Return the wallets help message. void AddWalletOptions() const override; //! Wallets parameter interaction bool ParameterInteraction() const override; //! Add wallets that should be opened to list of init interfaces. void Construct(InitInterfaces &interfaces) const override; }; const WalletInitInterface &g_wallet_init_interface = WalletInit(); void WalletInit::AddWalletOptions() const { gArgs.AddArg( "-avoidpartialspends", strprintf("Group outputs by address, selecting all or none, instead of " "selecting on a per-output basis. Privacy is improved as an " "address is only used once (unless someone sends to it after " "spending from it), but may result in slightly higher fees " "as suboptimal coin selection may result due to the added " "limitation (default: %u)", DEFAULT_AVOIDPARTIALSPENDS), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg("-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)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg("-keypool=", strprintf("Set key pool size to (default: %u)", DEFAULT_KEYPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg( "-maxtxfee=", strprintf("Maximum total fees (in %s) to use in a single wallet " "transaction or raw transaction; setting this too low may " "abort large transactions (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-mintxfee=", strprintf("Fees (in %s/kB) smaller than this are considered " "zero fee for transaction creation (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE_PER_KB)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg( "-paytxfee=", strprintf( "Fee (in %s/kB) to add to transactions you send (default: %s)", CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg( "-rescan", "Rescan the block chain for missing wallet transactions on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg( "-salvagewallet", "Attempt to recover private keys from a corrupt wallet on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg( "-spendzeroconfchange", strprintf( "Spend unconfirmed change when sending transactions (default: %d)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg("-wallet=", "Specify wallet database path. Can be specified multiple " "times to load multiple wallets. Path is interpreted relative " "to if it is not absolute, and will be created if " "it does not exist (as a directory containing a wallet.dat " "file and log files). For backwards compatibility this will " "also accept names of existing data files in .)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET); gArgs.AddArg( "-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %d)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg("-walletdir=", "Specify directory to hold wallets (default: " "/wallets if it exists, otherwise )", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg("-walletnotify=", "Execute command when a wallet transaction changes (%s in cmd " "is replaced by TxID)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg( "-zapwallettxes=", "Delete all wallet transactions and only recover those parts of the " "blockchain through -rescan on startup (1 = keep tx meta data e.g. " "payment request information, 2 = drop tx meta data)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg("-dblogsize=", strprintf("Flush wallet database activity from memory to disk " "log every megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST); gArgs.AddArg( "-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %d)", DEFAULT_FLUSHWALLET), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST); gArgs.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db " "environment (default: %d)", DEFAULT_WALLET_PRIVDB), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST); gArgs.AddArg("-walletrejectlongchains", strprintf("Wallet will not create transactions that violate " "mempool chain limits (default: %d)", DEFAULT_WALLET_REJECT_LONG_CHAINS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST); } bool WalletInit::ParameterInteraction() const { const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return true; } 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__); } } bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false); // -zapwallettxes implies dropping the mempool on startup if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) { LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> " "setting -persistmempool=0\n", __func__); } // -zapwallettxes implies a rescan if (zapwallettxes) { 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 enabled -> " "setting -rescan=1\n", __func__); } } 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("-maxtxfee")) { Amount nMaxFee = Amount::zero(); 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=: '%s' (must " "be at least the minrelay fee of %s to prevent " "stuck transactions)"), gArgs.GetArg("-maxtxfee", ""), minRelayTxFee.ToString())); } } return true; } bool VerifyWallets(const CChainParams &chainParams, interfaces::Chain &chain, const std::vector &wallet_files) { if (gArgs.IsArgSet("-walletdir")) { fs::path wallet_dir = gArgs.GetArg("-walletdir", ""); boost::system::error_code error; // The canonical path cleans the path, preventing >1 Berkeley // environment instances for the same directory fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error); if (error || !fs::exists(wallet_dir)) { - return InitError( + chain.initError( strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string())); + return false; } else if (!fs::is_directory(wallet_dir)) { - return InitError( + chain.initError( strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string())); + return false; // The canonical path transforms relative paths into absolute ones, // so we check the non-canonical version } else if (!wallet_dir.is_absolute()) { - return InitError( + chain.initError( strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string())); + return false; } gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string()); } LogPrintf("Using wallet directory %s\n", GetWalletDir().string()); uiInterface.InitMessage(_("Verifying wallet(s)...")); // Parameter interaction code should have thrown an error if -salvagewallet // was enabled with more than wallet file, so the wallet_files size check // here should have no effect. bool salvage_wallet = gArgs.GetBoolArg("-salvagewallet", false) && wallet_files.size() <= 1; // Keep track of each wallet absolute path to detect duplicates. std::set wallet_paths; for (const auto &wallet_file : wallet_files) { WalletLocation location(wallet_file); if (!wallet_paths.insert(location.GetPath()).second) { - return InitError(strprintf(_("Error loading wallet %s. Duplicate " - "-wallet filename specified."), - wallet_file)); + chain.initError(strprintf(_("Error loading wallet %s. Duplicate " + "-wallet filename specified."), + wallet_file)); + return false; } std::string error_string; std::string warning_string; bool verify_success = CWallet::Verify(chainParams, chain, location, salvage_wallet, error_string, warning_string); if (!error_string.empty()) { - InitError(error_string); + chain.initError(error_string); } if (!warning_string.empty()) { - InitWarning(warning_string); + chain.initWarning(warning_string); } if (!verify_success) { return false; } } return true; } void WalletInit::Construct(InitInterfaces &interfaces) const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { LogPrintf("Wallet disabled!\n"); return; } gArgs.SoftSetArg("-wallet", ""); interfaces.chain_clients.emplace_back(interfaces::MakeWalletClient( *interfaces.chain, gArgs.GetArgs("-wallet"))); } bool LoadWallets(const CChainParams &chainParams, interfaces::Chain &chain, const std::vector &wallet_files) { for (const std::string &walletFile : wallet_files) { std::shared_ptr pwallet = CWallet::CreateWalletFromFile( chainParams, chain, WalletLocation(walletFile)); if (!pwallet) { return false; } AddWallet(pwallet); } return true; } void StartWallets(CScheduler &scheduler) { for (const std::shared_ptr &pwallet : GetWallets()) { pwallet->postInitProcess(); } // Run a thread to flush wallet periodically scheduler.scheduleEvery( [] { MaybeCompactWalletDB(); return true; }, 500); } void FlushWallets() { for (const std::shared_ptr &pwallet : GetWallets()) { pwallet->Flush(false); } } void StopWallets() { for (const std::shared_ptr &pwallet : GetWallets()) { pwallet->Flush(true); } } void UnloadWallets() { auto wallets = GetWallets(); while (!wallets.empty()) { auto wallet = wallets.back(); wallets.pop_back(); RemoveWallet(wallet); UnloadWallet(std::move(wallet)); } } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ad167e3d0e..a546270f3c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1,4951 +1,4959 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include